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() {
}
}
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?
EDIT:
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());
}
}
I tried @Valerio Vaudi
solution, registered as Scope(scopeName = "request")
. Here is the three time result when I visit "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")
:
(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.
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
.
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.
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
if you want new instance to be injected every time you need, you should change your TestClass
to :
@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.
As mentioned, controller is by default singleton, that's why instantiation and injection of TestClass
is performed only once on its creation.
Solution can be to inject application context and get the bean manually:
@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.
Another solution is to make the controller @Scope("prototype")
.
Spring controllers are singletons by default (which is OK due to their stateless nature), as well as the other Spring beans.
That's why it is enough to instantiate only one TestClass
instance for the only TestController
instance.
It is easy to instantiate TestClass
one more time - just inject it in another controller or get from the context programmatically
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):
TestClass test = (TestClass) context.getBean("nameOfTestClassBeanInConfiguration");
In this way you're sure of getting a new instance of TestClass.
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.
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.
In your case you use the annotation style that actually is the default way for web layer starting 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.
A solution of your case may be use request scope.
Update I 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
<?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
来源:https://stackoverflow.com/questions/36278595/prototype-bean-doesnt-get-autowired-as-expected