Note that there are some explanatory texts on larger screens.

plurals
  1. PO
    primarykey
    data
    text
    <p>This solution will work, but I'd like to propose a slightly different one to you.</p> <p>Specifically, since you're going to traverse a deep object structure, this really looks like a job for the Visitor pattern. Also, what you're describing seems to call out for a two-stage injector: a "bootstrap" stage that can inject stuff needed by the pivot-created hierarchy (but can't inject any pivot-created elements) and a second stage that is the real injector used by your app (that can inject anything).</p> <p>What I would suggest is this basic pattern: make a visitor that traverses the hierarchy and, as it goes, it does injection on those things that need it and records those things that need to be injected elsewhere. Then, when it is done visitng everything, it uses <code>Injector.createChildInjector</code> to make a new <code>Injector</code> that can inject stuff from the original <code>Injector</code> and stuff from the pivot-created hierarchy.</p> <p>First define a visitor that can hit everything in this hierarchy:</p> <pre><code>public interface InjectionVisitor { void needsInjection(Object obj); &lt;T&gt; void makeInjectable(Key&lt;T&gt; key, T instance); } </code></pre> <p>Then define an interface for all your pivot-created elements:</p> <pre><code>public interface InjectionVisitable { void acceptInjectionVisitor(InjectionVisitor visitor); } </code></pre> <p>You'd implement this interface in your pivot-created classes as (assuming this code in the <code>FooContainer</code> class):</p> <pre><code>public void acceptInjectionVisitor(InjectionVisitor visitor) { visitor.needsInjection(this); visitor.makeInjectable(Key.get(FooContainer.class), this); for (InjectionVisitable child : children) { child.acceptInjectionVisitor(visitor); } } </code></pre> <p>Note that the first two statements are optional - it may be that some objects in the pivot hierarchy don't need injection and it could also be that some of them you wouldn't want to have injectable later. Also, notice the use of <code>Key</code> - this means that if you want some class to be injectable with a particular annotation you can do something like:</p> <pre><code>visitor.makeInjectable(Key.get(Foo.class, Names.named(this.getName())), this); </code></pre> <p>Now, how do you implement <code>InjectionVisitor</code>? Here's how:</p> <pre><code>public class InjectionVisitorImpl implements InjectionVisitor { private static class BindRecord&lt;T&gt; { Key&lt;T&gt; key; T value; } private final List&lt;BindRecord&lt;?&gt;&gt; bindings = new ArrayList&lt;BindRecord&lt;?&gt;&gt;(); private final Injector injector; public InjectionVisitorImpl(Injector injector) { this.injector = injector; } public void needsInjection(Object obj) { injector.injectMemebers(obj); } public &lt;T&gt; void makeInjectable(Key&lt;T&gt; key, T instance) { BindRecord&lt;T&gt; record = new BindRecord&lt;T&gt;(); record.key = key; record.value = instance; bindings.add(record); } public Injector createFullInjector(final Module otherModules...) { return injector.createChildInjector(new AbstractModule() { protected void configure() { for (Module m : otherModules) { install(m); } for (BindRecord&lt;?&gt; record : bindings) { handleBinding(record); } } private &lt;T&gt; handleBinding(BindRecord&lt;T&gt; record) { bind(record.key).toInstance(record.value); } }); } } </code></pre> <p>You then use this in your <code>main</code> method as:</p> <pre><code>PivotHierarchyTopElement top = ...; // whatever you need to do to make that Injector firstStageInjector = Guice.createInjector( // here put all the modules needed to define bindings for stuff injected into the // pivot hierarchy. However, don't put anything for stuff that needs pivot // created things injected into it. ); InjectionVisitorImpl visitor = new InjectionVisitorImpl(firstStageInjector); top.acceptInjectionVisitor(visitor); Injector fullInjector = visitor.createFullInjector( // here put all your other modules, including stuff that needs pivot-created things // injected into it. ); RealMainClass realMain = fullInjector.getInstance(RealMainClass.class); realMain.doWhatever(); </code></pre> <p>Note that the way <code>createChildInjector</code> works ensures that if you have any <code>@Singleton</code> things bound in the stuff injected into the pivot hierarchy, you'll get the same instances injected by your real injector - the <code>fullInjector</code> will delegate injectoion to the <code>firstStageInjector</code> so long as the <code>firstStageInjector</code> is able to handle the injection.</p> <p>Edited to add: An interesting extension of this (if you want to delve into deep Guice magic) is to modify <code>InjectionImpl</code> so that it records the place in your source code that called <code>makeInjectable</code>. This then lets you get better error messages out of Guice when your code accidentally tells the visitor about two different things bound to the same key. To do this, you'd want to add a <code>StackTraceElement</code> to <code>BindRecord</code>, record the result of <code>new RuntimeException().getStackTrace()[1]</code> inside the method <code>makeInjectable</code>, and then change <code>handleBinding</code> to:</p> <pre><code>private &lt;T&gt; handleBinding(BindRecord&lt;T&gt; record) { binder().withSource(record.stackTraceElem).bind(record.key).toInstance(record.value); } </code></pre>
    singulars
    1. This table or related slice is empty.
    plurals
    1. This table or related slice is empty.
    1. This table or related slice is empty.
    1. This table or related slice is empty.
    1. VO
      singulars
      1. This table or related slice is empty.
    2. VO
      singulars
      1. This table or related slice is empty.
    3. VO
      singulars
      1. This table or related slice is empty.
 

Querying!

 
Guidance

SQuiL has stopped working due to an internal error.

If you are curious you may find further information in the browser console, which is accessible through the devtools (F12).

Reload