代理模式

什么是代理模式

定义

定义1:给目标对象提供一个代理对象,并由代理对象控制对目标对象的引用。

定义2:为某个对象提供一个代理,以控制对这个对象的访问,可在不修改源代码的基础上做方法增强。

以一个通俗的例子解释下上面的定义:

我们在Windows电脑上安装了QQ,然后我们找到了QQ的安装目录,右键点击QQ.exe,创建一个桌面快捷方式。

YES,通过上面的操作,你已经成功创建了一个QQ.exe的代理

安装目录下的QQ.exe就是目标对象,桌面快捷方式则是代理对象,而创建快捷方式的过程,就是创建了一个代理,我们通过点击桌面快捷方式,就能调用到安装目录下的QQ.exe。

熟悉Windows的童靴可能知道,快捷方式不仅仅可以启动目标对象,还能携带一些参数去启动目标对象,这就是代理的作用之一,增强功能,添加控制。

再举几个栗子

  1. 买火车票不一定在火车站买,也可以去代售点
  2. 买二手房时的房产中介
  3. 一张支票或银行存单是账户中资金的代理

为什么要用代理模式

优点

  1. 协调调用者和被调用者,降低了系统的耦合度
  2. 代理对象作为客户端和目标对象之间的中介,起到了保护目标对象的作用

缺点

  1. 由于在客户端和真实主题之间增加了代理对象,因此会造成请求的处理速度变慢
  2. 实现代理模式需要额外的工作(有些代理模式的实现非常复杂),从而增加了系统实现的复杂度

怎么实现代理代理模式(Java)

静态代理

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
import java.util.*;

//接口
interface Image {
public void displayImage();
}

//真实对象
class RealImage implements Image {
private String filename;
public RealImage(String filename) {
this.filename = filename;
loadImageFromDisk();
}

private void loadImageFromDisk() {
System.out.println("Loading " + filename);
}

public void displayImage() {
System.out.println("Displaying " + filename);
}
}

//代理对象
class ProxyImage implements Image {
private String filename;
private Image image;

public ProxyImage(String filename) {
this.filename = filename;
}
public void displayImage() {
if(image == null)
image = new RealImage(filename);
image.displayImage();
}
}

//测试类
class ProxyExample {
public static void main(String[] args) {
Image image1 = new ProxyImage("HiRes_10MB_Photo1");
Image image2 = new ProxyImage("HiRes_10MB_Photo2");

image1.displayImage(); // loading necessary
image2.displayImage(); // loading necessary
}
}

程序输出如下

1
2
3
4
Loading    HiRes_10MB_Photo1
Displaying HiRes_10MB_Photo1
Loading HiRes_10MB_Photo2
Displaying HiRes_10MB_Photo2

动态代理

JDK代理

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
package tech.zhaojian.jdk;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

/**
* JDK 动态代理Demo
*/

//Subjuet(公共接口)
interface Subject {
void say();
}

//RealSubject(被代理类)
class RealSubject implements Subject {
@Override
public void say() {
System.out.println(this.getClass().getSimpleName() + ">>>" + "say 方法正在执行...");
}
}

//Proxy(代理类)
class InvocationHandlerImpl implements InvocationHandler {
private Object subject;

public InvocationHandlerImpl(Object subject) {
this.subject = subject;
}

@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {

System.out.println("JDK 动态代理>>>被代理类say方法被执行前...");
Object returnVal = method.invoke(subject, args);
System.out.println("JDK 动态代理>>>被代理类say方法被执行后...");

return returnVal;
}
}


public class JdkProxy {
public static void main(String[] args) {
Subject subject = new RealSubject();
Subject proxy = (Subject) Proxy.newProxyInstance(subject.getClass().getClassLoader(), subject.getClass().getInterfaces(), new InvocationHandlerImpl(subject));
proxy.say();
}
}

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
43
package tech.zhaojian.cglib;

import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;

import java.lang.reflect.Method;

/**
* Cglib 动态代理Demo
*/
//Subjuet
//注意,这里是没有接口的,因为没有接口,所以这个类无法使用JDK代理
class Subject {
public void say() {
System.out.println(this.getClass().getSimpleName() + ">>>" + "say 方法正在执行...");
}
}

//Cglib动态代理类,须实现MethondInterceptor接口
class SubjectMethodInterceptor implements MethodInterceptor{
@Override
public Object intercept(Object object, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {

//代理类可以进行方法增强
System.out.println("Cglib 动态代理>>>被代理类say方法被执行前...");
Object returnVal= methodProxy.invokeSuper(object,args);
System.out.println("Cglib 动态代理>>>被代理类say方法被执行后...");

return returnVal;
}
}

public class CglibProxy {
public static void main(String[] args) {
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(Subject.class);
enhancer.setCallback(new SubjectMethodInterceptor());

Subject subject = ((Subject) enhancer.create());
subject.say();
}
}

参考链接:

代理模式|菜鸟教程

代理模式(Proxy Pattern)- 最易懂的设计模式解析