Spring - Bean的作用域和生命周期

目录

一、作用域

二、Bean的生命周期

1、配置生命周期测试实现

1)、在项目resources中添加Spring配置文件

2)、SpringBoot项目启动类

3)、生命周期测试类

4)、两个类都实现了BeanPostProcess

5)、引入配置

6)、启动项目

2、总结


    Spring Bean的作用域和生命周期一直是面试的终点,但是每次都没有去梳理,这次好像梳理一下也为后面源码分析梳理一下。

一、作用域

   

sigleton:Spring默认会使用singleton的scope,这样在循环依赖的时候能通过提前暴露没有初始化完的对象到一个ConcurrentHashMap中实现。一般情况下,我们都会使用singleton模式的bean进行编程,所以在tomcat线程池下进行调用的时候,就需要注意全局的属性需要能支持多线程,这也是面试的重点。

prototype:每次调用都获取到一个新的对象,这里使用了原型模式,提高效率。并且将Bean的生命周期交给客户端进行管理。

request:Web容器中每次请求都会创建一个bean

session:每个Web的session都会创建一个会话

globalSession在web环境中不存在,通过上面的Spring xml提示也知道了。

 

二、Bean的生命周期

    Bean的生命周期是什么意思? 当Spring容器启动的时候,我们一般通过ApplicationContext进行启动,在refresh方法中,会先准备好环境,就会去初始化BeanFactory,通过读取Spring的xml配置将信息以BeanDefinition的形式存储在里面。当然现在都是通过注解扫描的信息进行初始化,是在refresh的invokeBeanFactoryPostProcessors中完成的。不管怎么样,在refresh的最后finishBeanFactoryInitialization中将非懒加载的所有单利Bean进行初始化,即调用getBean方法进行初始化。也就会走每个Bean的生命周期的每一步,我们可以完全利用Bean的生命周期做很多的事情。

    之前的时候基本都是会有Spring的xml配置文件进行启动,那么可以使用下面方式进行测试验证:

public static void main(String[] args) {
    BeanFactory beanFactory = new XmlBeanFactory(new ClassPathResource("spring-bean.xml"));
    Object kevin = beanFactory.getBean("kevin");
    System.out.println(kevin);
}

当然现在基本都是在Spring Boot中进行开发,那么也在Spring Boot中进行分析。但是注解@Bean的方式并不能更好的打印整个生命周期,所以还是进行配置文件的加载。

1、配置生命周期测试实现

 

1)、在项目resources中添加Spring配置文件

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans 
       http://www.springframework.org/schema/beans/spring-beans.xsd">

    <bean class="com.kevin.tool.spring.ioc.lifecycle.SingletonBeanLifeCycle"
          name="kevin"
          init-method="initMedthod"
          destroy-method="destroyMedthod">
        <property name="testName" value="testkevin-1" />
    </bean>
</beans>

2)、SpringBoot项目启动类

@SpringBootApplication
public class KevinToolApplication {
    public static void main(String[] args) {
        SpringApplication.run(KevinToolApplication.class, args);
    }
}

3)、生命周期测试类

@Data
@ToString
public class SingletonBeanLifeCycle implements BeanNameAware, BeanFactoryAware,
        ApplicationContextAware, InitializingBean, DisposableBean {

    private String testName;

    @Override
    public void setBeanName(String name) {
        System.out.println("1)、BeanNameAware.setBeanName方法回调:name=" + name);
    }

    @Override
    public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
        System.out.println("2)、BeanFactoryAware.setBeanFactory方法回调:beanFactory=" + beanFactory);
    }

    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        System.out.println("3)、ApplicationContextAware.setApplicationContext方法回调:applicationContext=" + applicationContext);
    }

    @Override
    public void afterPropertiesSet() throws Exception {
        System.out.println("5)、InitializingBean.afterPropertiesSet方法回调了!");
    }

    public void initMedthod() {
        System.out.println("6)、initMethod方法被调用了!");
    }

    @Override
    public void destroy() throws Exception {
        System.out.println("9)DisposableBean.destroy方法回调了!");
    }

    public void destroyMedthod() {
        System.out.println("10)destroyMedthod方法被调用了!");
    }

}

4)、两个类都实现了BeanPostProcess

@Component
@Order(1)
public class SingletonBeanLifeCycleProcessor implements BeanPostProcessor {

    @Override
    public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
        if ("kevin".equals(beanName)) {
            System.out.println("4-1)、Processor-1-Before: beanName=" + beanName + ",bean Class<?>=" + bean.getClass() + "的bean调用getBean方法,\n" +
                    "-----BeanPostProcessor.postProcessBeforeInitialization回调了SingletonBeanLifeCycleProcessor!");
            ((SingletonBeanLifeCycle)bean).setTestName("kevin-2");
        }
        return bean;
    }

    @Override
    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
        if ("kevin".equals(beanName)) {
            System.out.println("7-1)、Processor-1-After: beanName=" + beanName + ",bean Class<?>=" + bean.getClass() + "的bean调用getBean方法, \n" +
                    "-----BeanPostProcessor.postProcessAfterInitialization回调了SingletonBeanLifeCycleProcessor!");
        }
        return bean;
    }
}
@Component
@Order(5)
public class SingletonBeanLifeCycleProcessor2 implements BeanPostProcessor {

