序言 今天来总结Java代理。分为静态和动态。
什么是代理? 定义 给目标对象提供一个代理对象,并由代理对象控制目标对象的引用。
目的
通过引入代理对象的方式来简介访问目标对象,防止直接访问目标对象给系统带来的不必要复杂性;
通过代理对象对原有的业务增强;
注意事项
代理对象和真实对象必须实现同一个接口 ;
代理对象只是搬运工,代理对象必须包含真实的对象;
代理模式 给某一个对象提供一个代理,并由代理对象来控制对真实对象的访问。
代理模式是一种结构型设计模式。
代理模式角色分为 3 种:
Subject (接口):
定义代理类和真实主题的公共对外方法,也是代理类代理真实主题的方法,其实就是一个功能接口;
RealSubject (真实类):真正实现业务逻辑的类,这就是真实的对象;
Proxy (代理类):用来代理和封装真实主题;
三者关系如图所示:
如果根据字节码的创建时机 来分类,可以分为静态代理和动态代理:
所谓静态 也就是在程序运行前 就已经存在代理类的字节码文件 ,代理类和真实主题角色的关系在运行前就确定了。
而动态代理的源码是在程序运行期间由JVM 根据反射等机制动态的生成 ,所以在运行前并不存在代理类的字节码文件。(这个很牛逼)
静态代理 下面写个小demo看一下。
编写一个接口 UserService ,以及该接口的一个实现类 UserServiceImpl:
接口 UserService:
1 2 3 4 public interface UserService { public void select () ; public void update () ; }
接口实现类UserServiceImpl:
1 2 3 4 5 6 7 8 public class UserServiceImpl implements UserService { public void select () { System.out.println("查询 selectById" ); } public void update () { System.out.println("更新 update" ); } }
代理类UserServiceProxy:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 public class UserServiceProxy implements UserService { private UserService target; public UserServiceProxy (UserService target) { this .target = target; } public void select () { before(); target.select(); after(); } public void update () { before(); target.update(); after(); } private void before () { System.out.println(String.format("log start time [%s] " , new Date())); } private void after () { System.out.println(String.format("log end time [%s] " , new Date())); } }
写一个客户端:
1 2 3 4 5 6 7 8 9 public class Client { public static void main (String[] args) { UserService user = (UserService) new UserServiceImpl(); UserService proxy = new UserServiceProxy(user); proxy.select(); proxy.update(); } }
输出结果:
1 2 3 4 5 6 7 8 log start time [Mon Apr 13 21:28:09 CST 2020] 查询 selectById log end time [Mon Apr 13 21:28:09 CST 2020] log start time [Mon Apr 13 21:28:09 CST 2020] 更新 update log end time [Mon Apr 13 21:28:09 CST 2020] Process finished with exit code 0
模板:
1 2 3 4 接口 obj = new 接口实现类(); 代理类 proxy = new 代理类(obj); proxy.method_a(); proxy.method_b();
静态代理的缺点 虽然静态代理实现简单,且不侵入原代码,但是,当场景稍微复杂一些的时候,静态代理的缺点也会暴露出来。
1/当需要代理多个类的时候,由于代理对象要实现与目标对象一致的接口,有两种方式:
只维护一个代理类,由这个代理类实现多个接口,但是这样就导致代理类过于庞大
新建多个代理类,每个目标对象对应一个代理类,但是这样会产生过多的代理类
2/当接口需要增加、删除、修改方法的时候,目标对象与代理类都要同时修改,不易维护 。
动态代理 为什么类可以动态的生成? Java虚拟机类加载过程主要分为五个阶段:加载、验证、准备、解析、初始化。其中加载 阶段需要完成以下3件事情:
通过一个类的全限定名来获取定义此类的二进制字节流
将这个字节流所代表的静态存储结构转化为方法区的运行时数据结构
在内存中生成一个代表这个类的 java.lang.Class
对象,作为方法区这个类的各种数据访问入口
由于虚拟机规范对这3点要求并不具体,所以实际的实现是非常灵活的,关于第1点,获取类的二进制字节流 (class字节码)就有很多途径:
从ZIP包获取,这是JAR、EAR、WAR等格式的基础
从网络中获取,典型的应用是 Applet
运行时计算生成 ,这种场景使用最多的是动态代理技术,在 java.lang.reflect.Proxy
类中,就是用了 ProxyGenerator.generateProxyClass
来为特定接口生成形式为 *$Proxy
的代理类的二进制字节流
由其它文件生成,典型应用是JSP,即由JSP文件生成对应的Class类
从数据库中获取等等
所以,动态代理就是想办法,根据接口或目标对象,计算出代理类的字节码,然后再加载到JVM中使用。但是如何计算?如何生成?情况也许比想象的复杂得多,我们需要借助现有的方案。
JDK动态代理 两个核心类 创建动态代理类会使用到
java.lang.reflect.Proxy
类
和
java.lang.reflect.InvocationHandler
接口。
目标类必须实现接口,没有接口只能用CGLIB。
java.lang.reflect.Proxy主要用于生成动态代理类Class、创建代理类实例,该类实现了java.io.Serializable接口。
Proxy
是类 ,是调度器,帮助调度服务的员工,是所有动态代理的父类 ,它只管new实例出来,别的不插手。
InvocationHandler
是接口,用于调用Proxy
类生成的代理类方法,该类只有 一个invoke
方法。
只管“new的实例”的执行功能,别的不插手。
在程序运行过程中产生的代理类的对象,其实就是通过反射机制来生成的。
JDK提供的代理只能针对接口做代理。
每一个动态代理类都必须要实现InvocationHandler这个接口。
它的invoke方法表示代理对象要执行的功能代码。
proxy Proxy这个类的作用就是用来动态创建一个代理对象类 ,它提供了许多的方法:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 copypackage java.lang.reflect; import java.lang.reflect.InvocationHandler;public class Proxy implements java .io .Serializable { public static InvocationHandler getInvocationHandler (Object proxy) throws IllegalArgumentException { ... } public static Object newProxyInstance (ClassLoader loader, Class<?>[] interfaces, InvocationHandler h) throws IllegalArgumentException { ... } public static Class<?> getProxyClass(ClassLoader loader, Class<?>... interfaces) { ... } public static boolean isProxyClass (Class<?> cl) { return java.lang.reflect.Proxy.class .isAssignableFrom (cl ) && proxyClassCache .containsValue (cl ) ; } private static native Class defineClass0 (ClassLoader loader, String name, byte [] b, int off, int len) ; }
其中用的最多的就是 newProxyInstance ()
方法:
1 2 3 4 5 6 7 8 9 10 11 public static Object newProxyInstance (ClassLoader loader, Class<?>[] interfaces, InvocationHandler h) throws IllegalArgumentException
这个方法的作用就是得到一个动态代理对象 ,其中接收三个参数:
loader :一个ClassLoader对象,定义了由哪个ClassLoader对象来对生成的代理类对象进行加载;
实现类a,a.getClass().getClassLoader(),获取目标对象的类加载器。
interfaces参数 :一个Interface接口对象数组,说明将要给被代理类对象提供一组什么样的接口,如果提供了一组接口给被代理类对象,那么该对象就宣称实现了该接口(多态),这样就能调用这组接口中的方法了;
目标对象实现的接口,也是反射获取的。a.getClass().getInterfaces()
h :一个InvocationHandler对象,表示的是当这个动态代理对象在调用方法的时候,会关联到哪一个InvocationHandler对象上;
是我们自己写的,代理类要完成的功能,体现在invoke函数里。
返回值:就是代理对象。 把三个参数当作原料,加工出来代理对象。
Invocationhandler 作为InvocationHandler接口唯一的方法,invoke ()方法定义如下:
1 Object invoke (Object proxy, Method method, Object[] args) throws Throwable
proxy参数:jdk创建的代理对象,无需赋值;
method参数:目标类中的方法;
args参数:Method参数中,接收的参数;
以上三个参数都是jdk帮忙创建的,无需人为赋值。
怎么用:
创建一个类,来实现InvocationHandler
重写invoke方法,把原来静态代理中代理类要完成的功能,放在重写之后的invoke方法中实现。
invoke方法表示代理对象要执行的功能代码。
还需要一个Object字段:
1 2 3 4 5 private Object target;public MyHandler (Object target) { this .target = target; } ret = method.invoke(target,args);
newProxyInstance 源码 看看java.lang.reflect.Proxy#newProxyInstance
里面怎么实现的:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 public static Object newProxyInstance (ClassLoader loader, Class<?>[] interfaces, InvocationHandler h) throws IllegalArgumentException { if (h == null ) { throw new NullPointerException(); } Class<?> cl = getProxyClass0(loader, interfaces); try { final Constructor<?> cons = cl.getConstructor(constructorParams); final InvocationHandler ih = h; SecurityManager sm = System.getSecurityManager(); if (sm != null && ProxyAccessHelper.needsNewInstanceCheck(cl)) { return AccessController.doPrivileged(new PrivilegedAction<Object>() { public Object run () { return newInstance(cons, ih); } }); } else { return newInstance(cons, ih); } } catch (NoSuchMethodException e) { throw new InternalError(e.toString()); } }
这里面有一个关键函数是Class<?> cl = getProxyClass0(loader, intfs);
,这一步主要就是生成代理类。
JDK会生成一个叫$Proxy0的代理类,这个类文件是放在内存中的,在创建代理类对象时,通过反射机制获得这个类的构造方法,然后创建代理类实例。
动态代理Demo 实现动态代理的步骤:
创建接口,定义目标类要完成的功能
创建目标类来实现接口
创建InvocationHandler接口的实现类,在invoke方法中完成代理类的功能
调用目标方法
增强功能
使用Proxy类的newProxyInstance
方法,来创建代理对象,代理对象来执行目标方法调用,其实是去执行handler中的invoke方法。invoke方法主要还是做两件事情:1.调用目标方法 2.功能增强。然后invoke返回目标方法执行结果。
这里再写一个代购小demo:
如果说静态代理阶段,我是一个只会提供固定商品的代购,那么现在我强大了,我有一个小公司,我的公司旗下可以代购各种商品,对于代购每一种商品的员工,都有熟练且强大的业务能力。
那么我先写好我的“代购帝国公司”:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 import java.lang.reflect.InvocationHandler;import java.lang.reflect.Method;import java.lang.reflect.Proxy;public class LisonCompany implements InvocationHandler { private Object factory; public Object getFactory () { return factory; } public void setFactory (Object factory) { this .factory = factory; } public Object getProxyInstance () { return Proxy.newProxyInstance(factory.getClass().getClassLoader(),factory.getClass().getInterfaces(),this ); } @Override public Object invoke (Object proxy, Method method, Object[] args) throws Throwable { dobefore(); Object ret = method.invoke(factory,args); doafter(); return ret; } private void dobefore () { System.out.println("售前服务,精美包装,快递一条龙服务!" ); } private void doafter () { System.out.println("售后服务,无忧退换货!" ); } }
这之后需要新的功能,只需加类,加接口就可以完成实现。
这里再具体写一下,假设有两个水果工厂:
1 2 3 4 5 6 7 public interface AppleFactory { public void makeApple () ; } public interface OrangeFactory { public void makeOrange () ; }
分别写好对应实现类:
1 2 3 4 5 6 7 8 9 10 11 12 public class AppleFactoryImpl implements AppleFactory { @Override public void makeApple () { System.out.println("新鲜大苹果!!!" ); } } public class OrangeFactoryImpl implements OrangeFactory { @Override public void makeOrange () { System.out.println("新鲜大橙子!!!" ); } }
那么代购客户端这样写:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 public class FruitClient { public static void main (String[] args) { AppleFactory apl = new AppleFactoryImpl(); LisonCompany lisonComp = new LisonCompany(); lisonComp.setFactory(apl); AppleFactory lison1 = (AppleFactory)lisonComp.getProxyInstance(); lison1.makeApple(); System.out.println("------------------------" ); OrangeFactory org = (OrangeFactory) new OrangeFactoryImpl(); lisonComp.setFactory(org); OrangeFactory lison2 = (OrangeFactory)lisonComp.getProxyInstance(); lison2.makeOrange(); } }
运行结果:
1 2 3 4 5 6 7 售前服务,精美包装,快递一条龙服务! 新鲜大苹果!!! 售后服务,无忧退换货! ------------------------ 售前服务,精美包装,快递一条龙服务! 新鲜大橙子!!! 售后服务,无忧退换货!
流程图:
又写了一个好懂的版本:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 public class Buyer { public static void main (String[] args) { Factory realfactory = new FactoryImpl(); InvocationHandler handler = new Company(realfactory); Factory fac = (Factory) Proxy.newProxyInstance(handler.getClass().getClassLoader(),realfactory.getClass().getInterfaces(),handler); fac.make("大苹果" ); fac.taste("大西瓜" ); } } interface Factory { public void make (String name) ; public void taste (String name) ; } class FactoryImpl implements Factory { public void make (String name) { System.out.println("[*]买新鲜的" +name); } public void taste (String name) { System.out.println("[*]尝一口" +name); } } class Company implements InvocationHandler { Object subject; public Company (Object subject) { this .subject = subject; } public Object invoke (Object proxy, Method method, Object[] args) throws Throwable { System.out.println("来代理咯!" ); method.invoke(subject,args); System.out.println("代理完毕咯!" ); return null ; } }
结果:
总结 JDK 动态代理 :
为了解决静态代理中,生成大量的代理类造成的冗余;
JDK 动态代理只需要实现 InvocationHandler 接口,重写 invoke 方法便可以完成代理的实现,
jdk的代理是利用反射生成代理类 Proxyxx.class 代理类字节码,并生成对象
jdk动态代理之所以只能代理接口 是因为代理类本身已经extends了Proxy,而java是不允许多重继承的 ,但是允许实现多个接口
优点 :解决了静态代理中冗余的代理实现类问题。
缺点 :JDK 动态代理是基于接口设计实现的,如果没有接口,会抛异常。
通用模式:
1 2 3 4 接口 os = new 接口实现类(); Myhandler h = new Myhandler(os); 接口 proxy = Proxy.newproxyInstance(loader,interfaces,h); proxy.目标方法();