Spring Beans: Scan for Converters, Inject into CustomConversionServiceFactory - java

I've got a MyAppConversionServiceFactoryBean which I'm registering like:
<bean id="conversionService" class="com.MyProject.MyAppConversionServiceFactoryBean">
<property name="messageSource" ref="messageSource"/>
<property name="converters">
<set>
<bean class="com.MyProject.XRepresentationConverter" />
<bean class="com.MyProject.YRepresentationConverter" />
<bean class="com.MyProject.ZRepresentationConverter" />
</set>
</property>
</bean>
I can continue to list every converter we write into this list, but I'd love to be able to configure it such that this isn't necessary and that converters will automatically register themselves somehow with my factory.
Sidebar 1: If that's not possible with a custom factory, is it possible with the default spring one?
Sidebar 2: If neither the first part nor Sidebar 1 is possible, is it possible to #Autowired the conversionService into the converters (so they can easily call one another)? Attempting to #Autowired ConversionService conversionService has previously given me issues due to not being able to wire the conversionService into an object while it's still busy creating the service.
Note: We're using Spring, but not Spring MVC. I have no control over that, so any solutions on that route will be unfortunately unusable. I can change pretty much anything else about the configuration and Java classes, just not the overarching tools.

#Vikdor's comment on the question pointed me in the right direction.
Spring is apparently capable (and no one I asked in person knew this) of gathering collections of beans through the scanning process with #Autowired annotations. Here's what I needed to achieve the same effect I got from the configuration in the post:
applicationContent.xml must have:
<context:component-scan base-package="com.MyProject"/>
<bean id="conversionService" class="com.MyProject.MyAppConversionServiceFactoryBean" />
MyAppConversionServiceFactoryBean.java:
public class MyAppConversionServiceFactoryBean implements
FactoryBean<ConversionService>, InitializingBean {
#Autowired
private Set<BaseConverter> converters;
}
And then all of my converters now have the #Component annotation.
Relevant Docs on #Autowired do briefly mention that it can be used to collect all beans of a type, but I wouldn't have known that it could be done into any collection type without this thread by Grzegorz Oledzki which addresses the generic form of my question, but takes it down a philosophical route.

Related

Auto-wiring Spring constructor arguments from properties file in XML

I'm using Spring to auto-wire beans for configuration. Some parameters come from a properties file:
<bean id="mydb" class="myproject.mydb" autowire="constructor">
<constructor-arg name="host" value="${mydb.host}" />
<constructor-arg name="db" value="${mydb.db}" />
<constructor-arg name="user" value="${mydb.user}" />
<constructor-arg name="password" value="${mydb.password}" />
</bean>
Is there a way to auto-wire these properties based on the bean id so that I would just have to write the following?
<bean id="mydb" class="myproject.mydb" autowire="constructor" />
Edit: The point of this is to not have to explicity specify the non-bean constructor arguments. I want Spring to automatically check the properties for beanId.constructorArgName
To achieve exactly what you want, I think you'd need to implement a BeanPostProcessor and provide your custom wiring logic (where you read the .properties file) in postProcessBeforeInitialization. The bean name is available to that method, but there are multiple problems with this. The first is that argument names are not necessarily available at runtime, so indexes might be a better option. The second is that you already have an instantiated bean (so a default constructor would need to exists), and you'd instantiate another, throwing the first one away which is wasteful. To use the instance that already exists, you'd need to wire it by properties, not constructor, which violates encapsulation and is not what you asked. The third is that it's not at all obvious what is going on. So, all in all, you are probably better off avoiding this completely.
In your class myproject.mydb
#Autowired
public mydb(#Value("mydb.host") String host, ...){...}
As per your question, the only way Property values can be injected to the constructor is through the XMLfile as done above or using the #Value("${some.property}") annotation.
Refer this for more info
Use #Value("property key") annotation. look at eg.: http://java.dzone.com/articles/autowiring-property-values

How to autowire a class with non-empty constructor?

I'd like to #Autowired a class that has a non-empty constructor.
Just the the following as an example, it does not necessairly have to be a view/service. Could be whatever component you like, having non-default constructor:
#Component
class MyViewService {
//the "datasource" to show in the view
private List<String> companies companies;
private MyObject obj;
public MyViewService(List<String> companies, MyObject obj) {
this.companies = companies;
this.obj = obj;
}
}
Of course I cannot just write
#Autowired
private MyViewService viewService;
as I'd like to use the constructor with the list. But how?
Are there better approaches than refactoring these sort of constructors to setters? I wouldn't like this approach as ideally the constructor forces other classes to provide all objects that are needed within the service. If I use setters, one could easily forget to set certain objects.
If you want Spring to manage MyViewService you have to tell Spring how to create an instance of it. If you're using XML configuration:
<bean id="myViewService" class="org.membersound.MyViewService">
<constructor-arg index="0" ref="ref_to_list" />
<constructor-arg index="1" ref="ref_to_object" />
</bean>
If you're using Java configuration then you'd call the constructor yourself in your #Beanannotated method.
Check out the Spring docs on this topic. To address a comment you made to another answer, you can create a List bean in XML as shown in the Spring docs. If the list data isn't fixed (which it's probably not) then you want to use an instance factory method to instantiate the bean.
In short, the answers you seek are all in the Spring docs :)
If a component has a non-default constructor then you need to configure the constructor in the bean configuration.
If you are using XML,
it might look like this (example from the spring reference document):
<beans>
<bean id="foo" class="x.y.Foo">
<constructor-arg ref="bar"/>
<constructor-arg ref="baz"/>
</bean>
<bean id="bar" class="x.y.Bar"/>
<bean id="baz" class="x.y.Baz"/>
</beans>
The key here is constructor wiring of the bean that will be used for the #AutoWire.
The way you use the bean has no impact.

