原型Bean没有按预期自动装配

时间:2016-03-29 07:59:05

标签: java spring spring-mvc spring-web

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 }}

5 个答案:

答案 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:

enter image description here

和我的日志屏幕:

enter image description here

我不知道为什么它不适合你,可能你忘记了一些配置。

我希望这个更加严谨的解决方案可以帮助您了解如何解决您的问题