代理模式 静态代理、JDK动态代理、Cglib动态代理
1 代理模式
使用代理模式时必须让代理类和被代理类实现相同的接口;
客户端通过代理类对象来调用被代理对象方法时,代理类对象会将所有方法的调用分派到被代理对象上进行反射执行;
在分派的过程中还可以添加前置通知和后置处理。
2 为什么需要使用代理
2.1 需求
现有一个HelloApi接口和一个HelloApi接口的实现类Hello,现在需要统计HelloApi接口中say方法的耗时
package demo_test02;/*** @author 王杨帅* @create 2018-08-09 19:20* @desc**/ public interface HelloApi {public void say();}
Hello.java
package demo_test02;/*** @author 王杨帅* @create 2018-08-09 19:18* @desc**/ public class Hello implements HelloApi {@Overridepublic void say() {System.out.println("Hello Fury");} }
Hello.java
2.2 思路
思路01 -> 在实现类中实现
思路02 -> 利用静态代理实现
思路03 -> 利用动态代理实现
2.3 解决
2.3.1 在实现类中实现
缺点:
》需要针对方法进行操作,如果有多个方法需要相同的逻辑时工作量巨大
》违背了开闭原则:开闭原则是对扩展开放,但是对修改关闭;为了增加功能把每个方法都修改了,不便于维护
》违背了单一职责原则:每个方法除了完成自己本身的功能,还要计算耗时
开闭原则 开闭原则(OCP)是面向对象设计中“可复用设计”的基石,是面向对象设计中最重要的原则之一,其它很多的设计原则都是实现开闭原则的一种手段。对于扩展是开放的,对于修改是关闭的,这意味着模块的行为是可以扩展的。当应用的需求改变时,我们可以对模块进行扩展,使其具有满足那些改变的新行为。也就是说,我们可以改变模块的功能。对模块行为进行扩展时,不必改动模块的源代码或者二进制代码。模块的二进制可执行版本,无论是可链接的库、DLL或者.EXE文件,都无需改动
开闭原则
单一职责原则 单一职责原则(SRP:Single responsibility principle)又称单一功能原则,面向对象五个基本原则(SOLID)之一。它规定一个类应该只有一个发生变化的原因。该原则由罗伯特·C·马丁(Robert C. Martin)于《敏捷软件开发:原则、模式和实践》一书中给出的。马丁表示此原则是基于汤姆·狄马克(Tom DeMarco)和Meilir Page-Jones的著作中的内聚性原则发展出的。 所谓职责是指类变化的原因。如果一个类有多于一个的动机被改变,那么这个类就具有多于一个的职责。而单一职责原则就是指一个类或者模块应该有且只有一个改变的原因。
单一职责原则
package demo_test02;/*** @author 王杨帅* @create 2018-08-09 19:18* @desc**/ public class Hello implements HelloApi {@Overridepublic void say() {Long start = System.currentTimeMillis();System.out.println("Hello Fury");Long end = System.currentTimeMillis();System.out.println("耗时为:" + (end - start));}}
HelloApi.java
package demo_test02;/*** @author 王杨帅* @create 2018-08-09 19:18* @desc**/ public class Test {public static void main(String[] args) {Hello hello = new Hello();// 01 传统调用方式 hello.say();// 02 静态调用方式 // StaticProxy staticProxy = new StaticProxy(hello); // staticProxy.say();// HelloApi proxy = (HelloApi)new DynamicProxy().getProxy(hello); // proxy.say(); }}
调用测试
2.3.2 利用静态代理实现
》修改HelloApi的实现类Hello,将实现类中say方法中的计算耗时逻辑去掉
》创建一个类,该类实现了HelloApi接口
》定义一个类型为Hello的成员变量target
》重写say方法,在say方法中利用target成员变量去调用say方法,并在调用你的前后可以增加逻辑处理
》》静态代理实现的缺点
》》》需要针对每一个被代理对象编写一个静态代理类,如果需要进行代理的对象较多,那么工作量将很巨大
》》》静态代理类实现了被代理对象所属类实现的接口,一旦接口发生改变,静态代理类也必须跟着发生变化
package demo_test02;/*** @author 王杨帅* @create 2018-08-09 19:18* @desc**/ public class Hello implements HelloApi {@Overridepublic void say() { // Long start = System.currentTimeMillis();System.out.println("Hello Fury"); // Long end = System.currentTimeMillis(); // System.out.println("耗时为:" + (end - start)); }}
Hello.java
package demo_test02;import java.util.concurrent.TimeUnit;/*** @author 王杨帅* @create 2018-08-09 19:21* @desc 静态代理类**/ public class StaticProxy implements HelloApi {/*** 被代理对象*/private Hello hello;public StaticProxy(Hello hello) {this.hello = hello;}@Overridepublic void say() {Long start = System.currentTimeMillis();hello.say();try {TimeUnit.SECONDS.sleep(1);} catch (InterruptedException e) {e.printStackTrace();}Long end = System.currentTimeMillis();System.out.println("耗时:" + (end - start));}public Hello getHello() {return hello;}public void setHello(Hello hello) {this.hello = hello;} }
StaticProxy.java
package demo_test02;/*** @author 王杨帅* @create 2018-08-09 19:18* @desc**/ public class Test {public static void main(String[] args) {Hello hello = new Hello();// 01 传统调用方式 // hello.say();// 02 静态调用方式StaticProxy staticProxy = new StaticProxy(hello);staticProxy.say();// HelloApi proxy = (HelloApi)new DynamicProxy().getProxy(hello); // proxy.say(); }}
静态代理测试
2.3.3 利用动态代理实现
》创建一个动态代理类,该类实现了InvocationHandler
》定义一个Object类型的target成员变量
》定义一个getProxy方法,该方法接收一个Object类型对象【就是被代理对象】;方法体中初始化target成员变量,并返回一个动态代理对象
技巧01:调用Proxy的newProxyInstance 去创建动态代理对象,该方法接收三个参数,分别是:
ClassLoader loader -> 被代理对象的类加载器【PS: 可以通过被加载对象的类类型去获取类加载器】
Class<?>[] interfaces -> 被代理对象实现的接口组成的数组【PS: 可以通过被加载对象的类类型去获取实现的接口】
InvocationHandler h -> 动态代理类实例【PS: 通过this传入自定义的动态代理类实例即可】
/*** Returns an instance of a proxy class for the specified interfaces* that dispatches method invocations to the specified invocation* handler.** <p>{@code Proxy.newProxyInstance} throws* {@code IllegalArgumentException} for the same reasons that* {@code Proxy.getProxyClass} does.** @param loader the class loader to define the proxy class* @param interfaces the list of interfaces for the proxy class* to implement* @param h the invocation handler to dispatch method invocations to* @return a proxy instance with the specified invocation handler of a* proxy class that is defined by the specified class loader* and that implements the specified interfaces* @throws IllegalArgumentException if any of the restrictions on the* parameters that may be passed to {@code getProxyClass}* are violated* @throws SecurityException if a security manager, <em>s</em>, is present* and any of the following conditions is met:* <ul>* <li> the given {@code loader} is {@code null} and* the caller's class loader is not {@code null} and the* invocation of {@link SecurityManager#checkPermission* s.checkPermission} with* {@code RuntimePermission("getClassLoader")} permission* denies access;</li>* <li> for each proxy interface, {@code intf},* the caller's class loader is not the same as or an* ancestor of the class loader for {@code intf} and* invocation of {@link SecurityManager#checkPackageAccess* s.checkPackageAccess()} denies access to {@code intf};</li>* <li> any of the given proxy interfaces is non-public and the* caller class is not in the same {@linkplain Package runtime package}* as the non-public interface and the invocation of* {@link SecurityManager#checkPermission s.checkPermission} with* {@code ReflectPermission("newProxyInPackage.{package name}")}* permission denies access.</li>* </ul>* @throws NullPointerException if the {@code interfaces} array* argument or any of its elements are {@code null}, or* if the invocation handler, {@code h}, is* {@code null}*/@CallerSensitivepublic static Object newProxyInstance(ClassLoader loader,Class<?>[] interfaces,InvocationHandler h)throws IllegalArgumentException{Objects.requireNonNull(h);final Class<?>[] intfs = interfaces.clone();final SecurityManager sm = System.getSecurityManager();if (sm != null) {checkProxyAccess(Reflection.getCallerClass(), loader, intfs);}/** Look up or generate the designated proxy class.*/Class<?> cl = getProxyClass0(loader, intfs);/** Invoke its constructor with the designated invocation handler.*/try {if (sm != null) {checkNewProxyPermission(Reflection.getCallerClass(), cl);}final Constructor<?> cons = cl.getConstructor(constructorParams);final InvocationHandler ih = h;if (!Modifier.isPublic(cl.getModifiers())) {AccessController.doPrivileged(new PrivilegedAction<Void>() {public Void run() {cons.setAccessible(true);return null;}});}return cons.newInstance(new Object[]{h});} catch (IllegalAccessException|InstantiationException e) {throw new InternalError(e.toString(), e);} catch (InvocationTargetException e) {Throwable t = e.getCause();if (t instanceof RuntimeException) {throw (RuntimeException) t;} else {throw new InternalError(t.toString(), t);}} catch (NoSuchMethodException e) {throw new InternalError(e.toString(), e);}}
View Code
》重写invoke方法,在方法体通过mehtod的invoke方法去调用被代理对象的方法【PS: 利用反射实现的】
》》坑01:获取到的动态代理类的类型是Ojbect类型,必须进行类型转换成对应的类型
》》动态代理类实现的好处
》》》自定义动态代理类,被代理对象所属类所实现的接口变动时代理类也无需变动,因为动态代理是通过反射的方式实现被代理对象所属类所实现的接口的
》》》符合开闭原则,通过代理实现新功能,没有侵入到原有代码,也不用破坏原来的代码结构,完美的扩展方案
package demo_test02;import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; import java.lang.reflect.Proxy; import java.util.concurrent.TimeUnit;/*** @author 王杨帅* @create 2018-08-09 19:26* @desc 动态代理类**/ public class DynamicProxy implements InvocationHandler {/*** 被代理对象*/private Object target;/*** 利用JDK的动态代理创建代理对象* @param target* @return*/public Object getProxy(Object target) {this.target = target;return Proxy.newProxyInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces(), this);}@Overridepublic Object invoke(Object proxy, Method method, Object[] args) throws Throwable {Long start = System.currentTimeMillis();Object result = method.invoke(target, args);TimeUnit.SECONDS.sleep(1);Long end = System.currentTimeMillis();System.out.println("花费的时间为:" + (end - start));return result;}public Object getTarget() {return target;}public void setTarget(Object target) {this.target = target;} }
DynamicProxy.java
package demo_test02;/*** @author 王杨帅* @create 2018-08-09 19:18* @desc**/ public class Test {public static void main(String[] args) {Hello hello = new Hello();// 01 传统调用方式 // hello.say();// 02 静态调用方式 // StaticProxy staticProxy = new StaticProxy(hello); // staticProxy.say(); HelloApi proxy = (HelloApi)new DynamicProxy().getProxy(hello);proxy.say();}}
调用测试
3 动态代理
3.1 动态代理类编程步骤
》通过实现InvocationHandler接口来自定义自己的InvocationHandler;
3.2 繁琐方法
package demo_test02;import java.lang.reflect.*;/*** @author 王杨帅* @create 2018-08-09 19:59* @desc**/ public class Test02 {public static void main(String[] args) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {StudentImpl student = new StudentImpl();/*** 00 实现InvocationHandler来创建自己的动态代理类* 01 获取动态代理类的类类型* 02 获取动态代理类的构造器* 03 创建动态代理类的实例* 04 利用动态代理对象去调用被代理对象的方法*/Class<?> studentProxyClass = Proxy.getProxyClass(Student.class.getClassLoader(), Student.class);Constructor<?> studentProxyClassConstructor = studentProxyClass.getConstructor(InvocationHandler.class);Student studentProxy = (Student)studentProxyClassConstructor.newInstance(new StudentDynamicProxy(student));studentProxy.study();}interface Student {void study();}static class StudentImpl implements Student {@Overridepublic void study() {System.out.println("I am studying dynamic proxy of java.");}}/*** 自定义动态代理类*/static class StudentDynamicProxy implements InvocationHandler {/*** 被代理对象*/private Object target;public StudentDynamicProxy(Object target) {this.target = target;}@Overridepublic Object invoke(Object proxy, Method method, Object[] args) throws Throwable {System.out.println("前置处理");Object result = method.invoke(target, args);System.out.println("后置处理");return result;}}}
View Code
3.3 简便方法
package demo_test02;import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; import java.lang.reflect.Proxy;/*** @author 王杨帅* @create 2018-08-09 20:13* @desc**/ public class Test03 {public static void main(String[] args) {StudentImpl student = new StudentImpl();Student studentProxy = (Student)new StudentDynamicProxy().getDynamicProxyObject(student);studentProxy.hello();}interface Student {void hello();}static class StudentImpl implements Student {@Overridepublic void hello() {System.out.println("你好,王杨帅。");}}static class StudentDynamicProxy implements InvocationHandler {private Object target;public Object getDynamicProxyObject(Object target) {this.target = target;return Proxy.newProxyInstance(target.getClass().getClassLoader(),target.getClass().getInterfaces(),this);}@Overridepublic Object invoke(Object proxy, Method method, Object[] args) throws Throwable {System.out.println("前置处理");Object result = method.invoke(target, args);System.out.println("后置处理");return result;}}}
View Code
3.4 参考博文
01 02 03
4 代理模式
4.1 静态代理
技巧01:静态代理的目标者和代理者都需要实现相同的接口
》接口:代理者和目标者需要实现的接口
package cn.xinagxu.static_proxy;/*** @author 王杨帅* @create 2018-08-19 20:27* @desc**/ public interface ISservice {String someMethod01();void someMethod02(); }
ISservice.java
》目标者:被代理对象
package cn.xinagxu.static_proxy;/*** @author 王杨帅* @create 2018-08-19 20:25* @desc**/ public class SomeService implements ISservice {public String someMethod01() {return "someMethod01";}public void someMethod02() {System.out.println("someMethod02");}}
SomeService.java
》代理者:代理目标者需要用到的代理类
package cn.xinagxu.static_proxy;/*** @author 王杨帅* @create 2018-08-19 20:26* @desc**/ public class StaticProxy implements ISservice {SomeService target;public StaticProxy(SomeService target) {this.target = target;}public String someMethod01() {String result = target.someMethod01();return result.toUpperCase();}public void someMethod02() {target.someMethod02();} }
StaticProxy.java
》测试类
package cn.xinagxu.static_proxy;/*** @author 王杨帅* @create 2018-08-19 20:32* @desc**/ public class StaticProxtTest {public static void main(String[] args) {SomeService target = new SomeService();StaticProxy proxy = new StaticProxy(target);String result = proxy.someMethod01();System.out.println(result);proxy.someMethod02();}}
StaticProxtTest.java
4.2 JDK动态代理
技巧01:jdk动态代理不需要自己编写代理类,因为时直接使用Proxy类来创建代理对象的;目标者的类任然需要实现一个接口
》接口:目标者需要实现的接口
package cn.xinagxu.dynamic_proxy;/*** @author 王杨帅* @create 2018-08-19 20:27* @desc**/ public interface ISservice {String someMethod01();void someMethod02(); }
ISservice.java
》目标者
package cn.xinagxu.dynamic_proxy;/*** @author 王杨帅* @create 2018-08-19 20:25* @desc**/ public class SomeService implements ISservice {public String someMethod01() {return "someMethod01";}public void someMethod02() {System.out.println("someMethod02");}}
SomeService.java
》测试类
package cn.xinagxu.dynamic_proxy;import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; import java.lang.reflect.Proxy;/*** @author 王杨帅* @create 2018-08-19 20:40* @desc**/ public class DynamicProxyTest {public static void main(String[] args) {final SomeService target = new SomeService();ISservice proxy = (ISservice) Proxy.newProxyInstance(target.getClass().getClassLoader(),target.getClass().getInterfaces(),new InvocationHandler() {public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {if ("someMethod01".equals(method.getName())) {String str = (String) method.invoke(target, args);return str.toUpperCase();} else {Object result = method.invoke(target, args);return result;}}});System.out.println(proxy.someMethod01());proxy.someMethod02();}}
DynamicProxyTest.java
4.3 Cglib动态代理
技巧01:Cglib动态代理不需要接口,借助 Enhancer 类来创建代理对象,借助 MethodInterceptor 接口的 intercept 方法来实现增强逻辑
技巧02:Enhancer 就相当于JDK动态代理中的Proxy,MethodInterceptor 接口的 intercept 方法就相当于JDK动态代理中 InvocationHandler 中的 invoke 方法
技巧03:使用Cglib动态代理时需要引入外部依赖cglib
<!-- https://mvnrepository.com/artifact/cglib/cglib --><dependency><groupId>cglib</groupId><artifactId>cglib</artifactId><version>3.2.7</version></dependency>
》目标者
package cn.xinagxu.cglib_proxy;/*** @author 王杨帅* @create 2018-08-19 20:25* @desc**/ public class SomeService {public String someMethod01() {return "someMethod01";}public void someMethod02() {System.out.println("someMethod02");}}
SomeService.java
》代理类工厂:用于创建代理对象的【PS: 可以不需要的,直接利用内部类实现】
package cn.xinagxu.cglib_proxy;import net.sf.cglib.proxy.Enhancer; import net.sf.cglib.proxy.MethodInterceptor; import net.sf.cglib.proxy.MethodProxy;import javax.xml.ws.Service; import java.lang.reflect.Method;/*** @author 王杨帅* @create 2018-08-19 21:03* @desc**/ public class CglibFactory implements MethodInterceptor {private SomeService target;public CglibFactory(SomeService someService) {this.target = someService;}public CglibFactory() {}// 创建代理对象的方法public SomeService createProxy() {// 01 创建一个增强器Enhancer enhancer = new Enhancer();// 02 设置增强方 enhancer.setSuperclass(target.getClass());// 03 接口回调方法enhancer.setCallback(this);// 04 返回代理对象return (SomeService) enhancer.create();}// 跟JDK动态代理的invoke方法差不多public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {if ("someMethod01".equals(method.getName())) {String str = (String) method.invoke(target, objects);return str.toUpperCase();} else {Object result = method.invoke(target, objects);return result;}}}
CglibFactory.java
》测试类
package cn.xinagxu.cglib_proxy;/*** @author 王杨帅* @create 2018-08-19 21:12* @desc**/ public class CglibProxyTest {public static void main(String[] args) {SomeService target = new SomeService();// 创建代理对象SomeService proxy = new CglibFactory(target).createProxy();System.out.println(proxy.someMethod01());proxy.someMethod02();}}
CglibProxyTest.java
4.4 三种代理模的总结
4.4.1 静态代理
原材料:目标者、代理者、接口
特点:迅速、目标者和代理者需要实现同一个接口
比喻:
目标者 -> 公司
代理者 -> 法律顾问
4.4.2 JDK动态代理
原材料:目标者、接口
特点:只需要目标者实现接口、目标者必须是final类型、代理者的类型必须是接口类型
目标者 -> 个人
代理者 -> 事务所
4.4.3 Cglib动态代理
原材料:目标者、cglib依赖
特点:只需要目标者(目标者类型可以是类也可以是接口)、目标者不能是final类型、需要引入cglib依赖
目标者 -> 个人
代理者 -> 儿子
4.4.4 三种代理方式源代码
点击前往
转载于:https://www.cnblogs.com/NeverCtrl-C/p/9451673.html
代理模式 静态代理、JDK动态代理、Cglib动态代理相关推荐
- 代理模式——静态代理,动态代理(JDK代理和CGLib代理)
概述 由于某些原因需要给某对象提供一个代理以控制对该对象的访问. 这时,访问对象不适合或者不能直接引用目标对象,代理对象作为访问对象和目标对象之间的中介. Java中的代理按照代理类生成时机不同又分为 ...
- 第六周 Java语法总结_设计原则_工厂模式_单例模式_代理模式(静态代理_动态代理)_递归_IO流_网络编程(UDP_TCP)_反射_数据库
文章目录 20.设计原则 1.工厂模式 2.单例模式 1)饿汉式 2)懒汉式 3.Runtime类 4.代理模式 1)静态代理 2)动态代理 动态代理模板 21.递归 22.IO流 1.File 2. ...
- 【Android 插件化】Hook 插件化框架 ( Hook 技术 | 代理模式 | 静态代理 | 动态代理 )
Android 插件化系列文章目录 [Android 插件化]插件化简介 ( 组件化与插件化 ) [Android 插件化]插件化原理 ( JVM 内存数据 | 类加载流程 ) [Android 插件 ...
- [设计模式] - 代理模式(静态代理与动态代理)
文章目录 一.代理模式简介 1. 什么是代理模式 2. 简单举例 二.代理模式的设计思路 1. 代理模式的构成 1. 静态代理 2. 动态代理 (1)接口代理 (2)Cglib代理 三. 代理模式总结 ...
- Java代理模式/静态代理/动态代理
代理模式:即Proxy Pattern,常用的设计模式之一.代理模式的主要作用是为其他对象提供一种代理以控制对这个对象的访问. 代理概念 :为某个对象提供一个代理,以控制对这个对象的访问. 代理类和委 ...
- Java代理模式——静态代理动态代理
proxy mode 1. 什么是代理 1.1 例子解释 1.2 作用 2. 静态代理 2.1 优缺点分析 2.2 以厂家卖u盘用代码说明 3. 动态代理 3.1 什么是动态代理 3.2 jdk实现原 ...
- Android代理模式(静态代理,动态代理,Retrofit代理模式分析)
文章目录 代理模式 前言:AOP编程(面向切面编程) 一. 代理思想 1. 静态代理 2. 动态代理 3. 动态代理的实现 二. Retrofit代理模式分析 代理模式 前言:AOP编程(面向切面编程 ...
- 外观模式和代理模式的联系和区别_设计模式之代理设计模式
原文首发于微信公众号:jzman-blog,欢迎关注交流! 今天来看一下什么是代理设计模式,顾名思义,代理就是通过中介代替某人做某一件事,对应到我们的程序中就是通过代理对象来控制某个具体对象某个方法的 ...
- 外观模式和代理模式的联系和区别_设计模式之代理模式
代理模式 Proxy Intro 代理模式,给某一个对象提供一个代理对象,并由代理对象控制对原对象的引用. 在某些情况下,一个对象不适合或者不能直接引用另一个对象,而代理对象可以在客户端和目标对象之间 ...
最新文章
- 模式实例之——外观实例
- docker部署springboot应用
- 王之泰201771010131《面向对象程序设计(java)》第九周学习总结
- alert闪一下就没了_尾部贯穿式镀铬银饰条除了丑,还能闪瞎眼
- workstation虚拟机详尽教程
- TensorFlow从入门到理解(六):可视化梯度下降
- Linux父进程如何发信号给子进程,关于C#:fork和signal:如何将信号从父进程发送到特定的子进程...
- python读取txt文件数据并存到list中
- Microsoft DirectX组件v11.0完整版更新啦
- html5库存管理,库存管理的基本方法
- web前端课程设计源码大全(HTML+CSS+JS)
- HTML:设置背景颜色和图片
- 使用内核模块添加系统调用
- 几款常见的可视化HTML编辑器 WYSIWYG
- 音乐播放器android-1.0
- L. Spicy Restaurant(多源BFS+递推)
- Python 学习入门(5)—— 发送邮件
- Graphviz - 生成smc 的.sm文件对应的工作流程图
- 如何开发优秀的新浪微博应用
- win10台式电脑dns服务器未响应,如何解决Win10系统DNS服务器未响应