spring中bean的作用域

spring bean的作用域

 

先上配置文件和bean

spring_scope.xml

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:p="http://www.springframework.org/schema/p"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
       http://www.springframework.org/schema/beans/spring-beans-3.0.xsd">

    <bean id="xiaoming"
          class="beans.Singer"
          p:name="xiaoming"
          p:song="shihaofengqiu">

    </bean>

    <bean id="song"
          class="beans.Song"
          scope="prototype"
          >
        <property name="context" value="shihaofengqiu"/>
    </bean>


</beans>

Singer.java

package beans;

public class Singer {
    private String name;
    private String song;

    public Singer(){
        System.out.println("I'm a singer");

    }

    public void setName(String name) {
        this.name = name;
    }

    public void setSong(String song) {
        this.song = song;
    }

    public void play(){
        System.out.println("My name is:"+this.name);
        System.out.println("My song is:"+this.song);
    }

}

Song.java

package beans;

public class Song {
    private String context;
    public Song(){
        System.out.println("this is a song");
    }

    public void setContext(String context) {
        this.context = context;
    }
}

1.scope(只介绍singletonprototype)

       当一个bean的作用域时singleton的时候,ApplicationContext作为Spring Bean的工厂类,spring会在创建容器时就提前将bean的对象实例化,不管你是否使用它,它都都存在容器bean缓存中。即:

无论是在其他bean的创建过程的注入该bean,还是多次使用ApplicationContextgetbean方法返回对象实例,都是同一个对象实例,这与单例模式有些相似但不相同。

ApplicationContext ctx = new ClassPathXmlApplicationContext("spring_scope.xml");

创建ApplicationContext后,控制台输出:

但若在<bean/>中将lazy-init置“true”,则容器不会将bean提前实例化。

    <bean id="xiaoming"
          class="beans.Singer"
          p:name="xiaoming"
          p:song="shihaofengqiu"
          lazy-init="true">

    </bean>

  

        当一个bean的作用域是prototype时,表示一个bean对应多个对象实例,每次在注入另外一个bean或者调用ApplicationContextgetbean方法时,都会去创建一个新的对象,所以不同于singleton,在创建容器的时候不会预先对bean进行实例化。

 ApplicationContext ctx = new ClassPathXmlApplicationContext("spring_scope.xml");
        Song song1=ctx.getBean("song",Song.class);
        Song song2=ctx.getBean("song",Song.class);

结果为,创建了两个Song的实例(执行了两次构造方法)

2.

这样如果作用域为singletonbean注入作用域为singletonbean,或者prototypebean注入prototypebeanprototypebean注入singletonbean,都于我们从逻辑上都与我们想要的时一样的,但是当singletonbean注入prototypebean就有了一个问题;

将singer中的song属性改为Song对象的引用,并相应的修改xml配置文件:

执行代码:

ApplicationContext ctx = new ClassPathXmlApplicationContext("spring_scope.xml");
        Singer  xiaoming = ctx.getBean("xiaoming",Singer.class);
//        xiaoming.play();
        Singer xiaoming2 = ctx.getBean("xiaoming",Singer.class);

结果为:(song的构造函数执行了一次)

问题就出来了,我们将Song设置为prototype,但是因为注入Song的Singer的作用域为singleton,使得Song在容器创建时,Singer实例后注入的Song就无法创建多个对象,即我们想要的是我们多次创建Singer时返回的是单例的Singer,Singer中的song属性却可以是不同的Song实例。

spring官方文档:“A problem arises when the bean lifecycles are different. Suppose singleton bean A needs to use non-singleton (prototype) bean B, perhaps on each method invocation on A. The container creates the singleton bean A only once, and thus only gets one opportunity to set the properties. The container cannot provide bean A with a new instance of bean B every time one is needed.”

解决这个问题可以有下面的方法:

<1>继承ApplicationContextAware,ApplicationContextAware的子类中,可以注入ApplicationContext,这样就可以通过ApplicationContextsingletonbean获得prototype作用域的bean

CommandManager.java

 

package aware;

//import beans.Singer;
import beans.Song;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;

public class CommandManager implements ApplicationContextAware {
    private ApplicationContext applicationContext;

    public Song createSong(){
        return this.applicationContext.getBean("song",Song.class);
    }

    @Override
    public void setApplicationContext(ApplicationContext applicationContext) {
        this.applicationContext = applicationContext;
    }
}

 

修改singer类变为singer_2.java

package beans;

import aware.CommandManager;

public class Singer_2{
    private String name;
    private Song song;
    private CommandManager manager;

    public Singer_2(){
        System.out.println("I'm a singer");

    }

    public void setName(String name) {
        this.name = name;
    }

    public void setSong() {
        this.song = manager.createSong();
    }

    public void setManager(CommandManager manager) {
        this.manager = manager;
    }

    public void play(){
        System.out.println("My name is:"+this.name);
        System.out.println("My song is:"+this.song.getContext());
    }

}

xml配置文件中添加

 
<bean id="xiaoming_2"
          class="beans.Singer_2"
          p:name="xiaoming"
          p:manager-ref="manager"
          lazy-init="true"
          >

    </bean>

  <bean id="manager"
class="aware.CommandManager"/>
 

运行:

  ApplicationContext ctx = new ClassPathXmlApplicationContext("spring_scope.xml");
       Singer_2 xiaoming = ctx.getBean("xiaoming_2",Singer_2.class);
//       Singer xiaoming_2 = ctx.getBean("xiaoming",Singer.class);
        xiaoming.setSong();
        xiaoming.setSong()

可以通过setSong方法,在容器中放回不同的Song现象实例。

结果:

这样就可以实现不同作用域的bean的注入

<2>通过<look-method/>标签和抽象类与方法在singleton中注入prorotypebean,其中的abstract方法返回指定的bean

Singer_3.java

 

package beans;

public abstract class Singer_3 {
    private String name;
    private Song song;

    public Singer_3(){
    }

    public void setName(String name) {
        this.name = name;

    }

    public void setSong(){
        this.song=createSong();
    }

    public abstract Song createSong();

}

 

xml配置文件中添加:

  <bean id="xiaoming_3"
          class="beans.Singer_3"
          p:name="xiaoming">
        <lookup-method name="createSong" bean="song"/>
    </bean>

执行:

 ApplicationContext ctx = new ClassPathXmlApplicationContext("spring_scope.xml");
       Singer_3 xiaoming = ctx.getBean("xiaoming_3",Singer_3.class);
        xiaoming.setSong();
        xiaoming.setSong();

结果:

<3>通过注解省略<lookup-method/>的配置

  @Lookup("song")
    public abstract Song createSong();

xml配置文件中添加

<context:annotation-config/>

 

 

原文地址:https://www.cnblogs.com/Mrfanl/p/9768122.html