• Java的动态代理(DynamicProxy)


    代理的概述

           代理是一种常用的设计模式,其目的就是为其他对象提供一个代理以控制对某个对象的访问。代理类负责为委托类预处理消息,过滤消息并转发消息,以及进行消息被委托类执行后的后续处理。

    代理模式UML图:

     

    为了保持行为的一致性,代理类和委托类通常会实现相同的接口,所以在访问者看来两者没有丝毫的区别。通过代理类这中间一层,能有效控制对委托类对象的直接访问,也可以很好地隐藏和保护委托类对象,同时也为实施不同控制策略预留了空间,从而在设计上获得了更大的灵活性。Java 动态代理机制以巧妙的方式近乎完美地实践了代理模式的设计理念。

    动态代理的使用场景

    动态代理的好处是它比较灵活,可以在运行的时候才切入改变类的方法,而不需要预先定义它。

    动态代理一般我们比较少去手写,但我们用得其实非常多。

    在Spring项目中用的注解,例如依赖注入的@Bean、@Autowired,事务注解@Transactional等都有用到,

    换言之就是Srping的AOP(切面编程)。

    这种场景的使用是动态代理最佳的落地点,可以非常灵活地在某个类,某个方法,某个代码点上切入我们想要的内容,就是动态代理其中的内容。

    静态代理

    代理类和被代理类 实现同一个接口

    缺点就是一个代理类只能针对一个接口

    1 package Proxy.staticProxy;
    2 
    3 /**
    4  * 创建Person接口
    5  */
    6 public interface Person {
    7     // 交钱的方法
    8     void giveMoney();
    9 }
    package Proxy.staticProxy;

    /**
    * 需要代理的对象:
    *
    * 学生类
    */
    public class Student implements Person {
    private String name;

    public Student(String name) {
    this.name = name;
    }

    @Override
    public void giveMoney() {

    System.out.println(name + "交保护费50");
    }
    }
     1 package Proxy.staticProxy;
     2 
     3 /**
     4  * 学生代理类,也实现了Person接口,保存一个学生实体,这样既可以代理学生产生行为
     5  */
     6 public class StudentsProxy implements Person {
     7 
     8     //被代理的学生
     9     Student stu;
    10 
    11 
    12     public StudentsProxy(Person stu) {
    13         //只代理学生对象
    14         if (stu.getClass() == Student.class) {
    15             this.stu = (Student) stu;
    16         }
    17     }
    18 
    19     //代理上交保护费,调用被代理学生的交保护费行为
    20     @Override
    21     public void giveMoney() {
    22         stu.giveMoney();
    23     }
    24 }
     1 package Proxy.staticProxy;
     2 
     3 public class StaticProxyTest {
     4     public static void main(String[] args) {
     5 
     6         Person zhangsan = new Student("张三");
     7         Person moneitor = new StudentsProxy(zhangsan);
     8         moneitor.giveMoney();
     9         /**
    10          * 运行结果:
    11          * 
    12          * 张三交保护费50
    13          */
    14     }
    15 }

    动态代理

    动态代理分为两种 jdk 和 cglib

    jdk

    jdk 代理主要用到了

    接口InvocationHandler 此接口只有一个方法(代码如下)

    InvocationHandler的实现类可以理解成具体的代理实现

    类Proxy

    生成代理的具体的操作类,可以为一个or多个接口动态的实现代理类

    缺点 就是被代理的类必须是接口的实现类(依赖于接口),

    如果某些类没有实现接口 则不能用jdk代理

     1 package Proxy.DynamicProxy.JDKProxy;
     2 
     3 /**
     4  * 目标接口
     5  */
     6 public interface UserManager {
     7 
     8     public void addUser(String name ,String password);
     9     public void detUser(String name);
    10 }
     1 package Proxy.DynamicProxy.JDKProxy;
     2 
     3 public class UserManagerImpl implements UserManager {
     4     @Override
     5     public void addUser(String id, String password) {
     6         System.out.println("调用了UserManagerImpl.addUser()方法");
     7     }
     8 
     9     @Override
    10     public void detUser(String id) {
    11         System.out.println("调用了UserManagerImpl.detUser()的方法");
    12     }
    13 }
     1 package Proxy.DynamicProxy.JDKProxy;
     2 
     3 import java.lang.reflect.InvocationHandler;
     4 import java.lang.reflect.Method;
     5 import java.lang.reflect.Proxy;
     6 
     7 /**
     8  * JDK动态代理类
     9  */
    10 public class JDKDynamicProxy implements InvocationHandler {
    11     //需要代理的目标对象
    12     private Object targetObject;
    13 
    14     public Object newProxy(Object targetObject) {
    15         //将目标对象传入进行代理
    16         this.targetObject = targetObject;
    17         //将代理对象返回 //其中有三个参数
    18         return Proxy.newProxyInstance(
    19                 targetObject.getClass().getClassLoader(),
    20                 targetObject.getClass().getInterfaces(),
    21                 this);
    22     }
    23 
    24     // invoke 测试方法
    25     @Override
    26     public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
    27 
    28         // 代理前 :逻辑处理的方法(函数)
    29         checkPopedom();
    30         Object reflect = null;
    31         // 调用invoke()
    32         reflect = method.invoke(targetObject, args);
    33         // 代理后:
    34         isOK();
    35         return reflect;
    36     }
    37 
    38     private void isOK() {
    39         System.out.println("权限通过:isOK()");
    40     }
    41 
    42     private void checkPopedom() {
    43         System.out.println("检查权限:checkPopedom()");
    44     }
    45 
    46 }
     1 package Proxy.DynamicProxy.JDKProxy;
     2 
     3 /**
     4  * 测试类
     5  *
     6  */
     7 public class JDKDynamicProxyTest {
     8     public static void main(String[] args) {
     9 
    10         JDKDynamicProxy jdkDynamicProxy = new JDKDynamicProxy();
    11         //JDK动态代理对象传入一个需要代理的对象 然后用需要代理的对象接收就OK啦
    12         UserManager userManagerJDK = (UserManager) jdkDynamicProxy.newProxy(new UserManagerImpl());
    13         userManagerJDK.addUser("tom","root");
    14         userManagerJDK.addUser("jeck","root");
    15         /**
    16          * 运行结果:
    17          * 检查权限:checkPopedom()
    18          * 调用了UserManagerImpl.addUser方法
    19          * 权限通过:isOK()
    20          * 检查权限:checkPopedom()
    21          * 调用了UserManagerImpl.addUser方法
    22          * 权限通过:isOK()
    23          */
    24     }
    25 }

    cglib

    原理是针对target类 生成一个子类 覆盖方法实现增强

    缺点 基于继承 无法代理final类(final类无法被继承,如String)

    需要的jar包 :asm-3.3.1,cglib-2.2.jar ps:jar包版本不同可能会报错         

    详细情况请看:www.cnblogs.com/cruze/p/3865180.html#lable1

    参看文献:

    https://shifulong.iteye.com/blog/2166770

    https://www.cnblogs.com/gonjan-blog/p/6685611.html

    http://blog.jobbole.com/104433/

    https://baike.baidu.com/item/动态代理类/5087985?fr=aladdin

  • 相关阅读:
    sonar6.7.2启动报错
    linux 查看/修改jdk版本
    idea一款颜值很高的theme
    生成唯一UUID
    连接池异常
    手机网页点击后出现蓝色框
    iScroll4中事件点击一次却触发两次解决方案
    base.js
    javascript与css3动画学习笔记
    javascript对象学习笔记
  • 原文地址:https://www.cnblogs.com/kaikai-2018/p/10875030.html
Copyright © 2020-2023  润新知