六游的博客小站
Java的动态代理
发布于: 2019-03-24 更新于: 2019-03-31 阅读次数: 

什么是代理模式

代理模式的含义就是,存在一个代理类,代理类中持有真实对象的引用,以实现我们对真实对象的访问控制。代理模式有低耦合,外界无感知的优点。使用代理模式可以帮助我们简化许多需求的开发。比如说你需要对现有的业务代码加入日志记录与前置数据验证的功能,如果我们一个一个的去修改源码,就显得有点不够优雅了。面临这样的情况,我们使用代理模式会使开发过程清晰轻松很多。下面让我们一步一步地来揭开代理模式的外衣。

静态代理的底层实现

首先让我们来看一下最经典的代理模式–静态代理。从下面的静态代理的UML图我们可以大致了解到静态代理的工作方式,代理类与真实事务类需要实现同一接口,其中代理类中持有真实对象的引用,我们通过调用代理类中的方法间接调用真实类的方法,同时我们还可以在调用真实类的方法前后加入一些操作,比如访问控制、异常日志记录等。

1.jpg

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
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
//真实对象的接口
public interface UserDao {
void userAdd(String userName, String password);

void userDelete(String userName);
}

//真实事务类
public class UserDaoImpl implements UserDao{

@Override
public void userAdd(String userName, String password){
//Db操作语句
****;
****;
}

@Override
public void userDelete(String userName){
//Db操作语句
****;
****;
}
}

//代理类
public class UserDaoProxy implements UserDao{
//在代理类中持有真实事务类的对象
private UserDaoImpl userDaoImpl;
//初识化持有对象
public UserDaoProxy(){
this.userDaoImpl = new UserDaoImpl();
}

@Override
public void userAdd(String userName, String password){
//此处可添加数据删选,验证以及限流等操作
*****;
try{
userDaoImpl.userAdd(String userName, String password);
}catch(SQLException e){
//此处可添加对异常的记录或处理以及异常通知等
System.out.println("新增用户操作异常日志");
}
//此处可添加操作成功的日志或其他后置通知
System.out.println("新增用户成功日志记录");
}

@Override
public void userDelete(String userName){
try{
userDaoImpl.userDelete(String userName);
}catch(SQLException e){
System.out.println("删除用户操作异常日志");
}
System.out.println("删除用户成功日志记录");
}
}

通过JDK实现动态代理

通过以上静态代理的代码实现我们可以发现,用静态代理处理一个切面需求是很繁琐的,我们需要手动的去创建一个真实类的代理类,并且实现所有真实事务类接口中的所有方法。其实jdk为我们提供了更为方便与高效的实现代理模式的方法,那就是动态代理。我们不需要自己手动地创建代理类,而是让程序内部动态地为我们生成代理类实例。
动态代理通过JDK的Proxy类和一个调用处理器(实现InvocationHandler接口)实现,Proxy类帮助我们动态生成代理类,而调用处理器帮助我们控制真实对象方法的调用。

1
2
3
4
5
6
7
8
9
10
11
//InvocationHandler接口中仅有一个invoke方法
//第一个参数 proxy 为我们需要代理的对象
//第二个参数 method 执行的方法,用来获取方法的相关信息
//第三个参数 args 方法需要传入的参数
invoke(Object proxy, Method method, Object args[]);

//我们还需要了解Proxy类中的newProxyInstance方法,该方法帮助我们生成指定对象指定处理器的代理类
//第一个参数,类加载器
//第二个参数,真实对象的所有接口
//第三个参数,代理处理器
newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h)

newProxyInstance方法会获取当前类加载器,真实类的所有接口以及我们定义的代理处理器,并根据这些信息帮我们动态生成一个代理实例,这个实例所对应的类实现了真实类的所有接口,并且在方法实现的过程中添加了我们在代理处理器中通过invoke实现的对真实类方法的控制。其实可以理解为,这个newProxyInstance方法就是帮助我们生成了静态代理中我们需要手写的代理类,并且放回了代理类的对象。

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
//还以上文中静态代理的真实对象为例子,用动态代理去实现
public class UserDaoProxyHandler implements InvocationHandler(){
//持有真实事务类的实例
private UserDaoImpl target;
//初识化真实事务类的实例
public UserDaoProxyHandler(){
this.target = new UserDaoImpl();
}

@Override
public Object invoke(Object proxy, Method method, Object[] args)throws Throwable {
System.out.println("前置通知,可以做数据校验,限流等操作");
try{
//真正调用真实事务类中的方法
o = method.invoke(target, args);
System.out.println("返回通知");
}catch(Exception e){
System.out.println("异常通知,可以做异常相关处理");
}
System.out.println("后置通知");
}

//通过Proxy类放回代理实例
public Object getProxy(){
return Proxy.newProxyInstance(this.getClass.getClassLoader, target.getClass().getInterfaces(), this);
}
}

下面我们来写一个测试类去通过动态代理调用真实事务类中的方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public class proxyTest(){

@Test
public void test(){
//创建调度处理器实例
UserDaoProxyHandler h = new UserDaoProxyHandler();
//获取用户代理类实例
UserDaoImpl useDaoImpl=(UserDaoImpl) h.getProxy();
//直接调用方法
userDaoImpl.userAdd("user1","123456");

userDaoImpl.userDelete("user1");

}
}
--- 本文结束 The End ---