—-慢慢来比较快,虚心学技术—-
概念
为其他对象提供一种代理以控制对这个对象的访问。在某些情况下,一个对象不适合或者不能直接引用另一个对象,而代理对象可以在客户端和目标对象之间起到中介的作用。
推演
需求**:实现计算器简单的运算**
/*** 计算器*/class Calculator{/*** 加*/public int add(int a,int b){return a+b;}/*** 减*/public int sub(int a,int b) {return a-b;}/*** 乘*/public int mult(int a,int b){return a*b;}/*** 除*/public int div(int a,int b){return a/b;}}
反例1
在每个方法调用时执行逻辑之前和之后进行日志输出
class Calculator{/*** 加*/public int add(int a,int b){System.out.println("调用了add方法,参数是:a="+a+",b="+b);int sum = a+b;System.out.println("调用结束,返回结果是:"+sum);return sum;}/*** 减*/public int sub(int a,int b) {System.out.println("调用了sub方法,参数是:a="+a+",b="+b);int t = a-b;System.out.println("调用结束,返回结果是:"+t);return t;}/*** 乘*/public int mult(int a,int b){System.out.println("调用了mult方法,参数是:a="+a+",b="+b);int t = a*b;System.out.println("调用结束,返回结果是:"+t);return t;}/*** 除*/public int div(int a,int b){System.out.println("调用了div方法,参数是:a="+a+",b="+b);int t = a/b;System.out.println("调用结束,返回结果是:"+t);return t;}}
如调用add方法:
Calculator calculator = new Calculator();calculator.add(1, 2);
输出如下:
调用了add方法,参数是:a=1,b=2调用结束,返回结果是:3
我们都知道,这种做法实际上并不合理,第一个是代码冗余重复,第二个是非业务逻辑和业务逻辑耦合严重,此时如果新增了一个方法,依旧要写这么多内容
反例2
利用模板方法模式,抽离共同内容
class Calculator{/*** 执行前输出* @param methodName 方法名* @param params 参数列表*/private void begin(String methodName,Object... params){System.out.println("调用了"+methodName+"方法,参数是:"+ Arrays.toString(params));}/*** 执行后输出* @param methodName 方法名* @param re 返回值*/private void end(String methodName,Object re){System.out.println("调用"+methodName+"方法结束,返回结果是:"+re);}/*** 加*/public int add(int a,int b){begin("add", a,b);int sum = a+b;end("add", sum);return sum;}/*** 减*/public int sub(int a,int b) {begin("sub", a,b);int t = a-b;end("sub", t);return t;}/*** 乘*/public int mult(int a,int b){begin("mult", a,b);int t = a*b;end("mult", t);return t;}/*** 除*/public int div(int a,int b){begin("div", a,b);int t = a/b;end("div", t);return t;}}
此时调用add方法:
Calculator calculator = new Calculator();calculator.add(1, 2);
输出如下:
调用了add方法,参数是:[1, 2]调用add方法结束,返回结果是:3
此方法确实提高了代码重用性,但是依旧避免不了非业务代码和业务代码的耦合,不是我们想要的
正例(代理模式)
我们可以借助类的加载原理,动态代理Caculator的方法,也就是通过代理类调用Caculator方法,而不是直接执行Caculator的方法
首先改造Caculator为接口,因为动态代理只能代理接口**
interface Calculator{int add(int a,int b);int sub(int a,int b);int mult(int a,int b);int div(int a,int b);}/*** 计算器*/class CalculatorImpl implements Calculator{/*** 加*/@Overridepublic int add(int a,int b){int sum = a+b;return sum;}/*** 减*/@Overridepublic int sub(int a,int b) {int t = a-b;return t;}/*** 乘*/@Overridepublic int mult(int a,int b){int t = a*b;return t;}/*** 除*/@Overridepublic int div(int a,int b){int t = a/b;return t;}}
获取代理对象,其中的Positive是当前运行的对象
//实例化一个对象Calculator calculator = new CalculatorImpl();//获取代理对象//Proxy.newProxyInstance(ClassLoader loader,Class<?>[] interfaces,InvocationHandler h)Calculator proxyCaculator = (Calculator) Proxy.newProxyInstance(Positive.class.getClassLoader(), new Class[]{Calculator.class}, new MyHandler(calculator));
其中的MyHandler是一个处理类,实现InvocationHandler 接口,只有一个invoke方法,用于说明调用代理对象方法时执行的操作:
class MyHandler implements InvocationHandler{private Calculator calculator;public MyHandler(Calculator calculator){this.calculator = calculator;}//所有调用代理对象的方法都会进入这个方法,而不是对应的方法@Overridepublic Object invoke(Object proxy, Method method, Object[] args) throws Throwable {System.out.println("调用了"+method.getName()+"方法,参数是:"+ Arrays.toString(args));Object re = method.invoke(calculator, args);System.out.println("调用"+method.getName()+"方法结束,返回结果是:"+re);return re;}}
此时通过代理类调用目标类的方法:
proxyCaculator.add(1, 2);
输出如下:
调用了add方法,参数是:[1, 2]调用add方法结束,返回结果是:3
关键点:**通过代理类调用目标类的方法**
应用场景
需要对类中的方法进行批量处理时
需要对类中的方法进行隐藏细节时
如:Spring AOP
优点
可以扩展目标对象的功能且不用改变目标对象代码,符合开闭原则
将客户端与目标对象份力,一定程度解耦
作为中介代理目标对象,一定程度保护了目标对象
缺点
增加系统复杂度
减缓访问速度
如有贻误,还请评论指正
