较完整的 bean生命周期[通俗易懂]

2022-09-30 14:45:25 浏览数 (1)

大家好,又见面了,我是你们的朋友全栈君。

首先需要说明的是,Bean的生命周期主要指的是singleton bean,标签scope默认就是singleton。对prototype bean来说,当用户getBean获得prototype bean的实例后,IOC容器就不再对当前实例进行管理,而是把管理权交由用户,此后再getBean生成的是新的实例。

普通Java Bean和Spring Bean

  • 普通java对象就是new出来,然后不再使用的时候通过垃圾回收机制进行回收;
  • 而spring Bean是由spring容器来控制的,并且在创建的时候,赋予了一些特殊处理;

有关 Java Bean, Spring Bean 和 Spring IoC 容器有一个很形象的比喻:小学生 (Java Bean)通过提交资料申请(元数据配置)加入了少先队(Spring Ioc 容器),学习了一些精神与规定之后,变成了少先队员(Spring Bean)。

从这里可以看出,Java Bean 和 Spring Bean 都是具有特定功能的对象,小学生还是那个小学生,只不过加入了少先队之后有了新的身份,新的身份要按照组织 (Spring Ioc)的规定履行特定义务。

bean的生命周期 经典四步

由源码可知,最核心的逻辑就是四步:实例化 –> 填充属性 –> 初始化 –> 销毁

代码语言:javascript复制
protected Object doCreateBean(final String beanName, final RootBeanDefinition mbd, Object[] args) throws BeanCreationException {
		if (instanceWrapper == null) {
				//实例化
				instanceWrapper = this.createBeanInstance(beanName, mbd, args);
		}

		…………………………忽略其他代码

		try {
				//属性赋值
				this.populateBean(beanName, mbd, instanceWrapper);
				if (exposedObject != null) {
						//初始化
						exposedObject = this.initializeBean(beanName, exposedObject, mbd);
				}
		} catch (Throwable var18) {
				if (var18 instanceof BeanCreationException && beanName.equals(((BeanCreationException)var18).getBeanName())) {
						throw (BeanCreationException)var18;
				}
				throw new BeanCreationException(mbd.getResourceDescription(), beanName, "Initialization of bean failed", var18);
		}

		…………………………………………忽略其他代码

		try {
				//销毁
				this.registerDisposableBeanIfNecessary(beanName, bean, mbd);
				return exposedObject;
		} catch (BeanDefinitionValidationException var16) {
				throw new BeanCreationException(mbd.getResourceDescription(), beanName, "Invalid destruction signature", var16);
		}
}

实例化

实例化是bean生命周期中至关重要的一环,如何实例化呢?spring基于类,根据类的无参构造函数,反射得到一对象,这时候的对象,可以称为原始对象,因为最后得到bean,其实就是这个时候得对象,只不过是经过了一系列的处理。

属性赋值

对象实例化出来了,但是对象里边的属性,也得需要spring容器来自动赋值。

比如AServiceImpl依赖了BService,那么这时候就用到@Autowired或@Resource注解,所以属性赋值的原理就是依赖注入,也就是这两个注解的原理。

这两注解都可以用在属性和方法上,@Autowired是属于spring的,@Resource是数据java规范的;@Autowired是按byType注入,@Resource是默认按byName注入;

上一副图看看这两注解的区别:

初始化

属性赋值完了就是初始化了,但是初始化该如何理解呢?就是我们在调用前,对象的某些属性有我们想要的值。以下是三种初始化方式:

1. @PostConstruct

代码语言:javascript复制
public class UserService implements InitializingBean {

    @Autowired
    private OrderService orderService;

    //这种方式是 Spring 非常提倡的一种方式,我们通常将其标记在方法上即可,通常习惯将这个方法起名为 init()
    private final Map<String, String> map = new HashMap<>();
    @PostConstruct
    public void init(){
        this.map.put("支付", "1");
        this.map.put("部分支付", "2");
        this.map.put("未支付", "3");
    }
}

2、InitializingBean.afterPropertiesSet()

代码语言:javascript复制
public class UserService implements InitializingBean {

    @Autowired
    private OrderService orderService;

    //我们可以通过Spring 为我们提供的 InitializingBean 接口中的方法  afterPropertiesSet 内完成实例化的工作,
    //但是 Spring Framework 官方并不建议我们通过这种方法来完成 Bean 的实例化,这是一种强耦合的方式,我们看到框架层面才会用到这个方法。
    @Override
    public void afterPropertiesSet() throws Exception {
        this.map.put("支付", "1");
        this.map.put("部分支付", "2");
        this.map.put("未支付", "3");
    }
}

3、xml配置

<bean id=”myClass” class=”com.demo.MyClass” init-method=”init”/>

以上就是三种初始化 Spring Beans 的方式,我们在框架中看到过三种方式在组合使用,那么组合使用的调用顺序是什么呢?

1、首先@PostConstruct 会被最先调用

2、其次 InitializingBean.afterPropertiesSet() 方法将会被调用

3、最后调用通过 XML 配置的 init-method 方法或通过设置 @Bean 注解 设置 initMethod 属性的方法

销毁

  • 销毁和初始化的方法一样,方法一:使用注解@PreDestroy;方法二:实现接口DisposableBean,重写destroy()方法;方法三:xml配置指定destroy-method=”destroyMethod”
  • 容器销毁的时候,会销毁单例bean,但是容器什么时候销毁呢???

实例化前的处理

class –> BeanDefinition –> 推断构造方法 –> 实例化

BeanDefinition

bean的定义,spring扫描到类,将类的信息或者xml的信息保存到BeanDefinition结构,生成BeanDefinition;这个结构包括beanClass:bean的类型是什么、scope:bean的作用域、isLaze:),将类的属性放入一个map,后边有获取这个bean的时候,就可以通过这个map来获取bean的一些定义(比如scope就可以判断作用域,如果是单例的,直接获取,如果是原型,直接创建)

