目录
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的属性后,在容器中使用的时候就生效了。