7.1.【Spring学习】Bean生命周期

来源:互联网 发布:红遍中国的网络神曲 编辑:程序博客网 时间:2024/05/22 15:12

       我理解的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"/>
  • 1
  • 注解配置,直接使用@Scope注解
@Scope(value=ConfigurableBeanFactory.SCOPE_PROTOTYPE)
  • 1

1.2 枚举含义

  • singleton,表示单例模式,这个很好理解,表示在容器中,只存在该Bean的一个实例,每次请求Bean时,返回的都是同一个。例:
//例1.2.1@Componentpublic 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
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • prototype,表示每次请求Bean时,都会新建一个Bean实例,例:
//例1.2.1//上例中,在类前加一个注解:@Scope(value = ConfigurableBeanFactory.SCOPE_PROTOTYPE)输出:容器初始化完成Person 初始化...Person 初始化...false
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9

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"/>
  • 1
  • 注解配置,使用@Lazy注解
//在类前使用@Lazy
  • 1
  • 2

2.2 枚举含义

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

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 初始化...");    }}@Componentpublic 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=10745331Apple hashcode=10745331
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37

       本来以为,每次获取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@Overridepublic 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);    }}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38

4.2 配置lookup-method

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

配置后,测试一下:

//例4.1@Componentpublic 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=1528848756Apple 初始化...Apple hashcode=719205737
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33

       对比例子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#doGetBeanprotected <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    */@Overrideprotected 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;}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78

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=personBeanClassLoaderAware的setBeanClassLoader()方法注入Classloader,hashcode=918884489BeanFactoryAware的setBeanFactory()方法注入BeanFactory,hashcode=1574407511ApplicationContextAware的setApplicationContext()方法注入ApplicationContext,hashcode=916347070BeanPostProcessor的预处理方法,beanName=person@PostConstruct注解InitializingBean的afterPropertiesSet()方法init-method配置BeanPostProcessor的后处理方法,beanName=person
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66

4.总结

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

            <link rel="stylesheet" href="http://csdnimg.cn/release/phoenix/production/markdown_views-d4dade9c33.css">                </div>
原创粉丝点击