开发者

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.

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