1.5.深入分析代理模式
1.5.1.深度分析代理模式【上】
时长:46min
学习目标:
》代理模式的应用场景及实现原理
》区分静态代理与动态代理
》cglib和jdk Proxy实现动态代理的区别
》手写实现定义的动态代理
》spring Aop基于动态代理,打下基础
5.1.代理模式定义
5.1.1.什么是代理模式?
Proxy Pattern,是指为其他对象提供一种代理,以控制对这个对象的访问。
代理对象,在客户端与目标对象之间起到中介作用,起到功能增强的作用。
属于结构型设计模式。
代理之生活理解:
可以参照中介来理解。如:租房中介,用户需要租房子,但是找不到或没时间找房子【用户功能不够强大】,那么,
就找到一个代理【中介】,中介是专门找到房子的【他的功能比用户强大】,最后中介帮用户找到了房子。
所以,代理是对目标对象【用户】功能的增强。
生活中代理场景:
房产中介,快递小哥,黄牛党。
5.1.2.代理模式的适用场景
》保护目标对象
》增强目标对象
三层架构:典型的静态代理模式
5.1.3.代理模式的优点与缺点
优点:
代理模式,能够将代理对象与真实的目标对象分离
一定程度上,降低了系统的耦合程度,易于扩展。
代理可以起到保护目标对象的作用。
可以增强目标对象的职责,功能。
缺点:
会造成系统设计中类数量的增加。
在客户端与目标对象之间增加了一个代理对象,请求速度变慢。
增加系统复杂性。
5.1.4.spring中的代理选择原则
1.当Bean有实现接口时,默认使用Jdk的动态代理
2.当Bean没有实现接口时,Spring会选择cglib实现。
3.spring可以通过配置强制使用cglib,只需在spring配置文件中,使用如下配置:
<aop:aspectj-autoproxy proxy-target-class="true"/> //强制设置cglib实现
5.2.代理模式的实现示例代码
5.2.1.通用写法
5.2.1.1.目标对象定义
由于面向接口编程,定义顶层接口:
package com.wf.proxy.general; /** * @ClassName ISubject * @Description 目标对象的抽象接口 * @Author wf * @Date 2020/5/15 17:00 * @Version 1.0 */ public interface ISubject { void request(); }
目标对象实现:
package com.wf.proxy.general; /** * @ClassName RealSubject * @Description 目标对象 * @Author wf * @Date 2020/5/15 17:02 * @Version 1.0 */ public class RealSubject implements ISubject { @Override public void request() { System.out.println("目标对象真实的服务实现"); } }
5.2.1.2.代理类定义
package com.wf.proxy.general; /** * @ClassName Proxy * @Description 代理对象 * @Author wf * @Date 2020/5/15 17:05 * @Version 1.0 */ public class Proxy implements ISubject { //代理对象 需要对目标对象的功能进行增强 //即增强目标对象的方法,所以实现同一接口 //增强目标对象,所以个课程目标对象【基于多态,可以引入顶层接口bean】 private ISubject subject; //客户端要传参目标对象,这里定义构建器传参 public Proxy(ISubject subject) { this.subject = subject; } //程序中,什么叫功能增强,就是多加一段代码,提供更多功能 @Override public void request() { doBefore(); //方法前增强 subject.request(); //方法后增强 doAfter(); } private void doAfter() { System.out.println("方法后面进行增强"); } private void doBefore() { System.out.println("方法前面进行增强"); } }
5.2.1.3.客户端调用
package com.wf.proxy.general; /** * @ClassName Client * @Description 客户端调用 * @Author wf * @Date 2020/5/15 17:15 * @Version 1.0 */ public class Client { public static void main(String[] args) { RealSubject subject = new RealSubject(); Proxy proxy = new Proxy(subject); proxy.request(); } }
测试结果如下:
说明:
显然,request方法的功能得到了增强。
5.2.1.4.系统类图
5.3.静态代理与动态代理
5.3.1.什么是静态代理?
先以一个生活场景,通过代码来说明。
程序员平时加班较多,没时间找对象-----------父母着急,帮忙物色对象【充当代理】,安排相亲-------最后找到女朋友。
抽象出业务模型
Person----Parent-----------findLove
5.3.1.1.代码示例
1.定义顶层接口
package com.wf.proxy.staticproxy; /** * @ClassName IPerson * @Description 程序员顶层抽象接口 * @Author wf * @Date 2020/5/15 17:31 * @Version 1.0 */ public interface IPerson { void findLove(); }
2.目标对象
package com.wf.proxy.staticproxy; /** * @ClassName Person * @Description 程序员 目标对象 * @Author wf * @Date 2020/5/15 17:33 * @Version 1.0 */ public class Person implements IPerson { @Override public void findLove() { System.out.println("程序员,自己没有找到女朋友"); } }
3.代理类---父母
package com.wf.proxy.staticproxy; /** * @ClassName ParentProxy * @Description 父母 代理类 * @Author wf * @Date 2020/5/15 17:36 * @Version 1.0 */ public class ParentProxy implements IPerson { private IPerson person; public ParentProxy(IPerson person) { this.person = person; } @Override public void findLove() { // System.out.println("开始帮儿子,物色对象"); person.findLove(); System.out.println("儿子说,ok,开始交往"); } }
4.客户端代码
package com.wf.proxy.staticproxy; /** * @ClassName Client * @Description 客户端调用类 * @Author wf * @Date 2020/5/15 17:41 * @Version 1.0 */ public class Client { public static void main(String[] args) { Person person = new Person(); ParentProxy proxy = new ParentProxy(person); proxy.findLove(); } }
测试结果如下:
说明:
因为有大量人可能也没有找到对象,针对这种需求,社会上形成一个产业链,叫婚介所,婚恋网。
因此,系统需要扩展。
但是,张三的父母,力量有限,只能给儿子一个人物色对象。
如果李四也想找到张三的父母物色对象,就没办法完成。也就是父母这个代理只能代理儿子的事情。
这就是静态代理。
如果想要为更多人做代理,代理需要更为强大,于是,产生动态代理。
静态代理:
只能代理某特定的目标对象。如:房产中介,只能代理租客。
动态代理:
代理对象可以代理任意的目标对象。
5.3.2.动态代理的实现
在java中,动态代理有两种实现方案:
》jdk Proxy代理类实现
》cglib库实现
5.3.2.1.jdk实现动态代理
1.婚介所代理类创建
package com.wf.proxy.dynamicproxy.jdkproxy; import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; import java.lang.reflect.Proxy; /** * @ClassName MarriageAgency * @Description 婚介所 代理类 * @Author wf * @Date 2020/5/15 18:23 * @Version 1.0 */ public class MarriageAgency implements InvocationHandler { //jdk动态代理 实现一个抽象的接口【接口可以定义任意功能】 //加强目标对象功能,需要引入目标对象 private IPerson person; public IPerson getProxy(IPerson person) { this.person = person; //根据目标对象,得到代理对象 //jdk底层是基于字节码,需要传参代理类的类加载器,并且代理对象需要实现接口功能【传参接口,实例this】 Class<? extends IPerson> personClass = person.getClass(); return (IPerson)Proxy.newProxyInstance(personClass.getClassLoader(), personClass.getInterfaces(),this); } @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { //加强目标对象的方法 doBefore(); Object result = method.invoke(this.person, args); doAfter(); return result; } private void doAfter() { System.out.println("是不是确认关系,开始交往"); } private void doBefore() { System.out.println("开始物色对象"); } }
2.顶层接口类
package com.wf.proxy.dynamicproxy.jdkproxy; /** * @ClassName IPerson * @Description 程序员顶层抽象接口 * @Author wf * @Date 2020/5/15 17:31 * @Version 1.0 */ public interface IPerson { void findLove(); }
然后,就可以定义不同的人实现IPerson接口。测试类【略】
5.3.2.2.基于cglib实现动态代理
1.添加cglib pom依赖
<!-- https://mvnrepository.com/artifact/cglib/cglib-nodep -->
<dependency>
<groupId>cglib</groupId>
<artifactId>cglib-nodep</artifactId>
<version>2.2</version>
<!-- <scope>test</scope>-->
</dependency>
2.定义代理类
package com.wf.proxy.dynamicproxy.cglibproxy; import com.wf.proxy.dynamicproxy.jdkproxy.IPerson; import net.sf.cglib.proxy.Enhancer; import net.sf.cglib.proxy.MethodInterceptor; import net.sf.cglib.proxy.MethodProxy; import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; import java.lang.reflect.Proxy; /** * @ClassName CglibMarriageAgency * @Description 婚介所 代理类 * @Author wf * @Date 2020/5/15 18:23 * @Version 1.0 */ public class CglibMarriageAgency implements MethodInterceptor { //jdk动态代理 实现一个抽象的接口【接口可以定义任意功能】 //加强目标对象功能,需要引入目标对象 private IPerson person; public Object getProxy(Class<?> clazz) { //根据目标对象,得到代理对象 Enhancer enhancer = new Enhancer(); enhancer.setSuperclass(clazz); enhancer.setCallback(this); return enhancer.create(); } private void doAfter() { System.out.println("是不是确认关系,开始交往"); } private void doBefore() { System.out.println("开始物色对象"); } @Override public Object intercept(Object obj, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable { doBefore(); Object result = methodProxy.invokeSuper(obj, objects); doAfter(); return result; } }
测试类:
package com.wf.proxy.dynamicproxy.cglibproxy; /** * @ClassName Test * @Description 测试类 * @Author wf * @Date 2020/5/18 14:15 * @Version 1.0 */ public class Test { public static void main(String[] args) { PersonZhang person = (PersonZhang) new CglibMarriageAgency().getProxy(PersonZhang.class); person.findLove(); } }
测试结果如下:
1.5.2.深度分析代理模式【下】
时长:1h22min
5.2.动态代理实现原理分析
5.2.1.Jdk实现原理
5.2.1.1.示例代码
1.测试代码
package com.wf.proxy.dynamicproxy.jdkproxy; /** * @ClassName Test * @Description * @Author wf * @Date 2020/5/18 16:06 * @Version 1.0 */ public class Test { public static void main(String[] args) { MarriageAgency proxy = new MarriageAgency(); IPerson personLi = proxy.getProxy(new PersonLi()); personLi.findLove(); } }
2.断点调试程序
可以发现,personLi是一个特殊的类型声明。$Proxy0@521【以$开头的实例,都是动态代理生成的,只在内存可见】
3.工具类查看代理实现
package com.wf.proxy.dynamicproxy.jdkproxy; import sun.misc.ProxyGenerator; import java.io.FileNotFoundException; import java.io.FileOutputStream; /** * @ClassName Test * @Description * @Author wf * @Date 2020/5/18 16:06 * @Version 1.0 */ public class Test { public static void main(String[] args) { MarriageAgency proxy = new MarriageAgency(); IPerson personLi = proxy.getProxy(new PersonLi()); personLi.findLove(); //使用工具,获取代理类的代理对象内部实现 byte[] bytes = ProxyGenerator.generateProxyClass("$Proxy0", new Class[]{IPerson.class}); try { FileOutputStream fos = new FileOutputStream("proxy0.class"); fos.write(bytes); fos.close(); } catch (Exception e) { e.printStackTrace(); } } }
在文件目录下生成类:
打开内容如下:
// // Source code recreated from a .class file by IntelliJ IDEA // (powered by Fernflower decompiler) // import com.wf.proxy.dynamicproxy.jdkproxy.IPerson; import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; import java.lang.reflect.Proxy; import java.lang.reflect.UndeclaredThrowableException; public final class $Proxy0 extends Proxy implements IPerson { private static Method m1; private static Method m3; private static Method m2; private static Method m0; public $Proxy0(InvocationHandler var1) throws { super(var1); } public final boolean equals(Object var1) throws { try { return (Boolean)super.h.invoke(this, m1, new Object[]{var1}); } catch (RuntimeException | Error var3) { throw var3; } catch (Throwable var4) { throw new UndeclaredThrowableException(var4); } } public final void findLove() throws { try { super.h.invoke(this, m3, (Object[])null); } catch (RuntimeException | Error var2) { throw var2; } catch (Throwable var3) { throw new UndeclaredThrowableException(var3); } } public final String toString() throws { try { return (String)super.h.invoke(this, m2, (Object[])null); } catch (RuntimeException | Error var2) { throw var2; } catch (Throwable var3) { throw new UndeclaredThrowableException(var3); } } public final int hashCode() throws { try { return (Integer)super.h.invoke(this, m0, (Object[])null); } catch (RuntimeException | Error var2) { throw var2; } catch (Throwable var3) { throw new UndeclaredThrowableException(var3); } } static { try { m1 = Class.forName("java.lang.Object").getMethod("equals", Class.forName("java.lang.Object")); m3 = Class.forName("com.wf.proxy.dynamicproxy.jdkproxy.IPerson").getMethod("findLove"); m2 = Class.forName("java.lang.Object").getMethod("toString"); m0 = Class.forName("java.lang.Object").getMethod("hashCode"); } catch (NoSuchMethodException var2) { throw new NoSuchMethodError(var2.getMessage()); } catch (ClassNotFoundException var3) { throw new NoClassDefFoundError(var3.getMessage()); } } }
5.2.1.2.模仿jdk自定义动态代理实现
1.定义MyInvocationHandler
package com.wf.proxy.dynamicproxy.defproxy; import java.lang.reflect.Method; /** * @ClassName MyInvocationHandler * @Description 自定义handler * @Author wf * @Date 2020/5/18 16:35 * @Version 1.0 */ public interface MyInvocationHandler { public Object invoke(Object proxy, Method method, Object[] args) throws Throwable; }
2.自定义Proxy工具类
package com.wf.proxy.dynamicproxy.defproxy; import javax.tools.JavaCompiler; import javax.tools.JavaFileObject; import javax.tools.StandardJavaFileManager; import javax.tools.ToolProvider; import java.io.File; import java.io.FileWriter; import java.lang.reflect.Constructor; import java.lang.reflect.Method; /** * @ClassName MyProxy * @Description 自定义Proxy工具类 * @Author wf * @Date 2020/5/18 16:37 * @Version 1.0 */ public class MyProxy { public static final String ln = " "; public static Object newProxyInstance(MyClassLoader loader, Class<?>[] interfaces, MyInvocationHandler h){ //1.动态生成源码.java文件 String srcFile = generateSrc(interfaces); System.out.println(srcFile); //2.输出.java文件到磁盘 String filePath = MyProxy.class.getResource("").getPath(); File file = new File(filePath+"$Proxy0.java"); try { FileWriter fw = new FileWriter(file); fw.write(srcFile); fw.flush(); fw.close(); //3.编译.java文件,成$Proxy0.class文件 JavaCompiler compiler = ToolProvider.getSystemJavaCompiler(); StandardJavaFileManager manager = compiler.getStandardFileManager(null, null, null); Iterable<? extends JavaFileObject> it = manager.getJavaFileObjects(file); JavaCompiler.CompilationTask task = compiler.getTask(null, manager, null, null, null, it); task.call(); manager.close(); //4.把生成的.class文件加载到jvm中 Class<?> proxyClass = loader.findClass("$Proxy0"); Constructor<?> constructor = proxyClass.getConstructor(MyInvocationHandler.class); //5.返回新的代理对象 return constructor.newInstance(h); } catch (Exception e) { e.printStackTrace(); } return null; } private static String generateSrc(Class<?>[] interfaces) { StringBuilder sb = new StringBuilder(); sb.append(MyProxy.class.getPackage()).append(";").append(ln); sb.append("import ").append(interfaces[0].getName()).append(";").append(ln); sb.append("import java.lang.reflect.*;").append(ln); sb.append("public final class $Proxy0 implements ").append(interfaces[0].getSimpleName()).append("{").append(ln); sb.append("MyInvocationHandler h;").append(ln); sb.append("public $Proxy0(MyInvocationHandler h){").append(ln). append(" this.h = h;").append(ln). append(" }").append(ln); for(Method m: interfaces[0].getMethods()){ Class<?>[] params = m.getParameterTypes(); StringBuilder paramNames = new StringBuilder(); StringBuilder paramValues = new StringBuilder(); StringBuilder paramClasses = new StringBuilder(); for(int i=0; i < params.length; i++){ Class clazz = params[i]; String type = clazz.getName(); String paramName = toLowerFirstCase(clazz.getSimpleName()); paramNames.append(type).append(" ").append(paramName); paramValues.append(paramName); paramClasses.append(clazz.getName()).append(".class"); if(i < params.length -1){ paramNames.append(","); paramValues.append(","); paramClasses.append(","); } } sb.append("public ").append(m.getReturnType().getName()).append(" ").append(m.getName()).append("(") .append(paramNames).append("){").append(ln); sb.append(" try {" ).append(ln). append(" Method m = ").append(interfaces[0].getName()).append(".class.getMethod("").append(m.getName()). append("",new Class[]{").append(paramClasses).append("} );").append(ln); sb.append(" ").append(hasReturnValue(m.getReturnType()) ? "return ("+m.getReturnType().getTypeName()+")": ""). append("this.h.invoke(this, m, ").append("new Object[]{").append(paramValues).append("});").append(ln). append(" } catch (RuntimeException | Error var2) { " ). append(" throw var2; " ). append(" } catch (Throwable var3) { " ). append(" throw new UndeclaredThrowableException(var3); " ). append(" } " );//.append(getReturnEmptyCode(m.getReturnType())).append(ln); sb.append(" }"); } return sb.append(ln).append("}").toString(); } private static String getReturnEmptyCode(Class<?> returnType) { if(hasReturnValue(returnType)){ return "return null;"; } return "return;"; } private static boolean hasReturnValue(Class<?> returnType) { if(returnType == Void.class || returnType == void.class){ return false; } return true; } private static String toLowerFirstCase(String paramName) { char first = paramName.charAt(0); //首字母大写判断 if(first >= 'A' && first<= 'Z'){ first += 32; paramName = first + paramName.substring(1); } return paramName; } }
会在项目根目录cglib_proxy_classes生成cglib相关实例信息,如下所示:
5.3.cglib与jdk实现动态代理的总结
cglib采用继承的方式,覆盖父类的方法
jdk采用实现接口的方式,要求代理的目标对象,必须实现一个接口。、
两者的思想:
都是生成字节码,再重组成一个新的类。
缺点及优点:
Jdk Proxy,对于用户而言,依赖性更强,调用也更复杂。生成逻辑较为简单,执行效率较低,每次都要用到反射。
cglib 对目标类没有任何要求。效率更高,性能更好,底层没有用到反射。
cglib有个坑,目标代理对象不能有final修饰的方法,忽略final修饰的方法。
5.4.代理的运用案例
5.4.1.示例代码
5.4.1.1.Service服务
1.接口服务定义
package com.wf.proxy.dbroute; import com.wf.proxy.dbroute.dbsource.DynamicDataSourceEntity; import java.text.ChoiceFormat; import java.text.SimpleDateFormat; import java.util.Date; /** * @ClassName OrderServiceImpl * @Description 订单服务实现,OrderServiceImpl类就相当于代理类,orderDao就是目标对象,这是静态代理 * @Author wf * @Date 2020/5/19 14:24 * @Version 1.0 */ public class OrderServiceImpl implements IOrderService { private OrderDao orderDao; //时间年份格式化 private SimpleDateFormat yearFormat = new SimpleDateFormat("yyyy"); public OrderServiceImpl() { //spring中使用自动注入 //普通maven工程中,使用构造器注入 this.orderDao = new OrderDao(); } /** * 在调用orderDao.insert前后会做一些处理,比如,设置订单创建时间 * 这里的前后处理,需要架构师来统一操作,如果每个都写的话,很乱 * 在当前代理的上层,还需要一个父类代理 * @param order * @return */ @Override public int createOrder(Order order) { System.out.println("OrderService服务调用orderDao创建订单"); Long time = order.getCreateTime(); //根据订单创建年份,进行路由,选择数据源 Integer dbRouter = Integer.valueOf(yearFormat.format(new Date(time))); System.out.println("自动切换数据源到【DB_"+dbRouter+"】"); DynamicDataSourceEntity.set(dbRouter); int rows = this.orderDao.insert(order); //用完后,释放数据源 DynamicDataSourceEntity.restore(); return rows; } }
2.创建订单业务bean
package com.wf.proxy.dbroute; /** * @ClassName Order * @Description 订单业务bean * @Author wf * @Date 2020/5/19 14:23 * @Version 1.0 */ public class Order { private Long createTime; public void setCreateTime(Long createTime) { this.createTime = createTime; } public Long getCreateTime() { return createTime; } }
3.创建dao层服务
这里为示例简洁性,直接定义class服务,而没有面向接口编程。
package com.wf.proxy.dbroute; /** * @ClassName OrderDao * @Description 订单dao实现 * @Author wf * @Date 2020/5/19 15:01 * @Version 1.0 */ public class OrderDao { public int insert(Order order) { System.out.println("创建开始"); return 0; } }
4.数据源处理工具类
这里需要切换数据源,定义数据源处理工具类,内部使用ThreadLocal存储。
package com.wf.proxy.dbroute.dbsource; /** * @ClassName DynamicDataSourceEntity * @Description 动态切换数据源工具类 * @Author wf * @Date 2020/5/19 14:38 * @Version 1.0 */ public class DynamicDataSourceEntity { private static final ThreadLocal<String> local = new ThreadLocal<>(); private static String DEFAULT_SOURCE = null; //单例控制 private DynamicDataSourceEntity(){} public static String get(){ return local.get(); } public static void restore(){ local.set(DEFAULT_SOURCE); } public static void set(String source){ local.set(source); } //DB_2018 //DB_2019 public static void set(int year){ local.set("DB_"+year); } }
5.测试类
package com.wf.proxy.dbroute; import java.text.SimpleDateFormat; import java.util.Date; /** * @ClassName DBRouterProxyTest * @Description 数据源路由测试 * @Author wf * @Date 2020/5/19 14:55 * @Version 1.0 */ public class DBRouterProxyTest { public static void main(String[] args) { try{ Order order = new Order(); // order.setCreateTime(System.currentTimeMillis()); SimpleDateFormat sdf = new SimpleDateFormat("yyyy/MM/dd"); Date date = sdf.parse("2021/03/01"); order.setCreateTime(date.getTime()); IOrderService orderService = new OrderServiceImpl(); orderService.createOrder(order); }catch (Exception e){ e.printStackTrace(); } } }
测试结果如下:
说明:
这里主要输出OrderServiceImpl服务实现的逻辑,由于希望把insert逻辑调用与数据源处理的逻辑分离,对该类创建代理类。
5.4.1.2.定义代理类处理数据源切换
1.增加上层代理类
package com.wf.proxy.dbroute.proxy; import com.wf.proxy.dbroute.IOrderService; import com.wf.proxy.dbroute.Order; import com.wf.proxy.dbroute.dbsource.DynamicDataSourceEntity; import java.text.SimpleDateFormat; import java.util.Date; /** * @ClassName OrderServiceStaticProxy * @Description 定义代理类,处理数据源切换 * @Author wf * @Date 2020/5/19 14:49 * @Version 1.0 */ public class OrderServiceStaticProxy implements IOrderService { //时间年份格式化 private SimpleDateFormat yearFormat = new SimpleDateFormat("yyyy"); private IOrderService orderService; public OrderServiceStaticProxy(IOrderService orderService) { this.orderService = orderService; } @Override public int createOrder(Order order) { Long time = order.getCreateTime(); //根据订单创建年份,进行路由,选择数据源 Integer dbRouter = Integer.valueOf(yearFormat.format(new Date(time))); System.out.println("静态代理类自动分配到【DB_"+dbRouter+"】数据源处理数据"); DynamicDataSourceEntity.set(dbRouter); this.orderService.createOrder(order); //用完后,释放数据源 DynamicDataSourceEntity.restore(); return 0; } }
2.修改OrderServiceImpl服务
package com.wf.proxy.dbroute; import com.wf.proxy.dbroute.dbsource.DynamicDataSourceEntity; import java.text.ChoiceFormat; import java.text.SimpleDateFormat; import java.util.Date; /** * @ClassName OrderServiceImpl * @Description 订单服务实现,OrderServiceImpl类就相当于代理类,orderDao就是目标对象,这是静态代理 * @Author wf * @Date 2020/5/19 14:24 * @Version 1.0 */ public class OrderServiceImpl implements IOrderService { private OrderDao orderDao; //时间年份格式化 //private SimpleDateFormat yearFormat = new SimpleDateFormat("yyyy"); public OrderServiceImpl() { //spring中使用自动注入 //普通maven工程中,使用构造器注入 this.orderDao = new OrderDao(); } /** * 在调用orderDao.insert前后会做一些处理,比如,设置订单创建时间 * 这里的前后处理,需要架构师来统一操作,如果每个都写的话,很乱 * 在当前代理的上层,还需要一个父类代理 * @param order * @return */ @Override public int createOrder(Order order) { System.out.println("OrderService服务调用orderDao创建订单"); /*Long time = order.getCreateTime(); //根据订单创建年份,进行路由,选择数据源 Integer dbRouter = Integer.valueOf(yearFormat.format(new Date(time))); System.out.println("自动切换数据源到【DB_"+dbRouter+"】"); DynamicDataSourceEntity.set(dbRouter);*/ int rows = this.orderDao.insert(order); //用完后,释放数据源 // DynamicDataSourceEntity.restore(); return rows; } }
3.修改测试类
package com.wf.proxy.dbroute; import com.sun.org.apache.xpath.internal.operations.Or; import com.wf.proxy.dbroute.proxy.OrderServiceStaticProxy; import java.text.SimpleDateFormat; import java.util.Date; /** * @ClassName DBRouterProxyTest * @Description 数据源路由测试 * @Author wf * @Date 2020/5/19 14:55 * @Version 1.0 */ public class DBRouterProxyTest { /*public static void main(String[] args) { try{ Order order = new Order(); // order.setCreateTime(System.currentTimeMillis()); SimpleDateFormat sdf = new SimpleDateFormat("yyyy/MM/dd"); Date date = sdf.parse("2021/03/01"); order.setCreateTime(date.getTime()); IOrderService orderService = new OrderServiceImpl(); orderService.createOrder(order); }catch (Exception e){ e.printStackTrace(); } }*/ public static void main(String[] args) { try{ Order order = new Order(); // order.setCreateTime(System.currentTimeMillis()); SimpleDateFormat sdf = new SimpleDateFormat("yyyy/MM/dd"); Date date = sdf.parse("2021/03/01"); order.setCreateTime(date.getTime()); //调用代理类,创建代理对象 IOrderService orderService = new OrderServiceStaticProxy(new OrderServiceImpl()); orderService.createOrder(order); }catch (Exception e){ e.printStackTrace(); } } }
测试结果如下图:
说明:
现在的静态代理,只能代理OrderService不能代理其他Service服务,为了系统扩展性,和复用性。需要使用动态代理。
5.4.1.3.动态代理处理数据源切换
1.创建动态代理类
package com.wf.proxy.dbroute.proxy; import com.wf.proxy.dbroute.dbsource.DynamicDataSourceEntity; import com.wf.proxy.dynamicproxy.defproxy.MyClassLoader; import com.wf.proxy.dynamicproxy.defproxy.MyInvocationHandler; import com.wf.proxy.dynamicproxy.defproxy.MyProxy; import java.lang.reflect.Method; import java.text.SimpleDateFormat; import java.util.Date; /** * @ClassName ServiceDynamicProxy * @Description 动态代理,创建代理类 * @Author wf * @Date 2020/5/19 16:15 * @Version 1.0 */ public class ServiceDynamicProxy implements MyInvocationHandler { //时间年份格式化 private SimpleDateFormat yearFormat = new SimpleDateFormat("yyyy"); private Object targetObj; public Object getInstance(Object targetObj){ this.targetObj = targetObj; Class<?> clazz = targetObj.getClass(); return MyProxy.newProxyInstance(new MyClassLoader(), clazz.getInterfaces(),this); } @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { doBefore(args[0]); Object result = method.invoke(targetObj, args); doAfter(); return result; } private void doAfter() { System.out.println("Proxy after method"); //用完后,释放数据源 DynamicDataSourceEntity.restore(); } private void doBefore(Object arg) { System.out.println("Proxy before method"); //约定优于配置 try { Long time = (Long) arg.getClass().getMethod("getCreateTime").invoke(arg); //根据订单创建年份,进行路由,选择数据源 Integer dbRouter = Integer.valueOf(yearFormat.format(new Date(time))); System.out.println("静态代理类自动分配到【DB_"+dbRouter+"】数据源处理数据"); DynamicDataSourceEntity.set(dbRouter); } catch (Exception e) { e.printStackTrace(); } } }
2.修改测试类
5.5.代理模式相关问题思考
5.5.1.总结静态代理与动态代理的根本区别
静态代理:硬编码【只适用于某特定类型的bean的代理】,手动注入。
依赖目标对象的引用,手动调用目标对象的方法。
动态代理:
具有更强的扩展性,自动注入,自动生成一个新的类【和目标类同一继承体系】
特征:
拿到目标对象的引用
拿到目标对象的方法,进行增强
5.5.2.使用java代码实现Proxy类的带参方法实现