【问题标题】:Spring AOP CGLIB proxy's field is nullSpring AOP CGLIB 代理的字段为空
【发布时间】:2016-04-06 09:46:44
【问题描述】:

说明

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

媒体列表类

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
    }
}

自定义媒体列表类

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

目标类

public class TargetClass {
    private String returnValue;

    public TargetClass() {
        this.returnValue = "Hello World";
    }

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

方面类

@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 配置类

@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
    }
}

【问题讨论】:

  • 请展示mediaListInstance的实现并解释为什么它不应该返回null
  • 谢谢,已添加mediaListInstance 的实现。当你不使用 AOP 时,我确定返回值是正确的。
  • 我无法重现这个。请提供minimal reproducible example
  • 再次感谢,已添加最小示例。

标签: java spring spring-aop cglib


【解决方案1】:

这是潜在意外行为的组合。首先,Spring 使用 CGLIB 为 AOP 代理您的 bean。 CGLIB 代理是类的动态子类型的实例,它将所有方法调用委托给类的真实实例。然而,即使代理是一个子类型,它的字段也没有被初始化(即你的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 修饰符。如果做不到,您将不得不重新考虑您的设计。

【讨论】:

  • 很遗憾,我无法更改它。不过还是谢谢你。因为我的PointCut不是最终方法,而是其他方法。但是由于代理对象不能代理final方法,导致结果不正确。能否像ProceedingJoinPoint.getTarget()一样作为目标对象来获取,然后调用final方法?
  • @forDream 你不能。由于方法是final,因此建议不会应用于它。 aroundTarget 不会被调用。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-10-07
  • 1970-01-01
  • 1970-01-01
  • 2015-01-19
  • 2013-10-13
相关资源
最近更新 更多