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

较完整的 bean生命周期[通俗易懂]首先需要说明的是,Bean的生命周期主要指的是singletonbean,标签的scope默认就是singleton。对prototypebean来说,当用户getBean获得prototypebean的实例后,IOC容器就不再对当前实例进行管理,而是把管理权交由用户,此后再getBean生成的是新的实例。普通JavaBean和SpringBean普通java对象就是new出来,然后不再使用的时候通过垃圾回收机制进行回收; 而springBean是由spring容器来控制的,并且..

大家好,又见面了,我是你们的朋友全栈君。如果您正在找激活码,请点击查看最新教程,关注关注公众号 “全栈程序员社区” 获取激活教程,可能之前旧版本教程已经失效.最新Idea2022.1教程亲测有效,一键激活。

Jetbrains全系列IDE使用 1年只要46元 售后保障 童叟无欺

首先需要说明的是,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的生命周期 经典四步

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

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注入;

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

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

 

初始化

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

          1. @PostConstruct

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()

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的实现是基于动态代理:参考博客

 

初始化

接下来就是重头戏初始化

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

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

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

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 容器资源,类似这样:

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 接口,大家也应该有印象,里面只有两个方法:

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的生命周期。有不完整或错误的地方,感谢各位指教。

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

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

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

【正版授权,激活自己账号】: Jetbrains全家桶Ide使用,1年售后保障,每天仅需1毛

【官方授权 正版激活】: 官方授权 正版激活 支持Jetbrains家族下所有IDE 使用个人JB账号...

(0)
blank

相关推荐

  • GridLayout用法「建议收藏」

    GridLayout用法「建议收藏」概述  在Android中,使用的最多的布局是LinearLayout了,它可以让布局界面中的子控件以常见的方式比如水平或者垂直方向对齐。在使用LinearLayout时,开发者应该会记得,会经常遇到复杂的布局结构,所以会时常使用各种LinearLayout进行嵌套,而且应该注意嵌套层次不要过多。  有很多不错的文章(比如有:AndroidLayoutTricks#1,Flattening

  • Asp.net 视频摘要

    Asp.net 视频摘要

  • shiro框架使用_shiro配置

    shiro框架使用_shiro配置一、什么是ShiroApacheShiro是一个强大易用的Java安全框架,提供了认证、授权、加密和会话管理等功能:认证-用户身份识别,常被称为用户“登录”;授权-访问控制;密码加密-保护或隐藏数据防止被偷窥;会话管理-每用户相关的时间敏感的状态。对于任何一个应用程序,Shiro都可以提供全面的安全管理服务。并且相对于其他安全框架,Shiro要简单的多。二

  • 曝光情侣复合全过程…聊天记录羞耻到炸裂!!!

    大家引以为戒 (@凤凰周刊) 这得有多恶心才行… (@沙雕图 ) 这令人着迷的的五官! (@沙雕娱乐 ) 这狮子看起来不大聪明的样子 (@沙雕娱乐&…

  • 不管怎么选择,都会有遗憾「建议收藏」

    到底什么样的选择才是最好的,每一种选择都会有遗憾!最近有点想家,虽然在这个城市安了新家。那个小的城市–延安,和这个大的城市—长沙,两种感觉,不同的生活体验。现在都十一月了,要是在延安,那么现在已经是深秋,开始变得萧条,不想夏天那么生机盎然,而现在这个城市秋天和夏天在外观上看不出什么变化,只是现在的温度比夏天舒服了很多,不冷 不热。城市的车水马龙,霓虹灯,夜晚还是有很多的喧嚣,也看不到漫天的繁星。

  • Java学习之反射篇

    Java学习之反射篇0x00前言今天简单来记录一下,反射与注解的一些东西,反射这个机制对于后面的java反序列化漏洞研究和代码审计也是比较重要。0x01反射机制概述Java反射是Java非

    2021年12月12日

发表回复

您的电子邮箱地址不会被公开。

关注全栈程序员社区公众号