Spring JDK 动态代理和 CGLIB - 实现细节
Spring JDK Dynamic Proxy and CGLIB - implementation details
相对于 SO 上的其他热门问题,此问题所在的位置
我的问题与 "self-invocation problems inside proxies" 有关,但这不是我的问题。 (我已经找到了该问题的一些解决方案,例如 here). I also found the basic explanation for why this happens, here
现状介绍
从头说起,我第一次在工作中遇到"self-invocation"的问题。我们正在使用 Spring Boot,我们有一些服务需要审计一些数据(使用 AspectJ),并预授权该方法。该方法本身必须 return 一些东西,但审计其他东西。所以现在我们的服务看起来像:
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 一起工作,如上所述,但没有让我的 class 意识到它正在被代理。
所以我想确切地了解我在代理类型方面的选择。我发现 Spring 带有两种类型的代理:JDK 动态代理和 CGLIB 代理。原来我们使用的是 CGLIB 代理,我们配置 Spring 使用 CGLIB(无论如何,现在服务只是一个普通的 class,没有实现接口)。几个小时后,我阅读了 Spring documentation on AOP + 我在互联网上找到的任何其他内容,以了解我的 classes 在被代理后究竟是什么样子。我认为实现我想要的唯一选择是使用其他(第三个?) proxy
的实现
我对此有疑问。
1) 根据我对代码现在的描述,您将如何避免从上下文中获取 bean?我不想将每个服务分成两个 classes,以避免自调用问题。我想要一种使 aspectJ 和 PreAuthorize 像描述的那样工作的方法。
2) 我不明白为什么 CGLIB 会这样实现代理。
假设我有 class 服务
class Service {
public void methodA(){
// do methodA stuff
// call method b
methodB();
}
public void methodB(){
// do methodB stuff
}
}
然后GCLIB会生成一个class像:
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() ?为什么它需要装饰而不是委托?我认为我需要的是让代理委托给 super。我是这样做的,因为多态性,我对自我调用没有任何问题。是否有执行我期望 CGLIB 执行的代理的实现?
Cglib 代理的行为与 cglib 的工作方式无关,而与 Spring 使用 cglib 的方式无关。 Cglib 能够委托或继承调用。看看 Spring 的 DynamicAdvisedInterceptor
实现了这个委托。使用 MethodProxy
,它可以改为执行超级方法调用。
Spring 定义委托而不是子类化,以最小化使用 Cglib 或 Java 代理之间的差异。这只是一个选择。
相对于 SO 上的其他热门问题,此问题所在的位置
我的问题与 "self-invocation problems inside proxies" 有关,但这不是我的问题。 (我已经找到了该问题的一些解决方案,例如 here). I also found the basic explanation for why this happens, here
现状介绍
从头说起,我第一次在工作中遇到"self-invocation"的问题。我们正在使用 Spring Boot,我们有一些服务需要审计一些数据(使用 AspectJ),并预授权该方法。该方法本身必须 return 一些东西,但审计其他东西。所以现在我们的服务看起来像:
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 一起工作,如上所述,但没有让我的 class 意识到它正在被代理。 所以我想确切地了解我在代理类型方面的选择。我发现 Spring 带有两种类型的代理:JDK 动态代理和 CGLIB 代理。原来我们使用的是 CGLIB 代理,我们配置 Spring 使用 CGLIB(无论如何,现在服务只是一个普通的 class,没有实现接口)。几个小时后,我阅读了 Spring documentation on AOP + 我在互联网上找到的任何其他内容,以了解我的 classes 在被代理后究竟是什么样子。我认为实现我想要的唯一选择是使用其他(第三个?) proxy
的实现我对此有疑问。
1) 根据我对代码现在的描述,您将如何避免从上下文中获取 bean?我不想将每个服务分成两个 classes,以避免自调用问题。我想要一种使 aspectJ 和 PreAuthorize 像描述的那样工作的方法。 2) 我不明白为什么 CGLIB 会这样实现代理。 假设我有 class 服务
class Service {
public void methodA(){
// do methodA stuff
// call method b
methodB();
}
public void methodB(){
// do methodB stuff
}
}
然后GCLIB会生成一个class像:
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() ?为什么它需要装饰而不是委托?我认为我需要的是让代理委托给 super。我是这样做的,因为多态性,我对自我调用没有任何问题。是否有执行我期望 CGLIB 执行的代理的实现?
Cglib 代理的行为与 cglib 的工作方式无关,而与 Spring 使用 cglib 的方式无关。 Cglib 能够委托或继承调用。看看 Spring 的 DynamicAdvisedInterceptor
实现了这个委托。使用 MethodProxy
,它可以改为执行超级方法调用。
Spring 定义委托而不是子类化,以最小化使用 Cglib 或 Java 代理之间的差异。这只是一个选择。