I am playing around with a Spring MVC + Hibernate + MySQL "Hello World" app, and am currently trying run the following integration test on a Spring MVC controller using jUnit.
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration({"file:src/main/webapp/WEB-INF/springapp-servlet.xml"})
public class InventoryControllerIT
{
#Autowired
private InventoryController controller;
#Test
public void handleRequest_anyRequest_returnsSuccessfully() throws Exception
{
ModelAndView modelAndView = this.controller.handleRequest(null, null);
}
}
However, every time I do so I get the following exception:
org.springframework.beans.factory.NoSuchBeanDefinitionException: No matching bean of type [springapp.web.InventoryController] found for dependency: expected at least 1 bean which qualifies as autowire candidate for this dependency.
Previously I hadn't implemented any real data access and the test passed fine, but now that I have added a Hibernate implementation of my DAO along with spring transaction management I get this error. Here are the relevant parts of my applet context configuration xml:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:p="http://www.springframework.org/schema/p"
xmlns:tx="http://www.springframework.org/schema/tx"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx.xsd">
<bean name="/hello.htm" class="springapp.web.InventoryController">
<property name="productManager" ref="productManager" />
<property name="productDao" ref="productDao" />
<property name="sessionFactory" ref="sessionFactory" />
</bean>
...
<!-- Hibernate -->
<bean id="sessionFactory"
class="org.springframework.orm.hibernate4.LocalSessionFactoryBean">
<property name="dataSource">
<ref bean="dataSource" />
</property>
<property name="hibernateProperties">
<props>
<prop key="hibernate.dialect">org.hibernate.dialect.MySQLDialect</prop>
<prop key="hibernate.show_sql">true</prop>
</props>
</property>
<property name="mappingJarLocations">
<list>
<value>WEB-INF/lib/springapp-dataaccess*.jar</value>
</list>
</property>
</bean>
<bean id="productDao" class="springapp.dataaccess.dao.ProductHibernateDao">
<property name="sessionFactory" ref="sessionFactory" />
</bean>
<tx:annotation-driven />
<bean id="transactionManager"
class="org.springframework.orm.hibernate4.HibernateTransactionManager"
p:sessionFactory-ref="sessionFactory" />
</beans>
If I remove the <tx:annotation-driven /> from the config then the above exception does not occur, but then the test fails because the data access call that occurs in the handler no longer has an open transaction. The app runs just fine outside of the test. Anyone have any ideas as to what the issue is?
When InventoryController implements any interfaces Spring by default applies transactional aspect to it using interface-based proxy. Such a proxy implements interfaces of InventoryController, but it's not a subclass of InventoryController, therefore it cannot be injected into a field of type InventoryController.
You either need to use interface as a type of the field to be autowired, or configure Spring to apply target-class-based proxy instead.
See also:
7.6 Proxying mechanisms
I had a similar issues while running the unit tests of a small library I was building.
replace your :
<tx:annotation-driven />
with
<tx:annotation-driven transaction-manager="transactionManager" proxy-target-class="true"/>
Note that on my project I also had to add the following dependency (maven project) for the unit tests:
<dependency>
<groupId>cglib</groupId>
<artifactId>cglib</artifactId>
<version>2.2.2</version>
<scope>test</scope>
</dependency>
Best regards.
Related
In our application, we want to set multiple data source with Spring and JPA. Hence we have created 2 entityManagerFactory, 2 data source and 2 transaction- manager.
web.xml
<param-value>
/WEB-INF/a_spring.xml
/WEB-INF/b_spring.xml
</param-value>
Persistence.xml
<?xml version="1.0" encoding="UTF-8"?>
<persistence version="1.0" xmlns="http://java.sun.com/xml/ns/persistence" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/persistence http://java.sun.com/xml/ns/persistence/persistence_1_0.xsd">
<persistence-unit name="db1" transaction-type="RESOURCE_LOCAL">
<class>com.rh.domain.RcA</class>
</persistence-unit>
<persistence-unit name="db2" transaction-type="RESOURCE_LOCAL">
<class>com.rh.domain.Rcb</class>
</persistence-unit>
</persistence>
a_spring.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:tx="http://www.springframework.org/schema/tx"
xmlns:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx-2.0.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop-2.0.xsd">
<bean class="org.springframework.orm.jpa.support.PersistenceAnnotationBeanPostProcessor"/>
<bean id = "RcMaintenanceService" class="com.rh.services.RcAbcMaintenanceServiceImpl" autowire="byName" />
<aop:config>
<aop:pointcut id="rOperation" expression="execution(* com.rh.services.*.*(..))"/>
<aop:advisor advice-ref="txAdvice" pointcut-ref="rOperation"/>
</aop:config>
<tx:advice id="txAdvice" transaction-manager="transactionManager">
<tx:attributes>
<tx:method name="*"/>
</tx:attributes>
</tx:advice>
<bean id="dataSource" class="org.springframework.jndi.JndiObjectFactoryBean">
<property name="jndiName" value="java:comp/env/jdbc/db1" />
</bean>
<bean id="transactionManager" class="org.springframework.orm.jpa.JpaTransactionManager">
<property name="entityManagerFactory" ref="entityManagerFactory"/>
<property name="dataSource" ref="dataSource"/>
</bean>
<bean id="entityManagerFactory" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
<property name="persistenceUnitName" value="db1" />
<property name="dataSource" ref="dataSource"/>
<property name="jpaVendorAdapter">
<bean class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter">
<property name="showSql" value="true"/>
<property name="generateDdl" value="false"/>
<property name="database" value="MYSQL" />
<property name="databasePlatform" value="org.hibernate.dialect.MySQL5Dialect"/>
</bean>
</property>
<property name="jpaDialect">
<bean class="org.springframework.orm.jpa.vendor.HibernateJpaDialect">
</bean>
</property>
</bean>
I also declare another entityManagetFactory,Transaction Manager and dataSource to b_spring.xml.
Error
Initialization of bean failed; nested exception is
org.springframework.beans.factory.NoSuchBeanDefinitionException: No
unique bean of type [javax.persistence.EntityManagerFactory] is
defined: expected single bean but found 2 Caused by:
org.springframework.beans.factory.NoSuchBeanDefinitionException: No
unique bean of type [javax.persistence.EntityManagerFactory] is
defined: expected single bean but found 2 at
org.springframework.beans.factory.BeanFactoryUtils.beanOfTypeIncludingAncestors(BeanFactoryUtils.java:303)
at
org.springframework.orm.jpa.support.PersistenceAnnotationBeanPostProcessor.findDefaultEntityManagerFactory(PersistenceAnnotationBeanPostProcessor.java:451)
at
org.springframework.orm.jpa.support.PersistenceAnnotationBeanPostProcessor.findEntityManagerFactory(PersistenceAnnotationBeanPostProcessor.java:428)
at
org.springframework.orm.jpa.support.PersistenceAnnotationBeanPostProcessor$AnnotatedMember.resolveEntityManager(PersistenceAnnotationBeanPostProcessor.java:582)
at
org.springframework.orm.jpa.support.PersistenceAnnotationBeanPostProcessor$AnnotatedMember.resolve(PersistenceAnnotationBeanPostProcessor.java:553)
at
org.springframework.orm.jpa.support.PersistenceAnnotationBeanPostProcessor$AnnotatedMember.inject(PersistenceAnnotationBeanPostProcessor.java:489)
In case of multiple data source configuration, we need to define which one will be considered as a primary data source. we can specify that using #Primary annotation in java config or primary=true in XML bean config.
As there is two entity manager being created in XML, we need to use #Qualifier to specify which bean should be injected where. In your case, something like this.
#PersistenceContext(unitName = "db1")
public void setEntityManager(#Qualifier("entityManagerFactory") EntityManager entityMgr) {
this.em = entityMgr;
}
for XML configuration, we can do something like this
<bean id="BaseService" class="x.y.z.BaseService">
<property name="em" ref="entityManagerFactory"/>
<property name="em1" ref="entityManagerFactory1"/>
</bean>
<bean id = "RcMaintenanceService" class="com.rh.services.RcAbcMaintenanceServiceImpl" autowire="byName" parent="BaseService"/>
Did you try providing the package details which contains your EntityManagerFactory bean?
You can provide the package details as a property in you bean definition -
<property name="packagesToScan" value="com.XX.XX.XX.XX" />
new property to be added in this block -
<bean id="entityManagerFactory1" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
<property name="persistenceUnitName" value="db2" />
<property name="dataSource" ref="dataSource1"/>
<-- add here for both beans -->
<property name="jpaVendorAdapter">
<bean class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter">
<property name="showSql" value="true"/>
<property name="generateDdl" value="false"/>
<property name="database" value="MYSQL" />
<property name="databasePlatform" value="org.hibernate.dialect.MySQL5Dialect"/>
</bean>
</property>
<property name="jpaDialect">
<bean class="org.springframework.orm.jpa.vendor.HibernateJpaDialect">
</bean>
</property>
</bean>
Also, you are missing the persistenceXmlLocation property -
<property name="persistenceXmlLocation" value="***/persistence.xml" />
The error message you've posted indicates that you're autowiring by type for an object of type EntityManagerFactory. None of the code you've shown so far contains such an injection, which implies that it's probably in some code you haven't yet posted.
If you were to post the full stack trace of the error, you'd be able to walk up the stack to see which bean contains the un-satisfiable reference to an EntityManagerFactory object, which in turn would let you change how you're referencing it to allow a reference to the specific bean you want.
Update
Based on the further information you provided (thank you) and some Googling, it seems as though other users have similarly found that specifying a unitName isn't enough to get the correct EntityManager injected. Several posts online back up #lucid's recommendation to use the #Qualifier annotation to get Spring to select the correct bean, but unfortunately that annotation was introduced in 2.5 so it's only available to you if you upgrade. (Which, given the age of the Spring framework that you're using, is probably a good idea, but that's a separate conversation.)
However, several users have indicated that an alternate approach is available in 2.0.5, using a single PersistenceUnitManager that references multiple data sources rather than multiple persistence units that each reference a single data source. From the official Spring docs: https://docs.spring.io/spring/docs/2.0.x/reference/orm.html#orm-jpa-multiple-pu.
Overall I'd suggest you consider upgrading to a version of Spring that's not more than a decade old, which would let you specify an #Qualifier annotation per #lucid's answer. But if that's not possible for some reason, the PersistenceUnitManager approach should give you a way to make it work within Spring 2.0.5.
There are a couple of things that don't look good to me. The name of the setters won't match the property names, I think that is important, the second thing is about the inheritance, some annotations sometimes only works in concrete classes not in base classes. I would try to change the base service as follow.
public class BaseService {
#PersistenceContext(unitName = "db2")
private EntityManager em;
#PersistenceContext(unitName = "db1")
private EntityManager em1;
public EntityManager getEm() {
return em;
}
protected EntityManager getEm2() {
return em1;
}
public void setEm(EntityManager entityMgr) {
this.em = entityMgr;
}
public void setEm1(EntityManager entityMgr) {
this.em = entityMgr;
}
}
If that does not work I probably try to remove the base class and see if I put the annotation under the concrete class I can make that works, I would do that moving just the annotations to the RcAbcMaintenanceServiceImpl class and remove the extend statement that inherits from the BaseService
Also, I noticed that the PersistenceContext annotation has another param https://docs.oracle.com/javaee/6/api/javax/persistence/PersistenceContext.html, so you could try to use the name as well to match the id on the bean definition.
I have faced very strange issue with autowiring Hibernate SessionFactory bean into my Service.
I'm able to find SessionFactory bean in Spring Context object. So no issues with creating such bean.
But I can not autowire this bean into my service when it is marked with #Transactional annotation. Factory field is null.
Once I remove this annotation - everything works good.
Service Class:
#Service
#Transactional
public class ExampleRunner implements Runnable{
#Autowired
SessionFactory sessionFactory;
...
}
applicationContext.xml:
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:tx="http://www.springframework.org/schema/tx"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-3.0.xsd">
<context:property-placeholder location="classpath:application.properties"
ignore-resource-not-found="true" />
<context:component-scan base-package="org.edu" />
<context:annotation-config />
<bean id="dataSource"
class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="driverClassName"
value="${jdbc.driverClassName:org.hsqldb.jdbcDriver}" />
<property name="url" value="${jdbc.url:jdbc:hsqldb:mem:myAppDb}" />
<property name="username" value="${jdbc.username:sa}" />
<property name="password" value="$jdbc.password:}" />
</bean>
<bean id="sessionFactory"
class="org.springframework.orm.hibernate4.LocalSessionFactoryBean">
<property name="dataSource" ref="dataSource" />
<property name="packagesToScan" value="org.edu" />
<property name="hibernateProperties">
<props>
<prop key="hibernate.dialect">
${hibernate.dialect:org.hibernate.dialect.HSQLDialect}
</prop>
<prop key="hibernate.hbm2ddl.auto">${hibernate.hbm2ddl.auto:create-drop}</prop>
<prop key="hibernate.show_sql">${hibernate.show_sql:true}</prop>
<prop key="hibernate.format_sql">true</prop>
</props>
</property>
</bean>
<tx:annotation-driven transaction-manager="txManager"
proxy-target-class="true" />
<bean id="txManager"
class="org.springframework.orm.hibernate4.HibernateTransactionManager">
<property name="sessionFactory" ref="sessionFactory" />
</bean>
</beans>
Usage:
public static void main(String[] args) {
Runnable runner = new ClassPathXmlApplicationContext("applicationContext.xml").getBean(ExampleRunner.class);
runner.run();
}
Seems like when I make #Transactional as class level annotation Spring creates CGLib proxy bean where SessionFactory field is null.
But when I use #Transational as method level annotation everything works good.
So I just want to understand such behavior.
What have I missed from Spring documentation?
I use Spring 4.1.4.RELEASE.
I got your error actually why you getting problem here because you not allowing container to auto-wired bean for you as you using java Main method it will not auto-wired bean that's why you getting error.
I want my read methods not to use a transaction since this is just not needed at all, I only mark my create/update methods with #Transactional. But how do I do this? I have a pretty basic configuration of Spring with etc...
SessionFactory is injected in my DAO, and in each method I call sessionFactory.getCurrentSession().doQueryStuff();
This, however results in this error:
org.hibernate.HibernateException: No Hibernate Session bound to thread, and configuration does not allow creation of non-transactional one here
If you need my Spring configuration:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:mvc="http://www.springframework.org/schema/mvc"
xmlns:oxm="http://www.springframework.org/schema/oxm"
xmlns:tx="http://www.springframework.org/schema/tx"
xmlns:util="http://www.springframework.org/schema/util"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop-3.0.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-3.0.xsd
http://www.springframework.org/schema/mvc
http://www.springframework.org/schema/mvc/spring-mvc-3.0.xsd
http://www.springframework.org/schema/oxm
http://www.springframework.org/schema/oxm/spring-oxm-3.0.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx-3.0.xsd
http://www.springframework.org/schema/util
http://www.springframework.org/schema/util/spring-util-3.0.xsd">
<context:annotation-config />
<context:component-scan base-package="be.howest.kidscalcula" />
<mvc:annotation-driven />
<bean id="viewResolver"
class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="prefix">
<value>/WEB-INF/views/</value>
</property>
<property name="suffix">
<value>.jsp</value>
</property>
</bean>
<bean id="myDataSource"
class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="driverClassName" value="com.mysql.jdbc.Driver" />
<property name="url" value="jdbc:mysql://localhost/kidscalcula" />
<property name="username" value="root" />
<property name="password" value="" />
</bean>
<bean class="org.springframework.orm.hibernate3.LocalSessionFactoryBean"
id="sessionFactory">
<property name="dataSource" ref="myDataSource" />
<property name="mappingResources">
<list>
<value>be/howest/kidscalcula/model/Foto.hbm.xml</value>
<value>be/howest/kidscalcula/model/Kindleerplanonderdeel.hbm.xml
</value>
<value>be/howest/kidscalcula/model/Klas.hbm.xml</value>
<value>be/howest/kidscalcula/model/Leerkracht.hbm.xml</value>
<value>be/howest/kidscalcula/model/Leerling.hbm.xml</value>
<value>be/howest/kidscalcula/model/Leerplan.hbm.xml</value>
<value>be/howest/kidscalcula/model/LeerplanOefenreeks.hbm.xml
</value>
<value>be/howest/kidscalcula/model/Leerplanonderdeel.hbm.xml</value>
<value>be/howest/kidscalcula/model/Niveau.hbm.xml</value>
<value>be/howest/kidscalcula/model/Oefenreeks.hbm.xml</value>
<value>be/howest/kidscalcula/model/Overgangsregel.hbm.xml</value>
<value>be/howest/kidscalcula/model/Rapport.hbm.xml</value>
<value>be/howest/kidscalcula/model/RapportLeerplanonderdeel.hbm.xml
</value>
<value>be/howest/kidscalcula/model/Schooljaar.hbm.xml</value>
<value>be/howest/kidscalcula/model/Subonderdeel.hbm.xml</value>
</list>
</property>
<property name="hibernateProperties">
<props>
<prop key="hibernate.dialect">org.hibernate.dialect.MySQLDialect</prop>
<prop key="hibernate.connection.pool_size">3</prop>
<prop key="hibernate.show_sql">true</prop>
<prop key="hibernate.format_sql">true</prop>
<prop key="hibernate.use_sql_comments">true</prop>
<prop key="hibernate.cache.use_second_level_cache">false</prop>
</props>
</property>
</bean>
<!-- Configure the multipart resolver -->
<bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
<!-- one of the properties available; the maximum file size in bytes -->
<property name="maxUploadSize" value="500000" />
</bean>
<!--
Transaction manager for a single Hibernate SessionFactory (alternative
to JTA)
-->
<bean id="transactionManager"
class="org.springframework.orm.hibernate3.HibernateTransactionManager">
<property name="sessionFactory">
<ref bean="sessionFactory" />
</property>
</bean>
<tx:annotation-driven />
Does this error have anything to do with the fact that Propagation is standard Required?
Mark the method as transactional and read only to ensure the transaction does not modify any data:
#Transactional(readOnly=true)
This is an useful optimization when working with Hibernate. To read more on the implications of this annotation, here is a great post: Read-Only transactions with Spring and Hibernate
As the message clearly says, no session is bound to the thread and the configuration you have specified does NOT allow a "non-transaction" session to be created. I will try to explain each these of scenarios.
When Spring's transaction is enabled in your configuration(as you have done), Spring will wrap the SessionFactory object with a proxy that prevents creation of new sessions, if not already present, when SessionFactory.getCurrentSession is called. The proxy will only get the current session that is bound to the thread local and no ad-hoc session(non-transactional) creation is allowed by your code. This is how your configuration is preventing non-trasactional session creation.
When you annotate a method/class with #Transactional, Spring's TransactionInterceptor will create a session and bind it to the current thread when the method is called so that it is available later. Spring's OpenSessionInViewFilter, if enabled, will also bind a session to the current thread. Since you aren't doing any of these, no thread bound session is found.
What you should do is annotate your class with #Transactional(readOnly = True) and then annotate your create/update/delete methods with #Transactional(readOnly = False). It is important that you have a read-only transaction when you are reading data. This assures you that nobody can commit data during that call.
It is possible to start a session outside of a transaction even when using Spring transaction management. Call sessionFactory.openSession() to get hold of a new session. This session is local in that it is not bound to a thread and so won't be returned when you call sessionFactory.getCurrentSession(). You'll have to manage it and make sure it's properly closed at the the end of your session or if an error occurs.
You can not access the data without an (hibernate) session.
One way to provide such a session is a (when using spring):
org.springframework.orm.hibernate3.support.OpenSessionInViewFilter
or
org.springframework.orm.jpa.support.OpenEntityManagerInViewFilter
(this depends on the way you use your persistence provider)
The positive side effect of this fiters is, that the session is still open when the JSP Views are rendered - so you will have no lazy loading problems when accessing such a not yet loaded property while rendering.
#see http://community.jboss.org/wiki/OpenSessioninView
<bean id="projectService" class="org.springframework.transaction.interceptor.TransactionProxyFactoryBean">
<property name="transactionManager" ref="transactionManager"/>
<property name="target">
<bean class="com.company.project.company.services.ServiceImpl" init-method="init">
<property name="HRappsdao" ref="HRappsdao"/>
<property name="projectdao" ref="projectdao"/>
</bean>
</property>
<property name="transactionAttributes">
<props>
<prop key="store*">PROPAGATION_REQUIRED</prop>
<prop key="update*">PROPAGATION_REQUIRED</prop>
<prop key="remove*">PROPAGATION_REQUIRED</prop>
<prop key="bulkUpdate*">PROPAGATION_REQUIRED</prop>
<prop key="*">PROPAGATION_SUPPORTS,readOnly</prop>
</props>
</property>
</bean>
i have 2 datasource HRappsdao and projectdao, both are using different sessionFactory. in this case, my transactionmanager should be using which sessionfactory? (hrappsdao or projectdao) ?
editted
<bean id="transactionManager"
class="org.springframework.orm.hibernate3.HibernateTransactionManager">
<property name="sessionFactory" > //my HRappsdao using same
<ref local="sessionFactory"/>
</property>
</bean>
Actually, you're not showing the configuration of your transaction manager so I'm not really sure of what your are currently using but, quoting the documentation:
JTA (usually through JtaTransactionManager) is necessary for accessing multiple transactional resources within the same transaction.
With Spring 2.5, consider using the "new" <tx:jta-transaction-manager/> configuration element for automatic detection of the underlying JTA-based transaction platform (works with most app servers). See the chapter 9.8. Application server-specific integration for more details on this.
If you are using an older version of Spring, you'll need to configure your JtaTransactionManager manually. This will require some knowledge of your application server as the JNDI location of the JTA TransactionManager is specific to each J2EE server.
Please provide more details (like the version of Spring and the application server you are using if you need more guidance).
UPDATE: As I said, when using multiple datasources, you need to use the JtaTransactionManager and not the HibernateTransactionManager (see the javadoc). If you are using Spring 2.5, update your Spring configuration as below:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:tx="http://www.springframework.org/schema/tx"
xsi:schemaLocation="
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-2.5.xsd">
<tx:jta-transaction-manager />
<!--
<bean id="transactionManager" class="org.springframework.orm.hibernate3.HibernateTransactionManager">
<property name="sessionFactory">
//my HRappsdao using same
<ref local="sessionFactory" />
</property>
</bean>
-->
...
</beans>
Note that you'll need something like JOTM with Tomcat or Jetty. You should maybe consider moving to a J2EE app server like JBoss or Glassfish.
I have a Spring web application which is configured to use JDK proxying for AOP. The AOP annotations (such as #Transactional) are declared on the interfaces, rather than the implementation classes.
The application itself works fine, but when I run the unit tests, it seems to be attempting to use CGLIB for the AOP functionality (instead of JDK proxying). This causes the tests to fail - I've appended the stack trace below.
I don't understand why CGLIB is being used when I run the tests, because the Spring configuration is largely the same as when the application is running. One possibly significant difference is that the test configuration uses a DataSourceTransactionManager instead of a JTA transaction manager. The test classes themselves all extend AbstractJUnit4SpringContextTests, could it be that this class is somehow hard-wired to use CGLIB?
Caused by: org.springframework.aop.framework.AopConfigException: Could not generate CGLIB subclass of class [class $Proxy25]: Common causes of this problem include using a final class or a non-visible class; nested exception is java.lang.IllegalArgumentException: Cannot subclass final class class $Proxy25
at org.springframework.aop.framework.Cglib2AopProxy.getProxy(Cglib2AopProxy.java:213)
at org.springframework.aop.framework.ProxyFactory.getProxy(ProxyFactory.java:110)
at org.springframework.aop.framework.autoproxy.AbstractAutoProxyCreator.createProxy(AbstractAutoProxyCreator.java:488)
at org.springframework.aop.framework.autoproxy.AbstractAutoProxyCreator.wrapIfNecessary(AbstractAutoProxyCreator.java:363)
at org.springframework.aop.framework.autoproxy.AbstractAutoProxyCreator.postProcessAfterInitialization(AbstractAutoProxyCreator.java:324)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.applyBeanPostProcessorsAfterInitialization(AbstractAutowireCapableBeanFactory.java:361)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean(AbstractAutowireCapableBeanFactory.java:1343)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:473)
... 79 more
Caused by: java.lang.IllegalArgumentException: Cannot subclass final class class $Proxy25
at net.sf.cglib.proxy.Enhancer.generateClass(Enhancer.java:446)
at net.sf.cglib.transform.TransformingClassGenerator.generateClass(TransformingClassGenerator.java:33)
at net.sf.cglib.core.DefaultGeneratorStrategy.generate(DefaultGeneratorStrategy.java:25)
at net.sf.cglib.core.AbstractClassGenerator.create(AbstractClassGenerator.java:216)
at net.sf.cglib.proxy.Enhancer.createHelper(Enhancer.java:377)
at net.sf.cglib.proxy.Enhancer.create(Enhancer.java:285)
at org.springframework.aop.framework.Cglib2AopProxy.getProxy(Cglib2AopProxy.java:201)
... 86 more
EDIT: One of the commentators requested that I post the Spring configuration. I've included it below in abbreviated form (i.e. irrelevant beans and XML namespaces omitted).
spring-servlet.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans>
<!-- ANNOTATION SUPPORT -->
<!-- Include basic annotation support -->
<context:annotation-config/>
<!-- CONTROLLERS -->
<!-- Controllers, force scanning -->
<context:component-scan base-package="com.onebigplanet.web.controller,com.onebigplanet.web.ws.*"/>
<!-- Post-processor for #Aspect annotated beans, which converts them into AOP advice -->
<bean class="org.springframework.aop.aspectj.annotation.AnnotationAwareAspectJAutoProxyCreator">
<property name="proxyTargetClass" value="true"/>
</bean>
<!-- An #Aspect bean that converts exceptions thrown in POJO service implementation classes to runtime exceptions -->
<bean id="permissionAdvisor" class="com.onebigplanet.web.advisor.PermissionAdvisor"/>
<bean id="businessIntelligenceAdvisor" class="com.onebigplanet.web.advisor.bi.BusinessIntelligenceAdvisor"/>
<!-- Finds the controllers and sets an interceptor on each one -->
<bean class="org.springframework.web.servlet.mvc.annotation.DefaultAnnotationHandlerMapping">
<property name="interceptors">
<list>
<bean class="com.onebigplanet.web.interceptor.PortalInterceptor"/>
</list>
</property>
</bean>
<!-- METHOD HANDLER ADAPTER -->
<!-- Finds mapping of url through annotation on methods of Controller -->
<bean class="org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter">
<property name="cacheSeconds" value="0"/>
<property name="webBindingInitializer">
<bean class="com.onebigplanet.web.binder.WebBindingInitializer"/>
</property>
</bean>
</beans>
applicationContext-service.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans>
<!-- Declares a bunch of bean post-processors -->
<context:annotation-config/>
<context:component-scan base-package="com.onebigplanet.service.impl,com.onebigplanet.dao.impl.mysql" annotation-config="false"/>
<!-- Property configurer -->
<bean id="propertyConfigurer" class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
<property name="location" value="/WEB-INF/obp-service.properties" />
</bean>
<!-- Post-processor for #Aspect annotated beans, which converts them into AOP advice -->
<bean class="org.springframework.aop.aspectj.annotation.AnnotationAwareAspectJAutoProxyCreator"/>
<!-- An #Aspect bean that converts exceptions thrown in service implementation classes to runtime exceptions -->
<bean id="exceptionAdvisor" class="com.onebigplanet.service.advisor.ExceptionAdvisor"/>
<bean id="cachingAdvisor" class="com.onebigplanet.service.advisor.CacheAdvisor"/>
<bean id="businessIntelligenceAffiliateAdvisor" class="com.onebigplanet.service.advisor.BusinessIntelligenceAffiliateAdvisor"/>
<!-- Writable datasource -->
<jee:jndi-lookup id="dataSource" jndi-name="java:/ObpDS"/>
<!-- ReadOnly datasource -->
<jee:jndi-lookup id="readOnlyDataSource" jndi-name="java:/ObpReadOnlyDS"/>
<!-- Map the transaction manager to allow easy lookup of a UserTransaction -->
<bean id="transactionManager" class="org.springframework.transaction.jta.JtaTransactionManager"/>
<!-- Annotation driven transaction management -->
<tx:annotation-driven transaction-manager="transactionManager"/>
</beans>
applicationContext-test.xml This is only included when running the unit tests. It's purpose is to overwrite some of the beans declared in the other config files.
<?xml version="1.0" encoding="UTF-8"?>
<beans>
<!-- Overwrite the property configurer bean such that it reads the test properties file instead -->
<bean id="propertyConfigurer" class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
<property name="location" value="/obp-test.properties"/>
</bean>
<!-- All DAOs should use the test datasource -->
<bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource">
<property name="driverClassName" value="${testDataSource.driverClassName}"/>
<property name="url" value="${testDataSource.url}"/>
<property name="username" value="${testDataSource.username}"/>
<property name="password" value="${testDataSource.password}"/>
</bean>
<bean id="readOnlyDataSource" class="org.apache.commons.dbcp.BasicDataSource">
<property name="driverClassName" value="${testDataSource.driverClassName}"/>
<property name="url" value="${testDataSource.url}"/>
<property name="username" value="${testDataSource.username}"/>
<property name="password" value="${testDataSource.password}"/>
</bean>
<!--
Overwrite the JTA transaction manager bean defined in applicationContent-service.xml with this one because
the implementation of the former is provided by JBoss
-->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource" />
</bean>
<beans>
Sounds like you're referencing an implementation class instead of an interface. There is an excerpt here with more detail.
A Spring forum post: "Mixing JDK and CGLIB proxies"
A great blog post explaining pros and cons of JDK vs. CGLIB proxies.
Hey Jean, CGLib proxies are created by subclassing the class to be proxied -- you're attempting to proxy another proxy which isn't allowed since proxies are themselves final classes. Hence:
Caused by: java.lang.IllegalArgumentException: Cannot subclass final class class $Proxy25
I don't know if the solution was already shared and I am also sure the original requester must have found a solution, since it is a one year old query. For public interest however let me mention it here. Spring was using CGLIB because of the following declaration.
<!-- Post-processor for #Aspect annotated beans, which converts them into AOP advice -->
<bean class="org.springframework.aop.aspectj.annotation.AnnotationAwareAspectJAutoProxyCreator">
<property name="proxyTargetClass" value="true"/>
</bean>
The property should be set to false, so that the CGLIB is not triggered instead JDK Dynamic Proxying.
<property name="proxyTargetClass" value="false"/>
Hope that helps.