我理解的Bean生命周期包括两个方面:

  • Bean何时创建,何时销毁
  • Bean从创建到销毁的执行流程

一、Bean创建与销毁

Bean的创建时机主要由几个配置项共同来决定,包括:

  • scope属性,决定是Bean是单例模式(singleton)还是多例模式(prototype),默认为单例singleton;
  • lazy-init属性,只对单例模式有效,决定是否延时加载,默认为false,表示在容器初始化时,就会生成单例;
  • RequestMapping属性,这个注解MVC中才有,当有该属性时,lazy-init属性会失效(其实不是简单的冲突,而是RequestMapping会在另外的逻辑处理中生成单例);

1.scope属性

主要有两个枚举,singleton与prototype,默认值为singleton。

1.1配置scope,如果不配置,默认值都为singleton

  • xml配置
<bean id="loginService" class="com.springapp.mvc.service.impl.LoginServiceImpl" scope="prototype"/>
  • 注解配置,直接使用@Scope注解
@Scope(value=ConfigurableBeanFactory.SCOPE_PROTOTYPE)

1.2 枚举含义

  • singleton,表示单例模式,这个很好理解,表示在容器中,只存在该Bean的一个实例,每次请求Bean时,返回的都是同一个。例:
//例1.2.1
@Component
public class Person {public Person(){System.out.println("Person 初始化...");}public static void main(String args[]){BeanFactory beanFactory = new ClassPathXmlApplicationContext("mvc-dispatcher-servlet.xml");System.out.println("容器初始化完成");Person person1 = (Person) beanFactory.getBean("person");Person person2 = (Person) beanFactory.getBean("person");System.out.println(person1.equals(person2));//同一个实例,打印true}
}输出:
Person 初始化...
容器初始化完成
true
  • prototype,表示每次请求Bean时,都会新建一个Bean实例,例:
//例1.2.1
//上例中,在类前加一个注解:
@Scope(value = ConfigurableBeanFactory.SCOPE_PROTOTYPE)输出:
容器初始化完成
Person 初始化...
Person 初始化...
false

2.lazy-init属性

一个布尔型的枚举,就两个值true与false,默认值为true。这个配置只在scope=singleton时有效

2.1 配置lazy-init,默认值为true

  • xml中配置
<bean id="loginService" class="com.springapp.mvc.service.impl.LoginServiceImpl" lazy-init="true"/>
  • 注解配置,使用@Lazy注解
//在类前使用
@Lazy

2.2 枚举含义

  • lazy-init=false,表示容器在初始化时,就会创建单例Bean,这个是默认配置,所以例1.2.1中,先打印“Person 初始化…”,再打印“容器初始化完成”
  • lazy-init=true,在初次getBean时,才会去创建Bean,例
//例2.2.1
//在Person类前添加注解@Lazy输出:
容器初始化完成
Person 初始化...
true

3.配置嵌套

主要指singleton与prototype两种不同Bean的相互引用,更具体的来说,其实是singleton对prototype的引用。因为相互引用一共就四种方式,有三种结果非常明确,即:

