最初に言いたいのですが、これは奇妙な要件だと思います。また、DAO にサービスが必要な理由も気になります。通常の階層化された設計では、これは逆です (サービスは DAO を使用します)。
ただし、この課題は興味深いと思います。呼び出し元のパッケージに応じて、実行時に MyService の正しいインスタンスにリダイレクトFactoryBean
する Java クラスを作成するためにa を使用しようとしました。Proxy
コードは次のとおりです。
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
ます。
- もっと簡単な方法があるかどうかはまだ疑問に思っていますが、そうではないと思います。
- 構成マップを使用するように InvocationHandler の一部を置き換えることができます (パッケージ => MyService Bean 名)
- このようなコードを生産的な環境で使用することはお勧めしません。