回顾:
上一篇代理模式二:Java动态代理介绍了JDK动态代理,使用Proxy.newProxyInstance生成代理类对象,使用InvocationHandler接口定义回调,但是存在一个局限性,JDK动态代理不能代理没有实现任何接口的类。CGLib可以。先看看CGLib怎么用的。
1、定义一个类,不实现任何接口,如下:
package aop.demo4;
public class GreetingImpl {
public void sayHello(String name) {
System.out.println("Hello! " + name);
}
}
假如现在有个需求,想要在sayHello方法前后做些事,比如打印日志。但是不改动GreetingImpl.java本身
2、定义拦截器,实现MethodInterceptor接口,重写拦截方法intercept
public class CGLibDynamicProxy implements MethodInterceptor
@Override
public Object intercept(Object target, Method method, Object[] args, MethodProxy proxy) throws Throwable {
before();
Object result = proxy.invokeSuper(target, args);
after();
return result;
}
3、调用Enhancer.create()方法创建代理对象
public T getProxy(Class cls) {
return (T) Enhancer.create(cls, this);
}
2、3两步的完整代码如下:
package aop.demo4;
import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;
import java.lang.reflect.Method;
public class CGLibDynamicProxy implements MethodInterceptor {
private static CGLibDynamicProxy instance = new CGLibDynamicProxy();
private CGLibDynamicProxy() {
}
public static CGLibDynamicProxy getInstance() {
return instance;
}
@SuppressWarnings("unchecked")
public T getProxy(Class cls) {
return (T) Enhancer.create(cls, this);
}
@Override
public Object intercept(Object target, Method method, Object[] args, MethodProxy proxy) throws Throwable {
before();
Object result = proxy.invokeSuper(target, args);
after();
return result;
}
private void before() {
System.out.println("Before");
}
private void after() {
System.out.println("After");
}
}
4、客户端调用方式
package aop.demo4;
import aop.Greeting;
/**
* 4. CGLib 动态代理
*/
public class Client {
public static void main(String[] args) {
GreetingImpl greeting = CGLibDynamicProxy.getInstance().getProxy(GreetingImpl.class);
greeting.sayHello("Jack");
}
}
5、执行结果
Before
Hello! Jack
After
下面从源码了解CGLib代理普通类(没有实现接口的类)的特性,看看它是怎么做到的,有没有可以值得我们工作中借鉴的地方。
1、 创建代理类对象。入口是Enhancer.create(cls, this)。
/**
* Helper method to create an intercepted object.
* For finer control over the generated instance, use a new instance of Enhancer
* instead of this static method.
* @param type class to extend or interface to implement
* @param callback the callback to use for all methods
*/
public static Object create(Class type, Callback callback) {
Enhancer e = new Enhancer();
e.setSuperclass(type);
e.setCallback(callback);
return e.create();
}
从以上代码大概知道,Enhancer这个类是我们通过CGLib创建代理类对象的关键类,它把我们的真实业务类设置到SuperClass属性中,即作为代理类的父类来用,还设置了我们实现的MethodInterceptor 接口的类作为回调方法来用。
通过阅读Enhancer类的注释了解到:
(1)、Enhancer根据我们传入的真实业务类产生子类
(2)、Enhancer生成的子类覆盖父类的非final方法
(3)、Enhancer生成的子类中有钩子方法,用于调用用户定义的拦截器实现
(4)、MethodInterceptor这种回调(拦截器)在AOP术语中就是环绕增强(around advice),可以在调用真实业务类方法的前后增强,或者修改参数。
(5)、MethodInterceptor这种回调方式,是比较重的,生成的代理类,会为真实业务类(委托类)每一个非final方法增强,试着调用hashCode()方法。
public class Client {
public static void main(String[] args) {
GreetingImpl greeting = CGLibDynamicProxy.getInstance().getProxy(GreetingImpl.class);
greeting.sayHello("Jack");
greeting.hashCode();
}
}
执行结果:
Before
Hello! Jack
After
Before
After
从输出发现hashCode也被增强了。
CGLib默认提供了一些其他回调方式:
private static final CallbackInfo[] CALLBACKS = {
new CallbackInfo(NoOp.class, NoOpGenerator.INSTANCE),
new CallbackInfo(MethodInterceptor.class, MethodInterceptorGenerator.INSTANCE),
new CallbackInfo(InvocationHandler.class, InvocationHandlerGenerator.INSTANCE),
new CallbackInfo(LazyLoader.class, LazyLoaderGenerator.INSTANCE),
new CallbackInfo(Dispatcher.class, DispatcherGenerator.INSTANCE),
new CallbackInfo(FixedValue.class, FixedValueGenerator.INSTANCE),
new CallbackInfo(ProxyRefDispatcher.class, DispatcherGenerator.PROXY_REF_INSTANCE),
};
LazyLoader可以提高性能,使用懒加载方式生成代理类;
还可以使用CallbackFilter选择要增强的方法。
先留个疑问,是否可以只增强指定方法?Spring AOP是怎么做到只拦截指定方法的?
2、字节码生成入口从AbstractClassGenerator.create方法开始
byte[] b = strategy.generate(this);
public byte[] generate(ClassGenerator cg) throws Exception {
ClassWriter cw = getClassWriter();
transform(cg).generateClass(cw);
return transform(cw.toByteArray());
}
transform(cg).generateClass(cw)会调用具体的生成字节码的实现类去生成字节码,比如我们的Enhancer中的generateClass方法。
回过头来看,Enhancer本身就是AbstractClassGenerator的子类
public class Enhancer extends AbstractClassGenerator
Enhancer中具体的生成字节码的过程有兴趣可以去看一下。
3、加载生成的代理类
String className = ClassNameReader.getClassName(new ClassReader(b));
getClassNameCache(loader).add(className);
gen = ReflectUtils.defineClass(className, b, loader);
4、在Enhancer中实例化代理类
private Object createUsingReflection(Class type) {
setThreadCallbacks(type, callbacks);
try{
if (argumentTypes != null) {
return ReflectUtils.newInstance(type, argumentTypes, arguments);
} else {
return ReflectUtils.newInstance(type);
}
}finally{
// clear thread callbacks to allow them to be gc'd
setThreadCallbacks(type, null);
}
}
为了更直观的看看生成的代理类内部的方法,反编译看一下:
修改Client.java,将代理类输出出来:
public class Client {
public static final String DEFAULT_DEBUG_LOACATION = System.getProperty("user.home") +
System.getProperty("file.separator") + "cglib-debug";
public static void main(String[] args) {
System.setProperty(DebuggingClassWriter.DEBUG_LOCATION_PROPERTY,DEFAULT_DEBUG_LOACATION);
GreetingImpl greeting = CGLibDynamicProxy.getInstance().getProxy(GreetingImpl.class);
greeting.sayHello("Jack");
}
}
在用户目录下找到生成的文件:
用反编译工具procyon 查看代理类实现
java -jar /e/procyon-decompiler-0.5.30.jar ./GreetingImpl\$\$EnhancerByCGLIB\$\$315deeb6.class > 1.java
反编译后的代理类:
//继承GreetingImpl,实现Factory
public class GreetingImpl$$EnhancerByCGLIB$$315deeb6 extends GreetingImpl 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$sayHello$0$Method;
private static final MethodProxy CGLIB$sayHello$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];
final Class> forName = Class.forName("aop.demo4.GreetingImpl$$EnhancerByCGLIB$$315deeb6");
final Class> forName2;
final Method[] methods = ReflectUtils.findMethods(new String[] { "finalize", "()V", "equals", "(Ljava/lang/Object;)Z", "toString", "()Ljava/lang/String;", "hashCode", "()I", "clone", "()Ljava/lang/Object;" }, (forName2 = Class.forName("java.lang.Object")).getDeclaredMethods());
CGLIB$finalize$1$Method = methods[0];
CGLIB$finalize$1$Proxy = MethodProxy.create((Class)forName2, (Class)forName, "()V", "finalize", "CGLIB$finalize$1");
CGLIB$equals$2$Method = methods[1];
CGLIB$equals$2$Proxy = MethodProxy.create((Class)forName2, (Class)forName, "(Ljava/lang/Object;)Z", "equals", "CGLIB$equals$2");
CGLIB$toString$3$Method = methods[2];
CGLIB$toString$3$Proxy = MethodProxy.create((Class)forName2, (Class)forName, "()Ljava/lang/String;", "toString", "CGLIB$toString$3");
CGLIB$hashCode$4$Method = methods[3];
CGLIB$hashCode$4$Proxy = MethodProxy.create((Class)forName2, (Class)forName, "()I", "hashCode", "CGLIB$hashCode$4");
CGLIB$clone$5$Method = methods[4];
CGLIB$clone$5$Proxy = MethodProxy.create((Class)forName2, (Class)forName, "()Ljava/lang/Object;", "clone", "CGLIB$clone$5");
final Class> forName3;
CGLIB$sayHello$0$Method = ReflectUtils.findMethods(new String[] { "sayHello", "(Ljava/lang/String;)V" }, (forName3 = Class.forName("aop.demo4.GreetingImpl")).getDeclaredMethods())[0];
CGLIB$sayHello$0$Proxy = MethodProxy.create((Class)forName3, (Class)forName, "(Ljava/lang/String;)V", "sayHello", "CGLIB$sayHello$0");
}
final void CGLIB$sayHello$0(final String s) {
super.sayHello(s);
}
public final void sayHello(final String s) {
MethodInterceptor cglib$CALLBACK_2;
MethodInterceptor cglib$CALLBACK_0;
if ((cglib$CALLBACK_0 = (cglib$CALLBACK_2 = this.CGLIB$CALLBACK_0)) == null) {
//greetingImpl$$EnhancerByCGLIB$$315deeb6.CGLIB$CALLBACK_0 = (MethodInterceptor)((Callback[])o2)[0];
//cglib$CALLBACK_2被赋值为拦截器
CGLIB$BIND_CALLBACKS(this);
cglib$CALLBACK_2 = (cglib$CALLBACK_0 = this.CGLIB$CALLBACK_0);
}
if (cglib$CALLBACK_0 != null) {
cglib$CALLBACK_2.intercept((Object)this, GreetingImpl$$EnhancerByCGLIB$$315deeb6.CGLIB$sayHello$0$Method, new Object[] { s }, GreetingImpl$$EnhancerByCGLIB$$315deeb6.CGLIB$sayHello$0$Proxy);
return;
}
super.sayHello(s);
}
//...省略代码
}
这个代理类继承了真实业务类GreetingImpl,也就是说是真实业务类的子类,并且实现了Factory接口。
默认所有代理类都会实现Factory接口,除非设置了Enhancer.setUseFactory(false)。
这个接口可以改变已经生成的代理对象的回调,也提供了newInstance方法来实例化代理类对象。
看一下调用栈,然后分析一些调用细节。
调用细节:
1、开始调用代理类的sayHello方法
greeting.sayHello("Jack");
public final void sayHello(final String s) {
MethodInterceptor cglib$CALLBACK_2;
MethodInterceptor cglib$CALLBACK_0;
if ((cglib$CALLBACK_0 = (cglib$CALLBACK_2 = this.CGLIB$CALLBACK_0)) == null) {
//greetingImpl$$EnhancerByCGLIB$$315deeb6.CGLIB$CALLBACK_0 = (MethodInterceptor)((Callback[])o2)[0];
//cglib$CALLBACK_2被赋值为拦截器
CGLIB$BIND_CALLBACKS(this);
cglib$CALLBACK_2 = (cglib$CALLBACK_0 = this.CGLIB$CALLBACK_0);
}
if (cglib$CALLBACK_0 != null) {
cglib$CALLBACK_2.intercept((Object)this, GreetingImpl$$EnhancerByCGLIB$$315deeb6.CGLIB$sayHello$0$Method, new Object[] { s }, GreetingImpl$$EnhancerByCGLIB$$315deeb6.CGLIB$sayHello$0$Proxy);
return;
}
super.sayHello(s);
}
上述代码,如果使用的是MethodInterceptor回调,就会调用实现了MethodInterceptor接口的拦截器的intercept方法。因为通过代理类的默认构造器CGLIB$BIND_CALLBACKS方法cglib$CALLBACK_2会被转换为拦截器的引用。
public GreetingImpl$$EnhancerByCGLIB$$315deeb6() {
CGLIB$BIND_CALLBACKS(this);
}
private static final void CGLIB$BIND_CALLBACKS(final Object o) {
final GreetingImpl$$EnhancerByCGLIB$$315deeb6 greetingImpl$$EnhancerByCGLIB$$315deeb6 = (GreetingImpl$$EnhancerByCGLIB$$315deeb6)o;
if (!greetingImpl$$EnhancerByCGLIB$$315deeb6.CGLIB$BOUND) {
greetingImpl$$EnhancerByCGLIB$$315deeb6.CGLIB$BOUND = true;
Object o2;
if ((o2 = GreetingImpl$$EnhancerByCGLIB$$315deeb6.CGLIB$THREAD_CALLBACKS.get()) != null || (o2 = GreetingImpl$$EnhancerByCGLIB$$315deeb6.CGLIB$STATIC_CALLBACKS) != null) {
greetingImpl$$EnhancerByCGLIB$$315deeb6.CGLIB$CALLBACK_0 = (MethodInterceptor)((Callback[])o2)[0];
}
}
}
2、上面的CGLibDynamicProxy就是实现了MethodInterceptor,再看下它的intercept方法:
public Object intercept(Object target, Method method, Object[] args, MethodProxy proxy) throws Throwable {
before();
Object result = proxy.invokeSuper(target, args);
after();
return result;
}
传入四个参数:
Object target:生成的代理类的对象引用
Method method:真实业务类的方法引用
Object[] args:参数的数组引用
MethodProxy proxy:代理方法的对象
3、调用MethodProxy的invokeSuper方法
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类,是为了在调用代理类内部方法的时候,不需要再通过反射,而是通过方法索引的方式调用对应的方法。
FastClassInfo的数据结构中维持了两个FastClass引用f1,f2和两个索引i1,i2:
private static class FastClassInfo
{
FastClass f1;
FastClass f2;
int i1;
int i2;
}
FastClass类的数据结构也很简单,type维持是一个要包装的对象的引用。
private Class type;
FastClass.getIndex方法内部是对方法签名取hashcode,然后返回索引位置
public int getIndex(final Signature signature) {
final String string = signature.toString();
switch (string.hashCode()) {
case -1725733088: {
if (string.equals("getClass()Ljava/lang/Class;")) {
return 7;
}
break;
}
case -1026001249: {
if (string.equals("wait(JI)V")) {
return 2;
}
break;
}
case 243996900: {
if (string.equals("wait(J)V")) {
return 3;
}
break;
}
case 771401912: {
if (string.equals("sayHello(Ljava/lang/String;)V")) {
return 0;
}
break;
}
//...省略代码
FastClass.invoke方法就是根据索引位置调用对应的方法。方法内容的细节继续看下面的4。
FastClassInfo类是在init()中初始化的。最终f1指向的是包装真实业务类(委托类)的FastClass子类,f2指向的是包装代理类的FastClass子类,i1指向代理类中sayHello方法的位置,i2指向CGLIB$sayHello$0方法在代理类中的位置。
4、调用包装了代理类的FastClass子类的invoke方法,根据索引找到要调用的代理类方法
public Object invoke(final int n, final Object o, final Object[] array) throws InvocationTargetException {
final GreetingImpl$$EnhancerByCGLIB$$315deeb6 greetingImpl$$EnhancerByCGLIB$$315deeb6 = (GreetingImpl$$EnhancerByCGLIB$$315deeb6)o;
try {
switch (n) {
case 0: {
return new Boolean(greetingImpl$$EnhancerByCGLIB$$315deeb6.equals(array[0]));
}
case 1: {
return greetingImpl$$EnhancerByCGLIB$$315deeb6.toString();
}
//...省略代码
case 8: {
greetingImpl$$EnhancerByCGLIB$$315deeb6.sayHello((String)array[0]);
return null;
}
//...省略代码
case 14: {
greetingImpl$$EnhancerByCGLIB$$315deeb6.CGLIB$sayHello$0((String)array[0]);
return null;
}
5、调用代理类中的CGLIB$sayHello$0方法,这个方法直接调用真实业务类的方法
final void CGLIB$sayHello$0(final String s) {
super.sayHello(s);
}
6、调用真实业务类方法
7、调用完真实业务类方法后,一步步出栈,回到我们实现的拦截器方法intercept,然后调用after方法,最后返回
@Override
public Object intercept(Object target, Method method, Object[] args, MethodProxy proxy) throws Throwable {
before();
Object result = proxy.invokeSuper(target, args);
after();
return result;
}
这里有个疑问,为真实业务类生成的FastClass好像没有用上
细看CGLib生成的为真实业务类生成的FastClass,
public Object invoke(final int n, final Object o, final Object[] array) throws InvocationTargetException {
final GreetingImpl greetingImpl = (GreetingImpl)o;
try {
switch (n) {
case 0: {
greetingImpl.sayHello((String)array[0]);
return null;
}
//...省略代码
case 7: {
return greetingImpl.getClass();
}
}
}
//...省略代码
发现其实可以继承MethodProxy,自己实现invokeSuper,内部就直接调用CGLib生成的为真实业务类生成的FastClass
fci.f1.invoke(fci.i1, obj, args);
这样可以更快的调用真实业务类的方法。
可是为什么CGLib没有这样做呢?这个疑问有待思考
反编译工具:https://pan.baidu.com/s/1XD30RinqKxTy5uH104Q7Yg