Dependency Inject one bean into other using annotation - java

I have following spring annotation based configuration:
#Bean
public MarshallingMessageConverter marshallingMessageConverter() {
return new MarshallingMessageConverter();
}
#Bean
public Jaxb2Marshaller jaxb2Marshaller() {
return new Jaxb2Marshaller();
}
I need to inject jaxb2Marshaller to marshallingMessageConverter using setter based injection before marshallingMessageConverter is initialized by Spring. After lot of Googling still can not find this kind of scenario. Please help !!
UPDATE: I don't have have access to both classes as they are inside a jar file. When I Autowire them in a third class something like:
#Autorwired MarshallingMessageConverter converter; //I need something like #Autorwired(setMarshaller = jaxb2Marshaller) where setMarshaller is the setter inside `MarshallingMessageConverter`
#Autowired Jaxb2Marshaller marshaller;

Can you pass the marshaller to the converter constructor? If so, you can use the autowired to create the marshaller and then in the method annotated with #Bean you pass in the constructor:
#Autowired Jaxb2Marshaller marshaller;
#Bean
public MarshallingMessageConverter marshallingMessageConverter() {
return new MarshallingMessageConverter(marshaller);
}

#Bean
public MarshallingMessageConverter marshallingMessageConverter(Jaxb2Marshaller marshaller) {
MarshallingMessageConverter converter = new MarshallingMessageConverter();
converter.whatEverMethodCallIsNeeded(marshaller);
return converter;
}

Related

Spring inject a bean into another bean

I am trying to inject a bean into another bean that uses it. How can I do this?
public class MySpringConfig{
#Bean
public MyObject getMyObject() {
//.....
return MyObjectInstance;
}
#Bean
public SomeObject getSomeObject(MyObject myObject) {
//.....
return SomeObjectInstance;
}
}
I think you can do this with this way, this is working in my project.
#Configuration
public class AppConfig {
#Bean
public Bean1 foo(#Qualifier("bean2") Bean2 bean2) {
return new Bean1(bean2);
}
}
i think that might work!
#Configuration
public class AppConfig {
#Bean
public Bean2 bean2() {
return new Bean2();
}
#Bean
#DependsOn({"bean2"})
public Bean1 foo(#Autowired Bean2 bean2) {
return new Bean1(bean2); // or your can write new Bean1(bean2());
}
}
Parameters don't work exactly in the same way in #Bean and #Component.
For a class annotated with #Component, specifying them is required for the autowired constructor but in a #Bean declaration you don't need to provide a parameter to specify the MyObject dependency to use (while it will work) if that is accessible in the current class, which is your case.
So you want to inject directly the bean by invoking getMyObject() in the #Bean definition.
For example to pass it a constructor arg :
#Bean
public SomeObject getSomeObject() {
//....
// you injected MyObject in the current bean to create
SomeObject object = new SomeObject(getMyObject());
//...
return SomeObjectInstance;
}
And don't forget to annotate the class with #Configuration to make it considered by Spring.

Why does a Spring's FactoryBean getObject return null?

I want to create a Spring ConversionService with custom Converters, but the return value of ConversionServiceFactoryBean#getObject is null. See example:
#Bean
#Autowired
public ConversionService conversionService(Set<Converter<?, ?>> converters) {
final ConversionServiceFactoryBean factory = new ConversionServiceFactoryBean();
factory.setConverters(converters);
return checkNotNull(
factory.getObject(),
"conversionService must not be null.");
}
checkNotNull throws a NullPointerException. The converters are injected as expected. Why does the factory return null? How can I fix that?
Thanks to the comment of Sotirios Delimanolis I came to the following solution:
#Bean
public ConversionServiceFactoryBean conversionService(Set<Converter<?, ?>> converters) {
final ConversionServiceFactoryBean factory = new ConversionServiceFactoryBean();
factory.setConverters(converters);
return factory;
}
This is essentially a shorthand for the following configuration:
#Bean
public ConversionService conversionService(Set<Converter<?, ?>> converters) {
final ConversionServiceFactoryBean factory = new ConversionServiceFactoryBean();
factory.setConverters(converters);
factory.afterPropertiesSet(); // necessary
return factory.getObject();
}
The factory remains in an unfinished state until afterPropertiesSet (explanation) is called. However, one doesn't need to call it, if the ConversionServiceFactoryBean itself is returned instead of the ConversionService. Since the factory is a InitializingBean and a FactoryBean Spring will call afterPropertiesSet and getObject internally, if a ConversionService instance is needed.

Use ConversionServiceFactoryBean or GenericConversionService to register custom converters?

