@Slf4j
@Component
public class CallServiceV0 {
public void external() {
log.info("call external");
internal(); //내부 메서드 호출(this.internal())
}
public void internal() {
log.info("call internal");
}
}
@Slf4j
@Import(CallLogAspect.class)
@SpringBootTest
class CallServiceV0Test {
@Autowired
CallServiceV0 callServiceV0;
@Test
void external() {
callServiceV0.external();
}
@Test
void internal() {
callServiceV0.internal();
}
}
- 내부 메서드 호출에서는 프록시를 통한 호출이 아닌 내부 인스턴스 호출이기 때문에 aop가 적용되지 않는다.
/**
* 생성자 주입은 순환 사이클을 만들기 때문에 실패한다.
*/
@Slf4j
@Component
public class CallServiceV1 {
private CallServiceV1 callServiceV1;
@Autowired
public void setCallServiceV1(CallServiceV1 callServiceV1) {
this.callServiceV1 = callServiceV1;
}
public void external() {
log.info("call external");
callServiceV1.internal(); //외부 메서드 호출
}
public void internal() {
log.info("call internal");
}
}
@Slf4j
@Import(CallLogAspect.class)
@SpringBootTest()
class CallServiceV1Test {
@Autowired
CallServiceV1 callServiceV1;
@Test
void external() {
callServiceV1.external();
}
@Test
void internal() {
callServiceV1.internal();
}
}
spring.main.allow-circular-references=true
- 수정자 주입을 통해 주입을 받고 프록시 호출로 해결한다. 스프링 부트 버전2.6 이상부터는 spring.main.allow-circular-references=true 설정을 해주어야 한다(순환 사이클 방지). 생성자 주입도 불가하다.
/**
* ObjectProvider(Provider), ApplicationContext를 사용해서 지연(LAZY) 조회
*/
@Slf4j
@Component
public class CallServiceV2 {
// private final ApplicationContext applicationContext;
private final ObjectProvider<CallServiceV2> callServiceProvider;
public CallServiceV2(ObjectProvider<CallServiceV2> callServiceProvider) {
this.callServiceProvider = callServiceProvider;
}
public void external() {
log.info("call external");
// CallServiceV2 callServiceV2 = applicationContext.getBean(CallServiceV2.class);
CallServiceV2 callServiceV2 = callServiceProvider.getObject();
callServiceV2.internal(); //외부 메서드 호출
}
public void internal() {
log.info("call internal");
}
}
@Slf4j
@Import(CallLogAspect.class)
@SpringBootTest()
class CallServiceV2Test {
@Autowired
CallServiceV2 callServiceV2;
@Test
void external() {
callServiceV2.external();
}
@Test
void internal() {
callServiceV2.internal();
}
}
- 지연 조회를 통해 해결한다.
/**
* 구조를 변경(분리)
*/
@Slf4j
@Component
@RequiredArgsConstructor
public class CallServiceV3 {
private final InternalService internalService;
public void external() {
log.info("call external");
internalService.internal(); //외부 메서드 호출
}
}
@Slf4j
@Component
public class InternalService {
public void internal() {
log.info("call internal");
}
}
@Slf4j
@Import(CallLogAspect.class)
@SpringBootTest()
class CallServiceV3Test {
@Autowired
CallServiceV3 callServiceV3;
@Test
void external() {
callServiceV3.external();
}
}
- 구조를 변경시켜 InternalService에서 호출하기 때문에 모두 aop 적용이 된다(가장 권장하는 방법이다.)
'개발이 좋아서 > Spring이 좋아서' 카테고리의 다른 글
Springboot - 자동 구성(Auto Configuration), @Conditional (0) | 2025.01.03 |
---|---|
Springboot - 핵심 기능 5가지 (0) | 2025.01.02 |
AOP - 실전 예제 (0) | 2025.01.02 |
AOP - 포인트컷 (0) | 2025.01.02 |
AOP - 구현 (0) | 2024.12.31 |