Issue
I have an issue wiring beans via configuration. I have some Pipe that needs to read data from some source and process the data in one way or another. The data source and the way to process the data is based on configuration values. In code it is implemented like the following structure:
class Pipe {
DBReader reader;
List<DataProcessor> dataProcessors;
}
There are many pipes of the same type are configured. Each may have one of the DBReader implementation: for example JDBC reader which depends on particular data source or File reader which depends on some file name. It can also have 0..n DataProcessor, each implementation is also depends on its own set of parameters.
interface DBReader {
Data readData();
}
class JdbcReader implements DBReader {
DataSource dataSource;
}
class FileReader implements DBReader {
String fileName
}
interface DataProcessor {
void processData(Data data);
}
class CopyDataProcessor implements DataProcessor {
int param1;
int param2;
}
class DevNullDataProcessor implements DataProcessor {
String hostName;
}
In configuration it will be like this:
datasources:
dataSource:
id: 1
connectionString: "postgres://la-la-la"
dataSource:
id: 2
connectionString: "mysql://la-la-la"
dbReaders:
dbReader:
id: 1
type: jdbc // "com.example.reader.JdbcReader"
dataSourceRef: 1
dbReader:
id: 2
type: file
filename: "customers.json"
dbReader:
id: 3
type: jdbc
dataSourceRef: 2
dataProcessors:
dataProcessor:
id: 1
impl: "com.example.processors.CopyDataProcessor"
param1: 4
param2: 8
dataProcessor:
id: 2
impl: "com.example.processors.DevNullProcessor"
hostName: Alpha
pipes:
pipe:
readerRef: 1
dataProcessorsRef: [1, 2]
pipe:
readerRef: 2
dataProcessorsRef: [2]
Is it possible to do it in Spring using annotation based configuration? Can I autowire beans by id? For example can I make Spring to load all the configured data processors and readers and wire them by those Refs in configuration. Maybe Spring can load beans as maps and then autowire using id as a key to the map? Or maybe I can use some factories to do that? Could please anyone guide me in the right direction?
Solution
The easiest way is using qualifiers:
@Configuration
public class MyConfiguration {
@Bean
@Qualifier("jdbcReader")
public DbReader jdbcReader(String connStr /*read this from @ConfigurationProperties / @Value */) {
return new JdbcReader(connStr);
}
... // other readers here ...
@Bean
@Qualifier("devNullProcessor")
public DataProcessor devNullProcessor() {
return new DevNullProcessor();
}
@Bean
public Pipe samplePipe(@Qualifier("jdbcReader") DbReader reader, @Qualifier("devNullProcessor") DataProcessor processor) {
return new Pipe(reader, processor);
}
}
This however means that the configuration itself is done by the programmer in Java. In your example it seems like you would like to create beans without specifying them in advance, only based on the yaml configuration.
In this case you’ll probably want to use BeanFactoryPostProcessor
that will allow dynamic bean creation. This is a completely different approach from what I’ve described above though.
In a nutshell, its a spring’s abstraction (an interface) that you can implement. It will be called by Spring when the application context starts and will allow registration of bean definitions from which Spring will later create beans. Since its a code you can register as many bean definitions as you want in the implementation of the processor.
There are many examples of using bean factory post processors in the internet. Quick googling shows this one for instance
Answered By – Mark Bramnik
This Answer collected from stackoverflow, is licensed under cc by-sa 2.5 , cc by-sa 3.0 and cc by-sa 4.0