Spring Bean的生命周期

阅读数:228 评论数:0

跳转到新版页面

分类

python/Java

正文

一、概述

1、spring容器

spring中有三种bean容器:BeanFactory、ApplicationContext、WebApplicationContext。

(1)BeanFactory

BeanFactory是Bean的工厂,主要负责初始化各种Bean,并调用它们的生命周期方法,实现此接口的常见类是XmlBeanFactory。

(2)ApplicationContext

是BeanFactory的子接口。

二、BeanFactory

1、Bean自身的方法

Bean本身调用的方法和通过配置文件中的init-method和destory-method指定的方法(通过xml配置或@Bean注解)。

Spring要求init-method是一个无参数的方法,init-method指定的方法可以是public、protected以及private的,并且方法也可以是final的。

init-method是通过反射执行的。

2、Bean级生命周期接口方法

BeanNameAware、BeanFactoryAware、InitializingBean和DisposableBean这些接口的方法。

(1)InitializingBean接口

afterPropertiesSet() 作用是:当一个Bean实现InitializingBean,#afterPropertiesSet方法里面可以添加自定义的初始化方法或者做一些资源初始化。
public class NormalBeanA implements InitializingBean{
    @Override
    public void afterPropertiesSet() throws Exception {
        System.out.println("[InitializingBean] NormalBeanA");
    }
}

(2)DisposableBean接口

destroy() 作用是:当一个单例Bean实现DisposableBean,#destroy可以添加自定义的一些销毁方法或者资源释放操作。
public class NormalBeanA implements DisposableBean {
    @Override
    public void destroy() throws Exception {
        System.out.println("[DisposableBean] NormalBeanA");
    }
}

(3)Aware接口

Aware接口是指以Aware结尾的一些Spring提供的接口,当你的Bean实现了这些接口的话,在创建过程中会回调对应的set方法,并传入相应的对象。

ApplicationContextAware 注入ApplicationContext
ApplicationEventPublisherAware 注入ApplicationEventPublisher事件发布器
BeanFactoryAware 注入BeanFactory
BeanNameAware 注入Bean的名称
public class TestBeanFactoryAware implements BeanFactoryAware {
    @Override
    public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
        System.out.println("[TestBeanFactoryAware] " + beanFactory.getBean(TestBeanFactoryAware.class).getClass().getSimpleName());
    }
}

public class NormalBeanA implements BeanNameAware{
    public NormalBeanA() {
        System.out.println("NormalBean constructor");
    }

    @Override
    public void setBeanName(String name) {
        System.out.println("[BeanNameAware] " + name);
    }
}

3、容器级生命周期接口方法

InstantiationAwareBeanPostProcessor和BeanPostProcessor这两个接口

(1)BeanPostProcessor接口

作用是为Bean的初始化前后提供可扩展的空间。

这里说的Bean的初始化是:

bean实现了InitializingBean接口,对应的方法为afterPropertiesSet
在bean定义的时候,通过init-method设置的方法。

实现BeanPostProcessor接口可以在Bean(实例化之后)初始化的前后做一些自定的操作,但是拿到的参数只有BeanDefinition实例和实例名称。

Spring内置的BeanPostProcessor

AutowiredAnnotationBeanPostProcessor 处理@Autowired、@Value注解
CommonAnnotationBeanPostProcessor 处理@Resource、@PostConstruct、@PreDestroy注解
AnnotationAwareAspectJAutoProxyCreator 处理一些注解或者是AOP切面的动态代理
ApplicationContextAwareProcessor 处理Aware接口注入的
AsyncAnnotationBeanPostProcessor 处理@Async注解
ScheduledAnnotationBeanPostProcessor 处理@Scheduled注解

(2)InstantiationAwareBeanPostProcessor

感知Bean实例实例化的处理器。

该接口继承了BeanPostProcess接口,该类主要的扩展点有以下5个方法

postProcessBeforeInstantiation 实例化bean之前
postProcessAfterInstantiation 实例化bean之后
postProcessPropertyValues bean已经实例化完成,在属性注入时阶段触发,@Autowired,@Resource等注解原理基于此方法实现
postProcessBeforeInitialization 把bean注入spring上下文之前
postProcessAfterInitialization 把bean注入spring上下文之后
public class TestInstantiationAwareBeanPostProcessor implements InstantiationAwareBeanPostProcessor {

