Подтвердить что ты не робот

Прототип Bean не поддерживается автоматически, как ожидалось

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

    }

}

Как вы можете видеть, я пытаюсь выяснить, был ли введен новый 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"). Вот три результата, когда я посещаю "xxx/test"

(первый раз)

  • построен новый тестовый класс.
  • NULL

(второй)

  • NULL

(третий)

  • NULL

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

Затем я попробовал @Nikolay Rusev solution @Scope("prototype"):

(первый)

  • новый построенный.
  • новый построенный.
  • NULL

(второй)

  • новый построенный.
  • новый построенный.
  • NULL

(третий)

  • новый построенный.
  • новый построенный.
  • NULL

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

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

4b9b3361

Ответ 1

все приведенные выше ответы правильны. Контроллер по умолчанию - singleton, а введенный testClass создается один раз, потому что режим прокси-сервера с расширенным охватом <<22 > из spring doc.

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 и будет возвращать новый экземпляр каждый раз необходимо.

Ответ 2

Контроллеры

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

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

Легко создать экземпляр TestClass еще раз - просто ввести его в другой контроллер или получить из контекста программно

Ответ 3

Как уже упоминалось, контроллер по умолчанию singleton, поэтому создание и вставка 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").

Ответ 4

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

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

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

Ответ 5

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

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

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

Одна возможность для добавления свежих bean может быть достигнута с помощью bean, охваченного сеансом, но, на мой взгляд, если ваш прецедент - это отдых WS, который я считаю безстоящим, использование сеанса, на мой взгляд, является плохой выбор.

Решением вашего дела может быть область запроса использования.

Обновление Я пишу также простой пример

     @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:

введите описание изображения здесь

и экран моего журнала:

введите описание изображения здесь

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

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