I want to create a ConversionService with custom converters.
Inspired by another question I've tried to create a ConversionService by creating a ConversionServiceFactoryBean and setting the converters on it:
#Bean
public ConversionService conversionService(
Set<Converter<?, ?>> converters,
ConversionServiceFactoryBean factory) {
factory.setConverters(converters);
return factory.getObject();
}
#Bean
public ConversionServiceFactoryBean conversionServiceFactoryBean() {
return new ConversionServiceFactoryBean();
}
The Spring documentation suggests more or less the same in XML:
<bean id="conversionService"
class="org.springframework.context.support.ConversionServiceFactoryBean">
<property name="converters">
<set>
<bean class="example.MyCustomConverter"/>
</set>
</property>
</bean>
But configuring a ConversionService did not work in my case. Did not work means following error was thrown when i tried to use this autowired conversionService in my class;
ConverterNotFoundException: No converter found capable of converting from type [java.lang.String] to type [java.time.Duration]
I had to use a GenericConversionService and add my converters to it:
#Bean
public ConversionService conversionService(Set<Converter<?, ?>> converters) {
final GenericConversionService conversionService = new GenericConversionService();
converters.forEach(conversionService::addConverter);
return conversionService;
}
Now it works, but I want to understand why it doesn't work with ConversionServiceFactoryBean.
Why doesn't it work with ConversionServiceFactoryBean and Java config? Why does it work with GenericConversionService?
It is because you are trying to register the converter in your conversionService method after the ConversionServiceFactoryBean bean has been initialised
i.e. afterPropertiesSet has already been called while constructing the factory bean. If you want to use the first method in your question the converter needs to be registered before afterPropertiesSet method gets called. so I would change your conversionService and conversionServiceFactoryBean methods as follows;
#Bean
public ConversionService conversionService(ConversionServiceFactoryBean factory) {
return factory.getObject();
}
#Bean
public ConversionServiceFactoryBean conversionServiceFactoryBean(Set<Converter<?, ?>> converters) {
ConversionServiceFactoryBean factory = new ConversionServiceFactoryBean();
factory.setConverters(converters);
return factory;
}
However separating these two methods serves no purpose as the instance of conversionService would have already been registered in spring context. So better to just merge those two methods as following;
Note the method name is changed from conversionServiceFactoryBean to conversionService. Without the name change Spring will throw an exception failing to convert the data as intended.
#Bean
public ConversionServiceFactoryBean conversionService(Set<Converter<?, ?>> converters) {
ConversionServiceFactoryBean factory = new ConversionServiceFactoryBean();
factory.setConverters(converters);
return factory;
}
The spring xml example you quoted above can be converted to java config as above in the single method.

How to declare another Jackson ObjectMapper without affecting 'clients' of the original bean?

I have a spring-boot application that exposes a json REST API. For mapping objects to json it uses the built-in jackson ObjectMapper configured by spring-boot.
Now I need to read some data from a yaml file and I found that an easy way to do it is using Jackson - for this I need to declare a different ObjectMapper to convert yaml to objects. I declared this new mapper bean with a specific name to be able to inject it in my service dealing with reading from the yaml file:
#Bean(YAML_OBJECT_MAPPER_BEAN_ID)
public ObjectMapper yamlObjectMapper() {
return new ObjectMapper(new YAMLFactory());
}
But I need a way to tell all the other "clients" of the original json ObjectMapper to keep using that bean. So basically I would need a #Primary annotation on the original bean. Is there a way to achieve this without having to redeclare the original ObjectMapper in my own configuration (I'd have to dig through spring-boot code to find and copy its configuration)?
One solution I found is to declare a FactoryBean for ObjectMapper and make it return the already declared bean, as suggested in this answer. I found by debugging that my original bean is called "_halObjectMapper", so my factoryBean will search for this bean and return it:
public class ObjectMapperFactory implements FactoryBean<ObjectMapper> {
ListableBeanFactory beanFactory;
public ObjectMapper getObject() {
return beanFactory.getBean("_halObjectMapper", ObjectMapper.class);
}
...
}
Then in my Configuration class I declare it as a #Primary bean to make sure it's the first choice for autowiring:
#Primary
#Bean
public ObjectMapperFactory objectMapperFactory(ListableBeanFactory beanFactory) {
return new ObjectMapperFactory(beanFactory);
}
Still, I'm not 100% happy with this solution because it relies on the name of the bean which is not under my control, and it also seems like a hack. Is there a cleaner solution?
Thanks!
You can define two ObjectMapper beans and declare one as primary, e.g.:
#Bean("Your_id")
public ObjectMapper yamlObjectMapper() {
return new ObjectMapper(new YAMLFactory());
}
#Bean
#Primary
public ObjectMapper objectMapper() {
return new ObjectMapper();
}
Once done, you can use your objectmapper bean with #Qualifier annotation, e.g.:
#Autowired
#Qualifier("Your_id")
private ObjectMapper yamlMapper;
Update
You can dynamically add your ObjectMapper to Spring's bean factory at runtime, e.g.:
#Configuration
public class ObjectMapperConfig {
#Autowired
private ConfigurableApplicationContext context;
#PostConstruct
private void init(){
BeanDefinitionBuilder builder = BeanDefinitionBuilder.rootBeanDefinition(ObjectMapper.class);
builder.addConstructorArgValue(new JsonFactory());
DefaultListableBeanFactory factory = (DefaultListableBeanFactory) context.getBeanFactory();
factory.registerBeanDefinition("yamlMapper", builder.getBeanDefinition());
Map<String, ObjectMapper> beans = context.getBeansOfType(ObjectMapper.class);
beans.entrySet().forEach(System.out::println);
}
}
The above code adds a new bean into context without changing the existing bean (sysout prints two beans in the end of init method). You can then use "yamlMapper" as qualifier to autowire it anywhere.
Update 2 (from question author):
The solution suggested in 'Update' works and here's a simplified version:
#Autowired
private DefaultListableBeanFactory beanFactory;
#PostConstruct
private void init(){
BeanDefinitionBuilder builder = BeanDefinitionBuilder.rootBeanDefinition(YAMLMapper.class);
beanFactory.registerBeanDefinition("yamlMapper", builder.getBeanDefinition());
}
Other option is to wrap custom mapper into custom object:
#Component
public class YamlObjectMapper {
private final ObjectMapper objectMapper;
public YamlObjectMapper() {
objectMapper = new ObjectMapper(new YAMLFactory());
}
public ObjectMapper getMapper() {
return objectMapper;
}
}
Unfortunately this approach requires calling getMapper after you inject YamlObjectMapper.
I believe defining explicit primary object mapper for MVC layer should work this way:
#Primary
#Bean
public ObjectMapper objectMapper() {
return Jackson2ObjectMapperBuilder.json().build();
}
All beans that autowire object mapper via type will use above bean. Your Yaml logic can autowire via YAML_OBJECT_MAPPER_BEAN_ID.
I just realized that I don't need to use a FactoryBean, I could just as well declare a regular bean as #Primary and make it return the original bean, like this:
#Bean
#Primary
public ObjectMapper objectMapper(#Qualifier("_halObjectMapper") ObjectMapper objectMapper) {
return objectMapper;
}
This makes the configuration slightly cleaner, but still requires the exact name of the original ObjectMapper. I guess I'll stay with this solution, though.

