使用自定義註解向servlet注入spring bean
阿新 • • 發佈:2019-01-01
由於servlet容器和spring容器並不是同一個,所以當需要向servlet中注入spring bean是有以下操作:
1、使用proxy servlet(代理servlet),將實際servlet加入spring bean管理,在代理servlet的init方法中找到被代理servlet bean,後續請求處理由被代理servlet處理
2、使用Spring的Autowired註解(在servlet的bean屬性中加入此註解,由spring容器自動注入)
代理servlet實現向servlet注入spring bean真的好麻煩,所以我們選擇註解實現自動注入,但是不禁又想了想,既然spring可以通過掃描屬性註解來自動注入spring bean,那麼我們自己定義註解掃描是不是也可以呢?
首先,定義自定義註解
/**
* Servlet注入的bean屬性註解, 注入操作由AbstractBaseServlet完成
* @author jiashun
*/
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
public @interface AnnotationServletBean {
}
定義AbstractBaseServlet
/**
* 所有Servlet的基類, 用來實現向servlet注入bean
*/
public abstract class AbstractBaseServlet extends HttpServlet {
private static final long serialVersionUID = 1L;
/**
* 日誌物件
*/
private static Logger LOG = LogFactory.getLogger(AbstractBaseServlet.class);
/**
* servlet初始化
*/
public void init() throws ServletException {
/**
* 根據Servlet上下文獲取spring上下文
*/
WebApplicationContext ctx = WebApplicationContextUtils.getWebApplicationContext(getServletContext());
String className = this.getClass().getName();
Field[] beanFields = getServletBeanFields();
if (beanFields.length == 0) {
LOG.info("在類[" + className + "]中未找到具有AnnotationServletBean註解的屬性, 注入 spring bean操作結束");
return;
}
LOG.info("向類 [" + className + "] 注入spring bean 操作開始");
try {
for (Field field : beanFields) {
field.setAccessible(true);
Object value = ctx.getBean(field.getName());
field.set(this, value);
LOG.info("向類[" + className + "]注入spring bean [" + field.getName() + "]成功");
}
} catch (Exception e) {
LOG.error("注入 spring bean 操作失敗", e);
}
LOG.info("向類 [" + className + "] 注入spring bean 操作結束");
}
/**
* 獲取具有AnnotationServletBean屬性的servlet屬性集合
* @return
* 具有AnnotationServletBean註解的屬性集合, 不為null
*/
private Field[] getServletBeanFields() {
Field[] fields = ReflectUtil.getDeclaredFields(this.getClass());
if (fields.length > 0) {
List<Field> fieldList = new ArrayList<>();
for (Field field : fields) {
if (field.getAnnotation(AnnotationServletBean.class) != null) {
fieldList.add(field);
}
}
fields = fieldList.toArray(new Field[fieldList.size()]);
}
return fields;
}
}
AbstractBaseServlet的大致思想就是在init方法中找到繼承類中具有AnnotationServletBean註解的屬性,然後通過屬性名獲取spring bean,再通過反射向繼承類注入spring bean屬性。
然後我們寫一個繼承類
/**
* 通用登入Servlet
* @author jiashun
*/
public class LoginServlet extends AbstractBaseServlet {
private static final long serialVersionUID = 1L;
@AnnotationServletBean
private WebAppInitializer webappInitializer;
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException {
doPost(req, resp);
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException {
resp.getWriter().write("Hello, This is LoginServlet !");
}
}
WebAppInitializer是我自己的專案用到的服務初始化bean,已經發布為spring bean。
我們可以看到LoginServlet的webappInitializer屬性具有AnnotationServletBean註解,那麼在LoginServlet初始化時會呼叫執行AbstractBaseServlet的init方法來注入spring bean(具體注入操作就是注入具有AnnotationServletBean註解的屬性)
訪問LoginServlet服務列印日誌:
[WebApp] [INFO] [2017-05-07 09:48:59,645] [http-nio-8080-exec-14] com.jiashun.webapp.common.servlet.AbstractBaseServlet.init(AbstractBaseServlet.java:44) | 向類 [com.jiashun.webapp.common.servlet.LoginServlet] 注入spring bean 操作開始
[WebApp] [INFO] [2017-05-07 09:48:59,647] [http-nio-8080-exec-14] com.jiashun.webapp.common.servlet.AbstractBaseServlet.init(AbstractBaseServlet.java:50) | 向類[com.jiashun.webapp.common.servlet.LoginServlet]注入spring bean [webappInitializer]成功
[WebApp] [INFO] [2017-05-07 09:48:59,647] [http-nio-8080-exec-14] com.jiashun.webapp.common.servlet.AbstractBaseServlet.init(AbstractBaseServlet.java:55) | 向類 [com.jiashun.webapp.common.servlet.LoginServlet] 注入spring bean 操作結束