【问题标题】:Spring dynamic injection, factory-like patternSpring动态注入,类工厂模式
【发布时间】:2012-05-03 10:31:24
【问题描述】:

Dependency injection, delayed injection praxis 的延续。我有主课:

package test;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.springframework.stereotype.Component;

import java.util.List;
import java.util.Scanner;

@Component
public class Main {
    @Autowired
    private StringValidator stringValidator;

    @Autowired
    private StringService stringService;

    @Autowired
    private ValidationService validationService;

    public void main() {
        scanKeyboardCreateLists();

        stringValidator.validate();

        final List<String> validatedList = stringValidator.getValidatedList();
        for (String currentValid : validatedList) {
            System.out.println(currentValid);
        }
    }

    private void scanKeyboardCreateLists() {
        //Let's presume the user interacts with the GUI, dynamically changing the object graph...
        //Needless to say, this is past container initialization...
        Scanner scanner = new Scanner(System.in);
        int choice = scanner.nextInt();

        //Delayed creation, dynamic
        if (choice == 0) {
            stringService.createList();
            validationService.createList();
        } else {
            stringService.createSecondList();
            validationService.createSecondList();
        }
    }

    public static void main(String[] args) {
        ApplicationContext container = new ClassPathXmlApplicationContext("/META-INF/spring/applicationContext.xml");
        container.getBean(Main.class).main();
    }
}

并且对象图是动态创建的,取决于用户交互。我解决了应用程序耦合,让我可以非常简单地测试它。 此外,由于列表由容器维护,因此该应用程序(以及其他所有应用程序)的动态特性是无关紧要的,因为可以在应用程序需要它们的任何时候请求它们,并维护它们的元素。

剩下的代码在这里:

package test;

import java.util.List;

public interface Stringable {
    List<String> getStringList();
}

package test;

import org.springframework.stereotype.Component;

import java.util.ArrayList;

@Component
public class StringList extends ArrayList<String> {
}

package test;

import org.springframework.stereotype.Component;

import javax.inject.Inject;
import java.util.ArrayList;
import java.util.List;

@Component
public class StringService implements Stringable {

    private List<String> stringList;

    @Inject
    public StringService(final ArrayList<String> stringList) {
        this.stringList = stringList;
    }

    //Simplified
    public void createList() {
        stringList.add("FILE1.txt");
        stringList.add("FILE1.dat");
        stringList.add("FILE1.pdf");
        stringList.add("FILE1.rdf");
    }

    public void createSecondList() {
        stringList.add("FILE2.txt");
        stringList.add("FILE2.dat");
        stringList.add("FILE3.pdf");
        stringList.add("FILE3.rdf");
    }

    @Override
    public List<String> getStringList() {
        return stringList;
    }
}

package test;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import java.util.ArrayList;
import java.util.List;

@Component
public class StringValidator {
    private List<String> stringList;
    private List<String> validationList;

    private final List<String> validatedList = new ArrayList<String>();

    @Autowired
    public StringValidator(final ArrayList<String> stringList,
                           final ArrayList<String> validationList) {
        this.stringList = stringList;
        this.validationList = validationList;
    }

    public void validate() {
        for (String currentString : stringList) {
            for (String currentValidation : validationList) {
                if (currentString.equalsIgnoreCase(currentValidation)) {
                    validatedList.add(currentString);
                }
            }
        }
    }

    public List<String> getValidatedList() {
        return validatedList;
    }
}

package test;

import java.util.List;

public interface Validateable {
    List<String> getValidationList();
}

package test;

import org.springframework.stereotype.Component;

import java.util.ArrayList;

@Component
public class ValidationList extends ArrayList<String> {
}

package test;

import org.springframework.stereotype.Component;

import javax.inject.Inject;
import java.util.ArrayList;
import java.util.List;

@Component
public class ValidationService implements Validateable {

    private List<String> validationList;

    @Inject
    public ValidationService(final ArrayList<String> validationList) {
        this.validationList = validationList;
    }

    //Simplified...
    public void createList() {
        validationList.add("FILE1.txt");
        validationList.add("FILE2.txt");
        validationList.add("FILE3.txt");
        validationList.add("FILE4.txt");
    }

