почему смешивание прокси-механизмов Spring AOP (CGLIB и JDKDynmic) для зависимых компонентов не работает

У меня есть два общедоступных класса, настроенных следующим образом в контексте приложения Spring:

public class LoadErrorData{
  private ExceptionData exceptionData;
  public LoadErrorData() { } 
  // reminder
}

public class ExceptionData implements Serializable{
  private Resource exceptionDataResource;
  public ExceptionData() { } 
  // reminder
}

Spring applicationContext.xml:

<beans:bean id="loadErrorData" class="com.startup.LoadErrorData" init-method="startup">
  <beans:property name="exceptionData" ref="exceptionData"/> 
</beans:bean>

<beans:bean id="exceptionData" class="com.server.ExceptionData" init-method="startup">
  <beans:property name="exceptionDataResource" value="classpath:${exception.datafile.path}"/>
</beans:bean>

Это дает следующее исключение при инициализации:

Oct 07, 2013 1:45:21 PM org.apache.catalina.core.StandardContext
listenerStart SEVERE: Exception sending context initialized event to
listener instance of class
org.springframework.web.context.ContextLoaderListener
org.springframework.beans.factory.BeanCreationException: Error
creating bean with name 'loadErrorData' defined in class path resource
[spring/startup-config.xml]: Initialization of bean failed; nested
exception is
org.springframework.beans.ConversionNotSupportedException: Failed to
convert property value of type 'com.sun.proxy.$Proxy12 implementing
java.io.Serializable,org.springframework.aop.SpringProxy,org.springframework.aop.framework.Advised'
to required type 'com.server.IICExceptionData' for property
'exceptionData'; nested exception is java.lang.IllegalStateException:
Cannot convert value of type [com.sun.proxy.$Proxy12 implementing
java.io.Serializable,org.springframework.aop.SpringProxy,org.springframework.aop.framework.Advised]
to required type [com.server.IICExceptionData] for property
'exceptionData': no matching editors or conversion strategy found at
org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:527)
at
org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:456)
at
org.springframework.beans.factory.support.AbstractBeanFactory$1.getObject(AbstractBeanFactory.java:294)
at
org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:225)
at
org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:291)
at
org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:193)
at
org.springframework.beans.factory.support.DefaultListableBeanFactory.preInstantiateSingletons(DefaultListableBeanFactory.java:609)
at
org.springframework.context.support.AbstractApplicationContext.finishBeanFactoryInitialization(AbstractApplicationContext.java:918)
at
org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:469)
at
org.springframework.web.context.ContextLoader.configureAndRefreshWebApplicationContext(ContextLoader.java:383)
at
org.springframework.web.context.ContextLoader.initWebApplicationContext(ContextLoader.java:283)
at
org.springframework.web.context.ContextLoaderListener.contextInitialized(ContextLoaderListener.java:111)
at
org.apache.catalina.core.StandardContext.listenerStart(StandardContext.java:4723)
at
org.apache.catalina.core.StandardContext$1.call(StandardContext.java:5226)
at
org.apache.catalina.core.StandardContext$1.call(StandardContext.java:5221)
at java.util.concurrent.FutureTask$Sync.innerRun(FutureTask.java:334)
at java.util.concurrent.FutureTask.run(FutureTask.java:166) at
java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1145)
at
java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:615)
at java.lang.Thread.run(Thread.java:724) Caused by:
org.springframework.beans.ConversionNotSupportedException: Failed to
convert property value of type 'com.sun.proxy.$Proxy12 implementing
java.io.Serializable,org.springframework.aop.SpringProxy,org.springframework.aop.framework.Advised'
to required type 'com.server.IICExceptionData' for property
'exceptionData'; nested exception is java.lang.IllegalStateException:
Cannot convert value of type [com.sun.proxy.$Proxy12 implementing
java.io.Serializable,org.springframework.aop.SpringProxy,org.springframework.aop.framework.Advised]
to required type [com.server.IICExceptionData] for property
'exceptionData': no matching editors or conversion strategy found at
org.springframework.beans.BeanWrapperImpl.convertIfNecessary(BeanWrapperImpl.java:485)
at
org.springframework.beans.BeanWrapperImpl.convertForProperty(BeanWrapperImpl.java:516)
at
org.springframework.beans.BeanWrapperImpl.convertForProperty(BeanWrapperImpl.java:510)
at
org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.convertForProperty(AbstractAutowireCapableBeanFactory.java:1406)
at
org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.applyPropertyValues(AbstractAutowireCapableBeanFactory.java:1365)
at
org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.populateBean(AbstractAutowireCapableBeanFactory.java:1118)
at
org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:517)
... 19 more Caused by: java.lang.IllegalStateException: Cannot convert
value of type [com.sun.proxy.$Proxy12 implementing
java.io.Serializable,org.springframework.aop.SpringProxy,org.springframework.aop.framework.Advised]
to required type [com.server.IICExceptionData] for property
'exceptionData': no matching editors or conversion strategy found at
org.springframework.beans.TypeConverterDelegate.convertIfNecessary(TypeConverterDelegate.java:241)
at
org.springframework.beans.BeanWrapperImpl.convertIfNecessary(BeanWrapperImpl.java:470)
... 25 more

