Spring dynamic injection, factory-like pattern

20,356

Solution 1

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.1 here.

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.

Edit:

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

When you configure the Main class assign a lookup method to its List member - it will be called whenever you need a new instance of the List bean.

Good luck!

Solution 2

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

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 BeanFactory with BeanFactoryAware interface and the use of scope="prototype", you can generate a bean by invoking getBean() like in that example or from that other question: creating bean on demand.

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

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.

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

Solution 3

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.

Share:
20,356
pfh
Author by

pfh

Updated on May 12, 2020

Comments

  • pfh
    pfh about 4 years

    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.

    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.

    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.

    • Old Pro
      Old Pro about 12 years
      I have no idea what you are asking. What do you mean by "solve the method call createList() or createSecondList()"? If I'm right in my guess of what you are trying to do (and I doubt it), I would create a factory class that has a (static?) factory method that takes the interactive argument and creates the appropriate list, then inject a factory object of that class into your Main object.
    • pfh
      pfh about 12 years
      I thought you'd understand from the context. Not a great question writer. Yes, something like this. The question is bolded. Besides factory(static, of course) and using pull/initialization of the objects(with initialization in the Main class, "main" method), how can I construct the dynamic object graph so I don't have to worry about architecture "code" in my application. Why would you inject the factory object in your Main object? You'd have a lot of work if all your classes are dynamic. Since you should have a factory on every dynamic class. I still belive there is a simpler solution :)
  • pfh
    pfh about 12 years
    That's a good idea, but moving the factory into a method doesn't seem to solve the arhictecture code. Also, it adds complexity. Suddenly, the method must have return the required type. And then I have N methods when I require N types. Am I looking at this wrong? Spring doesn't have something like @AssistedInject(code.google.com/p/google-guice/wiki/Assisted‌​Inject, jira.springsource.org/browse/SPR-5192)? Do you usually do something like this if you have a "dynamic" application? This seems like an area that experienced programmers don't visit that often...
  • aviad
    aviad about 12 years
    I think that a single lookup method is enough (make sure you did not miss this point) :) the lookup method will return an object that implements some common interface (List?) or even a marker interface. The decision of what exact type of object the lookup method would produce can be based on some outer source (configuration file, user input etc) but all possible types need to be known in advance
  • pfh
    pfh about 12 years
    I'm trying not to miss the point, but seems like i'm failing. Don't get me wrong, I'm thankful for the answer. This - java.dzone.com/articles/pragmatic-look-method is very different from what I want to achive. I'm trying to look at this from all the angles I can think of... How about you actually try to make an example and demonstrate this? Yes, I can use marker interface. But then I "just" have to solve runtime dependency. And "method injection" doesn't really solve this. How can I instantiate different objects depending on runtime? I updated my question so you can have another look.
  • aviad
    aviad about 12 years
    The article you referred to is indeed very different from what I thought you wanted to achieve. (You do not need AOP, I pointed to specific paragraph in spring doc "lookup method injection"). Before I post an example I need small clarification: what do you mean by "...depending on runtime" ?
  • pfh
    pfh about 12 years
    I mean depending on runtime parameter like in the example. If the user types in 0 he'll recive completly different object tree than if he types in anything else. The parameter is requested after the container has been initialized, so the objects are created dynamically depending on what the user typed in. The point is - that method needs to initialize the object by calling a different method on the dependant object depending on the user input. That dependent method is actually a processing method of the class which modifies the list.
  • pfh
    pfh about 12 years
    BUT I don't need a List. I have Lists sorted out. I create them in the container. <bean id="validationList" class="java.util.ArrayList" scope="singleton"/>. <bean id="validationService" class="test.ValidationService" scope="singleton"> <constructor-arg name="validationList" ref="validationList"/> </bean> I cannot use the lookup method to actually implement some creational logic. I can't type in an if statement and expect from the container to pick it up. I cannot use the lookup-method as a factory, I can use it as a simple getter of the container beans. Correct?
  • aviad
    aviad about 12 years
    " I cannot use the lookup method to actually implement some creational logic" - Wrong - you can. "I cannot use the lookup-method as a factory" - Wrong, you can. " I can use it as a simple getter of the container beans" - Correct, you can but what is the point?
  • pfh
    pfh about 12 years
    Really? I can use the lookup method to implement some creational logic? The way I understood it, and the way the non-abstract AND the abstract way works is that - lookup method can't implement creational logic. I know that it can "produce" beans(in the case they are prototyped), or that it can fetch beans(in the case they are singletons). I need to call the object initialization code in the lookup method. I need to instantiate the class and initalize it with a method. So that's the problem. That's the question. How do I take care of the initalization method depending on the user input?
  • pfh
    pfh about 12 years
    Correct. But the problem is that the container "doesn't know" what are the objects at runtime. If you have a File, and expect the user to select it, you can't really statically predefine all the possible combinations. The idea is good, but the dynamic part is solved(using the list as a "global" bean). The real problem is how to instantiate the object WHEN the user acts(e.g. click a button), knowing that you have your initialization in your method. I need some kind of lazy initalization, but don't want to sacrifice code cleanliness with factories or initalization methods in my main method.
  • aviad
    aviad about 12 years
    Technically, you can produce java objects that do not appear in context configuration at all. However, then spring container will not manage their life-cycle (think of garbage collection).
  • pfh
    pfh about 12 years
    No, I, once again, tried to combine buisness data and architecture. The problem is, if you are using OOP architecture, you can't really use the container for a lot of injection. If you start using stateless services and divide the code into data objects and services, you can use spring alot. But i'm not for that. I think that statefull objects still rule(that's my view). I understand garbage collection, the object is a singleton, there won't be any problems. The slight memory increase is virtually invisibile. The problem still remains - you can't "cut" the code into visibile arhitecture code.
  • Yves Martin
    Yves Martin about 12 years
    OK for statefull component with business data embedded. But how handle concurrency or multi-user access if it is just a singleton. Will you use thread-local variables inside a singleton ? Will you switch to a factory pattern ? Sincerely JavaEE concepts have been designed around patterns to be thread-safe and scalable. Spring has been designed to use the same patterns in a lighter way but objective remains: concurrency for performance.
  • pfh
    pfh about 12 years
    "Make it work, then optimize". The goal isn't to deal with concurrency right now, the idea was to come up with a way to use a container in a dynamic enviroment. Dealing with statefull classes is still a problem when using some kind of a container in an application. Putting that aside, your ideas for dealing with it are pretty good. And the objective "concurrency for performance" isn't really a new thing. So, yes, you could make it work by using a singleton per user(you could argue that that wouldn't be a singleton). But to reach a truly great container would mean to make it dynamic.
  • Yves Martin
    Yves Martin about 12 years
    "Make it work, then optimize" only leads to a result if the initial architectural concepts are good enough to be able to optimize without a full re-write. My opinion is that statefull-based design requires much more work to parallelize, to avoid tricky-to-diagnose and even-more-tricky-to-fix concurrency issues. I am almost sure that a "singleton per user" may be implemented with multi-tenant Spring support. Try that way.