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

原文地址:https://www.cnblogs.com/kaikai-2018/p/10875030.html