我在Spring Framework(版本4.1.1)中遇到了一些DI问题。我写了一个小型库,它创建了自己的应用程序上下文,并使用DI来配置其组件。它由以下类组成。首先是应用程序上下文的配置文件。
@Configuration
@ComponentScan(basePackages = { "package.containing.services" })
@EnableTransactionManagement
@EnableJpaRepositories(basePackages = {"package.containing.repositories"})
@PropertySource({"classpath:config-file.properties"})
public class ConnectorConfig {
@Resource
private Environment env;
@Bean
public DataSource dataSource() { /* DataSource config here */ }
@Bean
public EntityManagerFactory entityManagerFactory() { /* EntityManagerFactory config here */ }
@Bean
public PlatformTransactionManager transactionManager() { /* TransactionManager config here */ }
}
JPA存储库:
@Repository
@Transactional(readOnly = true)
public interface LogEntryJpaRepository extends JpaRepository<LogEntryEntity, Long> {
@Query("SELECT l FROM LogEntryEntity l WHERE logentry_time >= :fromDate AND logentry_time <= :toDate AND logentry_data NOT LIKE 'EXTERNAL COMMAND:%' ORDER BY logentry_time ASC, logentry_id ASC")
public List<LogEntryEntity> findInTimeframe(@Param("fromDate") Timestamp fromDate, @Param("toDate") Timestamp toDate);
@Query("SELECT COUNT(l) FROM LogEntryEntity l WHERE logentry_time >= :fromDate AND logentry_time <= :toDate AND logentry_data NOT LIKE 'EXTERNAL COMMAND:%'")
public long countInTimeframe(@Param("fromDate") Timestamp fromDate, @Param("toDate") Timestamp toDate);
}
将调用转发到存储库的简单服务:
@Service
public class Connector {
@Autowired
private LogEntryJpaRepository logEntryRepo;
public LogEntryEntity getLogEntryById(long id) {
return logEntryRepo.findOne(id);
}
public List<LogEntryEntity> getLogEntriesInTimeframe(LocalDateTime fromDate, LocalDateTime toDate) {
return logEntryRepo.findInTimeframe(Timestamp.valueOf(fromDate), Timestamp.valueOf(toDate));
}
public long countLogEntriesInTimeframe(LocalDateTime fromDate, LocalDateTime toDate) {
return logEntryRepo.countInTimeframe(Timestamp.valueOf(fromDate), Timestamp.valueOf(toDate));
}
}
最后是一家工厂,为外部消费者提供服务的单例实例:
public class ConnectorFactory {
private static ApplicationContext ctx;
public static Connector getInstance() {
if(ctx == null) {
ctx = new AnnotationConfigApplicationContext(ConnectorConfig.class);
}
return ctx.getBean(Connector.class);
}
private ConnectorFactory() {};
}
我写了一些单元测试来测试工厂和存储库,它一切正常。我从工厂拿回了一个豆子。
但是当我将这个库包含在一个更大的项目中时,DI不再正常工作。较大的项目包含多个服务,这些服务使用上述连接器从外部服务获取数据。它们是使用@Autowired
注释注入的。它是一个maven项目,所以我将上面的库添加到依赖项中,编译工作正常。我在我的大项目中为一个配置类添加了一个bean定义,以获得对连接器服务的访问权限:
@Configuration
@EnableTransactionManagement(proxyTargetClass = true)
@ComponentScan({"de.decoit.imonitor.gui.dao"})
@PropertySource({"classpath:imonitor-gui.properties"})
public class PersistenceConfig {
@Resource
private Environment env;
@Bean
public Connector connector() {
return ConnectorFactory.getInstance();
}
}
这就是出现问题的地方:在我的大型项目中将Connector
bean注入服务时,库中的DI失败。它说他找不到适合LogEntryJpaRepository
依赖的bean。
为什么DI在单元测试中运行时工作正常但在其他应用程序上下文中使用时却失败了?根据我的理解,较大项目的上下文不需要知道存储库bean,配置是由库中的上下文完成的。该上下文只是将服务bean暴露给外部使用者,而不需要通过较大项目的上下文来完成配置。
我的配置是否存在错误,甚至是图书馆本身的设计缺陷?这是我的第一个使用DI配置自己的库,所以也许我只是做了一些完全错误的事情?
[编辑]从配置类中删除了@Import语句,这是一个测试的遗留物,而不是真正的代码。遗憾!