@Transactional 어노테이션을 어디에 넣어야합니까? 인터페이스 정의 또는 구현 클래스에서?


92

코드 제목의 질문 :

@Transactional (readonly = true)
public interface FooService {
   void doSmth ();
}


public class FooServiceImpl implements FooService {
   ...
}

vs

public interface FooService {
   void doSmth ();
}

@Transactional (readonly = true)
public class FooServiceImpl implements FooService {
   ...
}

답변:


122

에서 http://static.springsource.org/spring/docs/2.0.x/reference/transaction.html

Spring 팀의 권장 사항은 @Transactional인터페이스에 주석을다는 대신 주석으로 구체적인 클래스에만 주석 을다는 것입니다. 확실히 @Transactional인터페이스 (또는 인터페이스 메서드)에 주석을 배치 할 수 있지만 이는 인터페이스 기반 프록시를 사용하는 경우에만 예상대로 작동합니다. 주석이 있다는 사실 상속되지 당신이 다음 클래스 기반의 프록시를 사용하는 경우 트랜잭션 설정은 클래스 기반의 프록시 인프라에 의해 인식되지 않고 객체는 트랜잭션 프록시에 싸여되지 않음을 의미를 (확실히 될 것이라고 나쁜 ) . 따라서 Spring 팀의 조언을 따르고 구체적인 클래스 (및 구체적인 클래스의 메서드)에만 주석을 추가하십시오 @Transactional.

참고 :이 메커니즘은 프록시를 기반으로하기 때문에 프록시를 통해 들어오는 '외부'메서드 호출 만 차단됩니다. 즉, '자체 호출', 즉 대상 개체의 다른 메서드를 호출하는 대상 개체 내의 메서드는 호출 된 메서드가 @Transactional! 로 표시되어 있어도 런타임에 실제 트랜잭션으로 이어지지 않습니다 .

(첫 번째 문장에 강조가 추가되었으며 원본에서 다른 강조가 추가되었습니다.)


Big +1 BTW, 나는 인용문을 인용 할 자유를 얻었으므로 사람들은 혼란스럽지 않고 원본에서 기울임 꼴 등을 다시 추가했습니다.
TJ Crowder 2010-06-26

나는 Spring docu에서 전에이 진술을 읽었지만 여전히 트랜잭션 설정이 클래스 기반 프록시 인프라에서 인식되지 않는 이유를 이해할 수 없습니까? 개인적으로 이것은 기본 프록시 인프라의 문제가 아닌 Spring 구현 제한이라고 생각합니다.
dma_k

하나가 알 수 스프링 소스를 살펴보면 JdkDynamicAopProxy각 메소드 호출의 모든 콩 고문 통과 (참조 DefaultAdvisorChainFactory#getInterceptorsAndDynamicInterceptionAdvice()선언적인 트랜잭션 설정의 경우 포함) BeanFactoryTransactionAttributeSourceAdvisor. 차례로 TransactionAttributeSourcePointcut#matches()호출되어 트랜잭션 관련 정보를 수집해야합니다. 대상 클래스에 전달되며 항상이 클래스가 구현하는 모든 인터페이스를 순회 할 수 있습니다. 이제 : 이것이 안정적으로 작동하지 않는 이유는 무엇입니까?
dma_k

2
@dma_k-Java 런타임은 수퍼 클래스에서 상속 된 클래스 주석 또는 상속이 전혀없는 메서드 주석에만 직접 액세스 할 수 있습니다. 따라서 Pointcut.matches ()는 실제로 모든 인터페이스를 재귀 적으로 트래버스하고 리플렉션이있는 "실제"재정의 된 메서드를 찾고 브리지 메서드 처리, 오버로딩, varargs, 제네릭, 프록시를 찾아야하며 물론 빠르게 수행해야합니다 . 그런 다음 다이아몬드 상속을 처리해야합니다. 상속 된 @Transactional주석 중 어떤 것이 적용될 수 있습니까? 그래서 모든 것이 가능 하지만 저는 Spring을 비난하지 않습니다. jira.springsource.org/browse/SPR-975
Peter Davis

9

인터페이스에 배치 할 수 있지만 경우에 따라 트랜잭션이 발생하지 않을 수 있다는 경고를받습니다. Spring 문서의 Secion 10.5.6 에서 두 번째 팁을 참조하십시오 .

Spring은 인터페이스에 주석을 달지 않고 @Transactional 주석으로 구체적인 클래스 (및 구체적인 클래스의 메서드)에만 주석을 달도록 권장합니다. 인터페이스 (또는 인터페이스 메소드)에 @Transactional 어노테이션을 배치 할 수 있지만 이는 인터페이스 기반 프록시를 사용하는 경우 예상 한 대로만 작동합니다. Java 주석이 인터페이스에서 상속되지 않는다는 사실은 클래스 기반 프록시 (proxy-target-class = "true") 또는 위빙 기반 측면 (mode = "aspectj")을 사용하는 경우 트랜잭션 설정이 다음과 같음을 의미합니다. 프록시 및 위빙 인프라에 의해 인식되지 않으며 객체가 트랜잭션 프록시에 래핑되지 않으므로 확실히 나쁠 것입니다.

이런 이유로 구현하는 것이 좋습니다.

또한 나에게 트랜잭션은 구현 세부 사항처럼 보이므로 구현 클래스에 있어야합니다. 트랜잭션 일 필요가없는 로깅 또는 테스트 구현 (모의)을위한 래퍼 구현이 있다고 상상해보십시오.


9

Spring의 권장 사항 은 인터페이스 대신 구체적인 구현에 주석을 추가하는 것입니다. 인터페이스에서 주석을 사용하는 것은 잘못된 것이 아닙니다. 해당 기능을 오용하여 실수로 @Transaction 선언을 우회 할 수 있습니다.

인터페이스에서 트랜잭션을 표시 한 다음 스프링의 다른 곳에서 구현 클래스 중 하나를 참조하면 스프링이 생성하는 객체가 @Transactional 어노테이션을 존중하지 않는다는 것이 분명하지 않습니다.

실제로는 다음과 같이 보입니다.

public class MyClass implements MyInterface { 

    private int x;

    public void doSomethingNonTx() {}

    @Transactional
    public void toSomethingTx() {}

}

1

구체적인 클래스에서 @Transactional 지원 :

일반적으로 API, 구현 및 웹 (필요한 경우)의 3 개 섹션으로 솔루션을 설계하는 것을 선호합니다. 종속성을 최소화하여 API를 가능한 한 가볍고 / 단순 / POJO로 유지하기 위해 최선을 다합니다. API를 많이 공유해야하는 분산 / 통합 환경에서 플레이하는 경우 특히 중요합니다.

@Transactional을 넣으려면 IMHO가 효과적이지 않은 API 섹션에 Spring 라이브러리가 필요합니다. 그래서 트랜잭션이 실행되는 구현에 추가하는 것을 선호합니다.


0

IFC의 모든 예측 가능한 구현자가 TX 데이터에 관심을 갖는 한 인터페이스에 배치하는 것은 괜찮습니다 (트랜잭션은 데이터베이스가 처리하는 문제가 아닙니다). 메소드가 TX에 대해 신경 쓰지 않는다면 (하지만 Hibernate 등을 위해 거기에 넣어야합니다), impl에 넣습니다.

또한 @Transactional인터페이스의 메소드에 배치하는 것이 조금 더 나을 수 있습니다 .

public interface FooService {
    @Transactional(readOnly = true)
    void doSmth();
}
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.