Dynamic Spring bean creation
cdi, inject, spring
Solution
You can use a GenericApplicationContext to dynamically load the beans to an applicationContext along with the rest of your spring beans declared in xml. Here is an example implemented using the Reflections library...
private static final Pattern SERVICE_UTIL_PATTERN = Pattern.compile(".*LocalServiceUtil.*");
public static void main(String[] args) {
ConfigurationBuilder builder = new ConfigurationBuilder().addUrls(
ClasspathHelper.forPackage("x.y.z"))
.setScanners(new SubTypesScanner(false));
Reflections reflections = new Reflections(builder);
GenericApplicationContext applicationContext = new GenericApplicationContext();
Set<Class<? extends Object>> classes = reflections.getSubTypesOf(Object.class);
for (Class<? extends Object> serviceUtilClass : classes) {
String className = serviceUtilClass.getName();
if (SERVICE_UTIL_PATTERN.matcher(className).matches()) {
GenericBeanDefinition beanDefinition = new GenericBeanDefinition();
beanDefinition.setBeanClassName(className);
beanDefinition.setFactoryMethodName("getService");
beanDefinition.setLazyInit(true);
String beanName = StringUtils.uncapitalize(serviceClass.getSimpleName().replace("Util", ""));
applicationContext.registerBeanDefinition(beanName, beanDefinition);
}
}
XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader(applicationContext);
reader.loadBeanDefinitions("classpath:/applicationContext.xml");
applicationContext.refresh();
}
Update: To use this in a web application, you can simply extend Spring's XmlWebApplicationContext and override the `initBeanDefinitionReader` method as follows...
private static final Pattern SERVICE_UTIL_PATTERN = Pattern.compile(".*LocalServiceUtil.*");
@Override
protected void initBeanDefinitionReader(
XmlBeanDefinitionReader beanDefinitionReader) {
ConfigurationBuilder builder = new ConfigurationBuilder().addUrls(
ClasspathHelper.forPackage("x.y.z"))
.setScanners(new SubTypesScanner(false));
Reflections reflections = new Reflections(builder);
Set<Class<? extends Object>> classes = reflections.getSubTypesOf(Object.class);
BeanDefinitionRegistry registry = beanDefinitionReader.getRegistry();
for (Class<? extends Object> serviceClass : classes) {
String className = serviceClass.getName();
if (SERVICE_UTIL_PATTERN.matcher(className).matches()) {
GenericBeanDefinition beanDefinition = new GenericBeanDefinition();
beanDefinition.setBeanClassName(className);
beanDefinition.setFactoryMethodName("getService");
beanDefinition.setLazyInit(true);
String beanName = StringUtils.uncapitalize(serviceClass
.getSimpleName().replace("Util", ""));
registry.registerBeanDefinition(beanName, beanDefinition);
}
}
}
}
and add the following `context-param` to your web.xml...
<context-param>
<param-name>contextClass</param-name>
<param-value>x.y.z.MyXmlWebApplicationContext</param-value>
</context-param>
Problem
I'm using an API that exposes services in the form of XXXLocalServiceUtil classes which are static wrappers of singleton objects. Instead of using the static XXXLocalServiceUtil methods I want to inject the XXXLocalService objects themselves to use them directly in my code, e.g.: ``` @Named public class MyMailingService { @Inject UserLocalService userService; public String mailUser(String email) { User user = userService.getUser(email); emailUser(user); } } ``` And configure my `applicationContext.xml` like so: ``` <beans ...> <bean class="x.y.z.UserLocalServiceUtil" factory-method="getService"/> <bean class="x.y.z.CompanyLocalServiceUtil" factory-method="getService"/> ... </beans> ``` This works perfectly. Now, this API I'm talking about has about 100 of these XXXLocalServiceUtil classes, each with their own static `getService` method which returns the actual service. Instead of listing all those services in my `applicationContext.xml` I would like to let Spring do the magic of finding the right XXXLocalServiceUtil class for each XXXLocalService I inject. So what I need is some kind of dynamic bean factory that will do the work for me, on a lazy-loading basis of course. Anybody knows how this can be achieved easily?