我的疑问是,当带有 spring 的 cglib 代理尝试在 final方法内访问实例变量时,为什么实例变量为 null。这意味着即使我像下面的类一样直接声明了变量 -
class A {
String prop="a";
public final eat(){
return prop;
}
}
对代理类的 eat() 方法的调用将返回 null,因为该方法是 final方法。这似乎意味着当代理初始化时, prop 变量被设置为 null 。
我目前的理解,我想验证 -
Spring内部似乎使用SpringObjenesis#newInstance
来创建代理对象[我通过代码进行了调试]。这似乎创建了所有实例变量设置为 null 的类实例。当代理现在尝试返回实例变量时,它将返回 null。
最佳答案
Spring 使用 CGLIB 代理的方式是通过委托(delegate)模式,参见 this answer有关如何实现的示意性代码示例。
请理解,创建代理时,仅考虑方法委托(delegate)。代理对象的实例字段不会被初始化,因为通常在某个时刻代理会调用委托(delegate)的方法。因此,委托(delegate)将透明地访问其自己的(初始化的)字段。
但是,如果是 final
方法,则无法生成代理方法,因为 Final 方法无法被覆盖。即,在这种情况下,调用原始方法,但是当通过例如访问字段时return myField
(return this.myField
的简写),this
是代理实例,因为没有对原始对象进行方法调用委托(delegate)。这解释了为什么结果为 null
、0
或 false
,具体取决于字段类型。
package de.scrum_master.spring.q72993106;
import org.springframework.stereotype.Component;
@Component
public class MyComponent {
String food = "bread";
String beverage = "water";
public final String eat() {
return food;
}
public String drink() {
return beverage;
}
}
添加一个方面作为触发代理创建的简单方法:
package de.scrum_master.spring.q72993106;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.springframework.stereotype.Component;
@Aspect
@Component
public class MyAspect {
@Before("execution(* MyComponent.*())")
public void myAdvice(JoinPoint jp) {
System.out.println(jp);
}
}
package de.scrum_master.spring.q72993106;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.EnableAspectJAutoProxy;
@SpringBootApplication
@Configuration
@EnableAspectJAutoProxy(proxyTargetClass = true)
public class DemoApplication {
public static void main(String[] args) throws Throwable {
try (ConfigurableApplicationContext appContext = SpringApplication.run(DemoApplication.class, args)) {
doStuff(appContext);
}
}
private static void doStuff(ConfigurableApplicationContext appContext) {
MyComponent myComponent = appContext.getBean(MyComponent.class);
System.out.println("Eating " + myComponent.eat());
System.out.println("Drinking " + myComponent.drink());
}
}
运行此应用程序会生成以下控制台日志:
Eating null
execution(String de.scrum_master.spring.q72993106.MyComponent.drink())
Drinking water
看到了吗?您将获得非 final方法的预期值,因为发生了对原始对象的委托(delegate)(并且方面启动),但对于 final方法,结果为 null
,如上所述。
关于aop - cglib 代理和 null 实例变量内部结构,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/72993106/