AOP和DI:如何创建一个支持方面的接口依赖注入对象?

时间:2016-06-09 06:40:36

标签: delphi dependency-injection aop spring4d

我想通过Spring / 4D框架创建一个方面感知的接口依赖注入对象。我的问题是,我不知道如何将两个部分结合起来。一般的想法如下:

  1. 创建方面层对象并为其保留两个接口:一个作为依赖项传递给对象(IAspect),另一个作为方面编入对象(IInterceptor ):

    Temp := TAspect.Create;
    Aspect := Temp as IAspect;
    Interceptor := Temp as IInterceptor;
    
  2. 创建接口依赖注入对象:

    Instance := TInstance.Create(Aspect) {as IInstance};
    
  3. 编织方面:

    Result := TProxyGenerator.CreateInterfaceProxyWithTarget(Instance, [Interceptor]);
    
  4. 要解决这个问题,我想在这些方面注册一个带有自定义构造函数的工厂:

    Aspect := Resolve<IAspect>;
    Interceptor := Aspect as IInterceptor;
    Instance := InstanceFactory(Aspect); // InstanceFactory := Resolve<IInstanceFactory>;
    Result := TProxyGenerator.CreateInterfaceProxyWithTarget(Instance, [Interceptor]);
    

    我的问题是,我如何使用Spring的Container: TContainer注册?

    示例:以下程序的行为与我想要的相同,并演示GetValue调用运行的方面层。自定义对象创建在主例程中的$Region中。我如何重构此程序以使用Spring / 4D框架中的DI容器,同时保持方面感知对象的自定义构造?

    program Project1;
    
    {$AppType Console}
    
    {$R *.res}
    
    uses
      System.SysUtils,
      Spring,
      Spring.Interception;
    
    type
      IAspect = interface
        ['{AF8E19F6-176D-490E-A475-4682336CAB89}']
        function GetSetting: String;
        procedure SetSetting(const Value: String);
    
        property Setting: String read GetSetting write SetSetting;
      end;
    
      TAspect = class (TInterfacedObject, IInterceptor, IAspect)
      strict private
        FSetting: String;
    
        function GetSetting: String;
        procedure SetSetting(const Value: String);
    
        procedure Intercept(const Invocation: IInvocation);
    
      public
        constructor Create;
      end;
    
      IThingy = interface (IInvokable)
        function GetAspect: IAspect;
        function GetValue: String;
        procedure SetValue(const Value: String);
    
        property InstanceAspect: IAspect read GetAspect;
        property Value: String read GetValue write SetValue;
      end;
    
      TThingy = class (TInterfacedObject, IThingy)
      strict private
        FInstanceAspect: IAspect;
        FClassAspect: IAspect;
        FValue: String;
    
        function GetAspect: IAspect;
        function GetValue: String;
        procedure SetValue(const Value: String);
    
      public
        constructor Create(const InstanceAspect, ClassAspect: IAspect);
      end;
    
    { TAspect }
    
    constructor TAspect.Create;
    begin
      inherited;
      FSetting := ' intercepted by class aspect';
    end;
    
    function TAspect.GetSetting: String;
    begin
      Result := FSetting;
    end;
    
    procedure TAspect.Intercept(
      const Invocation: IInvocation);
    begin
      Invocation.Proceed;
    
      if Invocation.Method.Name = 'GetValue' then
        Invocation.Result := TValue.From<String>(Invocation.Result.AsString + FSetting);
    end;
    
    procedure TAspect.SetSetting(
      const Value: String);
    begin
      FSetting := Value;
    end;
    
    { TThingy }
    
    constructor TThingy.Create(const InstanceAspect, ClassAspect: IAspect);
    begin
      inherited Create;
      FInstanceAspect := InstanceAspect;
      FClassAspect := ClassAspect;
      FValue := 'Value';
    end;
    
    function TThingy.GetAspect: IAspect;
    begin
      Result := FInstanceAspect;
    end;
    
    function TThingy.GetValue: String;
    begin
      Result := FValue;
    end;
    
    procedure TThingy.SetValue(const Value: String);
    begin
      FValue := Value;
    end;
    
    { Main }
    
    procedure Main;
    var
      Temp: TInterfacedObject;
      ClassAspect: IAspect;
      ClassInterceptor: IInterceptor;
      InstanceAspect: IAspect;
      InstanceInterceptor: IInterceptor;
      Thingy1: IThingy;
      Thingy2: IThingy;
    begin
      {$Region 'How to do this with the Spring DI container?'}
      Temp := TAspect.Create;
      ClassAspect := Temp as IAspect;
      ClassInterceptor := Temp as IInterceptor;
    
      Temp := TAspect.Create;
      InstanceAspect := Temp as IAspect;
      InstanceInterceptor := Temp as IInterceptor;
      Thingy1 := TThingy.Create(InstanceAspect, ClassAspect);
      Thingy1 := TProxyGenerator.CreateInterfaceProxyWithTarget(Thingy1, [ClassInterceptor, InstanceInterceptor]);
    
      Temp := TAspect.Create;
      InstanceAspect := Temp as IAspect;
      InstanceInterceptor := Temp as IInterceptor;
      Thingy2 := TThingy.Create(InstanceAspect, ClassAspect);
      Thingy2 := TProxyGenerator.CreateInterfaceProxyWithTarget(Thingy2, [ClassInterceptor, InstanceInterceptor]);
      {$EndRegion}
    
      Thingy1.InstanceAspect.Setting := ' intercepted by instance aspect 1';
      Thingy2.InstanceAspect.Setting := ' intercepted by instance aspect 2';
    
      Thingy1.Value := 'Value 1';
      Thingy2.Value := 'Value 2';
    
      WriteLn(Format('Thingy1.Value: %s', [Thingy1.Value]));
      WriteLn(Format('Thingy2.Value: %s', [Thingy2.Value]));
    end;
    
    begin
      try
        Main;
      except
        on E: Exception do
          WriteLn(E.ClassName, ': ', E.Message);
      end;
      if DebugHook <> 0 then
      begin
        WriteLn('Press enter...');
        ReadLn;
      end;
    end.
    

    输出:

    Thingy1.Value: Value 1 intercepted by instance aspect 1 intercepted by class aspect
    Thingy2.Value: Value 2 intercepted by instance aspect 2 intercepted by class aspect
    Press enter...
    

