由于弹簧注入的代理副作用,AOP无法在弹簧中捕获DAO异常

问题描述

| 目的是处理所有持久性异常并将其包装为简单的常规异常,以便服务层可以轻松地处理它们。 解决方案是使用AOP拦截DAO实现中的异常。这是弹簧配置:
<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>
这是DAO的实现:
@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;
    }
这是使用DAO的代码
private UserDAO userDAO;
public User getUserById(int id) throws UserServiceException {
        try {
            Object user = userDAO.findById(User.class,id);
...
userDAO的实现是由spring注入的,但是对于普通的db异常,它可以被拦截,而对于连接异常,则它失败。 我认为是因为在spring注入DAO实现之前,它将首先构造连接,但是失败了,因此它没有调用目标操作。 我想拦截来自DAO的所有异常,如何通过弹簧注入解决代理的副作用。 这是两个不同的堆栈: db逻辑错误
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,Object[]) line: 202   
    $Proxy17.findUserByUsername(String) line: not available 
    UserService.getUser(String) line: 74
但是如果是数据库连接错误
at org.springframework.orm.jpa.JpaTransactionManager.dobegin(JpaTransactionManager.java:382)
    at org.springframework.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)
如何解决问题? 解决方案是指示每个代理的顺序,更改为
<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>
添加关键字顺序后,将首先调用DBExceptions代理,更改后请参见堆栈。
DAOExceptionTranslator.doDAOActions(Exception) line: 12 
    NativeMethodAccessorImpl.invoke0(Method,Object...) line: 597  
    AspectJAfterThrowingAdvice(AbstractAspectJAdvice).invokeAdviceMethodWithGivenArgs(Object[]) line: 621   
    AspectJAfterThrowingAdvice(AbstractAspectJAdvice).invokeAdviceMethod(JoinPointMatch,Throwable) line: 603   
    AspectJAfterThrowingAdvice.invoke(MethodInvocation) line: 59    
    ReflectiveMethodInvocation.proceed() line: 172  
    ExposeInvocationInterceptor.invoke(MethodInvocation) line: 89   
    ReflectiveMethodInvocation.proceed() line: 172  
    JdkDynamicAopProxy.invoke(Object,Object[]) line: 202   
    $Proxy17.findUserByUsername(String) line: not available 
    UserService.getUser(String) line: 74
    

解决方法

您应该在Service方法上移动@Transactional批注。当使用@Transaction注释一个类时,Spring将从中创建一个代理。 问题在于@Transactional拦截器(代理)尝试启动事务,但是由于没有与数据库的连接而失败。该错误未被DAOExceptionTranslator拦截,因为它是在拦截代码之前执行的。