协调作用域不同步的Bean

先了解一下Bean的作用域

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

<?xml version="1.0" encoding="UTF-8"?>
   <beans xmlns="http://www.springframework.org/schema/beans"
   xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
   xmlns:p="http://www.springframework.org/schema/p"
      xsi:schemaLocation="http://www.springframework.org/schema/beans   
         http://www.springframework.org/schema/beans/spring-beans.xsd">
    <bean id="Son" class="configrelax.Son" scope="prototype"/>
    <bean id="man" class="configrelax.man"/>
     </beans>

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、<bean…/>中添加<lookup-method…/>说明去实现哪个抽象方法,有两个属性
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

<?xml version="1.0" encoding="UTF-8"?>
   <beans xmlns="http://www.springframework.org/schema/beans"
   xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
   xmlns:p="http://www.springframework.org/schema/p"
      xsi:schemaLocation="http://www.springframework.org/schema/beans   
         http://www.springframework.org/schema/beans/spring-beans.xsd">
    <bean id="gunDog" class="configrelax.gunDog" scope="prototype"/>
    <bean id="Chinese" class="configrelax.Chinese">
     <lookup-method name="getDog" bean="gunDog"/>
     </bean>
     </beans>

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

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值