How to autowire dependency within a Spring Boot Converter? - spring-boot

I am struggling with a way to autowire a dependency within a converter class using spring boot. What is the most elegant solution to solve this problem?
Configuration
#Configuration
public class Config {
#Bean
public ConversionServiceFactoryBean conversionFacilitator() {
ConversionServiceFactoryBean factory = new ConversionServiceFactoryBean();
factory.setConverters(getConverters());
return factory;
}
private Set<Converter> getConverters() {
Set<Converter> converters = new HashSet<>();
converters.add(new MyConverter());
return converters;
}
}
Converter class
#Component
public class MyConverter implements Converter<Type1, Type2> {
#Autowired
private Dependency dependency; // Null here due to the component not being injected
#Override
public Type2 convert(Type1 type1) {
return dependency.something(type1);
}
}

The dependency is not being injected because you are creating MyConverter with new, instead of let Spring create it.
You do not need a method to return set of converters. Spring can do it for you, just auto wiring it. Spring is smart enough to give you a set with all the converter implementations it finds.
You should use something like:
#Configuration
public class Config {
#Bean
#Autowired
public ConversionServiceFactoryBean conversionFacilitator(Set<Converter> converters) {
ConversionServiceFactoryBean factory = new ConversionServiceFactoryBean();
factory.setConverters(converters);
return factory;
}
}

Related

Defining constructor in prototype bean

Using SpringBoot, I have a Component bean that is defined as #Scope("protoype"). It also injects another prototype bean
The class is defined as
#Component
#Scope("prototype")
public class MyClass{
#Autowired
public BeanFactory beanFactory
private InjectedBean injectedBean
public MyClass(DataObj data) {
this.injectedBean = beanFactory.getBean(InjectedBean.class, data)
}
}
However, IntelliJ complains about the data field on the constructor: Could not autowire. No beans of 'DataObj' type found.. But DataObj is a POJO. I pass it in at runtime in order to create the bean. Am I defining the constructor incorrectly?
Update
Had the same problem doing it this way. It still wants to treat DataObj as a bean on the factory constructor class. Doesn't matter if I annotate the class with #Component or #Configuration
#Component
public class MyClass{
#Autowired
public BeanFactory beanFactory
private InjectedBean injectedBean
public MyClass(InjectedBean injectedBean) {
this.injectedBean = injectedBean;
}
#Bean
#Scope("prototype")
public MyClass myClass(DataObj data) {
InjectedBean injectedBean = beanFactory.getBean(InjectedBean.class, data)
return new MyClass(injectedBean);
}
}
Also tried this example from that same link:
#Configuration
public class ServiceConfig {
#Bean
public Function<DataObj, MyClass> thingFactory() {
return data-> myClass(data); //
}
#Bean
#Scope(value = "prototype")
public MyClass myClass(DataObj data) {
return new MyClass(data);
}
}
Update
I think I resolved this with some information in Spring Java Config: how do you create a prototype-scoped #Bean with runtime arguments?. Part of my problem is that I tried to put the factory bean in the Component itself, which doesn't work
In other words
#Component
public class MyClass{
#Autowired
public BeanFactory beanFactory
private InjectedBean injectedBean
public MyClass(InjectedBean injectedBean) {
this.injectedBean = injectedBean;
}
#Bean
#Scope("prototype")
public MyClass myClass(DataObj data) {
InjectedBean injectedBean = beanFactory.getBean(InjectedBean.class, data)
return new MyClass(injectedBean);
}
}
In this cass, Spring tries to create a MyClass bean because of the #Component annotation, but another MyClass bean due to the #Bean annotation.
So I moved the #Bean to another class
#Configuration
public class ServiceConfig {
#Bean
public Function<DataObj, MyClass> thingFactory() {
return data-> myClass(data); //
}
#Bean
#Scope(value = "prototype")
public MyClass myClass(DataObj data) {
return new MyClass(data);
}
}
This appears to work, but IntelliJ still complains about DataObj. This might be an Intellij issue

property value injection into spring beans

