jdk动态代理概述
jdk动态代理的底层机制是让代理类和被代理类都实现相同的接口,通过反射创建动态代理类,然后通过反射去调用被代理类的方法,并可以在调用方法前后去增加一些增强功能,使得代码的耦合性降低,增强扩展性。
JDK动态代理demo
我们简单粗暴一些,接下来我们直接通过一个例子来理解jdk动态代理是如何实现的
我们用员工提交涨工资申请的例子来演示一下
1.首先定义一个公共接口Person,里面有一个方法riseSalary() 涨工资
public interface Person {
/**
* 涨工资
*/
void riseSalary();
}
2.然后定义一个staff类去实现这个接口,并重写这个方法
public class Staff implements Person {
private String name;
@Override
public void riseSalary() {
System.out.println(this.name + "提交涨工资申请");
}
}
这个时候我们想在员工提交涨工资申请前对员工进行资格审查,在提交涨工资申请后进行评估,但是又不想直接修改Staff类中的riseSalary()方法,避免代码侵入增加耦合性,所以我们这里用jdk动态代理来实现这个功能。
3.我们新增一个StaffInvokehanlder增强类。
该类继承了java反射包下的InvocationHandler类,该类下有一个invoke方法,最终通过调用invoke方法从而达到调用被代理类方法的能力,并可以在invoke方法前后增加功能,带到通过动态代理增强被代理类功能的目的。
public class StaffInvokehanlder<T> implements InvocationHandler {
private T target;
public StaffInvokehanlder(T target) {
this.target = target;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("提交涨工资申请前对员工进行资格审查");
Object object = method.invoke(target, args);
System.out.println("提交涨工资申请后进行评估");
return object;
}
}
4.接下来我们来增加一个测试类,来演示一下动态代理的功能
public class StaffTest {
public static void main(String[] args) {
//将动态代理类输出到工程中com.sun.proxy目录下
System.getProperties().put("sun.misc.ProxyGenerator.saveGeneratedFiles", "true");
//实例化员工王老五
Person staff = new Staff("王老五");
//将王老五放入StaffInvokehanlder增强类中
StaffInvokehanlder invokehanlder = new StaffInvokehanlder(staff);
//调用Proxy.newProxyInstance方法生成动态代理类
Person person = (Person) Proxy.newProxyInstance(Person.class.getClassLoader(), new Class<?>[]{Person.class}, invokehanlder);
/**
* 由于动态代理类也实现了Person接口,会重写riseSalary,这里调用动态代理类的riseSalary方法,调用此方法就是调用StaffInvokehanlder的中的invoke方法
* 调用StaffInvokehanlder的invoke方法就会调用被代理类staff的riseSalary,并在方法前后进行增强
*/
person.riseSalary();
}
}
因为动态代理类也实现了Person接口,会重写riseSalary方法,这里调用动态代理类的riseSalary方法就是调用StaffInvokehanlder的中的invoke方法,调用StaffInvokehanlder的invoke方法就会调用被代理类staff的riseSalary方法,并在方法前后进行增强
至此我们就完成动态代理的demo,我们来看看程序执行的输出结果
提交涨工资申请前对员工进行资格审查
王老五提交涨工资申请
提交涨工资申请后进行评估
在王老五提交涨工资申请前后增强了资格审查和评估功能,定位到代码中就是执行invoke方法的执行,通过反射执行被代理类的riseSalary方法,并在方法前后增强功能,此时并不需要对staff中的代码进行修改,只需要增加一个invokeHanlder增强器,就可以实现这个功能
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("提交涨工资申请前对员工进行资格审查");
Object object = method.invoke(target, args);
System.out.println("提交涨工资申请后进行评估");
return object;
}
5.动态代理底层源码分析
那么jdk动态代理是如何通过调用代理类的方法,从而去调用Invoke方法的呢,我们接下来通过生成的动态代理类来具体分析一下
我们先看这一行代码
//调用Proxy.newProxyInstance方法生成动态代理类
Person person = (Person) Proxy.newProxyInstance(Person.class.getClassLoader(), new Class<?>[]{Person.class}, invokehanlder);
这行代码就是通过java的Proxy生成动态代理类,传入参数为Person的类加载器,Person类元数据信息,invokeHanlder增强类实例,我们点进newProxyInstance看看
我们主要关注那么几行代码
/*
* Look up or generate the designated proxy class.
*/
Class<?> cl = getProxyClass0(loader, intfs);
这行代码就是通过反射获得动态代理类的.class信息,返回类型是Class<?>,看他源码上的注释
Look up or generate the designated proxy class.(查找或生成指定的代理类。)
接下来我们关注一下这行代码
//通过反射获取代理类的构造器
final Constructor<?> cons = cl.getConstructor(constructorParams);
final InvocationHandler ih = h;
if (!Modifier.isPublic(cl.getModifiers())) {
AccessController.doPrivileged(new PrivilegedAction<Void>() {
public Void run() {
cons.setAccessible(true);
return null;
}
});
}
//通过构造器实体化代理类,将增强类InvocationHandler 的实例作为参数传入到代理类中
return cons.newInstance(new Object[]{h});
这个代码我们就很熟悉了吧,知道反射原理的看这段代码应该都不陌生,上面我们已经获得代理类的.class信息,下面的代码便是获得代理类的构造器,并通过构造器的newInstance()方法实例化生成代理类实例,并且包含参数InvocationHandler h,这个增强类实例不就是我们
Person person = (Person) Proxy.newProxyInstance(Person.class.getClassLoader(), new Class<?>[]{Person.class}, invokehanlder);
这行代码传进来的嘛。
至此我们应该知道生成代理类的全过程了,总结一下就是:
- 通过反射获得代理类的信息
- 获取代理类的构造器
- 通过构造器的newInstance()实例化代理类,代理类实例并拥有传入的增强类实例
经过这个方法后我们便获得了代理类的实例
接着便是对代理类方法的调用
person.riseSalary()
这时候我们便需要去深入代理类的实现细节去看看,这行代码调用是怎么实现动态代理的
我们在测试类中增加了这么一行代码
//将动态代理类输出到工程中com.sun.proxy目录下
System.getProperties().put("sun.misc.ProxyGenerator.saveGeneratedFiles", "true");
通过该行代码我们便可以在项com.sun.proxy目录下找到动态代理的类文件,一个.class字节码文件
我们来看看这个代理类长什么样,我把这个代理类的源码贴在下面
public final class $Proxy0 extends Proxy implements Person {
private static Method m1;
private static Method m2;
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 riseSalary() throws {
try {
super.h.invoke(this, m3, (Object[])null);
} catch (RuntimeException | Error var2) {
throw var2;
} catch (Throwable var3) {
throw new UndeclaredThrowableException(var3);
}
}
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");
m3 = Class.forName("com.example.demo.designPatterns.proxy.jdk.proxy5.Person").getMethod("riseSalary");
m0 = Class.forName("java.lang.Object").getMethod("hashCode");
} catch (NoSuchMethodException var2) {
throw new NoSuchMethodError(var2.getMessage());
} catch (ClassNotFoundException var3) {
throw new NoClassDefFoundError(var3.getMessage());
}
}
}
我们先来看看该类的静态块
static {
try {
m1 = Class.forName("java.lang.Object").getMethod("equals", Class.forName("java.lang.Object"));
m2 = Class.forName("java.lang.Object").getMethod("toString");
m3 = Class.forName("com.example.demo.designPatterns.proxy.jdk.proxy5.Person").getMethod("riseSalary");
m0 = Class.forName("java.lang.Object").getMethod("hashCode");
} catch (NoSuchMethodException var2) {
throw new NoSuchMethodError(var2.getMessage());
} catch (ClassNotFoundException var3) {
throw new NoClassDefFoundError(var3.getMessage());
}
}
m3 = Class.forName("com.example.demo.designPatterns.proxy.jdk.proxy5.Person").getMethod("riseSalary");
是不是看到了这一行代码,这不就是通过反射获取我们类方法吗并用m3这个引用引用起来
我们接着往下看
public final void riseSalary() throws {
try {
super.h.invoke(this, m3, (Object[])null);
} catch (RuntimeException | Error var2) {
throw var2;
} catch (Throwable var3) {
throw new UndeclaredThrowableException(var3);
}
}
这个方法不就是我们的涨工资方法吗,看一下里面的实现
super.h.invoke(this, m3, (Object[])null);
这不就是调用我们增强类的invoke方法吗,看到这是不是就有头绪了,动态代理的过程已经了然于胸。
这时候我们再回头看看刚开始的概述
jdk动态代理的底层机制是让代理类和被代理类都实现相同的接口,通过反射创建动态代理类,然后通过反射去调用被代理类的方法,并可以在调用方法前后去增加一些增强功能,使得代码的耦合性降低,增强扩展性。
进一步解释一下
代理类和被代理类实现相同的接口,代理类重写和被代理类一样的方法,代理类重写方法中调用增强类的Invoke方法,增强类拥有被代理类的实例,invoke方法中通过反射调用被代理类的真正实现方法,并在Invoke方法中实现功能的增强。