一、代理介绍
代理(Proxy),是一种设计模式,使我们可以不直接访问目标对象,而通过代理对象进行访问,这样做的好处,我们可以在目标对象的基础上,添加一些额外的操作,相当于扩展了功能。
常用的创建代理对象的方式有静态代理与动态代理:
1、静态代理:编译时就已经实现,代理类和被代理类应该共同实现一个接口,或者是共同继承某个类。
2、动态代理:利用了 JDK API,运行时动态生成
静态代理相比于动态代理:
静态代理:代理类要实现与被代理类相同的接口,如果代理较多,需要编写多个代理类。
另外,如果需要增加方法,代理类与被代理类都需要进行修改,不易维护
动态代理:不需要提前编写代理类,使用上面比较灵活。
二、静态代理
直接上代码:
先定义代理类与被代理类共同需要实现的接口:
public interface People { public void printName(Integer years); }
实现被代理类:
public class Man implements People{ public String name = ""; @Override public void printName(Integer years ) { System.out.println(" name:"+ name + " years:" + years); } }
实现代理类:
public class ManProxy implements People { private Man man; public ManProxy(Man man){ this.man = man; } @Override public void printName(Integer years) { System.out.println("begin-------------"); man.printName(years); System.out.println("end-------------"); } }
代理类一般都包含被代理类的引用,也可直接继承代理类。打印begin、end处即可添加需要添加的流程
相关方法调用:
Man man = new Man(); man.name = "zhang san";//静态代理测试 ManProxy manProxy = new ManProxy(man); manProxy.printName(1000);
静态代理比较简单,这里就不做过多的陈述了。
三、动态代理
3.1、动态代理的实现
动态代理的实现主要分为以下三个步骤:
1、和静态代理一样,先创建被代理类所需实现的接口以及实现被代理类
2、实现InvocationHandler接口
3、调用Proxy.newProxyInstance方法,动态生成代理对象。
下面我们就围绕这三个步骤来做相关的介绍
3.2、创建被代理类所需实现的接口以及实现被代理类
这一步和静态代理一致,直接贴代码:
public interface People { public void printName(Integer years); }
public class Man implements People{ public String name = ""; @Override public void printName(Integer years ) { System.out.println(" name:"+ name + " years:" + years); } }
3.3、实现InvocationHandler接口
代码如下:
1、需要一个object对象保存被代理的对象
2、注意invoke方法,方法传入三个参数,代理对象,调用的方法,已经调用该方法传入的参数。invoke的实现,主要是利用了反射,调用了被代理对象的方法。反射相关介绍可参考另一篇文章(java 反射原理学习_weixin_43863193的博客-CSDN博客)
3.4、调用Proxy.newProxyInstance方法,动态生成代理对象。
调用方法代码如下:
People people = (People) Proxy.newProxyInstance(Man.class.getClassLoader(),new Class[]{People.class},invocationHandler);
newProxyInstance方法的申明如下:
public static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h)
接收三个参数:
1、被代理类的ClassLoader
2、生成的动态代理类中需要实现的接口
3、3.3中实现的InvocationHandler
调用Proxy.newProxyInstance方法就能动态的生成动态代理对象,调用动态代理对象的方法,就会执行到3.3中实现的TestInvocationHandler,中的invoke方法。进而通过反射调用被代理对象的相关方法。
why?为什么通过调用动态代理对象的方法会执行到TestInvocationHandler的invoke方法?
我们来看newProxyInstance的实现:
进入getProxyConstructor()方法:
标记位置,可以简单的理解为构建了一个代理类。生成的代理类可以在项目的更目录下找到,包名一般为com.sun.proxy,示例中生成的代理类名称为$Proxy0。我们来看生成的代理类源码:
public final class $Proxy0 extends Proxy implements People { private static Method m1; private static Method m3; private static Method m2; 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 void printName(Integer var1) throws { try { super.h.invoke(this, m3, 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 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")); m3 = Class.forName("com.example.lib.People").getMethod("printName", Class.forName("java.lang.Integer")); m2 = Class.forName("java.lang.Object").getMethod("toString"); m0 = Class.forName("java.lang.Object").getMethod("hashCode"); } catch (NoSuchMethodException var2) { throw new NoSuchMethodError(var2.getMessage()); } catch (ClassNotFoundException var3) { throw new NoClassDefFoundError(var3.getMessage()); } } }
其中有 equals、hashCode、toString等方法,我们主要关注printName这个方法,因为这个类也继承了People接口,所以实现了printName方法,来看他的实现:
调用了super.h.invoke(this, m3, new Object[]{var1});
接下来看代理类的构造方法:
再进入父类的构造方法:
传入了InvocationHandler对象,复制给了this.h。传入的InvocationHandler对象也就是调用newProxyInstance方法时传入的第三个参数,将我们实现的TestInvocationHandler对象传入了进来。
也就是说调用 Proxy.newProxyInstance方法,返回的其实就是动态生成的代理类($Proxy0)对应的对象。所以调用该对象的printName方法,会直接调用到3.3中实现的TestInvocationHandler,中的invoke方法。进而通过反射调用被代理对象的相关方法。