Spring 트랜잭션 트랜잭션 및 동적 프록시 (b)는 -cglib 동적 프록시

인덱스 시리즈 :

  1. Spring 트랜잭션 트랜잭션 및 동적 프록시 (A) -JDK 프록시 구현
  2. Spring 트랜잭션 트랜잭션 및 동적 프록시 (b)는 -cglib 동적 프록시
  3. Spring 트랜잭션 트랜잭션 및 동적 프록시 (C) - 트랜잭션 실패 시나리오

CGLIB는 무엇인가

CGLIB 널리 절편 그들에게 방법을 제공 많은 AOP 프레임 워크에서 사용하는 강력한 고성능 코드 생성 패키지입니다. 그것은 인터페이스를 구현하지 않는 에이전트 클래스에 대해 제공하는 JDK의 다이내믹 프록시에 좋은 보완을 제공합니다. : JDK 기반 인터페이스 인터페이스 유형 강제해야한다 스프링 트랜잭션 트랜잭션 및 동적 프록시 (에) -JDK 프록시 구현

CGLIB 응용 프로그램

Github에서 CGLIB 따른 CGLIB 매우 광범위한 애플리케이션 (에 기재된 CGLIB ) 다음 애플리케이션 :

  1. 바이트 코드 엔지니어링 라이브러리
    , 라이브러리 쉽게 만들고, 분석하고 바이트 코드 파일을 조작 할 수있는 자바 Class 바이트 코드 파일입니다
  2. XORM는
    ORM에 대한 확장 가능한 프레임 워크를 사용 CGLIB는, 지속적인 엔티티 인터페이스는 RDBMS입니다 제공하기 위해 영속 객체 매핑을 생성하는 비즈니스 객체 모델에 집중하는 개발자 수 있습니다
  3. 최대 절전 모드
    Hibernate는 자바 객체 / 관계형 지속성 프레임 워크를위한 또 다른 강력한 초 고성능입니다. 영구 객체 협회, 상속, 다형성, 그리고 자바 컬렉션 프레임 워크의 조합을 포함하는 개발 될 수있다
  4. 상기 자바 클래스 파일 편집기
    사용자가 읽거나 할 때 디스크 / 수정 클래스 파일에 런타임에 클래스를로드, 그것은 또한 동적으로 새로운 클래스를 생성 할 수 있도록하는 자바 클래스 파일 편집기
  5. 난닝 측면들은
    자바 기반 AOP 프레임 워크 소개
  6. iBatis를 / MyBatis로
  7. ASM
  8. Proxool을의
    자바 기반의 연결 풀
  9. Guice
  10. 모델 매퍼

CGLIB 사용

사용 CGLIB의 필요성은 메이븐의 의존성을 추가, 항아리 패키지를 소개 :

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

마지막 방법은 두 가지 방법에 대한 비교 CGLIB 직조 결과 비 방법 최종 목표 클래스를 생성 :

public class Student {

    public void study(){
        System.out.println("study");
    }

    public final void eat(){
        System.out.println("eat");
    }

}

인터셉터 프록시 클래스 다음과 같습니다 :

public class CglibInterceptor implements MethodInterceptor {
    //织入前的处理
    private void beforeInvoke(Method method){
        System.out.println("before " + method.getName());
    }

    //织入后的处理
    private void afterInvoke(Method method){
        System.out.println("after "  + method.getName());
    }

    @Override
    public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
        beforeInvoke(method);
        //调用cglib的invokeSuper而不是invoke方法
        Object object = methodProxy.invokeSuper(o,objects);
        afterInvoke(method);
        return object;
    }
}

테스트 클래스는 주문 전화

  1. 향상된 내장 증강 예제 만들기
  2. 대상 클래스 메소드의 setSuperclass으로 설정
  3. 설정 인터셉터는 setCallback에 의해 차단
  4. 증강은 메서드 호출이 프록시 클래스 생성 생성
    다음과 같이 코드를 :
public class CglibTesst {

    public static void main(String[] args) {
        //把生产的代理类保存到磁盘指定文件夹
        System.getProperties().put(DebuggingClassWriter.DEBUG_LOCATION_PROPERTY, ".");
        Enhancer enhancer = new Enhancer();
        enhancer.setSuperclass(Student.class);
        enhancer.setCallback(new CglibInterceptor());

        Student studentProxy = (Student) enhancer.create();
        studentProxy.study();
        studentProxy.eat();
    }
}

