1. 程式人生 > >使用自定義註解向servlet注入spring bean

使用自定義註解向servlet注入spring bean

由於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 操作結束