spring 生命周期 扩展点

按标题顺序会一个一个执行

1,ApplicationContextInitializer

接口
实现方法:

  • initialize(ConfigurableApplicationContext applicationContext)

调用时机:配置初始化完成

  • 用于在spring容器刷新之前初始化Spring ConfigurableApplicationContext的回调接口。(就是在容器刷新之前调用该类的 initialize 方法。并将 ConfigurableApplicationContext 类的实例传递给该方法)
  • 通常用于需要对应用程序上下文进行编程初始化的web应用程序中。例如,根据上下文环境注册属性源或激活配置文件等。
  • 可排序的(实现Ordered接口,或者添加@Order注解)

因为是在ApplicationContext之前调用的,所以无法由它管理初始化,只能通过配置。有3种方法

1.1,配置文件

context.initializer.classes=com.example.demo.test.TestSpringInitProcess

1.2,Main方法里

    public static void main(String[] args) {
        SpringApplication sa = new SpringApplication(MainApplication.class);
        sa.addInitializers(new TestSpringInitProcess());
        sa.run(args);

    }

1.3,SpringBoot的SPI扩展---META-INF/spring.factories中配置

org.springframework.context.ApplicationContextInitializer=com.example.demo.test.TestSpringInitProcess

只会执行一次

用例:spring boot Appollo加载时间点

2,@Import

注解
会在ApplicationContextInitializer 后执行,无论写在哪里
@Import(xx.class)
xx.class 要实现ImportBeanDefinitionRegistrar 接口
用法
参考@Import,ImportSelector,ImportBeanDefinitionRegistrar的详解

@SpringBootApplication
@Import(EnableConfigImport.class)
public class MainApplication {

    public MainApplication () {
        System.out.println("MainApplication -> constructor");
    }

    public static void main(String[] args) {
        SpringApplication sa = new SpringApplication(MainApplication.class);
        sa.addInitializers(new TestApplicationContextInitializer());
        sa.run(args);

    }
}

public class EnableConfigImport implements ImportBeanDefinitionRegistrar {
    @Override
    public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
        System.out.println("EnableConfigImport -> registerBeanDefinitions");
        // 这里可以做一些Bean的注册,这里注册的Bean,要早于其他 @Component 标注的类的注册
    }
}

public class TestApplicationContextInitializer implements ApplicationContextInitializer{

    public TestApplicationContextInitializer() {
        System.out.println("TestApplicationContextInitializer -> constructor");
    }

    @Override
    public void initialize(ConfigurableApplicationContext applicationContext) {
        System.out.println("TestApplicationContextInitializer -> initialize");
    }

}


TestApplicationContextInitializer -> constructor
TestApplicationContextInitializer -> initialize
2021-03-05 15:17:03.309  INFO 14844 --- [           main] com.example.demo.MainApplication         : Starting MainApplication on 
2021-03-05 15:17:03.314  INFO 14844 --- [           main] com.example.demo.MainApplication         : No active profile set, falling back to default profiles: default
EnableConfigImport -> registerBeanDefinitions

ImportBeanDefinitionRegistrar

用例:spring boot Appollo加载时间点

3,BeanDefinitionRegistryPostProcessor

接口 继承自 BeanFactoryPostProcessor
实现方法:

  • postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException
  • postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException

调用时机:这个接口在读取项目中的beanDefinition之后执行,提供一个补充的扩展点
使用场景:你可以在这里动态注册自己的beanDefinition,可以加载classpath之外的bean

实现这个接口的类必须是被Spring容器管理的类,也就是类上要有@Component等注解
两个函数会在构造函数执行完之后执行

只会执行一次

4,BeanFactoryPostProcessor

接口
实现方法:

  • postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException

调用时机:这个接口在读取项目中的beanDefinition之后执行,提供一个补充的扩展点
使用场景:你可以在这里动态注册自己的beanDefinition,可以加载classpath之外的bean

用例:spring boot Appollo加载时间点

5,InstantiationAwareBeanPostProcessor