다음과 같이 출력이 방법은 이전과 논리 후 연구에 만이 아닌 최종 짠 것을 알 수있다되며, 마지막 방법은 먹지한다 :

before study
study
after study

eat

CGLIB 생성 된 프록시 클래스 파일 분석

테스트 클래스를 추가하여

System.getProperties () (DebuggingClassWriter.DEBUG_LOCATION_PROPERTY을 ".") 넣습니다.;

코드 후, 자세한 내용을 알아 지역의 .class 파일의 수는 다음과 같다 :

학생 및 구현 공장 인터페이스 (인터페이스 방법을 주로 newInstance와, setCallback 및 getCallbacks) 상속 학생 $ EnhancerByCGLIB $ 92f3e3f6, 너무 많은 클래스 코드에서 먼저 보면, 다음과 같은 코드를 발췌 한 것입니다 :

public class Student$EnhancerByCGLIB$92f3e3f6 extends Student implements Factory {
    
    //静态初始化类
    static void CGLIB$STATICHOOK1() {
        CGLIB$THREAD_CALLBACKS = new ThreadLocal();
        CGLIB$emptyArgs = new Object[0];
        Class var0 = Class.forName("com.randy.dynamicproxy.cglib.Student$$EnhancerByCGLIB$$92f3e3f6");
        Class var1;
        Method[] var10000 = ReflectUtils.findMethods(new String[]{"equals", "(Ljava/lang/Object;)Z", "toString", "()Ljava/lang/String;", "hashCode", "()I", "clone", "()Ljava/lang/Object;"}, (var1 = Class.forName("java.lang.Object")).getDeclaredMethods());
        CGLIB$equals$1$Method = var10000[0];
        CGLIB$equals$1$Proxy = MethodProxy.create(var1, var0, "(Ljava/lang/Object;)Z", "equals", "CGLIB$equals$1");
        CGLIB$toString$2$Method = var10000[1];
        CGLIB$toString$2$Proxy = MethodProxy.create(var1, var0, "()Ljava/lang/String;", "toString", "CGLIB$toString$2");
        CGLIB$hashCode$3$Method = var10000[2];
        CGLIB$hashCode$3$Proxy = MethodProxy.create(var1, var0, "()I", "hashCode", "CGLIB$hashCode$3");
        CGLIB$clone$4$Method = var10000[3];
        CGLIB$clone$4$Proxy = MethodProxy.create(var1, var0, "()Ljava/lang/Object;", "clone", "CGLIB$clone$4");
        CGLIB$study$0$Method = ReflectUtils.findMethods(new String[]{"study", "()V"}, (var1 = Class.forName("com.randy.dynamicproxy.cglib.Student")).getDeclaredMethods())[0];
        CGLIB$study$0$Proxy = MethodProxy.create(var1, var0, "()V", "study", "CGLIB$study$0");
    }

    static {
        CGLIB$STATICHOOK1();
    }

    final void CGLIB$study$0() {
        super.study();
    }

    public final void study() {
        MethodInterceptor var10000 = this.CGLIB$CALLBACK_0;
        //检查当前Callback拦截对象
        if (var10000 == null) {
            CGLIB$BIND_CALLBACKS(this);
            var10000 = this.CGLIB$CALLBACK_0;
        }
        //根据是否存在判定是通过拦截类来调用还是直接调用父类Student的study方法
        if (var10000 != null) {
            var10000.intercept(this, CGLIB$study$0$Method, CGLIB$emptyArgs, CGLIB$study$0$Proxy);
        } else {
            super.study();
        }
    }

    final boolean CGLIB$equals$1(Object var1) {
        return super.equals(var1);
    }

    public final boolean equals(Object var1) {
       ...
    }

    final String CGLIB$toString$2() {
        return super.toString();
    }

    public final String toString() {
       ...
    }

    final int CGLIB$hashCode$3() {
        return super.hashCode();
    }

    public final int hashCode() {
        ...
    }

    final Object CGLIB$clone$4() throws CloneNotSupportedException {
        return super.clone();
    }

    protected final Object clone() throws CloneNotSupportedException {
       ...
    }
}

마지막 방법 알려 먹고 학생의 객체 Wati을 위해 생성 된 클래스, 메소드 공장 인터페이스를 구현하는 것 외에, 복제 및 비 - 최종 (마지막 방법의 학생 클래스의 슈퍼 클래스 Object 방법을 모두 볼 수있다,가는 notifyAll 같은 어떤 복제), CGLIB는 자바 복제 방법은 최종 최종 접근에 대한 프록시하지 허용하지 않습니다 수 있기 때문에 이유

