敞开成长之旅!这是我参与「日新计划 12 月更文挑战」的第4天
Spring Bean生命周期,从入门到进阶:
【Spring Bean生命周期】小白也能看懂的入门篇
【Spring Bean生命周期】高手如何解读源码(一) – 资源的扫描和注册
【Spring Bean生命周期】高手如何解读源码(二) – Bean的生命周期
Spring之所以具备如此强壮的生态,扩展能力强是很重要的一方面,这得益于它本身供给了十分多的扩展点。本节咱们针对Spring生命周期中涉及到的扩展点来看一看,究竟它们是何方神圣?
1 扩展点流程
Spring生命周期的主要扩展点如图:
2 扩展点解析及运用场景
ApplicationContextInitializer接口
用于在履行refresh()
之前初始化ConfigurationApplicationContext
的回调接口。
一般用于需求对运用程序上下文进行编程初始化的web运用程序中。例如,注册特点源或依据上下文环境激活装备文件。
比方:Spring boot就写了几个ApplicationContextInitializer的完结类,常见作用:
-
ConfigurationWarningsApplicationContextInitializer
:关于一般装备错误在日志中作出正告 -
ContextIdApplicationContextInitializer
:设置ApplicationContext#getId()所获取的ID值,默许取spring.application.name特点值,没有装备时默许为 application -
DelegatingApplicationContextInitializer
:运用环境特点context.initializer.classes指定的初始化器(initializers)进行初始化工作,假如没有指定则什么都不做
……
完结方式如下
public class CustomApplicationContextInitializer implements ApplicationContextInitializer {
@Override
public void initialize(ConfigurableApplicationContext applicationContext) {
// 打印当时办法类名和办法名,调试用
CountUtil.println(getClass(),Thread.currentThread().getStackTrace()[2].getMethodName());
}
}
在装备文件中设置:
context.initializer.classes=com.zyq.demo.expand.CustomApplicationContextInitializer
BeanDefinitionRegistryPostProcessor
这个接口在读取项目中的beanDefinition
之后履行,供给一个补充的扩展点。运用场景:能够在这儿动态注册自己的beanDefinition
,能够加载classpath之外的bean。
比方咱们在常常运用的mybatis的mapper和咱们的feignClient,往往只需求界说接口而不需求写完结,那么这些完结的生成及注入便是在此处完结的。
@Component
public class CustomBeanDefinitionRegistryPostProcessor implements BeanDefinitionRegistryPostProcessor {
@Override
public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException {
// 能够在此处自界说扫描mapper,并生成BeanDefinition信息,经过registry注册到容器中
RootBeanDefinition rootBeanDefinition = new RootBeanDefinition(Test001.class);
registry.registerBeanDefinition("test001",rootBeanDefinition);
CountUtil.println(getClass(),Thread.currentThread().getStackTrace()[2].getMethodName());
}
@Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
CountUtil.println(getClass(),Thread.currentThread().getStackTrace()[2].getMethodName());
}
}
BeanFactoryPostProcessor
这个接口是beanFactory
的扩展接口,调用时机在spring在读取beanDefinition
信息之后,实例化bean之前。在这个时机,用户能够经过完结这个扩展接口来自行处理一些东西,比方修改现已注册的beanDefinition
的元信息。依据注释可知,此时只能承上(即修改元信息),坚决不允许启下(即触发实例化)
实践工作中,自界说BeanFactoryPostProcessor的情况确实少,比方对灵敏信息的解密处理,在数据库的衔接装备中,用户名和暗码都是明文装备的,这就存在走漏风险,还有redis的衔接装备、shiro的加密算法、rabbitmq的衔接装备等等,凡是涉及到灵敏信息的,都需求进行加密处理,信息安全十分重要。
装备的时分以密文装备,在真实用到之前在spring容器中进行解密,然后用解密后的信息进行真实的操作。
@Component
public class CustomBeanFactoryAware implements BeanFactoryPostProcessor {
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
MutablePropertySources propSources = environment.getPropertySources();
StreamSupport.stream(propSources.spliterator(), false)
.filter(ps -> ps instanceof OriginTrackedMapPropertySource)
.collect(Collectors.toList())
.forEach(ps -> convertPropertySource((PropertySource<LinkedHashMap>) ps));
LOGGER.info("灵敏信息解密完结.....");
}
}
InstantiationAwareBeanPostProcessor
InstantiationAwareBeanPostProcessor
接口承继BeanPostProcessor
接口,它内部供给了3个办法,再加上BeanPostProcessor
接口内部的2个办法,所以完结这个接口需求完结5个办法。能够称作是Spring生命周期的大宝剑!Spring的核心功能之一AOP便是根据它来完结的。
@Component
public class CustomInstantiationAwareBeanPostProcessor implements InstantiationAwareBeanPostProcessor {
//在实例化之前被调用
@Override
public Object postProcessBeforeInstantiation(Class<?> beanClass, String beanName) throws BeansException {
if(beanClass == TargetClass.class){
//运用cglib完结动态代理AOP
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(beanClass);
enhancer.setCallback(new TargetClassProxy());
TargetClass proxyClass = (TargetClass)enhancer.create();
return proxyClass;
}
return null;
}
}
BeanNameAware
这个类也是Aware扩展的一种,触发点在bean的初始化之前,也便是postProcessBeforeInitialization
之前,这个类的触发点办法只有一个:setBeanName
。运用场景:用户能够扩展这个点,在初始化bean之前拿到spring容器中注册的的beanName,来自行修改这个beanName的值。整个约定了在编写服务代码时,就能够知道客户端调用时的bean名称,可在此处打标签,用于一些特殊bean的捕获。
public class CustomBeanNameAware implements BeanNameAware {
@Override
public void setBeanName(String name) {
System.out.println("在这儿修改Bean名称");
}
}
@PostConstruct
@PostConstruct这个并不算一个扩展点,其实便是一个标示。从Java EE5规范开始,Servlet中添加的注解,被@PostConstruct修饰的办法会在服务器加载Servlet的时分运行,而且只会被服务器履行一次。其作用是在bean的初始化阶段,假如对一个办法标示了@PostConstruct
,会先调用这个办法。这儿重点是要重视下这个标准的触发点,这个触发点是在postProcessBeforeInitialization
之后,InitializingBean.afterPropertiesSet
之前。
运用场景:假如想在生成目标时分完结某些初始化操作,而偏偏这些初始化操作又依靠于依靠注入,那么就无法在构造函数中完结。为此,能够运用@PostConstruct注解一个办法来完结初始化,@PostConstruct注解的办法将会在依靠注入完结后被主动调用。
spring自带的@schedule,没有开关,项目发动总会发动一个线程;做项目的时分就运用Java的timer,这个设置开关即可自在的控制,封闭的时分,不会发动线程;Java的timer也需求找到一个发动类,能够放到main函数里边发动,这样的话,代码的耦合性太高了,而运用PostConstruct是很干净的。
@PostConstruct
public void init() {
// 发动守时任务线程
}
InitializingBean
InitializingBean的作用是Bean注入到Spring容器且初始化后,履行特定事务化的操作。Spring允许容器中的Bean,在Bean初始化完结后或者Bean毁掉前,履行特定事务化的操作,常用的完结方式有以下三种:
- 经过完结InitializingBean/DisposableBean接口来处理初始化后/毁掉前的操作;
- 经过标签的init-method/destroy-method特点处理初始化后/毁掉前的操作;
- 在指定办法上加上@PostConstruct或@PreDestroy注解来处理初始化后/毁掉前的操作。
在spring初始化bean的时分,假如该bean是完结了InitializingBean接口,而且一起在装备文件中指定了init-method,体系则是先调用afterPropertiesSet办法,然后在调用init-method中指定的办法
public class CustomInitializingBean implements InitializingBean {
@Override
public void afterPropertiesSet() throws Exception {
if (StringUtils.isEmpty(path)) {
log.error("PLEASE SET THE RULE'S PATH: (spring.drools.path = XXX).");
}
}
}
BeanPostProcessor
在Bean目标在实例化和依靠注入结束后,在显示调用初始化办法的前后添加咱们自己的逻辑。留意是Bean实例化结束后及依靠注入完结后触发的。
public interface BeanPostProcessor {
/**
* Apply this {@code BeanPostProcessor} to the given new bean instance <i>before</i> any bean
* initialization callbacks (like InitializingBean's {@code afterPropertiesSet}
* or a custom init-method). The bean will already be populated with property values.
* The returned bean instance may be a wrapper around the original.
*/
@Nullable
default Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
return bean;
}
/**
* Apply this {@code BeanPostProcessor} to the given new bean instance <i>after</i> any bean
* initialization callbacks (like InitializingBean's {@code afterPropertiesSet}
* or a custom init-method). The bean will already be populated with property values.
* The returned bean instance may be a wrapper around the original.
*/
@Nullable
default Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
return bean;
}
}
从代码能够看出来,回来的时经过处理后的Bean,这种就很适合代理类的场景,假如需求完结类似AOP的逻辑,能够在此处理。
好了,上面便是涉及到Spring生命周期的一些主要扩展点,主要是协助咱们集合Spring生命周期来理解Spring的整体逻辑。