Расскажите о ямах, с которыми сталкиваются динамические агенты cglib

задняя часть
Расскажите о ямах, с которыми сталкиваются динамические агенты cglib

Введение

cglib — еще один метод динамического прокси.Он отличается от реализации динамического прокси jdk.Мы уже видели, что класс динамического прокси jdk должен реализовывать интерфейс, в то время как cglib не должен реализовывать интерфейс, но должен гарантировать, что класс не содержит конечное ключевое слово, иначе делегировать его нельзя. Эта статья представляет собой анализ проблемы бесконечного цикла cglib, с которой я случайно столкнулся.

кейс cglib

Давайте покажем случай бесконечного цикла cglib. Первый — это класс, который нужно проксировать, или, как обычно, просто объявите свой собственный метод, но убедитесь, что класс и метод не изменены ключевым словом final. Изменение класса с помощью ключевого слова final будет напрямую сообщать об исключении, но модифицированный метод не будет вызывать исключение, но этот метод не будет проксироваться, но он не влияет на другие проксируемые методы.

public class InfoDemo {
    public void welcome (String person){
        System.out.println("welcome :" + person);
    }
}

Ниже приведена конкретная реализация прокси-класса.

public class CglibInfoProxy implements MethodInterceptor{
    private Object target;
    public Object newInstance(Object source){
        target = source;
        Enhancer enhancer = new Enhancer();
        enhancer.setSuperclass(this.target.getClass());
        enhancer.setCallback(this);
        return enhancer.create();
    }

    @Override
    public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
        System.out.println("before method!!!");
        Object value = methodProxy.invoke(o, objects);
        //Object value = methodProxy.invoke(this.target, objects);
        //Object value = methodProxy.invokeSuper(o, objects);
        return value;
    }
   public static void main(String[] args) {
        //System.setProperty(DebuggingClassWriter.DEBUG_LOCATION_PROPERTY, "D:\\\\classes");
      InfoDemo instance = (InfoDemo) new CglibInfoProxy().newInstance(new InfoDemo());
      instance.welcome("zhangsan");

    }
}

Он очень похож на наш динамический прокси jdk, за исключением того, что интерфейсы, реализованные двумя классами, отличаются, и методы генерации объектов также различны. здесь очень жалкоinvokeМетоды иinvokeSuperЕсли вы используете метод invoke, вы должны использовать прокси-объект, который является целью выше, а если вы вызываете метод invokeSuper, вы должны использовать прокси-объект o.

Приведенный выше пример вызовет бесконечный цикл, в результате чего StackOverflowFlow, эй, нет обучения, сцена переполнения стека

Конкретно почему так происходит, вы можете сначала подумать, а потом мы объясним это в реализации исходного кода. Теперь давайте посмотрим на текущие результаты

...
before method!!!
before method!!!
before method!!!
Exception in thread "main" java.lang.StackOverflowError
	at java.nio.CharBuffer.<init>(CharBuffer.java:281)
	at java.nio.HeapCharBuffer.<init>(HeapCharBuffer.java:70)
	at java.nio.CharBuffer.wrap(CharBuffer.java:373)
	at sun.nio.cs.StreamEncoder.implWrite(StreamEncoder.java:265)
	at sun.nio.cs.StreamEncoder.write(StreamEncoder.java:125)
	at java.io.OutputStreamWriter.write(OutputStreamWriter.java:207)
	at java.io.BufferedWriter.flushBuffer(BufferedWriter.java:129)
	at java.io.PrintStream.write(PrintStream.java:526)
	at java.io.PrintStream.print(PrintStream.java:669)
	at java.io.PrintStream.println(PrintStream.java:806)
	at com.eumji.proxy.cglib.CglibInfoProxy.intercept(CglibInfoProxy.java:30)
	at com.eumji.proxy.cglib.InfoDemo?EnhancerByCGLIB?870a84d7.welcome(<generated>)
	at com.eumji.proxy.cglib.InfoDemo?FastClassByCGLIB?2e560a7d.invoke(<generated>)
	at net.sf.cglib.proxy.MethodProxy.invoke(MethodProxy.java:204)
...

Здесь показаны только некоторые эффекты, вы можете попробовать сами.

Если мы заменим два других утверждения, это будет правильный вывод, конкретные результаты будут следующими

before method!!!
welcome :zhangsan

Аналитический принцип

Чтобы понять, что происходит, прежде всего, давайте посмотрим, как выглядит класс после прокси cglib.Чтобы сгенерировать файл класса прокси, нам просто нужно раскомментировать этот метод в нашем основном методе.

System.setProperty(DebuggingClassWriter.DEBUG_LOCATION_PROPERTY, "D:\\classes");

Соответствующий файл прокси-класса будет создан в файле классов на диске D. Следует отметить, что создано три прокси-класса.Сначала мы представим прокси-класс InfoDemo, который нас больше всего интересует, а другое подходящее время будет описанные ниже, два других.

Декомпилированный код InfoDemo

По сути, это перетаскивание файла класса прямо в IDEA

public class InfoDemo?EnhancerByCGLIB?870a84d7 extends InfoDemo implements Factory {
    private boolean CGLIB$BOUND;
    private static final ThreadLocal CGLIB$THREAD_CALLBACKS;
    private static final Callback[] CGLIB$STATIC_CALLBACKS;
    private MethodInterceptor CGLIB$CALLBACK_0;
    private static final Method CGLIB$welcome$0$Method;
    private static final MethodProxy CGLIB$welcome$0$Proxy;
    private static final Object[] CGLIB$emptyArgs;
    private static final Method CGLIB$finalize$1$Method;
    private static final MethodProxy CGLIB$finalize$1$Proxy;
    private static final Method CGLIB$equals$2$Method;
    private static final MethodProxy CGLIB$equals$2$Proxy;
    private static final Method CGLIB$toString$3$Method;
    private static final MethodProxy CGLIB$toString$3$Proxy;
    private static final Method CGLIB$hashCode$4$Method;
    private static final MethodProxy CGLIB$hashCode$4$Proxy;
    private static final Method CGLIB$clone$5$Method;
    private static final MethodProxy CGLIB$clone$5$Proxy;

    static void CGLIB$STATICHOOK1() {
        CGLIB$THREAD_CALLBACKS = new ThreadLocal();
        CGLIB$emptyArgs = new Object[0];
        Class var0 = Class.forName("com.eumji.proxy.cglib.InfoDemo?EnhancerByCGLIB?870a84d7");
        Class var1;
        Method[] var10000 = ReflectUtils.findMethods(new String[]{"finalize", "()V", "equals", "(Ljava/lang/Object;)Z", "toString", "()Ljava/lang/String;", "hashCode", "()I", "clone", "()Ljava/lang/Object;"}, (var1 = Class.forName("java.lang.Object")).getDeclaredMethods());
        CGLIB$finalize$1$Method = var10000[0];
        CGLIB$finalize$1$Proxy = MethodProxy.create(var1, var0, "()V", "finalize", "CGLIB$finalize$1");
        CGLIB$equals$2$Method = var10000[1];
        CGLIB$equals$2$Proxy = MethodProxy.create(var1, var0, "(Ljava/lang/Object;)Z", "equals", "CGLIB$equals$2");
        CGLIB$toString$3$Method = var10000[2];
        CGLIB$toString$3$Proxy = MethodProxy.create(var1, var0, "()Ljava/lang/String;", "toString", "CGLIB$toString$3");
        CGLIB$hashCode$4$Method = var10000[3];
        CGLIB$hashCode$4$Proxy = MethodProxy.create(var1, var0, "()I", "hashCode", "CGLIB$hashCode$4");
        CGLIB$clone$5$Method = var10000[4];
        CGLIB$clone$5$Proxy = MethodProxy.create(var1, var0, "()Ljava/lang/Object;", "clone", "CGLIB$clone$5");
        CGLIB$welcome$0$Method = ReflectUtils.findMethods(new String[]{"welcome", "(Ljava/lang/String;)V"}, (var1 = Class.forName("com.eumji.proxy.cglib.InfoDemo")).getDeclaredMethods())[0];
        CGLIB$welcome$0$Proxy = MethodProxy.create(var1, var0, "(Ljava/lang/String;)V", "welcome", "CGLIB$welcome$0");
    }

