Page 2 of 3 FirstFirst 123 LastLast
Results 11 to 20 of 26

Thread: Injecting non-spring managed objects

  1. #11
    Join Date
    Aug 2006
    Location
    Now Germany, previously Ukraine
    Posts
    1,546

    Default

    Quote Originally Posted by jwisard View Post
    Its not the NewService that needs the UserContext. Its a dependency of NewService, specifically UserService.
    As far as I understand at the point of creation you anyway know that you are creating UserService, right? Otherwise you would not wish to pass userContext parameter to the getBean(). So you anyway depend on knowledge that your NewService is UserService at this point. So it is relatevely harmless (and, from my point of view, even desirable) to express this dependency explicitly.

    Code:
    userContext = ...;
    NewService service = (NewService)ctx.getBean("newService");
    (UserService)service.setUserContext( userContext);
    Definitely, you may generalize to some extent, if needed by defining some Context and ContextDependentService interfaces, inheriting UserContext and UserService (along with another pairs) from them, and writing

    Code:
    userContext = ...;
    NewService service = (NewService)ctx.getBean("newService");
    (ContextDependentService)service.setContext( userContext);
    (it is assumed that in the UserService setContext delegates to setUserContext).

    Regards,
    Oleksandr

  2. #12
    Join Date
    May 2005
    Location
    Columbus, OH
    Posts
    12

    Default

    Oleksandr,

    Yes, I know that what you are suggesting is possible. That is, I know that I can manually, within my own java code, set the userContext on the UserService. However, in a complex system with multiple dependencies (and each dependency has its own dependencies) this becomes unwieldy and frankly defeats the purpose of Spring. Spring can construct my objects with its dependencies without my Java code knowing what those dependencies are. My Java code is then decoupled from the spider-web of interdependencies and can concentrate solely on the one thing that it needs to do.

    Also, what about the case where my service has multiple dependencies, and each of those dependencies requires a userContext? My service itself does not need a UserContext...but many of its dependencies do. Why should my service then care about a UserContext. It only should care about a UserService (in this case). The fact that UserService requires a UserContext is irrelevant to what my service (NewService) does. If NewService is required to pass a UserContext to its dependencies, then NewService has received dependencies from Spring that are incomplete. That is, those dependencies require a UserContext but have not been given one by Spring and therefore are not 'ready for use' by NewService when they are given to NewService. So the dependency graph is split. Some of it is handled by Spring, some by Java code outside of Spring. To me, this is ugly and error prone.

    The other issue here is that I'm dealing with legacy code that should not need to be changed solely for the purpose of my new service. Currently, the way that a UserService gets its UserContext is via the UserServiceFactory.createUserService() method. That cannot change.

    I think there may be merit in what Andreas was presenting. I need a way to introduce a non-Spring created object (UserContext) into Spring so that Spring can inject it into any Spring-created objects that have it as a dependency. In my particular case, I need to be able to do this each time I ask for a Spring bean.
    Last edited by jwisard; Mar 28th, 2007 at 12:05 PM.

  3. #13
    Join Date
    Aug 2006
    Location
    Now Germany, previously Ukraine
    Posts
    1,546

    Default

    Yes, it is possible and it does not introduce any more couplein. (comparing to hipotetical getBean(name, parameters). It rather express that coupling in more straitforward manner - which is good.

    Concerning Andreas proposition - I have not analyzed it in depth yet, so would not comment.

    Concerning splitted dependency graph - this split reflects real-world split between stqtic and dynamic configuration, so it has its positive side.

    By the way I do not completely understand relationship between your NewService and UserService (and other services) - would you like to explain it?

    BTW, I just has noticed your remark about cleaning ThreadLocal - but they need not to be cleaned by you. Garbage collector would clean instances for specific thread somewhen after death of that thread - without your intervention (ThreadLocal uses WeakReferences just for this purpose).

    Regards,
    Oleksandr

  4. #14
    Join Date
    Aug 2004
    Posts
    2,715

    Default

    Quote Originally Posted by jwisard View Post
    Andreas,

    Interesting, indeed. Could you explain this idea a bit?
    I think it's best demonstrated by some example code:

    Consider this class, implementing an interface:

    Code:
    public class TestClass implements Foo {
    
      private String str = "?";
      
      private String id = "?";
      
      public TestClass(String id) {
        this.id = id;
      }
    
      public void foo() {
        System.out.println(this.id + "/" + this.str + ": foo1");
      }
    
      public void setStr(String str) {
        this.str = str;
      }
    }
    
    public interface Foo {
    
      void foo();
    }
    Let's assume the id to be passed to the constructor is known only at runtime. So we need a LifecycleHandler for initialization:

    Code:
    public class TestLifecycleHandler implements LifecycleHandler {
    
      public TestLifecycleHandler() {}
    
      public Object initialize(Map parameterMap) {
        String id = (String) parameterMap.get("id");
        return new TestClass(id);
      }
    
      public void uninitialize(Object obj) {}
    
      public void postInitialize(Object obj, Map parameterMap) {}
    }
    In the application context it looks like that:
    Code:
      <bean id="testTarget" class="...TestClass" abstract="true">
        <property name="str" value="xyz"/>
      </bean>
    
      <bean id="test" class="...ExternalLifecycleFactoryBean">
        <property name="proxyInterfaces">
          <list>
            <value>...Foo</value>
          </list>
        </property>
    
        <property name="targetName" value="testTarget"/>
    
        <property name="lifecycleHandler">
          <bean class="...TestLifecycleHandler"/>
        </property>  
      </bean>
    Note that "testTarget" is declared abstract as it is not intended to be instantiated directly (indeed the constructor argument would be missing). Note also that wiring could take place as indicated with the "str" property.

    And it is used like this:
    Code:
    public class LcTest {
    
      private static Logger logger = Logger.getLogger(LcTest.class);
      
      public static void main(String[] args) {
        try {
          Logger.getRootLogger().setLevel(Level.INFO);
          
          ClassPathXmlApplicationContext ctx = new ClassPathXmlApplicationContext("appContext.xml");
    
          Foo o = (Foo) ctx.getBean("test");
          Map p1 = new HashMap();
          p1.put("id", "1");
          LifecycleHelper.initialize(o, p1);
          
          o.foo();
    
        } catch (Throwable t) {
          logger.error("ERROR", t);
        }
      }
    
    }
    Note that I do no uninitialization as that is not necessary in this case (but possible if required)

    Just complete the package names and it should work.

    Quote Originally Posted by jwisard View Post
    Also, would this work with objects that do not implement an interface? That is, my UserContext object does not implement an interface. Can a proxy be created for a concrete class? (via CGLIB, or something)
    Unfortunately, the current implementation requires interface-based proxies. But instead of proxying UserContext you might consider proxying UserService, passing in the UserContext at runtime.

    Hope that helps,
    Andreas

    P.S.: If you like this feature, feel free to vote for it on Jira
    Last edited by Andreas Senft; Mar 29th, 2007 at 01:19 AM.

  5. #15
    Join Date
    May 2005
    Location
    Columbus, OH
    Posts
    12

    Default

    Thanks very much, Andreas. My vote is in.

  6. #16
    Join Date
    Jun 2005
    Posts
    4,230

    Default

    How is the SPR-1996 proposal different from a custom scope (at least in relation to this post, but generally as well if you want to discuss it)?

  7. #17
    Join Date
    Aug 2004
    Posts
    2,715

    Default

    First to say, when I made that proposal the new scoping features have not been available yet. So this has not been intended to be an alternative.

    The main idea of SPR-1996 is, to programmatically initialize beans at runtime while retaining the possibility of wiring them in the application context. One point was to facilitate access to Stateful Session Beans which requires initialization and deinitialization.

    In the example of SFSB there is no defined scope, as initialization/ uninitialization is programmatically defined. So I do not think that SPR-1996 could be entirely reproduced using custom scopes. Though if you have ideas in that direction I would be interested to hear them.

    Regards,
    Andreas

  8. #18
    Join Date
    Aug 2004
    Posts
    2,715

    Default

    Adding to the general stuff I said above: As I have not used scoping yet (besides prototypes and singletons) and only skimmed the according docs: How would delayed construction of an instance based on runtime arguments be realized using a custom scope?

  9. #19
    Join Date
    Jun 2005
    Posts
    4,230

    Default

    Something like this?

    Code:
    <bean id="userService">
        <constructor-arg>
            <bean class="UserContext" scope="custom" factory-method="instance">
                  <aop:scoped-proxy/>
            </bean>
        </constructor-arg>
    </bean>
    Scope could maybe even be "request" in this case.

  10. #20
    Join Date
    Aug 2004
    Posts
    2,715

    Default

    Looks good. Just passing in of the runtime arguments still has to be done somewhere. I think that is the main difference of my proposal, easier parameter passing.

    Regards,
    Andreas

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •