天天看点

SpringBoot 启动流程(三):Bean的生命周期一、Bean的生命周期原理二、代码验证实现三 源码分析

文章目录

  • 一、Bean的生命周期原理
  • 二、代码验证实现
    • 2.1 定义测试用的实体类
    • 2.2 以java注解的方式注入Person
    • 2.3 定义BeanPostProcessor的实现类
    • 2.4 启动类
    • 2.5 测试结果
  • 三 源码分析

本文基于以下版本验证:

项目 版本 下载地址
SpringBoot 2.0.4release https://github.com/spring-projects/spring-boot
SpringFramework 5.0.8.release https://github.com/spring-projects/spring-framework

一、Bean的生命周期原理

以下内容摘自Spring Bean的生命周期

Spring 容器可以管理 singleton 作用域 Bean 的生命周期,在此作用域下,Spring 能够精确地知道该 Bean 何时被创建,何时初始化完成,以及何时被销毁。

而对于 prototype 作用域的 Bean,Spring 只负责创建,当容器创建了 Bean 的实例后,Bean 的实例就交给客户端代码管理,Spring 容器将不再跟踪其生命周期。每次客户端请求 prototype 作用域的 Bean 时,Spring 容器都会创建一个新的实例,并且不会管那些被配置成 prototype 作用域的 Bean 的生命周期。

了解 Spring 生命周期的意义就在于,可以利用 Bean 在其存活期间的指定时刻完成一些相关操作。这种时刻可能有很多,但一般情况下,会在 Bean 被初始化后和被销毁前执行一些相关操作。

在 Spring 中,Bean 的生命周期是一个很复杂的执行过程,我们可以利用 Spring 提供的方法定制 Bean 的创建过程。

当一个 Bean 被加载到 Spring 容器时,它就具有了生命,而 Spring 容器在保证一个 Bean 能够使用之前,会进行很多工作。Spring 容器中 Bean 的生命周期流程如图 1 所示。

SpringBoot 启动流程(三):Bean的生命周期一、Bean的生命周期原理二、代码验证实现三 源码分析

图 1 Bean 的生命周期的整个执行过程描述如下。

1)根据配置情况调用 Bean 构造方法或工厂方法实例化 Bean。

2)利用依赖注入完成 Bean 中所有属性值的配置注入。

3)如果 Bean 实现了 BeanNameAware 接口,则 Spring 调用 Bean 的 setBeanName() 方法传入当前 Bean 的 id 值。

4)如果 Bean 实现了 BeanFactoryAware 接口,则 Spring 调用 setBeanFactory() 方法传入当前工厂实例的引用。

5)如果 Bean 实现了 ApplicationContextAware 接口,则 Spring 调用 setApplicationContext() 方法传入当前 ApplicationContext 实例的引用。

6)如果 BeanPostProcessor 和 Bean 关联,则 Spring 将调用该接口的预初始化方法 postProcessBeforeInitialzation() 对 Bean 进行加工操作,此处非常重要,Spring 的 AOP 就是利用它实现的。

7)如果 Bean 实现了 InitializingBean 接口,则 Spring 将调用 afterPropertiesSet() 方法。

8)如果在配置文件中通过 init-method 属性指定了初始化方法,则调用该初始化方法。

9)如果 BeanPostProcessor 和 Bean 关联,则 Spring 将调用该接口的初始化方法 postProcessAfterInitialization()。此时,Bean 已经可以被应用系统使用了。

10)如果在 中指定了该 Bean 的作用范围为 scope=“singleton”,则将该 Bean 放入 Spring IoC 的缓存池中,将触发 Spring 对该 Bean 的生命周期管理;如果在 中指定了该 Bean 的作用范围为 scope=“prototype”,则将该 Bean 交给调用者,调用者管理该 Bean 的生命周期,Spring 不再管理该 Bean。

11)如果 Bean 实现了 DisposableBean 接口,则 Spring 会调用 destory() 方法将 Spring 中的 Bean 销毁;如果在配置文件中通过 destory-method 属性指定了 Bean 的销毁方法,则 Spring 将调用该方法对 Bean 进行销毁。

Spring 为 Bean 提供了细致全面的生命周期过程,通过实现特定的接口或 的属性设置,都可以对 Bean 的生命周期过程产生影响。虽然可以随意配置 的属性,但是建议不要过多地使用 Bean 实现接口,因为这样会导致代码和 Spring 的聚合过于紧密。

二、代码验证实现

2.1 定义测试用的实体类

package org.springframework.boot.demo.domain;

import org.springframework.beans.BeansException;
import org.springframework.beans.factory.*;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.stereotype.Component;

import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;

