-
Abstract Schema
As part of a project to get a better understanding of how to use CGLIB I've been creating an implementation of Rickard Öberg's "abstract schema" approach to AOP introduction and interception targeted for Spring's AOP subsystem. I've managed to get everything working nicely if I programmatically create my proxies using ProxyFactory however I've run into a small problem with creating proxies using an application context.
With "abstract schema" the target class declares it requires a specific introduction by declaring that it implements a particular interface but then *not* actually implementing any of the interface's methods. For example the following class requires that an implementation for the interfaces Foo and Bar be provided by the AOP system
Code:
public abstract class SomeClass
implements Foo,Bar
{
public String someMethod() {..}
}
I would like to be able to configure the introductions for this class in an application context using some config along these lines:
Code:
<beans>
<bean id="someClass" class="SomeClass" />
<bean id="autoProxyCreator"
class="...DefaultAdvisorAutoProxyCreator">
</bean>
<bean id="fooImpl" class="FooImpl" />
<bean id="fooIntroduction
class="...DelegatingIntroductionInterceptor">
<constructor-arg><ref local="fooImpl"/</constructor-arg>
</bean>
<bean id="barImpl" class="BarImpl" />
<bean id="barIntroduction
class="...DelegatingIntroductionInterceptor">
<constructor-arg><ref local="BarImpl"/</constructor-arg>
</bean>
</beans>
However as "SomeClass" is abstract the bean factory is unable to instantiate it.
So how do I instantiate this class? My first thought was to use a BeanFactoryPostProcessor that would find the bean definition for the abstract class, generate a new concrete subclass and replace the abstract class with this new subclass.
Code:
public void postProcessBeanFactory(
ConfigurableListableBeanFactory beanFactoryToProcess)
throws BeansException {
String[] beanNames = beanFactoryToProcess.getBeanDefinitionNames();
for (int i = 0; i < beanNames.length; i++) {
BeanDefinition bd = beanFactoryToProcess
.getBeanDefinition(beanNames[i]);
Class beanType = beanFactoryToProcess.getType(beanNames[i]);
if (Modifier.isAbstract(beanType.getModifiers())) {
((AbstractBeanDefinition)bd).setBeanClass(createContreteClass(beanType));
}
}
}
private Class createContreteClass(Class abstractClass) {
...
}
Unfortunately as I am generating the concrete subclass using a CGLIB enhancer I must use the Enhancer.create() method to create the instance so that the callbacks are correctly installed....
What about using my BeanFactoryPostProcessor to install a new InstantiationStrategy into the bean factory? Unfortunately the instantiationStrategy property on AbstractAutowireCapableBeanFactory is protected so I can't access it... As a stop gap measure I've resorted to setting the InstantiationStrategy using reflection.
Does anyone have a suggestion of how I can get around this? My first thought was that setInstantiationStrategy could be made public and moved over to ConfigurableBeanFactory but I assume there are strong reason this was not done when this interface was created.
Thanks,
Ollie
PS. If anyone's interested here's the programmatic configuration for this problem
Code:
SomeClass someClassTarget = (SomeClass) new MethodReplaceableSubclassCreator(
SomeClass.class, AbstractMethodMatcher.INSTANCE)
.instantiate(null, null);
((MethodReplaceable)someClassTarget)
.setMethodReplacer(CurrentAopProxyMethodReplacer.INSTANCE);
ProxyFactory pf = new ProxyFactory(someClassTarget);
pf.setExposeProxy(true);
pf.setProxyTargetClass(true);
pf.addAdvice(new DelegatingIntroductionInterceptor(
new FooImpl()));
pf.addAdvice(new DelegatingIntroductionInterceptor(
new BarImpl()));
SomeClass someClass = (SomeClass)pf.getProxy()
someClass.foo();
...
-
Oliver,
I've got an almost complete implementation of abstract schema, intended for Spring 1.2, but not in the sandbox yet, as I haven't had time to address all issues.
It might be best if we take this offline--send me a private message and we can compare notes. I would like to see your implementation. In my implementation, the abstract schema subclass is generated by an advice factory, so there don't need to be any changes to the IoC container.
R