Spring第七章:代理
1.代理模式
1.1 设计模式:前人总结的一套解决特定问题的代码.
1.2 代理设计模式优点:
1.2.1 保护真实对象
1.2.2 让真实对象职责更明确
1.2.3 利于扩展
1.3. 代理设计模式三要素
1.3.1 真实对象(老总)
1.3.2 代理对象(秘书)
1.3.3 抽象对象(抽象功能)
以下是代理模式的一个需求:
假设我们现在需要实现一个功能:你需要去找老总约个时间吃饭。那么无代理的情况就是,你到了公司,看到老总那么直接就约吃饭。
那么什么是代理呢,就是现在公司多了一个前台的秘书,你没办法直接找到老总了,你必须先和秘书说,秘书帮你和老板约吃饭。这个过程就是代理。
2 静态代理 由代理对象代理所有真实对象的功能.
2.1 自己编写代理类
2.2 每个代理的功能需要单独编写
2.3 静态代理设计模式的缺点:
2.3.1 当代理功能比较多时,代理类中方法需要写很多.
2.4 静态代理代码
首先我们需要定义个功能类,也就是抽象的接口
package com.suncl.proxy; public interface Gongneng{ //制定一个小目标 void zhidingxiaomubiao(); //吃饭 void chifan(); }
然后我们需要定义一个真实的被代理对象,去完成真实需要做的动作。这里就是老总,需要真实的完成吃饭的动作。
package com.suncl.proxy; public class Laozong implements Gongneng { private String name; public String getName() { return name; } public void setName(String name) { this.name = name; } public Laozong(String name) { super(); this.name = name; } public Laozong() { super(); } public void zhidingxiaomubiao(){ System.out.println("制定小目标"); } @Override public void chifan() { System.out.println("老总吃饭"); } }
之后就是最重点的代理类,也就是秘书了。首先需要实现功能类的接口,并且组合一个真实的代理对象。当调用秘书的代理类接口的时候,秘书再调用真实代理对象的代理方法,完成代理操作。
package com.suncl.proxy; public class MiShu implements Gongneng{ private Laozong laozong = new Laozong("云云"); @Override public void zhidingxiaomubiao() { System.out.println("约定时间"); laozong.zhidingxiaomubiao(); System.out.println("把访客信息备注"); } @Override public void chifan() { System.out.println("约定时间"); laozong.chifan(); System.out.println("把访客信息备注"); } }
然后使用的时候就只需要访问秘书这个代理类就可以完成和老总的吃饭动作了,这就是静态代理模式。
package com.suncl.proxy; public class Women { public static void main(String[] args) { //找秘书 MiShu mishu = new MiShu(); //约定制定小目标 mishu.zhidingxiaomubiao(); //约定吃饭 mishu.chifan(); } }
这里扩展下静态代理的弊端,就是代理的目标每多一个方法,那么接口和代理类相应的都需要多一个方法。之后的代码我又扩展了一个运动的方法,那么代理类就需要多一个运动的代理实现。很不方便。
3 动态代理-jdk
3.1和 cglib 动态代理对比
3.1.1 优点:jdk 自带,不需要额外导入 jar
3.1.2 缺点:
3.1.2.1 真实对象必须实现接口
3.1.2.2 利用反射机制.效率不高
动态代理的源码:
第一个功能类,抽象接口
package com.suncl.jdkProxy; public interface Gongneng { void chifan(); void mubiao(); void yundong(); }
第二个是真实实现类,老总
package com.suncl.jdkProxy; public class Laozong implements Gongneng{ @Override public void chifan() { System.out.println("吃饭"); } @Override public void mubiao() { System.out.println("目标"); } @Override public void yundong() { System.out.println("老总运动"); } }
第三个就是代理类了,这里的代理类就不需要实现接口了。主要需要实现InvocationHandle ,之后实现invoke方法就可以了。
package com.suncl.jdkProxy; import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; /** * Created by SCL-PC on 2019/3/3. */ public class DynamicProxyHandler implements InvocationHandler { Laozong laozong = new Laozong(); @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { System.out.println("预约时间"); Object result = method.invoke(laozong, args); System.out.println("记录访客信息"); return result; } }
使用类,使用的时候使用Proxy.newProxyInstance就可以完成基于JDK的动态代理。
import java.lang.reflect.Proxy; public class Women { public static void main(String[] args) { //第一个参数:反射时使用的类加载器 //第二个参数:Proxy需要实现什么接口 //第三个参数:通过接口对象调用方法时,需要调用哪个类的invoke方法 Gongneng gongneng = (Gongneng) Proxy.newProxyInstance(Women.class.getClassLoader(), new Class[]{Gongneng.class}, new DynamicProxyHandler()); gongneng.chifan(); gongneng.mubiao(); gongneng.yundong(); } }
4 动态代理-cglib
4.1cglib 优点:
4.1.1 基于字节码,生成真实对象的子类.
4.1.1 运行效率高于 JDK 动态代理.
4.2. cglib 缺点:
4.2.1 非 JDK 功能,需要额外导入 jar
4.3使用 spring aop 时,只要出现 Proxy 和真实对象转换异常可以设置<aop:aspectj-autoproxy proxy-target-class="true"></aop:aspectj-autoproxy>
4.3.1 设置为 true 使用 cglib
4.3.2 设置为 false 使用 jdk(默认值)
cglib代码
功能类(不变):
package com.suncl.cglibProxy; public interface Gongneng { void zhidingxiaomubiao(); void chifan(); void yundong(); }
真实实现类(不变):
package com.suncl.cglibProxy; public class Laozong implements Gongneng { @Override public void zhidingxiaomubiao() { System.out.println("目标"); } public void chifan() { System.out.println("吃饭"); } public void yundong() { System.out.println("运动"); } }
代理类:有变化需要实现 net.sf.cglib.proxy.MethodInterceptor; 并实现 intercept方法
package com.suncl.cglibProxy; import net.sf.cglib.proxy.Enhancer; import net.sf.cglib.proxy.MethodInterceptor; import net.sf.cglib.proxy.MethodProxy; import java.lang.reflect.Method; /** * Created by SCL-PC on 2019/3/3. */ public class MishuCglib implements MethodInterceptor { private Laozong laozong = new Laozong(); //相当于JDK动态代理中的绑定 public Object getInstance() { Enhancer enhancer = new Enhancer(); //创建加强器,用来创建动态代理类 enhancer.setSuperclass(laozong.getClass()); //为加强器指定要代理的业务类(即:为下面生成的代理类指定父类) //设置回调:对于代理类上所有方法的调用,都会调用CallBack,而Callback则需要实现intercept()方法进行拦 enhancer.setCallback(this); // 创建动态代理类对象并返回 return enhancer.create(); } // 实现回调方法 public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable { System.out.println("预处理——————"); proxy.invokeSuper(obj, args); //调用业务类(父类中)的方法 System.out.println("调用后操作——————"); return null; } }
代理使用(和静态代理没有区别):
package com.suncl.cglibProxy; import net.sf.cglib.proxy.Enhancer; public class Women { public static void main(String[] args) { MishuCglib mishuCglib = new MishuCglib(); Gongneng gongneng = (Gongneng)mishuCglib.getInstance(); gongneng.chifan(); gongneng.zhidingxiaomubiao(); gongneng.yundong(); } }
通过上述的jdk的动态代理和cglib的动态代理可以发现,对于功能类里面新增的新功能,代理类都不需要再新增对应的代理功能的实现了。解决了静态代理的代理膨胀的问题。
目前代理就写到这里,之后会详细研究jdk的代理实现机制和cglib的代理实现机制。
链接:https://pan.baidu.com/s/18vqlMkP5hyZPs-BvIGlk9g 提取码:2eby