Prototype Bean无法按预期自动assembly
TestController.java
@RestController public class TestController { @Autowired private TestClass testClass; @RequestMapping(value = "/test", method = RequestMethod.GET) public void testThread(HttpServletResponse response) throws Exception { testClass.doSomething(); } }
TestClass.java
@Component @Scope("prototype") public class TestClass { public TestClass() { System.out.println("new test class constructed."); } public void doSomething() { } }
正如您所看到的,我正在尝试确定在访问“xxx / test”时是否注入了新的TestClass
。 "new test class constructed."
只打印一次(我第一次触发“xxx / test”),而我期待它同样打印。 这是否意味着@Autowired
对象只能是@Singleton
? @Scope
如何工作呢?
编辑:
TestController.java
@RestController public class TestController { @Autowired private TestClass testClass; @RequestMapping(value = "/test", method = RequestMethod.GET) public void testThread(HttpServletResponse response) throws Exception { testClass.setProperty("hello"); System.out.println(testClass.getProperty()); } }
我尝试了@Valerio Vaudi
解决方案,注册为Scope(scopeName = "request")
。 这是我访问“xxx / test”时的三次结果
(第一次)
- 新的测试类构建。
- 空值
(第二)
- 空值
(第三)
- 空值
我不明白为什么结果为null,因为每次使用它都不会重建一个新结果。
然后我尝试了@Nikolay Rusev
解决方案@Scope("prototype")
:
(第一)
- 新建的。
- 新建的。
- 空值
(第二)
- 新建的。
- 新建的。
- 空值
(第三)
- 新建的。
- 新建的。
- 空值
这很容易理解,因为每次我使用它(TestClass)时,Spring都会自动重新生成它的新实例。 但是第一个场景我仍然无法理解,因为它似乎只为每个请求保留了一个新实例。
真正的目的是:在每个请求生命周期中,需要一个新的testClass
(如果需要),并且只需要一个。 此时似乎只有ApplicationContext
解决方案是可行的(我已经知道),但我只想知道是否可以使用@Component
+ @Scope
+ @Autowired
自动完成。
以上所有答案都是正确的。 默认情况下,控制器是singleton
,注入的testClass
实例化一次,因为默认的作用域代理模式是来自spring doc的 DEFAULT
。
public abstract ScopedProxyMode proxyMode指定是否应将组件配置为作用域代理,如果是,则指定代理应基于接口还是基于子类。 默认为ScopedProxyMode.DEFAULT,它通常表示除非在组件扫描指令级别配置了不同的默认值,否则不应创建范围代理。
类似于Spring XML中的支持。
另请参见:ScopedProxyMode默认值:org.springframework.context.annotation.ScopedProxyMode.DEFAULT
如果您希望每次需要时都注入新实例,则应将TestClass
更改为:
@Component @Scope(value="prototype", proxyMode=ScopedProxyMode.TARGET_CLASS) public class TestClass { public TestClass() { System.out.println("new test class constructed."); } public void doSomething() { } }
使用这个额外的配置,注入的testClass
将不是真正的TestClass
bean,而是TestClass
bean的代理,这个代理将理解prototype
范围,并且每次都需要返回新实例。
Spring控制器默认是单例(由于它们的无状态特性,这是正常的),以及其他Spring bean。
这就是为什么它只足以为唯一的TestController
实例实例化一个TestClass
实例。
很容易再次实例化TestClass
– 只需将其注入另一个控制器或以编程方式从上下文中获取
如前所述,控制器默认是单例,这就是为什么在创建时只执行一次TestClass
实例化和注入。
解决方案可以是注入应用程序上下文并手动获取bean:
@RestController public class TestController { @Autowired ApplicationContext ctx; @RequestMapping(value = "/test", method = RequestMethod.GET) public void testThread(HttpServletResponse response) throws Exception { ((TestClass) ctx.getBean(TestClass.class)).doSomething(); } }
现在,当请求TestClass
bean时,Spring知道它是@Prototype
,将创建一个新实例并返回它。
另一种解决方案是制作控制器@Scope("prototype")
。
你不能自动assembly原型bean(嗯,你可以,但bean将始终是相同的)…自动assemblyApplicationContext并手动获取所需原型bean的实例(例如在构造函数中):
TestClass test = (TestClass) context.getBean("nameOfTestClassBeanInConfiguration");
通过这种方式,您可以确保获得TestClass的新实例。
关键点是,restController bean是一个单例,Spring在创建bean时只会创建该bean的一个实例。
当您施加原型bean范围时,Spring将为每个DI点实例化一个新bean。 换句话说,如果你通过xml或java-config配置bean两次或者n次,那么这个bean将拥有一个原型范围的bean的新实例。
在您的情况下,您使用注释样式,实际上是Web层启动spring 3.x的默认方式。
注入一个新bean的一种可能性可以通过会话中的bean来实现,但在我看来,如果你的用例是我认为无状态的restWS,那么在我看来会话使用是一个糟糕的选择。
您的案例的解决方案可能是使用请求范围。
更新我写的只是一个简单的例子
@SpringBootApplication public class DemoApplication { public static void main(String[] args) { SpringApplication.run(DemoApplication.class, args); } @Bean @Scope(scopeName = "request",proxyMode = ScopedProxyMode.TARGET_CLASS) public RequestBeanTest requestBeanTest(){ return new RequestBeanTest(); } } class RequestBeanTest { public RequestBeanTest(){ Random random = new Random(); System.out.println(random.nextGaussian()); System.out.println("new object was created"); } private String prop; public String execute(){ return "hello!!!"; } public String getProp() { return prop; } public void setProp(String prop) { this.prop = prop; } } @RestController class RestTemplateTest { @Autowired private RequestBeanTest requestBeanTest; @RequestMapping("/testUrl") public ResponseEntity responseEntity(){ requestBeanTest.setProp("test prop"); System.out.println(requestBeanTest.getProp()); return ResponseEntity.ok(requestBeanTest.execute()); } }
我的pom.xml
4.0.0 com.example demo 0.0.1-SNAPSHOT jar demo Demo project for Spring Boot org.springframework.boot spring-boot-starter-parent 1.3.3.RELEASE UTF-8 1.8 org.springframework.boot spring-boot-starter org.springframework.boot spring-boot-starter-actuator org.springframework.boot spring-boot-starter-web org.springframework.boot spring-boot-starter-test test org.springframework.boot spring-boot-maven-plugin
bowser屏幕:
和我的日志屏幕:
我不知道为什么它不适合你,可能你忘记了一些配置。
我希望这个更加严格的解决方案可以帮助您了解如何解决您的问题