What is the best way of extending spring application context? - java

I need to extend spring applicatioContext xml file with new beans definitions and then add references to them to list, which is a property of one bean:
Basic applicationContext xml file:
<bean id="myBean" class="com.example.MyBean">
<property name="providers">
<list>
<ref bean="provider1">
</list>
</property>
</bean>
<bean id="provider1" class="com.example.Provider">
Depends on instance of application I have different providers, so I need to add them to the list. Now I have the additional beans definitions in database and use BeanFactoryPostProcessor to add them to the context and then add references to them to the list of providers. But I use #Transactional annotation on myBean and automatic transaction management (tx:annotation-driven) and because of using BeanFactoryPostProcessor the transaction annotations are not processed.
So I need another way to extend the application context and then the list of providers. What can I use?
My idea is to have xml file which at the beginning is empty and then fill it by data from database and then import it somehow in applicationContext. Is it possible?
Thanks for your help

You can override or extend the bean definitions in multiple ways. In short, here is one way..
main Application Context xml:
<bean id="myBaseBean" class="com.example.MyBean" abstract="true">
<property name="providers">
<list merge="true">
<ref bean="provider1" />
</list>
</property>
</bean>
<!-- default bean definition -->
<bean id="myBean" parent="myBaseBean">
<property name="providers">
<list merge="true">
</list>
</property>
</bean>
<bean id="provider1" class="com.example.Provider">
Some extendedApplication Context xml:
<bean id="myBean" parent="myBaseBean">
<property name="providers">
<list merge="true">
<ref bean="someOtherProvider" />
</list>
</property>
</bean>
<!-- bean definition of some other provider -->
This is nothing to do with Transactions You have to handle the transactions as usual for every other bean.
NOTE: All the application context files will be loaded/override based on the order of the file names you mention while creating the ApplicationContext.

Use #PostConstruct method in your MyBean class that loads and fills provider list from database.
#PostConstruct
public void initialize() {
providers.addAll(providerService.findAll());
}
Put all database related code into Service/Dao class and annotate it with #Transactional annotation

Related

Create Freemarker Configuration bean in Spring, together with parameter

I am having trouble creating a "freemarker.template.Configuration" bean and setting global shared variables in this instance of the Configuration. Something like:
<bean id="conf" class="freemarker.template.Configuraton">
<property name="sharedVariable" >
**??**
</property>
</bean>
Is this possible?
I can't use FreeMarkerConfigurer instead of Configurer because I am using servlets (full stack of Spring MVC) as controllers in my project. Is there any way to convert a FreemarkerConfigurer into a Configurer?
The problem stems from that shared variables is not a JavaBean property... but, accidentally, Configuration has a setAllSharedVariables(TemplateHashModelEx) method, that's technically a property, so something like this should work (I haven't tried it and my Spring XML is rusty... tell me if there are typos in it):
<bean id="conf" class="freemarker.template.Configuraton">
<property name="allSharedVariables">
<bean class="freemarker.template.SimpleHash">
<constructor-arg>
<map>
<entry key='someVarName' value='someValue' />
<entry key='otherVarName' value-ref='valueBeanId' />
</map>
</constructor-arg>
</bean>
</property>
</bean>

Is there a way to get all registered message-converters?

