1 代码演练
1.1 动态代理
2 疑难解答
2.1 动态代理invoke怎么执行的?
2.2 感觉这块理解的不是很好,下边有时间再看看
1 代码演练
1.1 动态代理
重点:
重点关注动态代理类
测试类:
package com.geely.design.pattern.structural.proxy.dynamicproxy; import com.geely.design.pattern.structural.proxy.IOrderService; import com.geely.design.pattern.structural.proxy.Order; import com.geely.design.pattern.structural.proxy.OrderServiceImpl; public class Test { public static void main(String [] args){ Order order = new Order(); order.setUserID(1); /** * new OrderServiceDynamicProxy(order) 该方法已经生成了一个新的代理类 * 它的bind方法返回了原目标类,强转之后变成了原目标类。 */ IOrderService orderServiceDynamicProxy = (IOrderService) new OrderServiceDynamicProxy(new OrderServiceImpl()).bind(); //注意,执行saveOrder方法,最终会执行invode方法。 orderServiceDynamicProxy.saveOrder(order); } }
动态代理类:
package com.geely.design.pattern.structural.proxy.dynamicproxy; import com.geely.design.pattern.structural.proxy.Order; import com.geely.design.pattern.structural.proxy.db.DataSourceContextHolder; import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; import java.lang.reflect.Proxy; /** * 目的:抽奖信息和订单等不同的类都可以通过这一个动态代理进行复用,不用每一个都写一个静态代理。 * 这就是静态代理和动态代理的区别 * 动态代理是自动生成的,静态代理需要显式的来描述和coding */ public class OrderServiceDynamicProxy implements InvocationHandler { //目标对象 public Object target; //通过构造方法传入目标对象 public OrderServiceDynamicProxy(Object target) { this.target = target; } /** * 主方法, 调用前置方法,主要方法,以及后置方法 * @param proxy * @param method * @param args * @return * @throws Throwable */ @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { //取得目标对象,argObject是目标类,也就是静态代理demo中的订单类 Object argObject = args[0]; beforeMethod(argObject); Object object = method.invoke(target,args); afterMethod(); return object; } public Object bind(){ //得到目标对象的class类 Class cls = target.getClass(); //这里边有三个参数,classLoader,复数的interface,它的类型是class,第三个是invoccationHandler 因为本类本身实现了InvocationHandler,所以把本类自己传过去即可。 return Proxy.newProxyInstance(cls.getClassLoader(),cls.getInterfaces(),this); } /** * 前置方法,用来取模运算 * @param obj */ private void beforeMethod(Object obj){ int userID = 0; System.out.println("动态代理 before code"); if(obj instanceof Order){//如果该对象属于Order类 Order order = (Order) obj;//强转成Order 类 userID = order.getUserID(); } int dbRouter = userID%2; System.out.println("动态代理分配到 【db"+dbRouter+"】数据库进行处理数据!"); DataSourceContextHolder.setDBType("db"+String.valueOf(dbRouter)); } /** * 后置方法 */ private void afterMethod(){ System.out.println("动态代理 after code"); } }
订单类:
package com.geely.design.pattern.structural.proxy; /** * 建立订单实体类 */ public class Order { private Object orderInfo; //之所以选择integer类型,是为了方便OrderServiceStaticProxy静态代理类进行分库 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; } }
订单dao:
package com.geely.design.pattern.structural.proxy; public interface IOrderDao { int insertOrder(Order order); }
订单daoIMPL:
package com.geely.design.pattern.structural.proxy; public class OrderDaoImpl implements IOrderDao{ @Override public int insertOrder(Order order) { System.out.println("新增一条订单!"); return 1; } }
订单Service:
package com.geely.design.pattern.structural.proxy; public interface IOrderService { int saveOrder(Order order); }
订单ServiceIMPL:
package com.geely.design.pattern.structural.proxy; public class OrderServiceImpl implements IOrderService { private IOrderDao orderDao; @Override public int saveOrder(Order order) { //Spring会自己注入,这里我们直接new了 orderDao = new OrderDaoImpl(); System.out.println("Service层调用dao层添加Order"); return orderDao.insertOrder(order); } }
打印日志:
Connected to the target VM, address: '127.0.0.1:12906', transport: 'socket' 动态代理 before code 动态代理分配到 【db1】数据库进行处理数据! Disconnected from the target VM, address: '127.0.0.1:12906', transport: 'socket' Service层调用dao层添加Order 新增一条订单! 动态代理 after code Process finished with exit code 0
2 疑难解答
2.1 动态代理invoke怎么执行的?
1.Proxy.newProxyInstance(//参数省略了...)的部分源码
2.程序运行时产生一个类$proxyQ
3.Sproxy0类继承自Proxy类,实现了目标对象的父类接口(借鉴的百度提供的源码
4.Sproxy0类有多个Method成员变量,它的静态代码块给Method赋值为我们自己的接口的实现类的对应的Method对象
5.Sproxyo实现接口的方法调用了super.h.invoke(参数),这里的参数包括Method变量
这样就缕顺了,整体流程:
代理对象调接口中的方法---代理对象的真身是$proxy0
调用了对应的方法---此方法内部调用其父类的成员h调用h的invoke方法---就是调用传入了InvocationHandler的invoke方法,至于返回值,那就看我们的InvocationHandler的实现类怎么写了。
这部分参考:https://www.jianshu.com/p/774c65290218
诸葛