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() {
}
}
Comme vous pouvez le voir, j'essaie de savoir si un nouveau TestClass
a été injecté lors de la visite "xxx/test". "new test class constructed."
A été imprimé une seule fois (la première fois que j'ai déclenché "xxx/test") alors que je m'attendais à ce qu'il soit imprimé également. Cela signifie-t-il que l'objet @Autowired
Ne peut être que @Singleton
? Comment fonctionne @Scope
?
MODIFIER:
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());
}
}
J'ai essayé la solution @Valerio Vaudi
, Enregistrée en tant que Scope(scopeName = "request")
. Voici le résultat trois fois lorsque je visite "xxx/test"
(première fois)
(seconde)
(troisième)
Je ne comprends pas pourquoi le résultat est nul car il n'en reconstruit pas un nouveau chaque fois que je l'utilise.
J'ai ensuite essayé la solution @Nikolay Rusev
@Scope("prototype")
:
(premier)
(seconde)
(troisième)
C'est assez facile à comprendre car chaque fois que je l'utilise (TestClass), Spring en régénère automatiquement une nouvelle instance. Mais la première scène que je ne comprends toujours pas car elle semble ne conserver qu'une seule nouvelle instance pour chaque requête.
Le véritable objectif est: Dans chaque cycle de vie de la demande, un nouveau testClass
est requis (si nécessaire), et un seul est requis. En ce moment, il semble que seule la solution ApplicationContext
soit faisable (que je connaissais déjà), mais je veux juste savoir si cela pourrait être fait automatiquement en utilisant @Component
+ @Scope
+ @Autowired
.
toutes les réponses ci-dessus sont correctes. Le contrôleur par défaut est singleton
et le testClass
injecté est instancié une fois, car le mode proxy de portée par défaut est DEFAULT
de doc de printemps .
public abstract ScopedProxyMode proxyMode Spécifie si un composant doit être configuré en tant que proxy de portée et, dans l'affirmative, si le proxy doit être basé sur une interface ou sur une sous-classe. La valeur par défaut est ScopedProxyMode.DEFAULT, ce qui indique généralement qu'aucun proxy de portée ne doit être créé, sauf si une valeur par défaut différente a été configurée au niveau de l'instruction d'analyse des composants.
Analogue à la prise en charge dans Spring XML.
Voir aussi: ScopedProxyMode Par défaut: org.springframework.context.annotation.ScopedProxyMode.DEFAULT
si vous voulez qu'une nouvelle instance soit injectée à chaque fois que vous en avez besoin, vous devez changer votre TestClass
en:
@Component
@Scope(value="prototype", proxyMode=ScopedProxyMode.TARGET_CLASS)
public class TestClass {
public TestClass() {
System.out.println("new test class constructed.");
}
public void doSomething() {
}
}
avec cette configuration supplémentaire, le testClass
injecté ne sera pas vraiment un bean TestClass
mais un proxy vers le bean TestClass
et ce proxy comprendra la portée prototype
et retournera une nouvelle instance à chaque fois est nécessaire.
Comme mentionné, le contrôleur est par défaut singleton, c'est pourquoi l'instanciation et l'injection de TestClass
n'est effectuée qu'une seule fois lors de sa création.
La solution peut être d'injecter le contexte de l'application et d'obtenir le bean manuellement:
@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();
}
}
Désormais, lorsqu'un bean TestClass
est demandé, Spring sachant qu'il s'agit de @Prototype
, Crée une nouvelle instance et la renvoie.
Une autre solution consiste à rendre le contrôleur @Scope("prototype")
.
Les contrôleurs Spring sont des singletons par défaut (ce qui est OK en raison de leur nature sans état), ainsi que les autres beans Spring.
C'est pourquoi il suffit d'instancier une seule instance TestClass
pour la seule instance TestController
.
Il est facile d'instancier TestClass
une fois de plus - il suffit de l'injecter dans un autre contrôleur ou de sortir du contexte par programmation
Le point clé à entendre est que le bean restController est un singleton et Spring ne créera qu'une seule instance de ce bean lors de la création du bean.
Lorsque vous imposez une portée de bean prototype, Spring instanciera un nouveau bean pour chaque point DI. En d'autres termes, si vous configurez un bean deux ou n fois via xml ou Java-config, ce bean aura une nouvelle instance de votre bean à portée de prototype.
Dans votre cas, vous utilisez le style d'annotation qui est en fait la méthode par défaut pour la couche Web à partir du printemps 3.x.
Une possibilité d'injecter un bean frais peut être obtenue avec un bean limité à la session, mais à mon avis, si votre cas d'utilisation est un reste WS que je considère comme apatride, l'utilisation de la session à mon avis est un mauvais choix.
Une solution de votre cas peut être d'utiliser la portée de la demande.
Mise à jour J'écris aussi juste un exemple simple
@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());
}
}
le mon 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>
l'écran bowser:
et l'écran mon journal:
Je ne sais pas pourquoi cela ne fonctionne pas pour vous, vous avez probablement oublié une configuration.
J'espère que cette solution plus détaillée peut vous aider à comprendre comment résoudre le problème
Vous ne pouvez pas câbler automatiquement le bean prototype (eh bien, vous pouvez, mais le bean sera toujours le même) ... câblez automatiquement ApplicationContext et obtenez manuellement une instance du bean prototype nécessaire (par exemple dans le constructeur):
TestClass test = (TestClass) context.getBean("nameOfTestClassBeanInConfiguration");
De cette façon, vous êtes sûr d'obtenir une nouvelle instance de TestClass.