【Spring学习】Bean生命周期

我理解的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 {
    @Resource
    Apple 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 all
    if (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();
    }

    @Lookup
    public 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);
    ...
    //创建Bean
    Object 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的预处理postProcessBeforeInitialization
        3.InitializingBean接口的afterPropertiesSet
        4.自定义的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;
    }

    @Override
    public void setBeanClassLoader(ClassLoader classLoader) {
        System.out.println("BeanClassLoaderAware的setBeanClassLoader()方法注入Classloader,hashcode="+classLoader.hashCode());
    }

    @Override
    public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
        System.out.println("BeanFactoryAware的setBeanFactory()方法注入BeanFactory,hashcode="+beanFactory.hashCode());
    }

    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        //ApplicationContext来初始化容器,会有很多额外处理与功能
        System.out.println("ApplicationContextAware的setApplicationContext()方法注入ApplicationContext,hashcode="+applicationContext.hashCode());
    }

    @Override
    public void setBeanName(String name) {
        System.out.println("BeanNameAware的setBeanName()方法name,name="+name);
    }

    @Override
    public void destroy() throws Exception {
        System.out.println("DisposableBean的destory()方法");
    }

    @Override
    public void afterPropertiesSet() throws Exception {
        System.out.println("InitializingBean的afterPropertiesSet()方法");
    }

    @PostConstruct
    public 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相耦合。

时间: 2024-10-07 06:58:05

【Spring学习】Bean生命周期的相关文章

spring 中bean生命周期

从头开始学习spring(一) 传统编程中,依赖关系比较多的情况下,导致维护成本直线上升,spring 采用Ioc对bean进行管理,减少了开发人员的工作量 正确理解spring bean 的生命周期非常重要 package com.study.spring.beans; import org.springframework.beans.BeansException; import org.springframework.beans.factory.BeanFactory; import org

spring中bean生命周期

一  bean指定初始化和销毁方法 1.bean的生命周期:bean的创建 ---> 初始化 ---> 销毁的过程   bean的生命周期有容器来管理   单例的bean在创建时执行初始化方法,在spring关闭时执行销毁方法   原型的bean在每次获取时执行初始化方法,没有销毁方法   我们可以自己指定初始化和销毁方法,要指定的方法必须是bean所属的类的方法.注意:这个方法必须是个无参的方法 2.创建实体类 public class Car { private String name;

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

在网上已经有跟多Bean的生命周期的博客,但是很多都是基于比较老的版本了,最近吧整个流程化成了一个流程图.待会儿使用流程图,说明以及代码的形式来说明整个声明周期的流程.注意因为代码比较多,这里的流程图只画出了大概的流程,具体的可以深入代码 一.获取Bea 第一阶段获取Bean 这里的流程图的入口在 AbstractBeanFactory类的 doGetBean方法,这里可以配合前面的 getBean方法分析文章进行阅读.主要流程就是 1.先处理Bean 的名称,因为如果以“&”开头的Bean名称

一步步剖析spring bean生命周期

关于spring bean的生命周期,是深入学习spring的基础,也是难点,本篇文章将采用代码+图文结论的方式来阐述spring bean的生命周期, 本篇文章将阐述清楚下图. 一  项目结构及源码 1.程序目录结构 2.applicationContext.xml <?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/sc

Spring点滴四:Spring Bean生命周期

Spring Bean 生命周期示意图: 了解Spring的生命周期非常重要,我们可以利用Spring机制来定制Bean的实例化过程. --------------------------------------------------------------------------------------------------------------------------------------------------- spring-service.xml: <?xml version=

Spring事务,Bean生命周期

一.事务相关: 1.Spring事务基于Spring AOP切面编程: 2.AOP基于代理模式,得到需要开启事务的代码的代理对象: 3.而没有开启事务的Service方法里调用了开启事务 @Transactional 的方法时,整个代码是不会开启事务的,原理还是代理模式插入事务的依据是最外层的注解: 4.对于上面3,反之,则可以,这是事务的传播机制. 二.Bean生命周期: 2.1生命周期图: 出自:<精通Spring 4.x> 2.2不同级别的接口分类: 1.Bean本身的方法:Bean的构

Bean 生命周期

对于普通的Java对象,当new的时候创建对象,当它没有任何引用的时候被垃圾回收机制回收.而由Spring IoC容器托管的对象,它们的生命周期完全由容器控制. Bean生命周期流程 1.实例化Bean 实例化Bean对于BeanFactory容器,当客户向容器请求一个尚未初始化的bean时,或初始化bean的时候需要注入另一个尚未初始化的依赖时,容器就会调用createBean进行实例化. 对于ApplicationContext容器,当容器启动结束后,便实例化所有的bean. 容器通过获取B

bean生命周期

那在spring中bean的生命周期究竟是怎样的呢 1. 容器寻找Bean的定义信息并将其实例化 2. 使用依赖注入,spring按照Bean定义信息配置Bean的所有属性 3. 如果Bean实现了BeanNameAware接口,工厂调用Bean的SetBeanName()方法传递Bean的ID 4. 如果Bean实现了BeanFactoryAware接口,工厂调用setBeanFactory()方法传入工厂自身 5. 如果BeanPostProcessor和Bean关联,那么其postProc

spring(二、bean生命周期、用到的设计模式、常用注解)

Spring作为当前Java最流行.最强大的轻量级框架,受到了程序员的热烈欢迎.准确的了解Spring Bean的生命周期是非常必要的.我们通常使用ApplicationContext作为Spring容器.这里,我们讲的也是 ApplicationContext中Bean的生命周期.而实际上BeanFactory也是差不多的,只不过处理器需要手动注册.  转载 http://www.cnblogs.com/zrtqsk/p/3735273.html 一.生命周期流程图: Spring Bean的