SpringBena的生命周期
Spring对bean进行实例化,调用Bean的构造参数
调用bean的set方法将属性注入到bean的属性
检查bean是否实现beanNameaware,beanFactoryAware
当经过上述几个步骤后,bean对象已经被正确构造,但如果你想要对象被使用前再进行一些自定义的处理,就可以通过BeanPostProcessor接口实现。
该接口提供了两个函数:postProcessBeforeInitialzation( Object bean, String beanName ) 当前正在初始化的bean对象会被传递进来,我们就可以对这个bean作任何处理。
这个函数会先于InitialzationBean执行,因此称为前置处理。 所有Aware接口的注入就是在这一步完成的。postProcessAfterInitialzation( Object bean, String beanName )
当前正在初始化的bean对象会被传递进来,我们就可以对这个bean作任何处理。 这个函数会在InitialzationBean完成后执行,因此称为后置处理。
查询是否实现benaPostProcessor,Spring会在初始化方法的前后分别调用postProcessBeforeInitialization和postProcessAfterInitialization方法
查看bean是否实现InitalizingBean接口,将调用afterPropertiesSet()方法
当BeanPostProcessor的前置处理完成后就会进入本阶段。
InitializingBean接口只有一个函数:afterPropertiesSet()这一阶段也可以在bean正式构造完成前增加我们自定义的逻辑,但它与前置处理不同,由于该函数并不会把当前bean对象传进来,因此在这一步没办法处理对象本身,只能增加一些额外的逻辑。 若要使用它,我们需要让bean实现该接口,并把要增加的逻辑写在该函数中。
然后Spring会在前置处理完成后检测当前bean是否实现了该接口,并执行afterPropertiesSet函数。当然,Spring为了降低对客户代码的侵入性,给bean的配置提供了init-method属性,该属性指定了在这一阶段需要执行的函数名。
Spring便会在初始化阶段执行我们设置的函数。init-method本质上仍然使用了InitializingBean接口。
查看是否声明初始化方法
使用bean,bean将会一直保留在应用的上下文中,直到被销毁
检查bena是否实现DisposableBean接口,Spring会调用他们的destory方法
如果bean声明销毁方法,该方法也会被调用
怎么用BeanPostProcessor,直译过来,就是“对象后处理器”,那么这个“后”,是指什么之后呢?BeanPostProcessor(下面简称BBP)。
试试便知。
@Component
public class Bean4BBP {
private static final Logger log = LoggerFactory.getLogger(Bean4BBP.class);
public Bean4BBP(){
log.info("construct Bean4BBP");
}
}
然后再写一个BeanPostProcessor,这时发现它是一个接口,没关系,那就写一个类实现它,CustomBeanPostProcessor:
@Component
public class CustomBeanPostProcessor implements BeanPostProcessor {
private static final Logger log = LoggerFactory.getLogger(CustomBeanPostProcessor.class);
public CustomBeanPostProcessor() {
log.info("construct CustomBeanPostProcessor");
}
@Override
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
if (bean instanceof Bean4BBP) {
log.info("process bean before initialization");
}
return bean;
}
@Override
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
if (bean instanceof Bean4BBP) {
log.info("process bean after initialization");
}
return bean;
}
}
然后启动我们的Spring Boot项目(直接运行Application类),看这几条日志打印的顺序:
construct CustomBeanPostProcessor
construct Bean4BBP
process bean before initialization
process bean after initialization
结论:“对象后处理器”,指的是“对象创建后处理器”。
BBP在Spring对象初始化流程的位置:
BBP的典型使用 - AOP
使用Spring AOP时,有没有发现,带有切面逻辑的对象,注入进来之后,都不是原来的对象了,aspectService是一个…$$EnhanceBySpringCGlib的对象,这其实和Spring AOP用到的动态代理有关。
这也就意味着,**最终放进Spring容器的,必须是代理对象,**而不是原先的对象,这样别的对象在注入时,才能获得带有切面逻辑的代理对象。
那么Spring是怎么做到这一点的呢?正是利用了这篇文章讲到的BBP。
显然,我只需要写一个BBP,在postProcessBeforeInitialization或者postProcessAfterInitialization方法中,对对象进行判断,看他需不需要织入切面逻辑,如果需要,那我就根据这个对象,生成一个代理对象,然后返回这个代理对象,那么最终注入容器的,自然就是代理对象了。
这个服务于Spring AOP的BBP,叫做AnnotationAwareAspectJAutoProxyCreator.
讲到Aop,ioc
IOC
所谓的控制反转。通俗地讲,就是把原本需要程序员自己创建和维护的一大堆bean统统交由Spring管理。
也就是说,spring将我们从盘根错节的依赖关系中解放路,当前对象如果需要依赖另一个对象,只要打上一个@autowired注解,spring帮你安装
AOP
面向切面编程,比如日志,事务,一个交叉业务就是要切入系统的一个方面,交叉业务的编程问题即为面向切面编程。aop的目标就是使交叉业务模块化,做法就是将切面转移到方式方法的周围。
原先不用aop的时候,交叉业务的代码直接硬编码在方法内部的前后,而AOP则是把交叉业务写在方法调用前后
。呢么为什么AOP不把代码也写在方法内部前后呢?两点原因
一、首先,这与AOP的底层实现方式有关:动态代理其实就是代理对象调用目标对象的同名方法,并在调用前后增强代码。
二、其次,这两种最终运行效果是一样的,所以没什么好纠结的。
模块化,就是将切面做成一个可以管理的状态,比如打印日志,不再是直接硬编码在方法中的零散语句,而是做成一个切面,通过通知的方式去执行切面代码。
后置处理器分为很多种,属于Spring的扩展点
Spring事务基于Spring AOP,Spring AOP底层用的动态代理,动态代理有两种方式:
基于接口代理(JDK代理)
基于接口代理,凡是类的方法非public修饰,或者用了static关键字修饰,那这些方法都不能被Spring AOP增强
基于CGLib代理(子类代理)
基于子类代理,凡是类的方法使用了private、static、final修饰,那这些方法都不能被Spring AOP增强
接口不可以实现方法,只可以定义方法,所以不能使用静态方法(因为静态方法必须实现)。
如果使用private,static无法覆盖子类所以不可以使用aop代理
借鉴知乎多篇高赞文章