代理模式

代理模式是一种非常好理解的设计模式,生活中处处可见代理:张三作为一个明星,不可能什么事都由他自己干,于是他请了经纪人;张三 LOL 水平不够,又想上分怎么办,请游戏代练;张三逃税被抓怎么办,请律师帮忙打官司。无论是经纪人、游戏代练或是律师,他们都是帮张三干活,但又不能一手包办,只能帮张三处理一些他不愿干或干不了的事情。

代理模式

代理模式(Proxy Pattern)是通过代理对象来访问目标对象,可以在实现目标对象功能的基础上,增加额外的操作,达到扩展目标对象功能的目的。

类结构图

静态代理

定义

静态代理是定义接口或者父类,然后被代理对象(RealSubject)与代理对象(Proxy)一起实现相同的接口或继承相同的父类。

示例

1
2
3
public interface Subject {
void action();
}
1
2
3
4
5
6
public class RealSubject implements Subject{
@Override
public void action() {
System.out.println("RealSubject do action.");
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
public class Proxy implements Subject {
private Subject target;

public Proxy(Subject target) {
this.target = target;
}

@Override
public void action() {
System.out.println("do something before.");
target.action();
System.out.println("do something after.");
}
}
1
2
3
4
5
6
7
8
9
10
public class Client {
public static void main(String[] args) {
//1.创建被代理对象
Subject target = new RealSubject();
//2.创建代理对象, 同时将被代理对象传递给代理对象
Proxy proxy = new Proxy(target);
//3.通过代理对象,调用到被代理对象的方法
proxy.action();
}
}

运行结果

JDK 动态代理

定义

JDK 代理的目标对象必须要实现接口,而代理对象则是在内存中动态生成的。

JDK 代理使用 java.lang.reflect.Proxy 中的 newProxyInstance 方法来创建代理对象,该方法需要如下三个参数:

  • ClassLoader loader
    • 代理类的类加载器
  • Class<?>[] interfaces
    • 代理类要实现的接口列表
  • InvocationHandler h
    • 执行代理方法的处理器

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
public class JDKProxy {
//被代理对象。
private Object target;

public JDKProxy(Object target) {
this.target = target;
}

//利用 JDK api 动态生成一个代理对象。
public Object getProxyInstance() {
return Proxy.newProxyInstance(
target.getClass().getClassLoader(),
target.getClass().getInterfaces(),
new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("JDK 动态代理开始");
//反射机制调用目标对象的方法
Object returnVal = method.invoke(target, args);
System.out.println("JDK 动态代理结束");
return returnVal;
}
});
}
}
1
2
3
4
5
6
7
8
9
10
11
12
public class Client {
public static void main(String[] args) {
//1.创建目标对象
RealSubject target = new RealSubject();
//2.创建代理对象
Subject proxyInstance = (Subject) new JDKProxy(target).getProxyInstance();
//3.打印内存中动态生的代理对象
System.out.println(proxyInstance.getClass());
//4.通过代理对象,调用目标对象的方法
proxyInstance.action();
}
}

运行结果

cglib 动态代理

定义

cglib(Code Generation Library) 代理的目标对象不需要实现接口,它是在内存中构建一个子类对象,从而实现对目标对象的代理。

cglib 是一个高性能的代码生成包,它可以在运行期扩展 java 类与实现 java 接口。被许多的 AOP 框架使用(如 Spring AOP)。cglib 包的底层是使用字节码处理框架 ASM 来生成新的类。

cglib 代理的类不能为 final,否则会报 java.lang.IllegalArgumentException 异常,这是因为 final 修饰的类不能被继承。 如果目标对象的方法为 final 或 static,那么也不会被拦截(只会执行目标对象的方法,不会执行代理逻辑)。

cglib 动态代理类图

代理对象需要实现 MethodInterceptor 接口中的 intercept() 方法。

示例

下载 jar 包

