我有2个包含类的模块:
blog.model.ArticleDAO
blog.model.CategoryDAO
users.model.UserDAO
users.model.UserGroupDAO
所有这些DAO都依赖于相同的服务,但我需要根据包注入不同的实例。
我的意思是模块博客应该有MyService
的特定实例,模块用户应该有另一个MyService
实例。
我不想创建2个命名服务,因为有一天我可能想为所有DAO使用相同的服务。或者我也想为特定的类注入另一个特定的实例......
有没有办法根据类的包来注入服务?
一种说法:
foo
(MyService
的实例)注入blog.*
bar
(MyService
的实例)注入users.*
但保持我的所有课程都没有意识到这一点!他们的配置只应说明“注入MyService
的实例”。
答案 0 :(得分:0)
首先我想说,我发现这是一个奇怪的要求。我也想知道为什么你的DAO需要服务。在正常的分层设计中,这是相反的(服务使用DAO)。
但是我发现挑战很有趣,我尝试使用FactoryBean
创建一个Java Proxy
类,它将根据调用程序包在运行时重定向到MyService的正确实例。这是代码:
public class CallerPackageAwareProxyFactoryBean implements
FactoryBean<MyService>, ApplicationContextAware {
private Class<?> targetServiceType;
private ApplicationContext applicationContext;
private InvocationHandler invocationHandler = new InvocationHandler() {
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable {
if (ReflectionUtils.isEqualsMethod(method)) {
// Only consider equal when proxies are identical.
return (proxy == args[0]);
} else if (ReflectionUtils.isHashCodeMethod(method)) {
// Use hashCode of service locator proxy.
return System.identityHashCode(proxy);
} else if (ReflectionUtils.isToStringMethod(method)) {
return "Service dispatcher: " + targetServiceType.getName();
} else {
String callerPackageFirstLevel = getCallerPackageFirstLevel();
Map<String, ?> beans = applicationContext
.getBeansOfType(targetServiceType);
for (Map.Entry<String, ?> beanEntry : beans.entrySet()) {
if (beanEntry.getKey().startsWith(callerPackageFirstLevel)) {
return method.invoke(beanEntry.getValue(), args);
}
}
throw new IllegalArgumentException(
String.format(
"Could not find any valid bean to forward call for method %s.",
method.getName()));
}
}
private String getCallerPackageFirstLevel() {
Throwable t = new Throwable();
StackTraceElement[] elements = t.getStackTrace();
String callerClassName = elements[3].getClassName();
return callerClassName.split("\\.")[0];
}
};
public MyService getObject() throws Exception {
return (MyService) Proxy.newProxyInstance(Thread.currentThread()
.getContextClassLoader(), new Class<?>[] { MyService.class },
invocationHandler);
}
public Class<?> getObjectType() {
return MyService.class;
}
public boolean isSingleton() {
return true;
}
public void setApplicationContext(ApplicationContext applicationContext) {
this.applicationContext = applicationContext;
}
public void setTargetServiceType(Class<?> targetServiceType) {
this.targetServiceType = targetServiceType;
}
}
我没有必要改变Dao或Service配置。我只需要在Spring上下文中添加FactoryBean的创建:
<bean id="myService" class="stackoverflow.CallerPackageAwareProxyFactoryBean">
<property name="targetServiceType" value="a.b.c.MyService" />
</bean>
也许有几条评论:
InvocationHandler
的代码受ServiceLocatorFactoryBean
启发。