но когда я положил

<aop:scoped-proxy proxy-target-class="true"/>

в конфигурации bean-компонента ExceptionData ИЛИ если я удалю

implements Serializable

от ExceptionData

исключение уходит. Мой точечный разрез AOP выглядит следующим образом:

expression="execution(* com..*(..))"

Кажется, что Spring пытается создать прокси-сервер CGLIB для bean-компонента LoadErrorData (поскольку он не реализует никакого интерфейса) и JDKdynamicProxy для его зависимости ExceptionData (поскольку он реализует интерфейс Serializable) . И это не в состоянии сделать это. Потому что, когда я говорю Spring создать прокси-сервер CGLIB явно для ExceptionData, он работает нормально. Почему это так? Почему он не может создать JDKDynamic Proxy для bean-компонента (ExceptionData), который является зависимостью bean-компонента (LoadErrorData), для которого он пытается создать CGLIB-прокси? Согласно документации говорится, что Spring обнаружит это автоматически.


person Shailesh Vaishampayan    schedule 07.10.2013    source источник


Ответы (1)


На самом деле ситуация, которую вы описываете, именно так и происходит.

LoadErrorData будет прокси CgLIB (из-за отсутствия интерфейсов. ExceptionData будет динамическим прокси JDK.

Однако ваш LoadErrorData ожидает bean-компонент типа ExceptionData, динамический прокси-сервер JDK - это только Serializable (поскольку он будет проксировать только интерфейсы, а не классы). Никогда нельзя будет привести this к экземпляру ExceptionData, и, следовательно, загрузка контекста завершится ошибкой. (Это также то, что трассировка стека сообщает вам из сообщения об ошибке).

Это будет работать, если вы форсируете прокси на основе классов для всего или удаляете интерфейс Serializable. В любом случае это приведет к прокси-серверу на основе классов.

person M. Deinum    schedule 07.10.2013
comment
будет ли это работать, если я изменю тип переменной экземпляра, хранящейся в LoadErrorData, на Serializable? По логике должно работать? - person Shailesh Vaishampayan; 08.10.2013
comment
Затем произойдет инъекция, но что вы собираетесь делать только с Serializable? Вы не можете применить его к ExceptionData... - person M. Deinum; 08.10.2013
comment
правильно .. это означает, что в идеале я должен программировать какой-то значимый интерфейс, а не просто интерфейс классов или маркеров (старый добрый принцип дизайна). спасибо @M. Дейнум - person Shailesh Vaishampayan; 08.10.2013
comment
Так что это не имеет никакого отношения к смешиванию прокси-механизмов. это все о простой Java, верно? - person Shailesh Vaishampayan; 08.10.2013
comment
Правильно, хотя это ограничение связано с фактом использования динамических прокси JDK (он создает прокси, который действует как все реализованные интерфейсы и, как таковой, не может быть приведен к фактическому типу). - person M. Deinum; 08.10.2013