    @Override
    public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
        if ("kevin".equals(beanName)) {
            System.out.println("4-2)、Processor-2-Before: beanName=" + beanName + ",bean Class<?>=" + bean.getClass() + "的bean调用getBean方法,\n" +
                    "-----BeanPostProcessor.postProcessBeforeInitialization回调了SingletonBeanLifeCycleProcessor-2!");
        }
        return bean;
    }

    @Override
    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
        if ("kevin".equals(beanName)) {
            System.out.println("7-2)、Processor-2-After: beanName=" + beanName + ",bean Class<?>=" + bean.getClass() + "的bean调用getBean方法, \n" +
                    "-----BeanPostProcessor.postProcessAfterInitialization回调了SingletonBeanLifeCycleProcessor-2!");
        }
        return bean;
    }
}

5)、引入配置

    不在启动类中去加载所以的测试配置。当Spring Boot启动的时候会扫描其启动了为根目录下的所有@Component(以及其子注解或者叫间接注解),就会将TestComponent加载进来。

1、会启动一个定时任务,3秒后,打印当前bean的属性testName,看看是否在SingletonBeanLifeCycleProcessor 中进行了更改(我们开发时候就可以利用这里改变Bean的属性信息) ,关闭Spring容器,测试destroy方法。

2、使用@ImportResource标签将配置文件加载进行解析,注入容器

@Component
@EnableScheduling
@ImportResource(locations= {"classpath:spring-bean.xml"})
public class TestComponent implements ApplicationContextAware {

    private ApplicationContext applicationContext;

    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        this.applicationContext = applicationContext;
    }

    @Scheduled(fixedRate = 3000, initialDelay = 3000)
    public void destroy() {
        SingletonBeanLifeCycle bean = applicationContext.getBean(SingletonBeanLifeCycle.class);
        System.out.println("8)、使用中bean当前的testName=" + bean.getTestName());
        ((AbstractApplicationContext) applicationContext).close();
    }
}

6)、启动项目

1)、BeanNameAware.setBeanName方法回调:name=kevin
2)、BeanFactoryAware.setBeanFactory方法回调:beanFactory=org.springframework.beans.factory.support.DefaultListableBeanFactory。。。
3)、ApplicationContextAware.setApplicationContext方法回调:applicationContext=org.springframework.boot.web.servlet.context
        .AnnotationConfigServletWebServerApplicationContext@79ad8b2f, started on Thu Nov 14 16:17:46 CST 2019
4-1)、Processor-1-Before: beanName=kevin,bean Class<?>=class com.kevin.tool.spring.ioc.lifecycle.SingletonBeanLifeCycle的bean调用getBean方法,
-----BeanPostProcessor.postProcessBeforeInitialization回调了SingletonBeanLifeCycleProcessor!
4-2)、Processor-2-Before: beanName=kevin,bean Class<?>=class com.kevin.tool.spring.ioc.lifecycle.SingletonBeanLifeCycle的bean调用getBean方法,
-----BeanPostProcessor.postProcessBeforeInitialization回调了SingletonBeanLifeCycleProcessor-2!
5)、InitializingBean.afterPropertiesSet方法回调了!
6)、initMethod方法被调用了!
7-1)、Processor-1-After: beanName=kevin,bean Class<?>=class com.kevin.tool.spring.ioc.lifecycle.SingletonBeanLifeCycle的bean调用getBean方法, 
-----BeanPostProcessor.postProcessAfterInitialization回调了SingletonBeanLifeCycleProcessor!
7-2)、Processor-2-After: beanName=kevin,bean Class<?>=class com.kevin.tool.spring.ioc.lifecycle.SingletonBeanLifeCycle的bean调用getBean方法, 
-----BeanPostProcessor.postProcessAfterInitialization回调了SingletonBeanLifeCycleProcessor-2!

2019-11-14 16:17:48.978  INFO 944 --- [           main] o.s.s.c.ThreadPoolTaskScheduler          : Initializing ExecutorService 'taskScheduler'
2019-11-14 16:17:49.059  INFO 944 --- [           main] o.s.b.w.embedded.tomcat.TomcatWebServer  : Tomcat started on port(s): 8080 (http) with context path ''
2019-11-14 16:17:49.061  INFO 944 --- [           main] com.kevin.tool.KevinToolApplication      : Started KevinToolApplication in 2.768 seconds (JVM running for 3.119)

8)、使用中bean当前的testName=kevin-2
2019-11-14 16:17:52.048  INFO 944 --- [   scheduling-1] o.s.s.c.ThreadPoolTaskScheduler          : Shutting down ExecutorService 'taskScheduler'
9)DisposableBean.destroy方法回调了!
10)destroyMedthod方法被调用了!

2、总结

Bean的生命周期:

0-0、Spring容器启动(并且调用getBean)

0-1、填充属性

1、BeanNameAware回调setBeanName方法

2、BeanFactoryAware回调setBeanFactory方法

3、ApplicationContextAware回调setApplicationContext方法

4-n、循环回调实现了BeanPostProcessor的postProcessBeforeInitialization方法

5、InitializingBean回调afterPropertiesSet方法

6、调用自定义的init-method方法

7、循环回调实现了BeanPostProcessor的postProcessAfterInitialization方法

8、正常使用bean,跟随容器的生命周期

9、DisposableBean的destroy方法回调

10、调用自定义的destroy-method方法

 

* 实现了BeanPostProcessor的接口,即使实现了@Order没有生效,但是各个实现之间没有关系,A修改了属性B拿到了还是原来的(后面在源码中分析,就很清晰了)。修改了bean的属性后,在容器中使用的时候就生效了。

 

 

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值