I would like to somehow inject all HttpMessageConverter instances registered in Spring-MVC. I can successfully inject all that have been registered via.
private HttpMessageConverter[] converters;
#Autowired
public void setConverters(HttpMessageConverter[] converters) {
this.converters = converters;
}
However this only injects if the converter was registered inside the context (i.e. if defined outside of <annotation-driven>).
I did figure I would try using <beans:ref inside the <annotation-driven><message-converters> but it is not supported in spring-web 3.1.
Is there some class I can inject that may have a property I could use to get converters? Ideally I'd like to see the order in the filter chain they are registered in too.
You are right, the message converters are directly instantiated within the RequestMappingHandlerAdapter registered using the <mvc:annotation-driven/> xml tag, and the message-converters subtag explicitly expect the bean to be defined inline.
However, a workaround is to define the handler adapter and inject in the converters this way:
<bean name="handlerAdapter" class="org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter">
<property name="webBindingInitializer">
<bean class="org.springframework.web.bind.support.ConfigurableWebBindingInitializer">
<property name="conversionService" ref="conversionService"></property>
<property name="validator">
<bean class="org.springframework.validation.beanvalidation.LocalValidatorFactoryBean">
<property name="providerClass" value="org.hibernate.validator.HibernateValidator"></property>
</bean>
</property>
</bean>
</property>
<property name="messageConverters">
<list>
<ref bean="byteArrayConverter"/>
<ref bean="jaxbConverter"/>
<ref bean="jsonConverter"/>
<bean class="org.springframework.http.converter.StringHttpMessageConverter"></bean>
<bean class="org.springframework.http.converter.ResourceHttpMessageConverter"></bean>
<bean class="org.springframework.http.converter.xml.SourceHttpMessageConverter"></bean>
<bean class="org.springframework.http.converter.xml.XmlAwareFormHttpMessageConverter"></bean>
</list>
</property>
</bean>
<bean name="byteArrayConverter" class="org.springframework.http.converter.ByteArrayHttpMessageConverter"></bean>
<bean name="jaxbConverter" class="org.springframework.http.converter.xml.Jaxb2RootElementHttpMessageConverter"></bean>
<bean name="jsonConverter" class="org.springframework.http.converter.json.MappingJacksonHttpMessageConverter"></bean>
<bean name="handlerMapping" class="org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping">
<property name="useSuffixPatternMatch" value="false"></property>
</bean>
Spring puts all the converters behind an implementation of org.springframework.core.convert.ConversionService . You need to inject an instance of that interface into your class, you can read more in the spring documentation (including an example of how to inject it).
You can try injecting a bean of type RequestMappingHandlerAdapter but depending on your configuration, you might not have an instance!

Common value configuration in Spring XML

I have several Spring beans in which one of the property value for all of them are same String value. Is there a way where I can define this String in XML at one place and refer it in all beans at property value settings?
<bean id="somebean" class="test.SomeBean">
<property name="property1" ref="someValue"></property>
<property name="commonProperty" value="commonValue"></property>
<bean id="nextBean" class="test.NextBean">
<property name="property2" ref="someValue"></property>
<property name="commonProperty" value="commonValue"></property>
How to set commonValue in a seperate place and refer it in both places?
Try like this.
<bean id="commonConfig" abstract="true">
<property name="commonField" value="CommonValue"></property>
</bean>
<bean id="class1" class="com.dataclass.Class1" parent="commonConfig">
<property name="field1" value="value1"></property>
</bean>
<bean id="class2" class="com.dataclass.Class2" parent="commonConfig">
<property name="field2" value="value2"></property>
</bean>
Class1 & Class2 having one common field name "commonField", parent attribute is use for this common purpose only.
In Spring this is called bean definition inheritance(this is not java class inheritance, above example Class1 & n Class not inheriting in their respective java file.)
For more detail, look at Spring doc's link.
I've never tried it before, but this should work
<bean id="commonProp" class="java.lang.String">
<constructor-arg name="original" value="yourString"></constructor-arg>
</bean>
Then, in every bean you need to reference it:
<bean id="somebean" class="test.SomeBean">
<property name="property1" ref="someValue"></property>
<property name="commonProperty" ref="commonProp"></property>
</bean>
You can define your string properties in some "init_constants.properties" file. Then you should load properties file in spring xml:
<bean id="properties"
class="org.springframework.context.support.PropertySourcesPlaceholderConfigurer">
<property name="locations">
<list>
<value>classpath:db.properties</value>
<value>classpath:mail.properties</value>
<value>classpath:init_constants.properties</value>
</list>
</property>
</bean>
And after that you can inject this properties using xml:
<bean id="somebean" class="test.SomeBean">
<property name="property1" ref="{$prop1}"></property>
<property name="commonProperty" value="commonValue"></property>
</bean>
or in code using #Value annotation:
#Value(value="${prop1}")
private String property1;
Well If commonValue is string then you can put it in properties file and read it using #Value annotation.

Configuring Spring Bean to Re-Use Its Own Properties?

