클라이언트와 서버 개념에서 클라이언트가 요청한 결과를 서버에 직접 요청하는 것이 아니라 어떤 대리자를 통해서 대신 간접적으로 서버에 요청할 수 있다. 이것을 프록시라고 한다.
객체에서 프록시가 되려면, 클라이언트는 서버에 요청을 한 것인지, 프록시에게 요청을 한 것인지 조차 몰라야 한다.
(즉 서버와 프록시는 같은 인터페이스를 사용해야함)
그리고 클라이언트가 사용하는 서버 객체를 프록시 객체로 변경해도 클라이언트 코드를 변경하지 않고 동작해야한다.
프록시의 주요기능
프록시를 통해서 할 수 있는 일은 크게 2가지로 구분할 수 있다.
- 접근제어
- 권한에 따른 접근차단
- 캐싱
- 지연로딩
- 부가 기능 추가
- 원래 서버가 제공하는 기능에 더해서 부가 기능을 수행한다.
프록시 객체가 중간에 있으면 크게 접근 제어와 부가 기능 추가를 수행할 수 있다.
프록시 패턴 vs 데코레이터 패턴?
- 프록시 패턴 : 접근 제어가 목적
- 데코레이터 패턴: 새로운 기능 추가가 목적
(둘다 프록시를 사용하지만, 의도가 다르다는점이 핵심)
프록시 패턴
실행코드
public class ProxyPatternTest {
@Test
void noProxyTest() {
RealSubject realSubject = new RealSubject();
ProxyPatternClient client = new ProxyPatternClient(realSubject);
client.execute();
client.execute();
client.execute();
//3초 소요
}
@Test
void cacheProxyTest() {
RealSubject realSubject = new RealSubject();
CacheProxy cacheProxy = new CacheProxy(realSubject);
ProxyPatternClient client = new ProxyPatternClient(cacheProxy);
client.execute();
client.execute();
client.execute();
//1.xx초 소요
}
}
public class ProxyPatternClient {
private Subject subject;
public ProxyPatternClient(Subject subject) {
this.subject = subject;
}
public void execute() {
subject.operation();
}
}
Proxy와, 실제 객체가 참조할 인터페이스
public interface Subject {
String operation();
}
Subject를 구현한 각각의 Proxy, 실제 객체 클래스
(중요한점은 CacheProxy는 실제 객체 클래스쪽으로 다시 호출해준다는점이 핵심)
@Slf4j
public class CacheProxy implements Subject{
private Subject target;
private String cacheValue;
public CacheProxy(Subject target) {
this.target = target;
}
@Override
public String operation() {
log.info("프록시 호출");
if(cacheValue == null){
cacheValue = target.operation();
}
return cacheValue;
}
}
@Slf4j
public class RealSubject implements Subject{
@Override
public String operation() {
log.info("실제 객체 호출");
sleep(1000);
return "data";
}
private void sleep(int millis) {
try {
Thread.sleep(millis);
}catch (InterruptedException e){
e.printStackTrace();
}
}
}
데코레이터 패턴
실행코드
@Slf4j
public class DecoratorPatternTest {
@Test
void decorator(){
RealComponent realComponent = new RealComponent();
MessageDecorator messageDecorator = new MessageDecorator(realComponent);
DecoratorPatternClient client = new DecoratorPatternClient(messageDecorator);
client.execute();
}
}
@Slf4j
public class DecoratorPatternClient {
private Component component;
public DecoratorPatternClient(Component component) {
this.component = component;
}
public void execute(){
String result = component.operation();
log.info("result ={}", result);
}
}
Decorator 패턴을 적용할 인터페이스
public interface Component {
String operation();
}
Decorator패턴이 적용된 MessageDecorator와 아무것도 적용되지 않은, RealComponent
@Slf4j
public class MessageDecorator implements Component {
private Component component;
public MessageDecorator(Component component) {
this.component = component;
}
@Override
public String operation() {
log.info("Message Decorator실행");
String result = component.operation();
String decoResult = result + "***********";
log.info("MessageDecorator 꾸미기 적용 전 ={} 후 ={}", result, decoResult);
return decoResult;
}
}
// 비교대상
@Slf4j
public class RealComponent implements Component {
@Override
public String operation() {
log.info("RealComponent 실행");
return "data";
}
}
Decorator패턴의 체인
MessageDecorator이후 동작시킬 TimeDecorator생성
@Slf4j
public class TimeDecorator implements Component{
private Component component;
public TimeDecorator(Component component) {
this.component = component;
}
@Override
public String operation() {
log.info("TimeDecorator 실행");
long startTime = System.currentTimeMillis();
String result = component.operation();
long endTime = System.currentTimeMillis();
long resultTime = endTime - startTime;
log.info("resultTime ={}", resultTime);
return result;
}
}
realComponent -> MessageDecorator -> TimeDecorator -> DecoratorPatternClient순으로 연결
@Slf4j
public class DecoratorPatternTest {
@Test
void decorator2(){
RealComponent realComponent = new RealComponent();
MessageDecorator messageDecorator = new MessageDecorator(realComponent);
TimeDecorator timeDecorator = new TimeDecorator(messageDecorator);
DecoratorPatternClient client = new DecoratorPatternClient(timeDecorator);
client.execute();
}
}
'SPRING > 스프링' 카테고리의 다른 글
[스프링 핵심 원리 - 고급편]인터페이스 ,클래스 기반의 프록시 (0) | 2022.02.17 |
---|---|
[스프링 핵심 원리-고급] 템플릿 콜백 패턴 (0) | 2022.01.27 |
[스프링 핵심 원리 -고급] 전략패턴 (0) | 2022.01.27 |
[스프링 핵심 원리-고급] 템플릿 메서드 패턴 (0) | 2022.01.25 |
[스프링 핵심 원리-고급] 쓰레드 로컬 주의사항 (0) | 2022.01.22 |