Spring AOP 기초 - CGlib 동적 에이전트

JDK 를 사용 하여 프 록 시 를 만 드 는 데 제한 이 있 습 니 다. 즉, 그 는 인터페이스 에 만 프 록 시 인 스 턴 스 를 만 들 수 있다 는 점 에서 프 록 시 인터페이스 new Proxy Instance (ClassLoader loader, Class [] interfaces, Invocation Handler h) 의 방법 서명 에서 잘 볼 수 있 습 니 다. 두 번 째 매개 변수 인 터 페 이 스 는 프 록 시 인 스 턴 스 가 필요 한 인터페이스 목록 입 니 다.인 터 페 이 스 를 통 해 업무 방법 을 정의 하지 않 은 클래스 에 대해 어떻게 프 록 시 인 스 턴 스 를 동적 으로 만 듭 니까?JDK 의 대리 기술 은 이미 하 찮 은 재주 가 부족 하고 CGLib 는 대체 자로 서 이 빈 자 리 를 메 웠 다.
CGLib 는 매우 밑바닥 에 있 는 바이트 코드 기술 을 사용 하여 하나의 클래스 에 하위 클래스 를 만 들 고 하위 클래스 에서 방법 으로 차단 하 는 기술 로 모든 부모 클래스 방법의 호출 을 차단 하고 횡 절 논리 에 따라 짜 낼 수 있다.다음은 CGLib 기술 을 사용 하여 성능 감 시 를 위 한 크로스 논리 프 록 시 대상 을 만 들 수 있 는 프 록 시 생 성 기 를 만 듭 니 다. 코드 는 다음 과 같 습 니 다.
JDK 에이전트 와 마찬가지 로 방법의 성능 을 감시 하 는 도구 클래스 가 필요 합 니 다. MethodPerformance.class
package com.cglib;

public class MethodPerformance {
    private long begin;
    private long end;
    private String serviceMethod;
    public MethodPerformance(String serviceMethod){
        this.serviceMethod = serviceMethod;
        this.begin = System.currentTimeMillis();
    }
    public void printPerformance(){
        end = System.currentTimeMillis();
        long elapse = end - begin;
        System.out.println(serviceMethod + "  " + elapse + "  ");
    }
}

그리고 성능 감시 코드 를 Performance Handler 에 설치 합 니 다.
package com.cglib;

public class PerformanceMonitor {
    private static ThreadLocal<MethodPerformance> performanceRecord = new ThreadLocal<MethodPerformance>();
    
    public static void begin(String method){
        System.out.println("begin monitor……");
        MethodPerformance mp = new MethodPerformance(method);
        performanceRecord.set(mp);
    }
    public static void end(){
        System.out.println("end monitor……");
        MethodPerformance mp = performanceRecord.get();
        mp.printPerformance();
    }
}

비 즈 니스 클래스 는 Forum ServiceImpl 로 JDK 에이전트 와 달리 이 곳 의 업무 방법 은 어떠한 인터페이스 도 계승 하지 않 았 습 니 다.
package com.cglib;

public class ForumServiceImpl {

    @SuppressWarnings("static-access")
    public void removeForum(int forumId) {
        System.out.println("    Forum  :" + forumId);
        try {
            Thread.currentThread().sleep(20);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

    @SuppressWarnings("static-access")
    public void removeTopic(int topicId) {
        System.out.println("    Topic  :" + topicId);
        try {
            Thread.currentThread().sleep(40);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

그리고 우 리 는 CGLib 기술 을 사용 하여 프 록 시 생 성 기 를 다음 과 같이 작성 합 니 다.
package com.cglib;

import java.lang.reflect.Method;

import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;

public class CGLibProxy implements MethodInterceptor {
    private Enhancer enhancer = new Enhancer();
    public Object getProxy(Class clazz){
        enhancer.setSuperclass(clazz);
        enhancer.setCallback(this);
        return enhancer.create();
    }
    public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
        PerformanceMonitor.begin(obj.getClass().getName() + "." + method.getName());
        Object result = proxy.invokeSuper(obj, args);
        PerformanceMonitor.end();
        return result;
    }

}

마지막 으로 테스트 클래스 를 통 해 테스트:
package com.cglib;

public class TestForumService {
    public static void main(String[] args) {
        CGLibProxy proxy = new CGLibProxy();
        ForumServiceImpl forumService = (ForumServiceImpl) proxy.getProxy(ForumServiceImpl.class);
        forumService.removeForum(10);
        forumService.removeTopic(1023);
    }
}

테스트 결 과 는 다음 과 같다.
begin monitor... 아 날로 그 포럼 기록 삭제: 10 end monitor... com. cglib. ForumServiceImpl $$$EnhancerByCGLIB $$$b80af08a. removeForum 31 밀리초 걸 려 begin monitor... 아 날로 그 삭제 Topic 기록 삭제: 1023 end monitor... com. cglib. ForumServiceImpl $$EnhancerByCGLIB $$b80af08a. removeTopic 47 밀리초 걸 립 니 다.
결과 출력 과 JDK 에이 전 트 를 사용 하 는 것 은 기본적으로 일치 하지만 클래스 이름 에 차이 가 있 습 니 다. 이 는 CGLib 가 Forum ServiceImpl 을 위 한 하위 클래스 를 실제 실행 하고 있 기 때 문 입 니 다. 위의 클래스 이름 은 만 든 하위 클래스 의 이름 입 니 다.
 
JDK 와 CGLib 의 비교:
JDK 동적 에이전트 가 만 든 에이전트 대상 은 JDK 1.3 에서 성능 이 좋 지 않 습 니 다.높 은 버 전의 JDK 에 서 는 동적 프 록 시 대상 의 성능 이 향상 되 었 지만 CGLib 가 만 든 동적 프 록 시 대상 의 성능 은 여전히 JDK 가 만 든 프 록 시 대상 의 성능 보다 훨씬 높다 (약 10 배).그러나 CGLib 는 프 록 시 대상 을 만 드 는 데 걸 리 는 시간 이 JDK 동적 에이전트 (약 8 배) 보다 많 기 때문에 Singleton 의 프 록 시 대상 이나 인 스 턴 스 풀 이 있 는 프 록 시 에 대해 서 는 프 록 시 대상 을 자주 만 들 필요 가 없 기 때문에 CGLib 동적 프 록 시 기술 을 사용 하 는 것 이 좋 고 반대로 JDK 동적 프 록 시 기술 을 적용 합 니 다.단, CGLib 는 하위 클래스 를 동적 으로 생 성 하 는 방식 으로 프 록 시 대상 을 생 성하 기 때문에 대상 클래스 의 final 방법 에 대해 프 록 시 를 할 수 없습니다. 또한 클래스 가 어떠한 인터페이스 도 실현 되 지 않 으 면 CGLib 를 선택 하여 동적 프 록 시 를 할 수 밖 에 없습니다.

좋은 웹페이지 즐겨찾기