JAVA的代理(proxy)模式
代理(proxy)模式: 是一种设计模式, 提供了间接对目标对象进行访问的方式. 即通过代理对象访问目标对象.这样做的好处是:可以在目标对象实现的功能上,增加额外的功能补充,即扩展目标对象的功能. 有的时候也可以使用代理模式, 在不修改他人代码的情况下, 增强功能(对方法进行扩展)
举例: 老板的助理, 明星的经纪人等, 这里的助理和经纪人都可以看做是代理对象, 而老板和明星就是目标对象, 别人通过联系助理和经纪人可以达到相同的目的, 通俗一点的解释就是在操作一个目标对象或对象中的方法时,不是直接操作这个对象,而是通过一个代理对象来操作这个实际的目标对象
关键词: 目标对象, 代理对象. (代理对象就是对目标对象的扩展, 并会调用目标对象)
图示:
代理分类: 静态代理 和 动态代理以及Cglib代理
注意: 接下来所有的代理模式示例都将以CURD操作为基础, 只是演示代理模式的方式, 不做真正的数据库操作!!!
1.静态代理
说明: 静态代理在使用的时候, 需要定义目标接口,被代理对象(子类, 也就是目标对象)与代理对象都需要一起实现相同的这个目标接口.
代码示例:
目标接口: ICurd.java
package demo.java.jiangkd.design_pattern.proxy.staticProxy.demo; /** * 定义目标接口 * * @author jiangkd * @date 2020/06/04 */ public interface ICurd { void add(); void delete(); void update(); void find(); }
目标对象: CurdImpl.java
package demo.java.jiangkd.design_pattern.proxy.staticProxy.demo; /** * 目标对象, 实现父接口 * * @author jiangkd * @date 2020/06/04 */ public class CurdImpl implements ICurd { @Override public void add() { System.out.println("CurdImpl执行了add操作"); } @Override public void delete() { System.out.println("CurdImpl执行了delete操作"); } @Override public void update() { System.out.println("CurdImpl执行了update操作"); } @Override public void find() { System.out.println("CurdImpl执行了find操作"); } }
代理对象: Proxy.java
package demo.java.jiangkd.design_pattern.proxy.staticProxy.demo; /** * 定义代理对象 * * @author jiangkd * @date 2020/06/04 */ public class Proxy implements ICurd { private ICurd curd; public Proxy(ICurd curd) { this.curd = curd; } @Override public void add() { // 扩展, 执行目标对象之前的行为 System.out.println("执行add操作之前 ..."); // 执行真正的目标对象的方法 curd.add(); // 扩展, 执行目标对象之后的行为 System.out.println("执行add操作之后 ..."); } @Override public void delete() { // 类似add方法 } @Override public void update() { // 类似add方法 } @Override public void find() { // 类似add方法 } }
测试类: ProxyTest.java
package demo.java.jiangkd.design_pattern.proxy.staticProxy.demo; import org.junit.Test; public class ProxyTest { @Test public void test() { // 创建目标对象 CurdImpl curdImpl = new CurdImpl(); // 创建代理对象, 代理目标对象 Proxy proxy = new Proxy(curdImpl); // 执行代理对象的add方法 proxy.add(); } }
结果输出:
执行add操作之前 ...
CurdImpl执行了add操作
执行add操作之后 ...
静态代理总结:
1. 可以做到在不修改目标对象的功能前提下,对目标功能扩展.(执行目标对象方法的前后的输出就是扩展, 可以改为其他方法的执行, 这里为了演示只是输出)
2. 缺点: 因为代理对象需要与目标对象实现一样的接口, 所以如果都采用这种方式, 久而久之会发现有很多的代理类, 很繁琐, 加入你有两个目标接口, 都需要代理, 那么你就会产生两个对应的目标对象和代理对象, 如果是20个呢, 简直要崩溃呀!!! 而且一旦目标接口改变方法, 例如添加新的方法, 那么目标对象和代理对象都需要相应的进行添加方法, 维护麻烦, 所以, 解决这个问题的方案就是动态代理!!!
2.动态代理
动态代理特点:
1. 代理对象不需要实现目标接口
2. 代理对象的生成,是利用JDK的API,动态的在内存中构建代理对象(需要我们指定创建代理对象/目标对象实现的接口的类型)
3. 动态代理也叫作JDK动态代理
JDK动态代理了解:
1. 代理类所在包:java.lang.reflect.Proxy
2. JDK实现代理只需要使用Proxy的静态方法newProxyInstance方法,但是该方法需要接收三个参数,完整的写法是:
static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces,InvocationHandler h )
接收的三个参数依次为:
ClassLoader loader
: 指定当前目标对象使用类加载器,获取加载器的方法是固定的
Class<?>[] interfaces: 目标对象实现的接口的类型,使用泛型方式确认类型
InvocationHandler h: 事件处理,执行目标对象的方法时,会触发事件处理器的方法,会把当前执行目标对象的方法作为参数传入
代码示例:
目标接口和目标对象依然同上面的静态代理, 这里不再展示, 不同是这里的代理对象不需要定义类来实现目标接口, 而是实现
JDK的代理类接口MyInvocationHandler中的invoke方法来动态的生成代理对象
代理对象: MyInvocationHandler.java, 实现JDK的代理类接口
package demo.java.jiangkd.design_pattern.proxy.dynamicProxy.jdk.demo; import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; /** * 实现jdk自带的动态代理类接口并重写invoke方法 * * @author jiangkd * @date 2020/06/04 */ public class MyInvocationHandler implements InvocationHandler { /** * 持有需要被代理的真实目标对象 */ private Object obj; public MyInvocationHandler(Object obj) { this.obj = obj; } @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { // 扩展:执行代理之前的操作 System.out.println("代理之前 ...");
// 注意: 这里传入的参数是持有的目标对象obj和参数args Object invoke = method.invoke(obj, args); // 扩展:执行代理之后的操作 System.out.println("代理之后 ..."); return invoke; } }
测试: ProxyTest.java
package demo.java.jiangkd.design_pattern.proxy.dynamicProxy.jdk.demo; import java.lang.reflect.Proxy; import org.junit.Test; public class ProxyTest { @Test public void test() { // 定义目标对象 CurdImpl curdImpl = new CurdImpl(); // 执行静态方法获取代理对象, 注意三个参数的获取方式, 也有其他写法, 但是变的都是表面而已, 实际类型都是一样的 ICurd curd = (ICurd)Proxy.newProxyInstance(curdImpl.getClass().getClassLoader(), curdImpl.getClass().getInterfaces(), new MyInvocationHandler(curdImpl)); // curd.add(); } }
测试结果输出:
同静态代理输出一样(懒得贴了)
总结:
代理对象不需要实现接口而是执行静态方法newProxyInstance来获取的, 但是目标对象一定要实现接口,否则不能用动态代理
3.Cglig代理
上面的静态代理和动态代理模式都是要求目标对象实现一个接口(所谓的目标接口), 但是有时候目标对象只是一个单独的对象, 并没有实现任何的接口, 这个时候就可以使用以目标对象子类的方式类实现代理,这种方法就叫做:Cglib代理
Cglib代理,也叫作子类代理,它是在内存中构建一个子类对象从而实现对目标对象功能的扩展. Cglib是一个强大的高性能的代码生成包,它可以在运行期扩展java类与实现java接口.它广泛的被许多AOP的框架使用,例如Spring AOP和synaop,为他们提供方法的interception(拦截)
Cglib子类代理实现方法:
1.需要引入cglib的jar文件,但是如果是Spring项目, 那么核心包中已经包括了Cglib功能,所以直接引入spring-core-3.2.5.jar
即可. 否则需要cglib-nodep-3.2.4.jar(版本自己视情况而定)
2.引入功能包后,就可以在内存中动态构建子类
3.目标对象的方法如果为final/static,那么就不会被代理(本人测试过static方法, 如果采用代理对象调用static方法, 会报出警告, 推荐使用类直接调用, 而且执行了也不会执行代理扩展的方法, 只会执行自身的方法)
4. 需要一个代理类, 实现接口MethodInterceptor中的invoke方法, 然后还需要有一个生成代理对象的方法
代码示例:
没有目标接口, 只是一个类, 作为目标对象
package demo.java.jiangkd.design_pattern.proxy.dynamicProxy.cglib.demo; /** * 单纯的一个类, 作为目标对象 * * @author jiangkd * @date 2020/06/04 */ public class CurdService { public void add() { System.out.println("CurdService -> add"); } public String delete() { System.out.println("CurdService -> delete"); return "delete"; } public static void update() { System.out.println("CurdService -> update"); } }
代理类: MyMethodInterceptor.java 需要实现MethodInterceptor接口中的invoke方法
package demo.java.jiangkd.design_pattern.proxy.dynamicProxy.cglib.demo; import java.lang.reflect.Method; import net.sf.cglib.proxy.Enhancer; import net.sf.cglib.proxy.MethodInterceptor; import net.sf.cglib.proxy.MethodProxy; /** * cglib代理类, 用来获取代理对象 * * @author jiangkd * @date 2020/06/04 */ public class MyMethodInterceptor implements MethodInterceptor { /** * 持有目标对象 */ private Object obj; public MyMethodInterceptor(Object obj) { this.obj = obj; } @Override public Object intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy) throws Throwable { // System.out.println("before ..."); Object invoke = method.invoke(obj, args); System.out.println("返回:" + invoke); System.out.println("after ..."); return invoke; } /** * 创建代理对象的方法 * * @return */ public Object getProxyObj() { // Enhancer enhancer = new Enhancer(); // 设置父类, 就是目标对象类 enhancer.setSuperclass(obj.getClass()); // 设置回调, 就是当前代理类 enhancer.setCallback(this); // 创建代理对象 return enhancer.create(); } }
测试类: ProxyTest.java
package demo.java.jiangkd.design_pattern.proxy.dynamicProxy.cglib.demo; import org.junit.Test; public class ProxyTest { @Test public void test() { // 目标对象 CurdService curdService = new CurdService(); // 获取代理对象 CurdService proxyObj = (CurdService)new MyMethodInterceptor(curdService).getProxyObj(); // proxyObj.add(); // 静态方法, 不会通过代理, 报出警告, 推荐下面的写法 proxyObj.update(); // 直接正常调用, 不使用代理 CurdService.update(); } }
测试输出:
不妨自己写一个代理, 测试一下看看啊(好吧, 其实就是我懒得贴了)