代理(Proxy)是一种设计模式,提供了对目标对象另外的访问方式;即通过代理对象访问目标对象。
好处:可以在目标对象实现的基础上,增强额外的功能操作,即扩展目标对象的功能。
按照代理类的创建时期分类:
静态代理:由程序员创建或特定工具自动生成源代码,再对其编译。在程序运行前,代理类的.class文件就已经存在了。
动态代理:在程序运行时,运用反射机制动态创建而成。
静态代理
代理模式包含如下角色:
- Subject:抽象主题角色。可以是接口,也可以是抽象类。
- RealSubject:真实主题角色。业务逻辑的具体执行者。
- ProxySubject:代理主题角色。内部含有RealSubject的引用,负责对真实角色的调用,并在真实主题角色处理前后做预处理和善后工作。

抽象角色
1
2
3
|
public interface IRole {
void doOneThing();
}
|
真实角色
1
2
3
4
5
6
|
public class Role implements IRole {
@Override
public void doOneThing() {
System.out.println("I can do this.");
}
}
|
代理角色
1
2
3
4
5
6
7
8
9
10
|
public class RoleProxy implements IRole{
IRole role = null;
public RoleProxy(IRole role) {
this.role = role;
}
@Override
public void doOneThing() {
role.doOneThing();
}
}
|
静态代理总结
代理模式优点:
- 职责清晰 真实角色只需关注业务逻辑的实现,非业务逻辑部分,后期通过代理类完成即可。
- 高扩展性 不管真实角色如何变化,由于接口是固定的,代理类无需做任何改动。
如果要想为多个类进行代理,则需要建立多个代理类,维护难度加大。因为代理在编译期就已经决定,如果代理哪个发生在运行期,这些问题解决起来就比较简单,所以动态代理的存在就很有必要了。
动态代理
JDK 动态代理
Java提供了动态代理的技术,允许开发者在运行期创建接口的代理实例。动态代理是实现AOP的绝好底层技术。
JDK的动态代理主要涉及到java.lang.reflect包中的两个类:Proxy和InvocationHandler。其中InvocationHandler是一个接口,可以通过实现该接口定义横切逻辑,并通过反射机制调用目标类的代码,动态将横切逻辑和业务逻辑编织在一起。
而Proxy利用InvocationHandler动态创建一个符合某一接口的实例,生成目标类的代理对象。
代理对象的生成,是利用JDK的API,动态的在内存中构建代理对象(需要我们指定创建代理对象/目标对象实现的接口的类型)
典型的动态代理可分为以下四个步骤:
- 创建抽象角色
- 创建真实角色
- 通过实现
InvocationHandler
接口创建中介类
- 通过场景类,动态生成代理类
代理类在程序运行时创建的代理方式被称为动态代理。
代理类所在包 java.lang.reflect.Proxy
1
|
java.lang.reflect.Proxy.newProxyInstance(ClassLoader, Class<?>[], InvocationHandler)
|
定义一个位于代理类与委托类之间的中介类,也叫动态代理类,这个类被要求实现InvocationHandler接口:
1
2
3
4
5
6
7
8
9
10
11
12
13
|
public class RoleDynamicProxy implements InvocationHandler {
IRole role = null;
public RoleDynamicProxy(IRole role) {
this.role = role;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("before invocation");
Object result = method.invoke(role, args);
System.out.println("after invocation");
return result;
}
}
|
当调用代理类对象的方法时,这个“调用”会转送到中介类的 invoke
方法中,参数method标识了我们具体调用的是代理类的哪个方法,args为这个方法的参数。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
|
public class DynamicClient {
public static void main(String[] args) {
//让JVM生成的 Proxy 类写入文件 保存在当前项目,路径为:com/sun/proxy
System.getProperties().put("sun.misc.ProxyGenerator.saveGeneratedFiles", "true");
// 要代理的真实对象
IRole role = new Role();
// 创建中介类实例
InvocationHandler handler = new RoleDynamicProxy(role);
// 获取类加载器
ClassLoader cl = role.getClass().getClassLoader();
// 动态产生一个代理类
IRole proxy = (IRole) Proxy.newProxyInstance(cl, role.getClass().getInterfaces(), handler);
// 通过代理类,执行doOneThing方法;
proxy.doOneThing();
}
}
|
磁盘中将会产生一个名为 ”$Proxy0.class” 的代理类Class文件,反编译( JD-GUI )。
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
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
|
public final class $Proxy0
extends Proxy
implements IRole
{
private static Method m1;
private static Method m3;
private static Method m2;
private static Method m0;
public $Proxy0(InvocationHandler paramInvocationHandler)
{
super(paramInvocationHandler);
}
public final boolean equals(Object paramObject)
{
try
{
return ((Boolean)this.h.invoke(this, m1, new Object[] { paramObject })).booleanValue();
}
catch (Error|RuntimeException localError)
{
throw localError;
}
catch (Throwable localThrowable)
{
throw new UndeclaredThrowableException(localThrowable);
}
}
public final void doOneThing()
{
try
{
this.h.invoke(this, m3, null);
return;
}
catch (Error|RuntimeException localError)
{
throw localError;
}
catch (Throwable localThrowable)
{
throw new UndeclaredThrowableException(localThrowable);
}
}
public final String toString()
{
try
{
return (String)this.h.invoke(this, m2, null);
}
catch (Error|RuntimeException localError)
{
throw localError;
}
catch (Throwable localThrowable)
{
throw new UndeclaredThrowableException(localThrowable);
}
}
public final int hashCode()
{
try
{
return ((Integer)this.h.invoke(this, m0, null)).intValue();
}
catch (Error|RuntimeException localError)
{
throw localError;
}
catch (Throwable localThrowable)
{
throw new UndeclaredThrowableException(localThrowable);
}
}
static
{
try
{
m1 = Class.forName("java.lang.Object").getMethod("equals", new Class[] { Class.forName("java.lang.Object") });
m3 = Class.forName("leetcode.proxy.IRole").getMethod("doOneThing", new Class[0]);
m2 = Class.forName("java.lang.Object").getMethod("toString", new Class[0]);
m0 = Class.forName("java.lang.Object").getMethod("hashCode", new Class[0]);
return;
}
catch (NoSuchMethodException localNoSuchMethodException)
{
throw new NoSuchMethodError(localNoSuchMethodException.getMessage());
}
catch (ClassNotFoundException localClassNotFoundException)
{
throw new NoClassDefFoundError(localClassNotFoundException.getMessage());
}
}
}
|
源码分析 (JDK 1.8)
1
|
IRole proxy = (IRole) Proxy.newProxyInstance(cl, role.getClass().getInterfaces(), handler);
|
1
2
3
4
5
6
7
|
public static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h) throws IllegalArgumentException
{
final Class<?>[] intfs = interfaces.clone();
Class<?> cl = getProxyClass0(loader, intfs);
final Constructor<?> cons = cl.getConstructor(constructorParams);
return cons.newInstance(new Object[]{h});
}
|
- 根据传入的参数interfaces动态生成一个类,它实现interfaces中的接口,该例中即IRole接口的doOneThing方法。假设动态生成的类为$Proxy0。
- 通过传入的classloder,将刚生成的$Proxy0类加载到jvm中。
- 利用中介类,调用$Proxy0的$Proxy0(InvocationHandler)构造函数,创建$Proxy0类的实例,其InvocationHandler属性,为我们创建的中介类。
1
2
3
4
5
6
7
8
9
10
|
private static Class<?> getProxyClass0(ClassLoader loader,
Class<?>... interfaces) {
if (interfaces.length > 65535) {
throw new IllegalArgumentException("interface limit exceeded");
}
// If the proxy class defined by the given loader implementing
// the given interfaces exists, this will simply return the cached copy;
// otherwise, it will create the proxy class via the ProxyClassFactory
return proxyClassCache.get(loader, interfaces);
}
|
ProxyClassFactory 类的 apply 方法,最终找到
1
2
3
|
//Generate the specified proxy class.
byte[] proxyClassFile = ProxyGenerator.generateProxyClass(proxyName, interfaces);
|
该方法用来完成生成字节码的动作,这个方法可以在运行时产生一个描述代理类的字节码byte[]数组。使用下面代码可以看到具体代码。
CGLib
CGLib采用非常底层的字节码技术,可以为一个类创建子类,并在子类中采用方法拦截的技术拦截所有父类方法的调用,并顺势织入横切逻辑。
1
2
3
4
5
|
<dependency>
<groupId>cglib</groupId>
<artifactId>cglib</artifactId>
<version>3.2.7</version>
</dependency>
|
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
|
public class CglibProxy implements MethodInterceptor {
private Enhancer enhancer = new Enhancer();
public Object getProxy(Class clazz) {
enhancer.setSuperclass(clazz); //① 设置需要创建子类的类
enhancer.setCallback(this);
return enhancer.create(); //②通过字节码技术动态创建子类实例
}
//③拦截父类所有方法的调用
public Object intercept(Object obj, Method method, Object[] args,
MethodProxy proxy) throws Throwable {
PerformanceMonitor.begin(obj.getClass().getName()+"."+method. getName());//③-1
Object result=proxy.invokeSuper(obj, args); ③-2
PerformanceMonitor.end();//③-1通过代理类调用父类中的方法
return result;
}
}
public class TestForumService {
public static void main(String[] args) {
CglibProxy proxy = new CglibProxy();
ForumServiceImpl forumService = ①
(ForumServiceImpl )proxy.getProxy(ForumServiceImpl.class);
forumService.removeForum(10);
forumService.removeTopic(1023);
}
}
|
动态代理的弊端
代理类和委托类需要都实现同一个接口。也就是说只有实现了某个接口的类可以使用Java动态代理机制。因此,对于没有实现接口的类,就不能使用该机制。
动态代理与静态代理的区别
- Proxy类的代码被固定下来,不会因为业务的逐渐庞大而庞大;
- 可以实现AOP编程,这是静态代理无法实现的;
- 解耦,如果用在web业务下,可以实现数据层和业务层的分离。
- 动态代理的优势就是实现无侵入式的代码扩展。 静态代理这个模式本身有个大问题,如果类方法数量越来越多的时候,代理类的代码量是十分庞大的。所以引入动态代理来解决此类问题
cglib 代理
CGLIB(Code Generation Library)是一个开源项目!是一个强大的,高性能,高质量的Code生成类库。
cglib动态代理是利用asm开源包,对代理对象类的class文件加载进来,通过修改其字节码生成子类来处理。
cglib代理,也叫作子类代理,它是在内存中构建一个子类对象从而实现对目标对象功能的扩展。
cglib是针对类来实现代理的,他的原理是对指定的目标类生成一个子类,并覆盖其中方法实现增强,但因为采用的是继承,所以不能对final修饰的类进行代理。
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
|
/**
* Cglib子类代理工厂
* 在内存中动态构建一个子类对象
*/
public class ProxyFactory implements MethodInterceptor{
//维护目标对象
private Object target;
public ProxyFactory(Object target) {
this.target = target;
}
//给目标对象创建一个代理对象
public Object getProxyInstance(){
//1.工具类
Enhancer en = new Enhancer();
//2.设置父类
en.setSuperclass(target.getClass());
//3.设置回调函数
en.setCallback(this);
//4.创建子类(代理对象)
return en.create();
}
@Override
public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
System.out.println("开始事务...");
//执行目标对象的方法
Object returnValue = method.invoke(target, args);
System.out.println("提交事务...");
return returnValue;
}
}
|
已实现用例
Spring的核心包中已经包括了Cglib功能。如果加入容器的目标对象有实现接口,用JDK代理;如果目标对象没有实现接口,用Cglib代理。