    @Override
    public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
        System.out.println("[TestInstantiationAwareBeanPostProcessor] before initialization " + beanName);
        return bean;
    }

    @Override
    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
        System.out.println("[TestInstantiationAwareBeanPostProcessor] after initialization " + beanName);
        return bean;
    }

    @Override
    public Object postProcessBeforeInstantiation(Class<?> beanClass, String beanName) throws BeansException {
        System.out.println("[TestInstantiationAwareBeanPostProcessor] before instantiation " + beanName);
        return null;
    }

    @Override
    public boolean postProcessAfterInstantiation(Object bean, String beanName) throws BeansException {
        System.out.println("[TestInstantiationAwareBeanPostProcessor] after instantiation " + beanName);
        return true;
    }

    @Override
    public PropertyValues postProcessPropertyValues(PropertyValues pvs, PropertyDescriptor[] pds, Object bean, String beanName) throws BeansException {
        System.out.println("[TestInstantiationAwareBeanPostProcessor] postProcessPropertyValues " + beanName);
        return pvs;
    }

三、ApplicationContext

我们通常使用ApplicationContext作为Spring容器。

1、ApplicationContextAware

相比于BeanFactory,在BeanFactoryAware的setBeanFactory与BeanPostProcessor之间多了一个ApplicationContextAware的setApplicationContext()方法的调用。

2、BeanFactoryPostProcessor

可以在Spring容器实例化Bean对象之前可以修改BeanDefinition

postProcessorBeanFactory()方法 在该方法中,可以通过BeanDefinitionRegistry来获取和悠Bean的配置元数据。

Spring IoC容器允许BeanFactoryPostProcessor在容器实际实例化任何其它的bean之前读取配置元数据,并有可能修改它。

如果你愿意,你可以配置多个BeanFactoryPostProcessor。你还能通过设置“order”属性来控制BeanFactoryPostProcessor的执行次序。

四、BeanDefinition

Spring容器启动的时候会定位配置文件,加载文件,并解析成BeanDefinition。

在实例化Bean之前在BeanDefinition里头已经有了所有需要实例化时用到的元数据,接下来Spring只需要选择合适的实例化方法以及策略即可。

实例化方法有两大类分别是工厂方法和构造方法实例化,后者更常见。其中Spring默认的实例方法就是无参构造函数实例化。

1、@import注解

spring的@import注解使用

2、注册Bean

五、springboot启动过程

1、确定应用程序类型

进入run方法(还没实例化SpringApplication对象,所以是静态方法run)后,会 new 一个SpringApplication 对象,创建这个对象的构造函数做了一些准备工作。

在SpringApplication的构造方法内,首先会通过 WebApplicationType.deduceFromClasspath(); 方法判断当前应用程序的容器,默认使用的是Servlet 容器,除了servlet之外,还有NONE 和 REACTIVE (响应式编程)

2、加载所有初始化器

这里加载的初始化器是springboot自带初始化器,从 META-INF/spring.factories 配置文件中加载(对应的是ApplicationContextInitializer接口),自带有2个,分别在源码的jar包的 spring-boot-autoconfigure 项目 和 spring-boot 项目里面各有一个。

3、加载所有的监听器

监听器加载的是实现了 ApplicationListener接口的类。

4、设置程序运行的主类

deduceMainApplicationClass(); 这个方法仅仅是找到main方法所在的类,为后面的扫包作准备,deduce是推断的意思。

5、开启计时器

public class SpringApplication {

    ...
        
