Spring AOP CGLIB 代理的字段为空
Spring AOP CGLIB proxy's field is null
描述
使用vlcj组件,由于AOP代理对象为空,出现自定义组件。
媒体列表Class
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
}
}
自定义媒体列表Class
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配置Class
@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 配置Class
@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() 方法以return 为正常值,但当spring 为return 代理对象时,null 为returned。同时,如果你不使用AOP,我也会尝试正确return这个值。
因此,我基本判断是AOP动态代理的问题,但不知道为什么,以前没有遇到过这种情况。
最小示例
包中的所有 class:vod.demo
目标Class
public class TargetClass {
private String returnValue;
public TargetClass() {
this.returnValue = "Hello World";
}
public final String test() {
System.out.println("TargetClass.test();");
return returnValue;
}
}
看点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 配置 Class
@Configuration
@EnableAspectJAutoProxy
@Import(AspectClass.class)
public class SpringConfig {
@Bean
public TargetClass target() {
return new TargetClass();
}
}
客户Class
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 代理是 class 的动态子类型的实例,它将所有方法调用委托给 class 的真实实例。然而,即使代理是一个子类型,它的字段也没有被初始化(即你的 TargetClass
超级构造函数没有被调用)。可以找到更长的解释 here.
另外,你的方法
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 o.s.aop.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
修饰符。如果不能,您将不得不重新考虑您的设计。
描述
使用vlcj组件,由于AOP代理对象为空,出现自定义组件。
媒体列表Class
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
}
}
自定义媒体列表Class
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配置Class
@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 配置Class
@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() 方法以return 为正常值,但当spring 为return 代理对象时,null 为returned。同时,如果你不使用AOP,我也会尝试正确return这个值。 因此,我基本判断是AOP动态代理的问题,但不知道为什么,以前没有遇到过这种情况。
最小示例
包中的所有 class:vod.demo
目标Class
public class TargetClass {
private String returnValue;
public TargetClass() {
this.returnValue = "Hello World";
}
public final String test() {
System.out.println("TargetClass.test();");
return returnValue;
}
}
看点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 配置 Class
@Configuration
@EnableAspectJAutoProxy
@Import(AspectClass.class)
public class SpringConfig {
@Bean
public TargetClass target() {
return new TargetClass();
}
}
客户Class
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 代理是 class 的动态子类型的实例,它将所有方法调用委托给 class 的真实实例。然而,即使代理是一个子类型,它的字段也没有被初始化(即你的 TargetClass
超级构造函数没有被调用)。可以找到更长的解释 here.
另外,你的方法
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 o.s.aop.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
修饰符。如果不能,您将不得不重新考虑您的设计。