由于静态代理带来扩展性差,可维护性差等缺点,所以就有了动态代理模式。
下面介绍一下JDK的动态代理:
动态代理有两个重要的类:
1. Proxy类:
provides static methods for creating dynamic proxy classes and instances, and it is also the superclass of all dynamic proxy classes created by those methods.
它是所有动态代理类的父类,同时提供一个静态方法来创建代理类的实例,方法名叫:newProxyInstance
2. InvocationHandler接口:
Processes a method invocation on a proxy instance and returns the result. This method will be invoked on an invocation handler when a method is invoked on a proxy instance that it is associated with.
其中只有一个方法invoke,大致是:在代理类的实例中调用真实方法,并返回结果。
有三个参数:
1. proxy:代理对象的实例
2. method: 通过它可以调用真实方法
3. args:传过来的参数
动态代理的简单应用:
1. 创建实现InvocationHandler接口的代理类:
/** * description 实现InvocationHandler接口的动态代理类 * * @author 70KG * @date 2018/8/2 */ public class ProxyClassBeans implements InvocationHandler { /** * 被代理的对象 **/ private Object tar; /** * 利用反射来调用目标方法 **/ @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { doBefore(); Object invoke = method.invoke(tar, args); doAfter(); return invoke; } /** 绑定该类实现的所有接口,取得代理类 **/ public Object getProxyInstance() { return Proxy.newProxyInstance(tar.getClass().getClassLoader(), tar.getClass().getInterfaces(), this); } // ===================动态代理类中的增强方法======================== public void doBefore() { System.out.println("在国外寻找优质的货源。"); } public void doAfter() { System.out.println("精包装安全邮寄回国内。"); } // ===========================get/set============================= public Object getTar() { return tar; } public void setTar(Object tar) { this.tar = tar; } }
2. 接口
/** * description * * @author 70KG * @date 2018/8/1 */ public interface IBusinessA { /** * Description: 卖包的业务接口 * Author:70KG * Param [brand] 品牌 * Return void * Date 2018/8/1 9:46 */ void saleBag(String brand); }
/** * description * * @author 70KG * @date 2018/8/1 */ public interface IBusinessB { /** * Description: 卖手表的业务接口 * Author:70KG * Param [brand] 品牌 * Return void * Date 2018/8/1 9:48 */ void saleWatch(String brand); }
3. 实现规定接口的被代理类
/** * description * * @author 70KG * @date 2018/8/1 */ public class CompanyA implements IBusinessA { @Override public void saleBag(String brand) { System.out.println("从国外A公司买到一款" + brand + "牌的包。"); } }
/** * description * * @author 70KG * @date 2018/8/1 */ public class CompanyB implements IBusinessB { @Override public void saleWatch(String brand) { System.out.println("从国外B公司买到一款" + brand + "牌的手表。"); } }
4. 测试方法:
/** * description * * @author 70KG * @date 2018/8/2 */ public class MainTest { public static void main(String[] args) { ProxyClassBeans proxyClassBeans = new ProxyClassBeans(); IBusinessA companyA = new CompanyA(); proxyClassBeans.setTar(companyA); IBusinessA instance1 = (IBusinessA) proxyClassBeans.getProxyInstance(); instance1.saleBag("Gucci"); System.out.println("================================="); IBusinessB companyB = new CompanyB(); proxyClassBeans.setTar(companyB); IBusinessB instance2 = (IBusinessB) proxyClassBeans.getProxyInstance(); instance2.saleWatch("Tissot"); } }
5. 运行结果:
在国外寻找优质的货源。 从国外A公司买到一款Gucci牌的包。 精包装安全邮寄回国内。 ================================= 在国外寻找优质的货源。 从国外B公司买到一款Tissot牌的手表。 精包装安全邮寄回国内。
6. JDK的动态代理只能代理接口,不能代理具体的类,通过JDK生成的代理类:public final class $Proxy0 extends Proxy implements YourInterface {},Java只支持单继承,所以不能代理具体类了。
相比静态代理,当业务扩展或者参数变动的时候,我们不需要改动代理类,只需要生成相应的代理实例,并传入接口需要的参数就好了,这样扩展性和维护性大大提高。
动态代理源码的简单分析:
debug跟进生成的代理类
这个代理类的名字很特殊,跟一下源码
跟进 Class<?> cl = getProxyClass0(loader, intfs);
跟进get方法
跟进apply方法
动态代理的源码大概就这么多了。