代理模式coding-动态代理

package com.learn.design.pattern.structural.proxy.dynamicproxy;import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;import com.learn.design.pattern.structural.proxy.Order;
import com.learn.design.pattern.structural.proxy.db.DataSourceContextHolder;/*** 在这里创建一个类* OrderServiceDynamicProxy* 让他来实现InvocationHandler* 然后写一下他的实现方法* * * * @author Leon.Sun**/
public class OrderServiceDynamicProxy implements InvocationHandler {/*** 目标对象* 那这个目标对象如何放进来呢* * */private Object target;/*** 我们通过构造器* * target是目标类* 目标类就是OrderService的实现* 那里面的DAO没有注入成功* 这个忽略就可以了* 因为我们是显示而写的* 只有saveOrder的时候才会生成* 这个忽略就行* target目标类已经在这里了* 于是给目标代理类target类进行赋值* * * * @param target*/public OrderServiceDynamicProxy(Object target) {this.target = target;}/*** 然后我们再写一个绑定的方法* 这里面就要获取proxy的代理对象* 这么一个静态方法* 怎么写呢* * 他通过Proxy* newProxyInstance* 来返回代理类* 所以这里面分号是不行的* 我们还要调用一下他的bind方法* 然后对他进行强转* 他就是一个IOrderService类型* 然后调用它的Order.saveOrder方法* * bind打上断点* 这个时候就调用了bind这个方法* 首先把目标对象的Class拿到* * * * @return*/public Object bind(){/*** 首先获取一个class* 这个class是目标类的class* 也就是target的getClass* 然后return一个对象* 这个对象通过Proxy.newProxyInstance* 静态方法* 来进行生成* target里面获得ClassLoader* 从这里面获得接口的一个复数* 返回值是class类型* 还有自己* 这样绑定之后* 就通过Proxy的newProxyInstance* 把这个Object就返回回去了* 那在这个类里面叫OrderService* 那如果使用动态代理的话* 这里面可以补充一些判断逻辑* 如果使用静态代理* 例如Order在分库的时候* 我们要写一个Order的静态代理* 如果这个时候要是有其他的类* 要做分库的时候* 还要对那个类写一个静态代理* 而动态代理不仅仅是针对Order这么一个实体* 比如说针对这个用户的抽奖信息* 我们也可以通过userId进行分库* 都可以通过这一个动态代理进行复用* 而不用每一个类都写一套静态代理* 那这个就是动态代理和静态代理很大的一个区别* 那因为动态代理是动态生成的* 静态代理要显示的来描述* 来coding* 那接下来我们接着写* * * */Class cls = target.getClass();/*** 然后看一下这行* 这行是动态代理的核心* 把这个class的ClassLoader* 声明的接口* 注意这里面是复数* 还有他本身自己传进来* 那我们就进来看一下* */return Proxy.newProxyInstance(cls.getClassLoader(),cls.getInterfaces(),this);}
//    InvocationHandler h;/*** 这个方法三个参数* 第一个参数在我们平时写动态代理invoke方法的时候* 几乎用不上他* 那第二个参数是method* 也就是说要被增强的方法对象* 第三个是具体的method的参数* 那我们再说以proxy* proxy在这个方法实现里边* 很少使用这个对象* 那为什么还要传过来呢* 一会我们debug的时候* 也会来说一下proxy这个对象* 我们先写* 一会debug的时候* 来讲解一下* 首先我们声明一个对象* * 那在invoke的时候* 我们也说了* beforeMethod* 还有afterMethod* * 接着我们回到invoke方法中* invoke很简单* 我们首先把argument参数拿出来* 那因为我们这里面比较简单* 对于说这个方法就一个参数* 后续逻辑复杂的时候* 我们可以自己完善这个逻辑* * invoke也打上断点* 现在进入到invoke方法里面了* invoke有三个参数* proxy也就是这个代理类* method就是要增强的哪个方法呢* saveOrder* OrderService这个类的saveorder这个方法* 那后边的arguments呢* 自然是参数* 参数是Order* saveOrder的时候传入的参数呢* 正是Order* 那刚刚我前面有说* proxy在invoke的实现里面呢* 很少用到他* 但是他要传过来* 后面的method* 和arguments* 正来自对应的proxy* 很简单* 这个proxy代理类* 他呢是动态生成的* 那有兴趣的可以把这个动态生成的类* 进行持久化* 然后通过反编译* 看一下这个$符的class文件* 反编译的过来的代码* 里面有获取method* 因为这个method是proxy的method* 他通过Class.forName* 也就是method这个反射包下的method* 只有在proxy的这个实例* 在我们现在实现的这个接口* InvocationHandler* 加载之后才能产生对应的method* 所以这个生成的proxy* 传给invoke方法* 我们看一下InvocationHandler接口里面的声明* * 现在就要调用method的invoke方法* * */@Overridepublic Object invoke(Object proxy, Method method, Object[] args) throws Throwable {/*** 首先我们创建一个对象* argObject等于什么呢* 等于argument的第0个元素* * */Object argObject = args[0];/*** 然后调用beforeMethod* 把Order传进去* 也就是把这个参数传进去* 因为我们在调用saveOrder方法的时候* 传的参数正是Order类型* * 进入beforeMethod* * */beforeMethod(argObject);/*** 那这个时候我们声明一个Object* 就是要被增强的方法的返回值* 因为增强不同的方法* 返回值不一定一样* 但是他们的父类都是Object* 所以用Object来接他* method.invoke* invoke什么呢* 目标对象target* 把参数传进去* * 代理哪个类呢* target* 参数传进来* F6单步* 这个时候可以看到* 进入了真正的目标类的saveOrder方法里边* * */Object object = method.invoke(target,args);/*** 我们再调用一下afterMethod* * */afterMethod();/*** 最后把方法的返回值返回回去* 那这个动态代理就写完了* 非常简单* 我们现在写一个Test来run一下* * */return object;}/*** 我们先写一个beforeMethod* 用动态代理来实现* 这里我们就把调用方法的参数* 传进来* 传进来干嘛呢* 进行取模运算* * 我们就要把数据库确定了* 我们现在写一下* * 通过obj的一个类型* instanceof这么一个判断* 然后进行相同的取模逻辑* 确定目标DB* * 到这里边单步走* * * @param obj*/private void beforeMethod(Object obj){/*** 首先初始化一个userId等于0* 那我们就要在beforeMethod里面* 对userId进行取模* 然后确定分配到哪个db上* 当然分配到哪个db上* 我们可以把里面的实现* 写到invoke里边* * 动态代理* * */int userId = 0;System.out.println("动态代理 before code");/*** 如果obj是Order类型* 刚刚说了* 那我们也可以在这里进行判断* 抽奖信息这个类* 我们只有Order这个类* * 然后进行类型判断* * */if(obj instanceof Order){/*** 如果是这个类型就对他进行强转* obj强转成Order* * */Order order = (Order)obj;/*** 然后把userId赋值* 通过order的getUserId* 接下来就和静态代理是一样的* 我们把这里面的拿过来* copy到这里面* * 对userId进行赋值* * */userId = order.getUserId();}/*** 取模*/int dbRouter = userId % 2;/*** 这里面是动态代理db* * * */System.out.println("动态代理分配到【db"+dbRouter+"】处理数据");//todo 设置dataSource;DataSourceContextHolder.setDBType("db"+String.valueOf(dbRouter));}private void afterMethod(){/*** 这里面是动态代理after code* * */System.out.println("动态代理 after code");}
}
package com.learn.design.pattern.structural.proxy.staticproxy;import com.learn.design.pattern.structural.proxy.IOrderService;
import com.learn.design.pattern.structural.proxy.Order;
import com.learn.design.pattern.structural.proxy.OrderServiceImpl;
import com.learn.design.pattern.structural.proxy.db.DataSourceContextHolder;/*** 这个类是静态代理类* OrderService的静态代理* 这里面也很简单* 我们首先在这个静态代理类里边* * * @author Leon.Sun**/
public class OrderServiceStaticProxy {/*** 来注入目标对象* 那这里面的IOrderService* 是目标对象* 我们要对saveOrder方法* 进行增强* 那我们来到orderService里边* * */private IOrderService iOrderService;/*** 当然使用不同的方法名也是OK的* 这里面并没有严格的限制* 写一个before和after* 那后面我们讲源码解析的时候* 也会领着大家来看一下* 那就是Spring里面的beforeAdvice* 在AOP包下的* 还有afterAdvice* 只不过我们通过静态代理* 非常简单的一个实现* 所以我们声明两个方法* 一个beforeMethod* 还有一个* afterMethod* 直接输出* 输出什么呢* * * @param order* @return*/public int saveOrder(Order order){/*** 首先调用beforeMethod* * */beforeMethod(order);/*** 先把这个Service new出来* 这里面和DAO层一样* 如果使用Spring容器的话就不需要显示的来new了* 那现在我们获取userId* * service的实现* 如果使用Spring容器的话* 这里面不需要显示的来new他* * */iOrderService = new OrderServiceImpl();/*** 调用saveOrder方法* 然后把Order传进来* Spring的分库实现* 首先我们创建一个包* 这个包叫db* 这里面我们创建一个类* DynamicDataSource* 动态的数据源* * */int result = iOrderService.saveOrder(order);/*** 这个after我们要拿到对应的saveOrder之后* 也就是我们增强的是saveOrder* 怎么办呢* 在这里面再优化一下* 然后调用afterMethod* 这里面的界限怎么划分呢* 很简单* saveOrder是为了增强OrderService的saveOrder* 所以我们要把saveOrder这一行* 单独领出来* 那执行完saveOrder之后* 这个才是比较符合代理模式* 那像刚才这种实现方案* afterMetho和beforeMethod* 都在saveOrder前面* 都是一个beforeMethod* 所以这里埋了一个伏笔* 就是要对这一段要印象深刻* 那现在这种实现* 通过before就把目标数据库* 已经确定了* 那这块大家肯定都理解了* * * */afterMethod();return result;}/*** 执行saveOrder之前要执行的* * 那首先在saveOrder之前* 我们调用一下beforeMethod* 对saveOrder进行一个增强* * beforeMethod里面并没有写实现* 而是把分库的实现写到saveOrder里边* 所以这个时候静态代理* 我们就要改进一下了* 如何改进呢* 很简单* 例如beforeMethod把order传进来* 我们要把方法的界限划清楚* 执行代理模式的时候* 很容易划分不清楚* 埋了一个伏笔* 那这个初始化还是放到这里边* 我们把下边拿走* 拿到beforeMethod这里边* 然后调用的时候传进来* 但是我们又想一下* * * * @param order*/private void beforeMethod(Order order){/*** order获取userId* * * */int userId = order.getUserId();/*** 获得DB的路由编号* 用userId对2进行取模* 这样只会得到0或者1* * 在这里取完模之后呢* 我们就要写一段代码* 设置dataSource* 怎么设置呢* * 然后对2取模* 2对2取余余数是0* 所以dbRouter是0* * */int dbRouter = userId % 2;/*** 然后我们输出一下* 静态代理增强了OrderService的实现* 把DB切到DB Router这个上* db0或者db1* * 输出这一行* * */System.out.println("静态代理分配到【db"+dbRouter+"】处理数据");//todo 设置dataSource;/*** 我们直接调用DataSourceContextHolder* 我们自己写的* 然后setDBType* 把什么传进来呢* 把dbRouter* 只不过我们现在这里没有和Spring容器集成* String类型的* String.valueOf* 那这个静态代理类就写完了* 根据userId的不同* 插入到不同的DB当中* 一个是db0* 一个是db1* 那我们现在来到静态代理这个包下* 写一个Test* * 然后把DataSourceContextHolder里面的DBType* 设置为0* 当然这里讲代理模式的时候* 这一行执不执行都没关系* 只不过这里面只是为了扩展* 所以分库的一些知识点* 调一下他的getDBType* 可以看到这里面是0* 那这里面是有问题的* 这里面要存的是db0* 也就是说在写分库的时候* 这里很容易忽略* 里面的dbType和我们配置文件里面的是一样* 这里不要出bug* 而我在这里面就做了一个反面教材* * * */DataSourceContextHolder.setDBType("db"+String.valueOf(dbRouter));System.out.println("静态代理 before code");}/*** 这个是执行saveOrder之后要执行的代码* * */private void afterMethod(){System.out.println("静态代理 after code");}
}
package com.learn.design.pattern.structural.proxy.dynamicproxy;import com.learn.design.pattern.structural.proxy.IOrderService;
import com.learn.design.pattern.structural.proxy.Order;
import com.learn.design.pattern.structural.proxy.OrderServiceImpl;/*** 我们看到结果和预期是一样的* 动态代理before code* 然后动态代理分配到db0上处理数据* service层调用DAO层* 添加Order* DAO层添加Order成功* 动态代理after code* 这里和预期完全一致* 分配到db1上处理数据* 和预期是一样的* 那接下来我们debug来跑一下* 同时进行讲解* 那我们直接在这里面打上两个断点* 然后debug来跑一下* 首先进入这个动态代理类的构造器* 我们进来* * * * @author Leon.Sun**/
public class Test {/*** 写一个主函数* 如何测试呢* * 这里和静态的测试类差不多* 我们先拿过来* * * @param args*/public static void main(String[] args) {/*** 声明一个Order* new一个Order出来* * */Order order = new Order();/*** 注意1对2取模的时候* 余数是1* 2对2取余的时候* 余数是0* 所以我们在run的时候* 这里面应该是db0数据库* 我们run一下* 结果已经出来了* 和我们预期是一样的* 那我们现在debug来运行一下* 我们一起来体会一下* 流程是怎么走的* 直接debug* * */order.setUserId(2);/*** 现在我们设置userId* 为1* * */
//        order.setUserId(1);/*** 然后获取静态代理类* 我们就直接new一个好了* 那在实际的项目当中* 我们可以在这个代理类上增加注解* 比如说Component* 来表示这个类是一个组件* 这样的话我们直接使用就可以了* 那这里面我们采用new的方式* 是为了讲解方便* * 我们进到这个静态代理类里边* * 我们使用OrderService这么一个接口* 我们现在是动态代理* 我们直接代理这个接口就OK了* 所以用接口来接收他* orderServiceDynamicProxy这个不是静态代理* 而是动态代理* 看一下后边* 后边要怎么写呢* 我们首先要new一个动态代理这个类* 然后传入一个参数* 我们要增强哪个类呢* 当然是要增强service的实现* 也就是OrderServiceImpl* 这么一个类* 但是呢并没有结束* 注意我们在动态代理里面写的* 返回值bind这个方法* * 可以看到现在这个代理类* 我们来看一下这个类型* 他呢是一个$Proxy0* 是这么一个类型* 但是它是用IOrderService进行引用声明的* * */IOrderService orderServiceDynamicProxy = (IOrderService) new OrderServiceDynamicProxy(new OrderServiceImpl()).bind();/*** 然后我们调用它的saveOrder方法* order传进来* 我们直接run一下* 我们看一下结果* 首先静态执行了beforeMethod* 然后把它分配到db1上处理数据* 然后执行了afterCode* 也就是afterMethod里面的内容* 然后service层调用了DAO层* 添加Order* DAO层添加order成功* 那我们现在把它改成2* * */orderServiceDynamicProxy.saveOrder(order);}
}
package com.learn.design.pattern.structural.proxy;/*** @author Leon.Sun**/
public interface IOrderService {/*** 这里面有一个方法* saveOrder* 里面传一个实体Order* 保存这个订单* 再写一个DAO层的接口* * 我们要增强这个方法* 所以我们使用同样的方法名* * * * @param order* @return*/int saveOrder(Order order);
}
package com.learn.design.pattern.structural.proxy;/*** 他来实现IOrderService* 实现IOrderService里的方法* 当然我们现在这个分层比较简单* Service下边是DAO* 那在Service之上是Controller* 那也有业务逻辑复杂的情况下呢* 加一个Manager层* Manager位于DAO层之上* * * @author Leon.Sun**/
public class OrderServiceImpl implements IOrderService {/*** 把DAO层注入进来* 只不过我们这里并没有集成Spring* 所以像@Autowired和@Resource我们并不用加* 然后我们会通过人工注入的方式* 把OrderDao注入进来* 那我们写一个Service的实现* */private IOrderDao iOrderDao;@Overridepublic int saveOrder(Order order) {//Spring会自己注入,我们课程中就直接new了/*** 我们课程中就直接new了* 直接new出来* DAO层的implement* 当然如果这一行使用Spring容器管理的话* 我们就不用显示的来写了* 或者在应用层使用set方法注入进来* 或者通过构造器注入也是OK的* 我们为了简单* 直接在这里new了* 但是我们实际在也业务的时候* 是不应该这么写的* 因为这样DAO层会new很多对象* 那对于DAO层来说* 保持一个单例是OK的* * 这里面创建了一个DAO层* * */iOrderDao = new OrderDaoImpl();/*** 然后我们输出* service层调用DAO添加Order* * */System.out.println("Service层调用Dao层添加Order");/*** DAO层调用insert方法* 把Order传进来* 那这个Service实现就写完了* 那我们再看一下包* 这个包的类是非常的common的* 实体* DAO层* service层* 只不过我们讲设计模式都放在这个包下了* 下面我们就不分包了* 那现在我们要进行分库了* 怎么分库呢* 我们看一下Order* * 调用DAO的insert方法* * */return iOrderDao.insert(order);}
}

 


本文来自互联网用户投稿,文章观点仅代表作者本人,不代表本站立场,不承担相关法律责任。如若转载,请注明出处。 如若内容造成侵权/违法违规/事实不符,请点击【内容举报】进行投诉反馈!

相关文章

立即
投稿

微信公众账号

微信扫一扫加关注

返回
顶部