//配置自动注入的注解
@Component
public class Person implements BeanFactoryAware, BeanNameAware ,ApplicationContextAware,
		InitializingBean, DisposableBean {

	private String name;
	private String address;
	private int phone;

	private BeanFactory beanFactory;
	private String beanName;
	private ApplicationContext applicationContext;

	public Person() {
		System.out.println("【构造器】调用Person的构造器实例化");
	}

	public String getName() {
		return name;
	}

	public void setName(String name) {
		System.out.println("【注入属性】注入属性name");
		this.name = name;
	}

	public String getAddress() {
		return address;
	}

	public void setAddress(String address) {
		System.out.println("【注入属性】注入属性address");
		this.address = address;
	}

	public int getPhone() {
		return phone;
	}

	public void setPhone(int phone) {
		System.out.println("【注入属性】注入属性phone");
		this.phone = phone;
	}

	@Override
	public String toString() {
		return "Person [address=" + address + ", name=" + name + ", phone="
				+ phone + "]";
	}

	// 这是BeanFactoryAware接口方法
	@Override
	public void setBeanFactory(BeanFactory arg0) throws BeansException {
		System.out
				.println("【BeanFactoryAware接口】调用BeanFactoryAware.setBeanFactory()");
		this.beanFactory = arg0;
	}

	@Override
	public void setApplicationContext(ApplicationContext arg0) throws BeansException{
		System.out.println("【ApplicationContextAware接口】调用ApplicationContextAware.setApplicationContext()");
		this.applicationContext=arg0;
	}

	// 这是BeanNameAware接口方法
	@Override
	public void setBeanName(String arg0) {
		System.out.println("【BeanNameAware接口】调用BeanNameAware.setBeanName()");
		this.beanName = arg0;
	}

	@PostConstruct
	public void postConstruct(){
		System.out.println("调用Bean的函数(postConstruct)");
	}

	// 这是InitializingBean接口方法
	@Override
	public void afterPropertiesSet() throws Exception {
		System.out
				.println("【InitializingBean接口】调用InitializingBean.afterPropertiesSet()");
	}

	// 通过<bean>的init-method属性指定的初始化方法
	public void myInit() {
		System.out.println("【init-method】调用<bean>的init-method属性指定的初始化方法");
	}

	@PreDestroy
	public void preDestroy(){
		System.out.println("调用Bean的函数(preDestroy)");
	}

	//DisposableBean接口的方法destroy
	@Override
	public void destroy() throws Exception {
		System.out.println("调用Bean的函数(destroy)");
	}

	// 通过<bean>的destroy-method属性指定的初始化方法
	public void myDestory() {
		System.out.println("【destroy-method】调用<bean>的destroy-method属性指定的初始化方法");
	}
}

           

2.2 以java注解的方式注入Person

一些参考资料,多使用xml配置的方式(在SpringMVC时流行),这里采用注解的方式。

package org.springframework.boot.demo.config;

import org.springframework.boot.demo.domain.Person;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class TestConfig {
	@Bean(initMethod = "myInit",destroyMethod = "myDestory")
	public Person person(){
		Person person =new Person();
		person.setAddress("广州");
		person.setName("张三");
		return person;
	}
}

           

2.3 定义BeanPostProcessor的实现类

package org.springframework.boot.demo.config;

import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanPostProcessor;
import org.springframework.boot.demo.domain.Person;
import org.springframework.stereotype.Component;

@Component
public class MyBeanPostProcessor implements BeanPostProcessor {
	public MyBeanPostProcessor() {
		super();
		System.out.println("这是BeanPostProcessor实现类构造器!!");
	}

	@Override
	public Object postProcessAfterInitialization(Object bean, String beanName)
			throws BeansException {
		//postProcessAfterInitialization方法在初始化过程中,会被调用多次,加上该判断,表示只对指定class进行处理。
		if(bean.getClass()==Person.class) {
			System.out
					.println("BeanPostProcessor接口方法postProcessAfterInitialization对属性进行更改!");
		}

		return bean;
	}

	@Override
	public Object postProcessBeforeInitialization(Object bean, String beanName)
			throws BeansException {
		if(bean.getClass()==Person.class) {
			System.out
					.println("BeanPostProcessor接口方法postProcessBeforeInitialization对属性进行更改!");
		}

		return bean;
	}
}

           

2.4 启动类

package org.springframework.boot.demo;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.demo.domain.Person;
import org.springframework.context.annotation.Bean;

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

2.5 测试结果

【构造器】调用Person的构造器实例化
【注入属性】注入属性address
【注入属性】注入属性name
【BeanNameAware接口】调用BeanNameAware.setBeanName()
【BeanFactoryAware接口】调用BeanFactoryAware.setBeanFactory()
【ApplicationContextAware接口】调用ApplicationContextAware.setApplicationContext()
BeanPostProcessor接口方法postProcessBeforeInitialization对属性进行更改!
调用Bean的函数(postConstruct)
【InitializingBean接口】调用InitializingBean.afterPropertiesSet()
【init-method】调用<bean>的init-method属性指定的初始化方法
BeanPostProcessor接口方法postProcessAfterInitialization对属性进行更改!
--- --- 
调用Bean的函数(preDestroy)
调用Bean的函数(destroy)
【destroy-method】调用<bean>的destroy-method属性指定的初始化方法
Process finished with exit code 1
           

三 源码分析

SpringBoot 启动流程(三):Bean的生命周期一、Bean的生命周期原理二、代码验证实现三 源码分析

源码图比较粗糙,暂用于个人笔记积累,源码部分,后面抽时间陆续完善UML图和原理解析。大致记录下涉及到的几个必要重要的方法:

finishBeanFactoryInitialization 进行初始化

instantiatebean 初始化bean,调用bean的构造器

invokeAwareMethod 调用bean的aware方法

文章参考:以下文章非常好,如果读到这里,也推荐看一看

Spring Boot Bean生命周期 https://www.jianshu.com/p/8e2d400492c7

Spring Bean的生命周期 http://c.biancheng.net/view/4261.html

Spring:源码解读Spring IOC原理 https://www.cnblogs.com/ITtangtang/p/3978349.html

Spring Bean的生命周期(非常详细)https://www.cnblogs.com/zrtqsk/p/3735273.html

Spring IOC 容器源码分析 https://javadoop.com/post/spring-ioc#toc_6