Spring AOP CGLIB代理的字段为空

描述

使用vlcj组件时,自定义组件将作为AOP代理对象null的结果出现。

MediaList类

public class MediaList { private libvlc_media_list_t mediaListInstance; public MediaList(LibVlc libvlc, libvlc_instance_t instance, libvlc_media_list_t mediaListInstance) { this.libvlc = libvlc; this.instance = instance; createInstance(mediaListInstance); } private void createInstance(libvlc_media_list_t mediaListInstance) { logger.debug("createInstance()"); if(mediaListInstance == null) { mediaListInstance = libvlc.libvlc_media_list_new(instance); } else { libvlc.libvlc_media_list_retain(mediaListInstance); } this.mediaListInstance = mediaListInstance; // <- assignment logger.debug("mediaListInstance={}", mediaListInstance); mediaListEventManager = libvlc.libvlc_media_list_event_manager(mediaListInstance); logger.debug("mediaListEventManager={}", mediaListEventManager); registerEventListener(); } public final libvlc_media_list_t mediaListInstance() { return mediaListInstance; // <- proxy object return null, if use aop } } 

自定义MediaList类

 public class TestMediaList extends MediaList { public TestMediaList(LibVlc libvlc, libvlc_instance_t instance) { super(libvlc, instance); } public void xTest(String test){ System.out.println(test); } } 

Spring配置类

 @Configuration public class PlayerBeanConfig { @Bean @Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE) @Resource public TestMediaList testMediaList(LibVlc libvlc, libvlc_instance_t instance) { return new TestMediaList(libvlc, instance); } } 

AOP配置类

 @Aspect public class MediaListAspect { @Pointcut("execution(* TestMediaList.xTest(..))") private void anyMethod() { } @Around("anyMethod()") public Object lockAndUnlock(ProceedingJoinPoint joinPoint) throws Throwable { Object object = joinPoint.proceed(); return object; } } 

测试代码

 public static void main(String[] args) { boolean b = new NativeDiscovery().discover(); if (b) { springContext = new AnnotationConfigApplicationContext(PlayerBeanConfig.class); String[] kkk = new String[]{}; TestMediaList list = springContext. getBean(TestMediaList.class, LibVlc.INSTANCE, LibVlc.INSTANCE.libvlc_new(kkk.length, kkk)); System.out.println(list.mediaListInstance()); // <- proxy object return null } else { logger.error("Cannot find vlc lib, exit application"); } } 

当TestMediaList构建完成时,我尝试单步跟踪。 方法的MediaListInstance()返回正常值,但是当spring返回到代理对象时,返回null。 同时,如果您不使用AOP,我也会尝试正确返回值。 因此,我确定了AOP动态代理的基本问题,但我不知道为什么,之前没有遇到过这样的情况。


最小的例子

包中的所有类: vod.demo

TargetClass

 public class TargetClass { private String returnValue; public TargetClass() { this.returnValue = "Hello World"; } public final String test() { System.out.println("TargetClass.test();"); return returnValue; } } 

Aspect Class

 @Aspect public class AspectClass { @Pointcut("execution(* vod.demo.TargetClass.*(..))") private void targetMethod() { } @Around("targetMethod()") public Object aroundTarget(ProceedingJoinPoint joinPoint) throws Throwable { System.out.println("AspectClass.aroundTarget();"); return joinPoint.proceed(); } } 

Spring Config类

 @Configuration @EnableAspectJAutoProxy @Import(AspectClass.class) public class SpringConfig { @Bean public TargetClass target() { return new TargetClass(); } } 

客户类

 public class Client { public static void main(String[] args) { AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(SpringConfig.class); TargetClass target = context.getBean(TargetClass.class); System.out.println("Client invoke:" + target.test()); // <- output null } } 

这是潜在意外行为的组合。 首先,Spring使用CGLIB为AOP代理您的bean。 CGLIB代理是类的动态子类型的实例,它将所有方法调用委托给类的实际实例。 但是,即使代理是子类型,其字段也不会被初始化(即,不会调用您的TargetClass超级构造函数)。 可以在这里找到更长的解释。

另外,你的方法

 public final libvlc_media_list_t mediaListInstance() { return mediaListInstance; // <- proxy object return null, if use aop } 

要么

 public final String test() { System.out.println("TargetClass.test();"); return returnValue; } 

final 。 因此CGLIB不能覆盖它们以委托给真实实例。 这将在Spring日志中暗示。 例如,你会看到

 22:35:31.773 [main] INFO osaop.framework.CglibAopProxy - Unable to proxy method [public final java.lang.String com.example.root.TargetClass.test()] because it is final: All calls to this method via a proxy will NOT be routed to the target instance. 

将上述所有内容放在一起,您将获得一个代理实例,其中该字段为null ,并且代理无法委托给实例的方法。 所以你的代码实际上会调用

 public final String test() { System.out.println("TargetClass.test();"); return returnValue; } 

对于returnValue字段为null的实例。


如果可以,请更改方法,删除final修改器。 如果你不能,你将不得不重新考虑你的设计。