将 jar 包添加到项目中

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
public class CglibProxy implements MethodInterceptor {
//目标对象
private Object target;

public CglibProxy(Object target) {
this.target = target;
}

//返回target对象的代理对象
public Object getProxyInstance() {
//创建一个工具类
Enhancer enhancer = new Enhancer();
//设置父类
enhancer.setSuperclass(target.getClass());
//设置回调函数
enhancer.setCallback(this);
//创建子类对象,即代理对象
return enhancer.create();
}

//重写intercept方法,会调用目标对象的方法
@Override
public Object intercept(Object o, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
System.out.println("cglib 动态代理开始");
Object returnVal = method.invoke(target, args);
System.out.println("cglib 动态代理结束");
return returnVal;
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public class Client {
public static void main(String[] args) {
//1.创建目标对象
RealSubject target = new RealSubject();

//2.获取到代理对象,并且将目标对象传递给代理对象
Subject proxyInstance = (Subject) new CglibProxy(target).getProxyInstance();

//3.打印内存中动态生的代理对象
System.out.println(proxyInstance.getClass());

//4.执行代理对象的方法,触发 intercept 方法,从而实现对目标对象的调用
proxyInstance.action();
}
}

运行结果

小结

静态代理

  • 优点:简单。
  • 缺点:每一个目标对象都需要一个代理对象,所以会产生很多的代理类。一旦接口中新增方法,目标对象与代理对象都需要维护,十分繁琐。

JDK 动态代理

  • 优点:不需要为每一个目标对象写一个代理类。
  • 缺点:目标对象必须要实现接口才能代理。

cglib 动态代理

  • 优点:因为是基于继承来实现的代理,目标对象没有实现接口也能代理。
  • 缺点:不能代理 final 修饰的类和方法,因为 final 修饰的类不能被继承。

拓展

为什么 JDK 动态代理只能代理实现了接口的类?

因为 JDK 生成的代理类已经继承了 java.lang.reflect.Proxy 类,而 Java 只支持单继承,所以只能通过接口来实现代理。

改造 JDKProxy 类

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
public class JDKProxy {
//被代理对象。
private Object target;

public JDKProxy(Object target) {
this.target = target;
}

//利用 JDK api 动态生成一个代理对象。
public Object getProxyInstance() {
return Proxy.newProxyInstance(
target.getClass().getClassLoader(),
target.getClass().getInterfaces(),
new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
//生成的代理类已经继承了 java.lang.reflect.Proxy 类。
System.out.println("proxy instanceof Proxy: " + (proxy instanceof Proxy));
//将生成的代理类输入到 JDKProxy.class 文件中,再通过 IDEA 反编译 JDKProxy.class 文件。
byte[] bytes = ProxyGenerator.generateProxyClass("proxy", target.getClass().getInterfaces());
File file = new File("./JDKProxy.class");
try {
OutputStream outputStream = new FileOutputStream(file);
outputStream.write(bytes);
outputStream.flush();
outputStream.close();
} catch (IOException e) {
e.printStackTrace();
}

System.out.println("JDK 动态代理开始");
Object returnVal = method.invoke(target, args);
System.out.println("JDK 动态代理结束");
return returnVal;
}
});
}
}

运行结果

JDKProxy.class

查看 cglib 生成的代理类

改造 CglibProxy 类

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
public class CglibProxy implements MethodInterceptor {
//目标对象
private Object target;

public CglibProxy(Object target) {
this.target = target;
}

//返回target对象的代理对象
public Object getProxyInstance() {
//创建一个工具类
Enhancer enhancer = new Enhancer();
//设置父类
enhancer.setSuperclass(target.getClass());
//设置回调函数
enhancer.setCallback(this);
//创建子类对象,即代理对象
return enhancer.create();
}

//重写intercept方法,会调用目标对象的方法
@Override
public Object intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
//生成的代理类已经继承了 RealSubject 类。
System.out.println("proxy instanceof RealSubject: " + (proxy instanceof RealSubject));

System.out.println("cglib 动态代理开始");
Object returnVal = method.invoke(target, args);
System.out.println("cglib 动态代理结束");
return returnVal;
}
}

改造 Client 类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
public class Client {
public static void main(String[] args) {
//0.将生成的代理类输入到 cglib 目录中。
System.setProperty(DebuggingClassWriter.DEBUG_LOCATION_PROPERTY, "./cglib");

//1.创建目标对象
RealSubject target = new RealSubject();

//2.获取到代理对象,并且将目标对象传递给代理对象
RealSubject proxyInstance = (RealSubject) new CglibProxy(target).getProxyInstance();

//3.打印内存中动态生的代理对象
System.out.println(proxyInstance.getClass());

//4.执行代理对象的方法,触发 intercept 方法,从而实现对目标对象的调用
proxyInstance.action();
}
}

运行结果

CglibProxy.class

引用