How to modify beans defined in a spring container

I have two xml files defining beans for the springframework (version 2.5.x):
containerBase.xml:
<beans>
<bean id="codebase" class="com.example.CodeBase">
<property name="sourceCodeLocations">
<list>
<value>src/handmade/productive</value>
</list>
</property>
</bean>
</beans>
... and
containerSpecial.xml:
<beans>
<import resource="containerBase.xml" />
</beans>
Now I want to adjust the property sourceCodeLocations of bean codebase within containerSpecial.xml. I need to add a second value src/generated/productive.
A simple approach is to override the definition of codebase in containerSpecial.xml and add both values, the one from containerBase.xml and the new one:
containerSpecial.xml:
<beans>
<import resource="containerBase.xml" />
<bean id="codebase" class="com.example.CodeBase">
<property name="sourceCodeLocations">
<list>
<value>src/handmade/productive</value>
<value>src/generated/productive</value>
</list>
</property>
</bean>
</beans>
Is there a way to extend the list without redefining the bean?
EDIT 2009-10-06:
The purpose of this is to have a shared standard container containerBase that is used by a lot of different projects. Each project can override/extend some properties that are special for that project in its own containerSpecial. If the project doesn't override, it's using the defaults defined in containerBase.
You could use a BeanFactoryPostProcessor to change the bean's metadata before the Spring container instantiates the CodeBase bean. For example:
public class CodebaseOverrider implements BeanFactoryPostProcessor {
private List<String> sourceCodeLocations;
public void postProcessBeanFactory(
ConfigurableListableBeanFactory beanFactory) throws BeansException {
CodeBase codebase = (CodeBase)beanFactory.getBean("codebase");
if (sourceCodeLocations != null)
{
codebase.setSourceCodeLocations(sourceCodeLocations);
}
}
public void setSourceCodeLocations(List<String> sourceCodeLocations) {
this.sourceCodeLocations = sourceCodeLocations;
}
}
Then in contextSpecial.xml:
<beans>
<import resource="context1.xml" />
<bean class="com.example.CodebaseOverrider">
<property name="sourceCodeLocations">
<list>
<value>src/handmade/productive</value>
<value>src/generated/productive</value>
</list>
</property>
</bean>
</beans>
Yes. A bean definition can have a "parent" attribute that references a parent bean definition. The new "child" definition inherits most of the properties of the parent and any of those properties can be overridden.
See Bean Definition Inheritance
Also you can use Collection Merging to merge the list property definition from the parent and child bean definitions. This way you can specify some list items in the parent bean definition and add more items to it in the child bean definition.
Is there a way to define the list in a properties or other configuration before hand?
It seems like the app configuration and wiring are tightly coupled. From my experience, if it is hard to do something in Spring, likely there is a different easier way to do it.
3 approaches:
Simple: have two lists defaultSourceCodeLocations and additionalSourceCodeLocations and have your accessor methods check both of these (or combine them). I've seen this done in some frameworks - a default list of handlers is populated then additional user created ones are added...
More complicated but keeps the original class clean: You could then create a CodeBaseModifier class. This would have a init-method to alter an injected instance of the bean.
<bean id="codebaseModifier" class="com.example.CodeBase" init-method="populateCodeBase">
<property name="sourceCodeLocations" ref="codebase"/>
<property name="additionalSourceCodeLocations">
<list>
<value>src/handmade/productive</value>
</list>
</property>
</bean>
If you wanted to make this really generic you could make a bean modifier that would do this by reflection. Be careful of the ordering if use this approach. Dependent beans of CodeBase would have to make sure this class was instantiated first (with depends on)
3 A variation on 2... Instead of directly creating a CodeBase class instead create a factory that returns a populated bean. This factory could then be configured with Spring in a similar fashion to 2. Have a defaultSourceCodeLocations and additionalSourceCodeLocations
Unless you need a lot of extensible properties I would go with option 1.
In Spring 3.0, you can specify merge="true" on the 'list' tag. See http://forum.springsource.org/archive/index.php/t-97501.html for details.

