【问题标题】:Spring JDK Dynamic Proxy and CGLIB - implementation detailsSpring JDK 动态代理和 CGLIB - 实现细节
【发布时间】:2016-08-27 07:55:38
【问题描述】:

相对于 SO 上的其他热门问题,这个问题的立场是什么

我的问题与“代理内部的自我调用问题”有关,但这不是我的问题。 (我已经找到了一些解决该问题的方法,例如here)。我还找到了为什么会发生这种情况的基本解释,here

现状介绍

从头说起,我在工作中第一次遇到“自调用”的问题。我们正在使用 Spring Boot,我们有一些服务既需要审计一些数据(使用 AspectJ),又需要预授权该方法。该方法本身必须返回一些东西,但要审计其他东西。所以现在我们的服务看起来像这样:

class Service {
    Service self;

    public Service() {
       // I don't like this because it makes my Service to be aware of the fact that it's a proxy. I just wanted my class to be a pojo with annotations
       self = AopContext.currentProxy();
    }

    @Audit
    public CustomClassContainingTheValuesToAudit getValuesFromServer() {
       CustomClassContainingTheValuesToAudit c;
       try {
           Object answer = self.doGetValuesFromServer();
           c = ... ; // create what to audit based on the answer
       } catch (...) {
          // log the fact that you didn't have enough privileges to run doGetValuesFromServer()
          // create c based on the exception I got
          c = ...;
       }
       return c; // object to be audited
    }

   @PreAuthorize(.....)
   public Object doGetValuesFromServer() {
       .........
   }
} 

我想要达到的目标

我主要关心的是让 AspectJ + PreAuthorize 一起工作,如上所述,但没有让我的班级意识到它被代理的事实。 所以我想确切地了解我在代理类型方面的选择。我发现 Spring 带有两种类型的代理:JDK 动态代理和 CGLIB 代理。事实证明,我们使用的是 CGLIB 代理,我们将 Spring 配置为使用 CGLIB(无论如何,现在 Service 只是一个普通的类,没有实现接口)。几个小时后,我阅读了Spring documentation on AOP + 我在互联网上找到的任何其他内容,以了解我的课程在被代理后的样子。我认为实现我想要的唯一选择是使用其他一些(第三个?)代理实现

我对此的疑问。

1) 根据我对代码现在的样子的描述,您会如何避免从上下文中获取 bean?我不想将每个服务分成两个类,以避免自调用问题。我想要一种方法来使 aspectJ 和 PreAuthorize 像描述的那样工作。 2) 我不明白 CGLIB 以这种方式实现代理的原因。 假设我有我的班级服务

class Service {
    public void methodA(){
        // do methodA stuff
        // call method b
        methodB();
    }
    public void methodB(){
        // do methodB stuff
    }
}

然后GCLIB会生成一个类:

class Service$CGLIB extends Service {
// it's only extending my class to make it compatible with the variable where i'm storing/autowiring the proxy
   Service privateField; // but it's actually using a decorator pattern, just like JDK Dynamic Proxies do
   public void methodA() {
      // do any magic before
      privateField.a();
      // any magic after the call
   }

   public void methodB() {
       // magic
       privateField.b();
       // some more magic
   }
}

为什么 CGLIB 不直接调用 super.a() 和 super.b() ?为什么需要装饰而不是委托?我认为我需要的是代理委托给超级。如果要这样做,由于多态性,我对自我调用没有任何问题。有没有实现我期望 CGLIB 做的代理?

【问题讨论】:

    标签: java spring proxy spring-aop cglib


    【解决方案1】:

    Cglib 代理的行为与 cglib 的工作方式无关,而是与 Spring 使用 cglib 的方式有关。 Cglib 能够委托或子类化调用。看看 Spring 的 DynamicAdvisedInterceptor 实现了这个委托。使用MethodProxy,它可以改为执行超级方法调用。

    Spring 定义了委托而不是子类化,以尽量减少使用 Cglib 或 Java 代理之间的差异。这只是一个选择。

    【讨论】: