java Spring动态注入,工厂式模式

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/10429572/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-10-31 01:00:52  来源:igfitidea点击:

Spring dynamic injection, factory-like pattern

javaspringdependency-injectionfactoryfactory-pattern

提问by pfh

A continuation from Dependency injection, delayed injection praxis. I have the Main class:

依赖注入的延续,延迟注入实践。我有主课:

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();
    }
}

And the object graph is dynamically created, depending on the user interaction. I solved the application coupling, allowing me to test this very simply. Also, since the lists are maintained by the container, the dynamic nature of this application(and every other) is irrelevant, since they can be requested any time the application needs them, maintaining their elements.

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

The rest of the code is here:

其余代码在这里:

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;
    }
}

Does anyone know how would I solve the method call createList() or createSecondList() - without using the constructor which pretty much forces the design. I was thinking of a factory, but a factory for every class in a project of a bigger magnitude doesn't seem like a good idea.

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

Something like:

就像是:

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

And in the factory method instantiate the class and call the method createList(). Or call it like this, from some method - which again looks bad, forcing the method to have the responsibility to instantiate the object graph.

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

The picture of the runtime dependencies that I want to resolve in runtime is bellow:

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

enter image description here

在此处输入图片说明

Is there some other way I could use the container to achive dynamic lazy initalization depending on the user interaction?

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

Thank you.

谢谢你。

回答by aviad

If you want some member of your class to be dynamically initialized\populated on every call to the corresponding getter, you can try the Lookup Method Injection. Read pp. 3.3.4.1here.

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

So even if the class that contains the dynamic member was created in scope=singletone(the default for spring bean container) every time you will accessthe field that has a lookup method assigned, you will get an appropriate object according to the business logic implemented inside the lookup method. In your case the list is an interface so you can easily implement the validation inside your lookup method and return a validated list.

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

Edit:

编辑:

I found better examplein Spring documentation - I think it is very clear. Take a look at "3.4.6.1 Lookup method injection"

我在 Spring 文档中找到了更好的例子——我认为它很清楚。看看《3.4.6.1 Lookup方法注入》

When you configure the Mainclass assign a lookup method to its Listmember - it will be called whenever you need a new instance of the Listbean.

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

Good luck!

祝你好运!

回答by Yves Martin

Spring is designed for re-usable component injection, not for business data manipulation and injection.

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

Indeed some data are used in dependency injection, but only to configure components behavior, not to create business data holder.

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

By the way, the following option may be used in your case: thanks a BeanFactorywith BeanFactoryAware interfaceand the use of scope="prototype", you can generate a bean by invoking getBean()like in that exampleor from that other question: creating bean on demand.

顺便说一句,下面的选项可能会在你的情况下使用:非常感谢BeanFactoryBeanFactoryAware接口和使用范围=“原型”,你可以通过调用生成一个beangetBean()就像这个例子其他问题:按需创建豆.

An alternative option if you have a limited number of beans to prepare is to use generic bean creation the same way lacking beans are mocked

如果要准备的 bean 数量有限,另一种选择是使用通用 bean 创建,就像模拟缺少 bean 的方式一样

Now consider that Spring never garbage collects beans in its Context. So it is risky for memory consumption to create Spring beans to hold business data.

现在考虑一下 Spring 从不垃圾收集其上下文中的 bean。所以创建Spring bean来保存业务数据是有内存消耗的风险的。

If your aim is different (I hope so), maybe you are trying to implement by your own a multi-tenant support. Spring provides tenancyin case you have different business context to implement with specific components or behaviors.

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

回答by dough

Sounds like a user can choose 1..N graphs of Objects and you only want to load the one that the user selects at runtime. If the graphs are known at design time but the user just chooses the one they want then it sounds to me like what you have is a bunch of ApplicationContexts and you only want to load the one ApplicationContext that the user selects at runtime. So why not just define the set of ApplicationContexts and then just instantiate the right one at runtime. Since Spring supports Java Config it might make sense to define these configs as Java classes so you can get inheritance and avoid cutting/pasting any code.

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