2011-04-13 48 views
0

目的是處理所有持久性異常並將其包裝爲簡單的一般異常,以便服務層可以輕鬆處理它們。春季因春季注入代理副作用導致AOP無法捕捉DAO異常

解決方法是使用AOP從DAO實現中攔截異常。下面是Spring配置:

<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的的實現是由彈簧注入,但是對於正常的db異常,它可以被攔截,並且對於連接異常,它失敗了。

我覺得因爲在spring注入DAO之前,它會先構造連接,然後失敗,所以它沒有調用目標操作。

我想攔截DAO的所有異常,如何通過彈簧注入來解決代理的副作用。

這裏是兩個不同的堆棧:

的數據庫邏輯錯誤:

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 

但如果是DB連接錯誤:

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, 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 

回答

1

您應該在Service方法上移動@Transactional註釋。當使用@Transaction註釋一個類時,Spring將創建一個代理。

問題是@Transactional攔截器(代理)試圖啓動一個事務,但它失敗,因爲沒有連接到數據庫。在你的DAOExceptionTranslator中沒有截獲錯誤,因爲它被執行了之前被攔截的代碼。

+0

解決方法是指出每個代理的順序。 添加關鍵字順序後,首先調用DBExceptions代理,更改後查看堆棧。 – 2011-04-13 07:01:43

+0

是的,那是另一種方式。但請注意,通常最好在服務層上保留@Transactional註釋,以保證多次調用Dao層的事務流程。 – 2011-04-13 07:14:37

+0

謝謝,我們將檢查服務層事務控制。 – 2011-04-13 08:59:39