다른 두 개의 클래스 $ EnhancerByCGLIB 학생 \ (92f3e3f6 \) FastClassByCGLIB \ (1d02f934 및 학생 \) FastClassByCGLIB $ ec571eb6 추상 클래스 FastClass CGLIB 상속하고,
주로 몇 가지 방법에 FastClass의 모양을 달성

    public abstract int getIndex(String var1, Class[] var2);
    public abstract int getIndex(Class[] var1);
    public abstract Object invoke(int var1, Object var2, Object[] var3) throws InvocationTargetException;
    public abstract Object newInstance(int var1, Object[] var2) throws InvocationTargetException;
    public abstract int getIndex(Signature var1);
    public abstract int getMaxIndex();

어느

CGLIB 원리

CGLIB 동적 서브 클래스에 대한 프록시 클래스를 생성, 모든 프록시 클래스의 메소드를 오버라이드 (override) 할 마지막 서브 클래스 (하지 CGLIB 마지막 방법 프록시 할 수 없습니다 ). 모든 부모 클래스 방법, 동종 요법 직조 가로 로직 차단 서브 클래스 메서드 호출을 차단 기술합니다.

CGLIB는, 바이트 코드 처리 프레임 워크 ASM을 기본 바이트 코드를 변환하고 새로운 클래스를 생성 할 수 있습니다. 자바 바이트 코드를 참조하십시오 정보 : [파일 형식 자바 클래스에서

증강 클래스의 소스 코드 분석

public class Enhancer extends AbstractClassGenerator {
    //设置目标类作为父类,也就是对应生成的Student$$EnhancerByCGLIB$$92f3e3f6类继承了Student
    public void setSuperclass(Class superclass) {
        if (superclass != null && superclass.isInterface()) {
            this.setInterfaces(new Class[]{superclass});
        } else if (superclass != null && superclass.equals(Object.class)) {
            this.superclass = null;
        } else {
            this.superclass = superclass;
        }

    }
    //通过Enhancer来创建代理类
    public Object create() {
        this.classOnly = false;
        this.argumentTypes = null;
        return this.createHelper();
    }
    
    private Object createHelper() {
        this.preValidate();
        //根据当前设置的父类等信心构造一个唯一的key
        Object key = KEY_FACTORY.newInstance(this.superclass != null ? this.superclass.getName() : null, ReflectUtils.getNames(this.interfaces), this.filter == ALL_ZERO ? null : new WeakCacheKey(this.filter), this.callbackTypes, this.useFactory, this.interceptDuringConstruction, this.serialVersionUID);
        this.currentKey = key;
        Object result = super.create(key);
        return result;
    }
}

    protected Object create(Object key) {
        try {
            ClassLoader loader = this.getClassLoader();
            Map<ClassLoader, AbstractClassGenerator.ClassLoaderData> cache = CACHE;
            //首先从缓存中查找key,如果就生成一个
            AbstractClassGenerator.ClassLoaderData data = (AbstractClassGenerator.ClassLoaderData)cache.get(loader);
            if (data == null) {
                Class var5 = AbstractClassGenerator.class;
                synchronized(AbstractClassGenerator.class) {
                    cache = CACHE;
                    data = (AbstractClassGenerator.ClassLoaderData)cache.get(loader);
                    if (data == null) {
                        Map<ClassLoader, AbstractClassGenerator.ClassLoaderData> newCache = new WeakHashMap(cache);
                        //核心是调用了AbstractClassGenerator的generate来生成字节码文件,并通过ReflectUtils.defineClass返回
                        data = new AbstractClassGenerator.ClassLoaderData(loader);
                        //加入缓存  
                        newCache.put(loader, data);
                        CACHE = newCache;
                    }
                }
            }

            this.key = key;
            Object obj = data.get(this, this.getUseCache());
            //如果是类就通过firstInstance初始化,而firstInstance在AbstractClassGenerator类中是一个抽象方法,具体实现如下
            //firstInstance和nextInstance都是通过cglib的ReflectUtils.newInstance来创建实例的
            return obj instanceof Class ? this.firstInstance((Class)obj) : this.nextInstance(obj);
        } catch (RuntimeException var9) {
            throw var9;
        } catch (Error var10) {
            throw var10;
        } catch (Exception var11) {
            throw new CodeGenerationException(var11);
        }
    }

MethodProxy

생성 된 프록시 클래스가 호출 될 때, MethodProxy 차단 호출 방법은 콜백 제공. MethodProxy.invokeSuper의 차단 방법과 위의 CglibInterceptor 클래스의 방법은 다음과 같은 소스 코드를 사용합니다 :

public Object invokeSuper(Object obj, Object[] args) throws Throwable {
        try {
            //单例初始化
            init();
            FastClassInfo fci = fastClassInfo;
            return fci.f2.invoke(fci.i2, obj, args);
        } catch (InvocationTargetException e) {
            throw e.getTargetException();
        }
    }

초기화 방법 :

초기화 () 메소드는 빈 선고하고 다시 잠기지 않은 경우 초기 객체가 초기화되었는지 여부를 결정하기 위해 고전 퍼펙트 단일 디자인 패턴입니다. 초기화의 주요 내용은 FastClassInfo 개체와 그 특성이다

private final Object initLock = new Object();

private void init()
    {
        if (fastClassInfo == null)
        {
            synchronized (initLock)
            {
                if (fastClassInfo == null)
                {
                    CreateInfo ci = createInfo;
                    FastClassInfo fci = new FastClassInfo();
                    fci.f1 = helper(ci, ci.c1);
                    fci.f2 = helper(ci, ci.c2);
                    //通过getIndex来查找到指定方法的索引
                    fci.i1 = fci.f1.getIndex(sig1);
                    fci.i2 = fci.f2.getIndex(sig2);
                    fastClassInfo = fci;
                    createInfo = null;
                }
            }
        }
    }

FastClass 메커니즘

FastClass 색인기구는 색인 대응하는 방법에 의해 직접 호출하는 클래스의 방법이며, 상기 (가변 FastClass 두 종류 채 내부 클래스)에 기재된 상기 invokeSuper INIT 초기화 주로 FastClassInfo.

 private static class FastClassInfo
    {
        //目标类的FastClass
        FastClass f1;
        //代理类的FastClass
        FastClass f2;
        //目标类方法的索引
        int i1;
        //代理类方法的索引
        int i2;
    }

전회 JDK 에이전트 구현 한 JDK 차단 오브젝트는 상대적으로 낮은 효율을 반영 인터셉트 메소드를 호출의 InvocationHandler기구에 의해 반사된다.
훌륭한 CGLIB 방법은 직접 적절한 방법을 호출하여 인덱스 인덱스의 종류에 기초한다.
이러한 학생의 발생 등 \ (FastClassByCGLIB \) ec571eb6가 FastClass, getIndex (서명) 메소드 서명하여 인덱스 위치 상속,

    public int getIndex(Signature var1) {
        String var10000 = var1.toString();
        switch(var10000.hashCode()) {
        case -1310345955:
            if (var10000.equals("eat()V")) {
                return 1;
            }
            break;
        case 1826985398:
            if (var10000.equals("equals(Ljava/lang/Object;)Z")) {
                return 2;
            }
            break;
        case 1876544780:
            if (var10000.equals("study()V")) {
                return 0;
            }
            break;
        case 1913648695:
            if (var10000.equals("toString()Ljava/lang/String;")) {
                return 3;
            }
            break;
        case 1984935277:
            if (var10000.equals("hashCode()I")) {
                return 4;
            }
        }

        return -1;
    }

방법은 추상적있어서, 서브 클래스 (즉 FastClass 학생 클래스 생성 호출 취득한 인덱스 위치 인보 호에 따른 방법에 \ (FastClassByCGLIB \) ec571eb6 FastClass 상속)의 구현은 다음과 같은 :

 public Object invoke(int var1, Object var2, Object[] var3) throws InvocationTargetException {
        Student var10000 = (Student)var2;
        int var10001 = var1;

        try {
            switch(var10001) {
            case 0:
                var10000.study();
                return null;
            case 1:
                var10000.eat();
                return null;
            case 2:
                return new Boolean(var10000.equals(var3[0]));
            case 3:
                return var10000.toString();
            case 4:
                return new Integer(var10000.hashCode());
            }
        } catch (Throwable var4) {
            throw new InvocationTargetException(var4);
        }
}

참조 :

  1. https://github.com/cglib/cglib/wiki
  2. https://tech.meituan.com/2019/09/05/java-bytecode-enhancement.html
  3. https://docs.oracle.com/javase/specs/jvms/se7/html/jvms-4.html
  4. https://www.baeldung.com/cglib
  5. https://www.ibm.com/developerworks/cn/java/j-lo-springaopcglib/index.html

추천

출처www.cnblogs.com/qizhelongdeyang/p/12412049.html