1 个答案:

答案 0 :(得分:2)

我不完全确定你到底想要实现什么,但在这里它是如何设置容器以获得你正在寻找的结果。什么不起作用的是上下文注入(在解析过程中根据当前构建的对象图做出决策) - 这是我们计划在未来实现的。

program Project1;

{$AppType Console}

{$R *.res}

uses
  System.SysUtils,
  Spring,
  Spring.Container,
  Spring.Interception;

type
  IThingy = interface (IInvokable)
    ['{FD337CC6-03EB-4384-A027-E993AB687BF0}']
    function GetValue: String;
    procedure SetValue(const Value: String);

    property Value: String read GetValue write SetValue;
  end;

  TThingy = class (TInterfacedObject, IThingy)
  strict private
    FValue: String;

    function GetValue: String;
    procedure SetValue(const Value: String);
  end;

{ TThingy }

function TThingy.GetValue: String;
begin
  Result := FValue;
end;

procedure TThingy.SetValue(const Value: String);
begin
  FValue := Value;
end;

type
  TClassInterceptor = class(TInterfacedObject, IInterceptor)
    procedure Intercept(const Invocation: IInvocation);
  end;

  TInstanceInterceptor = class(TInterfacedObject, IInterceptor)
  private
    class var InstanceCount: Integer;
    var FNo: Integer;
    procedure Intercept(const Invocation: IInvocation);
  public
    constructor Create;
  end;

{ Main }

procedure Main;
var
  Thingy1: IThingy;
  Thingy2: IThingy;
begin
  GlobalContainer.RegisterType<TClassInterceptor,TClassInterceptor>.AsSingleton;
  GlobalContainer.RegisterType<TInstanceInterceptor>('instance');
  GlobalContainer.RegisterType<IThingy, TThingy>.InterceptedBy<TClassInterceptor>.InterceptedBy('instance');
  GlobalContainer.Build;

  Thingy1 := GlobalContainer.Resolve<IThingy>;
  Thingy2 := GlobalContainer.Resolve<IThingy>;

  Thingy1.Value := 'Value 1';
  Thingy2.Value := 'Value 2';

  WriteLn(Format('Thingy1.Value: %s', [Thingy1.Value]));
  WriteLn(Format('Thingy2.Value: %s', [Thingy2.Value]));
end;

procedure TClassInterceptor.Intercept(const Invocation: IInvocation);
begin
  Invocation.Proceed;

  if Invocation.Method.Name = 'GetValue' then
    Invocation.Result := TValue.From<String>(Invocation.Result.AsString + ' intercepted by class aspect');
end;

constructor TInstanceInterceptor.Create;
begin
  Inc(InstanceCount);
  FNo := InstanceCount;
end;

procedure TInstanceInterceptor.Intercept(const Invocation: IInvocation);
begin
  Invocation.Proceed;

  if Invocation.Method.Name = 'GetValue' then
    Invocation.Result := TValue.From<String>(Invocation.Result.AsString + ' intercepted by instance aspect ' + IntToStr(FNo));
end;

begin
  try
    Main;
  except
    on E: Exception do
      WriteLn(E.ClassName, ': ', E.Message);
  end;
  if DebugHook <> 0 then
  begin
    WriteLn('Press enter...');
    ReadLn;
  end;
end.