推断构造方法

后边的实例化需要根据构造函数来生成对象,如果类里边只有一个无参构造函数,那就一切soEasy了,,但是如果,有自定义的有参构造函数,那spring实例化的的时候就麻烦了,到底该使用哪个构造函数嘞。。所以就有了推断构造函数,先bytype后byname,选择可用的构造方法。

初始化后的处理

初始化 –> AOP –> userService代理对象 –> bean

AOP:我们常说的aop就是在初始化之后,aop后得到的对象是一个代理对象。

如何知道需要aop呢?spring会也会扫描所有的切面bean,拿到切面bean中的@Before等注解中的切点,和当前类,进行匹配,如果有匹配的切点,就进行aop,得到一个代理对象,最后bean就是代理对象。aop的实现是基于动态代理:参考博客

初始化

接下来就是重头戏初始化

借用一张图,5、6是真实的初始化,在前边我们已经做过说明;3、4是在初始化之前;7是初始化之后;接下来我们就说说Aware和BeanPostProcessor。

我们先点进初始化代码initializeBean()瞅瞅

代码语言:javascript复制
protected Object initializeBean(final String beanName, final Object bean, RootBeanDefinition mbd) {
		// 3. 检查 Aware 相关接口并设置相关依赖
		if (System.getSecurityManager() != null) {
				AccessController.doPrivileged(new PrivilegedAction<Object>() {
						public Object run() {
								AbstractAutowireCapableBeanFactory.this.invokeAwareMethods(beanName, bean);
								return null;
						}
				}, this.getAccessControlContext());
		} else {
				this.invokeAwareMethods(beanName, bean);
		}

 
		// 4. BeanPostProcessor 前置处理
		Object wrappedBean = bean;
		if (mbd == null || !mbd.isSynthetic()) {
				wrappedBean = this.applyBeanPostProcessorsBeforeInitialization(bean, beanName);
		}

		// 5. 若实现 InitializingBean 接口,调用 afterPropertiesSet() 方法
		// 6. 若配置自定义的 init-method方法,则执行
		try {
				this.invokeInitMethods(beanName, wrappedBean, mbd);
		} catch (Throwable var6) {
				throw new BeanCreationException(mbd != null ? mbd.getResourceDescription() : null, beanName, "Invocation of init method failed", var6);
		}

		// 7. BeanPostProceesor 后置处理
		if (mbd == null || !mbd.isSynthetic()) {
				wrappedBean = this.applyBeanPostProcessorsAfterInitialization(wrappedBean, beanName);
		}

		return wrappedBean;
}

Aware

Aware类型的接口的作用就是让我们能够拿到Spring容器中的一些资源。基本都能够见名知意,Aware之前的名字就是可以拿到什么资源,例如BeanNameAware可以拿到BeanName,以此类推。调用时机需要注意:所有的Aware方法都是在初始化阶段之前调用的!

若 Spring 检测到 bean 实现了 Aware 接口,则会为其注入相应的依赖。所以通过让bean 实现 Aware 接口,则能在 bean 中获得相应的 Spring 容器资源,类似这样:

代码语言:javascript复制
public class UserService implements InitializingBean, BeanNameAware, BeanClassLoaderAware {

    @Autowired
    private OrderService orderService;


    @Override
    public void afterPropertiesSet() throws Exception {
        this.map.put("支付", "1");
        this.map.put("部分支付", "2");
        this.map.put("未支付", "3");
    }

    @Override
    public void setBeanClassLoader(ClassLoader classLoader) {
        
    }

    @Override
    public void setBeanName(String name) {

    }
}

spring提供的Aware接口有很多:

BeanFactory 类型的容器提供的aware接口:

  • BeanNameAware:注入当前 bean 对应 beanName;
  • BeanClassLoaderAware:注入加载当前 bean 的 ClassLoader;
  • BeanFactoryAware:注入 当前BeanFactory容器 的引用。

ApplicationContext 类型的容器提供的aware接口:

  • EnvironmentAware:注入 Enviroment,一般用于获取配置属性;
  • EmbeddedValueResolverAware:注入 EmbeddedValueResolver(Spring EL解析器),一般用于参数解析;
  • ApplicationContextAware(ResourceLoader、ApplicationEventPublisherAware、MessageSourceAware):注入 ApplicationContext 容器本身。

BeanPostProcessor

BeanPostProcessor 接口,大家也应该有印象,里面只有两个方法:

代码语言:javascript复制
public interface BeanPostProcessor {
    Object postProcessBeforeInitialization(Object var1, String var2) throws BeansException;

    Object postProcessAfterInitialization(Object var1, String var2) throws BeansException;
}

看方法名,BeforeInitialization 和 AfterInitialization,我们应该猜得出,这是在上述三种方式的前和后,算是一种全局的切面思想,我们经常会使用 postProcessAfterInitialization 方法,通过读取 Bean 的注解完成一些后续逻辑编写与属性的设定。

整合

所以比较完整的生命周期应该是:

扫描class –> BeanDefinition –> 推断构造方法 –> 实例化 –> 原始对象 –> 填充属性 –> Aware相关接口处理 –> BeanPostProcessor前置处理 –> 初始化 –> BeanPostProcessor后置处理 –> (AOP –> userService代理对象) –> bean

先记住四个主要步骤,然后每个步骤在细化理解,这样就可以得到一个比较完整的bean的生命周期。有不完整或错误的地方,感谢各位指教。

参考博客:参考一 参考二 参考三 参考四

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。

发布者:全栈程序员栈长,转载请注明出处:https://javaforall.cn/192870.html原文链接:https://javaforall.cn

0 人点赞