    public ConfigurableApplicationContext run(String... args) {
        // 这是 Spring 的一个计时器,计算代码的执行时间(ms级别)
        StopWatch stopWatch = new StopWatch();
        stopWatch.start();
        
        // 这俩变量在后面赋值处进行说明
        ConfigurableApplicationContext context = null;
        Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList<>();
        
        // 用来设置java.awt.headless属性值
        configureHeadlessProperty();
        
        // 该对象属于组合模式的实现,核心是内部关联的 SpringApplicationRunListener 集合,SpringApplicationRunListener 是 Spring Boot 的运行时监听器
        SpringApplicationRunListeners listeners = getRunListeners(args);
        // 会在不同的阶段调用对应的方法,这里表示启动run方法被调用
        listeners.starting();
        
        try {
        
            // 用来获取 SpringApplication.run(args)传入的参数
            ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
            
            // 获取 properties 配置文件
            ConfigurableEnvironment environment = prepareEnvironment(listeners, applicationArguments);
            
            // 设置 spring.beaninfo.ignore 的属性值,判断是否跳过搜索BeanInfo类
            configureIgnoreBeanInfo(environment);
            
            // 这里是项目启动时,控制台打印的 Banner
            Banner printedBanner = printBanner(environment);
            
            // 这里就是创建 Spring 应用上下文
            context = createApplicationContext();
            
            // 获取 spring.factories 中key为 SpringBootExceptionReporter 的类名集合
            exceptionReporters = getSpringFactoriesInstances(SpringBootExceptionReporter.class,
                    new Class[] { ConfigurableApplicationContext.class }, context);
                    
            // 这里是准备 Spring 应用上下文
            prepareContext(context, environment, listeners, applicationArguments, printedBanner);
            
            // 这里是启动 Spring 应用上下文,底层调用的是 ApplicationContext 的 refresh() 方法,到这里就正式进入了 Spring 的生命周期,同时,SpringBoot的自动装配特性也随之启动
            refreshContext(context);
            
            // 里面是空的,猜测应该是交由开发人员自行扩展
            afterRefresh(context, applicationArguments);
            stopWatch.stop();
            
            // 这里打印启动信息
            if (this.logStartupInfo) {
                new StartupInfoLogger(this.mainApplicationClass).logStarted(getApplicationLog(), stopWatch);
            }
            
            // ApplicationContext 启动时,调用该方法
            listeners.started(context);
            
            // 项目启动后,做的一些操作,开发人员可自行扩展
            callRunners(context, applicationArguments);
        }
        catch (Throwable ex) {
            handleRunFailure(context, ex, exceptionReporters, listeners);
            throw new IllegalStateException(ex);
        }
    
        try {
        
            // ApplicationContext 启动完成时,调用该方法
            listeners.running(context);
        }
        catch (Throwable ex) {
            handleRunFailure(context, ex, exceptionReporters, null);
            throw new IllegalStateException(ex);
        }
        return context;
    }
    
    ...
}

现在调用的run方法是非静态的,是需要实例化后才可以调用的。

// 实例化计时器
StopWatch stopWatch = new StopWatch(); 
// 开始计时
stopWatch.start();

这个定时器主要用于计算springboot启动花了多长时间。

6、java.awt.headless设置为true

这里将java.awt.headless设置为true,表示运行在服务器端,在没有显示器器和鼠标键盘的模式下照样可以工作,模拟输入输出设备功能。

7、获取并启用监听器

(1)创建所有 Spring 运行监听器并发布应用启动事件

(2)启动监听器

8、设置应用程序参数

将执行run方法时传入的参数封装成一个对象

9、准备环境变量

包含系统属性和用户配置的属性

10、忽略bean信息

11、打印banner信息

如果想要改成自己喜欢的图标该怎么办呢,其实很简单,只需要在resources目录下添加一个 banner.txt 的文件即可。

12、创建应用程序的上下文

创建应用上下文的同时也会创建一个DefaultListableBeanFactory,这个也是静态代理的体现。而在执行ApplicationContext的构造函数时,会创建一个AnnotatedBeanDefinitionReader,而这个类中会有一个BeanDefinitionRegistry属性,并将我们一些必须的BeanDefinition添加进去,例如最为核心的  ConfigurationClassPostProcessor也就是在这个时候被添加进去的。

13、实例化异常报告器

异常报告器是用来捕捉全局异常使用的,当springboot应用程序在发生异常时,异常报告器会将其捕捉并做相应处理,在spring.factories 文件里配置了默认的异常报告器。

需要注意的是,这个异常报告器只会捕获启动过程抛出的异常,如果是在启动完成后,在用户请求时报错,异常报告器不会捕获请求中出现的异常。

14、准备上下文环境

(1)实例化单例的beanName生成器

(2)执行初始化方法

(3)将启动参数注册到容器

15、刷新上下文

Spring创建Bean就在这一步完成

16、刷新上下文后置处理

afterRefresh 方法是启动后的一些处理,留给用户扩展使用,目前这个方法里面是空的。

17、结束计时器

到这一步,springboot其实就已经完成了,计时器会打印启动springboot的时长。

18、发布上下文准备就绪事件

19、执行自定义的run方法

(1)实现ApplicationRunner接口

(2)实现CommandLineRunner接口

参见:springboot自定义run方法

六、SpringBoot常用扩展点

1、可扩展的接口调用顺序图

2、可扩展接口分类图

 

3、应用程序生命周期扩展点

(1)SpringApplicationRunListener

public interface SpringApplicationRunListener {

    // 在run()方法开始执行时被调用,表示应用刚刚启动,对应的 Spring Boot 事件为 ApplicationStartingEvent
    void starting();

    // ConfigurableEnvironment 构建完成时调用,对应的 Spring Boot 事件为 ApplicationEnvironmentPreparedEvent
    void environmentPrepared(ConfigurableEnvironment environment);

