대리인이나 중개자를 뜻하며, 소프트웨어에서는 다른 객체에 대한 접근을 제어하거나 대체 역할을 하는 객체를 말한다
쉽게말해, 원래 객체에 접근하기 전에 중간에 끼어드는 객체
프록시의 필요성
1. 추가 작업 수행
- 원래 객체를 호출하기 전후에 부가 작업을 수행해야 할때
- 예: 로깅, 인증, 캐싱, 지연 로딩 등
2. 원래 객체 보호
- 원래 객체에 대한 접근을 제한하거나 제어
- 예: 보안 관련 작업
3. 성능 최적화
- 실제 객체의 생성이나 초기화를 나중으로 미루는 경우 (지연 로딩)
4. 구조적 유연성
- 객체 교체나 확장성을 높이기 위해 중간 계층을 추가
프록시의 종류
1. 정적 프록시 (Static Proxy)
- 컴파일 시점에 프록시 객체를 작성
- 원래 객체와 동일한 인터페이스를 구현
- 코드가 고정적이고, 클래스별로 프록시를 별도로 작성해야함
interface Service {
void performTask();
}
class RealService implements Service {
@Override
public void performTask() {
System.out.println("Real Service is performing the task.");
}
}
class StaticProxy implements Service {
private RealService realService;
public StaticProxy(RealService realService) {
this.realService = realService;
}
@Override
public void performTask() {
System.out.println("Proxy: Pre-processing before the task.");
realService.performTask();
System.out.println("Proxy: Post-processing after the task.");
}
}
public class Main {
public static void main(String[] args) {
RealService realService = new RealService();
StaticProxy proxy = new StaticProxy(realService);
proxy.performTask();
}
}
출력:
Proxy: Pre-processing before the task.
Real Service is performing the task.
Proxy: Post-processing after the task.
2. 동적 프록시 (Dynamic Proxy)
- 런타임에 프록시 객체를 생성
- 자바의 java.lang.reflect.Proxy 클래스와 InvocatonHandler 인터페이스를 사용
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
interface Service {
void performTask();
}
class RealService implements Service {
@Override
public void performTask() {
System.out.println("Real Service is performing the task.");
}
}
class DynamicProxyHandler implements InvocationHandler {
private Object target;
public DynamicProxyHandler(Object target) {
this.target = target;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("Proxy: Pre-processing before the task.");
Object result = method.invoke(target, args);
System.out.println("Proxy: Post-processing after the task.");
return result;
}
}
public class Main {
public static void main(String[] args) {
RealService realService = new RealService();
Service proxy = (Service) Proxy.newProxyInstance(
realService.getClass().getClassLoader(),
realService.getClass().getInterfaces(),
new DynamicProxyHandler(realService)
);
proxy.performTask();
}
}
출력:
Proxy: Pre-processing before the task.
Real Service is performing the task.
Proxy: Post-processing after the task.
3. 프레임워크 기반 프록시
- 스프링 프록시 : 스프링은 AOP (Aspect-Oriented Programming) 에서 프록시를 적극적으로 사용
- JDK 동적 프록시 : 인터페이스를 구현한 객체를 프록시로 생성
- CGLIB : 클래스의 서브클래스를 동적으로 생성하여 프록시를 생성
예제: 스프링 AOP에서의 프록시
- @Transactional 또는 @Aspect같은 기능은 프록시로 구현된다
@Service
public class MyService {
@Transactional
public void performTask() {
System.out.println("Performing task in a transactional context.");
}
}
- 여기서 @Transactional은 프록시를 통해 트랜잭션 처리가 추가된다
프록시의 주요 속성과 메서드
1. 속성
- target : 프록시가 대리하는 원래 객체
- method : 프록시 객체에서 호출된 메서드
- arguments : 메서드 호출시 전달된 매개변수
2. 주요 동작
- 메서드 호출 전/후 로직 삽입
- 원래 객체의 메서드 실행을 위임
- 동적으로 원래 객체를 교체 가능
프록시의 장 단점
장점
1. 원래 객체의 변경 없이 부가 기능 추가 가능
2. 코드의 재사용성과 유지보수성이 높아짐
3. 지연로딩, 로깅, 보안, 캐싱 등 다양한 용도로 활용 가능
단점
1. 정적 프록시는 클래스별로 프록시를 작성해야 하므로 코드가 중복될 수 있음
2. 동적 프록시는 성능 오버헤드가 발생할 수 있음
3. 복잡도가 증가하여 디버깅이 어려워질 수 있음
정리
1. 프록시란?
- 원래 객체를 대신하여 중간에서 작업을 수행하거나, 원래 객체에 대한 접근을 제어하는 객체
2. 종류
- 정적 프록시 : 미리 작성된 프록시 클래스
- 동적 프록시 : 런타임에 생성
3. 사용 사례
- 로깅, 인증, 트랜잭션 관리, 지연 로딩 등
4. 스프링에서의 활용
- AOP, @Transactional 등에서 프록시를 사용
출처 : ChatGPT
'BE > Java' 카테고리의 다른 글
[Java] @IdClass (0) | 2024.12.31 |
---|---|
[Java] @Builder, @ToString, @EqualsAndHashCode, @Singular (0) | 2024.12.23 |
[Java] 쓰레드 (0) | 2024.12.22 |
[Java] StringBuffer, StringBuilder (1) | 2024.12.19 |
[Java] 영속성 (0) | 2024.12.16 |