zoukankan      html  css  js  c++  java
  • 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出去打猎
    哈士奇跑丢了

    啦啦啦

  • 相关阅读:
    共享纸巾更换主板代码分析 共享纸巾主板更换后的对接代码
    Python Django Ajax 传递列表数据
    Python Django migrate 报错解决办法
    Python 创建字典的多种方式
    Python 两个list合并成一个字典
    Python 正则 re.sub替换
    python Django Ajax基础
    Python Django 获取表单数据的三种方式
    python Django html 模板循环条件
    Python Django ORM 字段类型、参数、外键操作
  • 原文地址:https://www.cnblogs.com/ClassNotFoundException/p/6275471.html
Copyright © 2011-2022 走看看