AOP can not catch the DAO exception in spring due to proxy side effect by spring injection
The purpose is to handle all persistence exception and wrapped to simple general exception, so service layer can easily handle them.
The solution is to use AOP to intercept the exception from DAO implementation. Here is the spring configuration:
<bean id="DBExceptions" class="com.dao.impl.DAOExceptionTranslator" />
<aop:config>
<aop:aspect id="dbExceptionsAspect" ref="DBExceptions">
<aop:after-throwing throwing="ex"
pointcut="execution(* com.dao.impl.*.*(*))" method="doDAOActions" />
</aop:aspect>
</aop:config>
Here is DAO implementation:
@Transactional
public class UserDAOImpl extends GenericDAOImpl implements UserDAO {
@PersistenceContext
protected EntityManager entityManager;
@Override
public User findUserByUsername(String username) throws DAOException {
Query query = entityManager
.createQuery("select u from User u where u.username=:username");
query.setParameter("username", username);
Object userObject = query.getSingleResult();
return (User) userObject;
}
And here is the code to use the DAO:
private UserDAO userDAO;
public User getUserById(int id) throws UserServiceException {
try {
Object user = userDAO.findById(User.class, id);
...
The implementation of userDAO is injected by spring, but for normal db exception, it can be intercepted, and for connection exception, it failed.
I think because before spring injected the DAO implementation, it will first construct the connection, and it failed, so it hasn't call the target operation.
I want to intercept all exception from DAO, how to solve the proxy side effect by spring injection.
here is two different stack:
The db logical error:
UserDAOImpl.findUserByUsername(String) line: 23
NativeMethodAccessorImpl.invoke0(Method, Object, Object[]) line: not available [native method]
NativeMethodAccessorImpl.invoke(Object, Object[]) line: 39
DelegatingMethodAccessorImpl.invoke(Object, Object[]) line: 25
Method.invoke(Object, Object...) line: 597
AopUtils.invokeJoinpointUsingReflection(Object, Method, Object[]) line: 309
ReflectiveMethodInvocation.invokeJoinpoint() line: 183
ReflectiveMethodInvocation.proceed() line: 150
AspectJAfterThrowingAdvice.invoke(MethodInvocation) line: 55
ReflectiveMethodInvocation.proceed() line: 172
TransactionInterceptor.invoke(MethodInvocation) line: 110
ReflectiveMethodInvocation.proceed() line: 172
ExposeInvocationInterceptor.invoke(MethodInvocation) line: 89
ReflectiveMethodInvocation.proceed() line: 172
JdkDynamicAopProxy.invoke(Object, Method, Object[]) line: 202
$Proxy17.findUserByUsername(String) line: not available
UserService.getUser(String) line: 74
But if it is DB connection error:
at org.springframework.orm.jpa.JpaTransactionManager.doBegin(JpaTransactionManager.java:382)
at org.springfra开发者_运维知识库mework.transaction.support.AbstractPlatformTransactionManager.getTransaction(AbstractPlatformTransactionManager.java:371)
at org.springframework.transaction.interceptor.TransactionAspectSupport.createTransactionIfNecessary(TransactionAspectSupport.java:335)
at org.springframework.transaction.interceptor.TransactionInterceptor.invoke(TransactionInterceptor.java:105)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:172)
at org.springframework.aop.interceptor.ExposeInvocationInterceptor.invoke(ExposeInvocationInterceptor.java:89)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:172)
at org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:202)
at $Proxy17.findUserByUsername(Unknown Source)
at com.service.UserService.getUser(UserService.java:74)
How to solve the problem?
The solution is to indicate the sequence of each proxy, the change is
<aop:config>
<aop:aspect id="dbExceptionsAspect" ref="DBExceptions" order="1">
<aop:after-throwing throwing="ex"
pointcut="execution(* com.dao.impl.*.*(*))" method="doDAOActions" />
</aop:aspect>
</aop:config>
After add the key word order, DBExceptions proxy will be invoked first, see the stack after change.
DAOExceptionTranslator.doDAOActions(Exception) line: 12
NativeMethodAccessorImpl.invoke0(Method, Object, Object[]) line: not available [native method]
NativeMethodAccessorImpl.invoke(Object, Object[]) line: 39
DelegatingMethodAccessorImpl.invoke(Object, Object[]) line: 25
Method.invoke(Object, Object...) line: 597
AspectJAfterThrowingAdvice(AbstractAspectJAdvice).invokeAdviceMethodWithGivenArgs(Object[]) line: 621
AspectJAfterThrowingAdvice(AbstractAspectJAdvice).invokeAdviceMethod(JoinPointMatch, Object, Throwable) line: 603
AspectJAfterThrowingAdvice.invoke(MethodInvocation) line: 59
ReflectiveMethodInvocation.proceed() line: 172
ExposeInvocationInterceptor.invoke(MethodInvocation) line: 89
ReflectiveMethodInvocation.proceed() line: 172
JdkDynamicAopProxy.invoke(Object, Method, Object[]) line: 202
$Proxy17.findUserByUsername(String) line: not available
UserService.getUser(String) line: 74
You should move the @Transactional annotation on your Service method. When annotating a class with @Transaction Spring will create a proxy out of it.
The problem is that the @Transactional interceptor (the proxy) tries to initiate a transaction but it fails because there is no connection to the db. The error in not intercepted by your DAOExceptionTranslator because it is executed Before your intercepted code.
精彩评论