I'm fairly new to Spring and I need a bean that has two properties -- the second of which is an inline bean that references the first property. Something like this:
<bean id="aBean" class="com.sample.Bean">
<property name="propertyOne" value="something" />
<property name="propertyTwo">
<bean class="com.sample.AnotherBean">
<property name="propertyThree" ref="propertyOne />
</bean>
</property>
</bean>
Making propertyOne its own bean isn't an option here. What would be the best way to accomplish this? Thanks!
Only way that I can think of would be to create a bean for your common property and refer to this common property in both Bean and AnotherBean - any reason why this is not an option for you?
Any other way would not work, because of the dependency graph - aBean is dependent on Another Bean and so AnotherBean would get instantiated before aBean and would not be able to refer to a child bean property.
If there had not been this dependency, you could have used Spring-EL to refer to the property:
<property name="propertyThree" value="${aBean.propertyOne}"/>
You can create "propertyOne" as a separate bean.
and reference that from aBean , and your inline bean.
<bean id="propertyOne" class="java.lang.String">
<constructor-arg><value>"blabla"</value></constructor-arg>
</bean>
<bean id="aBean" class="com.test.SimpleBean">
<property name="name" ref="firstProperty" />
<property name="newBean">
<bean class="com.test.OtherSimplwBean">
<property name="otherName" ref="propertyOne" />
</bean>
</property>

How can I define multiple sessionfactory instances in Spring?

I would like to have multiple Hibernate SessionFactories in a spring application, all of them with identical configurations except for the DataSource. Ideally, I would acquire a particular SessionFactory by name. I need to be able to do this based on runtime state, and it isn't possible to determine which session factories I will need at application startup time. Basically, I need a SessionFactoryTemplate or something like it.
Is this possible? How do I go about doing it?
You might define an abstract bean and use bean inheritance. This means you'll have a bean definition that works as a template and you may have multiple beans just copying the attributes set by the parent bean.
Here's an example:
<bean id="abstractSessionFactory" abstract="true"
class="org.springframework.orm.hibernate3.LocalSessionFactoryBean">
<property name="mappingResources">
<list>
<value>product.hbm.xml</value>
</list>
</property>
<property name="hibernateProperties">
<value>
hibernate.dialect=org.hibernate.dialect.HSQLDialect
</value>
</property>
</bean>
<bean id="mySessionFactory" parent="abstractSessionFactory">
<property name="dataSource" ref="myDataSource"/>
...
</bean>
<bean id="mySessionFactory2" parent="abstractSessionFactory">
<property name="dataSource" ref="myDataSource2"/>
...
</bean>
Using the attribute 'abstract' you ensure that bean won't be instantiated and it will be used just as a template.
More info here: link text
Are you sure you need multiple SessionFactories? If all the mappings/configurations are the same and you just have multiple identical databases (e.g. in a multi-tenant app?), then how about having a single SessionFactory that connects to a DataSource which dynamically supplies the appropriate database connection?
See this question for more details:
And this blog post on Dynamic DataSource Routing in Spring.
I have no idea what your current bean definition looks like now, but wouldn't you just ... define a second SessionFactory?
<bean id="mySessionFactory1"
class="org.springframework.orm.hibernate3.LocalSessionFactoryBean">
<property name="dataSource" ref="myDataSource1"/>
<property name="mappingResources">
<list>
<value>product.hbm.xml</value>
</list>
</property>
<property name="hibernateProperties">
<value>
hibernate.dialect=org.hibernate.dialect.HSQLDialect
</value>
</property>
</bean>
<bean id="mySessionFactory2"
class="org.springframework.orm.hibernate3.LocalSessionFactoryBean">
<property name="dataSource" ref="myDataSource2"/>
...
</bean>
You could then simply just wire your DAOs up with one sessionFactory vs the other:
<bean id="myProductDao" class="product.ProductDaoImpl">
<property name="sessionFactory" ref="mySessionFactory1"/>
</bean>
<bean id="myCompanyDao" class="product.ProductDaoImpl">
<property name="sessionFactory" ref="mySessionFactory2"/>
</bean>
I don't know of an easy solution for your problem using Spring.
However, you could be able to use Hibernate Interceptors, provided that your particular databases/data-sources can be reached through one master/admin database connection. This blog post explains how in detail, but the gist of it is to dynamically replace table names in SQL statements that Hibernate generates, with qualified names identifying different databases. This is relatively easy to understand and maintain, and works well in my company's multi-tenant set-up.
Apart from that, you can try writing your own TransactionManager, using the HibernateTransactionManager as a starting point, adding support for working with multiple session factories. However, this would mean you having to really dive into Spring ORM support internals, and that is something I tried, but then scrapped in favor of the first approach. I'm sure it can be done with moderate effort, but the previous solution was already doing the job for us.

Categories

Resources