    // ApplicationContext 构建完成时调用,对应的 Spring Boot 事件为 ApplicationContextInitializedEvent
    void contextPrepared(ConfigurableApplicationContext context);

    // ApplicationContext 完成加载但还未启动时调用,对应的 Spring Boot 事件为 ApplicationPreparedEvent
    void contextLoaded(ConfigurableApplicationContext context);

    // ApplicationContext 已启动,但 callRunners 还未执行时调用,对应的 Spring Boot 事件为 ApplicationStartedEvent
    void started(ConfigurableApplicationContext context);

    // ApplicationContext 启动完毕被调用,对应的 Spring Boot 事件为 ApplicationReadyEvent
    void running(ConfigurableApplicationContext context);

    // 应用出错时被调用,对应的 Spring Boot 事件为 ApplicationFailedEvent
    void failed(ConfigurableApplicationContext context, Throwable exception);

}

(2)事件监听

参见:springboot事件监听

(3)ApplicationContextInitializer

这是整个spring容器在刷新之前初始化ConfigurableApplicationContext的回调接口.用户可以在整个spring容器还没被初始化之前做一些事情。

public class TestApplicationContextInitializer implements ApplicationContextInitializer {
    @Override
    public void initialize(ConfigurableApplicationContext applicationContext) {
        System.out.println("[ApplicationContextInitializer]");
    }
}

因为这时候spring容器还没被初始化,所以想要自己的扩展的生效,有以下三种方式:

在启动类中用springApplication.addInitializers(new TestApplicationContextInitializer())语句加入
配置文件配置context.initializer.classes=com.example.demo.TestApplicationContextInitializer
Spring SPI扩展,在spring.factories中加入
org.springframework.context.ApplicationContextInitializer=com.example.demo.TestApplicationContextInitializer

4、容器扩展点

(1)BeanDefinitionRegistryPostProcessor

可以在Spring容器实例化Bean对象之前修改Bean的配置元数据,并向容器中添加新的Bean定义。

当容器读取完 Bean 定义信息后,就会调用 BeanDefinitionRegistryPostProcessor 接口的实现类的 postProcessBeanDefinitionRegistry() 方法。在该方法中,开发者可以通过 BeanDefinitionRegistry 接口来获取和修改 Bean 的配置元数据,并向容器中添加新的 Bean 定义。

public class CustomBeanDefinitionRegistryPostProcessor implements BeanDefinitionRegistryPostProcessor {

    @Override
    public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException {
        BeanDefinition beanDefinition = BeanDefinitionBuilder.genericBeanDefinition(CustomBean.class)
                .addPropertyValue("name", "Custom Bean")
                .getBeanDefinition();
        registry.registerBeanDefinition("customBean", beanDefinition);
    }

    @Override
    public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {

    }
}

(2)SmartInstantiationAwareBeanPostProcessor

该扩展接口有3个触发点方法:

predictBeanType 这个方法用于预测Bean的类型,返回第一个预测成功的Class类型,如果不能预测返回null;
determineCandidateConstructors 用于确定该bean的构造函数之用,返回的是该bean的所有构造函数列表。用户可以扩展这个点,来自定义选择相应的构造器来实例化这个bean。
getEarlyBeanReference 当有循环依赖的场景,当bean实例化好之后,为了防止有循环依赖,会提前暴露回调方法,用于bean实例化的后置处理。这个方法就是在提前暴露的回调方法中触发。
public class TestSmartInstantiationAwareBeanPostProcessor implements SmartInstantiationAwareBeanPostProcessor {

    @Override
    public Class<?> predictBeanType(Class<?> beanClass, String beanName) throws BeansException {
        System.out.println("[TestSmartInstantiationAwareBeanPostProcessor] predictBeanType " + beanName);
        return beanClass;
    }

    @Override
    public Constructor<?>[] determineCandidateConstructors(Class<?> beanClass, String beanName) throws BeansException {
        System.out.println("[TestSmartInstantiationAwareBeanPostProcessor] determineCandidateConstructors " + beanName);
        return null;
    }

    @Override
    public Object getEarlyBeanReference(Object bean, String beanName) throws BeansException {
        System.out.println("[TestSmartInstantiationAwareBeanPostProcessor] getEarlyBeanReference " + beanName);
        return bean;
    }
}

5、自动配置扩展点

(1)Spring SPI机制 SpringFactoriesLoader

Spring的SPI机制规定,配置文件必须在classpath路径下的META-INF文件夹内,文件名必须为spring.factories,文件内容为键值对,一个键可以有多个值,只需要用逗号分割就行,同时键值都需要是类的全限定名。

(2)SpringBoot的@EnableAutoConfiguration

