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屏幕:

在此处输入图像描述

和我的日志屏幕:

在此处输入图像描述

我不知道为什么它不适合你,可能你忘记了一些配置。

我希望这个更加严格的解决方案可以帮助您了解如何解决您的问题