Dev 달팽이 @_''

Java - 클래스의 프록시 본문

Java&Spring/더 자바, 코드를 조작하는 다양한 방법

Java - 클래스의 프록시

다본죽 2022. 1. 9. 23:54

Java - 클래스의 프록시

  • 서브 클래스를 만들 수 있는 라이브러리를 사용하여 프록시를 생성 가능
  • CGlib을 이용하는 방법과 ByteBuddy를 이용하는 방법이 있음

 

CGlib

  • https://github.com/cglib/cglib/wiki
  • 스프링, 하이버네이트가 사용하는 라이브러리
  • 버전 호환성이 좋지 않아서 서로 다른 라이브러리 내부에 내장된 형태로 제공되기도 함
  • 아래의 의존 추가
<dependency>
	<groupId>cglib</groupId>
	<artifactId>cglib</artifactId>
	<version>3.3.0</version>
</dependency>

 

BookServiceTest.java

    @Test
    public void diWithCglib(){

        MethodInterceptor handler = new MethodInterceptor() {
            BookService bookService = new BookService();
            @Override
            public Object intercept(Object o, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
                if(method.getName().equals("rent"))
                    System.out.println("This is Proxy");
                Object invoke = method.invoke(bookService, args);
                return invoke;
            }
        };

        BookService bookService = (BookService) Enhancer.create(BookService.class, handler);

        Book book = new Book();
        book.setTitle("Spring");
        bookService.rent(book);
        bookService.returnBook(book);
    }
  • Interceptor handler를 이용하여 BookService 객체가 들어오면 어떤 작업을 할 지 정해줌

 

ByteBuddy

  • 바이트코드 조작 뿐 아니라 런타임(다이나믹) 프록시를 만들 때도 사용 가능

BookServiceTest.java

@Test
    public void diWithByteBuddy() throws NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException {

        Class<? extends BookService> proxyClass = new ByteBuddy().subclass(BookService.class)
                .method(named("rent")).intercept(InvocationHandlerAdapter.of(new InvocationHandler() {
                    BookService bookService = new BookService();
                    @Override
                    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                        System.out.println("This is proxy");
                        Object invoke = method.invoke(bookService, args);
                        return invoke;
                    }
                }))
                .make().load(BookService.class.getClassLoader()).getLoaded();

        BookService bookService = proxyClass.getConstructor(null).newInstance();

        Book book = new Book();
        book.setTitle("Spring");
        bookService.rent(book);
        bookService.returnBook(book);
    }
  • 함수를 intercept하여 InvocationHandler를 이용해 어떤 작업을 할 지 정해줌

서브 클래스를 만드는 방법의 단점

  • 상속을 사용하지 못하는 경우 프록시를 만들 수 없다.
    • Private 생성자만 있는 경우
    • Final 클래스인 경우
  • 인터페이스가 있을 때는 인터페이스의 프록시를 만들어 사용할 것

다이나믹 프록시 정리

  • 다이나믹 프록시
    • 런타임에 인터페이스 또는 클래스의 프록시 인스턴스 또는 클래스를 만들어 사용하는 프로그래밍 기법
  • 다이나믹 프록시 사용처
    • 스프링 데이터 JPA
    • 스프링 AOP
    • Mockito
    • 하이버네이트 lazy initialization
    • ...