delphispring4d

BeforeResolve/AfterResolve in Spring4D


This is my second attempt to explain what I am trying to accomplish as I did not explain it very well before.

for this Pseudo example if you have the classes to resolve setup as below;

TOuter.constructor.Create(Name: TName; Inner1, Inner2: TInner);
TInner.constructor.Create(Name: TName; Sub: TSub);
TSub.constructor.Create(Name: TName);
TName.constructor.Create(Name: String);
var Names = TStack<String>.Create;

If it is possible to intercept before and after resolve it would be possible to implement a parent, ancestor or, 'i am being constructed for ...'.

for example

RegisterType<TName>(
  function: TName
    begin
      Result:=TName.Create(String.Join('.',Names.List.ToArray));
    end
);

RegisterType<TOuter>;
RegisterType<TInner>;
RegisterType<TSub>;

Resolver.BeforeResolve:=
    procedure(ClassType: TClass)
    begin
      Names.Push(ClassType.ClassName);
    end;

Resolver.AfterResolve:=
    procedure(ClassType: TClass)
    begin
      Names.Pop;
    end;

Resolve<TOuter> would produce

TOuter
    Name = 'Outer'
    Inner1 = 
        Name = 'Outer.Inner'
        Sub
            Name = 'Outer.Inner.Sub'
    Inner2
      Name = 'Outer.Inner'
        Sub 
          Name = 'Outer.Inner.Sub'
            

I did think it might be possible by sub classing the component activator and looking for if classname=TName or if arguments.contains(TName) then ...

For this example I have omitted making the Names stack handle multiple threads and in reality I am not using names but rather having a stack of interfaces each knowing who their 'parent' is but this demonstrates what I am trying to implement.


Solution

  • Instance creation is ultimately done within the component activator. If you create a decorator for that you can solve your requirement.

    I will just write down the code - you can package this into a container extension that does all the stuff automatically (look into the Spring.Container.*Extension units).

    For brevity, I will leave out all standard creation and cleanup code and any code to make this thread-safe.

    type
      TComponentActivatorDecorator = class(TInterfacedObject, IComponentActivator)
      private
        fActivator: IComponentActivator;
      public
        constructor Create(const activator: IComponentActivator);
        function CreateInstance(const context: ICreationContext): TValue;
      end;
    
    type
      TComponentActivatorBaseAccess = class(
        Spring.Container.ComponentActivator.TComponentActivatorBase)
      end;
    
    function TComponentActivatorDecorator.CreateInstance(
      const context: ICreationContext): TValue;
    var
      name: string;
    begin
      // yes, this is a bit ugly because the information about the handled type 
      // is not passed to the method but a protected state of the activator
      // actually this should (and actually is but only internally) be part of
      // the ICreationContext - this might change in the future
      name := TComponentActivatorBaseAccess(TObject(fActivator)).Model.ComponentType.Name;
      Names.Push(name);
      try
        Result := fActivator.CreateInstance(context);
      finally
        Names.Pop
      end;
    end;
    

    After the call to Build you run this code:

      for Model in GlobalContainer.Registry.FindAll() do
      begin
        if Model.ComponentActivator is TReflectionComponentActivator then
          Model.ComponentActivator := TComponentActivatorDecorator.Create(Model.ComponentActivator);
      end;
    

    Now every activator that does the object creation via reflection (so not the ones doing by a delegate) is being decorated and executes the extra push/pop logic and you access that in your TName delegate.

    I declared TName as follows:

    type
      TName = type string;
    

    and its registration:

    GlobalContainer.RegisterType<TName>(
      function: TName
      begin
        Result := string.Join('.',Names.ToArray);
      end);
    

    Here is how a resolved TOuter instance looks now:

    enter image description here