Autowire在单元测试中工作,但不在主java类中工作

时间:2017-04-05 04:09:48

标签: spring spring-boot autowired

我想要从外部配置自动填充的域类。这是我的域类:

@Data
@Configuration
@PropertySource("classpath:application.properties")
public class StudioVo {

   @Value("${studio.code}")
   private code;

    @Bean
    public static PropertySourcesPlaceholderConfigurer propertySourcesPlaceholderConfigurer() {
        return new PropertySourcesPlaceholderConfigurer();
    }
}

这是我的上下文xml:

<bean class="org.springframework.batch.core.scope.StepScope" />
<bean id="ItemReader" class="com.sdm.studio.reader.StudioReader" scope="step">
     <property name="studioVo" ref="StudioVo" />
</bean>

<bean id="StudioConfigVo" class="com.sdm.studio.domain.StudioVo" />

</bean>

这是我想要使用vo的课程:

@Slf4j
@Data
public class StudioReader implements ItemReader<List<Studio>> {

   private StudioVo studioVo;

   public List<Studio> read() throws Exception {

      System.out.println("getCode: " + studioVo.getCode()); //code is null here

      return null;
   }

}

然而,当我通过自动装配进行单元测试时,它运行正常。像这样:

@RunWith(SpringRunner.class)
@SpringBootTest
public class StudioTest {

   @Autowired
   private StudioVo studioVo;

    @Test
    public void testAutoPopulationOfStudio(){
      System.out.println("getCode: "+ studioVo.getCode()); // works!
      // Assert.assertTrue(studioVo.getCode().equals("102"));
    }
}

不确定这里发生了什么 - 我正在使用Spring Boot中包含的旧Spring Batch应用程序(因此混合了基于XML和基于Java的配置 - 可能就是这个问题的原因)。我错过了什么?

2 个答案:

答案 0 :(得分:0)

StudioTest中,当您错过了StudioReader代码中的@Autowired时,您正在自动装配StudioReader,因此请按以下所示添加:

@Slf4j
@Data
public class StudioReader implements ItemReader<List<Studio>> {

    @Autowired //add this so that studioVo can be injected
    private StudioVo studioVo;

    //add other code
}

答案 1 :(得分:0)

请务必注意,使用@Autowire需要在其下方使用一系列Spring托管bean 包括您正在使用@Autowire的类。这是因为Spring需要先前的引用来匹配对象引用层次结构。即,在业务逻辑层ClassA中,您希望@Autowire一个字段。 ClassA本身需要是一个托管bean。此外,如果您想要@Autowire的字段包含一个对其他对象具有引用依赖性的对象(并且大多数都这样做),那么这些对象也必须是Spring管理的。

例如,以下内容将起作用:

package com.example.demo;

import org.springframework.context.annotation.AnnotationConfigApplicationContext;


public class MessageRunner {

 private static SetterMessage setterMessage;

 public static void main(String[] args) {
    setterMessage = (SetterMessage) (new AnnotationConfigApplicationContext(DemoConfiguration.class)).getBean("setterMessage");
    setterMessage.setMessage("Finally it works.");
    p(setterMessage.getMessage());
 }

 private static void p(String s) {
    System.out.println(s);
 }

}

DemoConfiguration.java看起来像这样:

package com.example.demo;

import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;

@Configuration
@ComponentScan(basePackages = "com.example.demo")
 public class DemoConfiguration {
}

SetterMessage.java,这个:

package com.example.demo;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Service;

@Service
@Scope("prototype")
public class SetterMessage  {

 private String message = null;

 @Autowired
 private SetterMessage2 setterMessage2;

 public String getMessage(){
    return message+setterMessage2.getSubMessage();
 }

 public void setMessage(String message) {
    this.message = message;
    setterMessage2.setSubMessage("("+message+")");
 }

}

SetterMessage2.java:

package com.example.demo;

import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Service;


@Service
@Scope("prototype")
public class SetterMessage2 {

 private String subMsg = "";

 public void setSubMessage(String msg) {
    subMsg = msg;
 }

 public String getSubMessage() {
    return subMsg;
 }

}

请注意,SetterMessage2.java注释为Component(@Service),但其中没有自动装配的字段。那是因为它是对象引用链的末尾。但因为它是一个组件,它可以自动连接到SetterMessage.java。但是,请查看MessageRunner.java的main()方法和字段声明。请注意,类字段SetterMessage未自动装配。如果它被注释为@Autowired,则main()将在运行时失败,抛出一个NPE,其中引用了main()中的setterMessage。这是因为MessageRunner.java没有标记为某种组件。所以我们需要从应用程序上下文中获取一个有效的MessageSetter实例并使用它。

要强调的是,如果MessageRunner.java看起来像这样,则以下版本的MessageRunner.java的main()方法将失败,抛出一个NPE:

...
public class MessageRunner {

 @Autowired // <-- This will not do the job for us
 private static SetterMessage setterMessage;

 public static void main(String[] args) {
    setterMessage.setMessage("Finally it works."); // NPE here on ref to setterMessage
    p(setterMessage.getMessage());
 }
 ...

对于刚接触Spring的人来说,这是一个真正的知识。事实上,我把它放在了五大春季新手劝阻者之中,这是一个非常邪恶,有害的细节,这让新的程序员在加剧和谷歌搜索中花了不少时间。所以我希望在这里注意这个现象将至少节省一些新手时间和高血压峰值。

注意:如果您要在IDE中创建上述类,请记住这些是在启用Spring Boot的情况下开发的。