当前位置: 首页 > 工具软件 > CGLib > 使用案例 >

【spring】Cglib动态代理的使用

董高逸
2023-12-01

cglib代理使用ASM对字节码进行操作生成新的类,从而实现对对象方法的增强。我们都知道Java中自带了一个动态代理,那我们为什么不直接使用Java动态代理,而要使用cglib呢?因为cglib相比于JDK动态代理更加强大,JDK动态代理虽然简单易用,但是其有一个致命缺陷是,只能对接口进行代理。如果要代理的类为一个普通类、没有实现接口,那么Java动态代理就没法使用了。

cglib的使用

CallbackFilter

CglibCallbackFilter用来判断方法走那个Callback。

package com.morris.spring.demo.proxy.cglib;

import org.springframework.cglib.proxy.Callback;
import org.springframework.cglib.proxy.CallbackFilter;

import java.lang.reflect.Method;

/**
 * CglibCallbackFilter用来判断方法走那个Callback
 */
public class CglibCallbackFilter implements CallbackFilter {
	private final Callback[] callbacks;

	public CglibCallbackFilter(Callback[] callbacks) {
		this.callbacks = callbacks;
		Class<?>[] callbackTypes = new Class<?>[callbacks.length];
		for (int i = 0; i < callbacks.length; i++) {
			callbackTypes[i] = callbacks[i].getClass();
		}
	}

	@Override
	public int accept(Method method) {
		for (int i = 0; i < this.callbacks.length; i++) {
			Callback callback = this.callbacks[i];
			if (!(callback instanceof ConditionalCallback) || ((ConditionalCallback) callback).isMatch(method)) {
				return i;
			}
		}
		throw new IllegalStateException("No callback available for method " + method.getName());
	}
}

ConditionalCallback

参考spring中的ConditionalCallback。

package com.morris.spring.demo.proxy.cglib;

import org.springframework.cglib.proxy.Callback;

import java.lang.reflect.Method;

public interface ConditionalCallback extends Callback {
	boolean isMatch(Method candidateMethod);
}

InsertMethodInterceptor

InsertMethodInterceptor实现了MethodInterceptor接口,用来实现对方法的增强。

Insert开头的方法记录参数。

package com.morris.spring.demo.proxy.cglib;

import org.springframework.cglib.proxy.MethodInterceptor;
import org.springframework.cglib.proxy.MethodProxy;
import org.springframework.lang.Nullable;

import java.lang.reflect.Method;

/**
 * Insert开头的方法记录参数
 */
public class InsertMethodInterceptor implements MethodInterceptor, ConditionalCallback {

	@Override
	@Nullable
	public Object intercept(Object enhancedConfigInstance, Method beanMethod, Object[] beanMethodArgs,
							MethodProxy cglibMethodProxy) throws Throwable {
		System.out.println("InsertMethodInterceptor " + beanMethod.getName() + " args: " + beanMethodArgs);
		Object result = cglibMethodProxy.invokeSuper(enhancedConfigInstance, beanMethodArgs);
		return result;
	}

	@Override
	public boolean isMatch(Method method) {
		return method.getName().startsWith("insert");
	}
}

QueryMethodInterceptor

Query开头的方法记录返回值。

package com.morris.spring.demo.proxy.cglib;

import org.springframework.cglib.proxy.MethodInterceptor;
import org.springframework.cglib.proxy.MethodProxy;
import org.springframework.lang.Nullable;

import java.lang.reflect.Method;

/**
 * Query开头的方法记录返回值
 */
public class QueryMethodInterceptor implements MethodInterceptor, ConditionalCallback {

	@Override
	@Nullable
	public Object intercept(Object enhancedConfigInstance, Method beanMethod, Object[] beanMethodArgs,
							MethodProxy cglibMethodProxy) throws Throwable {
		Object result = cglibMethodProxy.invokeSuper(enhancedConfigInstance, beanMethodArgs);
		System.out.println("QueryMethodInterceptor " + beanMethod.getName() + " result: " + result);
		return result;
	}

	@Override
	public boolean isMatch(Method method) {
		return method.getName().startsWith("query");
	}
}

CglibProxyDemo

package com.morris.spring.demo.proxy.cglib;

import com.morris.spring.service.UserService;
import com.morris.spring.service.UserServiceImpl;
import org.springframework.cglib.proxy.Callback;
import org.springframework.cglib.proxy.Enhancer;
import org.springframework.cglib.proxy.NoOp;

/**
 * Cglib的使用
 */
public class CglibProxyDemo {
	public static void main(String[] args) {
		Enhancer enhancer = new Enhancer();
		enhancer.setSuperclass(UserServiceImpl.class);
		Callback[] callback = new Callback[]{
				new InsertMethodInterceptor(),
				new QueryMethodInterceptor(),
				NoOp.INSTANCE
		};
		CglibCallbackFilter cglibCallbackFilter = new CglibCallbackFilter(callback);
		enhancer.setCallbackFilter(cglibCallbackFilter);
		enhancer.setCallbacks(callback);

		UserService o = (UserService) enhancer.create();
		o.query("hello");
		o.insert("hi");
	}
}

