设计模式之代理模式

模式定义

为其他对象提供一种代理,以控制对这个对象的访问。代理对象起到中介作用,可以去掉功能服务或者增加额外的功能服务。

静态代理

代理和被代理对象在代理之前是确定的。他们都是事先相同的接口或者继承相同的抽象类。

Alt text

创建一个接口Moveable,Car继承Moveable接口,要求用代理类来记录Car的运行时间。

Car继承Moveable接口:

1
2
3
public interface Moveable {
void move();
}

Car:

1
2
3
4
5
6
7
8
9
10
public class Car implements Moveable{
@Override
public void move() {
try {
Thread.sleep(new Random().nextInt(1000));
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}

继承实现静态代理

1
2
3
4
5
6
7
8
9
10
11
public class Car2 extends Car{
@Override
public void move() {
long startTime = System.currentTimeMillis();
System.out.println(startTime);
super.move();
long endTime = System.currentTimeMillis();
System.out.println(endTime);
System.out.println(endTime - startTime);
}
}

聚合实现静态代理

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
public class Car3 implements Moveable{
private Car car;
public Car3(Car car) {
this.car = car;
}
@Override
public void move() {
long startTime = System.currentTimeMillis();
System.out.println(startTime);
car.move();
long endTime = System.currentTimeMillis();
System.out.println(endTime);
System.out.println(endTime - startTime);
}
}
  • 考虑到继承方式扩展代理功能的不方便,推荐使用聚合实现静态代理。也就是代理类和实际类实现共同的接口,代理类中包含所实现接口的引用。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public class CarLogProxy implements Moveable{
private Moveable moveable;
public CarLogProxy(Moveable moveable) {
this.moveable = moveable;
}
@Override
public void move() {
System.out.println("log start");
moveable.move();
System.out.println("log end");
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
public class CarTimeProxy implements Moveable{
private Moveable moveable;
public CarTimeProxy(Moveable moveable) {
this.moveable = moveable;
}
@Override
public void move() {
long startTime = System.currentTimeMillis();
System.out.println(startTime);
moveable.move();
long endTime = System.currentTimeMillis();
System.out.println(endTime);
System.out.println(endTime - startTime);
}
}
1
2
3
4
5
6
7
8
public class Test {
public static void main(String[] args) {
Moveable car = new Car();
CarTimeProxy carTimeProxy = new CarTimeProxy(car);
CarLogProxy carLogProxy = new CarLogProxy(carTimeProxy);
carLogProxy.move();
}
}

动态代理

前面实现了Car类的静态代理,为其添加了运行时间记录和日志记录的功能。如果需要为火车或者自行车添加类似的功能,需要重新定义相同的类,类十分臃肿,同时代码重用性不高,因此引入了动态代理,也就是在程序

jdk动态代理

Alt text

  • InvocationHandler接口,接口中定义了一个invoke方法,invoke方法中第一个参数表示代理类,第二个参数指被代理的方法,args指该方法的参数数组。这个抽象方法会在代理类中动态实现。
1
2
3
4
public interface InvocationHandler {
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable;
}
  • Proxy:该类为动态代理类,其中的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
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
public static Object newProxyInstance(ClassLoader loader,
Class<?>[] interfaces,
InvocationHandler h)
throws IllegalArgumentException
{
Objects.requireNonNull(h);
final Class<?>[] intfs = interfaces.clone();
final SecurityManager sm = System.getSecurityManager();
if (sm != null) {
checkProxyAccess(Reflection.getCallerClass(), loader, intfs);
}
/*
* Look up or generate the designated proxy class.
*/
Class<?> cl = getProxyClass0(loader, intfs);
/*
* Invoke its constructor with the designated invocation handler.
*/
try {
if (sm != null) {
checkNewProxyPermission(Reflection.getCallerClass(), cl);
}
final Constructor<?> cons = cl.getConstructor(constructorParams);
final InvocationHandler ih = h;
if (!Modifier.isPublic(cl.getModifiers())) {
AccessController.doPrivileged(new PrivilegedAction<Void>() {
public Void run() {
cons.setAccessible(true);
return null;
}
});
}
return cons.newInstance(new Object[]{h});
} catch (IllegalAccessException|InstantiationException e) {
throw new InternalError(e.toString(), e);
} catch (InvocationTargetException e) {
Throwable t = e.getCause();
if (t instanceof RuntimeException) {
throw (RuntimeException) t;
} else {
throw new InternalError(t.toString(), t);
}
} catch (NoSuchMethodException e) {
throw new InternalError(e.toString(), e);
}
}

动态代理实现步骤

  1. 创建一个实现接口InvocationHandler的类,实现invoke方法
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 TimeHandler implements InvocationHandler{
private Object target;
public TimeHandler(Object target) {
this.target = target;
}
/**
*
* @param proxy 被代理的对象
* @param method 被代理对象的方法
* @param args 被代理对象的方法的参数
* @return Object对象,方法的返回值
* @throws Throwable
*/
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("time begin");
method.invoke(target);
System.out.println("time end");
return null;
}
}
  1. 创建被代理的类以及接口(Car以及Moveable)
  2. 调用Proxy的静态方法,创建一个代理类
  3. 通过代理调用方法
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
public class Test {
public static void main(String[] args) {
Car car = new Car();
InvocationHandler h = new TimeHandler(car);
Class<?> cls = car.getClass();
/**
* loader:类加载器
* interfaces:实现接口
* h :invocationHandler
*/
Moveable m = (Moveable) Proxy.newProxyInstance(cls.getClassLoader(), cls.getInterfaces(), h);
m.move();
}
}

JDK动态代理与CGLIB动态代理的区别

jdk动态代理 CGLIB动态代理
只能代理实现了接口的类 针对类来实现代理
没有实现接口的类不能实现JDK动态代理 对指定目标类产生一个子类,通过方法拦截技术拦截所有父类方法调用
JDK动态代理利用反射机制生成一个实现代理接口的匿名类,在调用具体方法前调用InvocationHandler来处理 cglib是利用asm开源包,对代理对象的class文件加载进来,通过修改其字节码生成子类来处理

模拟JDK动态代理的实现

实现功能,通过Proxy的newproxyInstance返回代理对象
1、声明一段源码,动态产生代理
2、编译源码,产生新的类
3、将这个类load到内存中,产生一个新的对象
4、return代理对象

handler:

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
package proxy;
import java.lang.reflect.Method;
public interface InvocationHandler {
public void invoke(Object o, Method m);
}
package proxy;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
public class TimeHandler implements InvocationHandler{
private Object target;
public TimeHandler(Object target) {
this.target = target;
}
@Override
public void invoke(Object o, Method m) {
try {
long starttime = System.currentTimeMillis();
System.out.println("汽车开始行驶....");
m.invoke(target);
long endtime = System.currentTimeMillis();
System.out.println("汽车结束行驶.... 汽车行驶时间:"+ (endtime - starttime) + "毫秒!");
} catch (IllegalAccessException | InvocationTargetException e) {
e.printStackTrace();
}
}
}

proxy:

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
public class Proxy {
public static Object newProxyInstance(Class infce, InvocationHandler h) throws IOException, ClassNotFoundException, NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
//创建源码
String rt = "\r\n";
String methodStr = "";
for(Method m : infce.getMethods()){
methodStr += " @Override" + rt +
" public void " + m.getName() + "() {" + rt +
" try{" + rt +
" Method md = " + infce.getName() + ".class.getMethod(\""
+ m.getName() + "\");" + rt +
" h.invoke(this,md);" +rt+
" }catch(Exception e){ e.printStackTrace();}" + rt +
" }" ;
}
String str =
"package proxy;" + rt +
"import java.lang.reflect.Method;" + rt +
"import proxy.InvocationHandler;" + rt+
"public class $Proxy0 implements " + infce.getName() + " {" + rt +
" public $Proxy0(InvocationHandler h) {" + rt +
" this.h = h;" + rt +
" }" + rt +
" private InvocationHandler h;" + rt+
methodStr + rt +
"}" ;
String fileName = System.getProperty("user.dir") + "/out/production/Design_Patterns_new/proxy/$Proxy0.java";
File file = new File(fileName);
FileUtils.writeStringToFile(file, str);
//编译源码
JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
StandardJavaFileManager fileManager = compiler.getStandardFileManager(null, null, null);
Iterable units = fileManager.getJavaFileObjects(fileName);
//编译任务
JavaCompiler.CompilationTask task = compiler.getTask(null, fileManager, null, null, null, units);
//执行任务
task.call();
fileManager.close();
//load到内存
ClassLoader loader = ClassLoader.getSystemClassLoader();
Class<?> c = loader.loadClass("proxy.$Proxy0");
Constructor constructor = c.getConstructor(InvocationHandler.class);
return constructor.newInstance(h);
}
}
Donate comment here