How do I configure multiple JPA data sources using Spring #Configuration class?

I am trying to configure multiple JPA entity/transaction managers within the same application context using Spring's #Configuration class.
When the context loads, Spring is having difficulties auto-wiring the beans because they implement the same interfaces.
Unfortunately, I'm using legacy code so I can't auto-wire the beans directly and use the #Qualifier annotations, which is why I'm trying to do it using the configuration class.
Within a #Bean declaration, is there any way to qualify which bean should be injected? I thought that using a direct method call would be enough, but it typically results in errors such as
NoUniqueBeanDefinitionException: No qualifying bean of type
[javax.sql.DataSource] is defined: expected single matching bean but
found 4
Here's an example of what I'm trying to do below:
#Configuration
public class ApplicationConfig {
#Bean(name = "transactionManager1")
public PlatformTransactionManager transactionManager1() {
return new JpaTransactionManager(entityManagerFactory1());
}
#Bean(name = "entityManagerFactory1")
public EntityManagerFactory entityManagerFactory1() {
...
LocalContainerEntityManagerFactoryBean factory = new LocalContainerEntityManagerFactoryBean();
factory.setDataSource(dataSource1());
...
}
#Bean(destroyMethod = "")
#ConfigurationProperties(prefix = "datasource.test1")
public JndiObjectFactoryBean jndiObjectFactoryBean1() {
return new JndiObjectFactoryBean();
}
#Bean(name = "dataSource1")
public DataSource dataSource1() {
JndiDataSourceLookup lookup = new JndiDataSourceLookup();
return lookup.getDataSource(jndiObjectFactoryBean1().getJndiName());
}
#Bean(name = "transactionManager2")
public PlatformTransactionManager transactionManager2() {
return new JpaTransactionManager(entityManagerFactory2());
}
#Bean(name = "entityManagerFactory2")
public EntityManagerFactory entityManagerFactory2() {
...
LocalContainerEntityManagerFactoryBean factory = new LocalContainerEntityManagerFactoryBean();
factory.setDataSource(dataSource2());
...
}
#Bean(destroyMethod = "")
#ConfigurationProperties(prefix = "datasource.test2")
public JndiObjectFactoryBean jndiObjectFactoryBean2() {
return new JndiObjectFactoryBean();
}
#Bean(name = "dataSource2")
public DataSource dataSource2() {
JndiDataSourceLookup lookup = new JndiDataSourceLookup();
return lookup.getDataSource(jndiObjectFactoryBean2().getJndiName());
}
I suppose I could try to inject the beans directly via the Spring context's getBean() method, but is there a cleaner way of doing this?
I'm not too familiar with the #Primary annotation, but based on what I've read I don't know how spring would autowire the secondary data source in this case since it looks like it would always pick the beans with #Primary first.
If you cannot change the injection sites to add qualifiers, then you're going to have to create a delegating DataSource based on some logic (which you haven't detailed in the question).
Something like this.
#Primary #Bean
public DelegatingDataSource delegatingDataSource(List<DataSource> sources) {
return new DelegatingDataSource() {
#Override
public DataSource getTargetDataSource() {
// decide which dataSource to delegate to
return sources.get(0);
}
}
}
I've used DelegatingDataSource, but that may not be able to provide what you need. You may need to get more advanced with some kind of interceptor/aspect to get details of the caller on which to base the DataSource selection.
However it's implemented, you need to specify a #Primary bean and use it as a proxy.

Categories