接口 继承自 BeanPostProcessor
实现方法:

  • postProcessBeforeInstantiation:实例化bean(构造函数)之前,相当于new这个bean之前
    可以自定义实例化逻辑,如返回一个代理对象等,(如果此处返回的Bean不为null将中断后续Spring创建Bean的流程,且只执行postProcessAfterInitialization回调方法,如当AbstractAutoProxyCreator的实现者注册了TargetSourceCreator(创建自定义的TargetSource)将改变执行流程,不注册TargetSourceCreator我们默认使用的是SingletonTargetSource(即AOP代理直接保证目标对象),此处我们还可以使用如ThreadLocalTargetSource(线程绑定的Bean)、CommonsPoolTargetSource(实例池的Bean)等等,大家可以去spring官方文档了解TargetSource详情;
  • postProcessAfterInstantiation:实例化bean(构造函数)之后,相当于new这个bean之后
  • postProcessPropertyValues(5.1之前):
    postProcessProperties(5.1开始用这个)
    bean已经实例化完成,在属性注入时阶段触发,@Autowired,@Resource等注解原理基于此方法实现
  • postProcessBeforeInitialization:初始化bean之前,相当于把bean注入spring上下文之前
  • postProcessAfterInitialization:初始化bean之后,相当于把bean注入spring上下文之后

使用场景:这个扩展点非常有用 ,无论是写中间件和业务中,都能利用这个特性。比如对实现了某一类接口的bean在各个生命期间进行收集,或者对某个类型的bean进行统一的设值等等。

请注意

  1. 这个接口的方法是对于所有被Spring管理的Bean而言的
    如果:你有3个类 Bean1,Bean2,Bean3。
    每初始化一个bean,上面的5个方法都会走一遍
  2. 如果 Bean 实现了BeanDefinitionRegistryPostProcessor 或 BeanFactoryPostProcessor
    上面5个方法将不会接收到它

6,SmartInstantiationAwareBeanPostProcessor

接口 继承自 InstantiationAwareBeanPostProcessor
实现方法:

  • predictBeanType:该触发点发生在postProcessBeforeInstantiation之前,这个方法用于预测Bean的类型,返回第一个预测成功的Class类型,如果不能预测返回null;当你调用BeanFactory.getType(name)时当通过bean的名字无法得到bean类型信息时就调用该回调方法来决定类型信息。
    通过@Autowared注入的时候 会校验类型,也会调用这个方法
    通过@Reaource注入就不会调用这个方法
  • determineCandidateConstructors:该触发点发生在postProcessBeforeInstantiation之后,构造函数调用之前,用于确定该bean的构造函数之用,返回的是该bean的所有构造函数列表。用户可以扩展这个点,来自定义选择相应的构造器来实例化这个bean。
  • getEarlyBeanReference:该触发点发生在postProcessAfterInstantiation之后,当有循环依赖的场景,当bean实例化好之后,为了防止有循环依赖,会提前暴露回调方法,用于bean实例化的后置处理。这个方法就是在提前暴露的回调方法中触发。

使用场景:看类名Smart就知道,可以让你智能的处理,BeanType,Constructor, 循环依赖

7,BeanFactoryAware

接口 继承自 Aware
实现方法:

  • setBeanFactory:获得BeanFactory,发生在bean的实例化之后,注入属性之后,postProcessBeforeInitialization 之前

8,ApplicationContextAwareProcessor

实现了 BeanPostProcessor
在Bean初始化之前注入一些属性
同时这个类 印证了 postProcessBeforeInitialization 的用法

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);
        }
    }

}

9,BeanNameAware

接口 继承自 Aware
***这个在 BeanFactory 之前注入 (spring5.2.1)
实现方法:

  • setBeanName

10,@PostConstruct

这个触发点是在postProcessBeforeInitialization之后,InitializingBean.afterPropertiesSet之前。

11,InitializingBean

实现方法:

  • afterPropertiesSet
    这个触发点是在postProcessAfterInitialization之前。

当然还有一个 init-method (不常用)
执行顺序是
postProcessBeforeInitialization
@PostConstruct > InitializingBean > init-method
postProcessAfterInitialization

12,FactoryBean

接口
实现方法:

  • getObject: 返回一个对象的实例
  • getObjectType:返回FactoryBean管理的对象类型

故名思意:这个接口就是用来管理Bean 的生产的
但其实也可以通过@Bean来完成同样的功能
不过实现FactoryBean语义上更明确一些例如 DataSourceFactoryBean,一看就知道跟数据库相关

用例:spring boot mybatis 加载过程
spring boot Open Feign 客户端加载过程

13,SmartInitializingSingleton

接口
实现方法:

  • afterSingletonsInstantiated:

