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