How to create datasource bean at runtime without crashing app in Spring? - java

I am connecting to multiple datasources but sometimes some datasources may be offline and at that time I am geting errors on app and application is failing at startup.
I want to skip datasource configuration at startup... I have tried several ways by adding
spring.autoconfigure.exclude=org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration
to the application.properties and also I have tried adding
#SpringBootApplication(exclude={DataSourceAutoConfiguration.class})
to the main class but still it tries to configure the datasource.
I also tried to use #Lazy annotation on all methods and on constructor as below but still getting error while creating fooEntityManagerFactory
#Lazy
#Configuration
#EnableJpaRepositories(basePackages = "com.heyo.tayo.repository.foo", entityManagerFactoryRef = "fooEntityManagerFactory", transactionManagerRef = "fooTransactionManager")
public class PersistencefooConfiguration {
#Autowired
private DbContextHolder dbContextHolder;
#Lazy
#Bean
#ConfigurationProperties("tay.datasource.foo")
public DataSourceProperties fooDataSourceProperties() {
return new DataSourceProperties();
}
#Lazy
#Bean
#ConfigurationProperties("tay.datasource.foo.configuration")
public DataSource fooDataSource() {
DataSource dataSource = fooDataSourceProperties().initializeDataSourceBuilder()
.type(BasicDataSource.class).build();
dbContextHolder.addNewAvailableDbType(DbTypeEnum.foo);
return dataSource;
}
#Lazy
#Bean(name = "fooEntityManagerFactory")
public LocalContainerEntityManagerFactoryBean fooEntityManagerFactory(
EntityManagerFactoryBuilder builder) {
//THE CODE IS FAILING AT BELOW RETURN CASE
return builder
.dataSource(fooDataSource())
.packages("com.heyo.tayo.model.foo")
.build();
}
#Lazy
#Bean
public PlatformTransactionManager fooTransactionManager(
final #Qualifier("fooEntityManagerFactory") LocalContainerEntityManagerFactoryBean fooEntityManagerFactory) {
return new JpaTransactionManager(fooEntityManagerFactory.getObject());
}
}
I have multiple classes like above for different configs for different datasources and I am adding them to available dbs static list at datasource Bean.
Here is my dbadapter factory class.
Here is my dbAdaptor factory that creates corresponding db adaptor
#Service
public class DbAdapterFactory {
#Autowired
private BeanFactory beanFactory;
#Autowired
private DbContextHolder dbContextHolder;
public DBAdapter dbAdapter(){
DbTypeEnum currentDb = dbContextHolder.getCurrentDb();
DBAdapter dbAdapter = null;
if(currentDb == DbTypeEnum.FOODB) {
dbAdapter = beanFactory.getBean(foodbadaptor.class);
} else {
dbAdapter = beanFactory.getBean(koodbadaptor.class);
}
return dbAdapter;
}
Here is db context holder that makes operation like setting default db or getting current db etc.:
#Component
public class DbContextHolder {
private DbTypeEnum dbType = DbTypeEnum.FOODB;
private Set<DbTypeEnum> availableDbTypes = new HashSet<>();
public void setCurrentDb(DbTypeEnum dbType) {
this.dbType = dbType;
}
public DbTypeEnum getCurrentDb() {
return this.dbType;
}
public List<DbTypeEnum> getAvailableDbTypes() {
return new ArrayList<>(availableDbTypes);
}
public void addNewAvailableDbType(DbTypeEnum dbTypeEnum) {
availableDbTypes.add(dbTypeEnum);
}
}
I made all #Lazy or tried #SpringBootApplication(exclude={DataSourceAutoConfiguration.class}) but still something is calling to create bean and getting error and app is closing. I want to use that config and datasource in a try-catch block and don't stop application at runtime. How can I achieve this or what am I missing on that configs or annotations ?

I believe that you can simply add in your application properties
spring.sql.init.continue-on-error=true
According to the Spring Boot 2.5.5 user guide:
https://docs.spring.io/spring-boot/docs/2.5.5/reference/htmlsingle/#howto-initialize-a-database-using-spring-jdbc
Spring Boot enables the fail-fast feature of its script-based database initializer. If the scripts cause exceptions, the application fails to start. You can tune that behavior by setting spring.sql.init.continue-on-error.
Depending on your spring boot version the property will be named either
spring.sql.init.continue-on-error
or before Spring Boot 2.5
spring.datasource.continue-on-error

It is so dumb but I solved the problem by adding following to application.properties.
spring.jpa.database=sql_server
I have no idea why I need to specify that explicitly in properties file but the problem is solved. I will search for it

Related

#Value isn't working on particular #Configuration class on Springboot

Hi I have been making spring boot project which have 2 of datasource.
I want to use those datasources properties' value from application.yml file and
so that I configured #Configuration Class on our project.
But, even if both of Configuration class was set same way, #Value annotation isn't working on the OracleConfig.class.
I tested related oracle setting which wrote on application.yml file is working on the HiveConfig.class and I confirmed that.
how can I inject value to OracleConfig.class :( ,,,
I currently strongly doubt OracleConfig.class was working before #Value annotation or other Beans had been initialized.
should I configure order about bean creation process or (is that make sense)force that OracleConfig.java's #Bean will be created after injection of #Value?
HiveConfig.class
#Configuration
public class HiveConfig {
#Value("${hive.custom.use}")
private boolean useCustomHive;
#Value("${hive.custom.some-of-setting}")
private String SomeOfSetting;
#Bean
#ConfigurationProperties(prefix = "hive.datasource")
public HikariDataSource dataSourceHive() throws Exception {
HikariDataSource hikariDataSource = new HikariDataSource();
if (useCustomHive){
hikariDataSource.SetSomeOfSetting(SomeOfSetting);
}
return hikariDataSource;
}
OracleConfig.class
#Configuration
public class OracleConfig {
#Value("${oracle.custom.use}")
private boolean useCustomOracle;
#Value("${oracle.custom.some-of-setting}")
private String SomeOfSetting;
#Bean
#ConfigurationProperties(prefix = "oracle.datasource")
public HikariDataSource dataSourceOracle() throws Exception {
HikariDataSource hikariDataSource = new HikariDataSource();
if (useCustomOracle){
hikariDataSource.SetSomeOfSetting(SomeOfSetting);
}
return hikariDataSource;
}
application.yml (this values(oracle and hive) can read from HiveConfig.class via #Value)
oracle:
custom:
use:true
some-of-setting: str
hive:
custom:
use:true
some-of-setting: str
Have you tried with constructor injection? See if this works.
private boolean useCustomHive;
private String SomeOfSetting;
HiveConfig (#Value("${hive.custom.use}") boolean useCustomHive, #Value("${hive.custom.some-of-setting}") String SomeOfSetting) {
this.useCustomHive = useCustomHive;
this.SomeOfSetting = SomeOfSetting;
}

Error creating bean with name 'batchDataSource': Requested bean is currently in creation: Is there an unresolvable circular reference?

I have a batch configuration. I saw the batch process is default using InMemoryMap. Instead I need to use the MySQL to send all the execution details by Batch. But when I use the following code I am getting the following error,
Error creating bean with name 'batchDataSource': Requested bean is
currently in creation: Is there an unresolvable circular reference?
#Configuration
#EnableBatchProcessing
public class BatchProcess extends DefaultBatchConfigurer {
private #Autowired Environment env;
#Bean
#StepScope
public ItemReader reader() {
...
}
#Bean
#StepScope
public ItemProcessor processor() {
...
}
#Bean
#StepScope
public ItemWriter writer() {
...
}
#Bean
#Primary
public DataSource batchDataSource() {
HikariDataSource hikari = new HikariDataSource();
hikari.setDriverClassName(env.getProperty("spring.datasource.driver-class-name"));
hikari.setJdbcUrl(env.getProperty("spring.datasource.url"));
hikari.setUsername(env.getProperty("spring.datasource.username"));
hikari.setPassword(env.getProperty("spring.datasource.password"));
return hikari;
}
public JobRepository getJobRepository() {
JobRepositoryFactoryBean factory = new JobRepositoryFactoryBean();
factory.setDataSource(batchDataSource());
factory.setTransactionManager(manager());
factory.afterPropertiesSet();
return factory.getObject();
}
public PlatformTransactionManager manager() {
return new ResourcelessTransactionManager();
}
#Bean
public Step step() {
return stepBuilderFactory.get("step")
.chunk(1000)
.reader(reader())
.processor(processor())
.writer(writer())
.build();
}
#Bean
public Job job() {
return jobBuilderFactory.get("job")
.flow(step())
.end()
.build();
}
#Bean
public JobLauncher getJobLauncher() {
SimpleJobLauncher launcher = new SimpleJobLauncher();
launcher.setJobRepository(createJobRepository());
return launcher;
}
}
In property file I am using,
spring.batch.job.enabled=false
spring.batch.initialize-schema=always
So what I missed? I am using JPA. And even why it is not using available JPA datasource? How can I force the Spring batch to use default MySQL instead InMemoryMap?
The error message you are receiving may not be the clearest, but it should point you in the right direction. You appear to have a circular dependency within your code.
This happens when you have two (or more) beans that mutually depend upon one another, preventing the creation of one without the existence of the other (and vice versa) - the proverbial chicken and egg problem. You can generally avoid this with setter injection and some kind of post-construction initialization.
I think you have created this situation by extending DefaultBatchConfigurer and then defining the #Bean annotated method getJobLauncher() which directly calls DefaultBatchConfigurer's createJobRepository() method without ensuring that the DataSource is first set within DefaultBatchConfigurer.
This is entirely unnecessary, because DefaultBatchConfigurer already creates JobRepository, JobExplorer, and JobLauncher for you in the proper order.
From DefaultBatchConfigurer:
#PostConstruct
public void initialize() {
try {
this.jobRepository = createJobRepository();
this.jobExplorer = createJobExplorer();
this.jobLauncher = createJobLauncher();
} catch (Exception e) {
throw new BatchConfigurationException(e);
}
}
If you are going to extend DefaultBatchConfigurer, then I suggest you eliminate the following methods from your code:
getJobRepository()
manager()
getJobLauncher()
From your code sample, it appears that you are already setting the following properties (in your application.properties file?):
spring.datasource.jdbcUrl=...
spring.datasource.username=...
spring.datasource.password=...
spring.datasource.driverClassName=...
That should be sufficient to allow Spring's AutoConfiguration to create an Hikari DataSource for you automatically, and this is the approach I usually take. The Spring Bean name will be dataSource, and this will be autowired into DefaultBatchConfigurer via setDataSource().
However, in your code sample, you have also defined a #Bean annotated method named batchDataSource(), which looks no different to what you should receive from Spring AutoConfiguration. As long as you have the spring.datasource properties mentioned earlier configured, you should be able to eliminate batchDataSource() as well, but I don't think that's necessary, so your choice.
If you still want to manually configure your DataSource, then I suggest that you not extend DefaultBatchConfigurer, but instead define a custom bean for it in a configuration class where you can directly pass in your custom DataSource (based on what I currently know of your use case).
#Bean
public BatchConfigurer batchConfigurer(){
return new DefaultBatchConfigurer( batchDataSource() );
}
First of all, explicitly define the mysql-connector dependency in the pom.xml and remove anything related to in-memory map from the project.
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>
If you want to define your own configuration with beans manually, then you can't use the AutoConfiguration classes because they create the required beans for you on startup automatically and that might cause an issue if you are defining own custom DB configuration classes. Therefore, you have to exclude DataSourceAutoConfiguration, HibernateJpaAutoConfiguration and DataSourceTransactionManagerAutoConfiguration to resolve the issue.
Just update the #SpringBootApplication class :
#SpringBootApplication(
exclude = {DataSourceAutoConfiguration.class, HibernateJpaAutoConfiguration.class,
DataSourceTransactionManagerAutoConfiguration.class
}
)
public class App {
public static void main(String[] args) {
SpringApplication.run(App.class, args);
}
}

Spring Boot 2 Instanciating Bean from Configuration List

Hy all,
in my current application, I need to be able to load a number of database connections into my context (they are then used in camel routes, but thats not part of this question).
For this I have a List of settings in my application.yml file, where each entry is all that is needed for one connection:
- name: mysql1
jdbcUrl: "jdbc:mysql://localhost:3306/dbScheme"
username: a
password: a
- name: mssql1
jdbcUrl: "jdbc:sqlserver://localhost:1433;databaseName=dbScheme"
username: a
password: a
There is a variable amount of connections, that can be configured this way.
I need each of those configurations as a javax.sql.DataSource object/bean in the registy (bean name would be the name property from the configuration object):
DataSource dataSourceBean = DataSourceBuilder.create()
.driverClassName(driverClassName) // Resolved automatically from the jdbcUrl
.url(dataSourceFromYaml.getJdbcUrl())
.username(dataSourceFromYaml.getUsername())
.password(dataSourceFromYaml.getPassword())
.build();
The question now is, how I do put those objects into the context as beans?
As I see it, its not possible with annotations, because we have a variable amount of objects and they are also not loaded into the context directly, but have to be created first. (Or am I wrong here?)
Thanks in advance for any ideas
Chris
Edit:
To clarify: The two connections I put here are not the connections used in production, they are just an example.
The issue is, that they are configured in production and there can be any amount of them.
I have no way of predicting how many there are and how they are named.
After trying a lot of different spring context objects I finally found one, that worked.
Here is the solution:
#Configuration
#ConfigurationProperties(prefix = "jdbc")
#RequiredArgsConstructor // Lombok
public class DataSourceBeanFactory {
// The DataSourceConfiguration holds everything from one property object
#Setter // Lombok
private List<DataSourceConfiguration> dataSources;
private final ConfigurableApplicationContext applicationContext;
#PostConstruct
public void resolveAndCreateDataSourceBeans() {
dataSources.forEach(dataSourceFromYaml -> {
/*
* Code to resolve driver class name
*/
String driverClassName = ....
DataSource dataSourceBean = DataSourceBuilder.create()
.driverClassName(driverClassName)
.url(dataSourceFromYaml.getJdbcUrl())
.username(dataSourceFromYaml.getUsername())
.password(dataSourceFromYaml.getPassword())
.build();
applicationContext
.getBeanFactory()
.registerSingleton(dataSourceFromYaml.getName(), dataSourceBean);
});
}
Thank to everybody, that answered my question.
Chris
You could use the following approach
create properties file application-dev.properties
mysql1.jdbc.jdbcUrl=jdbc:mysql://localhost:3306/dbScheme
mysql1.jdbc.username=a
mysql1.jdbc.password=a
mssql1.jdbc.jdbcUrl=jdbc:sqlserver://localhost:1433;databaseName=dbScheme
mssql1.jdbc.username=a
mssql1.jdbc.password=a
Create configuration class
#Configuration
public class JDBCConfigExample {
#Bean(name = "mysql1DataSource")
#ConfigurationProperties(prefix = "mysql1.jdbc")
public DataSource mysql1DataSource() {
return DataSourceBuilder.create().build();
}
#Bean(name = "mssql1DataSource")
#ConfigurationProperties(prefix = "mssql1.jdbc")
public DataSource mssql1DataSource() {
return DataSourceBuilder.create().build();
}
#Bean(name = "mysql1JdbcTemplate")
public JdbcTemplate mysql1JdbcTemplate(#Qualifier("mysql1DataSource") DataSource dataSource) {
return new JdbcTemplate(dataSource);
}
#Bean(name = "mssql1JdbcTemplate")
public JdbcTemplate mssql1JdbcTemplate(#Qualifier("mssql1DataSource") DataSource dataSource) {
return new JdbcTemplate(dataSource);
}
}
After that you could use both JdbcTemplate and DataSource in any Spring managed bean by using Qualifier or name convention:
private final JdbcTemplate mssql1JdbcTemplate;
If you don't have list of datasources beforehand and want to configure this dynamically you can do following thing:
1) In your application.yml:
datasources:
ds1:
name:
pw:
ds2:
name:
pw:
2) Create properties class:
#Configuration
#ConfigurationProperties(prefix = "datasources")
public class DataSourcesInfo {
Map<String, DataSourceInfo> dataSources;
public #Data static class DataSourceInfo {
private String pw;
private String name;
}
}
In this map you will have list of entries for all datasources defined.
3) And now you can create beans dynamically:
#Configuration
public class Config {
#Autowired
private GenericApplicationContext genericApplicationContext;
#Autowired
DataSourcesInfo properties;
#PostConstruct
public void createDataSources() {
// iterate through properties and register beans
genericApplicationContext.registerBean(dataSourceName,
DataSource.class,
() -> {
DataSource ds = new DataSource();
ds.set(...);
....
return ds;
})
}
}

Better way to use HikariCP with Hibernate in Spring Boot Project

I'm a beginner, I have a simple Spring Boot project, it's my first time using a connection pool (HikariCP in this case) and I need your help. It's working but I want to know if I'm using it the right way with Hibernate, or if there are better ways to do it, and if my Spring Boot project structure is correct.
EDIT : It's working even if I remove the class HikariCPConfig, how can I know if connection pools are working or not?
The project is as follow :
- BankManager
src/main/java
|
|__com.manager
|__BankManagerApplication.java
|__HikariCPConfig.java
|__com.manager.dao
|__ClientRepository.java
|__com.manager.entities
|__Client.java
|__com.manager.service
|__ClientServiceImpl.java
|__ClientServiceInterface.java
src/main/resources
|__application.properties
BankManagerApplication.java :
#SpringBootApplication
public class BankManagerApplication {
public static void main(String[] args) {
ApplicationContext ctx = SpringApplication.run(BankManagerApplication.class, args);
ClientServiceInterface service = ctx.getBean(ClientServiceInterface.class);
service.addClient(new Client("client1"));
service.addClient(new Client("client2"));
}
}
HikariCPConfig.java :
#Configuration
#ComponentScan
class HikariCPConfig {
#Value("${spring.datasource.username}")
private String user;
#Value("${spring.datasource.password}")
private String password;
#Value("${spring.datasource.url}")
private String dataSourceUrl;
#Value("${spring.datasource.dataSourceClassName}")
private String dataSourceClassName;
#Value("${spring.datasource.poolName}")
private String poolName;
#Value("${spring.datasource.connectionTimeout}")
private int connectionTimeout;
#Value("${spring.datasource.maxLifetime}")
private int maxLifetime;
#Value("${spring.datasource.maximumPoolSize}")
private int maximumPoolSize;
#Value("${spring.datasource.minimumIdle}")
private int minimumIdle;
#Value("${spring.datasource.idleTimeout}")
private int idleTimeout;
#Bean
public HikariDataSource primaryDataSource() {
Properties dsProps = new Properties();
dsProps.put("url", dataSourceUrl);
dsProps.put("user", user);
dsProps.put("password", password);
dsProps.put("prepStmtCacheSize",250);
dsProps.put("prepStmtCacheSqlLimit",2048);
dsProps.put("cachePrepStmts",Boolean.TRUE);
dsProps.put("useServerPrepStmts",Boolean.TRUE);
Properties configProps = new Properties();
configProps.put("dataSourceClassName", dataSourceClassName);
configProps.put("poolName",poolName);
configProps.put("maximumPoolSize",maximumPoolSize);
configProps.put("minimumIdle",minimumIdle);
configProps.put("minimumIdle",minimumIdle);
configProps.put("connectionTimeout", connectionTimeout);
configProps.put("idleTimeout", idleTimeout);
configProps.put("dataSourceProperties", dsProps);
HikariConfig hc = new HikariConfig(configProps);
HikariDataSource ds = new HikariDataSource(hc);
return ds;
}
}
ClientServiceImpl.java
#Service
public class ClientServiceImpl implements ClientServiceInterface {
#Autowired
ClientRepository clientRepository; // this class extends JPARepository
#Override
public Client addClient(Client c) {
return clientRepository.save(c);
}
}
application.properties :
server.port = 8888
spring.jpa.databasePlatform=org.hibernate.dialect.MySQL5Dialect
spring.jpa.show-sql = true
spring.jpa.hibernate.ddl-auto = update
spring.datasource.dataSourceClassName=com.mysql.jdbc.jdbc2.optional.MysqlDataSource
spring.datasource.url=jdbc:mysql://localhost:3306/bank_manager
spring.datasource.username=root
spring.datasource.password=
spring.datasource.poolName=SpringBootHikariCP
spring.datasource.maximumPoolSize=5
spring.datasource.minimumIdle=3
spring.datasource.maxLifetime=2000000
spring.datasource.connectionTimeout=30000
spring.datasource.idleTimeout=30000
spring.datasource.pool-prepared-statements=true
spring.datasource.max-open-prepared-statements=250
Thank you in advance.
You project structure is standard, so it's correct.
About Hikari:
Hikari is indeed a great choice for pooling. I'm used to work with Hikari successfully by using a smaller set of params you are applying in your case, but if it's working for you that's fine.
For more info about Hikaru setup, I recommend reading the official wiki, if you haven't already.
About property loading:
You can make use of some SpringBoot features to read the DB parameters and apply into your runtime Beans with less code. Like:
In application.properties (define a custom prefix 'myproject.db' for your pool params)
myproject.db.dataSourceClassName=com.mysql.jdbc.jdbc2.optional.MysqlDataSource
myproject.db.url=jdbc:mysql://localhost:3306/bank_manager
myproject.db.username=root
... and the other params below
Create a Spring Configuration class
#Configuration
public class MyDBConfiguration {
#Bean(name = "myProjectDataSource")
#ConfigurationProperties(prefix = "myproject.db")
public DataSource dataSource(){
//This will activate Hikari to create a new DataSource instance with all parameters you defined with 'myproject.db'
return DataSourceBuilder.create().build();
}
}
In your ClientRepository class:
#Repository
public class ClientRepository {
//The code below is optional, but will work if you want to use jdbctemplate tied to the DataSource created above. By default all Hibernate Sessions will take the DataSource generated by Spring
#Bean(name = "myProjectJdbcTemplate")
public JdbcTemplate jdbcTemplate(#Qualifier("myProjectDataSource") DataSource dataSource){
return new JdbcTemplate(dataSource);
}
}
There are other options to manage the DataSource beans creation if you are going to use 2 or more different Databases. You can vary the properties prefix for the other databases and annotate 1 Datasource only as #Primary, which is mandatory when you have more than 1 DataSources in Spring context

Dependency injection fails when using a library with its own application context

I have some issues with DI in Spring Framework (version 4.1.1). I wrote a small library that creates its own application context and uses DI to configure its components. It consists of the following classes. First the config file for the application context.
#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 */ }
}
The JPA Repository:
#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);
}
A simple service that forwards calls to the repository:
#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));
}
}
And finally a factory that provides the singleton instance of the service to external consumers:
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() {};
}
I wrote some unit tests to test the factory and repository and it is all working fine. I get a bean returned from the factory.
But when I include this library in a larger project the DI does no longer work properly. The larger project consists of several services that consume connectors like the above to fetch data from external services. They are injected using the #Autowired annotation It is a maven project so I added the above library to the dependencies, compiling works fine. I added a bean definition to a config class in my larger project to gain access to the connector's service:
#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();
}
}
And this is where the problem occurs: When injecting the Connector bean into a service in my larger project, the DI inside the library fails. It says that he cannot find a suitable bean for the LogEntryJpaRepository dependency.
Why does the DI work fine when run inside a unit test but does fail when used inside another application context? In my understanding the context of the larger project does not need to know the repository bean, the configuration is done by the context inside the library. That context just exposes the service bean to external consumers, no configuration needs to be done by the context of the larger project.
Is there some mistake in my configuration or even design flaws for the library itself? This is my first library that uses DI to configure itself so maybe I just did something completely wrong?
[EDIT] Removed #Import statement from configuration class, that was a remains of testing, not there in real code. Sorry!

Categories

Resources