在所有单例Bean加载完后调用, 因此你可以通过ApplicationContext 来拿到所有加载好的Bean,并对他们进行一些处理

只会执行一次

用例:xxl job 配置 和 加载时间点

14,ApplicationRunner

接口
实现方法:

  • run: 接受ApplicationArguments 参数

在项目启动完之后执行
只会执行一次

15,CommandLineRunner

接口
实现方法:

  • run: 接受命令行参数

在项目启动完之后执行
只会执行一次

16,@PreDestroy

Bean被销毁之前执行

17,DisposableBean

接口
实现方法:

  • destroy
    Bean被销毁之前执行

执行顺序
@PreDestroy > DisposableBean.destroy > destory-method

18,ApplicationListener

ApplicationListener 说明
Spring执行ApplicationEvent事件顺序ServletWebServerInitializedEvent

用例:spring boot consul 客户端加载过程

19,@Configuration,@Bean

这两个注解并不是 spring生命周期里的
但,确实我们经常用到的

  • @Configuration:它本身就是一个@Component能被spring容器扫描到,所以它的加载时机与普通Bean是一样的,不同的是它内部被标注为@Bean的方法
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Component
public @interface Configuration

作用参照:https://www.jianshu.com/p/721c76c1529c

简单说下:
ConfigurationClassPostProcessor 实现了接口 BeanDefinitionRegistryPostProcessor
这里也验证了BeanDefinitionRegistryPostProcessor的作用

public class ConfigurationClassPostProcessor implements BeanDefinitionRegistryPostProcessor,
        PriorityOrdered, ResourceLoaderAware, BeanClassLoaderAware, EnvironmentAware {
      @Override
    public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) {
                。。。
        enhanceConfigurationClasses(beanFactory); // 这句
        。。。
    }

      public void enhanceConfigurationClasses(ConfigurableListableBeanFactory beanFactory) {
        Map<String, AbstractBeanDefinition> configBeanDefs = new LinkedHashMap<>();
        。。。
        ConfigurationClassEnhancer enhancer = new ConfigurationClassEnhancer();
        for (Map.Entry<String, AbstractBeanDefinition> entry : configBeanDefs.entrySet()) {
            AbstractBeanDefinition beanDef = entry.getValue();
            // If a @Configuration class gets proxied, always proxy the target class
            beanDef.setAttribute(AutoProxyUtils.PRESERVE_TARGET_CLASS_ATTRIBUTE, Boolean.TRUE);
            // Set enhanced subclass of the user-specified bean class
            Class<?> configClass = beanDef.getBeanClass();
// 生成代理类
            Class<?> enhancedClass = enhancer.enhance(configClass, this.beanClassLoader); 
            if (configClass != enhancedClass) {
                if (logger.isTraceEnabled()) {
                    logger.trace(String.format("Replacing bean definition '%s' existing class '%s' with " +
                            "enhanced class '%s'", entry.getKey(), configClass.getName(), enhancedClass.getName()));
                }
// 用代理类 替换掉原来的类
                beanDef.setBeanClass(enhancedClass);
            }
        }
    }
}

@Bean
作用与@Component一样,用它标记的方法会被当成一个工场方法,来生产Bean


在工厂方法里你可以调用非空构造函数,来创建一个复杂类的Bean
你还可以为同一个类创建多个对象
最常见的就是多数据源配置

    @Primary
    @Bean(name = "ds1")
    @ConfigurationProperties(prefix = "spring.datasource1")
    public DruidDataSource getDataSource1() {
        return DataSourceBuilder.create().type(DruidDataSource.class).build();
    }

    @Bean(name = "ds2")
    @ConfigurationProperties(prefix = "spring.datasource2")
    public DruidDataSource getDataSource2() {
        return DataSourceBuilder.create().type(DruidDataSource.class).build();
    }

Bean工厂方法如何被代理的
参考

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 194,761评论 5 460
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 81,953评论 2 371
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 141,998评论 0 320
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 52,248评论 1 263
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 61,130评论 4 356
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 46,145评论 1 272
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 36,550评论 3 381
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 35,236评论 0 253
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 39,510评论 1 291
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 34,601评论 2 310
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 36,376评论 1 326
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,247评论 3 313
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 37,613评论 3 299
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 28,911评论 0 17
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,191评论 1 250
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 41,532评论 2 342
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 40,739评论 2 335

推荐阅读更多精彩内容