2016-03-29 6 views
18

TestController.javaПрототип Bean не получает autowired как ожидалось

@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() { 

    } 

} 

Как вы можете видеть, я пытаюсь выяснить, имеет ли новый TestClass был вводится при посещении «xxx/test». "new test class constructed." был напечатан только один раз (первый раз я запускал «xxx/test»), в то время как я ожидал, что он будет напечатан одинаково. Это значит, что @Autowired объект может быть только @Singleton? Как работает @Scope?

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()); 
    } 
} 

Я попытался @Valerio Vaudi решение, зарегистрированное в качестве Scope(scopeName = "request"). Вот результат трех раз, когда я посещаю «ххх/тест»

(первый раз)

  • новый тестовый класс, построенный.
  • нуль

(второй)

  • нуль

(третий)

  • нуль

Я не понимаю, почему результат null, так как он не восстанавливает новый каждый раз, когда я его использую.

Затем я попытался @Nikolay Rusev решение @Scope("prototype"):

(первый)

  • новый построенный.
  • новый товар.
  • нуль

(второй)

  • новый построен.
  • новый товар.
  • нуль

(третий)

  • новый один построен.
  • новый товар.
  • нуль

Это довольно легко понять, так как каждый раз, когда я использую его (TestClass), Spring автоматически регенерировать новый экземпляр. Но первая сцена, которую я до сих пор не могу понять, поскольку она, похоже, сохраняет только один новый экземпляр для каждого запроса.

Настоящая цель: В каждом жизненном цикле запроса требуется новый testClass (при необходимости), и требуется только одно. На данный момент кажется возможным только решение ApplicationContext (что я уже знал), но я просто хочу знать, можно ли это сделать автоматически, используя @Component + @Scope + @Autowired.

+0

я не могу получить его, где приходит третий нуль? разместите свой полный код TestClass ... –

ответ

4

Весенние контроллеры по умолчанию являются одиночными (это нормально из-за их безгражданности), а также другие весенние бобы.

Именно поэтому достаточно создать экземпляр только одного экземпляра TestClass только для экземпляра TestController.

Легко инстанцирует TestClass один больше времени - просто вводить его в другой контроллер или получить из контекста программно

3

Как уже упоминалось, контроллер по умолчанию одноэлементных, поэтому конкретизации и инъекция TestClass выполняется только один раз о его создании.

Решение может быть впрыснуть контекст приложения и получить боб вручную:

@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 боб запрашивается, Spring, зная, что это @Prototype, создаст новый экземпляр и вернуть его.

Другим решением является создание контроллера @Scope("prototype").

2

Вы не можете autowire прототип фасоли (ну, вы можете, но фасоль всегда будет то же самое) ... autowire в ApplicationContext и получить экземпляр нужного прототипа бина вручную (например, в конструкторе):

TestClass test = (TestClass) context.getBean("nameOfTestClassBeanInConfiguration"); 

Таким образом, вы уверены, что получите новый экземпляр TestClass.

14

все приведенные выше ответы верны. Контроллер по умолчанию - singleton, а инъецируемый testClass - один раз, потому что режим прокси-сервера по умолчанию - DEFAULT от spring doc.

общественные абстрактные ScopedProxyMode ProxyMode Указывает, должен ли компонент быть сконфигурированы как Scoped прокси-сервер и, если да, то прокси должен быть интерфейс на основе или на основе подкласса. По умолчанию используется значение ScopedProxyMode.DEFAULT, которое обычно указывает на отсутствие прокси-сервера , если не настроено другое значение по умолчанию на уровне инструкций компонента-сканирования.

Аналогично поддержке весной 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 боб но прокси до TestClass bean, и этот прокси поймет область prototype и будет возвращать новый экземпляр каждый раз.

+0

Лучшее и простое решение. – Arundev

2

Ключевым моментом является то, что restController bean - это singleton, а Spring создаст только один экземпляр этого компонента во время создания bean-компонента.

Когда вы накладываете прототип бобового пространства, Spring будет представлять новый бит для каждой точки DI. Другими словами, если вы настроите bean-компонент на два или n-раз через xml или java-config, этот компонент будет иметь свежий экземпляр вашего компонента с прототипом.

В вашем случае вы используете стиль аннотации, который фактически является способом по умолчанию для веб-слоя, начиная с весны 3.x.

Одной из возможностей для добавления свежей фасоли может быть достигнута с помощью бобов, охваченных сеансом, но, на мой взгляд, если ваш прецедент - это отдых 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()); 
     } 
    } 

мой 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> 

экран топливозаправщик:

enter image description here

и т он мой журнал экран:

enter image description here

Я не знаю, почему это не работает для вас, вероятно, вы уже забыли некоторую конфигурацию.

Я надеюсь, что это более Поводу решение может помочь вам понять, как решить вашу проблему

+0

У меня есть свойство String в TestClass, и для этого есть getter и setter. Я устанавливаю свойство как некоторое значение в методе обработчика, тогда я получаю это свойство, он возвращает 'null', почему? Я вижу, что он строит новый «TestClass» ровно один раз для каждого запроса. – Kim

+0

Извините, но я не понимаю вариант использования. Возобновляя у вас такую ​​конфигурацию, какую я предлагаю, но теперь у вас также есть свойство String, которое вы задали в методе дескриптора, но когда вы загружаете лоток для получения данных, вы получаете нуль. Учтите, что если вы установите свойство в методе в вашем методе api для отдыха, но затем вы получите значение, которое вы установили в другом методе, ясно, что вы извлекаете нулевой, поскольку запрос отличается, а Spring вводит новый компонент, который будет иметь свойство ранее установленный нуль –

+0

Если вы хотите расширить срок службы вашего компонента, вам следует рассмотреть возможность использования области сеанса.но если вы создадите rest api, api должен быть апатридом и не statefull. Но если ваш случай использования навязывает вам разговор о взаимодействии rest api, вы должны рассмотреть @Scope (scopeName = «session», proxyMode = ScopedProxyMode.TARGET_CLASS) –

 Смежные вопросы

  • Нет связанных вопросов^_^