2010-08-06 3 views
67

저는 Spring Transaction을 처음 사용합니다. 내가 정말 이상하다고 생각한 것, 아마도 이것을 제대로 이해했을 것입니다. 메서드 수준에서 트랜잭션을 수행하고 싶었고 동일한 클래스 내에 호출자 메서드가 있고 그것을 좋아하지 않는 것처럼 보입니다.이 메서드는 별도의 클래스에서 호출해야합니다. 나는 그것이 어떻게 가능한지 이해하지 못한다. 누구든지이 문제를 해결하는 방법을 알고 있다면 크게 감사하겠습니다. 주석 처리 된 트랜잭션 메소드를 호출 할 때 동일한 클래스를 사용하고 싶습니다.Spring @Transaction 메서드 호출이 같은 클래스 내의 메서드로 작동하지 않습니까?

public class UserService { 

    @Transactional 
    public boolean addUser(String userName, String password) { 
     try { 
      // call DAO layer and adds to database. 
     } catch (Throwable e) { 
      TransactionAspectSupport.currentTransactionStatus() 
        .setRollbackOnly(); 

     } 
    } 

    public boolean addUsers(List<User> users) { 
     for (User user : users) { 
      addUser(user.getUserName, user.getPassword); 
     } 
    } 
} 

답변

70

그것은 봄 AOP의 제한이다 : 여기

는 코드입니다. (동적 객체 및 CGLIB)

AspectJ를 사용하여 트랜잭션을 처리하도록 Spring을 구성하면 코드가 작동합니다.

간단하고 아마도 가장 좋은 대안은 코드를 리팩토링하는 것입니다. 예를 들어 사용자를 처리하는 클래스와 각 사용자를 처리하는 클래스가 있습니다. 그런 다음 Spring AOP를 사용하는 기본 트랜잭션 처리가 작동합니다. 거래에 AspectJ를를 사용하는 스프링을 사용하려면 AspectJ를와

을 트랜잭션을 처리하기위한

구성 팁, 당신은 AspectJ를로 모드를 설정해야합니다

<tx:annotation-driven mode="aspectj"/> 

당신이 이전에 스프링을 사용하는 경우 버전 3.0 이상이면 스프링 설정에도 추가해야합니다 :

<bean class="org.springframework.transaction.aspectj 
     .AnnotationTransactionAspect" factory-method="aspectOf"> 
    <property name="transactionManager" ref="transactionManager" /> 
</bean> 
+0

감사합니다. 지금은 코드를 리팩토링했지만 AspectJ를 사용하여 예제를 보내거나 유용한 링크를 제공 해줄 수 있습니까? 미리 감사드립니다. 마이크. – Mike

+0

내 대답에 트랜잭션 특정 AspectJ 구성이 추가되었습니다. 나는 그것이 도움이되기를 바랍니다. – Espen

+1

당신의 모든 도움에 감사드립니다. 그것은 작동! – Mike

42

Spring의 AOP 프록시는 확장하지 않고 호출을 가로 채기 위해 서비스 인스턴스를 래핑합니다. 이것은 서비스 인스턴스 내에서 "this"에 대한 모든 호출이 해당 인스턴스에서 직접 호출되고 랩핑 프록시에 의해 인터셉트 될 수 없다는 효과가 있습니다 (프록시는 그러한 호출을 인식하지 못합니다). 한 가지 해결책이 이미 언급되었습니다. 또 다른 멋진 방법은 서비스의 인스턴스를 서비스 자체에 삽입하고 트랜잭션을 처리하는 프록시가 될 삽입 된 인스턴스에서 메소드를 호출하는 것입니다. 그러나 서비스 빈은 싱글이 아닌 경우이 너무 나쁜 부작용이있을 수 있음에 유의하십시오

<bean id="userService" class="your.package.UserService"> 
    <property name="self" ref="userService" /> 
    ... 
</bean> 

public class UserService { 
    private UserService self; 

    public void setSelf(UserService self) { 
     this.self = self; 
    } 

    @Transactional 
    public boolean addUser(String userName, String password) { 
     try { 
     // call DAO layer and adds to database. 
     } catch (Throwable e) { 
      TransactionAspectSupport.currentTransactionStatus() 
       .setRollbackOnly(); 

     } 
    } 

    public boolean addUsers(List<User> users) { 
     for (User user : users) { 
      self.addUser(user.getUserName, user.getPassword); 
     } 
    } 
} 
+2

이 경로를 선택하거나 (좋은 디자인인지 여부는 다른 문제) 생성자 주입을 사용하지 않으면 [this 질문 : (가) http://stackoverflow.com/questions/5152686/self-injection-with-spring – Jeshurun

0

당신은 같은 클래스 내부의 BeanFactory를 autowire가하고 할 수있는

getBean(YourClazz.class)

그것을 자동으로 클래스를 프록 시화하고 @ Transactional 또는 다른 aop 주석을 고려합니다.

+2

나쁜 연습으로 간주됩니다. 심지어 콩 자체에 재귀 적으로 주입하는 것이 좋습니다. getBean (clazz) 사용하여 타이트한 코드 내의 Spring ApplicationContext 클래스에 강한 의존성을 가지며 클래스를 변경하는 경우 스프링을 래핑하는 경우 (클래스가 변경 될 수 있음) 작동하지 않을 수 있습니다. –

5

이것은 자체 호출을위한 나의 해결책입니다.

private SBMWSBL self; 
@Autowired private ApplicationContext applicationContext; 

@PostConstruct 
public void postContruct(){ 
    self =applicationContext.getBean(SBMWSBL.class); 
}