aop - cglib 代理和 null 实例变量内部结构

标签 aop spring-aop cglib dynamic-proxy

我的疑问是,当带有 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)。这解释了为什么结果为 null0false,具体取决于字段类型。

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/

相关文章:

java - Spring中使用cglib的嵌套代理

java - 如何为mybatis框架生成的mapper类创建Aspect?

java - Spring AOP - 从 catch block 中调用建议

spring-boot - Spring Boot - 仅在属性启用时才加载 bean

spring - 以编程方式关闭或打开方面

spring-mvc - Spring 4 AOP @Aspect 不会触发 @RestController

Java MDC 记录器 - MDC.put() 过多的方法

java - Spring 框架 : Configure infrastructure bean via xml

java - 使用回调过滤器降低 CGLIB 性能

java - 装箱拆箱、cglib、spring 和 NoSuchMethodError 之谜