시작하기 전 ...
프록시 패턴은 목적에 따라 여러가지 패턴의 양상을 띈다. 이번 포스팅에서는 보호 프록시에 대해 알아보도록 하고,
전반적인 프록시 패턴의 개념은 아래 포스팅을 참고하면 된다.
https://dev-rosiepoise.tistory.com/80
Goal
- 보호 프록시에 대해 알아보고 이해한다
- 보호 프록시 샘플 코드를 통해 보호 프록시를 이해한다
프록시 패턴 (Proxy Pattern)
특정 객체로의 접근을 제어하는 대리인(특정 객체를 대변하는 객체)를 제공한다.
보호 프록시 (Protection Proxy)
접근 권한을 바탕으로 객체로의 접근을 제어하는 프록시
다이어그램
- 자바의 java.lang.reflect 패키지 안에 프록시 기능이 내장
- 하나 이상의 인터페이스를 구현하고 지정한 클래스에 메소드를 호출을 전달하는 프록시 클래스를 만들 수 있다.
- 자바에서 Proxy 클래스를 생성
- 프록시 자체는 실행 중에 생성된다
- 이런 자바 기술을 동적 프록시(Dynamic Proxy)라고도 한다.
- InvocatinHandler를 제공해야함
- Proxy 객체의 모든 메소드 호출을 전달받음
- Real Subject객체에 있는 메소드로의 접근을 제어
보호 프록시를 사용하는 이유
접근 권한이 필요한 자원에 대한 접근을 제어할 수 있어서 클라이언트가 주체 클래스를 호출하려 할 때, 그 사이에서 프록시가 허용 여부를 결정할 수 있기 때문이다
동적 프록시를 만드는 3단계
1. 2개의 InvocationHandler 만들기
InvocationHandler는 프록시의 행동을 구현한다. 프록시 클래스와 객체를 만드는 일은 자바에서 알아서 해주기에 프록시의 메소드가 호출되었을 때 할일을 지정해주는 핸들러를 만들면 된다.
2. 동적 프록시 생성 코드 만들기
프록시 클래스를 생성하고 그 인스턴스를 만드는 코드가 필요하다
3. 적절한 프록시로 해당 객체 감싸기
해당 객체를 사용하는 객체는 1.고객 자신의 객체 (본인) - 자기 빈에 직접 접근하는 경우 2.다른 고객의 객체 (타인) - 다른 사람의 빈에 접근하는 경우 둘 중하나다. 어떤 경우든 해당 객체에 따라 적절한 프록시를 생성해야 한다.
객체 마을 데이팅 서비스 Person 인터페이스용 동적 프록시 만들기
1. InvocationHandler 만들기
public class OwnerInvocationHandler implements InvocationHandler {
PersonBean person;
public OwnerInvocationHandler(PersonBean 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("setHotOrNotRating")) {
throw new IllegalAccessException();
} else if (method.getName().startsWith("set")) {
return method.invoke(person, args);
}
} catch (InvocationTargetException e) {
e.printStackTrace();
}
return null;
}
}
- public class OwnerInvocationHandler implements InvocationHandler {
- 호출 핸들러에서 반드시 InvocationHandler를 구현해야함
- public OwnerInvocationHandler(PersonBean person) {
- 생성자로부터 전달받은 주제의 레퍼런스를 저장
- public Object invoke(Object proxy, Method method, Object[] args)
- 프록시의 메소드가 호출될 때마다 호출되는 invoke메소드
- if (method.getName().startsWith("get")) {
- 게터 메소드라면 주제의 메소드를 호출한다
- } else if (method.getName().equals("setHotOrNotRating")) {
throw new IllegalAccessException();
- 반면 게터 메소드가 아닌 setHotOrNotRating라면 IllegalAccessException를 던져 호출을 막는다
- } else if (method.getName().startsWith("set")) {
return method.invoke(person, args);- 본인한테는 나머지 메소드는 모두 허용해줘야하므로 주제에 있는 메소드를 호출
- } catch (InvocationTargetException e) {
- 진짜 주제에서 예외를 던졌을 때
- return null;
- 다른 메소드가 호출된다면 그냥 null 리턴
2. 동적 프록시 생성코드 만들기
PersonBean getOwnerProxy(PersonBean person) {
return (PersonBean) Proxy.newProxyInstance(
person.getClass().getClassLoader(),
person.getClass().getInterfaces(),
new OwnerInvocationHandler(person));
}
- PersonBean getOwnerProxy(PersonBean person) {
- 이 메소드는 Person객체(진짜 주제)를 인자로 받아오고 프록시를 리턴한다.
- 프록시의 인터페이는 주제의 인터페이스와 같으므로 리턴형식은 Person이다
- return (PersonBean) Proxy.newProxyInstance(
- proxy 클래스에 있는 newProxyInstance 정적 메소드를 써서 프록시를 생성
- person.getClass().getClassLoader(),
- person의 클래스로더를 인자로 전달
- person.getClass().getInterfaces(),
- 프록시에서 구현해야 하는 인터페이스도 인자로 전달해야함
- new OwnerInvocationHandler(person));
- 호출 핸들러인 OwnerInvocationHandler도 인자로 전달해야함
- 호출 핸들러의 인자로 person을 전달.
3. 데이팅 서비스 코드 테스트
public class MatchMakingTestDrive {
public static void main(String[] args) {
MatchMakingTestDrive test = new MatchMakingTestDrive();
test.drive();
}
// 생성자는 데이팅 서비스 회원 db를 초기화
public MatchMakingTestDrive() {
initializeDatabase();
}
public void drive() {
// 인물 정보를 db로 부터 가져옴
PersonBean kjv = getPersonFromDatabase("김자바");
// 본인 프록시 생성
PersonBean ownerProxy = getOwnerProxy(kjv);
// 게터를 통한 이름 출력
System.out.println("이름 " + ownerProxy.getName());
// 세터를 통한 관심사 등록
ownerProxy.setInterests("볼링, 바둑");
System.out.println("본인 프록시에 관심 사항을 등록합니다");
// 본인 괴짜지수 설정은 설정 할 수 없음
try {
ownerProxy.setHotOrNotRating(10);
} catch (Exception e) {
System.out.println("본인 프록시에는 괴짜 지수를 매길 수 없습니다");
}
System.out.println("괴짜 지수 : " + ownerProxy.getHotOrNotRating());
// 타인용 프록시 생성
PersonBean nonOwnerProxy = getNonOwnerProxy(kjv);
// 게터를 통한 이름 출력
System.out.println("Name is " + nonOwnerProxy.getName());
// 타인 관심사는 내가 설정할 수 없음
try {
nonOwnerProxy.setInterests("볼링, 바둑");
} catch (Exception e) {
System.out.println("타인 프록시에는 관심 사항을 등록할 수 없습니다.");
}
// 타인 괴짜 지수는 설정 가능
nonOwnerProxy.setHotOrNotRating(3);
System.out.println("타인 프록시에 괴짜 지수를 매깁니다");
System.out.println("괴짜 지수 : " + nonOwnerProxy.getHotOrNotRating());
}
}
참고
헤드퍼스트 디자인패턴
'JAVA > 디자인 패턴' 카테고리의 다른 글
[Strategy Pattern] 전략 패턴 (1) | 2023.07.06 |
---|---|
[Decorator Pattern] 데코레이터 패턴 (0) | 2023.07.04 |
[Proxy Pattern] 프록시 패턴 - 가상 프록시 (0) | 2023.07.03 |
[Proxy Pattern] 프록시 패턴 - 원격 프록시 (0) | 2023.07.03 |
[Proxy Pattern] 프록시 패턴이란? (0) | 2023.07.03 |