动态Spring bean创建

p.m*_*ten 3 spring inject cdi

我正在使用一个API,它以XXXLocalServiceUtil类的形式公开服务,这些类是单例对象的静态包装器.我想要注入XXXLocalService对象本身,而不是使用静态XXXLocalServiceUtil方法,直接在我的代码中使用它们,例如:

@Named
public class MyMailingService {        
    @Inject UserLocalService userService;

    public String mailUser(String email) {
       User user = userService.getUser(email);
       emailUser(user);
    }
}
Run Code Online (Sandbox Code Playgroud)

并配置我applicationContext.xml喜欢:

<beans ...>
    <bean class="x.y.z.UserLocalServiceUtil" factory-method="getService"/>
    <bean class="x.y.z.CompanyLocalServiceUtil" factory-method="getService"/>
    ...
</beans>
Run Code Online (Sandbox Code Playgroud)

这非常有效.现在,我正在谈论的这个API有大约100个这样的XXXLocalServiceUtil类,每个类都有自己的静态getService方法,它返回实际的服务.我没有在我的列表中列出所有这些服务,而是applicationContext.xml让Spring为我注入的每个XXXLocalService找到正确的XXXLocalServiceUtil类.所以我需要的是某种动态bean工厂,它会为我做这项工作,当然是在延迟加载的基础上.

谁知道如何轻松实现这一目标?

hyn*_*ess 7

您可以使用GenericApplicationContext将bean动态加载到applicationContext以及在xml中声明的其余spring bean.这是使用Reflections库实现的示例...

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();
}
Run Code Online (Sandbox Code Playgroud)

更新: 要在Web应用程序中使用它,您可以简单地扩展Spring的XmlWebApplicationContext并覆盖该initBeanDefinitionReader方法,如下所示......

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);
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

}

并将以下内容添加context-param到您的web.xml中 ...

<context-param>
  <param-name>contextClass</param-name>
  <param-value>x.y.z.MyXmlWebApplicationContext</param-value>
</context-param>
Run Code Online (Sandbox Code Playgroud)