我正在使用以XXXLocalServiceUtil类的形式公开服务的API,这些类是单例对象的静态包装器。我想要注入XXXLocalService对象本身,而不是使用静态XXXLocalServiceUtil方法,直接在我的代码中使用它们,例如:
@Named
public class MyMailingService {
@Inject UserLocalService userService;
public String mailUser(String email) {
User user = userService.getUser(email);
emailUser(user);
}
}
并像我这样配置applicationContext.xml
:
<beans ...>
<bean class="x.y.z.UserLocalServiceUtil" factory-method="getService"/>
<bean class="x.y.z.CompanyLocalServiceUtil" factory-method="getService"/>
...
</beans>
这完美无缺。现在,我正在谈论的这个API有大约100个这样的XXXLocalServiceUtil类,每个类都有自己的静态getService
方法,它返回实际的服务。我没有在我的applicationContext.xml
中列出所有这些服务,而是让Spring为我注入的每个XXXLocalService找到正确的XXXLocalServiceUtil类。所以我需要的是某种动态bean工厂,它当然会在懒惰的基础上为我工作。
有人知道如何轻松实现这一目标吗?
答案 0 :(得分:6)
您可以使用 GenericApplicationContext 将bean动态加载到applicationContext以及在xml中声明的其余spring bean。以下是使用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();
}
更新:要在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);
}
}
}
}
并将以下context-param
添加到 web.xml ...
<context-param>
<param-name>contextClass</param-name>
<param-value>x.y.z.MyXmlWebApplicationContext</param-value>
</context-param>
答案 1 :(得分:3)
您可以尝试的一件事是将@Inject
更改为@Autowired
并在applicationContext.xml中定义为按类型自动装配,如下所示:
<beans ... default-autowire="byType">
...
</beans>
@Inject
和@Autowired
是等效的,但Spring @Autowired
注释的优势在于必须强制注入必需的属性。
另一种解决方案:
您可以使用范围原型的bean来代替使用工厂。
您的XXXLocalService
必须实现ApplicationContextAware
接口,并通过它获取原型bean,i。即:
@Named
public class MyMailingService implements ApplicationContextAware {
@Inject UserLocalService userService;
private ApplicationContext appContext;
@Override
public void setApplicationContext(ApplicationContext applicationContext) {
this.appContext = applicationContext;
}
public String mailUser(String email) {
User user = (User) appContext.getBean("user");
emailUser(user);
}
}
您的applicationContext.xml如下所示:
<beans ...>
<bean id="user" class="x.y.z.User" scope="prototype"/>
...
</beans>
每次调用getBean()
时,你都会得到一个这种类型的新对象,并且可以注入该bean中的所有内容。