Announcement Announcement Module
Collapse
No announcement yet.
Injecting runtime constructor-arg properties Page Title Module
Move Remove Collapse
X
Conversation Detail Module
Collapse
  • Filter
  • Time
  • Show
Clear All
new posts

  • Injecting runtime constructor-arg properties

    I was working with a friend on a command pattern he wanted to implement. The commands would be spring-managed (so non-singleton of course) and be constructor-injected. The Commands require runtime information (i.e. params from the request) as part of the constructor. I know it isn't supported, but thought it might actually be doable...so here are my thoughts.

    The getBean could be overloaded with a version that takes a map of runtime parameters...maybe with an additional RuntimeAwareBeanFactory interface (or not). Then the ref tag could have an additonal 'runtime-key' attribute that points to the value in the runtime map. So we could mix and match what properties were read from the runtime map and which ones were refs to other beans etc. It may be necessary to throw some sort of exception (or just log a warning) when a runtime-key is used on a singleton bean.

    So, (say I'm using struts), my action would be container aware (it is itself spring managed), and would setup the runtime map with the necessary data and then call getBean with the map...getting back the constructor-injected, non-singleton command object...wired with runtime and spring-managed properties...ready to execute.

    I'd searched the forum and found some similar needs that this solution (or one like it) could address:
    http://forum.springframework.org/showthread.php?t=18571
    http://forum.springframework.org/showthread.php?t=16900

    Thoughts?

    James
    Last edited by robyn; May 14th, 2006, 07:13 PM.

  • #2
    Personally I don`t like having a single 'import org.springframework' in my objects (only the ones that where design for Spring).

    So.. here is what I would do:

    I would create a CommandFactory that is parametrized in Spring (so it has all the dependencies a command should need). And the CommandFactory returns a Command object based on the inputArguments.

    Example:

    Code:
    interface Command{
          void execute();
    }
    
    class SillyCommand implements Command{
          private int _sillyValue;
         
           SillyCommand(int sillyValue, Object data){
                    _sillyValye = sillyValue;
                    _data = data;
           }
    
          void execute(){
                System.out.println(_sillyValue);
           }
    }
    
    interface CommandFactory{
         Command create(String s, .....);
    }
    
    class DefaultCommandFactory implements CommandFactory{
        private int _sillyValue;
       
        public DefaultCommandFactory(int sillyValue){
             _sillyValue = sillyValue;
        }
        
        Command create(String s, Object arg){
              if("silly".equals(s))
                    return new SillyCommand(_sillyValye,arg);
              ... more checks
             
              throw new IllegalArgumentException("unknown command");
        }
    }
    And you can glue it together in Spring:

    Code:
    <bean id="commandFactory" class="DefaultCommandFactory">
         <constructor-arg value="10"/>
    </bean>
    
    <bean id="anObjectThatNeedsCommands" class="Foo">
         <constructor-arg ref="commandFactory"/>
    </bean>
    [edit]
    And if you would like to create a ConcreteCommandFactory for every Command class if you want and connect them in the the 'GeneralCommandFactory'. But I`m not going to type that example right now

    Comment


    • #3
      >Personally I don`t like having a single 'import org.springframework'
      in my objects (only the ones that where design for Spring).

      I agree.

      I think something similar to what you've described can work. I could have a SomeController object that is injected with a reference to SomeCommandFactory instance (the factory itself, not the prototypes it
      is creating...I guess spring wouldn't even have to know it's a factory). Then, the SomeCommandFactory could be injected with the
      necessary spring managed beans, and then (finally) the createCommand
      (getObject) method would take the runtime parameters, and (internally)
      it would create the actual command object passing in all the necessary
      params (injected by spring and those passed to the function).
      Honestly, until your reply, I hadn't thought of this approach. So at least it is possible to do it without changing anything in spring.

      In the end, I think I'm happy with that. If spring were to support the overloaded getBean, I'd probably have a factory interface and a spring-specific implementation that would delegate to/use that feature anyway. So all it would really buy me is the freedom to change the command implementation class without actually modifying the factory (I'd just change the classname on the spring-runtime-injection-enabled-prototype bean as opposed to changing the factory code to new up a different impl of the command interface).


      Thanks,
      James

      Comment


      • #4
        Originally posted by james.estes
        >Personally I don`t like having a single 'import org.springframework'
        in my objects (only the ones that where design for Spring).

        I agree.

        I think something similar to what you've described can work. I could have a SomeController object that is injected with a reference to SomeCommandFactory instance (the factory itself, not the prototypes it
        is creating..
        Check the 'anObjectThatNeedsCommands' in the last Spring example.

        .I guess spring wouldn't even have to know it's a factory). Then, the SomeCommandFactory could be injected with the
        necessary spring managed beans
        Check the sillyVale in the DefaultCommandFactory

        , and then (finally) the createCommand
        (getObject) method would take the runtime parameters, and (internally)
        it would create the actual command object passing in all the necessary
        params (injected by spring and those passed to the function).
        Check the construction of the SillyCommand.

        So all it would really buy me is the freedom to change the command implementation class without actually modifying the factory (I'd just change the classname on the spring-runtime-injection-enabled-prototype bean as opposed to changing the factory code to new up a different impl of the command interface).
        What it also buys you is something that doesn`t depend on Spring. The only influence Spring has on my code is IOC, but apart from that I don`t let it influence my design. In a production environment I glue it together with Spring, and with a test environment I glue it together by hand (so in Java code). It works great. A good example of IOC would be the concurrency library from JSE 5. It works great in Spring, but it is totally unaware of Spring... and that is how I like my classes.

        [edit]
        Another influence Spring has on my code is the way I deal with some concurrency problems. Two problems I implement in a different way since Spring are:
        1) timed calls (every 3 second this should be called eg)
        2) repeated (multithreaded) calls.. like.. this call should be repeatedly called by a bunch of threads.

        Before Spring (and even in the beginning of my Spring experience) I created a Timer/ThreadPool in the class that needed it. The problem with this approach is that these classes are terrible to test... (and that is an understatement) and they have more dependencies, so it is difficult to use them in a different manner. I finally saw the solution for this problem: I just had to remove the threading/timer stuff and my object only has to provide some method that can be called to do a single piece of work. In my Spring configuration I just wire a Timer (or ScheduledThreadPoolExecutor) at it.. or a Repeater (a concurrency stone that can be compared to a threadpool.. but keeps repeating a single task)..

        Example of the repeater:
        Code:
        	<!-- de parser is verantwoordelijk voor het parsen van pagina`s-->
        	<bean id="parser"
        		  class="anchormen.kaloogaExp.parser.Parser">
        
        		<!-- hier worden fe-->
        		<constructor-arg index="0" ref="fetcherToParserChannel"/>
        
        		<!-- hier worden alle succesvolle geparste pagina`s naar toe gestuurd-->
        		<constructor-arg index="1" ref="parserOutputChannel"/>
        
        		<!-- hier worden alle pagina`s naar toe gestuurd waarbij er problemen zijn opgetreden tijdens het
        			parsen.-->
        		<constructor-arg index="2" ref="deadLetterOutputChannel"/>
        	</bean>
        
        	<bean id="parserRepeater"
        		  class="org.jph.concurrent.repeater.StdRepeaterService"
        		  init-method="start"
        		  destroy-method="shutdown">
        		<!-- number of threads -->
        		<constructor-arg index="0" value="1"/>
        
        		<constructor-arg index="1">
        			<bean class="org.jph.concurrent.StdThreadFactory">
        				<!-- priority -->
        				<constructor-arg index="0" value="3"/>
        				<!-- De naam van ThreadGroup waar de Threads onder vallen -->
        				<constructor-arg index="1" value="parserRepeaters"/>
        			</bean>
        		</constructor-arg>
        
        		<constructor-arg index="2">
        			<bean class="org.jph.spring.scheduling.MethodInvokeRunnable">
        				<constructor-arg>
        					<bean class="org.springframework.util.MethodInvoker">
        						<property name="targetObject" ref="parser"/>
        						<property name="targetMethod" value="processSingleMsg"/>
        					</bean>
        				</constructor-arg>
        			</bean>
        		</constructor-arg>
        	</bean>
        Example of the ScheduledThreadPoolExecutor.
        Code:
        <bean id="workPool"
        		  class="anchormen.kaloogaExp.workpool.StdWorkpool">
        
        		<constructor-arg index="0" ref="workpoolDb"/>
        
        		<!-- die queue die gebruikt wordt om domainworkpool klaar te zetten voor de fetcher-->
        		<constructor-arg index="1">
        			<bean class="java.util.concurrent.LinkedBlockingQueue">
        				<constructor-arg value="550"/>
        			</bean>
        		</constructor-arg>
        
        		<!-- die queue die gebruikt wordt om downloads die weggeschreven moeten worden op te slaan-->
        		<constructor-arg index="2">
        			<bean class="java.util.concurrent.LinkedBlockingQueue">
        				<constructor-arg value="50000"/>
        			</bean>
        		</constructor-arg>
        
        		<!-- seeds -->
        		<constructor-arg index="3">
        			<set>
        				<bean class="anchormen.kaloogaExp.Link">
        					<!-- constructor-arg value="http&#58;//www.dmoz.org"/ -->
        					<constructor-arg value="http&#58;//www.javalobby.org"/>
        				</bean>
        			</set>
        		</constructor-arg>
        
        		<!-- max depth -->
        		<constructor-arg index="4" value="4"/>
        
        		<!-- max number of links from a single site -->
        		<constructor-arg index="5" value="100"/>
        	</bean>
        
        	<bean id="workpoolScheduler"
        		  class="org.jph.spring.scheduling.concurrent.ScheduledThreadPoolExecutorFactoryBean">
        
        		<property name="threadFactory">
        			<bean class="org.jph.concurrent.StdThreadFactory">
        				<!-- priority -->
        				<constructor-arg index="0" value="8"/>
        				<!-- De naam van ThreadGroup waar de Threads onder vallen -->
        				<constructor-arg index="1" value="workpool"/>
        			</bean>
        		</property>
        
        		<property name="corePoolSize" value="1"/>
        
        		<property name="scheduledWithFixedDelayRunnable">
        			<bean class="org.jph.spring.scheduling.concurrent.ScheduledWithFixedDelayRunnable">
        				<property name="runnable">
        					<bean class="org.jph.spring.scheduling.MethodInvokeRunnable">
        						<constructor-arg>
        							<bean class="org.springframework.util.MethodInvoker">
        								<property name="targetObject" ref="workPool"/>
        								<property name="targetMethod" value="process"/>
        							</bean>
        						</constructor-arg>
        					</bean>
        				</property>
        
        				<property name="initialDelay" value="10"/>
        
        				<property name="delay" value="3"/>
        
        				<property name="timeUnit" ref="java.util.concurrent.TimeUnit.SECONDS"/>
        			</bean>
        		</property>
        	</bean>
        ps: execuse my dutch comments :P

        Comment


        • #5
          Yeah...don't know how I missed it. I think the '10' sillyValue constructor arg was throwing me off...thought that was intended to be answering the runtime val problem. I see you had it up there all along... Apparently my subconscious got it though...eventually

          Thanks
          James

          Comment

          Working...
          X