클래스 프록시

자바의 Proxcy는 오직 인터페이스만 적용할 수 있다.
그래서 이번에는 cglib과 bytebuddy를 이용해 클래스 프록시를 구현하는 방법을 알아본다.

cglib으로 구현하는 방법

cglib은 자바 클래스 파일을 조작하여 동적으로 클래스를 생성할 수 있는 라이브러리다.
따라서 프록시 객체를 생성하기 위해 대상 객체와 같은 클래스를 만들어내는 방식으로 구현된다.

maven dependency

<dependency>
     <groupId>cglib</groupId>
     <artifactId>cglib</artifactId>
     <version>3.3.0</version>
</dependency>

방법

Enhancer.create(Class type, Callback callback)
프록시 객체를 생성하는 함수
type에 프록시 적용할 클래스와 callback에 프록시를 구현할 MethodInterceptor를 매개 변수로 넘긴다.

예제 코드

BookService cglibBookService = (BookService) Enhancer.create(BookServiceImpl.class, new MethodInterceptor() {
    final BookServiceImpl bookService = new BookServiceImpl();

    @Override
    public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
        if(!method.getName().equals("rent")) {
            return method.invoke(bookService, args);
        }
        System.out.println("aaa");
        Object invoke = method.invoke(bookService, args);
        System.out.println("bbb");
        return invoke;
    }
});

Book book = new Book();
book.setTitle("spring");
cglibBookService.rent(book);
cglibBookService.returnBook(book);

bytebuddy로 구현하는 방법

bytebuddy는 cglib과 비슷한 동적 클래스 생성 라이브러리다.

maven dependency

<dependency>
    <groupId>net.bytebuddy</groupId>
    <artifactId>byte-buddy</artifactId>
    <version>1.10.16</version
</dependency>

방법

메소드를 인터셉트해 프록시를 적용한 새로운 클래스를 만든다.

예제 코드

Class<? extends BookServiceImpl> proxyClass = new ByteBuddy()
        .subclass(BookServiceImpl.class)
        .method(named("rent"))
        .intercept(InvocationHandlerAdapter.of(new InvocationHandler() {
            final BookService bookService = new BookServiceImpl();

            @Override
            public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                if (!method.getName().equals("rent")) {
                    return method.invoke(bookService, args);
                }
                System.out.println("aaa");
                Object invoke = method.invoke(bookService, args);
                System.out.println("bbb");
                return invoke;
            }
        }))
        .make().load(BookServiceImpl.class.getClassLoader()).getLoaded();
BookService bookService = proxyClass.getConstructor(null).newInstance();

Book book = new Book();
book.setTitle("spring");
bookService.rent(book);
bookService.returnBook(book);

주의

단 상속이 제한된 클래스에 한해서는 사용할 수 없다.

예제는 이곳 에서 확인하실수 있습니다.