【问题标题】:Inject based on the package of the class基于类的包注入
【发布时间】:2013-06-28 14:21:47
【问题描述】:

我有 2 个包含类的模块:

blog.model.ArticleDAO
blog.model.CategoryDAO

users.model.UserDAO
users.model.UserGroupDAO

所有这些 DAO 都依赖于同一个服务,但我需要根据包注入不同的实例。

我的意思是模块博客应该有一个特定的MyService 实例,模块用户应该有另一个MyService 的实例。

我不想创建 2 个命名服务,因为有一天我可能想为所有 DAO 使用相同的服务。或者我也可以为特定类注入另一个特定实例...

有没有办法根据类的包来注入服务?

一种说法:

  • fooMyService 的实例)注入到blog.* 中的类中
  • barMyService 的实例)注入到users.* 中的类中

但我所有的班级都不知道这一点!他们的配置应该只声明“注入MyService的实例”。

【问题讨论】:

    标签: java spring spring-ioc


    【解决方案1】:

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

    也许有几个cmets:

    • 只能通过创建异常并查看堆栈跟踪来获取调用程序包。
    • InvocationHandler 的代码灵感来自ServiceLocatorFactoryBean
    • 我仍然想知道是否有更简单的方法,但我认为没有。
    • 您可以替换部分 InvocationHandler 以使用配置映射(包 => MyService bean 名称)
    • 不建议在生产环境中使用此类代码。

    【讨论】:

    • 哇,好的,谢谢您的回答!我期待一个配置选项(Spring 支持的东西)。而我的问题中的例子确实是一个例子,没有什么正式的)。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-01-12
    • 1970-01-01
    • 2015-04-17
    • 1970-01-01
    • 2021-08-16
    相关资源
    最近更新 更多