动态代理之前首先介绍下代理模式,代理分静态代理和动态代理,如果代理类在程序运行前就已经存在,那么这种代理方式被成为 静态代理 。如果代理类是在程序运行中生成,那么这种代理方式被成为 动态代理。java对动态代理有很好的支持,提供了InvocationHandler接口和Proxy类
一、静态代理模式
1.1、静态代理模式的框架
代理是一种常用的设计模式,其目的就是为其他对象提供一个代理以控制对某个对象的访问。代理类负责为委托类代理类会将所有的方法调用分派到目标对象上反射执行,还可以在分派过程中添加前置处理和后置处理(如在调用目标方法前校验权限,在调用完目标方法后打印日志等)等功能。
静态代理模式一般会有三个角色:
抽象角色:指代理角色和真实角色对外提供的公共方法,一般为一个接口
真实角色:需要实现抽象角色接口,定义了真实角色所要实现的业务逻辑,以便供代理角色调用。也就是真正的业务逻辑在此。
代理角色:需要实现抽象角色接口,是真实角色的代理,通过真实角色的业务逻辑方法来实现抽象方法,并可以附加自己的操作。
一般将统一的流程控制都放到代理角色中处理!
1.2、静态代理模式的代码实现
定义一个抽象角色接口(Star)、代理角色实现(经纪人ProxyStar)、真实角色实现(明星RealStar)
抽象角色接口:提供了与明星合作的一系列流程
package com.pjf.proxy; public interface Star { public void sing();//唱歌 }
真实角色实现类:这里是明星真正的工作,这是真实角色真正的业务逻辑部分
package com.pjf.proxy; public class RealStar implements Star { @Override public void sing() { System.out.println("张学友.sing()");//真实角色的操作:真正的业务逻辑 } }
代理角色实现类:代理角色中代理了真实角色所需要的操作(唱歌),经纪人实现了前置处理(面谈、签约)和后置处理(收尾款)。
package com.pjf.proxy; public class ProxyStar implements Star { private Star star;// 真实对象的引用(明星) public ProxyStar(Star star) {// 通过构造函数给真实角色赋值 this.star = star; } @Override public void sing() { System.out.println("ProxyStar.confer()"); // 面谈 System.out.println("ProxyStar.signContract()"); // 签合同 star.sing();// 真实对象的操作(明星唱歌) System.out.println("ProxyStar.collectMoney()"); // 收尾款 } }
测试类
public class Test { public static void main(String[] args) { Star real = new RealStar(); Star proxy = new ProxyStar(real); proxy.sing();//真实对象的操作(明星唱歌) } }
输出结果
ProxyStar.confer()
ProxyStar.signContract()
张学友.sing()
ProxyStar.collectMoney()
二、动态代理模式(JDK)
2.1、动态代理相对于静态代理的优势
代理类在程序运行时创建的代理方式被成为 动态代理。 也就是说,这种情况下,代理类并不是在Java代码中定义的,而是在运行时根据我们在Java代码中的“指示”动态生成的。相比于静态代理, 动态代理的优势在于可以很方便的对代理类的函数进行统一的处理,而不用修改每个代理类的函数。
比如上面的经纪人和明星,如果该明星除了唱歌还需要接其他活动(电视剧、综艺、商演等),代码如下:
package com.pjf.proxy; public interface Star { public void sing();// 唱歌 public void variety(); // 上综艺 public void TVserial(); // 演电视剧 public void commercialPerformance(); // 商演 }
package com.pjf.proxy; public class RealStar implements Star { @Override public void sing() { System.out.println("RealStar.sing()"); // 唱歌 } @Override public void variety() { System.out.println("RealStar.variety()"); // 上综艺 } @Override public void TVserial() { System.out.println("RealStar.TVserial()"); // 演电视剧 } @Override public void commercialPerformance() { System.out.println("RealStar.commercialPerformance()"); // 商演 } }
而这时经纪人类需要在每个函数中添加前置处理和后置处理
package com.pjf.proxy; public class ProxyStar implements Star { private Star star;// 真实对象的引用(明星) public ProxyStar(Star star) {// 通过构造函数给真实角色赋值 this.star = star; } @Override public void sing() { System.out.println("ProxyStar.confer()"); // 面谈 System.out.println("ProxyStar.signContract()"); // 签合同 star.sing();// 真实对象的操作(明星唱歌) System.out.println("ProxyStar.collectMoney()"); // 收尾款 } @Override public void variety() { System.out.println("ProxyStar.confer()"); // 面谈 System.out.println("ProxyStar.signContract()"); // 签合同 Star.variety(); // 真实对象的操作:上综艺 System.out.println("ProxyStar.collectMoney()"); // 收尾款 } @Override public void TVserial() { System.out.println("ProxyStar.confer()"); // 面谈 System.out.println("ProxyStar.signContract()"); // 签合同 star.TVserial();// 真实对象的操作:演电视剧 System.out.println("ProxyStar.collectMoney()"); // 收尾款 } @Override public void commercialPerformance() { System.out.println("ProxyStar.confer()"); // 面谈 System.out.println("ProxyStar.signContract()"); // 签合同 star.commercialPerformance();// 真实对象的操作:商演 System.out.println("ProxyStar.collectMoney()"); // 收尾款 } }
这时候就比较麻烦,需要在每个函数中添加前置面谈、签合同和后置处理收尾款,如果该明星还有几十种活动,需要重复添加几十次,这时候使用动态代理就比较方便了,只需要添加一次就可以了。也就是上面说的,动态代理的优势在于可以很方便的对代理类的函数进行统一的处理,而不用修改每个代理类的函数。
2.2、动态代理的实现
框架如下
可以看到和静态对象相比,多了个代理类proxy和实现了InvocationHandler接口的实现类。
使用动态代理的步骤
- 创建一个实现接口InvocationHandler的类,它必须实现invoke方法
- 创建目标类以及接口
- 通过Proxy的静态方法newProxyInstance(ClassLoader loader, Class[] interfaces, InvocationHandler h)创建一个代理
- 通过代理调用方法
2.2.1、创建一个实现接口InvocationHandler的类
首先来看下这个接口
public interface InvocationHandler { Object invoke(Object proxy, Method method, Object[] args); }
从InvocationHandler这个名称我们就可以知道,实现了这个接口的中介类用做“调用处理器”。当我们调用代理类对象的方法时,这个“调用”会转送到invoke方法中,代理类对象作为proxy参数传入,参数method标识了我们具体调用的是代理类的哪个方法,args为这个方法的参数。这样一来,我们对代理类中的所有方法的调用都会变为对invoke的调用,这样我们可以在invoke方法中添加统一的处理逻辑。因此我们只需在中介类的invoke方法实现中添加前置处理和后置处理,在这里也就是面谈、签约、收尾款。
参数意义:
proxy:代理对象 (在这里也就是经纪人类)
method:需要实现代理的目标类的方法 (在这里也就是sing等方法)
args:为这个方法的参数 (在这里也就是sing方法的参数)
现在我们就可以写InvocationHandler的实现InvocationHandlerImpl
package com.pjf.proxy; import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; public class InvocationHandlerImpl implements InvocationHandler { //这个就是我们要代理的真实对象 private Object obj; //构造方法,给我们要代理的真实对象赋初值 public InvocationHandlerImpl(Object obj) { this.obj = obj; } public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { // 前置处理 System.out.println("ProxyStar.confer()"); // 面谈 System.out.println("ProxyStar.signContract()"); // 签合同 // 可以写个输出查看下调用的方法名是什么 System.out.println("Method:" + method); // 当代理对象调用真实对象的方法时,其会自动的跳转到代理对象关联的handler对象的invoke方法来进行调用 Object returnValue = method.invoke(obj, args); // 后置处理 System.out.println("ProxyStar.collectMoney()"); // 收尾款 return returnValue; } }
2.2.2、创建目标类以及接口
就是上面2.1节的Star接口和RealStar类,就不重复了
2.2.3、通过Proxy的静态方法newProxyInstance(ClassLoaderloader, Class[] interfaces, InvocationHandler h)创建一个代理和调用方法
首先来看下proxy类的静态方法
public static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h) throws IllegalArgumentException
通过调用Proxy类的newProxyInstance方法来获取一个代理类实例,返回后的代理类可以当作被目标类使用。这个代理类实现了我们指定的接口并且会把方法调用分发到指定的调用处理器,也就是上面我们实现的InvocationHandlerImpl类。
参数意义
loader:定义了代理类的ClassLoder,可百度ClassLoder是用来干嘛的
interfaces:定义了代理类实现的接口列表
h:调用处理器,也就是我们上面定义的实现了InvocationHandler接口的类实例
有兴趣可以看一下源代码,查看下newProxyInstance是怎么去调用InvocationHandlerImpl类的过程。
现在我们就可以使用newProxyInstance方法了
package com.pjf.proxy; import java.lang.reflect.InvocationHandler; import java.lang.reflect.Proxy; public class Test { public static void main(String[] args) { // 代理的真实对象 Star RealStar = new RealStar(); /** * InvocationHandlerImpl 实现了 InvocationHandler 接口,并能实现方法调用从代理类到委托类的分派转发 * 其内部通常包含指向委托类实例的引用,用于真正执行分派转发过来的方法调用. * 即:要代理哪个真实对象,就将该对象传进去,最后是通过该真实对象来调用其方法 */ InvocationHandler handler = new InvocationHandlerImpl(RealStar); ClassLoader loader = RealStar.getClass().getClassLoader(); Class[] interfaces = RealStar.getClass().getInterfaces(); /** * 该方法用于为指定类装载器、一组接口及调用处理器生成动态代理类实例 */ Star proxyStar = (Star) Proxy.newProxyInstance(loader, interfaces, handler); System.out.println("动态代理对象的类型:" + proxyStar.getClass().getName()); proxyStar.sing(); System.out.println(" "); proxyStar.variety(); System.out.println(" "); proxyStar.TVserial(); System.out.println(" "); proxyStar.commercialPerformance(); } }
2.2.4、测试结果
运行后可以看到结果如下
动态代理对象的类型:com.sun.proxy.$Proxy0 ProxyStar.confer() ProxyStar.signContract() Method:public abstract void com.pjf.proxy.Star.sing() RealStar.sing() ProxyStar.collectMoney() ProxyStar.confer() ProxyStar.signContract() Method:public abstract void com.pjf.proxy.Star.variety() RealStar.variety() ProxyStar.collectMoney() ProxyStar.confer() ProxyStar.signContract() Method:public abstract void com.pjf.proxy.Star.TVserial() RealStar.TVserial() ProxyStar.collectMoney() ProxyStar.confer() ProxyStar.signContract() Method:public abstract void com.pjf.proxy.Star.commercialPerformance() RealStar.commercialPerformance() ProxyStar.collectMoney()
可以看到动态代理已经生效了,可以发现我们首先是通过newProxyInstance方法获取代理类实例,而后我们便可以通过这个代理类实例调用代理类的方法,也就是通过调用中介类(调用处理器)的invoke方法,在invoke方法中我们调用委托类的相应方法,并且可以添加自己的前置处理和后置处理。相对于静态代理,我们只需要写一套处理逻辑。