Поле прокси Spring AOP CGLIB является нулевым
Описание
При использовании компонента vlcj пользовательский компонент появляется как результат нулевого объекта прокси-сервера AOP.
MediaList 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
}
}
Пользовательский класс 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 () метода для возврата к нормальным значениям, но когда пружина возвращается к прокси-объекту, возвращается значение 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
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
}
}
1 ответ
Это сочетание потенциально неожиданного поведения. Во-первых, Spring использует CGLIB для прокси ваших bean-компонентов для AOP. Прокси-серверы 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 не может переопределить их для делегирования реальному экземпляру. На это намекнули бы в весенних журналах. Например, вы увидите
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
модификатор. Если вы не можете, вам придется пересмотреть свой дизайн.