2014-12-22 4 views
2

DB에서 작업 (생성, 업데이트, 삭제)을 기록해야하는 측면 (아래 참조)이 있습니다. preProcess 또는 postProcess 메소드에서 발생하는 조치 로깅에 따라 다름. 이 작업을 통해 어떤 실패가 발생하면 아무 것도 기록해서는 안됩니다. 나는. 생성이 발생하지 않았다면, 로깅 할 필요가 없습니다.트랜잭션이 aspectj에서 작동하지 않습니다

나는 그것을 시험해 보았다. 조인 시점에 RunTimeException을 던지고 db에 새로운 로그가 없다고 예상합니다. 불행히도 조인 포인트에서 예외가 발생하더라도 새 로그가 저장됩니다.

측면 :

@Component 
@Aspect 
public class LoggingAspect { 
    @Autowired 
    private ApplicationContext appContext; 
    @Autowired 
    private LoggingService loggingService; 

    @Around("@annotation(Loggable)") 
    @Transactional 
    public void saveActionMessage(ProceedingJoinPoint joinPoint) throws Throwable { 
     MethodSignature ms = (MethodSignature) joinPoint.getSignature(); 
     Loggable m = ms.getMethod().getAnnotation(Loggable.class); 
     LoggingStrategy strategy = appContext.getBean(m.strategy()); 
     Object argument = joinPoint.getArgs()[0]; 
     strategy.preProcess(argument); 
     joinPoint.proceed(); 
     strategy.postProcess(argument); 
    } 
} 

TestApplicationConfig :

<context:spring-configured/> 
    <import resource="applicationConfig-common.xml"/> 
    <import resource="applicationConfig-security.xml"/> 
    <aop:aspectj-autoproxy/> 

    <util:map id="testValues"> 
     <entry key="com.exadel.mbox.test.testSvnFile" value="${svnFolder.configPath}${svnRoot.file[0].fileName}"/> 
     <entry key="com.exadel.mbox.test.testCommonRepositoryPath" value="${svnRoot.commonRepositoryPath}"/> 
     <entry key="com.exadel.mbox.test.testMailFile" value="${mailingList.configPath}"/> 
    </util:map> 

    <context:component-scan base-package="com.exadel.report.common" /> 

    <!-- Jpa Repositories --> 
    <jpa:repositories base-package="com.exadel.report.common.dao" /> 

    <tx:annotation-driven proxy-target-class="true" 
          transaction-manager="txManager" mode="aspectj"/> 

    <bean id="txManager" 
      class="org.springframework.jdbc.datasource.DataSourceTransactionManager"> 
     <property name="dataSource" ref="dataSource"/> 
    </bean> 

    <!-- Data Source --> 
    <bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource"> 
     <property name="driverClassName" value="org.hsqldb.jdbcDriver" /> 
     <property name="url" value="jdbc:hsqldb:mem:testdb" /> 
     <property name="username" value="sa" /> 
     <property name="password" value="" /> 
    </bean> 

    <!-- Entity Manager --> 
    <bean id="entityManagerFactory" 
      class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean"> 
     <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="true"/> 
       <property name="databasePlatform" value="org.hibernate.dialect.HSQLDialect"/> 
      </bean> 
     </property> 
     <property name="persistenceUnitName" value="exviewer-test"/> 
    </bean> 

    <!-- Transaction Manager --> 
    <bean id="transactionManager" class="org.springframework.orm.jpa.JpaTransactionManager"> 
     <property name="entityManagerFactory" ref="entityManagerFactory" /> 
    </bean> 

[업데이트]

LoggingStrategy :

public interface LoggingStrategy { 
    public void preProcess(Object obj); 
    public void postProcess(Object obj); 
} 

BaseLoggingStrategy :

public class BaseLoggingStrategy implements LoggingStrategy { 
    @Override 
    public void preProcess(Object obj) {} 

    @Override 
    public void postProcess(Object obj) {} 
} 

UpdateProcessStrategy : aspcet에 의해 차단

@Service 
public class UpdateProcessStrategy extends BaseLoggingStrategy { 
    @Autowired 
    private LoggingService loggingService; 
    @Autowired 
    private UserService userService; 
    @Autowired 
    DeviceService deviceService; 
    private Device currentDevice; 

    @Override 
    @Transactional 
    public void preProcess(Object obj) { 
     currentDevice = (Device) obj; 
     Device previousDevice = deviceService.getById(currentDevice.getId()); 
     String deviceDataBeforeUpdate = deviceService.getDeviceDetailsInJSON(previousDevice); 
     String deviceDataAfterUpdate = deviceService.getDeviceDetailsInJSON(currentDevice); 

     String login = userService.getCurrentUser().getLogin(); 
     String actionMessage = LoggingMessages.DEVICE_UPDATE.name(); 

     loggingService.save(
       new Logging(
         login, 
         actionMessage, 
         deviceDataBeforeUpdate, 
         deviceDataAfterUpdate, 
         new Date()) 
     ); 
    } 

