• 静态代理和动态代理简单实现


    静态代理:

    静态代理的实现要求:真实角色,代理角色;真实角色和代理角色要实现同一个接口,代理角色要持有真实角色的引用。

      在Java中线程的设计就使用了静态代理设计模式,其中自定义线程类实现Runable接口,Thread类也实现了Runalbe接口,

      在创建子线程的时候,传入了自定义线程类的引用,再通过调用start()方法,调用自定义线程对象的run()方法。实现了线程的并发执行。

    需求:在被代理类执行方法的时候,能够打印日志信息,比如当前的时间节点

    1、业务方法,被代理类和代理类都要实现这个接口:

    public interface LogService {
        void logTime();
    }

    2、被代理类

    public class BeProxy implements LogService{
        public void logTime() {
            System.out.println("被代理类执行了~");
        }
    }

    3、代理类:代理角色实现被代理类需要的功能。

    public class Proxy implements LogService {
        // 持有代理角色的引用
        private BeProxy beProxy;
    
        public Proxy() {
        }
    
        public Proxy(BeProxy beProxy) {
            this.beProxy = beProxy;
        }
    
        public void logTime() {
            System.out.println(new SimpleDateFormat("HH:mm:ss SSS").format(new Date(System.currentTimeMillis())) + " 代理对象开始执行");
            // 执行的是被代理角色的功能
            beProxy.logTime();
            System.out.println(new SimpleDateFormat("HH:mm:ss SSS").format(new Date(System.currentTimeMillis())) + " 代理对象结束执行");
        }
    }

    4、测试

    public class Test {
        public static void main(String[] args) {
            // 被代理对象
            BeProxy beProxy = new BeProxy();
    
            // 代理者对象
            Proxy proxy = new Proxy(beProxy);
    
            proxy.logTime();
        }
    }

    5、结果:

    00:48:21 119 代理对象开始执行
    被代理类执行了~
    00:48:21 121 代理对象结束执行

    静态代理的问题:

    上面的代码中,为了给被代理类类做日志增强,我们编写了代理类,而且准备了一个构造器接收目标对象。

    代理对象构造器的参数类型是LogService,这意味着它只能接受LogService 的实现类对象,亦即我们写的代理类Proxy只能给LogService做代理,它们绑定死了!

    如果现在我们系统需要全面改造,要给其他类也添加日志打印功能,就得为其他几百个接口都各自写一份代理类...

    动态代理案例一:

    动态代理和静态代理角色一样
    态代理的代理类是动态生成的,不是我们直接写好的!
    动态代理分为两大类:基于接口的动态代理,基于类的动态代理

    • 基于接口--- JDK动态代理 [我们在这里使用]
    • 基于类: cglib
    • java字节码实现 : javasist

    需要了解两个类:

    java.lang.reflect.Proxy; 代理,
        static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h) 
              返回一个指定接口的代理类实例,该接口可以将方法调用指派到指定的调用处理程序。 
    
    java.lang.reflect.InvocationHandler: 调用处理程序
        Object invoke(Object proxy, Method method, Object[] args) 
                  在代理实例上处理方法调用并返回结果。

    动态代理具体步骤:

    1. 通过实现 InvocationHandler 接口创建自己的调用处理器;
    2. 通过为 Proxy 类指定 ClassLoader 对象和一组 interface 来创建动态代理类;
    3. 通过反射机制获得动态代理类的构造函数,其唯一参数类型是调用处理器接口类型;
    4. 通过构造函数创建动态代理类实例,构造时调用处理器对象作为参数被传入。

    1、业务方法

    public interface LogService {
        void logTime();
    }

    2、被代理类

    public class BeProxy implements LogService{
        @Override
        public void logTime() {
            System.out.println("被代理类执行了~");
        }
    }

    3、处理程序类,可以自动生成代理类

    import java.lang.reflect.InvocationHandler;
    import java.lang.reflect.Method;
    import java.lang.reflect.Proxy;
    
    // 处理程序类,用这个类可以自动生成代理类
    public class ProxyInvocationHandler implements InvocationHandler {
        // 被代理的接口
        private LogService logService;
    
        public void setLogService(LogService logService) {
            this.logService = logService;
        }
    
        // 生成得到代理类
        public Object getProxy(){
            return Proxy.newProxyInstance(this.getClass().getClassLoader(),logService.getClass().getInterfaces(),this);
        }
    
        @Override
        /**处理代理实例,并返回结果*/
        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
            Object invoke = method.invoke(logService, args);
            return invoke;
        }
    }

    4、测试类

    public class Test {
        public static void main(String[] args) {
            // 真实角色
            BeProxy proxy = new BeProxy();
            // 代理角色【不存在】
            ProxyInvocationHandler handler = new ProxyInvocationHandler();
            // 设置要代理的对象
            handler.setLogService(proxy);
            // 生成代理类对象
            LogService logService = (LogService) handler.getProxy();
            // 执行角色方法
            logService.logTime();
        }
    }

    5、结果

    被代理类执行了~

    动态代理案例二:

    需求:

    需求:
        有一个service接口,调用这个接口中的方法:
            调用的方法是doSome-->在方法开始前打印系统当前时间,以及在最后打印一句话
            调用的方法是doOther-->就正常输出
    

     

    1、在开始和结束要输出的两句话(进行一个简单封装)

     1 import java.text.SimpleDateFormat;
     2 import java.util.Date;
     3 
     4 /**
     5  * @author zhangzhixi
     6  */
     7 public class Utils {
     8     public static void currentTime() {
     9         SimpleDateFormat sdf = new SimpleDateFormat("yyyy年MM月dd日 HH:mm:ss");
    10         String showTime = sdf.format(new Date());
    11         System.out.println("当前系统时间为:" + showTime);
    12     }
    13 
    14     public static void executeTransaction(){
    15         System.out.println("事务执行结束");
    16     }
    17 }

     2、业务接口:

    1 public interface UserService {
    2     void doSome();
    3     void doOther();
    4 }

    3、业务接口的实现类:

     1 public class UserServiceImpl implements UserService {
     2     @Override
     3     public void doSome() {
     4         System.out.println("doSome方法执行!~");
     5     }
     6 
     7     @Override
     8     public void doOther() {
     9         System.out.println("doOther方法执行!~");
    10     }
    11 }

    4、创建调用处理程序类

     1 package com.zhixi.invaction;
     2 
     3 import com.zhixi.service.UserService;
     4 import com.zhixi.util.Utils;
     5 
     6 import java.lang.reflect.InvocationHandler;
     7 import java.lang.reflect.Method;
     8 import java.lang.reflect.Proxy;
     9 
    10 /**
    11  * @author zhangzhixi
    12  */
    13 public class MyInvocation implements InvocationHandler {
    14 
    15     /**
    16      * 被代理的接口
    17      */
    18     UserService userService;
    19 
    20     public void setUserService(UserService userService) {
    21         this.userService = userService;
    22     }
    23 
    24     public Object getProxy() {
    25         // 生成得到代理类
    26         return Proxy.newProxyInstance(this.getClass().getClassLoader(), userService.getClass().getInterfaces(), this);
    27     }
    28 
    29 
    30     /**
    31      * @param proxy 代理对象
    32      * @param method 代理的方法对象
    33      * @param args 方法调用时参数
    34      */
    35     @Override
    36     public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
    37         Object invoke = null;
    38 
    39         // 调用了doSome就执行这段业务
    40         if ("doSome".equals(method.getName())) {
    41             // 增加的业务
    42             Utils.currentTime();
    43 
    44             /**
    45              * userService: 被代理的类
    46              * args: 参数
    47              */
    48             invoke = method.invoke(userService, args);
    49 
    50             // 增加的业务
    51             Utils.executeTransaction();
    52         } else {
    53             //说明调用了doOther
    54             invoke = method.invoke(userService, args);
    55         }
    56         return invoke;
    57     }
    58 }

    5、测试类:

     1 public class MyTest {
     2     public static void main(String[] args) {
     3         // 创建目标对象
     4         UserService userService= new UserServiceImpl();
     5         // 代理对象【不存在】
     6         MyInvocation invocation = new MyInvocation();
     7         // 设置要代理的对象
     8         invocation.setUserService(userService);
     9         // 生成代理
    10         UserService service = (UserService) invocation.getProxy();
    11         // 调用对象的业务方法
    12         service.doSome();
    13         System.out.println("===============");
    14         service.doOther();
    15     }
    16 }

    6、测试结果:

    当前系统时间为:2021年01月07日 21:56:41
    doSome方法执行!~
    事务执行结束
    ===============
    doOther方法执行!~
  • 相关阅读:
    数据结构之 线性表 逆序简历链表
    数据结构之 线性表--顺序创建链表
    参观——校园招聘大会
    SDUT OJ 之 1571 《人品,有没有?》 字符串的处理问题
    青岛理工交流赛 H题 素数间隙
    青岛理工ACM交流赛 J题 数格子算面积
    STL版 括号匹配(感觉不如之前自己用数组模拟的跑的快)
    1076: [SCOI2008]奖励关( dp )
    BZOJ 1079: [SCOI2008]着色方案( dp )
    BZOJ 1984: 月下“毛景树”( 树链剖分 )
  • 原文地址:https://www.cnblogs.com/zhangzhixi/p/14224602.html
Copyright © 2020-2023  润新知