zoukankan      html  css  js  c++  java
  • 单例bean依赖原型bean的 如何保证原型bean生命周期

    简介:

      将原型bean注入到单例bean,会破坏原型bean的生命周期,使其的生命周期变成与单例bean相同。

      好了,废话不多少,直接上栗子,边吃边说。

    情况模拟:

    1、单例bean

    @Component
    public class SingletonBean {
        @Autowired
        private PrototypeBean prototypeBean;
    
        public void printTime() {
            System.out.println("SingletonBean: "+this.hashCode());
            System.out.println("prototypeBean 注入给单例的:"+prototypeBean.hashCode());
            prototypeBean.printTime();
        }
    
    }

    2、原型bean

    @Component
    @Scope(value = ConfigurableBeanFactory.SCOPE_PROTOTYPE)
    public class PrototypeBean {
         Long timeMilis;
    
        public PrototypeBean() {
            //System.out.println("PrototypeBean Constructor");
            this.timeMilis = System.currentTimeMillis();
        }
    
        public void printTime() {
            System.out.println("PrototypeBean原始bean:"+this.hashCode());
            System.out.println("timeMils:" + timeMilis);
        }
    }

    3、测试类

    public class PrototypeTest {
        public static void main(String[] args) {
            AnnotationConfigApplicationContext applicationContext=
                    new AnnotationConfigApplicationContext(PrototypeBeanConfig.class);
    
            SingletonBean singletonBean = (SingletonBean) applicationContext.getBean("singletonBean");
            SingletonBean singletonBean1 = (SingletonBean) applicationContext.getBean("singletonBean");
            SingletonBean singletonBean2 = (SingletonBean) applicationContext.getBean("singletonBean");
    
    
            singletonBean.printTime();
            singletonBean1.printTime();
            singletonBean2.printTime();
        }
    }

    4、测试结果:

    5、原因分析:  

    要知道, SingletonBean 是单例模式的,只会被Spring进行类扫面的时候创建一次,然后存入到单例池SingletonObjects。之后每次使用SingletonBean,都会从单例池中获取,不会再次创建。因为属性的注入发生在创建bean的过程中,所以也只会在创建的时候注入一次。也就是说,在SingletonBean创建过程中,将生成一个PrototypeBean类的bean作为属性注入到SingletonBean中。而以后使用SingletonBean 的 prototypeBean 属性时不会重新生成PrototypeBean类的bean,只会使用已经注入到SingletonBean属性上的bean。因此PrototypeBean失去了原型bean的生命周期。

    解决方式:

    一、单例bean实现ApplicationContextAware接口

      单例bean不让spring进行属性原型bean的属性注入。而是每次使用的时候,通过getBean的方式获取原型bean。  

    1、单例bean

    @Component
    public class SingletonBeanImplementApplicationContextAware implements ApplicationContextAware {
    
        ApplicationContext applicationContext;
    
        private PrototypeBean prototypeBean;
    
        public void printTime() {
            System.out.println("SingletonBean: " + this.hashCode());
            prototypeBean= (PrototypeBean) applicationContext.getBean("prototypeBean");
            System.out.println("prototypeBean 注入给单例的:"+ prototypeBean.hashCode());
            prototypeBean.printTime();
        }
    
        @Override
        public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
            this.applicationContext=applicationContext;
        }
    }

    2、原型bean

    @Component
    @Scope(value = ConfigurableBeanFactory.SCOPE_PROTOTYPE)
    public class PrototypeBean {
         Long timeMilis;
    
        public PrototypeBean() {
            //System.out.println("PrototypeBean Constructor");
            this.timeMilis = System.currentTimeMillis();
        }
    
        public void printTime() {
            System.out.println("PrototypeBean原始bean:"+this.hashCode());
            System.out.println("timeMils:" + timeMilis);
        }
    }

    3、测试类

    public class PrototypeTest {
        public static void main(String[] args) {
            AnnotationConfigApplicationContext applicationContext=
                    new AnnotationConfigApplicationContext(PrototypeBeanConfig.class);
    
    
            SingletonBeanImplementApplicationContextAware singletonBean = (SingletonBeanImplementApplicationContextAware) applicationContext.getBean("singletonBeanImplementApplicationContextAware");
            SingletonBeanImplementApplicationContextAware singletonBean1 = (SingletonBeanImplementApplicationContextAware) applicationContext.getBean("singletonBeanImplementApplicationContextAware");
            SingletonBeanImplementApplicationContextAware singletonBean2 = (SingletonBeanImplementApplicationContextAware) applicationContext.getBean("singletonBeanImplementApplicationContextAware");
    
    
            singletonBean.printTime();
            singletonBean1.printTime();
            singletonBean2.printTime();
        }
    }

    4、测试结果:

    5、原因分析:

      让单例bean实现ApplicationContextAware接口,重写setApplicationContext方法,获取到ApplicationContex,每次使用 prototypeBean的时候,都从ApplicationContext中通过getBean的方式来获取最新的原型bean,每次获取的原型bean都是Spring新创建的bean。因此可以保证原型bean的生命周期。

      

    二、@Lookup

      通过加有@Lookup的抽象方法来获取prototypeBean对象。

    1、单例bean

    @Component
    public  abstract   class SingletonBeanByLookup {
    
        private PrototypeBean prototypeBean;
        @Lookup
        public abstract  PrototypeBean getPrototypeBean();
    
        public void printTime() {
            System.out.println(this.hashCode());
            prototypeBean= getPrototypeBean()  ;
            System.out.println(prototypeBean.hashCode());
            prototypeBean.printTime();
        }
    
    }

    2、原型bean

    @Component
    @Scope(value = ConfigurableBeanFactory.SCOPE_PROTOTYPE)
    public class PrototypeBean {
         Long timeMilis;
    
        public PrototypeBean() {
            //System.out.println("PrototypeBean Constructor");
            this.timeMilis = System.currentTimeMillis();
        }
    
        public void printTime() {
            System.out.println("PrototypeBean原始bean:"+this.hashCode());
            System.out.println("timeMils:" + timeMilis);
        }
    }

    3、测试类

    public class PrototypeTest {
        public static void main(String[] args) throws InterruptedException {
            AnnotationConfigApplicationContext applicationContext=
                    new AnnotationConfigApplicationContext(PrototypeBeanConfig.class);
    
    
            SingletonBeanByLookup singletonBean = (SingletonBeanByLookup) applicationContext.getBean("singletonBeanByLookup");
            SingletonBeanByLookup singletonBean1 = (SingletonBeanByLookup) applicationContext.getBean("singletonBeanByLookup");
            SingletonBeanByLookup singletonBean2 = (SingletonBeanByLookup) applicationContext.getBean("singletonBeanByLookup");
    
            singletonBean.printTime();
            Thread.sleep(1000);
            singletonBean1.printTime();
            Thread.sleep(1000);
            singletonBean2.printTime();
        }
    }

    4、测试结果:

    5、原因分析:

    系统会生成SingletonBeanByLookup的代理对象作为 SingletonBeanByLookup对象的bean,
    系统的代理对象是应用了Srping应用了CGLIB(动态代理)类库。Spring在初始化容器的时候对配置@lookup的bean做了特殊处理,Spring会对bean指定的class做动态代理,代理@lookup标签中name属性所指定的方法,返回bean属性指定的bean实例对象。
    每次我们调用@lookup方法时,其实是调用了CGLIB生成的动态代理类的方法。
    就是进入org.springframework.beans.factory.support.CglibSubclassingInstantiationStrategy.LookupOverrideMethodInterceptor#intercept(java.lang.Object, java.lang.reflect.Method, java.lang.Object[], org.springframework.cglib.proxy.MethodProxy) 方法中
    在此拦截器方法中 通过getBean 获取对应的bean。

     

    三、原型bean使用代理模式

      在原型bean的@Scope 标签中 添加“proxyMode = ScopedProxyMode.TARGET_CLASS”,如:  @Scope(value = ConfigurableBeanFactory.SCOPE_PROTOTYPE,proxyMode = ScopedProxyMode.TARGET_CLASS)

    1、单例bean

    @Component
    public class SingletonBean {
        @Autowired
        private PrototypeBean prototypeBean;
    
    
        public void printTime() {
            System.out.println("SingletonBean: "+this.hashCode());
            System.out.println("prototypeBean 注入给单例的:"+prototypeBean.hashCode());
            prototypeBean.printTime();
        }
    
    
    }

    2、原型bean

    @Component
    @Scope(value = ConfigurableBeanFactory.SCOPE_PROTOTYPE,proxyMode = ScopedProxyMode.TARGET_CLASS)
    public class PrototypeBean {
         Long timeMilis;
    
        public PrototypeBean() {
            //System.out.println("PrototypeBean Constructor");
            this.timeMilis = System.currentTimeMillis();
        }
    
        public void printTime() {
            System.out.println("PrototypeBean原始bean:"+this.hashCode());
            System.out.println("timeMils:" + timeMilis);
        }
    }

    3、测试类

    public class PrototypeTest {
        public static void main(String[] args) throws InterruptedException {
            AnnotationConfigApplicationContext applicationContext=
                    new AnnotationConfigApplicationContext(PrototypeBeanConfig.class);
    
    
    
    
            SingletonBean singletonBean = (SingletonBean) applicationContext.getBean("singletonBean");
    
            SingletonBean singletonBean1 = (SingletonBean) applicationContext.getBean("singletonBean");
    
            SingletonBean singletonBean2 = (SingletonBean) applicationContext.getBean("singletonBean");
    
    
    
            singletonBean.printTime();
            Thread.sleep(1000);
            singletonBean1.printTime();
            Thread.sleep(1000);
            singletonBean2.printTime();
        }
    }

    4、测试结果:

    5、原因分析:

    这种方法将prototypeBean类生成一个代理对象赋值给singletonBean,每次调用代理方法的时候,会通过getBean去获取一个原始的PrototypeBean。
    *
    * This will lead to the creation of a proxy. That proxy is created once and will be
    * returned for each call to getBean. As soon as you invoke a method on the proxy it will,
    * based on the scope, either create a new one or reuse an existing one.
    * As you have specified the scope as prototype each method invocation will lead to a new object.

  • 相关阅读:
    Kafka设计解析(二)- Kafka High Availability (上)
    Kafka设计解析(三)- Kafka High Availability (下)
    Kafka深度解析
    Cloudera Manager(CDH5)内部结构、功能包括配置文件、目录位置等
    Failed to start /etc/rc.d/rc.local Compatibility
    Offset Management For Apache Kafka With Apache Spark Streaming
    maven-assembly-plugin打包可执行的jar包
    How Cigna Tuned Its Spark Streaming App for Real-time Processing with Apache Kafka
    SystemTap Beginners Guide
    数据可视化的开源方案: Superset vs Redash vs Metabase (二)
  • 原文地址:https://www.cnblogs.com/gengaixue/p/14225180.html
Copyright © 2011-2022 走看看