    @Override 
    public void postProcess(Object obj) {} 
} 

클래스 :

@Service 
public class DeviceService { 
    @Loggable(value = LoggingMessages.DEVICE_CREATE, strategy = CreateProcessStrategy.class) 
    @Transactional 
    public void create(Device device) { 
     createOrUpdate(device); 
    } 

    @Loggable(value = LoggingMessages.DEVICE_UPDATE, strategy = UpdateProcessStrategy.class) 
    @Transactional 
    public void update(Device device) { 
     createOrUpdate(device); 
    } 

    private void createOrUpdate(Device device) { 
     deviceRepository.save(device);   
    } 

    @Loggable(value = LoggingMessages.DEVICE_REMOVE, strategy = RemoveProcessStrategy.class) 
    public void remove(Long deviceId) { 
     deviceRepository.delete(deviceId); 
    } 
} 

Loggable 주석 :

@Retention(RetentionPolicy.RUNTIME) 
@Target(ElementType.METHOD) 
public @interface Loggable { 
    LoggingMessages value(); 
    Class<? extends LoggingStrategy> strategy(); 
} 

업데이트 작업에 대한 로그가 포함되어 id, created_dtm, 작업 (DEVICE_UPDATE), device_data_before_action_on_the_device (json 형식), device_data_after_action_on_the_device (json 형식), created_by.

+0

'LoggingStrategy'에 대한 코드와 aspect에 의해 차단 된 대상 클래스를 게시하십시오. 그러면 전체 로그를 출력 할 수 있도록 로그 출력도 생성하십시오. 당신은 평범한 산문에서 중요한 부분들만을 설명하지만, 산문은 코드 없이는 분명히 이해하기에는 너무 모호합니다. 'Loggable' 주석 코드도 좋을 것입니다. – kriegaex

+0

나는 언급 한 모든 것을 추가했습니다. – Vadzim

+0

나는 여전히 문제를 이해하지 못한다 : 당신의 충고 내에서'proceed()'전에'preProcess() '를 호출한다. 그래서 joinpoint에서 실제 예외가 발생하기 전에 어떤 것이 기록된다는 것이 분명해야한다. 코드를 테스트하지 않아도 확인할 수 있습니다. 이를 변경하려면 일반적으로 로그인하는 방법이나 조언을 변경해야합니다. 그대로, 코드는 당신이 말하는대로합니다. 아니면 다른 문제가 있습니까? 기록 된 내용과 기록 ​​할 내용에 대한 세부 정보는 아직 게시하지 않았습니다. – kriegaex

답변

0

면책 조항 : 실제로 저는 스프링 전문가가 아니며, 다른 누군가가 당신을 도울 수 있습니다. 나의 전문 분야 AspectJ, 내가 당신의 질문을 찾은 방법이다. 사용자 측면의 조언 LoggingAspect.saveActionMessage(..)

  • @Transactional 주석 :

    어쨌든, 당신은 여기에 두 가지 문제가 있습니다. 사실 내가 전혀 작동하지 않는다면 (웹상의 aspect 메소드/advice에 대해 @Transactional을 사용하는 예제가 없다는 것을 알았지 만 잘못된 방법으로 검색했을 수도 있습니다) 왜냐하면 Spring의 선언적 트랜잭션 처리가 프록시 기반 기술을 통해 구현되기 때문입니다. AOP와 마찬가지로. 자세한 내용은 스프링 설명서의 chapter 12 about transaction management을 읽으십시오. 특히 chapter 12.5.1. 나는 당신이 원하는 것을 할 수있는 길을 찾을 것이라고 확신합니다.

  • 중첩 된 거래 (예 : UpdateProcessStrategy.preProcess(..)은 트랜잭션 인 것을 의미하는 매우 충고에 의해 호출되지만 역시 @Transactional으로 선언됩니다. 따라서 거래 내에서 거래가 이루어집니다. 스프링이 어떻게 처리하는지, 나는 모른다. 그러나 tutorial about Spring transaction propagation에는 계몽 세부 사항이 포함되어있을 수도있다.

스프링 설명서에는 프로그래밍 방식으로, 주석을 통해 선언적으로, XML 기반 <tx:advice> 등이 트랜잭션 동작을 구현하는 몇 가지 방법이 나와 있습니다. 어떤 방법이 최선인지 모르겠다. 나는 단지 일반적인 힌트를 제공하기를 원했다.

+0

죄송합니다, 새해 전날 저녁에 답변하는 것을 잊었습니다. 불행히도, 나는 내 문제의 해결책을 찾지 못했습니다. 그러나 나는 당신의 대답이 유용하다고 느낍니다. 고맙습니다! 문제를 해결하면 알려 드리겠습니다. – Vadzim

+0

아마도 약간의 GitHub 저장소를 공유 할 수 있습니다. 그러면 여기 누군가가 문제를 재현하여 해결책을 찾을 수 있습니다. – kriegaex