I am using the PF4J framework to load plugins into a Spring application. I would like the Component/Service beans defined by the plugin to be able to autowire beans that are defined by the parent context. That is, the application has some services that it provides that the plugin needs to use (e.g., a callback service). To that end, the plugin's application context (child) is setup so that it has a reference up to the parent context.
That all works fine. The plugins get loaded. Its beans get instantiated and they successfully autowire parent beans into their constructors.
The problem is: because the child has a reference to the parent context, any application events generated from the child context get propagated up to the parent context (e.g., ContextRefreshedEvent, etc...). For some of those events our application executes actions that are not safe to repeat and causes the application to experience unexpected errors.
The question is: is there a way to structure the child context such that autowiring is able to reach up into the parent context and get beans defined there BUT that the parent does not experience any side-effects from any actions on the child context?
For reference here is how the child context is setup. Commenting out the setParent
line successfully stops events from leaking up to the parent, but autowiring no longer works from parent -> child:
SpringPluginManager manager = (SpringPluginManager) getWrapper().getPluginManager();
ApplicationContext parent = manager.getApplicationContext();
AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext();
applicationContext.setParent(parent);
applicationContext.setClassLoader(getWrapper().getPluginClassLoader());
getComponentClasses().forEach(applicationContext::register);
applicationContext.refresh();
Alternatively, I suppose the plugin could access the parent context on its own and do a getBean(SomeParentInterface.class)
and do its own "wiring" of dependencies manually.
UPDATE1:
I think I may have found a solution. I was able to set the child context bean factory to point back to the parent bean factory. This allows beans from the parent to get autowired into child context beans without also propagating events. Not sure if this approach will have any other unintended side-effects but it appears to work.
SpringPluginManager manager = (SpringPluginManager) getWrapper().getPluginManager();
ApplicationContext parent = manager.getApplicationContext();
AutowireCapableBeanFactory parentBeanFactory = parent.getAutowireCapableBeanFactory();
DefaultListableBeanFactory combinedFactory = new DefaultListableBeanFactory(parentBeanFactory) ;
AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(combinedFactory);
applicationContext.setClassLoader(getWrapper().getPluginClassLoader());
getComponentClasses().forEach(applicationContext::register);
applicationContext.refresh();