目录

代理模式

Proxy

代理(Proxy)是一种设计模式,提供了对目标对象另外的访问方式;即通过代理对象访问目标对象。

好处:可以在目标对象实现的基础上,增强额外的功能操作,即扩展目标对象的功能。 按照代理类的创建时期分类: 静态代理:由程序员创建或特定工具自动生成源代码,再对其编译。在程序运行前,代理类的.class文件就已经存在了。 动态代理:在程序运行时,运用反射机制动态创建而成。

静态代理

代理模式包含如下角色:

  • Subject:抽象主题角色。可以是接口,也可以是抽象类。
  • RealSubject:真实主题角色。业务逻辑的具体执行者。
  • ProxySubject:代理主题角色。内部含有RealSubject的引用,负责对真实角色的调用,并在真实主题角色处理前后做预处理和善后工作。

/images/dp/proxy.png

抽象角色

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,动态的在内存中构建代理对象(需要我们指定创建代理对象/目标对象实现的接口的类型) 典型的动态代理可分为以下四个步骤:

  1. 创建抽象角色
  2. 创建真实角色
  3. 通过实现 InvocationHandler 接口创建中介类
  4. 通过场景类,动态生成代理类

代理类在程序运行时创建的代理方式被称为动态代理。 代理类所在包 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动态代理机制。因此,对于没有实现接口的类,就不能使用该机制。

动态代理与静态代理的区别

  1. Proxy类的代码被固定下来,不会因为业务的逐渐庞大而庞大;
  2. 可以实现AOP编程,这是静态代理无法实现的;
  3. 解耦,如果用在web业务下,可以实现数据层和业务层的分离。
  4. 动态代理的优势就是实现无侵入式的代码扩展。 静态代理这个模式本身有个大问题,如果类方法数量越来越多的时候,代理类的代码量是十分庞大的。所以引入动态代理来解决此类问题

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代理。