设计模式之动态代理

代理模式是几种常见的设计模式中比较难易理解的,通过代理模式可以为另一个对象提供代理,以便控制客户对对象的访问,管理访问的方式有很多种。动态代理是代理模式中的一种,JDK中 java.lang.reflect 包提供了对代理的支持,在运行时我们可以动态地创建代理类,间接地过一个句柄我们就可以访问实际的类方法了。

生成的代理 Proxy 实际上和要调用的目标类实现了同一接口,Proxy 还持有一个InvocationHandler实例的引用,而这个实例持有目标类的引用。当调用 Proxy 的方法的时候,Proxy会转发给InvocationHandler的实例,由其调用目标类的方法,从而达到由代理对象控制实际对象行为的目的。





先建立一个Student接口

一个Student 包含姓名、学号和成绩这几个属性,在接口里声明其setter和getter。

1
2
3
4
5
6
7
8
public interface Student {
void setName(String name);
void setNumber(String number);
void setScore(int score);
String getName();
String getNumber();
int getScore();
}

实现Student接口

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
public class StudentImpl implements Student {
String name;
String number;
int score;
@Override
public void setName(String name) {
this.name = name;
}
@Override
public void setNumber(String number) {
this.number = number;
}
@Override
public void setScore(int score) {
this.score = score;
}
@Override
public String getName() {
return name;
}
@Override
public String getNumber() {
return number;
}
@Override
public int getScore() {
return score;
}
}

实现自己的InvocationHandler

我们假设一个Student可以更改自己的姓名和学号信息,但是不可以随意更改自己的成绩。在MyInvocationHandler 的 invoke() 方法里进行判断,如果用户想调用Proxy的 setCore() 方法修改成绩,抛出一个异常;如果是Proxy的实现自 Student 的其它方法,则改由调用目标类的方法;如果调用的方法 Student 接口里没有,则返回null。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
public class MyInvocationHandler implements InvocationHandler {
Student student;
public MyInvocationHandler(Student s ) {
this.student = s;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws IllegalAccessException {
if (method.getName().equals("setScore")) {
throw new IllegalAccessException();
} else if (method.getName().startsWith("set") | method.getName().startsWith("get")){
try {
return method.invoke(student, args);
} catch (InvocationTargetException e) {
e.printStackTrace();
}
}
return null;
}
}

开始测试代码

为方便测试,我们先实例化一个Student,然后改由代理处理。getProxy() 调用系统提供的方法生成代理对象,我们按部就班地输入要求的参数即可。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
public class ProxyTest {
public static void main(String []args) {
Student s = new StudentImpl();
s.setName("jim");
Student proxy = getProxy(s);
try {
proxy.setName("tom");
System.out.println(proxy.getName());
proxy.setScore(12);
System.out.println(proxy.getScore());
}catch(Exception e){
System.out.println("Sorry! You can't set score!");
}
}
static Student getProxy(Student s) {
return (Student) Proxy.newProxyInstance(s.getClass().getClassLoader(), s.getClass().getInterfaces(),
new OwnerInvercationHandler(s));
}
}
//输出: tom
//输出: Sorry! You can't set score!