How do I inject a single property value into a string using spring 2.5.x?

I would really like to annotate a method with a reference to a single property in a property file for injection.
#Resource("${my.service.url}")
private String myServiceUrl;
Of course, this syntax does not work ;) Thats why I'm asking here.
I am aware that I can inject the full properties file, but that just seems excessive, I dont want the property file - I want the configured value.
Edit: I can only see PropertyPlaceholderConfigurer examples where XML is used to wire the property to the given field. I still cannot figure out how this can be achieved with an annotation ?
I know it has been a while since the original post but I have managed to stumble across a solution to this for spring 2.5.x
You can create instances of "String" beans in the spring xml configuration which can then be injected into the Annotated components
#Component
public class SomeCompent{
#Autowired(required=true
#Resource("someStringBeanId")
private String aProperty;
...
}
<beans ....>
<context:component-scan base-package="..."/>
<bean class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
...
</bean>
<bean id="someStringId" class="java.lang.String" factory-method="valueOf">
<constructor-arg value="${place-holder}"/>
</bean>
</beans>
I've created a project which addresses this problem for Spring 2.5.*:
http://code.google.com/p/spring-property-annotations/
For Spring 3 you can use the #Value("${propery.key}") annotation.
There's a thread about this on the Spring forum. The short answer is that there's really no way to inject a single property using annotations.
I've heard that the support for using annotations will be improved in Spring 3.0, so it's likely this will be addressed soon.
you can do this if you use XML configuration. Just configure PropertyPlaceholderConfigurer and specify property value in configuration
<bean class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
<property name="locations">
<value>classpath:com/foo/jdbc.properties</value>
</property>
</bean>
<bean ...>
<property name="myServiceUrl" value="${my.service.url}"/>
</bean>
You could try injecting value of property "my.service.url" to a filed in your bean.
Take a look at: http://static.springframework.org/spring/docs/2.5.x/reference/beans.html#beans-factory-placeholderconfigurer
HTH.

The Spring AOP Proxy that isn't

I have two Spring proxies set up:
<bean id="simpleBean" class="org.springframework.aop.framework.ProxyFactoryBean">
<property name="target">
<ref local="simpleBeanTarget"/>
</property>
<property name="interceptorNames">
<list>
<value>cacheInterceptor</value>
</list>
</property>
</bean>
<bean id="springDao" class="org.springframework.aop.framework.ProxyFactoryBean">
<property name="target" ref="springDaoTarget"/>
<property name="interceptorNames">
<list>
<value>daoInterceptor</value>
</list>
</property>
</bean>
simpleBean works just fine -- springDao does not.
The SpringDao class looks like:
public class SpringDao extends JdbcDaoSupport {
private SimpleJdbcTemplate simpleJdbcTemplate;
public SimpleJdbcTemplate getSimpleJdbcTemplate() {
if (simpleJdbcTemplate==null) {
simpleJdbcTemplate= new SimpleJdbcTemplate(getDataSource());
}
return simpleJdbcTemplate;
}
...
And I have my unit test autowired like this:
#Autowired
#Qualifier("springDao")
protected SpringDao springDao;
And the first indication something is wrong is I get this error:
Could not autowire field: . . . nested
exception is
java.lang.IllegalArgumentException
If I comment out the #Qualifier annotation and run my unit test again, I get this:
No unique bean of type ... expected
single matching bean but found 2:
[springDaoTarget, springDao]
That is what I expected.
So I changed my autowiring to
#Autowired
#Qualifier("springDaoTarget")
protected SpringCustomerCapacityDao springDao;
And added the following to my unit test:
Object proxy = applicationContext.getBean("springDao");
Assert.assertNotNull(proxy);
Assert.assertTrue(proxy instanceof SpringDao);
And the instanceof test failed, which (to me) means that my proxy is not really my proxy.
So I'm confused. What's going on? How can I fix this?
Edit Here is the requested springDaoTarget definition, which will disappoint many people:
<bean id="springDaoTarget" class="com.company.SpringDao">
If the target of your proxy implements at least one interface then Spring's default behavior is to create a JDK Proxy that implements all the interfaces of the target. This means it will not be a subclass of the target class. You can override this by forcing the creation of CGLIB proxies instead which are dynamic subclasses of the target.
As a general rule, if you are going to use AOP but only use interfaces in a limited fashion you'll want to force CGLIB. Otherwise you will have lots of JDK Proxies in your container which are not of the same type as the bean implementations you loaded.
See Cliff Meyers blog: Spring AOP: CGLIB or JDK Dynamic Proxies?
It was easy to fix, once I figured it out. SpringDao no longer inherits from JdbcDaoSupport and now it works.

Categories

Resources