协调作用域不同步的Bean

先了解一下Bean的作用域

spring提供了6种作用域:singleton、prototype、request、session、applicaton、websocket
我们常用的singleton和prototype
singleton是默认值,singleton作用域的Bean只生成一个实例
prototype:通过容器的getBean()方法获取实例时,将会产生不同的实例
例如:
beans.xml


   
    
    
     

configrelax.Son.java

package configrelax;
public class Son {
public int age;
public int getAge() {
 return this.age;
}
public void setAge(int age) {
 this.age = age;
}
}

configrelax.man.java

package configrelax;
public class man {
public Son son;
public Son getSon() {
 return son;
}
public void setSon(Son son) {
 this.son = son;
}
}

maintest.java

package configrelax;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class maintest {
 public static void main(String[] args)
 { 
 ApplicationContext cn=new ClassPathXmlApplicationContext("beans.xml");
  System.out.println(cn.getBean("Son",Son.class));
  System.out.println(cn.getBean("Son",Son.class));
  System.out.println(cn.getBean("man",man.class));
  System.out.println(cn.getBean("man",man.class));
 }
}

运行结果
configrelax.Son@56235b8e
configrelax.Son@3632be31
configrelax.man@5abca1e0
configrelax.man@5abca1e0
显然singleton作用域的实例只有一个,prototype则会创建多个实例

那问题就来了

首先我们知道bean实例化的时机是:
Spring什么时候实例化bean,首先要分2种情况
第一:如果你使用BeanFactory作为Spring Bean的工厂类,则所有的bean都是在第一次使用该 Bean的时候实例化
第二:如果你使用ApplicationContext作为Spring Bean的工厂类,则又分为以下几种情况:
(1):如果bean的scope是singleton的,并且lazy-init为false(默认是false,所以可以不用设置),则ApplicationContext启动的时候就实例化该Bean,并且将实例化的Bean放在一个map结构的缓存中,下次再使用该Bean的时候,直接从这个缓存中取
(2):如果bean的scope是singleton的,并且lazy-init为true,则该Bean的实例化是在第一次使用该Bean的时候进行实例化
(3):如果bean的scope是prototype的,则该Bean的实例化是在第一次使用该Bean的时候进行实例化
对于singleton作用域的Bean依赖prototype作用域的Bean时
对于singleton 作用域的Bean,如果没有强行取消其预初始化行为,系统会在创建spring容器时实例化所有的singletonBean,于此同时,所依赖的prototype Bean也一起被实例化,并将prototype Bean注入singleton Bean中。这就导致无论何时通过singleton Bean去访问prototype Bean,得到的永远是最初的prototype Bean,这就相当于prototype也变成了singleton,显然不合适

解决方法

1、放弃依赖注入。每次需要prototype Bean实例时,主动向容器请求新的实例(可用不可取)
2、利用方法注入
先看下lookup方法注入的执行过程再分析注意事项
为了使用lookup方法注入,大致分为两步
1、将调用者Bean的实现类定义为抽象类,并定义一个抽象方法获取被依赖的Bean,spring容器会自行重写容器中的Bean的抽象类的抽象方法
2、中添加说明去实现哪个抽象方法,有两个属性
1)name:spring去实现的方法
2)bean:实现该方法的返回值
实现一个人每次带着不同的狗去打猎
configrelax.Person.java

package configrelax;
public interface Person
{
 public void hunt();
 }

congigrelax.Chinese.java

package configrelax;
public abstract class Chinese implements Person {
 public abstract Dog getDog();
 public void hunt()
 {
  System.out.println("猎人带着"+getDog()+"去打猎");
  System.out.println(getDog().run());
 }
}

configrelax.Dog.java

package configrelax;
public interface Dog {
public String run();
}

configrelax.gunDog.java

package configrelax;
 public class gunDog implements Dog
 {
  private String name;
  public void setName(String name)
  {
   this.name = name;
  }
  public String getName()
  {
   return name;
  }
  public String run()
  {
   return "狗去追";
  }
 }

beans.xml


   
    
    
     
     
     

maintest.java

package configrelax;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class maintest {
 public static void main(String[] args)
 { 
 ApplicationContext cn=new ClassPathXmlApplicationContext("beans.xml");
 Person per1=cn.getBean("Chinese",Person.class);
 Person per2=cn.getBean("Chinese",Person.class);
 per1.hunt();
 per2.hunt();
 }

猎人带着configrelax.gunDog@6950e31去打猎
狗去追
猎人带着configrelax.gunDog@b7dd107去打猎
狗去追

spring容器自行负责重写getDog()

public Dog getDog()
{
 ApplicationContext cn=new ClassPathXmlApplicationContext("beans.xml");
 return cn.getBean("gunDog");
}

spring会采用运行时动态增强的方式实现指定的抽象方法
如果目标抽象类如上Chinese类实现过接口,spring会使用JDK动态代理实现该抽象类,并实现抽象方法
如果目标抽象类如上Chinese类没有实现过接口,spring会使用cglib实现该抽象类,并实现抽象方法,spring5.0已经有此类库

你可能感兴趣的:(spring)