java设计模式——代理模式
一. 定义与类型
定义:为其他对象提供一种代理,以控制对这个对象的访问,代理对象在客户端和目标对象之间起到中介作用
类型:结构性。
二. 使用场景
(1) 保护目标对象
(2) 增强目标对象
三. 优缺点
优点:
(1) 代理模式能将代理对象与真实被调用的目标对象分离
(2) 一定程度上降低了系统的耦合性,扩展性好
(3) 保护目标对象
(4) 增强目标对象
缺点:
(1) 代理模式会造成系统设计中类的数目增加
(2) 客户端和目标对象增加一个代理对象,会造成请求处理速度变慢
(3) 增加系统的复杂度
四. 代理—扩展
静态代理:通过在代码中显示的定义了一个业务实现类的代理类,在代理类中对同名的方法进行包装,再用户通过调用代理类的被包装过的业务方法,来调用目标对象的业务方法,同时进行增强。
动态代理:JDK中的动态代理只能对接口代理,无法代理类。而且用到的代理类,是在程序调用到代理类对象时,才由jvm真正创建。JVM根据传进来的业务实现类对象以及方法名动态的创建了一个代理类的class文件,并且这个class文件被字节码引擎执行,然后通过该代理类的对象进行方法调用。
CGLib代理:可以代理类。原理是生成一个可以被代理的类的子类,重写覆盖其中的方法
五. Spring代理选择—扩展
(1) 当Bean有实现接口时,Spring就会用JDK的动态代理
(2) 当Bean没有实现接口时,Spring使用CGlib
(3) 可以强制使用CGlib
在Spring配置中加入<aop:aspectj-autoproxy proxy-target-class="true"/>
六. 代理速度对比—扩展
CGLib:原理采用ASM字节码生成的,比使用java反射效率高,但是要注意final关键词。
与JDK动态代理相比,在万次实现效果下jdk7与jdk8的动态代理要比CGlib代理速度更快,
七. 相关设计模式
代理模式和装饰者模式
装饰者模式是给对象加上行为,而代理模式是控制访问,更注重通过设置代理人的方式来增加目标对象,一般是增强目标对象的某些行为。
代理模式和适配器模式
适配器模式主要改变所考虑对象的接口,而代理模式是不能改变所代理类的接口
八. Coding
其实代理模式跟我之前写的一篇有关动态代理的博客是如出一辙的,大致的原理都差不多
先模仿一下一般开发的业务代码订单实体Order:
/** * @program: designModel * @description: 订单实体 * @author: YuKai Fan * @create: 2019-02-12 17:21 **/ public class Order { private Object orderInfo; private Integer userId; public Object getOrderInfo() { return orderInfo; } public void setOrderInfo(Object orderInfo) { this.orderInfo = orderInfo; } public Integer getUserId() { return userId; } public void setUserId(Integer userId) { this.userId = userId; } }
service层:
/** * @program: designModel * @description: * @author: YuKai Fan * @create: 2019-02-12 17:22 **/ public interface IOrderService { int saveOrder(Order order); }
service实现类:
/** * @program: designModel * @description: * @author: YuKai Fan * @create: 2019-02-12 17:23 **/ public class OrderServiceImpl implements IOrderService { private IOrderDao iOrderDao; public int saveOrder(Order order) { //Spring会自己注入,但是并没有集成spring iOrderDao = new OrderDaoImpl(); System.out.println("Service层调用Dao层添加Order"); return iOrderDao.insert(order); } }
Dao层:
/** * @program: designModel * @description: * @author: YuKai Fan * @create: 2019-02-12 17:23 **/ public interface IOrderDao { int insert(Order order); }
Dao实现类:
/** * @program: designModel * @description: * @author: YuKai Fan * @create: 2019-02-12 17:24 **/ public class OrderDaoImpl implements IOrderDao { public int insert(Order order) { System.out.println("Dao层添加Order成功"); return 1; } }
其实代理模式的思想我感觉并不难,总之就是能够不改变目标方法的基础上,增强目标方法。
假设要在保存订单时,进行分库操作,分别将订单保存在不同的数据库中,也就是增强了保存订单的方法。
在此基础上创建分库的代码:
/** * @program: designModel * @description: * @author: YuKai Fan * @create: 2019-02-12 17:33 **/ public class DataSourceContextHolder { private static final ThreadLocal<String> CONTEXT_HOLDER = new ThreadLocal<String>(); public static void setDBType(String dbType) { CONTEXT_HOLDER.set(dbType); } public static String getDBType() { return CONTEXT_HOLDER.get(); } public static void clearDBType() { CONTEXT_HOLDER.remove(); } }
/** * @program: designModel * @description: * @author: YuKai Fan * @create: 2019-02-12 17:31 **/ public class DynamicDataSource extends AbstractRoutingDataSource { protected Object determineCurrentLookupKey() { return DataSourceContextHolder.getDBType(); } }
使用静态代理将上面的代码增强目标方法:
/** * @program: designModel * @description: OrderServie的静态代理类 * @author: YuKai Fan * @create: 2019-02-12 17:28 **/ public class OrderServieStaticProxy { private IOrderService iOrderService; public int saveOrder(Order order) { beforeMethod(order); iOrderService = new OrderServiceImpl(); //增强IOrderService中的saveOrder方法,把这个方法单独出来,用beforeMethod和afterMethod来增强 int result = iOrderService.saveOrder(order); afterMethod(); return result; } private void beforeMethod(Order order) { int userId = order.getUserId(); int dbRouter = userId % 2; System.out.println("静态代理分配到【db" + dbRouter + "】"); //todo 设置dataSource DataSourceContextHolder.setDBType("db" + String.valueOf(dbRouter)); System.out.println("静态代理 before code"); } private void afterMethod() { System.out.println("静态代理 after code"); } }
/** * @program: designModel * @description: * @author: YuKai Fan * @create: 2019-02-12 17:48 **/ public class Test { public static void main(String[] args) { Order order = new Order(); order.setUserId(2); OrderServieStaticProxy orderServieStaticProxy = new OrderServieStaticProxy(); orderServieStaticProxy.saveOrder(order); } }
结果:
使用动态代理,实现了InvocationHandler接口,通过Proxy中的newProxyInstance()方法再创建一个新类,来代理目标类,并且使用invoke()方法实现其中的方法:
/** * @program: designModel * @description: * @author: YuKai Fan * @create: 2019-02-13 10:19 **/ public class OrderServiceDynamicProxy implements InvocationHandler { private Object target; public OrderServiceDynamicProxy(Object target) { this.target = target; } public Object bind() { Class cls = target.getClass(); return Proxy.newProxyInstance(cls.getClassLoader(), cls.getInterfaces(), this); } public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { Object argObject = args[0]; beforeMethod(argObject); Object object = method.invoke(target, args); afterMethod(); return object; } private void beforeMethod(Object obj) { int userId = 0; System.out.println("动态代理 before code"); if (obj instanceof Order) { Order order = (Order) obj; userId = order.getUserId(); int dbRouter = userId % 2; System.out.println("动态代理分配到【db" + dbRouter + "】"); //todo 设置dataSource DataSourceContextHolder.setDBType("db" + String.valueOf(dbRouter)); } } private void afterMethod() { System.out.println("动态代理 after code"); } }
/** * @program: designModel * @description: * @author: YuKai Fan * @create: 2019-02-12 17:48 **/ public class Test { public static void main(String[] args) { Order order = new Order(); order.setUserId(2); OrderServieStaticProxy orderServieStaticProxy = new OrderServieStaticProxy(); orderServieStaticProxy.saveOrder(order); } }
结果:
用debug模式跑完整个流程,会更加清晰的看到动态代理的原理
通过构造器将目标类传入
然后调用bind()方法,其中的Proxy中newProxyInstance()方法来创建一个新的代理类
newProxyInstance()的核心方法就是,getProxyClass0方法创建新的代理类,还有代理缓存的方法这里暂时放着,不做细说
在调用代理类的saveOrder()方法,其实是调用invoke()方法
可以在下面的代码看到,将beforeMethod()和afterMethod()的增加代码插入到代理的方法中
九. 源码分析
(1)
Spring实现AOP的动态代理核心类有JdkDynamicAopProxy针对接口,CglibAopProxy针对类
Spring中的ProxyFactoryBean,是代理工厂bean,其中的核心方法getObject,生成各种对象(单例,多例,原型)的代理对象
(2)
Mybaits中的MapperProxyFactory,是mapper的代理工厂,其中的newInstance()方法
下面是MapperProxy调用目标方法的代理方法的代码,可以看到cachedMapperMethod方法采用的是享元模式