Announcement Announcement Module
Collapse
No announcement yet.
Injecting list of objects created by a class method Page Title Module
Move Remove Collapse
X
Conversation Detail Module
Collapse
  • Filter
  • Time
  • Show
Clear All
new posts

  • Injecting list of objects created by a class method

    Hi,
    I know this question may sound silly for some of you but I am new to Spring and I would like to learn the best practices of this powerful framework.

    I have the following class that produce a list of objects:

    Code:
    @Component
    public class DataProducer implements ProducerInterface{
    
        public List<ResultObject> getData(String param) {
                ....
         } 
    }
    and then I have this other class that should use the list of objects :

    Code:
    @Component
    public class DataConsumer implements ConsumerInterface{
        
        
        private  List<ResultObject> data;
    
         @Inject
        public void setData(List<ResultObject> data) {   this.data=data; }
        
         public void consumeData() {
                ....
         } 
    }
    I would like to inject the producer into the consumer but I can't find the best way of doing it. I would like to build something like (ES from a main)

    Code:
    String param = args[0];
    ConsumerInterface consumer = (ConsumerInterface)ctx.getBean(ConsumerInterface.class);
    consumer.consumeData();
    But the way I have shown you doen't work. One of the problems is that I don't know how to pass the String param to the DataProducer.getData(String param) method!
    The second thing is that the Injection does not work and the consumer and the producer are not tied.
    Can someone help?
    thank you.

  • #2
    Can please someone help?
    basically my problem is that I am trying to inject in the consumer class the producer return list. The main problems are as follows:
    1 - Don't really know the best way how to pass the string parameter to the consumer
    2 - I can't find a way to inject a list of objects

    Thank you

    Comment


    • #3
      You can use

      Code:
      @Component("dataProducer")
      public class DataProducer implements ProducerInterface{
      
          public List<ResultObject> getData(String param) {
                  ....
           } 
      }
      
      @Component
      public class DataConsumer implements ConsumerInterface{
          
          private  List<ResultObject> data;
      
          @Value("#{dataProducer.getData('parameter')}")
          public void setData(List<ResultObject> data) {   this.data=data; }
          
           public void consumeData() {
                  ....
           } 
      }
      But as you can see, the wiring is static. I don't thing that Spring provides a dynamic injection. You probably have to write your own factory class for this.

      Comment


      • #4
        That's a good suggestion thank you!
        but unfortunately as you mentioned it is static.. and it is a too code dependent solution... it reduce the written code but it is not a "best practice" if you want.
        As you said the best solution that I found so far is through the use of a factory method, something like:

        Code:
        @Component("dataProducer")
        public class DataProducer implements ProducerInterface{
        
            public List<ResultObject> getData(String param) {
                    ....
             } 
        }
        
        @Component
        public class DataConsumer implements ConsumerInterface{
            
            private  List<ResultObject> data;
        
            @Resource(name = "nonStaticProducerDataFactory")
            public void setData(List<ResultObject> data) {   this.data=data; }
            
             public void consumeData() {
                    ....
             } 
        }
        and in the xml config

        Code:
        <bean id="nonStaticProducerDataFactory"
                factory-bean="dataProducer" 
                factory-method="getData">
                <constructor-arg><value>input param</value></constructor-arg>
            </bean>
        but... is this the best practice?? is there any best way?
        this solution works at the moment, but in the near future I need to built it multi-threading... in the sense that I need many thread producing many data through the producer each with different input parameters for the getData method... and then the same thread will use the consumer to work on it... something like
        Code:
         public List<ResultObject> getData(String param) {
                    if(param.equals("type a")) {
                           .....
                        }
                  else
                      ....
        
             }
        so the singleton approach is still good.
        but... this will not work. Because I cannot tie the input parameter as a static value in the factory constructor xml config. Do you see my point?
        I am really into find the best way and not just a working solution, but I hope that I can find a good trade-off. Unfortunately looks like this thread did get too much attention... or this forum is not really followed.
        Thank you very much for your reply, very much appreciated.

        Comment


        • #5
          >> <bean id="nonStaticProducerDataFactory"
          The bean name does not sound good. You use a factory method, but the product of the method available under the id is a bean of type List. So it should by called like "someList" or something. It is just a bean name, but I want to be sure that you know what is going on with the factory method definition.

          I am not sure what is your goal exactly. Maybe it would be better to describe your particular problem to make the problem less abstract. Spring DI is used to build a configuration to wire application components. It is static by definition.

          If the List<ResultObject> represents a configuration that tells Spring how to assemble its components and getData(String) has just a few options as its parameter, best way is probably to use a factory bean (in xml) for every parameter value as you described it in your post.

          If getData(String) has lots of possible paramter values and its result determines how application behaves at runtime, it is not meant to be configured in Spring and should be part of your application code.
          By the way, how many consumers do you want to have in your app? In the example code in your post, you define the consumer as a singleton bean.

          Comment


          • #6
            Thank you very much for your reply!
            I think that we are finally digging into the issue and I am very happy of this and I really appreciate your help.
            I do understand that there is a need for you to have the full picture and I will try to explain as good as I can, I will try to make it less abstract.
            There is basically a producer class that is connecting to a database to pull some data (a report) based on a particular parameter ( a string representing a month).
            So basically the producer is just a class that execute some queries and build some objects based on the data from the database.
            Here are the classes:

            Code:
            @Component("dataProducer")
            public class DataProducer implements ProducerInterface{
            
                private DataSource dataSource;
            
                @Inject
                 public DataEstractor(DataSource ds) {	
            		this.dataSource = ds;
            	}
            
                public List<ResultObject> getData(String month) {
                        ....
                        // use the the dataSource to get the data from the DB
                 } 
            }
            
            @Component
            public class DataConsumer implements ConsumerInterface{
                
                private  List<ResultObject> data;
            
                @Resource(name = "resultObjectListProducer")
                public void setData(List<ResultObject> data) {   this.data=data; }
                
                 public void consumeData() {
                        ....
                 } 
            }
            xml bean definitions
            Code:
            <bean id="resultObjectListProducer"
                    factory-bean="dataProducer" 
                    factory-method="getData">
                    <constructor-arg><value>input param</value></constructor-arg>
                </bean>
            
            
            	<bean id="dataSource" class="com.jolbox.bonecp.BoneCPDataSource" destroy-method="close">
            		<property name="driverClass" value="com.mysql.jdbc.Driver" />
            	   <property name="jdbcUrl" value="jdbc:mysql://...." />
            	   <property name="username" value="....."/>
            	   <property name="password" value="....."/>
            	   <property name="idleConnectionTestPeriod" value="60"/>
            	   <property name="idleMaxAge" value="240"/>
            	   <property name="maxConnectionsPerPartition" value="30"/>
            	   <property name="minConnectionsPerPartition" value="10"/>
            	   <property name="partitionCount" value="3"/>
            	   <property name="acquireIncrement" value="5"/>
            	   <property name="statementsCacheSize" value="100"/>
            	   <property name="releaseHelperThreads" value="3"/>
            	</bean>
            In my scenario the user specify the months on which to generate the reports. So the solution of the resultObjectListProducer bean (previously called nonStaticProducerDataFactory ) is not feasible because the month is statically hard coded by the <constructor-arg><value>input param</value></constructor-arg> line in the xml config.
            The program must be able to dynamically identify the list of months and use these to create the data through the data producer and present it through the data consumer.
            At the end of the day, what I am trying to archive is something like (for a single thread scenario):

            Code:
            String[] monthList; 
            // get the months list from user
            
            for(String month:monthList) {
            
                  DataConsumer csvReport= (DataConsumer)ctx.getBean(DataConsumer.class);
                  csvReport.consumeData();
            }
            The power of this is that spring take care of everything. The programmer at this point is not aware of the dataProducer, the dataSource or nothing, and on top of that is that all of the elements are amazingly loosely coupled!! The DataConsumer consumer class is completely not aware of what is a data producer!
            They are just tied in the spring xml config.

            Unfortunately the code I have just shown you it will work if I have just a month to work on... I will just hard code it like
            <constructor-arg><value>"2012-09"</value></constructor-arg>
            And it will work. But if the user needs to specify a list that is every time different, it will not. What is the best way of handling this? I don't want to build a class that link the producer and the consumer... this is not dependency injection!
            what do you advice?
            The next question will be about concurrency and multi threading, but I think it is better to first understand this point.
            As the matter of the fact (to make it simple) let's suppose that a maximum of 5 month can be specified in the list, I would like to start a thread concurrently on each month.

            I hope I have been clear enough, if not please let me know, I will be happy to clarify any points.

            P.S. I would like not to have any class context aware.

            Comment


            • #7
              Do you have any general consumer interface with many implementations that consume different data? If not, the best way is to create void consumeData(List<ResultObject> list) in your Consumer method. The consumer can be a singleton here.

              Another option is to pass arguments into your consumer constructor.
              Code:
              public class DataConsumer {
                  public DataConsumer(List<ResultObject> list) {...}
              }
              
              <bean id="dataConsumer class="DataConsumer" scope="prototype" />
              
              for(String month:monthList) {
                    DataConsumer csvReport= (DataConsumer) ctx.getBean("dataConsumer", producer.getData(month));
                    csvReport.consumeData();
              }
              If you don't want to use ctx.getBean (or encapsulate it), you can create a consumer factory when you create instance of a consumer by hand or do ctx.getBean here (remember that consumer must be of scope prototype here).
              Code:
              public class DataConsumerFactory() {
                  public DataConsumer getConsumer(List<ResultObject> list) {
                      DataConsumer consumer = new DataConsumer(list);
                      // you can autowire beans into factory and pass it to DataConsumer instance here
                      return consumer;
                  }
              }
              
              @Autowire
              DataConsumerFactory consumerFactory;
              
              for(String month:monthList) {
                    DataConsumer csvReport= consumerFactory.getConsumer(producer.getData(month));
                    csvReport.consumeData();
              }
              To process each month in different thread, look at Spring task execution and scheduling here. You will be interested with @Async annotation in combination with <task:annotation-driven /> element in xml.

              Comment


              • #8
                WOW! lot's of stuff here! thank you so much! At a glance the task scheduler and TaskExecutor stuff is extremely interesting! I will dig into it and eventually open another thread if I have problems thank you!
                And thank you so much for the prototype advice! good one!
                Back to the question, actually I do have a general consumer interface with many implementations for the consumer, this is why I have the .consumeData() method.

                The third solution you proposed is the one that I like the most!
                I have changed a little bit like this:

                Code:
                @Component
                public class DataConsumerFactory() {
                
                    @Autowired
                     private ProducerInterface producer;
                
                    public DataConsumer getConsumer(String month) {
                        DataConsumer consumer = new DataConsumer();
                        
                        consumer.setMonth(month);
                        consumer.setData1(producer.getData1(month));
                        consumer.setData2(producer.getData2(month));
                        
                        return consumer;
                    }
                }
                The weak point of this design is that you are not taking the most of the spring magic (in my humble opinion).
                In my opinion these are few weak points:
                1 - I am creating a factory class... usually Spring is the one in charge of handling the instance of all beans... I am actually overstepping its territory. What is your opinion about this? is that a problem or I am just over thinking?
                2 - the factory method (due to the point above) must know the producer class.
                3 - Those above are not that important, the main problem is the following (that you anticipated in your previous reply): I do actually have a general consumer interface with many implementations for the consumer. This design force me to actually create a factory method for each different implementation. The spring magic is that I could just use the @Component annotation and implement the interface to have a report produced in any form I want!

                What is your opinion about this? is there a way to overcome this problem?

                P.S. the solution works, and it is nice. I am just trying to deeply understand the framework and its capabilities to exploit its best practices

                Comment


                • #9
                  What Spring gives you is to configure a bean wiring and maybe some AOP or lifecycle callbacks and do "its magic" to creating the beans according the rules you define. So you can tell, how to create a producer of some type (which you did) and how to create a consumer of some type (for example as a factory, as you did).
                  Providing a consumer with some produced data is something which happens on runtime, so you cannot provide a configuration rule of wiring concrete produced data with a concrete consumer in time when the Spring application context is created (the rules are static in the meaning of all wiring has to be resolved on the context startup). So it is perfectly fine to manage the application flow in your own code.

                  Comment


                  • #10
                    Perfect. Clear.
                    Thank you very much for your support.

                    Comment

                    Working...
                    X