금일 듣는 강의 내용 중 아주 중요한 내용이 있었다. 프록시가 그 주인공이다.
가짜 객체를 하나 생성해서 실제 객체와의 작업을 진행한다.
내가 그동안 알고 있었던 프록시의 개념이다.
하지만 어디까지나 어렴풋이 알고 있었던 지식이고, 확실하게 알고 있는 개념인지도 헷갈리는 것이 사실이다.
프록시 또한 중요한 개념이기 때문에, 이번 기회에 확실히 알아보고자 한다.
AOP 와 관련된 기능을 제공하거나, Bean 을 동적으로 생성하기 위해서 사용Proxy 를 통해 간접적으로 상호작용Proxy 객체를 사용@Transactional 어노테이션은 Proxy 를 통해 트랜잭션 경계를 설정하고 관리@Transactional 어노테이션이 붙은 메서드나 클래스는 Proxy 객체를 통해 트랜잭션 처리가 이루어짐Proxy 가 함1) 스프링은 @Transcation 이 붙은 클래스의 원본이 아닌, Proxy 객체를 생성
2) 클라이언트로부터 호출이 발생하면, Proxy 가 호출을 가로챔
3) 트랜잭션의 시작과 끝을 설정하고, 메서드를 실행
4) 메서드 실행이 완료된 후 커밋 또는 롤백을 진행
1) @Transactional 의 동작 제한
@Transactional 메서드를 호출할 경우 this 를 통해 객체 자신의 메서드 호출Proxy 객체를 통하는 것이 아니므로 트랜잭션이 적용되지 않음2) AOP 및 프록시의 영향
Proxy 객체를 보게 될 수 있음🤔 그렇다면 같은 클래스 내부의
@Transactional메서드를Proxy객체를 통해 호출하려면 어떻게 해야 할까?
‼️ 스프링은 지연 로딩 프록시를 사용하여 순환 참조를 우회할 수 있음. 초기에는 실제 객체 대신
Proxy객체를 주입하고, 이후 필요하다면 실제 객체로 초기화하여 사용
Bean 의 초기화를 지연시키는 역할을 하는 어노테이션Bean 을 초기화@Lazy 를 사용한 Bean의 경우, 실제로 필요할 때까지 초기화를 진행하지 않고 연기@Lazy 와 @Autowired 로 자기 자신을 주입할 경우, 이후 초기화 시점에 필요에 따라 Proxy 객체를 스프링 컨테이너에 등록1) 순환 참조 해결
Bean 의 경우, @Lazy 를 미리 붙여두어 초기화를 지연2) 리소스 절약
Bean 은 필요할 때만 생성하여 애플리케이션의 성능 개선✔️ 이제 이전의 질문에 대한 답을 할 수 있다.
@Transactional 메서드를 내부 호출하기 위해서는 자기 참조(self-reference) 필드를 선언해야 함@Autowired 로 자동 생성자 주입@Lazy 로 Bean 의 초기화를 미룸@Configuration
@RequiredArgsConstructor
public class Board {
// 생략
@Autowired // 필수
@Lazy // 필수
private Board self; // 변수명 설정은 자유, @RequiredArgsConstructor로 인한 초기화를 막기 위해 final은 붙이지 않음
@Bean
public ApplicationRunner init() {
return args -> self.work();
}
@Transactional
public void work() {
// 생략
}
}
@Transactional 메서드인 work() 메서드를 내부 호출@Lazy 로 설정된 self는 이 때 초기화@Transactional 메서드인 work() 메서드를 실행하므로, self는 Proxy 객체로 스프링 컨테이너에 주입됨Proxy 객체가 work() 메서드를 수행1) Bean
Bean 으로 등록된 객체는 기본적으로 원본 객체가 생성되어 스프링 컨테이너에 등록AOP 가 적용되거나, Proxy 기반의 기능이 활성화된 경우, 원본 객체가 아닌 Proxy 객체가 대신 생성되어 컨테이너에 등록2) Proxy
Proxy 객체를 생성하여 트랜잭션 관리를 수행Proxy 객체가 스프링 컨테이너에 등록되며, 메서드 호출 시 Proxy 객체를 사용참고) OpenAI. (2024).ChatGPT(4o)[Large language model].https://chatgpt.com/