• 静态代理,jdbc动态代理和cglib动态代理


    静态代理

     1、定义抽象主题接口。

    复制代码
    package com.zhangguo.Spring041.aop02;
    
    /**
     * 接口
     * 抽象主题
     */
    public interface IMath {
        //加
        int add(int n1, int n2);
    
        //减
        int sub(int n1, int n2);
    
        //乘
        int mut(int n1, int n2);
    
        //除
        int div(int n1, int n2);
    
    }
    复制代码

    2、主题类,算术类,实现抽象接口。

    复制代码
    package com.zhangguo.Spring041.aop02;
    
    /**
     * 被代理的目标对象
     *真实主题
     */
    public class Math implements IMath {
        //加
        public int add(int n1,int n2){
            int result=n1+n2;
            System.out.println(n1+"+"+n2+"="+result);
            return result;
        }
        
        //减
        public int sub(int n1,int n2){
            int result=n1-n2;
            System.out.println(n1+"-"+n2+"="+result);
            return result;
        }
        
        //乘
        public int mut(int n1,int n2){
            int result=n1*n2;
            System.out.println(n1+"X"+n2+"="+result);
            return result;
        }
        
        //除
        public int div(int n1,int n2){
            int result=n1/n2;
            System.out.println(n1+"/"+n2+"="+result);
            return result;
        }
    }
    复制代码

    3、代理类

    复制代码
     1 package com.zhangguo.Spring041.aop02;
     2 
     3 import java.util.Random;
     4 
     5 /**
     6  * 静态代理类
     7  */
     8 public class MathProxy implements IMath {
     9 
    10     //被代理的对象
    11     IMath math=new Math();
    12     
    13     //加
    14     public int add(int n1, int n2) {
    15         //开始时间
    16         long start=System.currentTimeMillis();
    17         lazy();
    18         int result=math.add(n1, n2);
    19         Long span= System.currentTimeMillis()-start;
    20         System.out.println("共用时:"+span);
    21         return result;
    22     }
    23 
    24     //减法
    25     public int sub(int n1, int n2) {
    26         //开始时间
    27         long start=System.currentTimeMillis();
    28         lazy();
    29         int result=math.sub(n1, n2);
    30         Long span= System.currentTimeMillis()-start;
    31         System.out.println("共用时:"+span);
    32         return result;
    33     }
    34 
    35     //乘
    36     public int mut(int n1, int n2) {
    37         //开始时间
    38         long start=System.currentTimeMillis();
    39         lazy();
    40         int result=math.mut(n1, n2);
    41         Long span= System.currentTimeMillis()-start;
    42         System.out.println("共用时:"+span);
    43         return result;
    44     }
    45     
    46     //除
    47     public int div(int n1, int n2) {
    48         //开始时间
    49         long start=System.currentTimeMillis();
    50         lazy();
    51         int result=math.div(n1, n2);
    52         Long span= System.currentTimeMillis()-start;
    53         System.out.println("共用时:"+span);
    54         return result;
    55     }
    56 
    57     //模拟延时
    58     public void lazy()
    59     {
    60         try {
    61             int n=(int)new Random().nextInt(500);
    62             Thread.sleep(n);
    63         } catch (InterruptedException e) {
    64             e.printStackTrace();
    65         }
    66     }
    67 }
    复制代码

    4、测试运行

    复制代码
     1 package com.zhangguo.Spring041.aop02;
     2 
     3 public class Test {
     4     
     5     IMath math=new MathProxy();
     6     @org.junit.Test
     7     public void test01()
     8     {
     9         int n1=100,n2=5;
    10         math.add(n1, n2);
    11         math.sub(n1, n2);
    12         math.mut(n1, n2);
    13         math.div(n1, n2);
    14     }
    15 }
    复制代码

    5、小结

    通过静态代理,是否完全解决了上述的4个问题:

    已解决:

    5.1、解决了“开闭原则(OCP)”的问题,因为并没有修改Math类,而扩展出了MathProxy类。

    5.2、解决了“依赖倒转(DIP)”的问题,通过引入接口。

    5.3、解决了“单一职责(SRP)”的问题,Math类不再需要去计算耗时与延时操作,但从某些方面讲MathProxy还是存在该问题。

    未解决:

    5.4、如果项目中有多个类,则需要编写多个代理类,工作量大,不好修改,不好维护,不能应对变化。

    如果要解决上面的问题,可以使用动态代理。

    动态代理,使用JDK内置的Proxy实现

    只需要一个代理类,而不是针对每个类编写代理类。

    在上一个示例中修改代理类MathProxy如下:

    复制代码
     1 package com.zhangguo.Spring041.aop03;
     2 
     3 import java.lang.reflect.InvocationHandler;
     4 import java.lang.reflect.Method;
     5 import java.lang.reflect.Proxy;
     6 import java.util.Random;
     7 
     8 /**
     9  * 动态代理类
    10  */
    11 public class DynamicProxy implements InvocationHandler {
    12 
    13     //被代理的对象
    14     Object targetObject;
    15     
    16     /**
    17      * 获得被代理后的对象
    18      * @param object 被代理的对象
    19      * @return 代理后的对象
    20      */
    21     public Object getProxyObject(Object object){
    22         this.targetObject=object;
    23         return Proxy.newProxyInstance(
    24                 targetObject.getClass().getClassLoader(), //类加载器
    25                 targetObject.getClass().getInterfaces(),  //获得被代理对象的所有接口
    26                 this);  //InvocationHandler对象
    27         //loader:一个ClassLoader对象,定义了由哪个ClassLoader对象来生成代理对象进行加载
    28         //interfaces:一个Interface对象的数组,表示的是我将要给我需要代理的对象提供一组什么接口,如果我提供了一组接口给它,那么这个代理对象就宣称实现了该接口(多态),这样我就能调用这组接口中的方法了
    29         //h:一个InvocationHandler对象,表示的是当我这个动态代理对象在调用方法的时候,会关联到哪一个InvocationHandler对象上,间接通过invoke来执行
    30     }
    31     
    32     
    33     /**
    34      * 当用户调用对象中的每个方法时都通过下面的方法执行,方法必须在接口
    35      * proxy 被代理后的对象
    36      * method 将要被执行的方法信息(反射)
    37      * args 执行方法时需要的参数
    38      */
    39     public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
    40         //被织入的内容,开始时间
    41         long start=System.currentTimeMillis();
    42         lazy();
    43         
    44         //使用反射在目标对象上调用方法并传入参数
    45         Object result=method.invoke(targetObject, args);
    46         
    47         //被织入的内容,结束时间
    48         Long span= System.currentTimeMillis()-start;
    49         System.out.println("共用时:"+span);
    50         
    51         return result;
    52     }
    53     
    54     //模拟延时
    55     public void lazy()
    56     {
    57         try {
    58             int n=(int)new Random().nextInt(500);
    59             Thread.sleep(n);
    60         } catch (InterruptedException e) {
    61             e.printStackTrace();
    62         }
    63     }
    64 
    65 }
    复制代码

    测试运行:

    复制代码
     1 package com.zhangguo.Spring041.aop03;
     2 
     3 public class Test {
     4     
     5     //实例化一个MathProxy代理对象
     6     //通过getProxyObject方法获得被代理后的对象
     7     IMath math=(IMath)new DynamicProxy().getProxyObject(new Math());
     8     @org.junit.Test
     9     public void test01()
    10     {
    11         int n1=100,n2=5;
    12         math.add(n1, n2);
    13         math.sub(n1, n2);
    14         math.mut(n1, n2);
    15         math.div(n1, n2);
    16     }
    17     
    18     IMessage message=(IMessage) new DynamicProxy().getProxyObject(new Message());
    19     @org.junit.Test
    20     public void test02()
    21     {
    22         message.message();
    23     }
    24 }
    复制代码

     小结:

     JDK内置的Proxy动态代理可以在运行时动态生成字节码,而没必要针对每个类编写代理类。中间主要使用到了一个接口InvocationHandler与Proxy.newProxyInstance静态方法,参数说明如下:

     使用内置的Proxy实现动态代理有一个问题:被代理的类必须实现接口,未实现接口则没办法完成动态代理。

    动态代理,使用cglib实现

    CGLIB(Code Generation Library)是一个开源项目,是一个强大的,高性能,高质量的Code生成类库,它可以在运行期扩展Java类与实现Java接口,通俗说cglib可以在运行时动态生成字节码。

     4.1、引用cglib,通过maven

     修改pom.xml文件,添加依赖

    保存pom.xml配置文件,将自动从共享资源库下载cglib所依赖的jar包,主要有如下几个:

    4.2、使用cglib完成动态代理,大概的原理是:cglib继承被代理的类,重写方法,织入通知,动态生成字节码并运行,因为是继承所以final类是没有办法动态代理的。具体实现如下:

    复制代码
     1 package com.zhangguo.Spring041.aop04;
     2 
     3 import java.lang.reflect.Method;
     4 import java.util.Random;
     5 
     6 import net.sf.cglib.proxy.Enhancer;
     7 import net.sf.cglib.proxy.MethodInterceptor;
     8 import net.sf.cglib.proxy.MethodProxy;
     9 
    10 /*
    11  * 动态代理类
    12  * 实现了一个方法拦截器接口
    13  */
    14 public class DynamicProxy implements MethodInterceptor {
    15 
    16     // 被代理对象
    17     Object targetObject;
    18 
    19     //Generate a new class if necessary and uses the specified callbacks (if any) to create a new object instance. 
    20     //Uses the no-arg constructor of the superclass.
    21     //动态生成一个新的类,使用父类的无参构造方法创建一个指定了特定回调的代理实例
    22     public Object getProxyObject(Object object) {
    23         this.targetObject = object;
    24         //增强器,动态代码生成器
    25         Enhancer enhancer=new Enhancer();
    26         //回调方法
    27         enhancer.setCallback(this);
    28         //设置生成类的父类类型
    29         enhancer.setSuperclass(targetObject.getClass());
    30         //动态生成字节码并返回代理对象
    31         return enhancer.create();
    32     }
    33 
    34     // 拦截方法
    35     public Object intercept(Object object, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
    36         // 被织入的横切内容,开始时间 before
    37         long start = System.currentTimeMillis();
    38         lazy();
    39 
    40         // 调用方法
    41         Object result = methodProxy.invoke(targetObject, args);
    42 
    43         // 被织入的横切内容,结束时间
    44         Long span = System.currentTimeMillis() - start;
    45         System.out.println("共用时:" + span);
    46         
    47         return result;
    48     }
    49 
    50     // 模拟延时
    51     public void lazy() {
    52         try {
    53             int n = (int) new Random().nextInt(500);
    54             Thread.sleep(n);
    55         } catch (InterruptedException e) {
    56             e.printStackTrace();
    57         }
    58     }
    59 
    60 }
    复制代码

    测试运行:

    复制代码
    package com.zhangguo.Spring041.aop04;
    
    public class Test {
        //实例化一个DynamicProxy代理对象
        //通过getProxyObject方法获得被代理后的对象
        Math math=(Math)new DynamicProxy().getProxyObject(new Math());
        @org.junit.Test
        public void test01()
        {
            int n1=100,n2=5;
            math.add(n1, n2);
            math.sub(n1, n2);
            math.mut(n1, n2);
            math.div(n1, n2);
        }
        //另一个被代理的对象,不再需要重新编辑代理代码
        Message message=(Message) new DynamicProxy().getProxyObject(new Message());
        @org.junit.Test
        public void test02()
        {
            message.message();
        }
    }
    复制代码

    运行结果:

    4.3、小结

    使用cglib可以实现动态代理,即使被代理的类没有实现接口,但被代理的类必须不是final类。

    如果项目中有些类没有实现接口,则不应该为了实现动态代理而刻意去抽出一些没有实例意义的接口,通过cglib可以解决该问题。

  • 相关阅读:
    【转】验证实例名称是否正确并且 SQL Server 已配置为允许远程连接。
    【转】在控制台、WinForm项目中的嵌入mdf文件的烦恼
    【转】ADB server didn't ACK
    【转】android中AVD的使用
    【转】你不可以不知道的findViewById
    【转】Android中的view
    【转】c# 如何获取项目的根目录
    TYVJ 1452 最大权闭合图
    NOIP 最后的总结
    POJ 2396 有上下界的可行流
  • 原文地址:https://www.cnblogs.com/yangsongxiao/p/6628818.html
Copyright © 2020-2023  润新知