Circular Dependency Injection

Hello,

New to ColdBox coming over from the no-XML FuseBox 5.5. I’m running ColdBox 3.0.0 M5 on ColdFusion 8.

I have two services which have dependency on other, but whenever I access a handler that is injecting both the application seems to lock indefinitely until it times out or runs out of memory. I can confirm this is the issue by removing either or both of the services.

I’m using Lightwire for IOC and here are the twp services declared in my BeanConfig.cfc:

addTransient(“components.PickService”);
addConstructorProperty(“PickService”, “datasource”, local.datasource);
addTransient(“components.PoolService”);
addConstructorProperty(“PoolService”, “datasource”, local.datasource);

In my Service objects here are the snippets as to how they’re injected at the top:

And:

I know I’m probably do something fundamentally wrong with my injection but not sure what. I tried adding setter properties and taking out the cfproperties but it seemed to have the same result. I looked through the docs and searched the forums but didn’t find anything. Any help is much appreciated.

Thanks,

William

Do you need PoolService in PickService and PickService in PoolService?

This is not really a good design in my eyes.

Hey Andrew,

Thanks for your response. There are a couple of methods I’m using, and I also have my application constants inited within each service such that I can have something like:

Variables.pickService.constants.PICK_TYPE_OPEN_ID, Variables.poolService.constants.POOL_FORMAT_PICK_ID, etc.

I’m in that place kind of halfway between OO programming. I could always pass the constants separately and try and move around the methods but I thought it was possible and trying to solve it.

  • William

Personally I think that if you need this circular reference then the two should be migrated into one.

You can't have circular dependencies with constructor injection. You
need to change to setter injection. Not sure if Lightwire supports
that. ColdSpring certainly does. This is a pretty standard DI problem
and it's why most Spring (and ColdSpring) advocates say "favor setter
injection over constructor injection".

I disagree. I don't think it's inherently poor design. It may or may
not be in William's specific case but we'd need to know a lot more
about his code to make that call.

In any reasonably complex app, I tend to find circular dependencies
crop up all the time, without there being any design problem.

Initially I *did* support circular constructor injection, but to do so required me to do a hack which would break applications in certain normal use cases (basically I created all the objects in a given dependency tree, then worked back through to call the init() method on them). I decided before even releasing LightWire that it wasn't an acceptable hack as people reasonably expect the constructor to be called on instantiation of an object, so I went back to the default approach which means that circular constructor injection isn't supported.

Side note, I have mixed feelings about favoring setter over constructor injection. I think there is a story to say that you should use constructor injection when you can because it does a cleaner job of defining your interface (after all, if your object needs FooService, conceptually it should be part of the constructor method signature rather than some random setter called setFooService() which might suggest that anyone can and should call setFooService() at any time). Martin Fowler had something to say about this a while back on his wiki. That said, the downside with that approach is that you can easily end up with circular dependencies and then have to change things out. So I understand favoring setter injection and it has a good practical rationale, but while I think it's a reasonable and pragmatic design choice, we really should feel a *little* dirty every time we do it!

Do consider ordering your methods in your CFC's something like constructor, then DI setters, then public methods, then private methods. Doing that (especially if you put a comment for each section) will make your setter design choice more transparent and will distinguish your DI setters from actual setters which is particularly important if you're DI'ing into transients which are more likely than singletons to actually have business setters.

Best Wishes,
Peter

Sean and Peter,

Thanks much for the response. I agree based on what I’ve read setter injection is the way to go. It’s actually what I’ve been trying to do after my initial constructor approach but so far I haven’t been able to get it to work without hanging. I must be doing something wrong.

In my LightWire BeanConfig here’s what I have for those two services:

// Pick Service
addTransient(“components.PickService”);
addConstructorProperty(“PickService”, “datasource”, local.datasource);
// Pool Service
addTransient(“components.PoolService”);
addConstructorProperty(“PoolService”, “datasource”, local.datasource);
// Add setter dependencies for circular
addSetterDependency(“PoolService”,“PickService”);
addSetterDependency(“PickService”,“PoolService”);

Then in each service I have a respective setter method - setPickService() in pool and setPoolService() in pick.

They’re both injected into one of my ColdBox handlers as such:

The problem is when I call ANY event in the handler the system spins for a long time before eventually giving me a JRun 500 error - coldfusion.runtime.EventHandlerException: Event handler exception.

Subsequent calls tend to usually timeout or produce Java heap memory errors. If I take out either of the service injections in the handlers above the methods all run fine. What am I doing wrong in my approach?

Thank you to all you guys for taking time out to help this newb.

  • William
    // Pick Service
    addTransient\("components\.PickService"\);
    addConstructorProperty\("PickService", "datasource",

local.datasource);
// Pool Service
addTransient("components.PoolService");
addConstructorProperty("PoolService", "datasource",
local.datasource);

But at this point you're already using constructor injection (and
you're hosed because of the circular dependency).

    // Add setter dependencies for circular
    addSetterDependency\("PoolService","PickService"\);
    addSetterDependency\("PickService","PoolService"\);

You need to use these *instead* of the constructor properties.

Then in each service I have a respective setter method - setPickService() in
pool and setPoolService() in pick.

Correct.

The problem is when I call ANY event in the handler the system spins for a
long time before eventually giving me a JRun 500 error -
coldfusion.runtime.EventHandlerException: Event handler exception.

See above - you haven't removed the constructor injection.

But at this point you're already using constructor injection (and
you're hosed because of the circular dependency).

