spring boot启动扩展点整理及实测
-
ApplicationContextInitializer接口initialize方法
package com.gitee.small.extend; import lombok.extern.slf4j.Slf4j; import org.springframework.context.ApplicationContextInitializer; import org.springframework.context.ConfigurableApplicationContext; @Slf4j public class ApplicationContextInitializerExtend implements ApplicationContextInitializer<ConfigurableApplicationContext> { @Override public void initialize(ConfigurableApplicationContext configurableApplicationContext) { log.info("spring启动扩展点1: 容器初始化之前,可用于激活配置文件或动态注入字节码"); } }
由于这个扩展点在spring初始化之前,所以想要扩展类生效需要额外的配置:
-
启动类手动加入
package com.gitee.small; import com.gitee.small.extend.ApplicationContextInitializerExtend; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.BeansException; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.ApplicationArguments; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.context.ApplicationContext; import org.springframework.context.ApplicationContextAware; import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity; import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; import springfox.documentation.oas.annotations.EnableOpenApi; @Slf4j @SpringBootApplication @EnableWebSecurity @EnableOpenApi @EnableGlobalMethodSecurity(prePostEnabled = true) public class Application { public static void main(String[] args) { SpringApplication springApplication = new SpringApplication(Application.class); springApplication.addInitializers(new ApplicationContextInitializerExtend()); springApplication.run(args); } }
-
配置文件配置context.initializer.classes=com.gitee.small.extend.ApplicationContextInitializerExtend
-
在resources目录新建/META-INFI/spring.factories文件,并预置以下内容,即可完成自定义ApplicationContextInitializerExtend的注册
org.springframework.context.ApplicationContextInitializer=com.gitee.small.extend.ApplicationContextInitializerExtend
-
-
BeanDefinitionRegistryPostProcessor接口,可以动态注册自己的beanDefinition
package com.gitee.small.extend; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.BeansException; import org.springframework.beans.factory.config.ConfigurableListableBeanFactory; import org.springframework.beans.factory.support.BeanDefinitionRegistry; import org.springframework.beans.factory.support.BeanDefinitionRegistryPostProcessor; import org.springframework.stereotype.Component; @Slf4j @Component public class BeanDefinitionRegistryPostProcessorExtend implements BeanDefinitionRegistryPostProcessor { @Override public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry beanDefinitionRegistry) throws BeansException { log.info("spring启动扩展点2: spring bean实例化之前,已读取所有的beanDefinition信息"); } @Override public void postProcessBeanFactory(ConfigurableListableBeanFactory configurableListableBeanFactory) throws BeansException { log.info("spring启动扩展点3: 在postProcessBeanDefinitionRegistry之后执行"); } }
-
BeanFactoryPostProcessor接口,可以修改已注册的beanDefinition信息
package com.gitee.small.extend; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.BeansException; import org.springframework.beans.factory.config.BeanFactoryPostProcessor; import org.springframework.beans.factory.config.ConfigurableListableBeanFactory; import org.springframework.stereotype.Component; @Slf4j @Component public class BeanFactoryPostProcessorExtend implements BeanFactoryPostProcessor { @Override public void postProcessBeanFactory(ConfigurableListableBeanFactory configurableListableBeanFactory) throws BeansException { log.info("spring启动扩展点4: spring在读取beanDefinition信息之后, 实例化bean之前, beanDefinition注册之后"); } }
-
InstantiationAwareBeanPostProcessor接口,这个扩展点非常有用 ,无论是写中间件和业务中,都能利用这个特性。比如对实现了某一类接口的bean在各个生命期间进行收集,或者对某个类型的bean进行统一的设值等等
package com.gitee.small.extend; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.BeansException; import org.springframework.beans.PropertyValues; import org.springframework.beans.factory.config.InstantiationAwareBeanPostProcessor; import org.springframework.stereotype.Component; import java.beans.PropertyDescriptor; @Slf4j @Component public class InstantiationAwareBeanPostProcessorExtend implements InstantiationAwareBeanPostProcessor { @Override public Object postProcessBeforeInstantiation(Class<?> beanClass, String beanName) throws BeansException { if (beanName.equals("userService")) log.info("spring启动扩展点5: 实例化bean之前, 相当于new这个bean之前"); return null; } @Override public boolean postProcessAfterInstantiation(Object bean, String beanName) throws BeansException { if (beanName.equals("userService")) log.info("spring启动扩展点6: 实例化bean之后,相当于new这个bean之后"); return true; } @Override public PropertyValues postProcessPropertyValues(PropertyValues pvs, PropertyDescriptor[] pds, Object bean, String beanName) throws BeansException { if (beanName.equals("userService")) log.info("spring启动扩展点7: bean已经实例化完成,在属性注入时阶段触发,@Autowired,@Resource等注解原理基于此方法实现"); return pvs; } @Override public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException { if (beanName.equals("userService")) log.info("spring启动扩展点8: 初始化bean之前,相当于把bean注入spring上下文之前"); return bean; } @Override public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException { if (beanName.equals("userService")) log.info("spring启动扩展点9: 初始化bean之后,相当于把bean注入spring上下文之后"); return bean; } }
-
SmartInstantiationAwareBeanPostProcessor接口
package com.gitee.small.extend; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.BeansException; import org.springframework.beans.factory.config.SmartInstantiationAwareBeanPostProcessor; import org.springframework.stereotype.Component; import java.lang.reflect.Constructor; @Slf4j @Component public class SmartInstantiationAwareBeanPostProcessorExtend implements SmartInstantiationAwareBeanPostProcessor { @Override public Class<?> predictBeanType(Class<?> beanClass, String beanName) throws BeansException { if (beanName.equals("userService")) log.info("spring启动扩展点10: 该触发点发生在postProcessBeforeInstantiation之前,预测bean类型"); return null; } @Override public Constructor<?>[] determineCandidateConstructors(Class<?> beanClass, String beanName) throws BeansException { if (beanName.equals("userService")) log.info("spring启动扩展点11: 该触发点发生在postProcessBeforeInstantiation之后,用于确定该bean的构造函数之用,返回的是该bean的所有构造函数列表"); return null; } @Override public Object getEarlyBeanReference(Object bean, String beanName) throws BeansException { if (beanName.equals("userService")) log.info("spring启动扩展点12: 该触发点发生在postProcessAfterInstantiation之后,当有循环依赖的场景时,ean实例化好之后,为了防止有循环依赖," + "会提前暴露回调方法,用于bean实例化的后置处理。这个方法就是在提前暴露的回调方法中触发"); return bean; } }
-
BeanFactoryAware接口
package com.gitee.small.extend; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.BeansException; import org.springframework.beans.factory.BeanFactory; import org.springframework.beans.factory.BeanFactoryAware; import org.springframework.stereotype.Component; @Slf4j @Component public class BeanFactoryAwareExtend implements BeanFactoryAware { @Override public void setBeanFactory(BeanFactory beanFactory) throws BeansException { log.info("spring启动扩展点13: 发生在bean的实例化之后,注入属性之前,也就是Setter之前," + "可以在bean实例化之后,但还未初始化之前,拿到 BeanFactory,在这个时候,可以对每个bean作特殊化的定制"); } }
-
ApplicationContextAwareProcessor类
这个类本身没有提供扩展点,但是其内部方法涉及到了六个扩展点,源码如下class ApplicationContextAwareProcessor implements BeanPostProcessor { private final ConfigurableApplicationContext applicationContext; private final StringValueResolver embeddedValueResolver; /** * Create a new ApplicationContextAwareProcessor for the given context. */ public ApplicationContextAwareProcessor(ConfigurableApplicationContext applicationContext) { this.applicationContext = applicationContext; this.embeddedValueResolver = new EmbeddedValueResolver(applicationContext.getBeanFactory()); } @Override @Nullable public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException { if (!(bean instanceof EnvironmentAware || bean instanceof EmbeddedValueResolverAware || bean instanceof ResourceLoaderAware || bean instanceof ApplicationEventPublisherAware || bean instanceof MessageSourceAware || bean instanceof ApplicationContextAware)){ return bean; } AccessControlContext acc = null; if (System.getSecurityManager() != null) { acc = this.applicationContext.getBeanFactory().getAccessControlContext(); } if (acc != null) { AccessController.doPrivileged((PrivilegedAction<Object>) () -> { invokeAwareInterfaces(bean); return null; }, acc); } else { invokeAwareInterfaces(bean); } return bean; } private void invokeAwareInterfaces(Object bean) { if (bean instanceof EnvironmentAware) { ((EnvironmentAware) bean).setEnvironment(this.applicationContext.getEnvironment()); } if (bean instanceof EmbeddedValueResolverAware) { ((EmbeddedValueResolverAware) bean).setEmbeddedValueResolver(this.embeddedValueResolver); } if (bean instanceof ResourceLoaderAware) { ((ResourceLoaderAware) bean).setResourceLoader(this.applicationContext); } if (bean instanceof ApplicationEventPublisherAware) { ((ApplicationEventPublisherAware) bean).setApplicationEventPublisher(this.applicationContext); } if (bean instanceof MessageSourceAware) { ((MessageSourceAware) bean).setMessageSource(this.applicationContext); } if (bean instanceof ApplicationContextAware) { ((ApplicationContextAware) bean).setApplicationContext(this.applicationContext); } } }
- EnvironmentAware:用于获取EnviromentAware的一个扩展类,这个变量非常有用, 可以获得系统内的所有参数。当然个人认为这个Aware没必要去扩展,因为spring内部都可以通过注入的方式来直接获得
- EmbeddedValueResolverAware:用于获取StringValueResolver的一个扩展类, StringValueResolver用于获取基于String类型的properties的变量,一般我们都用@Value的方式去获取,如果实现了这个Aware接口,把StringValueResolver缓存起来,通过这个类去获取String类型的变量,效果是一样的
- ResourceLoaderAware:用于获取ResourceLoader的一个扩展类,ResourceLoader可以用于获取classpath内所有的资源对象,可以扩展此类来拿到ResourceLoader对象
- ApplicationEventPublisherAware:用于获取ApplicationEventPublisher的一个扩展类,ApplicationEventPublisher可以用来发布事件,结合ApplicationListener来共同使用,下文在介绍ApplicationListener时会详细提到。这个对象也可以通过spring注入的方式来获得
- MessageSourceAware:用于获取MessageSource的一个扩展类,MessageSource主要用来做国际化
- ApplicationContextAware:用来获取ApplicationContext的一个扩展类,ApplicationContext应该是很多人非常熟悉的一个类了,就是spring上下文管理器,可以手动的获取任何在spring上下文注册的bean,我们经常扩展这个接口来缓存spring上下文,包装成静态方法。同时ApplicationContext也实现了BeanFactory,MessageSource,ApplicationEventPublisher等接口,也可以用来做相关接口的事情
-
BeanNameAware接口
package com.gitee.small.extend; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.BeanNameAware; import org.springframework.stereotype.Component; @Slf4j @Component public class BeanNameAwareExtend implements BeanNameAware { @Override public void setBeanName(String s) { log.info("spring启动扩展点14: 触发点在bean的初始化之前,也就是postProcessBeforeInitialization之前," + "用户可以扩展这个点,在初始化bean之前拿到spring容器中注册的的beanName,来自行修改这个beanName的值"); } }
-
@PostConstruct注解
在bean的初始化阶段,如果对一个方法标注了@PostConstruct
,会先调用这个方法,这个触发点是在postProcessBeforeInitialization之后,InitializingBean.afterPropertiesSet之前 -
SmartInitializingSingleton接口
package com.gitee.small.extend; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.SmartInitializingSingleton; import org.springframework.stereotype.Component; @Slf4j @Component public class SmartInitializingSingletonExtend implements SmartInitializingSingleton { @Override public void afterSingletonsInstantiated() { log.info("spring启动扩展点16: 在spring容器管理的所有单例对象(非懒加载对象)初始化完成之后调用的回调接口。其触发时机为postProcessAfterInitialization之后"); } }
-
CommandLineRunner接口
package com.gitee.small.extend; import lombok.extern.slf4j.Slf4j; import org.springframework.boot.CommandLineRunner; import org.springframework.stereotype.Component; @Slf4j @Component public class CommandLineRunnerExtend implements CommandLineRunner { @Override public void run(String... args) throws Exception { log.info("spring启动扩展点17: 触发时机为整个项目启动完毕后,自动执行。如果有多个CommandLineRunner,可以利用@Order来进行排序"); } }
-
DisposableBean接口
package com.gitee.small.extend; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.DisposableBean; import org.springframework.stereotype.Component; @Slf4j @Component public class DisposableBeanExtend implements DisposableBean { @Override public void destroy() throws Exception { log.info("spring启动扩展点18: 触发时机为当此对象销毁时,会自动执行这个方法"); } }
代码地址:https://gitee.com/smallJ_L/small
参考文章:https://www.jianshu.com/p/7fec7b087774