Filter使用@Autowired註解失敗及解決辦法
filter中無法使用@Autowired 原因
在Spring中,web應用啟動的順序是:listener->filter->servlet,先初始化listener,然後再來就filter的初始化,再接著才到我們的dispathServlet的初始化,因此,當我們需要在filter裡注入一個註解的bean時,就會注入失敗,因為filter初始化時,註解的bean還沒初始化,沒法注入。
由一個簡單filter的使用引發的“血案”
前情回顧:專案需要一個filter過濾器來攔截所有請求,過濾器的內容很簡單,就是過濾請求url判斷使用者是否登入。如果使用者登入,則更新存在redis裡的使用者資訊過期時間;如果未登入則返回資訊給前端,跳轉登入。
<filter> <filter-name>ExtensionFilter</filter-name> <filter-class>com.extension.filters.ExtensionFilter</filter-class> </filter> <filter-mapping> <filter-name>ExtensionFilter</filter-name> <url-pattern>/*</url-pattern> </filter-mapping>
給自己挖的坑:
首先在這個filter過濾器中,需要從請求頭中獲取token,根據token從redis中拿到使用者資訊。此時需要在filter中注入封裝好的redis實體bean,我使用@Autowired註解,此時程式碼編譯沒有報錯,看起來程式沒問題。
接下來就是在坑裡轉悠直到把坑填上:
緊接著問題就來了,專案能正常跑起來,但是一請求就報NULLPOINTEREXCEPTION異常,debug除錯也找不到原因。折磨了我將近半個小時,終於找到問題。細心閱讀並且對Web容器初始化熟悉的朋友應該知道我犯了什麼錯誤了。
問題:在過濾器中採用@Autowired方式注入
1 @Autowired2 rivate RedisCache redisCache;
然鵝,實際證明注入失敗。
分析原因:Web容器初始化順序是按照Listener-filter-servlet的順序進行,因為dispatcherServlet是在filter之後才進行初始化,也就是這個時候我們要自動注入的bean才被初始化。所以此時在filter中自動注入的時候還沒有bean,所以會注入失敗。
解決辦法:
1、採用xml配置方式
- Web.xml這樣配置
<filter> <filter-name>ExtensionFilter</filter-name> <filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class> <init-param> <param-name>targetBeanName</param-name> <param-value>ExtensionFilter</param-value> </init-param> <init-param> <param-name>targetFilterLifecycle</param-name> <param-value>true</param-value> </init-param> </filter> <filter-mapping> <filter-name>DelegatingFilterProxy</filter-name> <url-pattern>/*</url-pattern> </filter-mapping>
- 在spring配置檔案中使用bean標籤配置serviceImpl和ExtensionFilter
<bean id="ExtensionFilter" class="com.extension.filters.ExtensionFilter"> <property name="redisCache" ref="redisCache"></property> </bean> <!--要注入的bean--> <bean id="redisCache" class="com.htjc.interceptor.redis.RedisCache"> </bean>
這種方式的重點是在web.xml中用到了DelegatingFilterProxy這個filter代理類。具體用法可以去看看DelegatingFilterProxy原始碼。
2、使用ApplicationContext物件獲取
//要注入的物件 private RedisCache redisCache; ServletContext context = request.getServletContext(); ApplicationContext ctx = WebApplicationContextUtils.getWebApplicationContext(context); redisCache = ctx.getBean(RedisCache.class);
以上內容純屬個人總結,如有問題請在評論區留言!