    public void createSecondList() {
        validationList.add("FILE5.txt");
        validationList.add("FILE6.txt");
        validationList.add("FILE7.txt");
        validationList.add("FILE8.txt");
    }

    @Override
    public List<String> getValidationList() {
        return validationList;
    }
}

有谁知道我将如何解决方法调用 createList() 或 createSecondList() - 不使用几乎强制设计的构造函数。我在想一个工厂,但是在一个更大的项目中为每个班级都建一个工厂似乎不是一个好主意。

类似:

<bean ... factory-method="..." depends-on="..." lazy-init="..."/>

并在工厂方法中实例化类并调用方法createList()。 或者像这样从某个方法调用它 - 这又看起来很糟糕,迫使该方法有责任实例化对象图。

我想在运行时解决的运行时依赖的图片如下:

是否有其他方法可以根据用户交互使用容器来实现动态延迟初始化?

谢谢。

【问题讨论】:

  • 我不知道你在问什么。 “解决方法调用 createList() 或 createSecondList()”是什么意思?如果我对您尝试做的事情的猜测是正确的(我对此表示怀疑),我将创建一个工厂类,该类具有(静态?)工厂方法,该方法采用交互式参数并创建适当的列表,然后注入将该类的工厂对象放入您的 Main 对象中。
  • 我以为你会从上下文中理解。不是一个伟大的问题作家。是的,像这样。问题加粗了。除了工厂(当然是静态的)和使用对象的拉取/初始化(在主类中进行初始化,“main”方法)之外,我如何构造动态对象图,这样我就不必担心架构“代码“在我的申请中。为什么要在 Main 对象中注入工厂对象?如果你所有的课程都是动态的,你会有很多工作。因为您应该在每个动态类上都有一个工厂。我仍然相信有一个更简单的解决方案:)

标签: java spring dependency-injection factory factory-pattern


【解决方案1】:

如果您希望类的某些成员在每次调用相应的 getter 时动态初始化\填充,您可以尝试查找方法注入。阅读页。 3.3.4.1 here

所以即使包含动态成员的类是在scope=singletone(spring bean容器的默认值)中创建的,每次访问分配了lookup方法的字段时,都会根据业务得到一个合适的对象在查找方法内部实现的逻辑。在您的情况下,列表是一个接口,因此您可以轻松地在查找方法中实现验证并返回经过验证的列表。

编辑:

我在 Spring 文档中找到了更好的 example - 我认为这很清楚。 看看《3.4.6.1 Lookup方法注入》

当您配置 Main 类时,为其 List 成员分配一个查找方法 - 只要您需要 List bean 的新实例,就会调用它。

祝你好运!

【讨论】:

  • 这是个好主意,但是将工厂移动到方法中似乎并不能解决架构代码。此外,它增加了复杂性。突然,该方法必须返回所需的类型。然后当我需要 N 种类型时,我有 N 种方法。我看错了吗? Spring 没有类似 @AssistedInject(code.google.com/p/google-guice/wiki/AssistedInject, jira.springsource.org/browse/SPR-5192) 的东西?如果您有一个“动态”应用程序,您通常会这样做吗?这似乎是经验丰富的程序员不经常访问的领域......
  • 我认为单个查找方法就足够了(确保您没有错过这一点):) 查找方法将返回一个实现一些通用接口(列表?)甚至标记接口的对象.查找方法将产生什么确切类型的对象的决定可以基于一些外部源(配置文件、用户输入等),但所有可能的类型都需要提前知道
  • 我尽量不要错过重点,但似乎我失败了。不要误会我的意思,我很感谢你的回答。这 - java.dzone.com/articles/pragmatic-look-method 与我想要实现的非常不同。我正试图从我能想到的所有角度来看待这个问题......你实际上尝试做一个例子并证明这一点怎么样?是的,我可以使用标记界面。但后来我“只是”必须解决运行时依赖性。而“方法注入”并不能真正解决这个问题。如何根据运行时实例化不同的对象?我更新了我的问题,所以你可以再看看。
  • 您提到的文章确实与我认为您想要实现的目标大不相同。 (你不需要AOP,我在spring doc“查找方法注入”中指出了特定的段落)。在发布示例之前,我需要澄清一下:“...取决于运行时”是什么意思?
  • 我的意思是取决于示例中的运行时参数。如果用户输入 0,他将收到与输入其他任何内容完全不同的对象树。该参数是在容器初始化后请求的,因此对象是根据用户输入的内容动态创建的。关键是 - 该方法需要通过调用依赖对象上的不同方法来初始化对象,具体取决于用户输入.该依赖方法实际上是修改列表的类的处理方法。
