java Prototype Bean 没有按预期自动装配
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/36278595/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Prototype Bean doesn't get autowired as expected
提问by Kim
TestController.java
测试控制器.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
测试类.java
@Component
@Scope("prototype")
public class TestClass {
public TestClass() {
System.out.println("new test class constructed.");
}
public void doSomething() {
}
}
As you can see, I'm trying to figure out whether a new TestClass
has been injected when visit "xxx/test". "new test class constructed."
got printed only once(first time I triggered "xxx/test") while I was expecting it printed equally. Is that mean @Autowired
object can only be @Singleton
? How does @Scope
work then?
正如你所看到的,我试图弄清楚TestClass
在访问“xxx/test”时是否注入了一个新的。"new test class constructed."
只打印了一次(我第一次触发“xxx/test”),而我期待它同样打印。这意味着@Autowired
对象只能是@Singleton
吗?那怎么操作@Scope
呢?
EDIT:
编辑:
TestController.java
测试控制器.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());
}
}
I tried @Valerio Vaudi
solution, registered as Scope(scopeName = "request")
. Here is the three time result when I visit "xxx/test"
我尝试了@Valerio Vaudi
解决方案,注册为Scope(scopeName = "request")
. 这是我访问“xxx/test”时的三次结果
(first time)
(第一次)
- new test class constructed.
- null
- 构建了新的测试类。
- 空值
(second)
(第二)
- null
- 空值
(third)
(第三)
- null
- 空值
I don't understand why the result is null since it doens't reconstruct a new one each time I use it.
我不明白为什么结果为空,因为我每次使用它时都不会重建一个新的结果。
Then I tried @Nikolay Rusev
solution @Scope("prototype")
:
然后我尝试了@Nikolay Rusev
解决方案@Scope("prototype")
:
(first)
(第一的)
- new one constructed.
- new one constructed.
- null
- 新建造的。
- 新建造的。
- 空值
(second)
(第二)
- new one constructed.
- new one constructed.
- null
- 新建造的。
- 新建造的。
- 空值
(third)
(第三)
- new one constructed.
- new one constructed.
- null
- 新建造的。
- 新建造的。
- 空值
This is rather easy to understand since each time I use it(TestClass), Spring auto-regenerate a new instance of it. But the first scene I still cannot understand since it seems to retain only one new instance for each request.
这很容易理解,因为每次我使用它(TestClass)时,Spring 都会自动重新生成它的一个新实例。但是第一个场景我仍然无法理解,因为它似乎只为每个请求保留一个新实例。
The real purpose is:In each request lifecycle, a new testClass
is required(if needed), and only one is required. At this moment it seems only ApplicationContext
solution is feasible(which I already knew), but I just want to know if this could be done automatically by using @Component
+ @Scope
+ @Autowired
.
真正的目的是:在每个请求生命周期中,testClass
都需要一个new (如果需要),并且只需要一个。在这一刻,似乎只有ApplicationContext
可行的解决方案(我已经知道了),但我只是想知道,如果这可以通过使用自动完成@Component
+ @Scope
+ @Autowired
。
回答by Nikolay Rusev
all the answers above are correct. Controller by default is singleton
and the injected testClass
is instantiated once, because default scoped proxy mode is DEFAULT
from spring doc.
以上所有答案都是正确的。默认情况下控制器是singleton
并且注入的testClass
被实例化一次,因为默认范围代理模式DEFAULT
来自spring doc。
public abstract ScopedProxyMode proxyMode Specifies whether a component should be configured as a scoped proxy and if so, whether the proxy should be interface-based or subclass-based. Defaults to ScopedProxyMode.DEFAULT, which typically indicates that no scoped proxy should be created unless a different default has been configured at the component-scan instruction level.
Analogous to support in Spring XML.
See Also: ScopedProxyMode Default: org.springframework.context.annotation.ScopedProxyMode.DEFAULT
public abstract ScopedProxyMode proxyMode 指定是否应将组件配置为范围代理,如果是,则代理应基于接口还是基于子类。默认为 ScopedProxyMode.DEFAULT,这通常表示不应创建作用域代理,除非在组件扫描指令级别配置了不同的默认值。
类似于 Spring XML 中的支持。
另请参见:ScopedProxyMode 默认值:org.springframework.context.annotation.ScopedProxyMode.DEFAULT
if you want new instance to be injected every time you need, you should change your TestClass
to :
如果您希望每次需要时都注入新实例,您应该将您的更改TestClass
为:
@Component
@Scope(value="prototype", proxyMode=ScopedProxyMode.TARGET_CLASS)
public class TestClass {
public TestClass() {
System.out.println("new test class constructed.");
}
public void doSomething() {
}
}
with this additional configuration the injected testClass
will not be really a TestClass
bean but proxy to TestClass
bean and this proxy will understand the prototype
scope and will return new instance every time is needed.
有了这个额外的配置,注入的testClass
将不是一个真正的TestClass
bean,而是 bean 的代理TestClass
,这个代理将理解prototype
范围并在每次需要时返回新实例。
回答by Alex Salauyou
As mentioned, controller is by default singleton, that's why instantiation and injection of TestClass
is performed only once on its creation.
如前所述,控制器默认是单例的,这就是为什么TestClass
在其创建时只执行一次实例化和注入。
Solution can be to inject application context and get the bean manually:
解决方案可以是注入应用程序上下文并手动获取 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();
}
}
Now, when a TestClass
bean is requested, Spring knowing that it is @Prototype
, will create a new instance and return it.
现在,当TestClass
请求一个bean时,Spring 知道它是@Prototype
,将创建一个新实例并返回它。
Another solution is to make the controller @Scope("prototype")
.
另一个解决方案是制作控制器@Scope("prototype")
。
回答by Cootri
Spring controllers are singletons by default (which is OK due to their stateless nature), as well as the other Spring beans.
Spring 控制器在默认情况下是单例的(由于它们的无状态性质,这是可以的),以及其他 Spring bean。
That's why it is enough to instantiate only one TestClass
instance for the only TestController
instance.
这就是为什么TestClass
对于唯一TestController
实例仅实例化一个实例 就足够了。
It is easy to instantiate TestClass
one more time - just inject it in another controller or get from the context programmatically
再次实例化很容易TestClass
- 只需将其注入另一个控制器或以编程方式从上下文中获取
回答by Matteo Baldi
You cannot autowire prototype bean (well, you can but the bean will be always the same)... autowire the ApplicationContext and get an instance of the needed prototype bean manually (for example in the constructor):
您不能自动装配原型 bean(好吧,您可以,但 bean 将始终相同)...自动装配 ApplicationContext 并手动获取所需原型 bean 的实例(例如在构造函数中):
TestClass test = (TestClass) context.getBean("nameOfTestClassBeanInConfiguration");
In this way you're sure of getting a new instance of TestClass.
通过这种方式,您肯定会获得一个新的 TestClass 实例。
回答by Valerio Vaudi
The key point hear is that the restController bean is a singleton and Spring will create only one instance of that bean during the creation of bean.
听到的关键点是 restController bean 是一个单例,Spring 将在创建 bean 期间只创建该 bean 的一个实例。
When you impose a prototype bean scope Spring will instance a new bean for every DI point. In other words if you configure a bean a two or n-times via xml or java-config this bean will have a fresh instance of your prototype-scoped bean.
当您强加原型 bean 范围时,Spring 将为每个 DI 点实例一个新 bean。换句话说,如果你通过 xml 或 java-config 配置一个 bean 两次或 n 次,这个 bean 将有一个新的原型作用域 bean 实例。
In your case you use the annotation style that actually is the default way for web layer starting spring 3.x.
在您的情况下,您使用的注释样式实际上是 web 层从 spring 3.x 开始的默认方式。
One possibility to inject a fresh bean may be achieved with a bean scoped on the session, but in my opinion if your use case is a rest WS that I consider stateless, the session use in my opinion is a bad choice.
注入新 bean 的一种可能性可以通过会话范围内的 bean 来实现,但在我看来,如果您的用例是我认为无状态的休息 WS,那么在我看来,会话使用是一个糟糕的选择。
A solution of your case may be use request scope.
您的情况的解决方案可能是使用请求范围。
UpdateI write also just a simple example
更新我也写了一个简单的例子
@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());
}
}
the my pom.xml
我的 pom.xml
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.example</groupId>
<artifactId>demo</artifactId>
<version>0.0.1-SNAPSHOT</version>
<packaging>jar</packaging>
<name>demo</name>
<description>Demo project for Spring Boot</description>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>1.3.3.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<java.version>1.8</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
the bowser screen:
鲍泽屏幕:
and the my log screen:
和我的日志屏幕:
I don't know why it don't work for you probably you had forgot some configuration.
我不知道为什么它对你不起作用,可能你忘记了一些配置。
I hope that this more detalied solution can help you to understand how solve the your problem
我希望这个更详细的解决方案可以帮助您了解如何解决您的问题