• 设计模式-代理模式-静态代理和动态代理在Java中的使用示例


    场景

    代理模式(Proxy Pattern)

    租房中介、代理律师、售票黄牛、中介、婚介、经纪人、快递等,都是代理模式的实际体现。

    代理模式是指为其他对象提供一种代理,以控制对这个对象的访问。代理对象在客户端和目标对象

    之间起到中介作用,代理模式属于结构型设计模式。

    使用代理模式两个目的:一是保护目标对象,二是增强目标对象。

    静态代理

    比如,父母为自己的孩子相亲,这个相亲的过程实现如下

    顶层接口Person代码实现

    1. package com.ruoyi.demo.designPattern.staticProxy;
    2. /**
    3.  * 人有很多行为,比如下面的谈恋爱、找对象
    4.  */
    5. public interface Person {
    6.     public void findLove();
    7. }

    儿子要找对象,实现Son类

    1. package com.ruoyi.demo.designPattern.staticProxy;
    2. public class Son implements Person{
    3.     @Override
    4.     public void findLove() {
    5.         System.out.println("儿子要求:白富美");
    6.     }
    7. }

    父亲要帮儿子相亲,实现Father类

    1. package com.ruoyi.demo.designPattern.staticProxy;
    2. public class Father {
    3.     private Son son;
    4.     public Father(Son son){
    5.         this.son = son;
    6.     }
    7.     //获取目标对象的引用
    8.     public void  findLove(){
    9.         System.out.println("父亲帮忙安排相亲");
    10.         this.son.findLove();
    11.         System.out.println("双方同意,确定关系");
    12.     }
    13. }

    注意这里:

    父亲通过构造方法获取儿子的引用,并且在findLove中进行了增强,给儿子安排了相亲,然后再

    促使儿子去谈恋爱,调用findLove方法。

    测试代码

    1. package com.ruoyi.demo.designPattern.staticProxy;
    2. public class fatherTest {
    3.     public static void main(String[] args) {
    4.         Father father = new Father(new Son());
    5.         father.findLove();
    6.     }
    7. }

    注:

    博客:
    霸道流氓气质的博客_CSDN博客-C#,架构之路,SpringBoot领域博主
    关注公众号
    霸道的程序猿
    获取编程相关电子书、教程推送与免费下载。

    静态代理业务场景举例

    看一个实际的业务场景。

    在分布式业务中,需要分库分表,分库分表之后使用Java操作时可能需要配置多个数据源。

    通过设置数据源路由来动态切换数据源。

    创建Order订单类

    1. package com.ruoyi.demo.designPattern.staticProxy;
    2. public class Order {
    3.     private Object orderInfo;
    4.     private Long createTime;
    5.     private String id;
    6.     public Object getOrderInfo() {
    7.         return orderInfo;
    8.     }
    9.     public void setOrderInfo(Object orderInfo) {
    10.         this.orderInfo = orderInfo;
    11.     }
    12.     public Long getCreateTime() {
    13.         return createTime;
    14.     }
    15.     public void setCreateTime(Long createTime) {
    16.         this.createTime = createTime;
    17.     }
    18.     public String getId() {
    19.         return id;
    20.     }
    21.     public void setId(String id) {
    22.         this.id = id;
    23.     }
    24. }

    创建Order持久层操作类

    1. package com.ruoyi.demo.designPattern.staticProxy;
    2. public class OrderDao {
    3.     public int insert(Order order){
    4.         System.out.println("OrderDao创建order成功!");
    5.         return 1;
    6.     }
    7. }

    创建IOrderService接口

    1. package com.ruoyi.demo.designPattern.staticProxy;
    2. public interface IOrderService {
    3.     int createOrder(Order order);
    4. }

    创建OrderService实现类

    1. package com.ruoyi.demo.designPattern.staticProxy;
    2. public class OrderService implements IOrderService{
    3.     private OrderDao orderDao;
    4.     public OrderService(){
    5.         //如果使用Spring应该是自动注入的,这里为方便,直接在构造方法中初始化
    6.         orderDao = new OrderDao();
    7.     }
    8.     @Override
    9.     public int createOrder(Order order) {
    10.         System.out.println("OrderService 调用orderDao创建订单");
    11.         return orderDao.insert(order);
    12.     }
    13. }

    然后进行静态代理,主要完成的功能是:根据订单创建时间自动按年进行分库。

    先创建数据源路由对象,使用ThreadLocal的单例实现DynamicDataSourceEntry

    1. package com.ruoyi.demo.designPattern.staticProxy;
    2. //动态切换数据源
    3. public class DynamicDataSourceEntry {
    4.     //默认数据源
    5.     public final static String DEFAULT_SOURCE = null;
    6.     private final static ThreadLocal local = new ThreadLocal<>();
    7.     private DynamicDataSourceEntry(){}
    8.     //清空数据源
    9.     public static void clear(){
    10.         local.remove();
    11.     }
    12.     //获取当前正在使用的数据源名字
    13.     public static String get(){
    14.         return local.get();
    15.     }
    16.     //还原当前切换的数据源
    17.     public static void restore(){
    18.         local.set(DEFAULT_SOURCE);
    19.     }
    20.     //设置已知名字的数据源
    21.     public static void set(String source){
    22.         local.set(source);
    23.     }
    24.     //根据年份动态设置数据源
    25.     public static void set(int year){
    26.         local.set("DB_"+year);
    27.     }
    28. }

    创建切换数据源的代理类OrderServiceStaticProxy

    1. package com.ruoyi.demo.designPattern.staticProxy;
    2. import java.text.SimpleDateFormat;
    3. import java.util.Date;
    4. public class OrderServiceStaticProxy implements IOrderService{
    5.     private SimpleDateFormat yearFormat = new SimpleDateFormat("yyyy");
    6.     private IOrderService orderService;
    7.     public OrderServiceStaticProxy(IOrderService orderService){
    8.         this.orderService = orderService;
    9.     }
    10.     @Override
    11.     public int createOrder(Order order) {
    12.         before();
    13.         Long time = order.getCreateTime();
    14.         Integer dbRouter = Integer.valueOf(yearFormat.format(new Date(time)));
    15.         System.out.println("静态代理类自动分配到【DB_"+dbRouter+"】数据源处理数据");
    16.         DynamicDataSourceEntry.set(dbRouter);
    17.         orderService.createOrder(order);
    18.         after();
    19.         return 0;
    20.     }
    21.     private void before(){
    22.         System.out.println("Proxy before method");
    23.     }
    24.     private void after(){
    25.         System.out.println("Proxy after method");
    26.     }
    27. }

    来看测试代码

    1. package com.ruoyi.demo.designPattern.staticProxy;
    2. import java.text.ParseException;
    3. import java.text.SimpleDateFormat;
    4. import java.util.Date;
    5. public class StaticProxyTest {
    6.     public static void main(String[] args) {
    7.         try {
    8.             Order order = new Order();
    9.             SimpleDateFormat sdf = new SimpleDateFormat("yyyy/MM/dd");
    10.             Date date = sdf.parse("2022/10/29");
    11.             order.setCreateTime(date.getTime());
    12.             IOrderService orderService = new OrderServiceStaticProxy(new OrderService());
    13.             orderService.createOrder(order);
    14.         }catch (ParseException e) {
    15.             e.printStackTrace();
    16.         }
    17.     }
    18. }

    运行结果

     

    动态代理-JDK实现方式

    动态代理和静态代理思路基本一致,只不过动态代理功能更加强大,随着业务的扩展适应性更强。

    还以上面找对象为例,那么动态代理能适用复杂的业务场景。

    不仅包括父亲给儿子找对象,还包括媒婆、婚介所等,所以需要一个更加通用的解决方案。

    创建媒婆类

    1. package com.ruoyi.demo.designPattern.jdkDynamicProxy;
    2. import java.lang.reflect.InvocationHandler;
    3. import java.lang.reflect.Method;
    4. import java.lang.reflect.Proxy;
    5. public class JDKMeipo implements InvocationHandler {
    6.     //被代理的对象,把引用保存下来
    7.     private Object target;
    8.     public  Object getInstance(Object target) throws Exception{
    9.         this.target = target;
    10.         Class clazz = target.getClass();
    11.         return Proxy.newProxyInstance(clazz.getClassLoader(),clazz.getInterfaces(),this);
    12.     }
    13.     @Override
    14.     public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
    15.         before();
    16.         Object obj = method.invoke(this.target,args);
    17.         after();
    18.         return obj;
    19.     }
    20.     private void before(){
    21.         System.out.println("我是媒婆,已经确定你的需求,开始给你找对象");
    22.     }
    23.     private void after(){
    24.         System.out.println("合适的话,就在一起吧");
    25.     }
    26. }

    创建单身客户类

    1. package com.ruoyi.demo.designPattern.jdkDynamicProxy;
    2. import com.ruoyi.demo.designPattern.staticProxy.Person;
    3. public class Customer implements Person {
    4.     @Override
    5.     public void findLove() {
    6.         System.out.println("高富帅单身优质对象");
    7.     }
    8. }

    测试代码

    1. package com.ruoyi.demo.designPattern.jdkDynamicProxy;
    2. import com.ruoyi.demo.designPattern.staticProxy.Person;
    3. public class jdkDynamicProxy {
    4.     public static void main(String[] args) {
    5.         try {
    6.             Person obj = (Person) new JDKMeipo().getInstance(new Customer());
    7.             obj.findLove();
    8.         } catch (Exception e) {
    9.             e.printStackTrace();
    10.         }
    11.     }
    12. }

    为加深印象,用JDK动态代理修改上面数据源动态路由的业务

    创建动态代理的类

    1. package com.ruoyi.demo.designPattern.jdkDynamicProxy;
    2. import com.ruoyi.demo.designPattern.staticProxy.DynamicDataSourceEntry;
    3. import java.lang.reflect.InvocationHandler;
    4. import java.lang.reflect.Method;
    5. import java.lang.reflect.Proxy;
    6. import java.text.SimpleDateFormat;
    7. import java.util.Date;
    8. public class OrderServiceDynamicProxy implements InvocationHandler {
    9.     private SimpleDateFormat yearFormat = new SimpleDateFormat("yyyy");
    10.     private Object target;
    11.     public Object getInstance(Object target){
    12.         this.target = target;
    13.         Class clazz = target.getClass();
    14.         return Proxy.newProxyInstance(clazz.getClassLoader(),clazz.getInterfaces(),this);
    15.     }
    16.     @Override
    17.     public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
    18.         before(args[0]);
    19.         Object object = method.invoke(target,args);
    20.         after();
    21.         return object;
    22.     }
    23.     private void before(Object target){
    24.         System.out.println("Proxy before method");
    25.         try {
    26.             Long time = (Long) target.getClass().getMethod("getCreateTime").invoke(target);
    27.             Integer dbRouter = Integer.valueOf(yearFormat.format(new Date(time)));
    28.             System.out.println("动态代理类自动分配到【DB_"+dbRouter+"】数据源处理数据");
    29.             DynamicDataSourceEntry.set(dbRouter);
    30.         } catch (Exception e) {
    31.             e.printStackTrace();
    32.         }
    33.     }
    34.     private void after(){
    35.         System.out.println("Proxy after method");
    36.     }
    37. }

    测试代码如下

    1. package com.ruoyi.demo.designPattern.jdkDynamicProxy;
    2. import com.ruoyi.demo.designPattern.staticProxy.IOrderService;
    3. import com.ruoyi.demo.designPattern.staticProxy.Order;
    4. import com.ruoyi.demo.designPattern.staticProxy.OrderService;
    5. import java.text.ParseException;
    6. import java.text.SimpleDateFormat;
    7. import java.util.Date;
    8. public class DyDataDyProTest {
    9.     public static void main(String[] args) {
    10.         Order order = new Order();
    11.         SimpleDateFormat sdf = new SimpleDateFormat("yyyy/MM/dd");
    12.         Date date;
    13.         {
    14.             try {
    15.                 date = sdf.parse("2022/10/31");
    16.                 order.setCreateTime(date.getTime());
    17.                 IOrderService orderService = (IOrderService) new OrderServiceDynamicProxy().getInstance(new OrderService());
    18.                 orderService.createOrder(order);
    19.             } catch (ParseException e) {
    20.                 e.printStackTrace();
    21.             }
    22.         }
    23.     }
    24. }

    动态代理-CGLib方式

    还是 以上面媒婆为例

    1. package com.ruoyi.demo.designPattern.cglibDynamicProxy;
    2. import org.springframework.cglib.proxy.Enhancer;
    3. import org.springframework.cglib.proxy.MethodInterceptor;
    4. import org.springframework.cglib.proxy.MethodProxy;
    5. import java.lang.reflect.Method;
    6. public class CglibMeipo implements MethodInterceptor {
    7.     public Object getInstance(Class clazz) throws Exception{
    8.         Enhancer enhancer = new Enhancer();
    9.         enhancer.setSuperclass(clazz);
    10.         enhancer.setCallback(this);
    11.         return enhancer.create();
    12.     }
    13.     @Override
    14.     public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
    15.         //业务增强
    16.         before();
    17.         Object obj = methodProxy.invokeSuper(o,objects);
    18.         after();
    19.         return obj;
    20.     }
    21.     private void before(){
    22.         System.out.println("我是媒婆:要按照你的要求给你找对象");
    23.     }
    24.     private void after(){
    25.         System.out.println("如果觉得合适你们就谈恋爱吧");
    26.     }
    27. }

    创建单身客户类

    1. package com.ruoyi.demo.designPattern.cglibDynamicProxy;
    2. public class Customer {
    3.     public void findLove(){
    4.         System.out.println("白富美");
    5.     }
    6. }

    测试代码

    1. package com.ruoyi.demo.designPattern.cglibDynamicProxy;
    2. public class CGlibProxyTest {
    3.     public static void main(String[] args) {
    4.         try {
    5.             Customer obj = (Customer) new CglibMeipo().getInstance(Customer.class);
    6.             obj.findLove();
    7.         } catch (Exception e) {
    8.             e.printStackTrace();
    9.         }
    10.     }
    11. }

  • 相关阅读:
    NPS:使用 Windows NPS Server 部署 802.1X 无线认证(4)
    振南技术干货集:比萨斜塔要倒了,倾斜传感器快来!(1)
    遗传算法GA求解TSP问题
    自动化办公01 smtplib 邮件⾃动发送
    Android 音频框架 基于android 12
    MySQL开发技巧——行列转换
    filp_open
    网络安全工程师的职责 103.219.28.X
    ffmpeg的下载和编译(vs2022)
    初探工厂抽象模式
  • 原文地址:https://blog.csdn.net/BADAO_LIUMANG_QIZHI/article/details/127615676