cglib代理使用ASM对字节码进行操作生成新的类,从而实现对对象方法的增强。我们都知道Java中自带了一个动态代理,那我们为什么不直接使用Java动态代理,而要使用cglib呢?因为cglib相比于JDK动态代理更加强大,JDK动态代理虽然简单易用,但是其有一个致命缺陷是,只能对接口进行代理。如果要代理的类为一个普通类、没有实现接口,那么Java动态代理就没法使用了。
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());
}
}
参考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实现了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");
}
}
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");
}
}
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
细心的你肯定发现了上面例子中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'
}
可以通过设置属性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();
}
}