    final void CGLIB$welcome$0(String var1) {
        super.welcome(var1);
    }

    public final void welcome(String var1) {
        MethodInterceptor var10000 = this.CGLIB$CALLBACK_0;
        if (this.CGLIB$CALLBACK_0 == null) {
            CGLIB$BIND_CALLBACKS(this);
            var10000 = this.CGLIB$CALLBACK_0;
        }

        if (var10000 != null) {
            var10000.intercept(this, CGLIB$welcome$0$Method, new Object[]{var1}, CGLIB$welcome$0$Proxy);
        } else {
            super.welcome(var1);
        }
    }
  ...

}

Глядя на код, мы видим, что cglib все еще очень сложен. Теперь мы можем взглянуть на метод приветствия, который нас интересует. Из приведенного выше кода мы видим, что cglib сгенерирует два прокси-метода для методов проксируемого класс в то же время.Одно и то же имяwelcome方法иCGLIB$welcome$0方法

1.CGLIB$welcome$0方法Способ прямого вызова агента не сухой.

2.welcome方法Сначала оцените, установлен callback или нет, очевидно, если мы задали его в коде, то это CglibInfoProxy, поэтому будет вызван метод CglibInfoProxy.intercept.

Изначально я хотел проанализировать процесс генерации волны прокси-классов, но после прочтения он оказался немного сложным, поэтому пока не буду его анализировать. . . .

метод invokeSuper

Я также упомянул проблему, что методы invoke и invokeSuper вызовут проблемы, если вы не будете осторожны.Здесь мы проследим причину проблемы на уровне кода.

Давайте посмотрим на поток выполнения метода proxy-класса invokeSuper.

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

Основная функция invokeSuper здесь заключается в инициализации fastClassInfo.

метод инициализации

private void init() {
    if (this.fastClassInfo == null) {
        Object var1 = this.initLock;
        synchronized(this.initLock) {
            if (this.fastClassInfo == null) {
                MethodProxy.CreateInfo ci = this.createInfo;
                MethodProxy.FastClassInfo fci = new MethodProxy.FastClassInfo();
                fci.f1 = helper(ci, ci.c1);
                fci.f2 = helper(ci, ci.c2);
                fci.i1 = fci.f1.getIndex(this.sig1);
                fci.i2 = fci.f2.getIndex(this.sig2);
                this.fastClassInfo = fci;
                this.createInfo = null;
            }
        }
    }
}

Вышеупомянутый метод в основном предназначен для загрузки methodProxy.FastClassInfo. ci инициализируется раньше, где c1 относится к прокси-классу InfoDemo, а c2com.eumji.proxy.cglib.InfoDemo?EnhancerByCGLIB?efe38465Это класс прокси.

Затем сгенерируйте соответствующие f1 и f2 и нижние индексы i1 и i2 метода, i1 и i2 соответствуют тому, что было сказано в началеwelcome方法иCGLIB$welcome$0方法, код позади можно увидеть.

А f1 соответствуетInfoDemo?FastClassByCGLIB?2e560a7dКласс прокси, f2 соответствуетInfoDemo?EnhancerByCGLIB?efe38465?FastClassByCGLIB?38345933класс прокси. Их можно просмотреть в сгенерированном прокси-классе.

Конечно, здесь не сказано, что здесь генерируется, если вам интересно, вы можете посмотреть, как генерируется байт-код. Лично не понимаю.

Разница между вызовом и вызовомSuper

Почему должны генерироваться два прокси-класса f1 и f2?Я думаю, вы должны были обратить внимание на предыдущий метод.Выше мы упоминали метод invoke и метод invokeSuper. Давайте сравним разницу между методом invoke и методом invokeSuper.

