不改变源码的基础上,对已有的方法增强。(是AOP思想的实现技术)
要求: 被代理类最少实现一个接口
提供者: JDK官方
涉及的类: Proxy
创建代理对象的方法: newProxyInstance(ClassLoader, Class[], InvocationHandler)
参数的含义:
ClassLoader: 类加载器,和被代理对象使用相同的类加载器。代理对象.getClass().getClassLoader();
Class[]: 字节码数组.被代理类实现的接口。(要求代理代理对象和被代理对象实现相同接口)。代理对象.getClass().getInterfaces();
InvocationHandler: 是一个接口,就是用于我们提供增强代码的,我们一般都是写一个该接口的实现类实现类可以是匿名内部类,也可以不是。他的含义是:如何代理。具体的做法,由用户自己实现。
该参数使用了策略模式:使用要求 :
数据已经存在;目的明确;达成目标的过程就是策略
IActor actor = new Actor();
IActor actor = (ICustomerDao)Proxy.newProxyInstance(actor .getClass().getClassLoader(),
actor .getClass().getInterfaces(), new InvocationHandler() {
//执行被代理对象的任何方法都会经过此方法;
//该方法有拦截的功能
//方法参数:
// Object o:代理对象的引用,不怎么用
// Method method:当前执行的方法
// Object[] objects:当前执行方法所需的参数
//返回值:当前执行方法的返回值
@Override
public Object invoke(Object o, Method method, Object[] objects) throws Throwable {
return null;
}
});
InvocationHandler的作用:
动态代理工作的基本模式就是将自己的方法功能的实现交给 InvocationHandler角色,外界对Proxy角色中的每一个方法的调用,Proxy角色都会交给InvocationHandler来处理,而InvocationHandler则调用具体对象角色的方法。如下图所示:
在这种模式之中: 代理Proxy 和代理类应该实现相同的功能,这一点相当重要。
在面向对象的编程之中,如果我们想要约定Proxy 和RealSubject可以实现相同的功能,有两种方式:
1. 一个比较直观的方式,就是定义一个功能接口,然后让Proxy 和代理类来实现这个接口。
2. 还有比较隐晦的方式,就是通过继承。因为如果Proxy 继承自代理类,这样Proxy则拥有了RealSubject的功能,
Proxy还可以通过重写RealSubject中的方法,来实现多态。
其中JDK中提供的创建动态代理的机制,是以a 这种思路设计的,而cglib 则是以b思路设计的。
JDK的动态代理创建机制----通过接口
1.获取 代理类上的所有接口列表;
2. 确定要生成的代理类的类名,默认为:com.sun.proxy.$ProxyXXXX ;
3. 根据需要实现的接口信息,在代码中动态创建 该Proxy类的字节码;
4 . 将对应的字节码转换为对应的class 对象;
5. 创建InvocationHandler 实例handler,用来处理Proxy所有方法调用;
6. Proxy 的class对象 以创建的handler对象为参数,实例化一个proxy对象
JDK通过 java.lang.reflect.Proxy包来支持动态代理,一般情况下,我们使用下面的newProxyInstance方法:
//返回一个指定接口的代理类实例,该接口可以将方法调用指派到指定的调用处理程序。
IActor actorPro = (IActor)Proxy.newProxyInstance(actor.getClass().getClassLoader(),
actor.getClass()).getInterfaces(), new InvocationHandler() {
@Override
public Object invoke(Object o, Method method, Object[] objects) throws Throwable {
re = method.invoke(;)
return null;
}
});
如果把最后的return 改为 return method(proxy, args);
invoke的对象不是actor ,而是proxy,根据上面的说明猜猜会发生什么?
是的,会不停地循环调用。因为proxy是代理类的对象,当该对象方法被调用的时候,会触发InvocationHandler,而InvocationHandler里面又调用一次proxy里面的对象,所以会不停地循环调用。并且,proxy对应的方法是没有实现的。所以是会循环的不停报错
在newProxyInstance()发放中有这样几段
其实大概就是把接口复制出来,通过这些接口和类加载器,拿到这个代理类cl。然后通过反射的技术复制拿到代理类的构造函数(这部分代码在Class类中的getConstructor0方法),最后通过这个构造函数new个一对象出来,同时用InvocationHandler绑定这个对象。
而对于InvocationHandler,我们需要实现下列的invoke方法:
在调用代理对象中的每一个方法时,在代码内部,都是直接调用了InvocationHandler 的invoke方法,而invoke方法根据代理类传递给自己的method参数来区分是什么方法。
生成动态代理类的字节码并且保存到硬盘中:
System.out.println(proxy.getClass());
输出结果:class com.sun.proxy.$Proxy0
JDK提供了sun.misc.ProxyGenerator.generateProxyClass(String proxyName,class[] interfaces) 底层方法来产生动态代理类的字节码:
/*
* 将根据类信息 动态生成的二进制字节码保存到硬盘中,
* 默认的是clazz目录下
* params :clazz 需要生成动态代理类的类
* proxyName : 为动态生成的代理类的名称
*/
public static void generateClassFile(Class clazz,String proxyName)
{
//根据类信息和提供的代理类名称,生成字节码
byte[] classFile = ProxyGenerator.generateProxyClass(proxyName, clazz.getInterfaces());
String paths = clazz.getResource(".").getPath();
System.out.println(paths);
FileOutputStream out = null;
try {
//保留到硬盘中,根目录+文件名
out = new FileOutputStream(paths+proxyName+".class");
out.write(classFile);
out.flush();
} catch (Exception e) {
e.printStackTrace();
} finally {
try {
out.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
这样将在CustomerDaoImp.class 同级目录下产生Proxy.class文件
仔细观察可以看出生成的动态代理类有以下特点:
1.继承自 java.lang.reflect.Proxy,实现了 代理类实现的接口;
2.类中的所有方法都是final 的;
3.所有的方法功能的实现都统一调用了InvocationHandler的invoke()方法。
生成代理对象的方法:
在生成代理对象前执行这个方法:
System.getProperties().put("sun.misc.ProxyGenerator.saveGeneratedFiles", "true");
在class文件的同级目录下会生成com.sun.proxy.$Proxy0文件(.class)
生的代理类源文件:
package com.sun.proxy;
import com.aiun.core.IActor;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.lang.reflect.UndeclaredThrowableException;
public final class $Proxy0 extends Proxy implements IActor {
private static Method m1;
private static Method m2;
private static Method m4;
private static Method m3;
private static Method m0;
public $Proxy0(InvocationHandler var1) throws {
super(var1);
}
public final boolean equals(Object var1) throws {
try {
return (Boolean)super.h.invoke(this, m1, new Object[]{var1});
} catch (RuntimeException | Error var3) {
throw var3;
} catch (Throwable var4) {
throw new UndeclaredThrowableException(var4);
}
}
public final String toString() throws {
try {
return (String)super.h.invoke(this, m2, (Object[])null);
} catch (RuntimeException | Error var2) {
throw var2;
} catch (Throwable var3) {
throw new UndeclaredThrowableException(var3);
}
}
public final void actionComplex(int var1) throws {
try {
super.h.invoke(this, m4, new Object[]{var1});
} catch (RuntimeException | Error var3) {
throw var3;
} catch (Throwable var4) {
throw new UndeclaredThrowableException(var4);
}
}
public final Integer actionSimple(int var1) throws {
try {
return (Integer)super.h.invoke(this, m3, new Object[]{var1});
} catch (RuntimeException | Error var3) {
throw var3;
} catch (Throwable var4) {
throw new UndeclaredThrowableException(var4);
}
}
public final int hashCode() throws {
try {
return (Integer)super.h.invoke(this, m0, (Object[])null);
} catch (RuntimeException | Error var2) {
throw var2;
} catch (Throwable var3) {
throw new UndeclaredThrowableException(var3);
}
}
static {
try {
m1 = Class.forName("java.lang.Object").getMethod("equals", Class.forName("java.lang.Object"));
m2 = Class.forName("java.lang.Object").getMethod("toString");
m4 = Class.forName("com.aiun.core.IActor").getMethod("actionComplex", Integer.TYPE);
m3 = Class.forName("com.aiun.core.IActor").getMethod("actionSimple", Integer.TYPE);
m0 = Class.forName("java.lang.Object").getMethod("hashCode");
} catch (NoSuchMethodException var2) {
throw new NoSuchMethodError(var2.getMessage());
} catch (ClassNotFoundException var3) {
throw new NoClassDefFoundError(var3.getMessage());
}
}
}
System.out.println(dao.getClass().getName()); 输出的结果为:com.sun.proxy.$Proxy0
动态代理的缺点: JDK动态代理有一个限制,即他只能为接口创建代理实例。
CGLIB采用底层的字节码技术,可以为一个类创建子类,在子类中采用方法拦截的技术,拦截所有父类方法的调用并顺势织入横切逻辑。
要求: 被代理类不能是最终类。被代理类不能被final修饰。因为被final修饰的类不能被继承。
提供者: 第三方CGLIB
涉及的类: Enhancer
创建代理对象的方法: creat(Class, Callback);
参数的含义:
Class: 被代理对象的字节码
Callback: 如何代理。他和InvocationHandler的作用是一样的。他也是一个接口。我们一般使用该接口的子接口MethodInterceptor.
在使用时创建该接口的匿名内部类。
Actor actor = new Actor();
Enhancer.create(actor.getClass(), new MethodInterceptor() {
//和JDK动态代理的invoke方法作用是一样的
//方法参数:
// methodProxy:当前执行方法的代理对象
public Object intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
return null;
}
});
采用CGLIB技术编一个可以为可以为任何类创建织入的代理对象的代理创建器:
public class CglibProxy implements MethodInterceptor {
private Enhancer enhancer = new Enhancer();
public Object getProxy(Class klass) {
enhancer.setSuperclass(klass); //设置需要创建的子类
enhancer.setCallback(this);
return enhancer.create(); //通过字节码技术动态的创建子类实例
}
//拦截父类所有方法的调用
@Override
public Object intercept(Object obj, Method method, Object[] args,
MethodProxy methodProxy) throws Throwable {
System.out.println("前置增强");
Object result = methodProxy.invokeSuper(obj, args);
System.out.println("后置增强");
return result;
}
}
用户可以通过getProxy(Class klass);方法为一个类创建动态代理对象,该代理对象通过扩展klass实现代理。intercept(Object obj, Method method, Object[] args, MethodProxy methodProxy);是CGLIB定义的Interceptor接口方法,他拦截所有目标类方法的调用。obj表示目标了的实例;method为目标类方法的反射对象;args为方法的动态入参;proxy为代理类实例。
下面通过CglibProxy为Actor类创建代理对象,并测试代理对象的方法:
public static void main(String[] args) {
CglibProxy proxy = new CglibProxy();
//通过动态生成子类的方式创建代理类
IActor actor = (IActor)proxy.getProxy(Actor.class);
actor.actionSimple(12);
}
通过CglibProxy为actor动态创建了一个织入性能的代理对象,并调用代理类的业务方法,输出结果:
前置增强
简单12
后置增强
代理类的名字变为:
com.aiun.core.Actor$$EnhancerByCGLIB$$d1978608@108c4c35;
这个特殊的类就是CGLIB为actor动态创建的子类。
缺点: 不能对目标类中的final或private方法进行代理
研究表明:CGLIB所创建的动态代理对象的性能依旧比JDK所创建的动态代理对象的性能高不少(大概10倍)。但CGLIB在创建代理对象时所花费的时间却比JDK动态代理多(大概8倍)。对于singleton的代理对象或者具有实例池的代理,因为无需频繁的创建代理对象,所以比较适合采用CGLIB动态代理技术,反之则适合采用JDK动态代理技术。
动态代理的好处我们从例子就能看出来,它比较灵活,可以在运行的时候才切入改变类的方法,而不需要预先定义它。
这种场景的使用是动态代理最佳的落地点,可以非常灵活地在某个类,某个方法,某个代码点上切入我们想要的内容,就是动态代理其中的内容。