较完整的 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)


相关推荐

  • SpringBootTest用法

    SpringBootTest用法SpringBootTest测试

  • 【综合帖】Java并发多线程编程学习专栏

    本帖主要用于记录Java多线程或并发编程的一些学习书籍和一些优秀的博文,方便自己学习并发多线程编程的知识!也为了通过自己的学习整理出学习笔记,输出一个java并发编程的专栏。让自己的学习的知识沉淀下来!本篇记录的博文或者些图片内容都来自互联网,如果有任何侵权或者疑问,可以联系我!

  • NFS固定端口配置

    NFS固定端口配置NFS固定端口的配置一、环境介绍二、配置步骤1、安装nfs工具2、创建共享目录3、修改nfs配置文件,指定固定的端口4、重启相关服务并检查相应端口是否存在一、环境介绍1、内网环境,使用NFS作为存储;2、需开通工作节点服务器访问存储服务器的NFS的网络策略,所以需要将端口固定;二、配置步骤1、安装nfs工具yuminstall-yrpcbindnfs-utils2、创建共享目录mkdir/data/hoschmod777/data/hos/编辑/etc/exports

  • C++ Qt常用面试题整理(不定时更新)[通俗易懂]

    C++ Qt常用面试题整理(不定时更新)[通俗易懂]1.Qt多线程同步的几种实现方式(1)互斥量:QMutexQMutex类提供的是线程之间的访问顺序化。QMutex的目的是保护一个对象/数据结构或者代码段在同一时间只有一个线程可以访问。基本使用方法如下:QMutexmutex;intvar;voidfunction(){mutex.lock();//访问varvar*var;mutex.unlock();}如果使用mutex加锁,却没有使用unlock解锁,那么就会造成..

  • Linux环境redis集群搭建「建议收藏」

    (要让集群正常工作至少需要3个主节点,在这里我们要创建6个redis节点,其中三个为主节点,三个为从节点,对应的redis节点的ip和端口对应关系如下)127.0.0.1:7000127.0.0.1:7001127.0.0.1:7002127.0.0.1:7003127.0.0.1:7004127.0.0.1:7005 1、安装redis上传服务器,解压,编译

  • 五子棋 人机对战 思路「建议收藏」

    五子棋 人机对战 思路「建议收藏」五子棋之人机对战思路:1,棋盘与棋子的实现2,玩家下完棋后,计算机要根据当前玩家所下的棋子找出最佳下棋点3,输赢的判断之前已经把人人对战的大概方法实现了,接下要实现的是人机的对战。我的思路如下(当然只是思路哈~还没具体实现呢):其实人机对战就是就在我们下完一步起后,通过电脑分析下一步该走哪,对此做出判断并在最佳的位置上出棋。1,首先需要定义权值,我做…

发表回复

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

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