Spring-Bean的作用域-Prototype

Spring-Bean的作用域-Prototype

一、Prototype

非单一原型范围导致每次对特定 bean 发出请求时都会创建一个新 bean 实例。也就是说,bean 被注入到另一个 bean 中,或者通过getBean()容器上的方法调用来请求它,都是返回一个全新的对象实例。

通常,应该对所有有状态 bean 使用原型作用域,对无状态 bean 使用单例作用域。

与其他作用域相比,Spring并不管理原型bean的完整生命周期。容器实例化、配置和组装一个原型对象,并将其传递给客户端,而不需要进一步缓存该原型实例。因此,尽管初始化生命周期回调方法在所有对象上都被调用,而不管作用域如何,但在原型的情况下,配置的销毁回调方法不会被调用。客户端代码必须清理原型作用域对象,并释放原型bean所持有的资源。要让Spring容器释放由原型作用域bean持有的资源,需要使用自定义bean后处理器,它保存了对需要清理的bean的引用。

看起来,似乎比单例模式要复杂,因为这种方式会不断创建实例,如果不进行清除,最终会导致资源耗尽

二、应用场景

  • 代替new 实例化对象
  • 资源不可共享,保证资源的安全性(某些情况下,需要每次获得处于某个状态的对象,但在每次使用后状态会发生变化)

三、基于XML

声明一个实体类


<bean id="prototypeEntity" class="com.example.PrototypeEntity" scope="prototype"/>

请求多个实例

<bean id="prototypeService" class="com.example.PrototypeService">
	<property name="one" ref="prototypeEntity"/>
	<property name="two" ref="prototypeEntity"/>
	<property name="three" ref="prototypeEntity"/>
bean>

这样子将获得三个不同的实例

这里需要注意的是:对于大局来说,每个请求获得的仍是一个实例,因为prototypeService是单例的

四、基于注解

创建一个实体类(或者其他有状态的对象)

public class PrototypeEntity {

    private int flag = 0;

    public int getFlag() {
        return flag;
    }

    public void setFlag(int flag) {
        this.flag = flag;
    }

}

创建service类,这个类中有一个属性,以及一个方法,每次调用这个方法将会进行自增。并声明他的prototype作用域

@Service
public class PrototypeService {

    @Autowired
    private PrototypeEntity one;
    @Autowired
    private PrototypeEntity two;
    @Autowired
    private PrototypeEntity three;

    public String getProto() {
        one.setFlag(one.getFlag() + 1);
        two.setFlag(two.getFlag() + 1);
        three.setFlag(three.getFlag() + 1);
        return "one:"+one.getFlag()+"\ntwo:"+two.getFlag()+"three:"+three.getFlag()+"\n";
    }

  /**
     * 注册一个作用域为prototype的bean
     * @return
     */
    @Bean
    @Scope("prototype")
    public PrototypeEntity prototypeEntity(){
        return new PrototypeEntity();
    }
}

在Controller进行请求注入Service

@RestController
public class PrototypeController {

    @Autowired
    private PrototypeService prototypeService;

    @RequestMapping("/getProto")
    public String getProto(){
       return  prototypeService.getProto();
    }
}

最后启动项目,在浏览器访问 http://ip:port/getProto ,看到浏览器返回的内容也许会有疑惑,“这不还是会在原有基础上自增吗?”

没错,这也就是前面所提到的在大局上来说,每个PrototypeEntity仍是单例的。(这涉及到单例注入原型的问题,以后会说明)

如果还察觉不出和Singleton的区别,你可以尝试把@Scope(“prototype”)去掉

	/**
	* 单例bean
	*/
	@Bean
    public PrototypeEntity prototypeEntity(){
        return new PrototypeEntity();
    }

将会以+3的形式递增,因为每次请求的是同一个实例

可以将Controller和Server的作用域都声明为原型,这样每次整条链路都是新的实例,更多的应用可以自由发挥。

最后

当使用具有对原型 bean 的依赖的单例作用域 bean 时,请注意在实例化时解析依赖关系。因此,如果您将原型范围的 bean 依赖注入到单例范围的 bean 中时,则会实例化一个新的原型 bean,然后将依赖项注入到单例 bean 中。原型实例是唯一提供给单例作用域 bean 的实例。

但是,假设希望单例范围的 bean 在运行时重复获取原型范围的 bean 的新实例。不能将原型范围的 bean 依赖注入到您的单例 bean 中,因为该注入仅发生一次,当 Spring 容器实例化单例 bean 并解析并注入其依赖项时。(想在单例bean中获取原型的bean可以通过方法注入的方式去使得每次获取的原型bean是一个新的)

你可能感兴趣的:(后端,Spring,Boot,代码案例,spring,原型模式,java)