【解决方案2】:

Spring 是为可重用的组件注入而设计的,而不是为业务数据操作和注入而设计的。

确实在依赖注入中使用了一些数据,但仅用于配置组件行为,而不是创建业务数据持有者。

顺便说一句,在您的情况下可以使用以下选项:感谢 BeanFactoryBeanFactoryAware interface 以及使用 scope="prototype",您可以像在 @987654322 中一样通过调用 getBean() 来生成 bean @ 或来自that other question: creating bean on demand

如果要准备的 bean 数量有限,另一种选择是使用通用 bean 创建 the same way lacking beans are mocked

现在考虑 Spring 从不会在其 Context 中对 bean 进行垃圾收集。所以创建 Spring bean 来保存业务数据存在内存消耗的风险。

如果您的目标不同(我希望如此),也许您正在尝试自己实现多租户支持。 Spring 提供 tenancy 以防您有不同的业务上下文来实现特定组件或行为。

【讨论】:

  • 不,我再次尝试将业务数据和架构结合起来。问题是,如果您使用的是 OOP 架构,您不能真正使用容器进行大量注入。如果开始使用无状态服务,将代码分为数据对象和服务,可以使用spring alot。但我不是为了那个。我认为状态对象仍然是规则(这是我的观点)。我理解垃圾回收,对象是单例,不会有任何问题。轻微的内存增加几乎是不可见的。问题仍然存在 - 您无法将代码“剪切”成可见的架构代码。
  • 对于嵌入了业务数据的有状态组件可以。但是如果它只是一个单例,如何处理并发或多用户访问。你会在单例中使用线程局部变量吗?你会切换到工厂模式吗?真诚地,JavaEE 概念是围绕模式设计的,以实现线程安全和可扩展。 Spring 被设计为以更轻松的方式使用相同的模式,但目标仍然存在:性能并发。
  • “让它工作,然后优化”。现在的目标不是处理并发,而是想出一种在动态环境中使用容器的方法。在应用程序中使用某种容器时,处理有状态的类仍然是一个问题。撇开这一点不谈,你处理它的想法非常好。客观的“性能并发”并不是什么新鲜事物。所以,是的,您可以通过为每个用户使用一个单例来使其工作(您可以争辩说这不是单例)。但要达到一个真正伟大的容器就意味着让它充满活力。
  • “让它工作,然后优化”只有在初始架构概念足够好,能够在不完全重写的情况下进行优化时才会产生结果。我的观点是,基于 statefull 的设计需要更多的工作来并行化,以避免难以诊断和更难以修复的并发问题。我几乎可以肯定,可以通过多租户 Spring 支持来实现“每个用户一个人”。试试这种方式。
【解决方案3】:

听起来用户可以选择 1..N 个对象图,而您只想加载用户在运行时选择的那个。如果图表在设计时是已知的,但用户只是选择了他们想要的那个,那么在我看来,你拥有的是一堆 ApplicationContext,而你只想加载用户在运行时选择的一个 ApplicationContext。那么为什么不直接定义一组 ApplicationContext,然后在运行时实例化正确的。由于 Spring 支持 Java Config,因此将这些配置定义为 Java 类可能是有意义的,这样您就可以获得继承并避免剪切/粘贴任何代码。

【讨论】:

  • 正确。但问题是容器“不知道”运行时的对象是什么。如果你有一个文件,并希望用户选择它,你就不能真正静态地预定义所有可能的组合。这个想法很好,但是解决了动态部分(使用列表作为“全局”bean)。真正的问题是如何在用户行为(例如单击按钮)时实例化对象,知道您的方法中有初始化。我需要某种惰性初始化,但不想在我的 main 方法中使用工厂或初始化方法牺牲代码清洁度。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多