为什么在使用 Spring AOP 时 属性 值为空,但通过 getter 可以使用相同的值?

Why is the property value null when using Spring AOP but the same value works via getter?

我了解到使用 AOP 时会创建一个代理 bean。我不明白为什么我不能在我的 main 方法中直接访问 属性?我们在使用AOP时是否总是必须使用方法来获取值?

package power.sam;

import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.*;
import river.sam.Ganga;

import javax.inject.Inject;
import java.util.stream.Stream;

@Slf4j
@ComponentScan(basePackages = {"power.sam"})
@EnableAspectJAutoProxy
@Data
public class MyTest {

    Employee emp;

    @Autowired
    MyTest(Employee emp){
        this.emp = emp;
    }

    public static void main(String[] args) {
        AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(MyTest.class);
        context.registerShutdownHook();
        MyTest bean = (MyTest) context.getBean("myTest");
        log.info("Bean is {}", bean);
        log.info("Bean.Employee via getter is {}", bean.getEmp());
        log.info("Bean.Employee via property is {}", bean.emp);

    }
}

输出

14:51:12.925 [main] 信息 power.sam.MyTest - Bean 是 MyTest(emp=power.sam.Employee@3700ec9c)

14:51:12.955 [main] INFO power.sam.MyTest - Bean.Employee 通过 get 是 power.sam.Employee@3700ec9c

14:51:12.955 [main] 信息 power.sam.MyTest - Bean.Employee 通过 属性 为空

更新:这似乎只有在我有像

这样的所有方法建议时才会发生
@Before("execution(public * *(..))")

要清楚地了解后台发生的情况,请将以下内容添加到测试代码中。

System.out.println("Bean.Employee runtime class is "+ bean.getClass());

完整的main方法如下

public static void main(String[] args) throws Exception {
    AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(MyTest.class);
    context.registerShutdownHook();
    MyTest bean = (MyTest) context.getBean("myTest");
    System.out.println("Bean is "+ bean);
    System.out.println("Bean.Employee via getter is "+ bean.getEmp());
    System.out.println("Bean.Employee runtime class is "+ bean.getClass());
    System.out.println("Bean.Employee via property is "+ bean.emp);
}

现在当 运行 的切入点表达式为:@Before("execution(public * *(..))")

控制台日志为

Bean is rg.so.qn64919052.MyTest@662f5666
Bean.Employee via getter is rg.so.qn64919052.entity.Employee@75ed9710
Bean.Employee runtime class is class rg.so.qn64919052.MyTest$$EnhancerBySpringCGLIB$e53892a
Bean.Employee via property is null

注意:bean.getClass() 返回了创建的代理对象的 class。

为了清楚起见,Employee class 是在包 rg.so.qn64919052.entity 中创建的,现在 运行 的切入点表达式为:@Before("execution(public * rg.so.qn64919052.entity.Employee.*(..))")

控制台日志为

Bean is rg.so.qn64919052.MyTest@acdb094
Bean.Employee via getter is rg.so.qn64919052.entity.Employee@674bd420
Bean.Employee runtime class is class rg.so.qn64919052.MyTest
Bean.Employee via property is rg.so.qn64919052.entity.Employee@674bd420

注意:bean.getClass() 返回原始 class。

解释:

Spring AOP 是基于代理的。动态代理只继承方法,不继承任何实例变量。

第一个 运行 中的切入点表达式具有全局作用域,它将建议所有 Spring bean 的 public(受保护和包作用域,自 CGLIB 起)方法执行.为此,创建了 MyTest 的代理。

第二个 运行 中的切入点表达式的范围有限 (rg.so.qn64919052.entity.Employee.*),MyTest 未被代理。

参考资料:

https://docs.spring.io/spring-framework/docs/current/reference/html/core.html#aop-introduction-proxies.