设计模式-代理模式

什么是静态代理模式?

  在代理模式中,用一个类代表另一个类的功能,从而达到控制对真实对象的访问的目的。静态代理模式的代理类由程序员编写。

优缺点

  静态代理设计模式可以保护真实对象,让真实对象的职责更加清晰,同时使对象具有高可扩展性。
  但如果代理功能较多,代理类中需要编写较多的方法,同时代理模式可能会使效率下降。

实现:

UML图:

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 interface Image {
void display();
}
// 真实类
public class RealImage implements Image{
String fileName;

public RealImage(String fileName) {
super();
this.fileName = fileName;
loadFromDisk(fileName);
}

@Override
public void display() {
System.out.println("Displaying" + fileName);
}

private void loadFromDisk(String fileName) {
System.out.println("Loading" + fileName);
}
}
// 代理类
public class ProxyImage implements Image{
private RealImage realImage;
private String fileName;

public ProxyImage(String fileName) {
this.fileName = fileName;
}

@Override
public void display() {
if (realImage == null) {
realImage = new RealImage(fileName);
}
realImage.display();
}

}
// 测试代码
public class Test {
public static void main(String[] args) {
Image image = new ProxyImage("test.jpg");
image.display();
System.out.println("----");
image.display();
}
}

应用场景

什么是JDK动态代理模式?

  相比于静态代理,JDK动态代理解决了静态代理模式频繁编写代理功能的缺点。
  用到的核心技术有:反射与静态代理。

优缺点:

  其不需要自己实现代理类,但由于使用了反射机制,效率不高,且真实对象需要实现接口。

实现:

  实现时需要用到java.lang.reflect.Proxy和java.lang.reflect.InvocationHandler,前者用来动态生成代理类和对象,后者是处理器接口,用来实现具体的逻辑,可以通过invoke方法实现对真实角色的代理访问。
  具体实现原理为:Proxy类通过newProxyInstance动态生成了一个代理类,当执行代理方法时,代理类直接调用了InvocationHandler处理器(相当于静态代理)
对象的invoke方法,并把通过反射产生的相应方法当做参数传入,InvocationHandler加入逻辑后通过反射调用真实对象的方法:method.invoke(readSubject, 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
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
// 功能接口
public interface Subject {
public int sellBooks() ;
public String speak();
}
// 真实类
public class RealSubject implements Subject{

@Override
public int sellBooks() {
System.out.println("买书");
return 1;
}

@Override
public String speak() {
System.out.println("说话");
return "张三";
}

}
// 处理器
public class MyInvocationHandler implements InvocationHandler{
Subject readSubject;

public MyInvocationHandler(Subject readSubject) {
super();
this.readSubject = readSubject;
}

@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
if (method.getName().equals("sellBooks")) {
int invoke = (int) method.invoke(readSubject, args);
System.out.println("调用买书方法");
return invoke;
}else {
String string = (String) method.invoke(readSubject, args);
System.out.println("调用说话方法");
return string;
}
}

}
// 代理类
public class Client {
public static void main(String[] args) {
Subject realSubject = new RealSubject();
MyInvocationHandler myInvocationHandler = new MyInvocationHandler(realSubject);
Subject proxyClass = (Subject) Proxy.newProxyInstance(ClassLoader.getSystemClassLoader(), new Class[] {Subject.class}, myInvocationHandler);
proxyClass.sellBooks();
proxyClass.speak();
}
}

应用场景:

  spring中的aop。由于JDK动态代理使用了反射,性能比cglib差,且不能进行类代理,只能进行接口代理。

什么是cglib动态代理?

cglib动态代理通过继承要被动代理的类,重写父类的方法,在子类方法中加入业务逻辑代码,从而实现代理。

  首先要明白cglib是什么。它是一个强大的代码生成类库,它可以在运行期扩展Java类与实现Java接口。其底层依赖asm库实现。asm库是一个字节码处理类库,它可以转换字节码并生成新的类。
  cglib用到的主要技术点有:字节码技术。

优缺点:

  JDK动态代理要求被代理对象实现了接口,而cglib动态代理则不需要被代理对象实现接口,并且由于JDK动态代理使用了反射机制,cglib运行效率会更高。

实现:

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
// 被代理类
public class Engineer {
// 可以被代理
public void eat() {
System.out.println("工程师吃饭");
}
// 不会被子类覆盖
public final void work() {
System.out.println("工程师正在工作");
}
// 不会被子类覆盖
public void play() {
System.out.println("工程师在玩游戏");
}
}
// 代理
public class CglibProxy implements MethodInterceptor{
private Object target;
public CglibProxy(Object target) {
this.target = target;
}
@Override
public Object intercept(Object arg0, Method arg1, Object[] arg2, MethodProxy arg3) throws Throwable {
System.out.println("前置通知");
Object result = arg1.invoke(target);
System.out.println("后置通知");
return result;
}
public static Object getProxy(Object target) {
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(target.getClass());
enhancer.setCallback(new CglibProxy(target));
return enhancer.create();
}
}
// 测试代码
public class Test {
public static void main(String[] args) {
Engineer engineerProxy = (Engineer) CglibProxy.getProxy(new Engineer());
engineerProxy.eat();
}
}

应用场景:

  spring中的aop。由于cglib创建对象花费时间较久,所有cglib适合代理单例对象,或者具有实例池的对象。   

参考文献

https://www.runoob.com
https://segmentfault.com/a/1190000018923333?utm_source=tag-newest
http://www.chenglong.ren/2019/02/18/cglib%E5%8A%A8%E6%80%81%E4%BB%A3%E7%90%86%E4%BD%BF%E7%94%A8%E4%B8%8E%E5%8E%9F%E7%90%86%E7%9A%84%E7%AE%80%E5%8D%95%E5%88%86%E6%9E%90/
《精通spring4.x企业应用开发实战》