프록시 패턴(Proxy Pattern)
프록시 패턴은 특정 객체로의 접근을 제어하는 대리인(특종 객체를 대변하는 객체)을 제공합니다.
•
프록시 패턴을 사용하면 원격 객체라던가 생성하기 힘든 객체, 보안이 중요한 객체와 같은 다른 객체로의 접근을 제어하는 대리인 객체를 만들 수 있다.
•
프록시에서 접근을 제어하는 방법
◦
원격 프록시를 써서 원격 객체로의 접근을 제어할 수 있음
◦
가상 프록시를 써서 생성하기 힘든 자원으로의 접근을 제어할 수 있음
◦
보호 프록시를 써서 접근 권한이 필요한 자원의 접근을 제어할 수 있음
•
호출을 중간에서 가로챈다는게 중요하다.
원격 프록시
•
원격 프록시는 원격 객체의 로컬 대변자 역할을 하게 됩니다.
•
원격 객체란 다른 자바 가상머신의 힙에서 살고있는 객체를 뜻합니다.
•
로컬 대변자를 호출하면 다른 원격 객체에게 그 메소드를 호출을 전달해주는 객체를 뜻합니다.
•
결과적으로 클라이언트는 로컬 객체를 사용하는것처럼 행동할 수 있습니다.
원격 메소드의 호출 과정
1.
클라이언트 객체에서 특정 메소드를 사용
2.
메소드 명과 인자 값 등을 직렬화해서 네트워크로 원격 객체에 전달
3.
원격 객체는 로직을 수행
4.
결과를 다시 직렬화하여 프록시 객체에게 전달
5.
프록시 객체는 메소드가 완료된 것처럼 작동
가상 프록시
•
객체 생성하는데 많은 비뵹이 드는 객체를 대신합니다.
가상 프록시의 작동 과정
이미지를 불러오는 객체를 기준으로 했을 때
1.
이미지를 불러오는 객체를 가상 프록시로 만들어 이미지를 가져오는 메소드를 실행
a.
가상 프록시는 화면에 이미지를 가져오는 중이라는 메세지를 나타나게 함
2.
잠시 후 이미지 로딩이 완료되고, 실제 객체가 완성됨
3.
이미지 생성이 완료된 객체를 통해서 화면에 이미지를 노출 시킴
원격 프록시와 가상 프록시 비교
•
원격 프록시
◦
원격 프록시는 다른 JVM에 들어있는 객체의 대리인에 해당하는 로컬 객체
◦
프록시의 메소드를 호출하면 그 호출이 네트워크로 전달되어 결국 원격 객체의 메소드가 호출됨
◦
그 결과는 다시 프록시를 거쳐서 클라이언트에게 전달됨
•
가상 프록시
◦
가상 프록시는 생성하는 데 많은 비용이 드는 객체를 대신함
◦
진짜 객체가 필요한 상황이 오기 전까지 객체의 생성을 미루는 기능을 제공
◦
객체 생성 전이나 객체 생성 도중에 객체를 대신하기도하고, 생성이 끝나면 직접 요청을 전달 하기도함
데코레이터와 프록시의 차이점
•
사용 용도로 구분이 가능하다.
•
데코레이터는 클래스에 새로운 동작을 추가하는 용도로 사용된다.
•
프록시는 어떤 클래스로의 접근을 제어하는 용도로 사용된다.
어댑터와 프록시의 차이점
•
어댑터는 다른 객체의 인터페이스를 바꿔줍니다.
•
프록시는 동일한 인터페이스를 사용합니다.
보호 프록시
•
자바의 java.lang.reflect 패키지 안에 프록시 기능이 내장되어 있다.
•
지정한 클래스에 메소드 호출을 전달하는 프록시 클래스를 만들 수 있다.
•
진짜 프록시 클래스는 실행 중 생성되므로 이러한 자바 기술을 동적 프록시(dynamic proxy)라고 부른다.
•
특정 인터페이스 메소드 접근의 권한에 따라서 이용 가능 여부를 지정할 수 있다.
만들어보기
public interface Person {
String getName();
String getGender();
String getInterests();
int getGeekRating();
void setName(String name);
void setGender(String gender);
void setInterests(String interests);
void setGeekRating(int rating);
}
Java
복사
import java.lang.reflect.*;
public class OwnerInvocationHandler implements InvocationHandler {
Person person;
public OwnerInvocationHandler(Person person) {
this.person = person;
}
public Object invoke(Object proxy, Method method, Object[] args)
throws IllegalAccessException {
try {
if (method.getName().startsWith("get")) {
return method.invoke(person, args);
} else if (method.getName().equals("setGeekRating")) {
throw new IllegalAccessException();
} else if (method.getName().startsWith("set")) {
return method.invoke(person, args);
}
} catch (InvocationTargetException e) {
e.printStackTrace();
}
return null;
}
}
Java
복사
Person getOwnerProxy(Person person) {
return (Person) Proxy.newProxyInstance(
person.getClass().getClassLoader(),
person.getClass().getInterfaces(),
new OwnerInvocationHandler(person));
}
Java
복사