  • prototype或singleton引用singleton,因为singleton在容器中只会有一个实例,所以结果肯定都是引用同一个类;
  • prototype引用prototype,重新创建的实例再次注入新的依赖,肯定还是会重新创建的。

所以,只剩singleton对prototype的引用,一开始我就理解错误,以为prototype每次都会变,如下实例:

//例3.1@Component
@Scope(value = ConfigurableBeanFactory.SCOPE_PROTOTYPE)
public class Apple {public Apple(){System.out.println("Apple 初始化...");}
}@Component
public class Person {@ResourceApple apple;public Person(){System.out.println("Person 初始化...");}public void eat(){System.out.println("Apple hashcode="+apple.hashCode());}public static void main(String args[]){BeanFactory beanFactory = new ClassPathXmlApplicationContext("mvc-dispatcher-servlet.xml");System.out.println("容器初始化完成");Person person1 = (Person) beanFactory.getBean("person");Person person2 = (Person) beanFactory.getBean("person");person1.eat();person2.eat();}
}输出:
Person 初始化...
Apple 初始化...
容器初始化完成
Apple hashcode=10745331
Apple hashcode=10745331

本来以为,每次获取Person的Bean时,都会重新注入Apple,结果证明注入只会在Bean创建时进行一次,后续getBean时,发现单例Person存在,直接就会返回该实例,不会重新进行注入操作。
       但是如果有这种需求,就是需要每次都重新创建新的Apple,如何实现呢?在网上查询了一下资料,需要使用lookup-method属性配置。

4 lookup-method属性

这个属性用于singleton的Bean中获取prototype的Bean,实现每次调用都重新创建新的Bean。

4.1 原理

用户自己指定一个Bean注入时的方法,然后使用Bean时调用这个方法获取。Spring容器会自动用cglib替换方法体,动态调用getBean获取Bean并返回。看看源码:

//1.解析lookup-method属性并注册
//org.springframework.beans.factory.xml.BeanDefinitionParserDelegate#parseLookupOverrideSubElements
/**
* Parse lookup-override sub-elements of the given bean element.
*/
public void parseLookupOverrideSubElements(Element beanEle, MethodOverrides overrides) {NodeList nl = beanEle.getChildNodes();for (int i = 0; i < nl.getLength(); i++) {Node node = nl.item(i);if (isCandidateElement(node) && nodeNameEquals(node, LOOKUP_METHOD_ELEMENT)) {Element ele = (Element) node;String methodName = ele.getAttribute(NAME_ATTRIBUTE);String beanRef = ele.getAttribute(BEAN_ELEMENT);//新建lookup重载对象LookupOverride override = new LookupOverride(methodName, beanRef);override.setSource(extractSource(ele));//注册到Bean定义中overrides.addOverride(override);}}
}//2.调用lookup-method方法时,调用cglib替换后的代码,再去调用getBean,具体方法没去研究,有时间看看
//org.springframework.beans.factory.support.CglibSubclassingInstantiationStrategy.LookupOverrideMethodInterceptor#intercept
@Override
public Object intercept(Object obj, Method method, Object[] args, MethodProxy mp) throws Throwable {// Cast is safe, as CallbackFilter filters are used selectively.LookupOverride lo = (LookupOverride) getBeanDefinition().getMethodOverrides().getOverride(method);Object[] argsToUse = (args.length > 0 ? args : null);  // if no-arg, don't insist on args at allif (StringUtils.hasText(lo.getBeanName())) {//有beanName,优先使用beanName匹配return this.owner.getBean(lo.getBeanName(), argsToUse);}else {//没有配置beanName,尝试使用类型去匹配,如果匹配到多个,会抛异常return this.owner.getBean(method.getReturnType(), argsToUse);}
}

4.2 配置lookup-method

  • xml配置
<bean id="person" class="com.springapp.mvc.service.Person"><lookup-method bean="apple" name="getApple"/>
</bean>
  • 注解配置,使用@Lookup在方法前配置
@Lookup
public Apple getApple(){//方法体由Spring自动实现return null;
}

配置后,测试一下:

//例4.1
@Component
public class Person {public Person(){System.out.println("Person 初始化...");}public void eat(){Apple apple = getApple();System.out.println("Apple hashcode="+apple.hashCode());}public static void main(String args[]){BeanFactory beanFactory = new ClassPathXmlApplicationContext("mvc-dispatcher-servlet.xml");System.out.println("容器初始化完成");Person person1 = (Person) beanFactory.getBean("person");Person person2 = (Person) beanFactory.getBean("person");person1.eat();person2.eat();}@Lookuppublic Apple getApple(){return null;}
}测试输出:
Person 初始化...
容器初始化完成
Apple 初始化...
Apple hashcode=1528848756
Apple 初始化...
Apple hashcode=719205737

对比例子4.1与3.1,可以看到实现了每次调用Apple都重新创建的功能,当然,有人可能会问,这样写与自己去实现getApple的方法体,去new一个对象有什么区别呢?还是为了解耦,这样写的好处就是Apple的创建依然交给Spring,Person不依赖于Apple的实现。

二、Bean的执行流程

1.流程图

首先上一张老图,这里主要是用BeanFactory来初始化容器时,创建Bean会经历的流程。如果使用ApplicationContext,则会有很多额外处理与功能。

2.Spring源码,Bean的创建与初始化

org.springframework.beans.factory.support.AbstractBeanFactory#doGetBean
protected <T> T doGetBean(final String name, final Class<T> requiredType, final Object[] args, boolean typeCheckOnly)throws BeansException {...return createBean(beanName, mbd, args);...
}org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#createBean
/*** Central method of this class: creates a bean instance,* populates the bean instance, applies post-processors, etc.* @see #doCreateBean*/
@Override
protected Object createBean(final String beanName, final RootBeanDefinition mbd, final Object[] args)throws BeanCreationException {//查找Bean相关的类// Make sure bean class is actually resolved at this point.resolveBeanClass(mbd, beanName);// Prepare method overrides....//检查重载(字面意思,非对象继承)方法是否存在mbd.prepareMethodOverrides();// Give BeanPostProcessors a chance to return a proxy instead of the target bean instance.Object bean = resolveBeforeInstantiation(beanName, mbd);...//创建BeanObject beanInstance = doCreateBean(beanName, mbd, args);...return beanInstance;
}/*** Actually create the specified bean. Pre-creation processing has already happened* at this point, e.g. checking {@code postProcessBeforeInstantiation} callbacks.* <p>Differentiates between default bean instantiation, use of a* factory method, and autowiring a constructor.* @param beanName the name of the bean* @param mbd the merged bean definition for the bean* @param args arguments to use if creating a prototype using explicit arguments to a* static factory method. This parameter must be {@code null} except in this case.* @return a new instance of the bean* @throws BeanCreationException if the bean could not be created*/
//真正创建Bean的地方
protected Object doCreateBean(final String beanName, final RootBeanDefinition mbd, final Object[] args) {// Instantiate the bean....//创建Bean实例,会调用构造函数instanceWrapper = createBeanInstance(beanName, mbd, args);...//进行属性设置populateBean(beanName, mbd, instanceWrapper);.../*org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#initializeBean初始化Bean,按顺序包括:1.BeanNameAware,BeanClassLoaderAware,BeanFactoryAware的对应接口方法2.BeanPostProcessor的预处理postProcessBeforeInitialization3.InitializingBean接口的afterPropertiesSet4.自定义的init-method方法5.BeanPostProcessor的后处理postProcessAfterInitialization*/exposedObject = initializeBean(beanName, exposedObject, mbd);...// Register bean as disposable.//注册DisposableBean,只对singleton的Bean处理registerDisposableBeanIfNecessary(beanName, bean, mbd);return exposedObject;
}

3.测试代码

public class Person implements BeanNameAware,BeanFactoryAware,BeanClassLoaderAware,InitializingBean,DisposableBean,ApplicationContextAware {public Person(){System.out.println("Person 初始化...");}public static void main(String args[]){BeanFactory beanFactory = new ClassPathXmlApplicationContext("mvc-dispatcher-servlet.xml");System.out.println("容器初始化完成");Person person = (Person) beanFactory.getBean("person");person = null;}@Overridepublic void setBeanClassLoader(ClassLoader classLoader) {System.out.println("BeanClassLoaderAware的setBeanClassLoader()方法注入Classloader,hashcode="+classLoader.hashCode());}@Overridepublic void setBeanFactory(BeanFactory beanFactory) throws BeansException {System.out.println("BeanFactoryAware的setBeanFactory()方法注入BeanFactory,hashcode="+beanFactory.hashCode());}@Overridepublic void setApplicationContext(ApplicationContext applicationContext) throws BeansException {//ApplicationContext来初始化容器,会有很多额外处理与功能System.out.println("ApplicationContextAware的setApplicationContext()方法注入ApplicationContext,hashcode="+applicationContext.hashCode());}@Overridepublic void setBeanName(String name) {System.out.println("BeanNameAware的setBeanName()方法name,name="+name);}@Overridepublic void destroy() throws Exception {System.out.println("DisposableBean的destory()方法");}@Overridepublic void afterPropertiesSet() throws Exception {System.out.println("InitializingBean的afterPropertiesSet()方法");}@PostConstructpublic void postConstruct(){System.out.println("@PostConstruct注解");}public void initMethod() {System.out.println("init-method配置");}}测试输出:
Person 初始化...
BeanNameAware的setBeanName()方法name,name=person
BeanClassLoaderAware的setBeanClassLoader()方法注入Classloader,hashcode=918884489
BeanFactoryAware的setBeanFactory()方法注入BeanFactory,hashcode=1574407511
ApplicationContextAware的setApplicationContext()方法注入ApplicationContext,hashcode=916347070
BeanPostProcessor的预处理方法,beanName=person
@PostConstruct注解
InitializingBean的afterPropertiesSet()方法
init-method配置
BeanPostProcessor的后处理方法,beanName=person

4.总结

Spring在Bean的初始化过程中提供了很多可以进行干涉的方法,不过一般不建议使用实现Spring接口的方式,如实现InitializingBean,而使用@PostConstruct注解或自定义init-method配置,因为实现接口会与Spring相耦合。

【Spring学习】Bean生命周期相关推荐

  1. 【Spring】Bean生命周期源码分析 总结

    [Spring]Bean生命周期源码总结 1.案例验证 定义两个bean A,B 以及实现MyBeanFactoryProcess,MyBeanProcessor,MyInstantiationAwa ...

  2. 【Spring】- Bean生命周期

    2019独角兽企业重金招聘Python工程师标准>>> Spring Bean的生命周期: bean对象实例化 属性注入 beanfactory ApplicationContext ...

  3. springboot学习:bean生命周期

    1.bean 生命周期 bean创建-初始化-销毁 构造(对象创建): 单实例:在容器启动的时候创建对象; 多实例:在每次获取的时候创建对象: 初始化: 对象创建完成,并赋值好,调用初始化方法 销毁: ...

  4. 浅谈Spring的Bean生命周期和作用域

    学习spring有一段时间了.有的人说Spring的发展就是Java的发展,Spring就是Java的半壁江山,而且AOP思想更是OOP思想的一种扩展和延申.所以今天来浅谈一下spring的生命周期和 ...

  5. Spring 的 Bean 生命周期,11 张高清流程图及代码,深度解析

    点击上方"方志朋",选择"设为星标" 回复"666"获取新整理的面试文章 来源:撸码识途 jianshu.com/p/70b935f2b3f ...

  6. Spring的Bean生命周期,11 张高清流程图及代码,深度解析

    点击上方"方志朋",选择"设为星标" 回复"666"获取新整理的面试资料 来源:https://tech.souyunku.com/?p=1 ...

  7. Spring配置文件-Bean生命周期配置(init-method方法,destory-method方法)

    1.UserDaoImpl类 public class UserDaoImpl implements UserDao {public UserDaoImpl(){System.out.println( ...

  8. Spring中Bean生命周期、实例化与初始化

    创建Bean的入口:org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#doCreateBean. ...

  9. 【Spring】Bean生命周期

    一.背景: 自动注入 UserService 对象, UserService 结构如下 二.创建 Bean 的整体流程: UserService.class ------> 无参的构造方法 -- ...

  10. 【Spring学习之生命周期】什么是生命周期?什么是作用域?了解六种作用域

    前言:

最新文章

  1. Android Studio安卓开发中使用json来作为网络数据传输格式
  2. docker日志位置
  3. Java - 正则表达式的运用(Pattern模式和Matcher匹配)
  4. 模板 字段_Anki学习之路(08)|什么是Anki模板类型?什么是字段?
  5. CodeForces - 985F Isomorphic Strings(字符串哈希)
  6. 小学二年级上学期计算机教案,小学二年级上学期信息技术教案范文【三篇】
  7. netstat 用法
  8. linux ubuntu 查看历史命令
  9. oracle中trim,ltrim,rtrim函数用法
  10. PLY——Python Lex Yacc
  11. Java 学习笔记·十二 —— Java 案例·网上商城系统
  12. Vue启动项目报错travel@1.0.0 dev: `webpack-dev-server--inline --progress --config build/webpack.dev.conf.js
  13. 360路由器v2刷第三方固件_不走弯路:小米路由器3G 刷Padavan固件简单教程
  14. 配流06—frank_wolfe配流算法
  15. Python绘制正态分布图及求分位数
  16. 杭电acm a+b问题
  17. asp.net保存图片
  18. 苹果审核规则研究,实时更新
  19. macOS上的改键指南--使用Karabiner
  20. Android 平台电容式触摸屏硬件基本原理

热门文章

  1. GIFCAM GIF录制编辑工具 V5.5中文版 GIF制作
  2. 集成平台Ensemble环境搭建(HealthShare) 及MIRROR镜像(主备机)的配置
  3. 苹果x处理器多少_精仿苹果iPhone XR手机配置介绍
  4. Druid监控页面配置用户密码、去除Ad
  5. 原件常识:以“光”为传媒的光电耦合器
  6. 数据结构单链表插入和删除操作
  7. Q-learning(强化学习)
  8. js:开启弹窗和关闭弹窗
  9. macd的python代码同花顺_同花顺MACD金叉买点主图指标公式
  10. 史上最全Maven教程(二)