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"
(第一次)
(第二)
(第三)
我不明白为什么结果为空,因为每次使用它都会重建一个新结果。
然后我尝试了@Nikolay Rusev
解决方案@Scope("prototype")
:
(第一)
(第二)
(第三)
这很容易理解,因为每次使用它(TestClass)时,Spring都会自动重新生成它的新实例。但是第一个场景我仍然无法理解,因为它似乎只为每个请求保留了一个新实例。
真正的目的是:在每个请求生命周期中,都需要一个新的testClass
(如果需要),只需要一个。{}此时似乎只有ApplicationContext
解决方案是可行的(我已经知道),但我只想知道是否可以使用@Component
+ @Scope
+ {{1 }}
答案 0 :(得分:23)
以上所有答案都是正确的。控制器默认为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
范围并将返回新的每次都需要实例。
答案 1 :(得分:4)
默认情况下,Spring控制器是单例(由于它们没有状态,这是正常的),以及其他Spring bean。
这就是仅仅为column4 = Count(column1+column2)
实例仅实例化一个TestClass
实例的原因。
很容易再次实例化TestController
- 只需将其注入另一个控制器或以编程方式从上下文中获取
答案 2 :(得分:4)
如前所述,控制器默认为单例,这就是为什么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")
。
答案 3 :(得分:2)
你不能自动装配原型bean(好吧,你可以但bean总是一样的)...自动装配ApplicationContext并手动获取所需原型bean的实例(例如在构造函数中):
TestClass test = (TestClass) context.getBean("nameOfTestClassBeanInConfiguration");
通过这种方式,您可以确保获得TestClass的新实例。
答案 4 :(得分:2)
关键点是,restController bean是一个单例,Spring在创建bean时只会创建该bean的一个实例。
当您施加原型bean范围时,Spring将为每个DI点实例化一个新bean。换句话说,如果你通过xml或java-config配置bean两次或者n次,那么这个bean将有一个原型范围bean的新实例。
在您的情况下,您使用的注释样式实际上是Web图层启动弹簧3.x的默认方式。
注入一个新bean的一种可能性可以通过会话中的bean来实现,但在我看来,如果你的用例是我认为无状态的休息WS,那么在我看来会话使用是一个糟糕的选择。 / p>
您的案例的解决方案可能是使用请求范围。
<强>更新强> 我还写了一个简单的例子
@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());
}
}
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>
bowser screen:
和我的日志屏幕:
我不知道为什么它不适合你,可能你忘记了一些配置。
我希望这个更加严谨的解决方案可以帮助您了解如何解决您的问题