Inyección de dependencia: determinación del alcance por región (Guice, primavera, lo que sea)

Aquí hay una versión simplificada de mis necesidades.

Tengo un programa donde cada objeto B tiene su propio objeto C y D, inyectado a través de Guice. Además, se inyecta un objeto A en cada objeto C y D.

Lo que quiero: que para cada objeto B, sus objetos C y D serán inyectados con el mismo objeto A.

[Editar-Inicio]

(1) Guice admite los modos "singleton" y "prototipo". Sin embargo, lo que necesito es algo intermedio: necesito que A sea WRT singleton para un objeto B dado (de modo que la C y D inyectadas en un objeto B compartan un objeto A). Para otro objeto B, quiero otro A. Entonces es un singleton pero para un alcance limitado del programa (en realidad, un alcance limitado de una estructura de datos).

(2) No me importa una solución que use el método (setter) - o inyección de campo.

(3) Intenté, varias veces, lograr esto y siempre sentí que solo necesitaba implementar algo personalizado del contenedor DI para que esto funcionara, pero nunca funcionó. Por lo tanto, estoy buscando una solución detallada (no solo "agitar la mano")

[Editar-Fin]

Específicamente, quiero que la salida del programa (a continuación) sea:

Created C0 with [A0]
Created D0 with [A0]
Created B0 with [C0, D0]
Created C1 with [A1]
Created D1 with [A1]
Created B1 with [C1, D1]

Donde actualmente produce el siguiente resultado:

Created C0 with [A0]
Created D0 with [A1]  <-- Should be A0
Created B0 with [C0, D0]
Created C1 with [A2]  <-- Should be A1
Created D1 with [A3]  <-- Should be A1
Created B1 with [C1, D1]

Espero que los contenedores DI permitan este tipo de personalización, pero hasta ahora no tuve suerte en encontrar una solución. A continuación se muestra mi código basado en Guice, pero una solución basada en Spring (u otra basada en contenedores DI) es bienvenida.

  import java.util.Arrays;
  import com.google.inject.*;

  public class Main {

    public static class Super {
      private static Map<Class<?>,Integer> map = new HashMap<Class<?>,Integer>();

      private Integer value;

      public Super(Object... args) {
        value = map.get(getClass());
        value = value == null ? 0 : ++value;
        map.put(getClass(), value);

        if(args.length > 0)
          System.out.println("Created " + this + " with " + Arrays.toString(args));
      }

      @Override
      public final String toString() {
        return "" + getClass().getSimpleName().charAt(0) + value;
      }
    }

    public interface A { }  
    public static class AImpl extends Super implements A  { } 

    public interface B { }  
    public static class BImpl extends Super implements B {
      @Inject public BImpl(C c, D d) { super(c,d); }
    }

    public interface C { }  
    public static class CImpl extends Super implements C  {
      @Inject public CImpl(A a) { super(a); }
    }

    public interface D { }  
    public static class DImpl extends Super implements D {
      @Inject public DImpl(A a) { super(a); }
    }


    public static class MyModule extends AbstractModule {
      @Override
      protected void configure() {
        bind(A.class).to(AImpl.class);
        bind(B.class).to(BImpl.class);      
        bind(C.class).to(CImpl.class);      
        bind(D.class).to(DImpl.class);      
      }    
    }

    public static void main(String[] args) {
      Injector inj = Guice.createInjector(new MyModule());
      inj.getInstance(B.class);    
      inj.getInstance(B.class);    
    }  
  }

Respuestas a la pregunta(7)

Su respuesta a la pregunta