i want to know why #Value property injection works on classes with #Service annotation but not on classes with #Bean within #Configuration annotated class.
Works means that the property value is not null.
This value is also injected into two other service which i see during debugging in DefaultListableBeanFactory.doResolveDependency. But i dont see the bean WebserviceEndpoint.
Configuration
#Configuration
public class WebserviceConfig {
// do some configuration stuff
#Bean
public IWebserviceEndpoint webserviceEndpoint() {
return new WebserviceEndpoint();
}
}
Webservice interface
#WebService(targetNamespace = "http://de.example/", name = "IWebservice")
#SOAPBinding(parameterStyle = SOAPBinding.ParameterStyle.BARE)
public interface IWebserviceEndpoint {
#WebMethod
#WebResult(name = "response", targetNamespace = "http://de.example/", partName = "parameters")
public Response callWebservice(#WebParam(partName = "parameters", name = "request", targetNamespace = "http://de.example/") Request request) throws RequestFault;
}
Webservice class
public class WebserviceEndpoint implements IWebserviceEndpoint {
#Value("${value.from.property}")
private String propertyValue;
}
application.yml
value:
from:
property: property-value
When does the injection of #Value happen in this case.
Basically propertyValue is null because Spring injects value after bean's creation.
So when you do:
#Bean
public IWebserviceEndpoint webserviceEndpoint() {
return new WebserviceEndpoint();
}
Spring creates a new instance with propertyValue=null.
You can initialize your instance attribue with #ConfigurationProperties
#Bean
#ConfigurationProperties(prefix=...)
public IWebserviceEndpoint webserviceEndpoint() {
return new WebserviceEndpoint();
}
Note that propertyValue should have a setter.
You have several ways to solve this problem, usually it's good to centralize properties in one utils class.
#Component
public class Configs {
#Value("${propery}"
String property;
String getProperty(){
return property;
}
}
And then:
#Bean
#ConfigurationProperties(prefix=...)
public IWebserviceEndpoint webserviceEndpoint() {
WebserviceEndpoint we = new WebserviceEndpoint();
we.setProperty(configs.getProperty())
return we;
}
Again there are many many different ways to solve this problem

how to add a dynamically created bean in a #PostConstruct in Spring Boot

I need to add a dynamically created bean when my 'normal' bean gets created. I tried this so far:
//generate a health bean dynamically, and register it
#PostConstruct
public void init(){
solrhealth = new SolrHealthIndicator(solr);
//context.??
}
I build a SolrHealthIndicatior bean programatically, as I am not using Spring Solr Data. Now I want it registered so it shows up in /health.
I have my context wired, but cannot find how to register the newly created bean in there...
You should be able to programatically define your bean by using the #Bean annotation within a #Configuration class.
#Bean
public SolrHealthIndicator solrHealthIndicatior() {
//you can construct the object however you want
return new SolrHealthIndicator();
}
Then you can just inject it like any other bean(#Autowired constructor, field, setter injection, etc.), if there are multiple beans with the same type you can use #Qualifier to distinguish between them.
You need to use #Lookup annotation.
#Component
public class SolrHealthIndicator {
public SolrHealthIndicator(Solr solr) {
}
}
public class BeanInQuestion {
#PostConstruct
public void init() {
solrHealthIndicator = getHealthIndicatorBean();
}
#Lookup
public SolrHealthIndicator getHealthIndicatorBean() {
//Spring creates a runtime implementation for this method
return null;
}
}
You could make the class containing your #PostConstruct implement BeanDefinitionRegistryPostProcessor. Then you'd then be able to register your beans programmatically:
#Bean
public class MyBean implements BeanDefinitionRegistryPostProcessor {
private BeanDefinitionRegistry registry;
#Override
public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException {
this.registry = registry;
}
#PostConstruct
public void init(){
registry.registerBeanDefinition("solrHealthIndicator", new SolrHealthIndicator(solr));
}
}

Spring Java Configuration: overridden #Bean method invoked before autowiring

I have RepositoryConfig extending Neo4jConfiguration. The latter sets up a number of beans with #Bean annotated methods. RepositoryConfigoverrides getGraphDatabaseService which is invoked before any fields in RepositoryConfig are autowired. That is a problem since I want to use the autowired stuff inside the getGraphDatabaseServicemethod.
#ConfigurationProperties(prefix = "neo4j")
public class RepositoryProperties {
[...]
}
#Configuration
#EnableNeo4jRepositories("com.foo.bar")
#EnableConfigurationProperties(RepositoryProperties.class)
public class RepositoryConfig extends Neo4jConfiguration {
#Autowired
private RepositoryProperties properties;
#Override
#Bean(name = "graphDatabaseService", destroyMethod = "shutdown")
public GraphDatabaseService getGraphDatabaseService() {
[...] // properties is 'null' at this point
}
#PostContstruct
public void foo() {
[...] // properties is initiated OK here
}
}
Why is getGraphDatabaseServicebeing called before autowiring is complete? I guess it has to do with the inheritance... If I remove the inheritance then autowiring is complete at the time getGraphDatabaseServiceis called. I've also tried annotating the method with #DependsOn, with no luck.
Any ideas is much appreciated!
Yes, I have seen this too occasionally. I think there are two workarounds.
Option 1. Autowire the bean definition
#Override
#Bean(name = "graphDatabaseService", destroyMethod = "shutdown")
#Autowired
public GraphDatabaseService getGraphDatabaseService() {
[...] // properties is 'null' at this point
}
Option 2. Inject the bean
#Override
#Bean(name = "graphDatabaseService", destroyMethod = "shutdown")
public GraphDatabaseService getGraphDatabaseService(#Autowired RepositoryProperties properties) {
// can probably delete the Config member with this approach
[...] // properties is 'null' at this point
}

#Configuration and Bean property setting annotations

I'm using #Configuration to create beans and set their properties from other bean properties such as is possible with xml configuration
#Configuration
public class AppConfig {
#Bean
public MyBean myBean(#Value("#{someObject.value}") double value) {
MyBean myBean = new MyBean();
myBean.setValue(value);
return myBean;
}
#Bean
public SomeObject someObject() {
SomeObject someObject = new SomeObject();
someObject.setValue(1.0);
return someObject;
}
}
However, it is cumbersome and unnecessary to have to use the setter methods [eg. myBean.setValue(value)]. Instead I want something like so:
#Configuration
public class AppConfig {
#Bean
#MysticalPropertySetter(name="value", value="#{someObject.value}")
public MyBean myBean() {
return new MyBean();
}
#Bean
#MysticalPropertySetter(name="value", value="1.0", type="java.lang.Double")
public SomeObject someObject() {
return new SomeObject();
}
}
Anything like this possible? Much Thanks!!!
I think that this approach impossible because in your factory method annotated with #Bean you create objects manually, but Spring can inject properties only to beans that are instanteated by itself.
How about constructor injection?
#Bean
public MyBean myBean(#Value("#{someObject.value}") double value) {
return new MyBean(value);
}

Resources