SpringIOC中的后处理器
beanFactory后处理器
beanFactory后处理器,是为beanFactory而服务的,比如某些后处理器是为了增加各种对bean的定义而产生的,比较熟悉的有解析各种注解的后处理器、解析xml文件的后处理器。
常见的解析注解的BeanFactory后处理器,通过调用ApplicationContext实例对象中的方法registerBean(),将后处理器添加进容器:
// context是ApplicationContext的实例对象,下文中的代码同理
context.registerBean(ConfigurationClassPostProcessor.class);
包 | 类 | 功能 |
---|---|---|
org.springframework.context.annotation | ConfigurationClassPostProcessor | 解析注解: @Component @PropertySources @ComponentScan @ComponentScans注解的类 @Import注解的类 @ImportResource @Bean注解方法 |
org.springframework.context.annotation | CommonAnnotationBeanPostProcessor | 解析注解: @Resource @PostConstruct @PreDestroy |
org.springframework.beans.factory.annotation | AutowiredAnnotationBeanPostProcessor | 解析注解: @Autowired @Value |
注意事项
刷新context对象
在添加完后处理器之后,容器并没有立刻加载所有的bean,如果此时调用获取bean的代码,可能会报错,控制台报错如下:
Exception in thread "main" java.lang.IllegalStateException: org.springframework.context.support.GenericApplicationContext@5e91993f has not been refreshed yet
报错的提示是:还没有将context对象刷新。想解决此类问题,就必须在获取bean之前让容器中把bean加载进去,即让context执行刷新,在获取bean之前执行一行代码即可:
context.refresh();
注入String类型
若IOC注入中,有String类型的对象需要注入,比如下面的代码:
public String home;
@Autowired
public void setHome(@Value("${MAVEN_HOME}") String home) {
System.out.println("注入了home:" + home);
this.home = home;
}
SpringIOC在注入时,会报错,控制台报错如下:
Caused by: org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type 'java.lang.String' available: expected at least 1 bean which qualifies as autowire candidate. Dependency annotations: {@org.springframework.beans.factory.annotation.Value(value=${MAVEN_HOME})}
报错的提示大概意思是:没有在容器中找到java.lang.String类型的Bean。
这个问题产生的原因,是因为AutowiredAnnotationBeanPostProcessor
后处理只能识别@Autowired、@Value并将其注入的需求描述给到容器,但是无法将@Value中的值解析出来给到容器进行注入。想要解决此类问题,需要用到@Autowire注解的候选解析器ContextAnnotationAutowireCandidateResolver.clas
,代码如下:
// 添加候选解析器。因为字符串类型在容器中并不是一个Bean,在注入的时候会报错。
// 添加了这个候选解析器,配合使用@Value注解后,就不会报错。如果找不到${}中对应的值,就会原样输出
context.getDefaultListableBeanFactory().setAutowireCandidateResolver(new ContextAnnotationAutowireCandidateResolver());
关于为什么会是给BeanFactory设置备选解析器,而不是ApplicationContext。个人的理解是:由于注入之前的解析工作本就应该属于BeanFactory的活儿,而不是ApplicationContext的。因为这个是String对象进入容器之前就应该完成的解析。
bean后处理器
简单的来理解,bean后处理就是基于模版方法设计模式1而产生的,是用于对bean的生命周期各个阶段进行扩展的。
下面是Spring中Bean后处理器的实现案例,也是对bean生命周期进行管理的简单案例:
上方类图中实现类MyBeanPostProcessor.class
的定义如下:
// 这个类,就是我们需要实现的类,并将此类的实例添加到spring容器中即可
public class MyBeanPostProcessor implements InstantiationAwareBeanPostProcessor, DestructionAwareBeanPostProcessor {
// 将所有的接口即可实现即可
// 具体接口方法实现略
}
BeanPostProcessor是Bean后处理器的顶层接口类(根接口类),凡是BeanPostProcessor类型的Bean被添加到spring容器中,都会被视为Bean后处理器,并在bean的生命周期不同阶段调用不同的接口函数。
编辑文章时的测试代码
模版方法设计模式:就是将一些固定的流程提前设计好后,在每一步流程的前或后,加上提前定义好的接口,采用多态的方式调用,这样即可实现动静结合。静即是固定好的流程,动即是调用的接口,接口的实现可根据未来的需求而变化。不论动的部分如何变化,都不需要改变原有设计模式内部的封装。 ↩︎