在工作之余看一些优秀源码的时候发现很多地方使用了动态代理,所以抽了一些时间对java的动态代理深入熟悉一下,这篇文章记录我学习java动态代理的一些足迹,本篇文章的主要内容如下:
jdk动态代理
cglib动态代理
jdk动态代理
首先我们通过一个例子来回顾一下如何实现一个jdk动态代理
1.定义一个业务接口,并且实现它
public interface IFunction {
int function(int a, int b);
void print();
}
public class FunctionImpl implements IFunction {
@Override
public int function(int a, int b) {
return a + b;
}
@Override
public void print() {
System.out.println("FunctionImpl print!");
}
}
2.通过实现 java.lang.reflect.InvocationHandler 来完成一个代理类, 并且使用java.lang.reflect.Proxy 的newProxyInstance方法来获取代理对象
public class FunctionProxy implements InvocationHandler{
private IFunction target;
public FunctionProxy(IFunction function) {
this.target = function;
}
//获取代理对象
public Object getProxy() {
return Proxy.newProxyInstance(Thread.currentThread()
.getContextClassLoader(), target.getClass().getInterfaces(), this);
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
before();
Object obj = method.invoke(target,args); //注意点1
after();
return obj;
}
private void before(){
System.out.println("before!");
}
private void after(){
System.out.println("after!");
}
}
3.使用jdk动态代理
public class JdkProxyEntrance {
public static void main(String[] args) {
IFunction function = new FunctionImpl();
FunctionProxy proxy1 = new FunctionProxy(function);
IFunction obj = (IFunction)getProxy(function, proxy1);
int result = obj.function(3,2);
System.out.println(result);
}
}
通过上面的例子我们已经能够跑起来一个jdk动态代理,接下来就要探寻一下其中的原理,那么我们的入口毫无疑问,就是Proxy 的 newProxyInstance 方法,我们很好奇它是怎么动态生成一个代理对象的。
public static Object newProxyInstance(ClassLoader loader,
Class>[] interfaces,
InvocationHandler h)
进入上面的那个方法,其核心实现如下:
/*
* Look up or generate the designated proxy class.
*/
//①
Class> cl = getProxyClass0(loader, interfaces);
//②
final Constructor> cons = cl.getConstructor(constructorParams);
return cons.newInstance(new Object[] {h} );
代码简析:
①先获取代理类的 Class 对象
②通过反射创建一个此Class的实例
对于反射获取实例这个不用说,点开我们不熟悉的getProxyClass0方法,其核心代码如下:
//proxyName:系统设定的代理类名字,格式 "com.sun.proxy.$Proxy1"
//interfaces:被代理类实现的所有接口
byte[] proxyClassFile = ProxyGenerator.generateProxyClass( proxyName, interfaces);
Class> proxyClass = defineClass0(loader, proxyName,proxyClassFile, 0, proxyClassFile.length);
代码简析:
①根据给定参数,使用字节码技术生成动态代理类的字节码(这其中还是挺复杂的)
②调用本地方法 defineClass0 生成Class对象
到了这儿读者都明白了jdk动态代理的大概流程是通过字节码技术动态生成了一个动态代理类的Class对象,然后利用反射创建一个代理类的实例给用户使用。这时候我们就想要是能看看这个代理类的字节码,也就是这个代理类到底长什么样就更好了。
运行如下代码来获得动态代理类的 .class文件
public static void generaterClassFile(Class[] interfaces){
String proxyName = "com.sun.proxy.$Proxy0";
byte[] proxyClassFile = ProxyGenerator.generateProxyClass(
proxyName, interfaces);
try {
FileOutputStream var1 = new FileOutputStream(proxyName + ".class");
var1.write(proxyClassFile);
var1.close();
} catch (IOException var2) {
throw new InternalError("I/O exception saving generated file: " + var2);
}
}
ps:也可以通过设置如下的代码来自动保留生成的字节码,只是我在试验的报了FileNotFoundException错误 ,后面我会找找原因,如果有热心的读者给我指点出来我也很高兴。
System.setProperty("sun.misc.ProxyGenerator.saveGeneratedFiles", "true");
细心的你会发现上面 generaterClassFile 方法中生成字节码的方式其实就是和jdk动态代理生成字节码的方式一样,使用 ProxyGenerator.generateProxyClass 方法,生成的 .class文件使用java 反编译工具jd 反编译后的代码如下:
package com.sun.proxy;
import com.zcd.learn.designpattern.proxy.IFunction;
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 IFunction
{
private static Method m1;
private static Method m4;
private static Method m0;
private static Method m3;
private static Method m2;
public $Proxy0(InvocationHandler paramInvocationHandler)
throws
{
super(paramInvocationHandler);
}
public final boolean equals(Object paramObject)
throws
{
try
{
return ((Boolean)this.h.invoke(this, m1, new Object[] { paramObject })).booleanValue();
}
catch (Error|RuntimeException localError)
{
throw localError;
}
catch (Throwable localThrowable)
{
throw new UndeclaredThrowableException(localThrowable);
}
}
public final int function(int paramInt1, int paramInt2)
throws
{
try
{
return ((Integer)this.h.invoke(this, m4, new Object[] { Integer.valueOf(paramInt1), Integer.valueOf(paramInt2) })).intValue();
}
catch (Error|RuntimeException localError)
{
throw localError;
}
catch (Throwable localThrowable)
{
throw new UndeclaredThrowableException(localThrowable);
}
}
public final int hashCode()
throws
{
try
{
return ((Integer)this.h.invoke(this, m0, null)).intValue();
}
catch (Error|RuntimeException localError)
{
throw localError;
}
catch (Throwable localThrowable)
{
throw new UndeclaredThrowableException(localThrowable);
}
}
public final void print()
throws
{
try
{
this.h.invoke(this, m3, null);
return;
}
catch (Error|RuntimeException localError)
{
throw localError;
}
catch (Throwable localThrowable)
{
throw new UndeclaredThrowableException(localThrowable);
}
}
public final String toString()
throws
{
try
{
return (String)this.h.invoke(this, m2, null);
}
catch (Error|RuntimeException localError)
{
throw localError;
}
catch (Throwable localThrowable)
{
throw new UndeclaredThrowableException(localThrowable);
}
}
static
{
try
{
m1 = Class.forName("java.lang.Object").getMethod("equals", new Class[] { Class.forName("java.lang.Object") });
m4 = Class.forName("com.zcd.learn.designpattern.proxy.IFunction").getMethod("function", new Class[] { Integer.TYPE, Integer.TYPE });
m0 = Class.forName("java.lang.Object").getMethod("hashCode", new Class[0]);
m3 = Class.forName("com.zcd.learn.designpattern.proxy.IFunction").getMethod("print", new Class[0]);
m2 = Class.forName("java.lang.Object").getMethod("toString", new Class[0]);
return;
}
catch (NoSuchMethodException localNoSuchMethodException)
{
throw new NoSuchMethodError(localNoSuchMethodException.getMessage());
}
catch (ClassNotFoundException localClassNotFoundException)
{
throw new NoClassDefFoundError(localClassNotFoundException.getMessage());
}
}
}
我们会发现这个动态代理类会持有我们的代理类实例(h),无论是调用function 方法还是 print 方法都是调用我们的代理类FunctionProxy 的invoke 方法(this.h.invoke(this, m3, null)),并且传入本方法的Method对象,而在FunctionProxy 的 invoke方法则是采用反射来调用委托类的具体方法。
最后记录一个我遇到的一个问题:
把FunctionProxy 中 invoke 方法中我标注的注意点1 换成如下代码,会出现死循环:
Object obj = method.invoke(proxy,args);
原因就是proxy就是代理类实例本身,这样写就是重复调用自己会出现死循环。也是在反编译后我才彻底明白为什么会有这样的错误,也让我对jdk动态代理理解更深刻。
Cglib动态代理
简介:
在我们上一节中使用jdk动态代理,必须实现一个接口,而在实际的生产环境中并不是所有的类都是实现了某某接口的,这时候我们要实现这种类的代理就可以使用cglib动态代理。它是通过动态生成一个子类继承于被代理类,从而达到代理目的。
老规矩通过一个例子来看看如何实现一个cglib动态代理。
cglib动态需要引入外部jar包,我通过maven引入方式如下:
cglib
cglib
3.1
1.定义被代理类
public class CglibFunction {
public void funcA(){
System.out.println("CglibFunction funcA");
}
public int funcB(int a, int b){
System.out.println("CglibFunction funcB");
return a + b;
}
}
2.通过实现 net.sf.cglib.proxy.MethodInterceptor 接口来实现cglib代理类
import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;
import java.lang.reflect.Method;
/**
* Created by zhangyida on 2017/9/15.
*/
public class CglibProxy implements MethodInterceptor {
public Object intercept(Object obj, Method method, Object[] args,
MethodProxy proxy) throws Throwable {
System.out.println("cglib before!");
//通过代理类调用父类中的方法
Object result = proxy.invokeSuper(obj, args);
System.out.println("cglib after!");
return result;
}
}
3.通过net.sf.cglib.proxy.Enhancer 来动态获取代理实例
CglibProxy proxy = new CglibProxy();
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(CglibFunction.class);
enhancer.setCallback(proxy);
CglibFunction proxyImp = (CglibFunction)enhancer.create();
proxyImp.funcA();
运行结果如下:
cglib before!
CglibFunction funcA
cglib after!
Enhancer是一个字节码增强器,下面是cglib源代码中对Enhancer作用的描述:
Generates dynamic subclasses to enable method interception.
其生成动态代理子类的核心代码如下:
byte[] b = strategy.generate(this);
String className = ClassNameReader.getClassName(new ClassReader(b));
getClassNameCache(loader).add(className);
Class gen = ReflectUtils.defineClass(className, b, loader);
故事总是惊人的相似,cglib动态获取代理实例的步骤如下:
①生成代理类的字节码二进制数组
②根据字节码获取Class对象
③利用反射机制创建代理类的实例。
接下来可以通过如下的方法来生成动态代理类的.class文件,因为在net.sf.cglib.core.DebuggingClassWriter 中给我们预留了这个功能,有兴趣的读者可以去看看,这个类中的toByteArray 方法。
1.在代码中加入
System.setProperty(DebuggingClassWriter.DEBUG_LOCATION_PROPERTY, "E:\\Bode");
2.或者在vm 启动参数参数中添加如下参数:
-Dcglib.debugLocation="E:\\Bode"
这两种方法均能达到在E盘的Bode目录下生成代理类的.class,用反编译工具(有感兴趣的读者推荐使用procyon-decompiler,因为我用jd反编译有很多缺失)打开这些 .class文件,其中较重要的内容展示如下(由于内容比较多,删除了一些无关紧要的代码):
//
// Decompiled by Procyon v0.5.29
//
package com.zcd.learn.designpattern.proxy.cglibproxy;
import net.sf.cglib.core.Signature;
import net.sf.cglib.core.ReflectUtils;
import net.sf.cglib.proxy.MethodProxy;
import java.lang.reflect.Method;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.Callback;
import net.sf.cglib.proxy.Factory;
public class CglibFunction$$EnhancerByCGLIB$$97816eeb extends CglibFunction implements Factory
{
private MethodInterceptor CGLIB$CALLBACK_0;
private static final Method CGLIB$funcA$0$Method;
private static final MethodProxy CGLIB$funcA$0$Proxy;
private static final Method CGLIB$funcB$1$Method;
private static final MethodProxy CGLIB$funcB$1$Proxy;
static void CGLIB$STATICHOOK1() {
final Method[] methods2 = ReflectUtils.findMethods(new String[] { "funcA", "()V", "funcB", "(II)I" }, (forName3 = Class.forName("com.zcd.learn.designpattern.proxy.cglibproxy.CglibFunction")).getDeclaredMethods());
CGLIB$funcA$0$Method = methods2[0];
CGLIB$funcA$0$Proxy = MethodProxy.create((Class)forName3, (Class)forName, "()V", "funcA", "CGLIB$funcA$0");
CGLIB$funcB$1$Method = methods2[1];
CGLIB$funcB$1$Proxy = MethodProxy.create((Class)forName3, (Class)forName, "(II)I", "funcB", "CGLIB$funcB$1");
}
final void CGLIB$funcA$0() {
super.funcA();
}
public final void funcA() {
MethodInterceptor cglib$CALLBACK_;
MethodInterceptor cglib$CALLBACK_0;
if ((cglib$CALLBACK_0 = (cglib$CALLBACK_ = this.CGLIB$CALLBACK_0)) == null) {
CGLIB$BIND_CALLBACKS(this);
cglib$CALLBACK_ = (cglib$CALLBACK_0 = this.CGLIB$CALLBACK_0);
}
if (cglib$CALLBACK_0 != null) {
cglib$CALLBACK_.intercept((Object)this, CglibFunction$$EnhancerByCGLIB$$97816eeb.CGLIB$funcA$0$Method, CglibFunction$$EnhancerByCGLIB$$97816eeb.CGLIB$emptyArgs, CglibFunction$$EnhancerByCGLIB$$97816eeb.CGLIB$funcA$0$Proxy);
return;
}
super.funcA();
}
final int CGLIB$funcB$1(final int n, final int n2) {
return super.funcB(n, n2);
}
public final int funcB(final int n, final int n2) {
MethodInterceptor cglib$CALLBACK_;
MethodInterceptor cglib$CALLBACK_0;
if ((cglib$CALLBACK_0 = (cglib$CALLBACK_ = this.CGLIB$CALLBACK_0)) == null) {
CGLIB$BIND_CALLBACKS(this);
cglib$CALLBACK_ = (cglib$CALLBACK_0 = this.CGLIB$CALLBACK_0);
}
if (cglib$CALLBACK_0 != null) {
final Object intercept = cglib$CALLBACK_.intercept((Object)this, CglibFunction$$EnhancerByCGLIB$$97816eeb.CGLIB$funcB$1$Method, new Object[] { new Integer(n), new Integer(n2) }, CglibFunction$$EnhancerByCGLIB$$97816eeb.CGLIB$funcB$1$Proxy);
return (intercept == null) ? 0 : ((Number)intercept).intValue();
}
return super.funcB(n, n2);
}
public static MethodProxy CGLIB$findMethodProxy(final Signature signature) {
final String string = signature.toString();
switch (string.hashCode()) {
case -1555444918: {
if (string.equals("funcB(II)I")) {
return CglibFunction$$EnhancerByCGLIB$$97816eeb.CGLIB$funcB$1$Proxy;
}
break;
}
case 1379354712: {
if (string.equals("funcA()V")) {
return CglibFunction$$EnhancerByCGLIB$$97816eeb.CGLIB$funcA$0$Proxy;
}
break;
}
}
return null;
}
static {
CGLIB$STATICHOOK1();
}
}
以上就是cglib动态代理生成的字节码最需要我们关注的部分,我们得出结论:
1.此动态生成的类继承于需要被代理的类(委托类)
public class CglibFunction$$EnhancerByCGLIB$$97816eeb extends CglibFunction implements Factory
2.此类持有我们编写的代理类的实例(经过调试确认),因为其有一个私有属性,这个属性被引用的地方很多
private MethodInterceptor CGLIB$CALLBACK_0;
3.此代理类为每个方法(无论是Object还是CglibFunction的方法),都对应着两个方法,对于funcA 方法对应 CGLIB$funcA$0 和 funcA。这个同名的方法funcA会直接调用我们写的代理类的intercept 方法,CGLIB$funcA$0 方法则是直接调用委托类的目标方法 funcA。
4.此代理类为每个方法都构建了一个MethodProxy 对象
private static final MethodProxy CGLIB$funcB$1$Proxy;
CGLIB$funcB$1$Proxy = MethodProxy.create((Class)forName3, (Class)forName, "(II)I", "funcB", "CGLIB$funcB$1");
5.为什么要提这个MethodProxy呢,因为我们自己实现的代理类的intercept方法中最重要的一步就是
Object result = proxy.invokeSuper(obj, args);
而这个proxy就是一个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, 和 FastClass
private static class FastClassInfo
{
FastClass f1; //指向委托类的Class
FastClass f2; //指向动态代理类的Class
int i1; //funcA在代理对象中的索引位置
int i2; //CGLIB$funcA$0 在代理对象中的索引位置
}
abstract public class FastClass
{
private Class type;
//省略很多
}
那么我们就可以知道invokeSuper 方法中
fci.f2.invoke(fci.i2, obj, args);
就是调用的父类的同名方法,在MethodProxy 类中还有invoke 方法,
return fci.f1.invoke(fci.i1, obj, args);
读者可以试试调用这个方法,也会出现在jdk动态代理最后我提的死循环问题。
总结:
无论是jdk动态代理还是 cglib 动态代理采用字节码技术生成了一个新的代理类,这个动态代理类持有我们编写的代理类的实例,我们调用目标方法其实都是调用InvocationHandler 的 invoke 方法,或者MethodInterceptor 的 intercept 方法,然后通过一定的技术调用委托类的方法,一个采用反射,一个采用类似数组下标的方式来定位方法,直接进行类方法的执行。
后记:
此次是本人第一次写技术博客,个中出现的不正确或者表达不恰当或者不明白之处望热心读者可以给我指正。同时我也认为博客能记录自己的学习足迹,也能与广大读者共同交流技术‘思想’,在这里自勉自己能一直坚持下去。