代理模式 :客户端 不直接 访问目标对象,而是通过代理访问目标对象
作用:
客户端不想直接访问实际的对象,或者访问实际的对象存在困难
原程序代码的前提下,对功能进行扩充
使用场景:
1.可以记录日志 目标对象 时长
2.可以拦截
3.事务控制 权限控制
代理模式:
静态代理 :一个代理只能代理 一个目标对象
动态代理:一个代理 可以代理多种对象( jdk cglib 这两种)
静态代理主要包括以下几个主要部分
接口:
//租房 public interface Rent { public void rent(); }真实角色:
//房东要出租房子 public class HouseHolder implements Rent{ public void rent() { System.out.println("房东要出租房子"); } }代理角色:
public class Proxy implements Rent { //用组合的方式引入房东,继承也行,但是有局限性,不好 private HouseHolder houseHolder; public Proxy() { } public Proxy(HouseHolder houseHolder) { this.houseHolder = houseHolder; } public void rent() { houseHolder.rent(); fee(); } //收中介费,中介还能实现其他功能 public void fee(){ System.out.println("收中介费"); } }客服端访问代理角色:
public class Client { public static void main(String[] args) { //房东要租房子 HouseHolder houseHolder = new HouseHolder(); //中介代理房东租房子,代理也可由自己的附属操作 Proxy proxy = new Proxy(houseHolder); //你不用面对房东,直接租房即可 proxy.rent(); } }
1、可以使真实角色的操作更加纯粹,不用关注一些公共业务
2、公共业务交给代理角色,方便业务的分工
3、公共业务发生扩展的时候,方便集中管理
一个真实角色就会产生一个代理角色,这样代码量会翻倍,开发效率低
这就间接的引出了AOP的实现机制,在原有的基础增加新的功能,但又不能修改原有的代码,于是便使用代理模式,用代理对象去实现。(代理模式就是AOP机制实现的底层)
动态代理包含的几个主要的部分和静态代理是一样的,但是动态代理的代理角色是自己生成的,不是写死的。动态代理的实现主要利用Proxy类和InvocationHandler接口
接口:
//租房 public interface Rent { public void rent(); }真实角色:
//房东要出租房子 public class HouseHolder implements Rent { public void rent() { System.out.println("房东要出租房子"); } }准备生成代理角色的类:
//用这个类,去实现自动生成代理类 public class ProxyInvocationHandler implements InvocationHandler { //被代理的接口 private Rent rent; public void setRent(Rent rent){ this.rent=rent; } //生成得到代理类 public Object getProxy(){ /** *ClassLoader loader, 类加载器 类加载器 将class 加载到内存中, *Class<?>[] interfaces, 目标对象 实现的接口 *InvocationHandler h 当代理对象要 对应的接口方法时 ,会回调(触发invoke 方法) ,然后 调用 目标对象的方法 */ return Proxy.newProxyInstance(this.getClass().getClassLoader(),rent.getClass().getInterfaces(),this); } //处理代理实例,并返回结果 public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { Object result = method.invoke(rent, args); return result; } }客服端访问代理角色:
public class Client { public static void main(String[] args) { //真实角色 HouseHolder houseHolder = new HouseHolder(); //代理角色,原本不存在,用刚才创建的类去自动生成 ProxyInvocationHandler proxyInvocationHandler = new ProxyInvocationHandler(); proxyInvocationHandler.setRent(houseHolder); //动态生成代理类 Rent proxy = (Rent) proxyInvocationHandler.getProxy(); proxy.rent(); } }
1、可以使真实角色的操作更加纯粹,不用关注一些公共业务
2、公共业务交给代理角色,方便业务的分工
3、公共业务发生扩展的时候,方便集中管理
4、一个动态代理类代理的是一个借口,一般就是对应的一类业务
5、一个动态代理类可以代理多个类,只要是实现同一个接口即可
【总结】动态代理中首先要记着的就是代理角色不是写死的,而是通过实现Proxy类和InvocationHandler接口而自动创建的。全程都是基于反射的思想。
cglib动态代理 原理是 通过asm 生成字节码文件(按照你的目标类,创建一个对应的子类并生成 字节码)子类会继承目标类。
首先需要先导入依赖
<!-- 导入cglib 依赖 --> <dependency> <groupId>cglib</groupId> <artifactId>cglib-nodep</artifactId> <version>2.2</version> </dependency>也是包含几个重要部分:
接口:
//租房 public interface Rent { public void rent(); }真实角色:
//房东要出租房子 public class HouseHolder implements Rent { public void rent() { System.out.println("房东要出租房子"); } }代理角色:
public class CgLibProxy implements MethodInterceptor { /** * 创建代理角色 * Class superclass 目标对象实现类 * @return */ public Object getProxyInstance(Class superclass){ // cglib 工具类 Enhancer enhancer = new Enhancer(); // 设置 目标类 为代理类的 父类 enhancer.setSuperclass(superclass); // 设置回调方法 enhancer.setCallback(this); // 创建代理对象 return enhancer.create(); } // 在代理对象 执行方法是回调 public Object intercept(Object proxy, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable { // Object proxy 代理对象 // MethodProxy methodProxy 代理对象执行的方法 // Object[] objects 代理对象执行方法传递的参数 System.out.println("代理对象 开始调用 父类 对应方法"); // 调用目标对象 父类 Object result = methodProxy.invokeSuper(proxy, objects); System.out.println("代理对象 完成调用 父类 对应方法"); return result; } }客户端访问代理角色:
public class Client { public static void main(String[] args) { // 设置 cglib 产生 字节码文件的位置 System.setProperty(DebuggingClassWriter.DEBUG_LOCATION_PROPERTY,"c://cglib_proxy_classes"); //真实角色 HouseHolder houseHolder = new HouseHolder(); // 代理对象 和目标对象 是同一对象 只不过代理对象 是 目标对象 的 子类 HouseHolder proxy = (HouseHolder) cgLibProxy.getProxyInstance(HouseHolder.class); proxy.buyTicket(); // cglib 代理目标 类 无论是否实现接口 都可以完成 代理 } }
jdk: 使用jdk 默认实现 ,只能实现目标对象有接口的对象 执行(通过反射调用目标对象) 效率(反射效率低) 加载速度快(运行时反射执行)
cglib: 使用asm 技术生成目标类对应子类的字节码文件 , 目标类无论是否实现接口都可实现代理 执行(调用父类执行)
效率(效率高和普通继承一致) 加载速度慢(因为在程序执行时,先生成字节码文件,再加载jvm,再生成代理类)