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."
_は1回だけ印刷されました(初めて "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());
}
}
_
Scope(scopeName = "request")
として登録された_@Valerio Vaudi
_ソリューションを試しました。 「xxx/test」にアクセスしたときの3回の結果を次に示します
(初めて)
(秒)
(三番)
使用するたびに新しい結果が再構築されないため、結果がnullになる理由がわかりません。
次に、_@Nikolay Rusev
_ solution @Scope("prototype")
を試しました:
(最初)
(秒)
(三番)
これは、使用するたびに(TestClass)、Springが新しいインスタンスを自動再生成するため、かなり理解しやすいです。しかし、リクエストごとに新しいインスタンスを1つしか保持していないように見えるため、最初のシーンはまだ理解できません。
実際の目的は:各リクエストのライフサイクルで、新しいtestClass
が必要であり(必要な場合)、1つだけが必要です。現時点ではApplicationContext
解しか実現できないようですが(これは既に知っています)、_@Component
_ + _@Scope
_ + _@Autowired
_。
上記の答えはすべて正しいです。デフォルトのスコーププロキシモードはsingleton
from spring doc であるため、コントローラーはデフォルトでtestClass
であり、挿入されたDEFAULT
は1回インスタンス化されます。
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
スコープを理解して戻ります。毎回新しいインスタンスが必要です。
前述のように、コントローラーはデフォルトでシングルトンであるため、TestClass
のインスタンス化と挿入は、作成時に1回だけ実行されます。
解決策は、アプリケーションコンテキストを注入し、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")
にすることです。
Springコントローラーは、デフォルトではシングルトンであり(ステートレスであるため、問題ありません)、他のSpring Beanも同様です。
そのため、TestClass
インスタンスのみに対してTestController
インスタンスを1つだけインスタンス化するだけで十分です。
もう一度TestClass
をインスタンス化するのは簡単です-別のコントローラーに挿入するか、プログラムからコンテキストから取得するだけです
重要な点は、restController Beanはシングルトンであり、SpringはBeanの作成中にそのBeanのインスタンスを1つだけ作成するということです。
プロトタイプBeanスコープを課すと、SpringはDIポイントごとに新しいBeanをインスタンス化します。つまり、xmlまたはJava-configを使用してBeanを2回またはn回構成すると、このBeanにはプロトタイプスコープBeanの新しいインスタンスが含まれます。
あなたの場合、Spring 3.xから始まるWebレイヤーの実際のデフォルトの方法である注釈スタイルを使用します。
新しいBeanを注入する可能性の1つは、セッションでスコープされたBeanで達成される可能性がありますが、私の意見では、ユースケースがステートレスと見なすWSである場合、私の意見ではセッションの使用は悪い選択です。
あなたのケースの解決策は、使用リクエストのスコープかもしれません。
Update私も簡単な例を書いています
@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>
クッパ画面:
と私のログ画面:
なぜ設定が機能しなかったのかわかりませんが、おそらく設定を忘れていたのでしょう。
このより詳細なソリューションが、問題の解決方法を理解するのに役立つことを願っています
プロトタイプBeanを自動配線することはできません(ただし、Beanは常に同じです)... ApplicationContextを自動配線し、必要なプロトタイプBeanのインスタンスを手動で(たとえば、コンストラクターで)取得します。
TestClass test = (TestClass) context.getBean("nameOfTestClassBeanInConfiguration");
この方法で、TestClassの新しいインスタンスを確実に取得できます。