06-动态代理模式

参考资料1:http://c.biancheng.net/design_pattern/

参考资料2:https://refactoringguru.cn/design-patterns/catalog

Java动态代理设计模式

代理模式是Java常见的设计模式之一。
代理模式是指不直接调用实际对象,而是通过调用代理,来间接的调用实际的对象。

为什么要采用这种间接的形式来调用对象呢?
场景①:不想直接访问实际的对象;
场景②:对实际的对象的访问存在困难
场景③:有需求需要将封装对象访问/操作的成员方法进行逻辑增强,而不修改原方法
在现实生活中,这种情形非常的常见,比如请一个律师代理来打官司

代理模式可以有 2 种实现的方式:
一种是静态代理,另一种是各大框架都喜欢的动态代理
还有一种类似静态代理的装饰者模式

现有 User 实体类:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
public class User {
private int id; // 编号属性 id
private String name; // 姓名属性 name
public User() { }
public User(int id, String name) {
this.id = id;
this.name = name;
}
public int getId() { return id; }
public void setId(int id) { this.id = id; }
public String getName() { return name; }
public void setName(String name) { this.name = name; }
@Override
public String toString() {
return "User{" + "id=" + id + ", name='" + name + '\'' + '}';
}
}

DAO封装数据访问思想:用来封装对实体类 User 类的访问/操作。
UserDao 接口:

1
2
3
4
5
6
public interface UserDao {
void add(String id, String id2)
void delete();
void update();
void select();
}

UserDaoImpl 实现类:

1
2
3
4
5
6
7
8
9
10
11
12
public class UserDaoImpl implements UserDao {
@Override
public void add(String id, String id2) {
System.out.println("add:" + id + "," + id2);
}
@Override
public void delete() { System.out.println("delete"); }
@Override
public void update() { System.out.println("update"); }
@Override
public void select() { System.out.println("select"); }
}

需求:通过 静态代理、装饰者、动态代理模式分别 增强 add() 方法的逻辑功能

动态代理是通过反射来实现的,借助Java自带的 java.lang.reflect.Proxy 通过固定的规则生成。
动态代理本质就是通过反射来生成的一个代理。
在Java中 java.lang.reflect 包下提供了一个 Proxy 类InvocationHandler 接口,通过使用这个类和接口就可以生成动态代理对象,Proxy类中的方法创建动态代理对象。

JDK提供的代理只能针对接口做代理,有更强大的代理cglib
一个强大的,高性能,高质量的Code生成类库,它可以在运行期扩展Java类与实现Java接口。

1.1 实现步骤

步骤:——基于接口的方法增强
① 编写一个代理类的接口
② 实现一个真正的代理类,实现该接口
③ 创建一个动态的代理类,实现 InvocationHandler 接口,并重写该 invoke() 方法
④ 在测试类中调用 Proxy.newProxyInstance() 方法生成动态代理对象

优化:③④可合并为实现了对应接口匿名内部类替代。
即 在调用该动态代理对象的成员方法时,是 invoke 增强的方法执行。

1.2 逻辑图 & 源码示例

顶部。

Proxy.newProxyInstance() 方法的参数解释:

1
2
3
4
5
6
7
8
9
10
11
/*
* 通过反射来实现的动态代理核心方法
* @loader: 被代理类对应的类加载器
* @interfaces: 被代理类所实现的所有接口
* @h: 一个用以增强代理类的处理器
* @return Object对象
*/
@CallerSensitive
public static Object newProxyInstance(ClassLoader loader,
Class<?>[] interfaces,
InvocationHandler h);

InvocationHandler 接口的 invoke 方法参数解释:

1
2
3
4
5
6
7
8
9
/**
* 在代理实例上处理方法调用,并返回结果
* @param proxy 代理类的实例
* @param method 被代理类的原方法
* @param args 被代理类的原方法的实际参数
* @return Object对象
*/
@Override
public Object invoke(Object proxy, Method method, Object[] args);

匿名内部类直接测试:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
import org.springframework.cglib.proxy.Proxy;

public class TestProxy {
public static void main(String[] args) {
UserDao userDao = new UserDaoImpl();
//动态代理设计模式:匿名内部类对象
UserDao p = (UserDao) Proxy.newProxyInstance(
userDao.getClass().getClassLoader(),
userDao.getClass().getInterfaces(),
//Lambda表达式,等价于 new InvocationHandler(){ invoke(){...} }
(proxy, method, argss) -> {
String methodName = method.getName();
//argss 永不为空,无参时 argss.length=0
for (int i = 0; i < argss.length; i++) {
//代理对象对入参进行更改
argss[i] = "88";
}
Object result;
//方法名比较:只增强指定的需要增强的方法
if ("add".equals(methodName)) {
System.out.println("权限校验");
result = method.invoke(userDao, argss);
System.out.println("日志记录");
} else {
result = method.invoke(userDao, argss);
}
return result;
});
p.add("22", "33");
p.delete();
}
}

Java动态代理设计模式

image-20210628204434989

1.3 动态代理设计模式 - 优缺

优点:不需要重写接口中的所有方法,耦合度更低
缺点:没有,动态代理就是这么任性,这条gai最靓的仔本仔!


06-动态代理模式
https://janycode.github.io/2018/04/28/10_设计模式/06-动态代理模式/
作者
Jerry(姜源)
发布于
2018年4月28日
许可协议