运行结果如下:

UserServiceImpl query hello
QueryMethodInterceptor query result: hello hello
InsertMethodInterceptor insert args: [Ljava.lang.Object;@18e5cde
UserServiceImpl insert hi

spring中的cglib与net.sf.cglib的区别

细心的你肯定发现了上面例子中cglib的API类都是位于org.springframework.cglib下,而不是net.sf.cglib,正常来说,使用cglib应该在pom.xml中引入下面的坐标:

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

这是为什么呢?

这种方式这称为重新打包,项目不是使用某个库作为依赖项,而是将依赖项的副本作为其自己项目的一部分,并将其放在不同的包中。这样做的原因是使用Spring的项目可能想要使用cglib本身。如果Spring将特定版本的cglib作为依赖项,那么使用Spring的项目就不可能选择不同的版本。但是如果Spring使用不同包中的重新打包的cglib,则没有版本冲突,如果他们喜欢,项目可以使用任何版本的cglib。

我们可以在源码spring-core.gradle中发现重新打包的脚本:

task cglibRepackJar(type: ShadowJar) {
	baseName = 'spring-cglib-repack'
	version = cglibVersion
	configurations = [project.configurations.cglib]
	relocate 'net.sf.cglib', 'org.springframework.cglib'
	relocate 'org.objectweb.asm', 'org.springframework.asm'
}

task objenesisRepackJar(type: ShadowJar) {
	baseName = 'spring-objenesis-repack'
	version = objenesisVersion
	configurations = [project.configurations.objenesis]
	relocate 'org.objenesis', 'org.springframework.objenesis'
}

查看cglib动态生成的类

可以通过设置属性System.setProperty(DebuggingClassWriter.DEBUG_LOCATION_PROPERTY, "target/cglib")来查看cglib动态生成的类,当然也可以使用arthas工具。

cglib生成了多个类,其中最重要的是UserServiceImpl E n h a n c e r B y C G L I B EnhancerByCGLIB EnhancerByCGLIBc09a6558,他会继承目标类UserServiceImpl。

package com.morris.spring.service;

import java.lang.reflect.Method;
import org.springframework.cglib.core.ReflectUtils;
import org.springframework.cglib.core.Signature;
import org.springframework.cglib.proxy.Callback;
import org.springframework.cglib.proxy.Factory;
import org.springframework.cglib.proxy.MethodInterceptor;
import org.springframework.cglib.proxy.MethodProxy;
import org.springframework.cglib.proxy.NoOp;

public class UserServiceImpl$$EnhancerByCGLIB$$c09a6558 extends UserServiceImpl implements Factory {
    private boolean CGLIB$BOUND;
    public static Object CGLIB$FACTORY_DATA;
    private static final ThreadLocal CGLIB$THREAD_CALLBACKS;
    private static final Callback[] CGLIB$STATIC_CALLBACKS;
    private MethodInterceptor CGLIB$CALLBACK_0;
    private MethodInterceptor CGLIB$CALLBACK_1;
    private NoOp CGLIB$CALLBACK_2;
    private static Object CGLIB$CALLBACK_FILTER;
    private static final Method CGLIB$insert$0$Method;
    private static final MethodProxy CGLIB$insert$0$Proxy;
    private static final Object[] CGLIB$emptyArgs;
    private static final Method CGLIB$query$1$Method;
    private static final MethodProxy CGLIB$query$1$Proxy;

    static void CGLIB$STATICHOOK1() {
        CGLIB$THREAD_CALLBACKS = new ThreadLocal();
        CGLIB$emptyArgs = new Object[0];
        Class var0 = Class.forName("com.morris.spring.service.UserServiceImpl$$EnhancerByCGLIB$$c09a6558");
        Class var1;
        Method[] var10000 = ReflectUtils.findMethods(new String[]{"insert", "(Ljava/lang/String;)V", "query", "(Ljava/lang/String;)Ljava/lang/String;"}, (var1 = Class.forName("com.morris.spring.service.UserServiceImpl")).getDeclaredMethods());
        CGLIB$insert$0$Method = var10000[0];
        CGLIB$insert$0$Proxy = MethodProxy.create(var1, var0, "(Ljava/lang/String;)V", "insert", "CGLIB$insert$0");
        CGLIB$query$1$Method = var10000[1];
        CGLIB$query$1$Proxy = MethodProxy.create(var1, var0, "(Ljava/lang/String;)Ljava/lang/String;", "query", "CGLIB$query$1");
    }

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

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

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

    final String CGLIB$query$1(String var1) {
        return super.query(var1);
    }

    public final String query(String var1) {
        MethodInterceptor var10000 = this.CGLIB$CALLBACK_1;
        if (var10000 == null) {
            CGLIB$BIND_CALLBACKS(this);
            var10000 = this.CGLIB$CALLBACK_1;
        }

        return var10000 != null ? (String)var10000.intercept(this, CGLIB$query$1$Method, new Object[]{var1}, CGLIB$query$1$Proxy) : super.query(var1);
    }

    public static MethodProxy CGLIB$findMethodProxy(Signature var0) {
        String var10000 = var0.toString();
        switch(var10000.hashCode()) {
        case -997399735:
            if (var10000.equals("query(Ljava/lang/String;)Ljava/lang/String;")) {
                return CGLIB$query$1$Proxy;
            }
            break;
        case -982250266:
            if (var10000.equals("insert(Ljava/lang/String;)V")) {
                return CGLIB$insert$0$Proxy;
            }
        }

        return null;
    }

    public UserServiceImpl$$EnhancerByCGLIB$$c09a6558() {
        CGLIB$BIND_CALLBACKS(this);
    }

    public static void CGLIB$SET_THREAD_CALLBACKS(Callback[] var0) {
        CGLIB$THREAD_CALLBACKS.set(var0);
    }

    public static void CGLIB$SET_STATIC_CALLBACKS(Callback[] var0) {
        CGLIB$STATIC_CALLBACKS = var0;
    }

    private static final void CGLIB$BIND_CALLBACKS(Object var0) {
        UserServiceImpl$$EnhancerByCGLIB$$c09a6558 var1 = (UserServiceImpl$$EnhancerByCGLIB$$c09a6558)var0;
        if (!var1.CGLIB$BOUND) {
            var1.CGLIB$BOUND = true;
            Object var10000 = CGLIB$THREAD_CALLBACKS.get();
            if (var10000 == null) {
                var10000 = CGLIB$STATIC_CALLBACKS;
                if (var10000 == null) {
                    return;
                }
            }

            Callback[] var10001 = (Callback[])var10000;
            var1.CGLIB$CALLBACK_2 = (NoOp)((Callback[])var10000)[2];
            var1.CGLIB$CALLBACK_1 = (MethodInterceptor)var10001[1];
            var1.CGLIB$CALLBACK_0 = (MethodInterceptor)var10001[0];
        }

    }

    public Object newInstance(Callback[] var1) {
        CGLIB$SET_THREAD_CALLBACKS(var1);
        UserServiceImpl$$EnhancerByCGLIB$$c09a6558 var10000 = new UserServiceImpl$$EnhancerByCGLIB$$c09a6558();
        CGLIB$SET_THREAD_CALLBACKS((Callback[])null);
        return var10000;
    }

    public Object newInstance(Callback var1) {
        throw new IllegalStateException("More than one callback object required");
    }

    public Object newInstance(Class[] var1, Object[] var2, Callback[] var3) {
        CGLIB$SET_THREAD_CALLBACKS(var3);
        UserServiceImpl$$EnhancerByCGLIB$$c09a6558 var10000 = new UserServiceImpl$$EnhancerByCGLIB$$c09a6558;
        switch(var1.length) {
        case 0:
            var10000.<init>();
            CGLIB$SET_THREAD_CALLBACKS((Callback[])null);
            return var10000;
        default:
            throw new IllegalArgumentException("Constructor not found");
        }
    }

    public Callback getCallback(int var1) {
        CGLIB$BIND_CALLBACKS(this);
        Object var10000;
        switch(var1) {
        case 0:
            var10000 = this.CGLIB$CALLBACK_0;
            break;
        case 1:
            var10000 = this.CGLIB$CALLBACK_1;
            break;
        case 2:
            var10000 = this.CGLIB$CALLBACK_2;
            break;
        default:
            var10000 = null;
        }

        return (Callback)var10000;
    }

    public void setCallback(int var1, Callback var2) {
        switch(var1) {
        case 0:
            this.CGLIB$CALLBACK_0 = (MethodInterceptor)var2;
            break;
        case 1:
            this.CGLIB$CALLBACK_1 = (MethodInterceptor)var2;
            break;
        case 2:
            this.CGLIB$CALLBACK_2 = (NoOp)var2;
        }

    }

    public Callback[] getCallbacks() {
        CGLIB$BIND_CALLBACKS(this);
        return new Callback[]{this.CGLIB$CALLBACK_0, this.CGLIB$CALLBACK_1, this.CGLIB$CALLBACK_2};
    }

    public void setCallbacks(Callback[] var1) {
        this.CGLIB$CALLBACK_0 = (MethodInterceptor)var1[0];
        this.CGLIB$CALLBACK_1 = (MethodInterceptor)var1[1];
        this.CGLIB$CALLBACK_2 = (NoOp)var1[2];
    }

    static {
        CGLIB$STATICHOOK1();
    }
}
 类似资料: