-
프록시 패턴프로그래밍언어/Java(중급) 2020. 4. 24. 21:25
안녕하세요 현우입니다. 이번 포스팅은 [ 프록시 패턴에대한 이해 ] 입니다. 자바 학습에 도움을 주신 백기선 개발자님께 항상 감사드립니다 :)
https://www.inflearn.com/course/the-java-code-manipulation/lecture/23430
1. 프록시 패턴
- 프록시란 ? 사전적의미 : 대리, 대리인이라는 듯
- 프로그래밍에서 프록시는?
위 그림과 같이 프록시와 리얼프로젝트는 동일한 하나의 인터페스를 공유하고 있다. 그리고 프록시는 리얼프로젝트를 참조하고 있다. 즉 프록시와 리얼 서브젝트는 똑같은 인터페이스로 구현되어있는 것이다. 그런데 클라이언트는 리얼 서브 프로젝트를 바로 참조하지않고 중간계층의 프록시를 참조하고 있다. 이러한 기능으로 프록시는 리얼 서브젝트에 대한 접근을 관리하거나 부가기능을 제공하거나, 리턴값을 변경할 수 있다.
리얼 서브젝트는 자신이 해야 할 일만 하면서(Self responsibility principle) 프록시를 사용해서 부가적인 기능(접근제한, 로깅, 트랜잭셩 등)을 제공할 때 이러한 패턴을 주로 사용한다.
예제코드
- 인터페이스
public interface BookService{ void rent(Book book); }
- 구현체
public class DefaultBookService implements BookService{ BookRepository bookRepository = new BookRepository(); public void rent(Book book){ System.out.pringln("rent: " + book.getTitle()); } }
- 테스트코드
public class BookServiceTest{ BookService bookService = new BookService(); @Test public void di() { Book book = new Book(); book.setTitle("spring"); bookService.rent(book); } } ------------------- rent : spring
위의 코드를 바탕으로 프록시 구조를 사용해서 서비스 구현체에 코드를 추가하지 않고 로그를 찍어보자. 어떻게 하면될까? 위의 그림과같은 프록시 패턴으로 클래스를 구성하면 된다. bookService는 클라이언트가 사용할 서브젝트가 되고 이것을 실제 구현할 DefaultBookService가 리얼 서브젝트가 된다.
- 서브젝트 : bookservice 인터페이스
- 리얼서브젝트 : DefaultBookService 구현체
- 만들어줄 프록시 : BookServiceProxy
1. proxy는 리얼 서브젝트를 가지고 있어야한다.
public class BookServiceProxy{ BookService bookService; //생성자로 리얼서브젝트를 받아온다 public BookServiceProxy(BookService bookService){ this.bookService = bookService; } @Override public void rent(Book book){ System.out.println("aaaaa"); // 추가하고 싶은 메세지 bookService.rent(book); // 리얼서브프로젝트 본래일 System.out.println("bbbb"); // 추가하고 싶은 메세지 } }
2. 클라이언트가 프록시를 사용하게 끔 한다.
public class BookServiceTest{ //프록시는 내부에서 리얼서브젝트를 참조하고 있다. BookService bookService = newBookServiceProxy(new DefaultBookService()); @Test public void di() { Book book = new Book(); book.setTitle("spring"); bookService.rent(book); } } ------------------- aaaaa rent : spring bbbbb
프록시패턴은 굉장히 번거롭다. 부가적인 기능을 추가할때마다 별도의 프록시를 만들어야하고 프록시를 프록시로 감싸야되는 일이 생길수있고 모든 구현체에서 타겟으로 위임하는 코드가 중복해서 발생할 수 있다. 그래서 동적으로 런타임으로 생성하는 다이나믹 프록시를 사용한다.
2. 다이나믹 프록시
다이나믹 프록시는 런타임에 즉 애플리케이션이 실행하는 도중에 특정 인터페이스를 구현하는 클래스 또는 인스턴스를 만드는 기술이다.
위에서 작성한 코드는 소스코드로 직접 컴파일 타임에 만든 것이다. 위의 코드를 실행되는 시점 즉 런타임 코드로 만들어 보자.
public class BookServiceTest{ //1.클래스로더를 읽힌다. //2.BookService타입으로 만든다고 호출해야한다. //3. 프록시의 메서드 호출이될때 어떻게 처리해야 되는지에 대한 설명 BookService bookService = (BookService) Proxy.newProxyInstance(BookService.class.getClassLoader(),new Class[]{BookService.class}, new InvocationHandler(){ BookService bookService = new DefaultBookService(); @Override //proxy : 여기서 생성된 proxy, method : 호출된 프록시가 참조하는 함수 public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { System.out.println("aaa"); // 추가해준다 return method.invoke(bookService, args); // 아무것도 하지않는다 System.out.println("bbb"); // 추가해준다 } return method.invoke(bookService, args); } }); @Test public void di() { Book book = new Book(); book.setTitle("spring"); bookService.rent(book); } }
1. 클래스로더를 읽혀서 BookService의 프록시를 사용한다
2. 어떠한 클래스로 반환시켜줄건지 명시해줘야한다.
3. new InvocationHandler() {
public object invoke( ... )
proxy를 생성해서 method를 다룬다 invoke할경우 아무런일도 하지않고 리얼서브젝트에서 수행된 코드만 출력한다. 코드를 추가해야할 경우 프린트문을 찍어주면 되는데 이때 rent함수 외에 다른 함수도 위의 프록시에서 설정한 값과 그대로 출력된다. 이때는 직접 코드를 작성하여 함수값을 변경해야한다.
if(method.getName().equals("rent")) : rent함수에만 아래의 프록시설정을 적용시키라는 뜻.
여러가지 부가기능을 넣으면 코드가 커지고 이러한 프록시를 감싸는 또다른 프록시를 만들어야 될 수 있다. 그래서 스프링 AOP에서 해당구조를 스프링이 정의한 인터페이스로 뜯어 고쳤다. 그래서 스프링 AOP를 프록시기반 이라고 불린다.
reference
'프로그래밍언어 > Java(중급)' 카테고리의 다른 글
6. Spring에서 바이트 코드 조작 활용 예 (0) 2020.03.26 5. 바이트코드 조작마술 (0) 2020.03.26 4. 바이트코드 조작 (0) 2020.03.26 3. 클래스 로더 (0) 2020.03.26 2. JVM 구조 (0) 2020.03.26