@Import(AutoConfigurationImportSelector.class)
public @interface EnableAutoConfiguration {
    //忽略
}

在SpringBoot中,@EnableAutoConfiguration是通过@SpringBootApplication来使用的。

当项目启动的时候,会去从所有的spring.factories文件中读取@EnableAutoConfiguration键对应的值,拿到配置类,然后根据一些条件判断,决定哪些配置可以使用,哪些不能使用。

自动装配机制是SpringBoot的一个很重要的扩展点,很多框架在整合SpringBoot的时候,也都通过自动装配来的,实现项目启动,框架就自动启动的。

(3)Springboot的PropertySourceLoader(@PropertySource)

在SpringBoot环境下,外部化的配置文件支持properties和yaml两种格式。

YamlPropertySourceLoader:解析以yml或者yaml结尾的配置文件。

所以可以看出,要想实现json格式的支持,只需要自己实现可以用来解析json格式的配置文件的PropertySourceLoader就可以了。

SpringBoot支持多种类型的PropertySource扩展及支持@PropertySource注解。

RandomValuePropertySource 对 random.int, random.long, random.uuid 的支持
MapPropertySource 对 K,V 形式的配置支持
JsonPropertySource 对 Json 类型的配置支持
SystemEnvironmentPropertySource 对配置 key 中使用 .、下划线、中划线、大小写的支持
CommandLinePropertySource 对启动命令参数的支持
ServletContextPropertySource 对 ServletContext 的 initParam 的支持

 

6、Web扩展点

(1)WebMvcConfigurer 参见:spring内部的配置方式

(2)HandlerInterceptor 参见:SpringMVC HandlerMapping组件

(3)HandlerMethodArgumentResolver和HandlerMethodReturnValueHandler 参见:Spring HandlerAdapter组件

7、数据库扩展点

参见:springboot mybatis多数据源的两种整合方法

mybatis调用链路

springboot事务详解

8、安全扩展点

参见:spring security

9、基它扩展点

(1)CommandLineRunner和ApplicationRunner

参见:http://1024s.top/mbstudy/mbBlog/blogDetail?blogId=13674

(2)WebServerFactoryCustomizer

springboot2.0以上配置嵌入式servlet容器时EmbeddedServletContainerCustomizer类被WebServerFactoryCustomizer替代了。

//定制嵌入式的servlet容器
    //在Spring Boot2.0以上配置嵌入式Servlet容器时EmbeddedServletContainerCustomizer类被WebServerFactoryCustomizer替代
    @Bean
    public WebServerFactoryCustomizer<ConfigurableWebServerFactory> webServerFactoryCustomizer(){
        return new WebServerFactoryCustomizer<ConfigurableWebServerFactory>(){
            //定制嵌入式的servlet容器相关的规则
            @Override
            public void customize(ConfigurableWebServerFactory factory) {
                factory.setPort(8083);
            }
        };
    }



相关推荐

这个注解主要用在方法上,声明当前方法中包含了最终产生bean实例的逻辑,方法的返回值是一个Bean。这个bean会被Spring加入到容器中进行管理,默认情况下

一、概述 CommandLineRunner和ApplicationRunner接口是在容器启动成功后的最后一步回调。 参见:spring生命周期 中的springboot启动过程。 使用Comman

Headless模式是在缺少显示屏、键盘或者鼠标是的系统配置。在java.awt.toolkit和java.awt.graphicsenvironment类中有

Selenium在使用headless模式时,有以下几个问题 1、userAgent信息与正常模式不一致 2、window.navigator.webdriver 值

@ConditionalOnMissingBean只能在@Bean注解的方法上使用。 可以给该注解传入参数例如@ConditionOnMissingBean(name = "exa

一、概述 ApplicationContext事件机制是观察者设计模式的实现,通过ApplicationEvent类和ApplicationListener接口,可以实现ApplicationCont

一、概述 1、java注解 java在jdk1.5中引入了注解,spring框架也正好把java注解发挥得淋漓尽致。 2、spring 的@Import注解 @Import注解是Spring用来注入

一、@ComponentScan注解 @Component只写入value,可扫描路径下装配的@Controller、@Service、@Repository。 1、常用属性 basePacka

一、概述 1、读写分离 数据库主节点压力比较大,需要增加从节点提供读操作,以减少压力。 2、多数据源 一个复杂发项目,因为没有拆分服务,需要连接多个业务的数据源。 这些场景都需要使用springboo

方式1:通过@PostConstruct和@PreDestroy方法。 从Java EE5开始,Servlet增加了两个影响Servlet生命周期的注解。 方式2:通