@PostConstruct和使用配置类中的new创建的bean

时间:2018-11-30 20:14:02

标签: spring spring-boot testing postconstruct

我在Spring Boot中使用@PostConstruct方法提供服务。在某些集成测试中,不得执行此方法(在这些集成测试中根本不使用该服务)。所以我想我可以让new在此测试(@ContextConfiguration)中加载的配置类中创建bean。

但是,这不符合我的想法。 @PostConstruct仍然被调用,甚至两次,如下所示(是,这是整个代码):

应用

@SpringBootApplication
public class DemoApplication {

  public static void main(String[] args) {
    SpringApplication.run(DemoApplication.class, args);
  }
}

服务

@Service
public class MyService {

  @PostConstruct
  public void startup() {
    System.out.println("@PostConstruct - " + this);
  }
}

测试

@RunWith(SpringRunner.class)
@SpringBootTest(
    webEnvironment = WebEnvironment.RANDOM_PORT,
    classes = {DemoApplication.class})
@ContextConfiguration(classes = TestConfiguration.class)
public class DemoApplicationTests {

  @Test
  public void test() {
    System.out.println("Test");
  }

  @Configuration
  static class TestConfiguration {

    @Bean
    public MyService xxx() {
      MyService myService = new MyService();
      System.out.println("@Bean - " + myService);
      return myService;
    }

  }
}

如果执行测试,将输出以下输出:

 :: Spring  Boot ::        (v2.1.1.RELEASE)
...
2018-11-30  20:34:28.422  INFO 16916 --- [           main] o.a.c.c.C.[Tomcat].[localhost].[/]       : Initializing Spring embedded WebApplicationContext
2018-11-30  20:34:28.422  INFO 16916 --- [           main] o.s.web.context.ContextLoader            : Root WebApplicationContext: initialization completed in 1573 ms
@PostConstruct  - com.example.demo.MyService@41c89d2f
@Bean - com.example.demo.MyService@2516fc68
@PostConstruct  - com.example.demo.MyService@2516fc68
2018-11-30  20:34:28.838  INFO 16916 --- [           main] o.s.s.concurrent.ThreadPoolTaskExecutor  : Initializing ExecutorService 'applicationTaskExecutor'
2018-11-30  20:34:29.086  INFO 16916 --- [           main] o.s.b.w.embedded.tomcat.TomcatWebServer  : Tomcat started on port(s): 62040 (http) with context path ''
2018-11-30  20:34:29.090  INFO 16916 --- [           main] com.example.demo.DemoApplicationTests    : Started DemoApplicationTests in 2.536 seconds (JVM running for 4.187)
Test
2018-11-30  20:34:29.235  INFO 16916 --- [       Thread-3] o.s.s.concurrent.ThreadPoolTaskExecutor  : Shutting down ExecutorService 'applicationTaskExecutor'

有人可以向我解释吗?

  • 为什么第一个@PostConstruct被称为
  • 其次,为什么
  • 为什么要@PostConstruct调用由new构造的bean。为什么这个豆在春天被管理?
  • 我如何才能实现我所需要的?

编辑
我试图返回使用Mockito.mock(...)创建的bean,而不是使用new创建的bean。从某种意义上说,这有助于避免执行第二个@PostConstruct。所以问题仍然存在:为什么第一个?以及如何摆脱它呢?

1 个答案:

答案 0 :(得分:2)

我将解释所有情况下的行为,以便您了解发生了什么。

调用第一个PostConstruct是因为您正在使用SpringRunner@SpringBootTest运行测试,这是扫描类路径并将MyService注册为Bean,因为它带有{{1 }}。

之所以调用第二个PostConstruct,是因为即使您正在更新MyService,您仍在用@Service注释的方法中进行了此操作,该方法在Spring上下文中注册了bean,因此它仅参与了生命周期。其他任何bean所采用的方式(包括调用其@Bean@PostConstruct方法)。

如果您不希望在SpringBootTests中使用@PreDestroy的真实实例,则可以使用MyService。在您的SpringBootTests中,您可能需要MockBean胜过Mock,因为它将在您的Spring上下文中模拟bean。