0

クラスを含む2つのモジュールがあります:

blog.model.ArticleDAO
blog.model.CategoryDAO

users.model.UserDAO
users.model.UserGroupDAO

これらの DAO はすべて同じサービスに依存していますが、パッケージに基づいて別のインスタンスを挿入する必要があります。

つまり、モジュール ブログには の特定のインスタンスがMyService必要であり、モジュール ユーザーには の別のインスタンスが必要ですMyService

2 つの名前付きサービスを作成したくありません。いつの日か、すべての DAO で同じサービスを使用したいかもしれないからです。または、特定のクラスに別の特定のインスタンスを注入することもできます...

クラスのパッケージに基づいてサービスを注入する方法はありますか?

言い方:

  • にあるクラスにfoo(のインスタンス) を注入するMyServiceblog.*
  • にあるクラスにbar(のインスタンス) を注入するMyServiceusers.*

しかし、私のすべてのクラスにそれを知らせないでください!それらの構成は、「のインスタンスを注入する」のみを示す必要がありますMyService

4

1 に答える 1

0

最初に言いたいのですが、これは奇妙な要件だと思います。また、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 名)
  • このようなコードを生産的な環境で使用することはお勧めしません。
于 2013-06-28T20:34:37.703 に答える