Java @EntityListeners注入&x2B;jUnit测试

Java @EntityListeners注入&x2B;jUnit测试,java,spring-mvc,junit,dependency-injection,entitylisteners,Java,Spring Mvc,Junit,Dependency Injection,Entitylisteners,我使用@EntityListeners在保存到数据库之前和加载之后进行操作。 在我的Listener类中,我调用了一个Ecryptor(它需要从配置文件中获取信息),因此加密程序不能静态调用,需要注入到我的Listener中。对吧? 当然,EntityListeners中的注入不能马上完成,但是您有一些方法可以做到这一点,比如使用SpringBeanAutowiringSupport.processInjectionBasedOnCurrentContext(this)甚至这里显示的方法 酷,问

我使用
@EntityListeners
在保存到数据库之前和加载之后进行操作。 在我的Listener类中,我调用了一个
Ecryptor
(它需要从配置文件中获取信息),因此加密程序不能静态调用,需要注入到我的Listener中。对吧?

当然,EntityListeners中的注入不能马上完成,但是您有一些方法可以做到这一点,比如使用
SpringBeanAutowiringSupport.processInjectionBasedOnCurrentContext(this)
甚至这里显示的方法

酷,问题是:没有一个解决方案支持单元测试!运行测试时,我在模型侦听器中注入的加密器总是
null

这里有一个解决方案来创建这个上下文并传递给一个实例化的对象,但它不能解决我的问题,因为我有“注入”要添加到它

有没有办法在我的测试中创建一个上下文并以某种方式将它传递给我的侦听器? 如果没有,我可以用什么方法为我的加密程序创建一个静态方法,并且仍然可以访问环境API来读取我的属性

包侦听器:

public class PackageListener{
   @Autowired
   Encryptor encryptor;

   @PrePersist
   public void preSave(final Package pack){
      pack.setBic(encryptor.encrypt(pack.getBic()));
   }
   ...
我的测试

 @Test
 @WithuserElectronics
 public void testIfCanGetPackageById() throws PackageNotFoundException{
     Package pack = packagesServiceFactory.getPackageService().getPackage(4000000002L);
 }
套餐服务

  public Package getPackage(Long id) throws PackageNotFoundException{
    Package pack = packageDao.find(id);

    if (pack == null) {
        throw new PackageNotFoundException(id);
    }

    return pack;
}
加密机:

public class Encryptor{
    private String salt;

    public Encryptor(String salt){
        this.salt = salt;
    }

    public String encrypt(String string){
        String key = this.md5(salt);
        String iv = this.md5(this.md5(salt));
        if (string != null) {
            return encryptWithAesCBC(string, key, iv);
        }
        return string;
    }
    ...

要回答您的需要,您必须创建两个类来完成所有需要的配置

您必须创建带有以下注释的testConfig:

@Configuration
@ComponentScan(basePackages = { "yourPath.services.*",
        "yourPath.dao.*" })
@EnableAspectJAutoProxy
@EnableTransactionManagement
@EnableJpaRepositories(basePackages = "yourPath.dao.entities", 
    entityManagerFactoryRef = "entityManagerFactory", 
    transactionManagerRef = "transactionManager", 
    repositoryBaseClass = Dao.class)
@Import({ DataSourceConfig.class }) //Explained below
public class TestConfig {

    @Autowired
    private DataSource dataSource;

    @Bean
    public List<String> modelJPA() {
        return Collections.singletonList("es.carm.sms.ortopedia.entities");
    }

    @Bean(name = "transactionManager")
    public PlatformTransactionManager transactionManager(EntityManagerFactory entityManagerFactory) {
        JpaTransactionManager transactionManager = new JpaTransactionManager();
        transactionManager.setEntityManagerFactory(entityManagerFactory);
        return transactionManager;
    }

    @Bean(name = "entityManagerFactory")
    public LocalContainerEntityManagerFactoryBean entityManagerFactory() {
        LocalContainerEntityManagerFactoryBean entityManagerFactory = new LocalContainerEntityManagerFactoryBean();
        entityManagerFactory.setPackagesToScan(modelJPA().toArray(new String[modelJPA().size()]));
        entityManagerFactory.setDataSource(this.dataSource);
        JpaVendorAdapter jpaVendorAdapter = new HibernateJpaVendorAdapter();
        entityManagerFactory.setJpaVendorAdapter(jpaVendorAdapter);
        return entityManagerFactory;
    }
}
现在已经全部设置好了,只需创建测试导入配置:

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = TestConfig.class)
public class TestCase {...}

您将通过访问所有资源(MVC)服务、DAO和模型来初始化spring上下文。

您可以创建一个
DemoApplicationContextInitializer
类,将
appliationContext
引用存储在主类的静态属性中

public class DemoApplicationContextInitializer implements
        ApplicationContextInitializer<ConfigurableApplicationContext> {

    @Override
    public void initialize(ConfigurableApplicationContext ac) {
        Application.context = ac;
    }
}


@SpringBootApplication
public class Application {

    public static ApplicationContext context;

    public static void main(String[] args) throws Exception {
        new SpringApplicationBuilder(Application.class)
        .initializers(new DemoApplicationContextInitializer())
        .run(args);
    }
}
要在junit测试中实现这一点,只需在测试中添加初始值设定项,如下所示

@RunWith(SpringRunner.class)
@SpringBootTest(webEnvironment=WebEnvironment.RANDOM_PORT, classes = Application.class)
@ContextConfiguration(classes = Application.class, initializers = DemoApplicationContextInitializer.class)
public class MyTest {
...
}

它在我的环境中没有任何问题。希望它也能对您有所帮助。

如果它是
null
您没有使用上下文。您的测试让我怀疑您是否正在使用测试创建的上下文(我怀疑您在测试中做了什么)。感谢您的评论@M.Deinum,我正在使用我的
BaseTest
类中的
@ContextConfiguration(classes={ApplicationConfiguration.class})
创建上下文。除了如上所述的
加密机(从EntityListener调用)之外,所有注入和配置都能正常工作。我怀疑您在获取服务的过程中是否实际使用了加密机……再次感谢!我知道你在说什么,但是,即使我将上下文传递给服务,我以后如何将其传递给加密程序?你不应该传递这些东西。。。我只是说,您在测试中做的事情与在实际代码中做的事情不同。事实上,您有某种服务工厂来为测试获取服务,这提示了我。您应该将依赖项注入到测试用例中,而不是使用其他对象来获取它们。但是这里的代码太少了,用这里的内容,充其量只是一个猜测游戏。谢谢你的回答。正如我在评论中提到的,注射在我的测试中非常有效。我唯一的问题是运行测试时JPA EntityListener内部的注入。
public class PackageListener{
   //@Autowired
   Encryptor encryptor;

   @PrePersist
   public void preSave(final Package pack){
      encryptor = Application.context.getBean(Encryptor.class);
      pack.setBic(encryptor.encrypt(pack.getBic()));
   }
}
@RunWith(SpringRunner.class)
@SpringBootTest(webEnvironment=WebEnvironment.RANDOM_PORT, classes = Application.class)
@ContextConfiguration(classes = Application.class, initializers = DemoApplicationContextInitializer.class)
public class MyTest {
...
}