Register external @AspectJ aspects dynamically in main spring project
I'm currently tying to integrate external @AspectJ aspects into a Spring+JSF project. That is, my aspects are implemented in seperate projects and should be loaded into the main application context at runtime. This is working fine if I declare the external aspect in my application context which is then loaded first thing in my main.
ClassPathXmlApplicationContext ctx = new ClassPathXmlApplicationContext("app-context.xml");
However, I want to be able to load an unknown number of Aspects dynamically so I can package some aspects into plugins that get deployed depending on dependency settings in my maven pom.
here's my app-context:
<?xml version="1.0" encoding="UTF-8" ?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:task="http://www.springframework.org/schema/task"
xmlns:jee="http://www.springframework.org/schema/jee"
xmlns:jpa="http://www.springframework.org/schema/data/jpa"
xmlns:tx="http://www.springframework.org/schema/tx"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-3.0.xsd
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.xsd
http://www.springframework.org/schema/task http://www.springframework.org/schema/task/spring-task-3.0.xsd
http://www.springframework.org/schema/jee http://www.springframework.org/schema/jee/spring-jee-3.0.xsd
http://www.springframework.org/schema/data/jpa http://www.springframework.org/schema/data/jpa/spring-jpa.xsd
http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-3.0.xsd"
default-autowire="byName">
<context:annotation-config />
<context:component-scan base-package="..." />
<import resource="app-ds-jpa.xml"/>
<!-- a explicit -->
<bean id="stopWatchProviderAspect" class="....util.StopWatchProviderAspect" />
<!-- b dynamic -->
<bean class="org.springframework.aop.aspectj.annotation.AnnotationAwareAspectJAutoProxyCreator" />
<!-- Enable the @AspectJ support -->
<aop:aspectj-autoproxy proxy-target-class="true" />
</beans>
StopWatchProvider is loaded via a maven dependency. Defining the bean explicitly (a) will work fine. The dynamic approach however fails with the following exception:
Exception in thread "main" org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'defaultData': Injection of autowired dependencies failed; nested exception is org.springframework.beans.factory.BeanCreationException: Could not autowire field: private ....manager.CustomerManager ....util.DefaultData.customerManager; nested exception is java.lang.IllegalArgumentException: Can not set ....manager.CustomerManager field ....util.DefaultData.customerManager to $Proxy26
at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor.postProcessPropertyValues(AutowiredAnnotationBeanPostProcessor.java:285)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.populateBean(AbstractAutowireCapableBeanFactory.java:1074)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:517)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:456)
at org.springframework.beans.factory.support.AbstractBeanFactory$1.getObject(AbstractBeanFactory.java:291)
at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:222)
at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFa开发者_如何学编程ctory.java:288)
at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:190)
at org.springframework.beans.factory.support.DefaultListableBeanFactory.preInstantiateSingletons(DefaultListableBeanFactory.java:580)
at org.springframework.context.support.AbstractApplicationContext.finishBeanFactoryInitialization(AbstractApplicationContext.java:895)
at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:425)
at org.springframework.context.support.ClassPathXmlApplicationContext.<init>(ClassPathXmlApplicationContext.java:139)
at org.springframework.context.support.ClassPathXmlApplicationContext.<init>(ClassPathXmlApplicationContext.java:83)
at ....App.main(App.java:190)
Caused by: org.springframework.beans.factory.BeanCreationException: Could not autowire field: private ....manager.CustomerManager ....util.DefaultData.customerManager; nested exception is java.lang.IllegalArgumentException: Can not set ....manager.CustomerManager field ...e.util.DefaultData.customerManager to $Proxy26
at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor$AutowiredFieldElement.inject(AutowiredAnnotationBeanPostProcessor.java:502)
at org.springframework.beans.factory.annotation.InjectionMetadata.inject(InjectionMetadata.java:84)
at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor.postProcessPropertyValues(AutowiredAnnotationBeanPostProcessor.java:282)
... 13 more
Caused by: java.lang.IllegalArgumentException: Can not set ....manager.CustomerManager field ....util.DefaultData.customerManager to $Proxy26
at sun.reflect.UnsafeFieldAccessorImpl.throwSetIllegalArgumentException(UnsafeFieldAccessorImpl.java:146)
at sun.reflect.UnsafeFieldAccessorImpl.throwSetIllegalArgumentException(UnsafeFieldAccessorImpl.java:150)
at sun.reflect.UnsafeObjectFieldAccessorImpl.set(UnsafeObjectFieldAccessorImpl.java:63)
at java.lang.reflect.Field.set(Field.java:657)
at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor$AutowiredFieldElement.inject(AutowiredAnnotationBeanPostProcessor.java:498)
Don't know if that's even possible. However I found in an old post in the comments, that the AnnotationAwareAspectJAutoProxyCreator conflicts with my Autowireing byName.
Found somewhere else, that this kind of error suggests, I am proxying a proxy which is illegal, though I don't understand what that's supposed to mean.
One answer to a Related Question is, that I need to do the autoproxy after all bean initialization but that didn't help (as seen in my app-context)
I've been looking around forever for a way to autodetect my Aspects in a main application without having to staticly define them in the main app-context.xml I don't need to do this via the AnnotationAwareAspectJAutoProxyCreator. As long as I don't have to "register" all aspects in the one app-context.xml file I am happy.
My alternative would be to have all plugins implement a common interface "Pluggable" with access to a PluginManager via which they could get start()ed, loading up their own application context so that I can put the bean definition in their child context .xml as a resource within their respective projects. I kind of got this to work (at least the loading of several application contexts so I can keep the bean information in the corresponging package) but I'd still prefer a more dynamic approach.
Thanks for any pointers or maybe even answers to why I get that exception!
EDIT:
Since no one seems to know a solution for the conflict between autowire by name and AnnotationAwareAspectJAutoProxyCreator
we register our aspects inside their project's context now and import all project contexts into the web app that uses the aspect:
Core Project:
Aspect:
@Aspect
public class SomeAspect { ... }
core-context.xml:
<bean id="someAspect" class="path.to.core.SomeAspect />
<aop:aspectj-autoproxy proxy-target-class="true" />
Web Project:
<import resource="classpath:core-context.xml"/>
Then the aspect will fire in the web project as well. This suffices for us for now.
I guess another dynamic approach would be to scan the classpath for @Aspect
annotations inside a setup bean and register the aspects via a BeanFactory.
Still, if anyone knows more about the conflict between Autowire: by-name
and AnnotationAwareAspectJAutoProxyCreator
please share it.
精彩评论