0

我有 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”。

4

1 回答 1

0

首先我想说,我觉得这是一个奇怪的要求。我也想知道为什么你的 DAO 需要服务。在正常的分层设计中,情况正好相反(Service 使用 DAO)。

但是我发现挑战很有趣,我尝试使用 aFactoryBean创建一个 JavaProxy类,该类将在运行时重定向到 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.
  • 我仍然想知道是否有更简单的方法,但我认为没有。
  • 您可以替换部分 InvocationHandler 以使用配置映射(包 => MyService bean 名称)
  • 不建议在生产环境中使用此类代码。
于 2013-06-28T20:34:37.703 回答