Prototype Bean没有按预期自动装配

Kim*_*Kim 25 java spring spring-mvc spring-web

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();
    }
}
Run Code Online (Sandbox Code Playgroud)

TestClass.java

@Component
@Scope("prototype")
public class TestClass {

    public TestClass() {
        System.out.println("new test class constructed.");
    }

    public void doSomething() {

    }

}
Run Code Online (Sandbox Code Playgroud)

正如你所看到的,我正在试图弄清楚TestClass当访问"xxx/test"时是否注入了新的."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());
    }
}
Run Code Online (Sandbox Code Playgroud)

我试过@Valerio Vaudi解决方案,注册为Scope(scopeName = "request").这是我访问"xxx/test"时的三次结果

(第一次)

  • 新的测试类构建.
  • 空值

(第二)

  • 空值

(第三)

  • 空值

我不明白为什么结果为null,因为每次使用它都不会重建一个新结果.

然后我试着@Nikolay Rusev解决方案@Scope("prototype"):

(第一)

  • 新建的.
  • 新建的.
  • 空值

(第二)

  • 新建的.
  • 新建的.
  • 空值

(第三)

  • 新建的.
  • 新建的.
  • 空值

这很容易理解,因为每次使用它(TestClass)时,Spring都会自动重新生成它的新实例.但是第一个场景我仍然无法理解,因为它似乎只为每个请求保留了一个新实例.

真正的目的是:在每个请求生命周期中,testClass需要一个新的(如果需要),并且只需要一个.此时似乎只有ApplicationContext解决方案是可行的(我已经知道),但我只是想知道这是否可以通过使用@Component+ @Scope+ 自动完成@Autowired.

Nik*_*sev 23

以上所有答案都是正确的.默认情况下singleton,控制器是,并且注入testClass实例化一次,因为默认范围代理模式DEFAULT来自spring doc.

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() {

    }

}
Run Code Online (Sandbox Code Playgroud)

使用这个额外的配置,注入的testClass不是TestClassbean,而是bean的代理TestClass,这个代理将理解prototype范围,并且每次都需要返回新的实例.


Ale*_*you 5

如前所述,控制器默认为单例,这就是为什么实例化和注入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();
    }
}
Run Code Online (Sandbox Code Playgroud)

现在,当TestClass请求一个bean 时,Spring知道它是@Prototype,将创建一个新实例并返回它。

另一个解决方案是制造控制器@Scope("prototype")


Coo*_*tri 4

默认情况下,Spring 控制器是单例(由于其无状态性质,这是可以的),其他 Spring bean 也是如此。

TestClass这就是为什么只为唯一 实例实例化一个实例就足够了TestController

TestClass再次实例化很容易- 只需将其注入另一个控制器或以编程方式从上下文中获取