Announcement Announcement Module
Collapse
No announcement yet.
IoC when dependencies are not constants Page Title Module
Move Remove Collapse
X
Conversation Detail Module
Collapse
  • Filter
  • Time
  • Show
Clear All
new posts

  • IoC when dependencies are not constants

    I'm new in spring world and I don't grasp some concepts. I realize the utility of the container when I need to manage beans built from constant values or other constant-based collaborators as dependencies, but what about those cases when dependencies are objects known just at execution time. I'll describe some examples what I mean:

    Case 1. Bean A is a prototype bean requiring a java.util.Date to work. I need to tell BeanFactory that each time I invoke getBean("A") a fresh A instance be created with the current date as a dependency. How can I specify this?

    Case 2. Bean B has a writer property as a dependency. I want to inject an standard io writer as a value (the container should do B.setWriter (new PrintWriter (System.out)); ). How can tell that to the container?

    Case 3. I have 3 beans ADAO, BDAO, and CDAO having each one a diferente (prototype) Logger instance as a dependeny. The logger requires as a constructor argument the java.lang.Class object of the bean where it will live. Thus, for ADAO the container might inject ADAO.setLogger (new Logger (ADAO.class); ). How can I tell that to the container?

    Thanks.
    Best Regards Javier Velez
    Spain (Madrid).

  • #2
    1) Using IOC doesn't mean that you NEVER use 'new'.

    In your case, it is not antithetical to IoC programming to have your prototype bean construct an internal data object each time it is constructed. This date object doesn't need to be injected by spring.

    However, if the data object needs to be shared amongst other beans or is a runtime property, it would be a good candidate to have the container construct and inject anywhere it's needed. This doesn't look to be the case.

    2) As for your writer...you'd construct a writer bean (that is wrapped around system.out) and inject that into bean B.

    3) Logger (and logging in general) is not something that you have to manage with spring.

    It is common enough to just declare your logger (e.g. log4j, slf4j) as a static final in your class:

    Code:
    private static final Logger LOG = LoggerFactory.getLogger(MyClass.class);
    Again, see first comment in (1) above.

    Comment


    • #3
      Dear chudak,

      Thanks for your soon answer, but I think that we have a misundertanding

      In Case 1, I didn't said that the date of bean A must be ALWAYS the current date. The bean A requires a java.util.Date to work but, in my particular use case, I need to inject, as the value of that property, the system current Date. I am not allowed to change the design to adapt to a particular scenario.

      In case 2, the question is not about how can I wrap a java.io.OutputStream with a java.io.writer but how can I inject a "System object" like System.out using the xml syntax in the config file. That is, which is the syntax to do that.

      In case 3, as aforementioned in case 1, I think the design of a software artifact cannot depends on a particular use of it. As you know this is a design principle to achieve reusability. In that sense, in spring literature, it is hightlighted that the use of this framework does not force us to change the architecture of the application. In my case, the architecture states that the logger is a dependency that must be injected in the DAO and it has a dependency of type java.langClass that must corresponds to the DAO class. My answer is, it is possible to specify this using the xml syntax?

      I like a lot your sentence "Using IOC doesn't mean that you NEVER use new" because I just did think so. I did think that, using IoC frameworks, the construction logic for "all" beans in the bussiness tier can be delegated to the container. But - it seems that - the container only is able to create beans in those cases where the construction logic depends of literal values (<value>) or another collaborator built inthe same way (<ref>). I am right?

      Thaks a lot

      Comment


      • #4
        Since you are injecting Objects and not primitives, you should define a bean for each of the cases (IE a Date bean with scope prototype for case a) and then use <ref/> to pass them your other beans or use inner beans declarations.
        Take a look Here

        Case two is not doable. You cant use Java syntax in an XML configuration file. The only way to do that would be create a wrapper for System.out and use that instead.

        Comment


        • #5
          Ok Amiag,

          Thanks for your answer. I think, thaks to you, I now better understand the boundaries of the Spring core framework.

          Nevertheless, for future versions, I would like to give an idea. How about creating a fresh xml tag <creation-logic> to express code implementing the creation for a property in most complex cases?

          <bean id='myBean'
          class='com.foo.bar.MyBean'>
          ...
          <property name='writer'>
          <creation-logic language='groovy'>
          writer = new PrintWriter (Sytem.out)
          return writer
          </creation-logic>
          </property>
          </bean>

          <bean id='logger'
          class='com.foo.bar.Logger'
          prototype='true'>
          ...
          <constructor-arg> <!-- A Class instance is required in constructor-->
          <creation-logic language='groovy'>
          return targetBean.class
          </creation-logic>
          </constructor-arg>
          </bean>

          As it can be seen, both samples use <creation-logic> tags to implement short creation logics for getting a value for a property or constructor dependency. They wold be expressed in a scripting language (groovy for instance) which wold be evalated in a context with some "spring-managed variables".

          In sample 1, groovy allows us to access the System.out variable that let us to create a new Writer and inject it as a value for "(get/set)writer" dependency. In sample, 2 the spring-managed variable "targetBean" would reference to the target object where the dependency is to be done. This allows us to get the java.lang.Class object ob the target bean where logger is to live.

          I would like to know your opinions. is it a good idea? is it interesting? Recalling the Chudak assertion I think this new ability would reduce the number of times appearing "new" operator in our bussiness code since container would be responsible of all creation logic. am I right?

          Best regards.

          Comment


          • #6
            I am not certain I am following your requirements correctly, but below is my attempt to meet them.

            Case 1:

            The example below will ensure anytime the BeanA is injected it will create a new Date. It is good to note that if BeanC is a singleton and has an execute method on it that prints the date of its dependency BeanA that the Date will remain the same (this is because BeanC is a singleton so it does not keep looking up BeanA from the container).


            Code:
              <bean scope="prototype" class="foo.BeanA">
                <property name="date">
                  <bean class="java.util.Date"/>
                </property>
              </bean>
            Case 2:

            The easiest way to access constants is to use the util namespace. Below is an example that will inject new PrintWriter(System.out) into BeanB.

            Code:
              <bean class="foo.BeanB">
                <property name="writer">
                  <bean class="java.io.PrintWriter">
                    <constructor-arg>
                      <util:constant static-field="java.lang.System.out"/>
                    </constructor-arg>
                  </bean>
                </property>
              </bean>
            Case 3:

            Below is a configuration that will work for your third scenario (I only illustrate 2 of the Dao's though). If this is a bit more verbose than you like, you might look into creating a BeanPostProcessor that injects the Logger for you.

            Code:
              <bean class="foo.ADao">
                <property name="logger">
                  <bean class="foo.Logger">
            	<constructor-arg value="foo.ADao"/>
                  </bean>
                </property>
              </bean>
              <bean class="foo.BDao">
                <property name="logger">
                  <bean class="foo.Logger">
                    <constructor-arg value="foo.BDao"/>
                  </bean>
                </property>
              </bean>
            Lastly you might check out SpEL as it is similar to your creation logic proposal.

            HTH,
            Rob

            Comment

            Working...
            X