public Object invoke(Object obj, Object[] args) throws Throwable {
  this.init();
  MethodProxy.FastClassInfo fci = this.fastClassInfo;
  return fci.f1.invoke(fci.i1, obj, args);
}

Мы видим, что invoke использует метод f1.invoke, а invokeSuper использует метод f2.invoke.

Сначала посмотрите на логику метода вызова, соответствующего f1.

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

        try {
            switch(var10001) {
            case 0:
                var10000.welcome((String)var3[0]);
                return null;
                ....
}

Вызовите метод приветствия объекта InfoDemo напрямую.

Таким образом, это также может объяснить, почему мы ранее вызывали метод invoke в цикле, потому что var2, которую мы передали, является прокси-объектом InfoDemo. Глядя на код прокси-класса в начале, мы видим, что он вернется к вызовите метод снова, что приведет к бесконечному циклу.

Давайте посмотрим на реализацию соответствующего вызова в f2.

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

        try {
            switch(var10001) {
             ....
                var10000.CGLIB$finalize$1();
                return null;
            case 16:
                var10000.CGLIB$welcome$0((String)var3[0]);
                return null;
           ....
    }

Поскольку var2, который мы передали в этот раз, является прокси-объектом InfoDemo, прокси-класс в конечном итоге будет вызыватьсяCGLIB$welcome$0метод.

резюме

Это просто неудачная попытка анализа исходного кода, но я разобрался с причиной вызова бесконечного цикла.Могу лишь сказать, что cglib гораздо сложнее, чем динамический прокси jdk, в основном отражается в логике сгенерированного кода и сгенерированного код, который требует дальнейшего изучения.

И есть два метода вызова, invoke и invokeSuper, поэтому будьте осторожны при их использовании.

Эпилог

Эта статья из личных заметок, если есть какие-то неуместные выражения или упущения, прошу меня поправить.

Поделиться с тобой! ! !

Введение

cglib — еще один метод динамического прокси.Он отличается от реализации динамического прокси jdk.Мы уже видели, что класс динамического прокси jdk должен реализовывать интерфейс, в то время как cglib не должен реализовывать интерфейс, но должен гарантировать, что класс не содержит конечное ключевое слово, иначе делегировать его нельзя.

кейс cglib

Давайте покажем случай бесконечного цикла cglib. Первый — это класс, который нужно проксировать, или, как обычно, просто объявите свой собственный метод, но убедитесь, что класс и метод не изменены ключевым словом final. Изменение класса с помощью ключевого слова final будет напрямую сообщать об исключении, но модифицированный метод не будет вызывать исключение, но этот метод не будет проксироваться, но он не влияет на другие проксируемые методы.

public class InfoDemo {
    public void welcome (String person){
        System.out.println("welcome :" + person);
    }
}

Ниже приведена конкретная реализация прокси-класса.

public class CglibInfoProxy implements MethodInterceptor{
    private Object target;
    public Object newInstance(Object source){
        target = source;
        Enhancer enhancer = new Enhancer();
        enhancer.setSuperclass(this.target.getClass());
        enhancer.setCallback(this);
        return enhancer.create();
    }

    @Override
    public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
        System.out.println("before method!!!");
        Object value = methodProxy.invoke(o, objects);
        //Object value = methodProxy.invoke(this.target, objects);
        //Object value = methodProxy.invokeSuper(o, objects);
        return value;
    }
   public static void main(String[] args) {
        //System.setProperty(DebuggingClassWriter.DEBUG_LOCATION_PROPERTY, "D:\\\\classes");
      InfoDemo instance = (InfoDemo) new CglibInfoProxy().newInstance(new InfoDemo());
      instance.welcome("zhangsan");

    }
}

Он очень похож на наш динамический прокси jdk, за исключением того, что интерфейсы, реализованные двумя классами, отличаются, и методы генерации объектов также различны. здесь очень жалкоinvokeМетоды иinvokeSuperЕсли вы используете метод invoke, вы должны использовать прокси-объект, который является целью выше, а если вы вызываете метод invokeSuper, вы должны использовать прокси-объект o.

Приведенный выше пример вызовет бесконечный цикл, в результате чего StackOverflowFlow, эй, нет обучения, сцена переполнения стека

Конкретно почему так происходит, вы можете сначала подумать, а потом мы объясним это в реализации исходного кода. Теперь давайте посмотрим на текущие результаты

...
before method!!!
before method!!!
before method!!!
Exception in thread "main" java.lang.StackOverflowError
	at java.nio.CharBuffer.<init>(CharBuffer.java:281)
	at java.nio.HeapCharBuffer.<init>(HeapCharBuffer.java:70)
	at java.nio.CharBuffer.wrap(CharBuffer.java:373)
	at sun.nio.cs.StreamEncoder.implWrite(StreamEncoder.java:265)
	at sun.nio.cs.StreamEncoder.write(StreamEncoder.java:125)
	at java.io.OutputStreamWriter.write(OutputStreamWriter.java:207)
	at java.io.BufferedWriter.flushBuffer(BufferedWriter.java:129)
	at java.io.PrintStream.write(PrintStream.java:526)
	at java.io.PrintStream.print(PrintStream.java:669)
	at java.io.PrintStream.println(PrintStream.java:806)
	at com.eumji.proxy.cglib.CglibInfoProxy.intercept(CglibInfoProxy.java:30)
	at com.eumji.proxy.cglib.InfoDemo?EnhancerByCGLIB?870a84d7.welcome(<generated>)
	at com.eumji.proxy.cglib.InfoDemo?FastClassByCGLIB?2e560a7d.invoke(<generated>)
	at net.sf.cglib.proxy.MethodProxy.invoke(MethodProxy.java:204)
...

Здесь показаны только некоторые эффекты, вы можете попробовать сами.

Если мы заменим два других утверждения, это будет правильный вывод, конкретные результаты будут следующими

before method!!!
welcome :zhangsan

Принципиальный анализ

Чтобы понять, что происходит, прежде всего, давайте посмотрим, как выглядит класс после прокси cglib.Чтобы сгенерировать файл класса прокси, нам просто нужно раскомментировать этот метод в нашем основном методе.

System.setProperty(DebuggingClassWriter.DEBUG_LOCATION_PROPERTY, "D:\\classes");

Соответствующий файл прокси-класса будет создан в файле классов на диске D. Следует отметить, что создано три прокси-класса.Сначала мы представим прокси-класс InfoDemo, который нас больше всего интересует, а другое подходящее время будет описанные ниже, два других.

Декомпилированный код InfoDemo

По сути, это перетаскивание файла класса прямо в IDEA

public class InfoDemo?EnhancerByCGLIB?870a84d7 extends InfoDemo implements Factory {
    private boolean CGLIB$BOUND;
    private static final ThreadLocal CGLIB$THREAD_CALLBACKS;
    private static final Callback[] CGLIB$STATIC_CALLBACKS;
    private MethodInterceptor CGLIB$CALLBACK_0;
    private static final Method CGLIB$welcome$0$Method;
    private static final MethodProxy CGLIB$welcome$0$Proxy;
    private static final Object[] CGLIB$emptyArgs;
    private static final Method CGLIB$finalize$1$Method;
    private static final MethodProxy CGLIB$finalize$1$Proxy;
    private static final Method CGLIB$equals$2$Method;
    private static final MethodProxy CGLIB$equals$2$Proxy;
    private static final Method CGLIB$toString$3$Method;
    private static final MethodProxy CGLIB$toString$3$Proxy;
    private static final Method CGLIB$hashCode$4$Method;
    private static final MethodProxy CGLIB$hashCode$4$Proxy;
    private static final Method CGLIB$clone$5$Method;
    private static final MethodProxy CGLIB$clone$5$Proxy;

    static void CGLIB$STATICHOOK1() {
        CGLIB$THREAD_CALLBACKS = new ThreadLocal();
        CGLIB$emptyArgs = new Object[0];
        Class var0 = Class.forName("com.eumji.proxy.cglib.InfoDemo?EnhancerByCGLIB?870a84d7");
        Class var1;
        Method[] var10000 = ReflectUtils.findMethods(new String[]{"finalize", "()V", "equals", "(Ljava/lang/Object;)Z", "toString", "()Ljava/lang/String;", "hashCode", "()I", "clone", "()Ljava/lang/Object;"}, (var1 = Class.forName("java.lang.Object")).getDeclaredMethods());
        CGLIB$finalize$1$Method = var10000[0];
        CGLIB$finalize$1$Proxy = MethodProxy.create(var1, var0, "()V", "finalize", "CGLIB$finalize$1");
        CGLIB$equals$2$Method = var10000[1];
        CGLIB$equals$2$Proxy = MethodProxy.create(var1, var0, "(Ljava/lang/Object;)Z", "equals", "CGLIB$equals$2");
        CGLIB$toString$3$Method = var10000[2];
        CGLIB$toString$3$Proxy = MethodProxy.create(var1, var0, "()Ljava/lang/String;", "toString", "CGLIB$toString$3");
        CGLIB$hashCode$4$Method = var10000[3];
        CGLIB$hashCode$4$Proxy = MethodProxy.create(var1, var0, "()I", "hashCode", "CGLIB$hashCode$4");
        CGLIB$clone$5$Method = var10000[4];
        CGLIB$clone$5$Proxy = MethodProxy.create(var1, var0, "()Ljava/lang/Object;", "clone", "CGLIB$clone$5");
        CGLIB$welcome$0$Method = ReflectUtils.findMethods(new String[]{"welcome", "(Ljava/lang/String;)V"}, (var1 = Class.forName("com.eumji.proxy.cglib.InfoDemo")).getDeclaredMethods())[0];
        CGLIB$welcome$0$Proxy = MethodProxy.create(var1, var0, "(Ljava/lang/String;)V", "welcome", "CGLIB$welcome$0");
    }

    final void CGLIB$welcome$0(String var1) {
        super.welcome(var1);
    }

    public final void welcome(String var1) {
        MethodInterceptor var10000 = this.CGLIB$CALLBACK_0;
        if (this.CGLIB$CALLBACK_0 == null) {
            CGLIB$BIND_CALLBACKS(this);
            var10000 = this.CGLIB$CALLBACK_0;
        }

        if (var10000 != null) {
            var10000.intercept(this, CGLIB$welcome$0$Method, new Object[]{var1}, CGLIB$welcome$0$Proxy);
        } else {
            super.welcome(var1);
        }
    }
  ...

}

Глядя на код, мы видим, что cglib все еще очень сложен. Теперь мы можем взглянуть на метод приветствия, который нас интересует. Из приведенного выше кода мы видим, что cglib сгенерирует два прокси-метода для методов проксируемого класс в то же время.Одно и то же имяwelcome方法иCGLIB$welcome$0方法

1.CGLIB$welcome$0方法Вызовите делегированный метод напрямую, то есть ничего не делайте.

2.welcome方法Сначала оцените, установлен callback или нет, очевидно, если мы задали его в коде, то это CglibInfoProxy, поэтому будет вызван метод CglibInfoProxy.intercept.

Изначально я хотел проанализировать процесс генерации волны прокси-классов, но после прочтения он оказался немного сложным, поэтому пока не буду его анализировать. . . .

метод invokeSuper

Я также упомянул проблему, что методы invoke и invokeSuper вызовут проблемы, если вы не будете осторожны.Здесь мы проследим причину проблемы на уровне кода.

Давайте посмотрим на поток выполнения метода proxy-класса invokeSuper.

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

Основная функция invokeSuper здесь заключается в инициализации fastClassInfo, а затем вызове целевого метода через fastClassInfo.

метод инициализации

Этот метод также важен, потому что генерируются прокси-классы f1 и f2.

private void init() {
    if (this.fastClassInfo == null) {
        Object var1 = this.initLock;
        synchronized(this.initLock) {
            if (this.fastClassInfo == null) {
                MethodProxy.CreateInfo ci = this.createInfo;
                MethodProxy.FastClassInfo fci = new MethodProxy.FastClassInfo();
                fci.f1 = helper(ci, ci.c1); //fastclass代理类生成
                fci.f2 = helper(ci, ci.c2);//fastclass代理类生成
                fci.i1 = fci.f1.getIndex(this.sig1); //获取下标
                fci.i2 = fci.f2.getIndex(this.sig2);
                this.fastClassInfo = fci;
                this.createInfo = null;
            }
        }
    }
}

Вышеупомянутый метод в основном предназначен для загрузки methodProxy.FastClassInfo. ci инициализируется раньше, где c1 относится к прокси-классу InfoDemo, а c2com.eumji.proxy.cglib.InfoDemo?EnhancerByCGLIB?efe38465Это класс прокси.

Затем сгенерируйте соответствующие f1 и f2 и нижние индексы i1 и i2 метода, i1 и i2 соответствуют тому, что было сказано в началеwelcome方法иCGLIB$welcome$0方法, код позади можно увидеть.

А f1 соответствуетInfoDemo?FastClassByCGLIB?2e560a7dКласс прокси, f2 соответствуетInfoDemo?EnhancerByCGLIB?efe38465?FastClassByCGLIB?38345933класс прокси. Их можно просмотреть в сгенерированном прокси-классе.

Конечно, здесь не сказано, что здесь генерируется, если вам интересно, вы можете посмотреть, как генерируется байт-код. Лично не понимаю.

Разница между вызовом и вызовомSuper

Почему должны генерироваться два прокси-класса f1 и f2?Я думаю, вы должны были обратить внимание на предыдущий метод.Выше мы упоминали метод invoke и метод invokeSuper. Давайте сравним разницу между методом invoke и методом invokeSuper.

public Object invoke(Object obj, Object[] args) throws Throwable {
  this.init();
  MethodProxy.FastClassInfo fci = this.fastClassInfo;
  return fci.f1.invoke(fci.i1, obj, args);
}

Мы видим, что invoke использует метод f1.invoke, а invokeSuper использует метод f2.invoke.

Сначала посмотрите на логику метода вызова, соответствующего f1.

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

        try {
            switch(var10001) {
            case 0:
                var10000.welcome((String)var3[0]);
                return null;
                ....
}

Вызовите метод приветствия объекта InfoDemo напрямую.

Таким образом, это также может объяснить, почему мы ранее вызывали метод invoke в цикле, потому что var2, которую мы передали, является прокси-объектом InfoDemo. Глядя на код прокси-класса в начале, мы видим, что он вернется к вызовите метод снова, что приведет к бесконечному циклу.

Давайте посмотрим на реализацию соответствующего вызова в f2.

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

        try {
            switch(var10001) {
             ....
                var10000.CGLIB$finalize$1();
                return null;
            case 16:
                var10000.CGLIB$welcome$0((String)var3[0]);
                return null;
           ....
    }

Поскольку var2, который мы передали в этот раз, является прокси-объектом InfoDemo, прокси-класс в конечном итоге будет вызыватьсяCGLIB$welcome$0метод.

резюме

Это просто неудачная попытка анализа исходного кода, но я разобрался с причиной вызова бесконечного цикла.Могу лишь сказать, что cglib гораздо сложнее, чем динамический прокси jdk, в основном отражается в логике сгенерированного кода и сгенерированного код, который требует дальнейшего изучения.

И есть два метода вызова, invoke и invokeSuper, поэтому будьте осторожны при их использовании.

Эпилог

Эта статья из личных заметок, если есть какие-то неуместные выражения или упущения, прошу меня поправить.

Поделиться с тобой! ! !