一、我们先来分析一下错误信息的具体情况
首先,出现该问题时出现的报错信息如下:
主报错信息:
Exception starting filter [rfidUtils]
报错信息解释为:异常启动过滤器 [rfidUtils]
说明:rfidUtils是我项目中的一个filter工具类,对于大家来说,类名会和我的不同。
错误定位信息:
java.lang.NullPointerException: null
at com.gainet.common.filter.RfidUtils.openTcpAll(RfidUtils.java:131)
at com.gainet.common.filter.RfidUtils.init(RfidUtils.java:121)
过滤器启动异常的原因:java.lang.NullPointerException: null
空指针异常出现的位置在:
at com.gainet.common.filter.RfidUtils.openTcpAll(RfidUtils.java:131)
at com.gainet.common.filter.RfidUtils.init(RfidUtils.java:121)
解释:在RfidUtils类的121行,调用了方法openTcpAll(),在openTcpAll()方法的131行,出现了空指针的异常。在这个位置调用了一个bean对象redisCache的方法
String redisEPC = redisCache.getCacheObject(epc);
bean对象redisCache的注入方式如下:
@Autowired private RedisCache redisCache;
二、我们再来分析一下问题出现的原因
问题出现是因为项目加载机制造成的
在spring项目启动时, 加载和初始化bean的顺序大致如下:
1、读取并解析项目的配置文件(如application.properties,application.yml)。
2、检测@Component和@Configuration等注解标注的类,并将其注册为bean。
3、扫描@ComponentScan指定的包路径,注册bean。
4、处理@PropertySource注解,加载更多的配置文件。
5、处理@Import注解,导入额外的配置类或者bean。
6、创建bean定义,这个阶段会应用@Bean定义,后处理器等。
7、应用bean的后处理器(如AutowiredAnnotationBeanPostProcessor)来注入依赖。
8、初始化bean(如果实现了InitializingBean接口,调用afterPropertiesSet方法,如果有@PostConstruct注解,调用这个注解标注的方法)。
9、最后,bean可以使用了,如果配置了lazy-init,则在容器启动时bean不会被初始化。
注意:具体的加载顺序可能会受到其他配置(如@Conditional注解等)的影响。
filter类在 2、检测@Component和@Configuration等注解标注的类 时加载,此时RedisCache类还没有被加载,所以就报错了。
在filter类中使用@Autowired等方式注入bean是不行的。
三、我们确定下解决方案
1、给filter类RfidUtils加上@Component注解,将该类交给spring管理。
@Component public class RfidUtils implements Filter {}
2、让filter类RfidUtils 实现 ApplicationContextAware 接口,将applicationContext容器存起来备用。
@Component public class RfidUtils implements Filter, ApplicationContextAware { private static ApplicationContext applicationContext; @Override public void setApplicationContext(ApplicationContext applicationContext) throws BeansException { this.applicationContext = applicationContext; } }
3、 在filter类的init方法中,用applicationContext容器通过getBean方法加载bean对象并存入变量备用。
@Component
public class RfidUtils implements Filter, ApplicationContextAware {
private static ApplicationContext applicationContext;private static RedisCache redisCache;
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
this.applicationContext = applicationContext;
}
@Override public void init(FilterConfig filterConfig) throws ServletException{ this.redisCache=applicationContext.getBean(RedisCache.class); openTcpAll(); } }
4、此时,我们在filter类中通过变量redisCache就可以调用RedisCache类了。