Spring原型范围是否与CDI相关范围相同。
谷歌搜索引导我发布声称他们是相同的博客文章和其他声称他们相似但不完全相同而没有解释差异的人。
那么弹簧原型范围和cdi依赖范围之间有什么区别?
答案 0 :(得分:5)
根据CDI documentation和javadoc
当声明bean具有@Dependent范围时:
- 不会在多个之间共享bean的注入实例 注射点。
- ...
非单例,bean部署的原型范围导致了 每次请求特定时,都会创建一个新的bean实例 豆是制作的。
他们在行为上是一样的。
我能找到的唯一区别是bean的生命周期。 In Spring
因此,尽管调用了初始化生命周期回调方法 所有对象,无论范围如何,在原型的情况下都配置为 不会调用销毁生命周期回调。客户端代码必须 清理原型范围的对象并释放昂贵的资源 原型豆正在举行。
In CDI however容器管理bean的整个生命周期,直接在它作为方法调用参数注入时或在销毁注入的bean时间接管理。这些条件都在链接的文档中描述。
正如Luiggi在评论中提到的,重要的是要注意bean声明的默认范围。 In the Spring docs state
单例范围是默认范围[...]
答案 1 :(得分:0)
我认为正确的答案是:这取决于使用的proxyMode
!
Spring的prototype
范围在ScopedProxyMode.NO
(通常等于ScopedProxyMode.DEFAULT
,除非在组件扫描指令级别配置了不同的默认值)和{ {1}}(或ScopedProxyMode.TARGET_CLASS
)。
在Spring中,当ScopedProxyMode.INTERFACES
用prototype
声明了作用域bean时,其行为实际上与CDI的默认作用域ScopedProxyMode.NO
几乎相同。唯一的区别是生命周期管理。在春季,“未调用已配置的销毁生命周期回调”,而CDI管理@Dependent
bean的完整生命周期。
但是,当@Dependent
用prototype
(或ScopedProxyMode.TARGET_CLASS
)声明了范围的bean时,则在Spring中,它的行为与CDI的ScopedProxyMode.INTERFACES
范围完全不同。 @Dependent
bean不以任何方式绑定到注入它们的实例的生命周期。如果将此类原型bean注入到singleton bean中,则会代之以注入代理,并且每次调用任何代理方法时都会创建新的bean实例!
这是什么意思?
看下面的代码:
prototype
一次调用public static class MyBean {
public MyBean() {
System.out.println(MyBean.class.getSimpleName() + " " + this + " created!");
}
public void doSomething() {
System.out.println(MyBean.class.getSimpleName() + " " + this + " doSomething() invoked!");
}
}
@Bean
@Scope(value = "prototype", proxyMode = ScopedProxyMode.TARGET_CLASS)
public MyBean myBean() {
return new MyBean();
}
@RestController
public static class MyController {
@Autowired
private MyBean myBean;
@GetMapping("/hello")
public String hello() {
myBean.doSomething();
myBean.doSomething();
myBean.doSomething();
return "Hello from " + MyController.class.getSimpleName();
}
}
方法将创建多少个myBean
实例?你觉得一个吗?没有!将为一个控制器的方法调用创建MyController.hello()
的三个实例。每次调用myBean
时。一次myBean.doSomething();
的调用将显示如下内容:
MyController.hello()
所有6行仅用于一个调用MyBean demo.DemoApplication$MyBean@30853c6d created!
MyBean demo.DemoApplication$MyBean@30853c6d doSomething() invoked!
MyBean demo.DemoApplication$MyBean@a0b81cf created!
MyBean demo.DemoApplication$MyBean@a0b81cf doSomething() invoked!
MyBean demo.DemoApplication$MyBean@4caa7eb1 created!
MyBean demo.DemoApplication$MyBean@4caa7eb1 doSomething() invoked!
。
它确实与CDI的MyController.hello()
范围不同,在CDI的范围中,@Dependent
的注入实例将为每个要注入的实例创建一次。它甚至与Spring的MyBean
的{{1}}范围也不同。
例如,如果您创建另一个控制器prototype
,其代码等于ScopedProxyMode.NO
(请记住,Spring控制器始终具有MyController2
的作用域)并将MyController
的声明更改为:
singleton
然后,Spring将仅创建MyBean
的两个实例-每个控制器一个。两者都将与控制器一起创建。在应用程序启动时,您将看到类似这样的内容:
@Bean
@Scope(value = "prototype", proxyMode = ScopedProxyMode.NO)
public MyBean myBean() {
return new MyBean();
}
在每个调用MyBean
方法时(每次MyBean demo.DemoApplication$MyBean@4e1800d0 created!
MyBean demo.DemoApplication$MyBean@5e5cedf8 created!
实例都一样),如下所示:
MyController.hello()
在每个调用MyBean
方法时(每次MyBean demo.DemoApplication$MyBean@4e1800d0 doSomething() invoked!
MyBean demo.DemoApplication$MyBean@4e1800d0 doSomething() invoked!
MyBean demo.DemoApplication$MyBean@4e1800d0 doSomething() invoked!
实例都一样),如下所示:
MyController2.hello()
因此,从这个意义上讲,它实际上不是“原型” bean。它更像是没有范围的bean-它的范围完全取决于封装bean(控制器)的范围。就像CDI的MyBean
范围一样。
请记住:默认情况下,以代理模式MyBean demo.DemoApplication$MyBean@5e5cedf8 doSomething() invoked!
MyBean demo.DemoApplication$MyBean@5e5cedf8 doSomething() invoked!
MyBean demo.DemoApplication$MyBean@5e5cedf8 doSomething() invoked!
创建的Spring @Dependent
bean与prototype
相同。
通常,这不是您希望从名为ScopedProxyMode.DEFAULT
的范围中获得的。还有其他方法可以将原型bean的新副本注入到范围更广的bean(即ScopedProxyMode.NO
)中。其中之一:使用prototype
。
通过singleton
,您可以在ObjectProvider
中拥有原型bean,但可以将ObjectProvider
而不是仅ScopedProxyMode.NO
注入控制器。
这样,您可以使用控制器方法,该方法每次调用ObjectProvider<MyBean> myBeanProvider
时都会获得MyBean myBean
的新副本:
MyBean
此代码将打印:
myBeanProvider.getObject()
调用@RestController
public static class MyController {
@Autowired
private ObjectProvider<MyBean> myBeanProvider;
@GetMapping("/hello")
public String hello() {
MyBean myBean = myBeanProvider.getObject();
myBean.doSomething();
myBean.doSomething();
myBean.doSomething();
return "Hello from " + MyController.class.getSimpleName();
}
}
方法。
请注意:MyBean demo.DemoApplication$MyBean@52689e05 created!
MyBean demo.DemoApplication$MyBean@52689e05 doSomething() invoked!
MyBean demo.DemoApplication$MyBean@52689e05 doSomething() invoked!
MyBean demo.DemoApplication$MyBean@52689e05 doSomething() invoked!
在MyController.hello()
的同一个实例中被调用了三次!
每次调用doSomething()
方法时,都会创建 {1>}的另一个实例-每次调用MyBean
时都会创建。
MyController.hello()
或MyBean
。myBeanProvider.getObject()
。@Scope(value = "prototype", proxyMode =
ScopedProxyMode.TARGET_CLASS)
并注入@Scope(value = "prototype", proxyMode =
ScopedProxyMode.INTERFACES)
而不是bean本身。