簡體   English   中英

Spring AOP CGLIB 代理的字段為空

[英]Spring AOP CGLIB proxy's field is null

描述

使用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);
    }
}

彈簧配置類

@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();
    }
}

彈簧配置類

@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  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修飾符。 如果你不能,你將不得不重新考慮你的設計。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM