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
修改器。 如果你不能,你将不得不重新考虑你的设计。