I disagree in this specific scenario. The only args to his constructor is his DAO which assumedly doesn't depend on either the PickService or the PoolService. Lightwire is capable of inserting depencies into the same object using constructor AND setter injection at the same time.

// Add setter dependencies for circular
addSetterDependency("PoolService","PickService");
addSetterDependency("PickService","PoolService");

You need to use these *instead* of the constructor properties.

This won't work (in LightWire) regardless of whether he is passing in the DAO to the constructor or not as a constructor dependancy. AFAIK, you simply cannot define a circular relationship like this. The only hack that I know of that will work is to "lazy load" one of the dependancies yourself within the CFC the first time it is needed. This obviously will ONLY work if you do not need that dependancy in the init! For instance, wire the PickService into the PoolService. Constructor, or setter-- doesn't matter. Then in the PoolService, replace all references to variables.PickService with a new method called getPickService. Put a reference to your beanFactory in PoolService, and the first time getPickService is called, ask the beanFactory for it and set it at that point. Or simply ask the beanFactory directly every time. Once again, this will only work if the init does not use the dependancy.

~Brad

Initially I *did* support circular constructor injection, but to do so required me to do a hack which would break applications in certain normal use cases

Interesting to know Peter. I had wondered about this before. Could you give an example of an app which will not work if the init is not called right away?

How does ColdSpring handle this? I would have assumed the only way to solve it would be to create all the objects and delay their init(). Would it help if you used an onDIComplete method like ColdBox's beanFactory does?

Actually, the more I think about it, it does still seem that you can create an impossible situation where Service A and Service B's respective inits have to be called before either object can exist in a usable state, Service A's init is dependant upon a method in Service B, and Service B's init is dependant upon a method in Service A. In this case, even if you created both objects up front, you couldn't call either init because the opposing object would never be ready. Perhaps that would just be poor design...

~Brad

I disagree in this specific scenario. The only args to his constructor is
his DAO which assumedly doesn't depend on either the PickService or the
PoolService.

Ah, you're right - I misread the dependencies (I'm not used to working
with Lightwire).

This won't work (in LightWire) regardless of whether he is passing in the
DAO to the constructor or not as a constructor dependancy. AFAIK, you
simply cannot define a circular relationship like this.

I'm used to ColdSpring where this works just fine. Good to know that
Lightwire doesn't support circular dependencies.

No. ColdSpring doesn't allow circular dependencies on constructor
injection but it allows circular dependencies on setter injection.
ColdSpring creates the objects and initializes them (after resolving
the constructor arguments) and then ColdSpring moves on to setter
injection, injecting the already created and initialized objects.

Constructor injection is fine for dependencies on lower-level beans,
e.g., DAOs depending on an ORM and, in general, for services depending
on DAOs. Setter injection is necessary for DAOs that depend on other
DAOs or services that depend on other services.

I see. So basically my Service A/Service B scenario would essentially be disallowed, since any dependancies neccessary to init the object HAVE to be a constructor dependancy, and circular dependancies are not allowed to exist there.

Thanks!

~Brad

So to clarify, it sounds like the only two solutions presented so far are:

  • Change the architecture to remove the circular dependency
  • Change Lightwire to ColdSpring

I was avoiding ColdSpring due to the XML config and because anecdotally it seems LW is a lot faster than CS because of it’s lightweight nature (e.g. http://www.mikechandler.com/post.cfm/lightwire-versus-coldspring).

If anyone else notices I’ve configured ColdBox or LightWire incorrectly please let me know. Brad was right about the only constructor dependency being the datasouce which is independent of any of the services.

Thanks,

William

I'm not sure of the context for this remark: "since any dependancies
neccessary to init the object HAVE to be a constructor dependancy".

I have circular dependencies between my services all the time and it's
fine because I use setter injection in ColdSpring.

LW supports setter injection - as I understand it from other posts in
this thread. What is forcing you to use constructor injection?

I'm not sure of the context for this remark: "since any dependancies
neccessary to init the object HAVE to be a constructor dependancy".

The context was your statement on the 8th:

"ColdSpring creates the objects and initializes them (after resolving
the constructor arguments) and then ColdSpring moves on to setter
injection, injecting the already created and initialized objects."

Basically, any dependancies you plan on using in the init method of your component must come from a constructor argument since the init is run prior to the setter injection. It kind of goes without saying, but I was simply re-affirming it since it answered my earlier question.

I have circular dependencies between my services all the time and it's
fine because I use setter injection in ColdSpring.

Yes, we've established that. I also have circular dependancies between many of my services. I use ColdBox's built-in autowiring which is essentially mixin injection which is tantamount to setter injection without bothering to write the setters.

LW supports setter injection - as I understand it from other posts in
this thread. What is forcing you to use constructor injection?

Nothing is forcing me to use setter injection. In fact, I'm not even using Lightwire. I am not the OP, and FWIW, the OP has stated that he doesn't need constructor injection either. The OP's problem with LW is that it is not smart enough to recognize a circular dependacy. It simply recurses through your components and injects as it creates. If it encountersa a circular dependancy, it recursivley attempts to satisfy it until the thread runs out of heap space.

~Brad

Ah, OK. That wasn't clear by that point in the thread. As my wife
sometimes says "We're in violent agreement!" :slight_smile: :slight_smile:

You've said this twice now, and it is simply not true. LightWire support circular setter dependencies, and I'd need to check the version in CB, but I'm pretty sure I wrote something that would catch circular constructor dependencies and throw an error (although I'll admit it's been a while since I reviewed the code).

Best Wishes,
Peter

As my wife
sometimes says "We're in violent agreement!" :slight_smile: :slight_smile:

heh-- I like that one.