• 7 -- Spring的基本用法 -- 9...容器中Bean的生命周期


        7.9 容器中Bean的生命周期

          Spring可以管理singleton作用域的Bean的生命周期,Spring可以精确地知道该Bean何时被创建,何时被初始化完成、容器何时准备销毁该Bean实例。

          对于prototype作用域的Bean,Spring容器仅仅负责创建,当容器创建了Bean实例之后,Bean实例完全交给容器代码管理,容器不再跟踪其声明周期。每次客户端情i去prototype作用域的Bean时,Spring都会残生一个新的实例。

          对于singleton作用域的Bean,Spring可以管理实例化结束之后和销毁之前的行为。

          管理Bean的声明周期行为主要有:注入依赖关系之后;即将销毁Bean之前;

          7.9.1 依赖关系注入之后的行为

            Spring提供来年各种方式在Bean全部属性设置成功后执行特定行为。

            ⊙ 实现InitalizingBean接口。(先与init-method属性指定的初始化方法执行)(void afterPropertiesSet() throws Exception;)

            ⊙ 使用init-method属性

            Class : Chinese

    package edu.pri.lime._7_9_1.bean.impl;
    
    import org.springframework.beans.BeansException;
    import org.springframework.beans.factory.BeanNameAware;
    import org.springframework.beans.factory.InitializingBean;
    import org.springframework.context.ApplicationContext;
    import org.springframework.context.ApplicationContextAware;
    
    import edu.pri.lime._7_9_1.bean.Axe;
    import edu.pri.lime._7_9_1.bean.Person;
    
    public class Chinese implements Person , InitializingBean , ApplicationContextAware , BeanNameAware {
    
        private Axe axe;
        private String beanName;
        private ApplicationContext ctx;
        
        public Chinese() {
            super();
            System.out.println("1.Spring实例化主调Bean:Chinese实例...");
        }
        public void setAxe(Axe axe) {
            System.out.println("2.Spring调用setAxe执行依赖注入...");
            this.axe = axe;
        }
        public void setBeanName(String name) {
            System.out.println("3.===setBeanName===");
            this.beanName = name;
        }
        public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
            System.out.println("4.===setApplicationContext===");
            ctx = applicationContext;
        }
        public void afterPropertiesSet() throws Exception {
            System.out.println("5.正在执行初始化方法afterPropertiesSet()...");
        }
        public void init(){
            System.out.println("6.正在执行初始化方法init...");
        }
        public void useAxe() {
            System.out.println("7." + axe.chop());
        }
    
        public Axe getAxe() {
            return axe;
        }
    
    
        public ApplicationContext getCtx() {
            return ctx;
        }
    
        public void setCtx(ApplicationContext ctx) {
            this.ctx = ctx;
        }
    
        public String getBeanName() {
            return beanName;
        }
        
    }

            XML : 

    <?xml version="1.0" encoding="UTF-8"?>
    <!-- Spring 配置文件的根元素,使用Spring-beans-4.0.xsd语义约束 -->
    <beans xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xmlns="http://www.springframework.org/schema/beans"
        xsi:schemaLocation="http://www.springframework.org/schema/beans
    http://www.springframework.org/schema/beans/spring-beans-4.0.xsd">
    
        <bean id="chinese" class="edu.pri.lime._7_9_1.bean.impl.Chinese" init-method="init">
            <property name="axe">
                <bean class="edu.pri.lime._7_9_1.bean.impl.SteelAxe"/>
            </property>
        </bean>
    
    </beans>

            Class : SpringTest

    package edu.pri.lime._7_9_1.bean.main;
    
    import org.springframework.context.ApplicationContext;
    import org.springframework.context.support.ClassPathXmlApplicationContext;
    
    import edu.pri.lime._7_9_1.bean.Person;
    import edu.pri.lime._7_9_1.bean.impl.Chinese;
    
    public class SpringTest {
    
        public static void main(String[] args){
            ApplicationContext ctx = new ClassPathXmlApplicationContext("app_7_9_1.xml");
            Person person = ctx.getBean("chinese",Chinese.class);
            person.useAxe();
        }
    }

        Console :

    1.Spring实例化主调Bean:Chinese实例...
    2.Spring调用setAxe执行依赖注入...
    3.===setBeanName===
    4.===setApplicationContext===
    5.正在执行初始化方法afterPropertiesSet()...
    6.正在执行初始化方法init...
    7.用钢斧砍材真快

          7.9.2 Bean销毁之前的行为

            Spring提供两种方式定制Bean实例销毁之前的特定行为,这两种方式如下:

            ⊙ 实现DisposableBean 接口(优于destory-method属性指定的销毁方法执行)(void destory() throws Exception;)

            ⊙ 使用destory-method属性

            Class : Chinese

    package edu.pri.lime._7_9_2.bean.impl;
    
    import org.springframework.beans.BeansException;
    import org.springframework.beans.factory.BeanNameAware;
    import org.springframework.beans.factory.DisposableBean;
    import org.springframework.context.ApplicationContext;
    import org.springframework.context.ApplicationContextAware;
    
    import edu.pri.lime._7_9_2.bean.Axe;
    import edu.pri.lime._7_9_2.bean.Person;
    
    public class Chinese implements Person , DisposableBean , BeanNameAware , ApplicationContextAware{
    
        private Axe axe;
        private String BeanNane;
        private ApplicationContext ctx;
        
        public Chinese() {
            super();
            System.out.println("1.Spring实例化主调Bean:Chinse实例...");
        }
        public void setAxe(Axe axe) {
            System.out.println("2.Spring执行依赖关系的注入...");
            this.axe = axe;
        }
        public void setBeanNane(String beanNane) {
            BeanNane = beanNane;
            System.out.println("为什么不是我?");
        }
        public void setBeanName(String name) {
            System.out.println("3.调用setBeanName方法...");
            this.BeanNane = name;
        }
        public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
            System.out.println("4.调用setApplicationContest方法...");
            this.ctx = applicationContext;
            
        }
        public void useAxe() {
            System.out.println("5." + axe.chop());
        }
        public void destroy() throws Exception {
            System.out.println("6.正在指定销毁质地去年的方法destory...");
        }
        public void close(){
            System.out.println("7.正在执行销毁之前的方法close...");
        }
        public ApplicationContext getCtx() {
            return ctx;
        }
        public void setCtx(ApplicationContext ctx) {
            this.ctx = ctx;
        }
        public String getBeanNane() {
            return BeanNane;
        }
        public Axe getAxe() {
            return axe;
        }
    }

            XML :

    <?xml version="1.0" encoding="UTF-8"?>
    <!-- Spring 配置文件的根元素,使用Spring-beans-4.0.xsd语义约束 -->
    <beans xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xmlns="http://www.springframework.org/schema/beans"
        xsi:schemaLocation="http://www.springframework.org/schema/beans
    http://www.springframework.org/schema/beans/spring-beans-4.0.xsd">
    
        <bean id="chinese" class="edu.pri.lime._7_9_2.bean.impl.Chinese" destroy-method="close">
            <property name="axe">
                <bean class="edu.pri.lime._7_9_2.bean.SteelAxe"/>
            </property>
        </bean>
    
    </beans>

            Class : SpringTest

    package edu.pri.lime._7_9_2.bean.main;
    
    import org.springframework.context.support.AbstractApplicationContext;
    import org.springframework.context.support.ClassPathXmlApplicationContext;
    
    import edu.pri.lime._7_9_2.bean.Person;
    import edu.pri.lime._7_9_2.bean.impl.Chinese;
    
    public class SpringTest {
    
        public static void main(String[] args){
    //        AbstractApplicationContext 与 ApplicationContext 是什么亲戚?
            AbstractApplicationContext ctx = new ClassPathXmlApplicationContext("app_7_9_2.xml");
            Person person = ctx.getBean("chinese",Chinese.class);
            person.useAxe();
    //        为Spring容器注册关闭钩子,为毛在AbstractApplicationContext中?
            ctx.registerShutdownHook();
        }
    }

            Console : 

    1.Spring实例化主调Bean:Chinse实例...
    2.Spring执行依赖关系的注入...
    3.调用setBeanName方法...
    4.调用setApplicationContest方法...
    5.用钢斧砍材真快
    6.正在指定销毁质地去年的方法destory...
    7.正在执行销毁之前的方法close...

          7.9.3 协调作用域不同步的Bean

            当Spring容器初始化时,容器会预初始化容器中所有singleton Bean,由于singleton Bean依赖于prototype Bean,因此Spring在初始化singleton Bean之前,会先创建prototype Bean,然后才创建singleton Bean,接下来将prototype Bean注入singleton Bean。一旦singleton Bean初始化完成,它就持有了一个prototype Bean,容器在也不会为singleton Ben执行注入了。

            如果客户端通过该singleton Bean 去掉用prototype Bean的方法时,始终都是调用同一个prototype Bean 实例,这就违背了设置prototype Bean 的初衷,本来希望它具有prototype行为,但实际实际上它却表现出singleton行为。

            当singleton 作用域的Bean依赖于prototype 作用域的Bean时,会产生不同步的现象。

            解决该问题有两种思路:

            ⊙ 放弃依赖注入:singleton作用域的Bean每次需要prototype作用域的Bean时,主动向容器请求新的Bean实例,即可保证每次注入的prototype Bean实例都是最新的实例。

            ⊙ 利用方法注入,通常使用lookup方法注入。

            使用lookup方法注入可以让Spring容器重写容器中Bean的抽象或具体方法,返回查找容器中其他Bean的结果,被查找的Bean通常是一个non-singleton Bean(尽管也可以是一个singleton的)。Spring通过使用JDK动态代理或cglib库修改客户端的二进制码,从而实现上述要求。

             使用lookup方法注入,大致需要如下两步:

            ① 将调用者Bean的实现类定义为抽象类,并定义一个抽象方法来获取被依赖的Bean。

            ② 在<bean.../>元素中添加<lookup-method.../>子元素让Spring为调用者Bean的实现类实现指定的抽象方法。

             在配置文件中为<bean.../>元素添加<lookup-method.../>子元素,<lookup-method.../>子元素告诉Spring需要实现那个抽象方法。Spring为抽象方法提供实现体之后,这个方法就会变成具体方法,这个类也就变成了具体类,接下来Spring就可以创建该Bean的实例了。

            使用<lookup-method.../>元素需要指定如下两个属性:

            ⊙ name : 指定需要让Spring实现的方法;

            ⊙ bean : 指定Spring实现该方法的返回值;

            Spring会采用运行时动态增强的方式来实现<lookup-method.../>元素所指定的抽象方法,如果目标抽象类实现过接口,Spring会采用JDK动态代理来实现该抽象类,并为之实现抽象方法;如果目标抽象类没有实现过接口,Spring会采用cglib实现该抽象类,并为之实现抽象方法。

            Class : Husky

    package edu.pri.lime._7_9_3.bean;
    
    public class Husky implements Dog {
    
        private String name;
        public String run() {
            return name + "跑丢了";
        }
        public String getName() {
            return name;
        }
        public void setName(String name) {
            this.name = name;
        }
    
    }

            Class : Chinese

    package edu.pri.lime._7_9_3.bean.impl;
    
    import edu.pri.lime._7_9_3.bean.Dog;
    import edu.pri.lime._7_9_3.bean.Person;
    
    //1.将调用者Bean的实现类定义为抽象类
    public abstract class Chinese implements Person{
    
        private Dog dog;
    //    2.定义抽象方法,用于获取被依赖的Bean
        public abstract Dog getDog();
        public void hunt(){
            System.out.println("我带着:" + getDog() + "出去打猎");
            System.out.println(getDog().run());
        }
        public void setDog(Dog dog) {
            this.dog = dog;
        }
    }

            XML : 

    <?xml version="1.0" encoding="UTF-8"?>
    <!-- Spring 配置文件的根元素,使用Spring-beans-4.0.xsd语义约束 -->
    <beans xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xmlns="http://www.springframework.org/schema/beans"
        xsi:schemaLocation="http://www.springframework.org/schema/beans
    http://www.springframework.org/schema/beans/spring-beans-4.0.xsd">
    
    
        <bean id="chinese" class="edu.pri.lime._7_9_3.bean.impl.Chinese">
            <!--3. Spring 只要检测到lookup-method元素,Spring会自动为该元素的name属性所制定的方法提供实现体 -->
            <!-- name:指定需要让Spring实现的方法; -->
            <!-- bean:指定SPring实现该方法的返回值 -->
            <lookup-method name="getDog" bean="husky"/>
        </bean>
        
        <!-- 指定husky Bean的作用域为prototype,希望程序每次使用该Bean时用到的总是不同的实例 -->
        <bean id="husky" class="edu.pri.lime._7_9_3.bean.Husky" scope="prototype">
            <property name="name" value="哈士奇"/>
        </bean>
    
    </beans>

            Class : SpringTest

    package edu.pri.lime._7_9_3.bean.main;
    
    import org.springframework.context.ApplicationContext;
    import org.springframework.context.support.ClassPathXmlApplicationContext;
    
    import edu.pri.lime._7_9_3.bean.Person;
    import edu.pri.lime._7_9_3.bean.impl.Chinese;
    
    public class SpringTest {
    
        public static void main(String[] args){
            ApplicationContext ctx = new ClassPathXmlApplicationContext("app_7_9_3.xml");
            Person perA = ctx.getBean("chinese",Chinese.class);
            Person perB = ctx.getBean("chinese",Chinese.class);
            System.out.println(perA == perB);
            perA.hunt();
            perB.hunt();
        }
    }

            Console : 

    true
    我带着:edu.pri.lime._7_9_3.bean.Husky@574b560f出去打猎
    哈士奇跑丢了
    我带着:edu.pri.lime._7_9_3.bean.Husky@ba54932出去打猎
    哈士奇跑丢了

    啦啦啦

  • 相关阅读:
    case when then 中判断null的方法
    在SELECT的时候,加入一列固定值
    拿到iframe页面里面的变量及元素的方法
    datatables 多一列报错Cannot read property 'sWidth' of undefined(…)/少一列报错Cannot read property 'style' of undefined(…)
    MySQL 显示表字段及注释等信息
    MYSQL escape用法--转义
    MyBatis insert操作返回主键
    Java关键字final、static使用总结
    数据库往表中插入数据报错
    洛谷 题解 P1287 【盒子与球】
  • 原文地址:https://www.cnblogs.com/ClassNotFoundException/p/6275471.html
Copyright © 2020-2023  润新知