Skip to content

Unity #37

@Henrik-Xu

Description

@Henrik-Xu

基于 AOPUnity 容器的使用

回顾

AOP 本质是在 OOP 基础上,解决项目动态扩展的问题。

使用场景

我们如果把项目的业务,分为功能性业务,和系统级业务两部分的话,功能性业务通常是我们开发某些模块最主要的,

是第一时间要开发完成的。所以,我们对功能性业务的设计是第一位的。但是系统级业务,往往是在功能性业务的基

础上的扩展。这种扩展可以延迟到项目运营后期,也可以在项目功能性业务完成后,独立完成,再集合到一起。

AOP的本质:就是希望在不破坏原有对象的封装前提下,动态的给对象增加额外行为的能力。

实现

  1. 基于装饰器设计模式,对于小项目或者小的功能性业务扩展,还是非常灵活的。
  • 局限性:只能针对某一个功能性业务类实现“装饰”。受制于特定的接口。

  • 试想,如果我们想给不同的对象,扩展相同的系统级业务,如何做到?

  1. 框架的代理
  • 特点:就是通过泛型类的引入,可以对任意需要扩展的类实现动态行为的注入。

  • 好处:可以针对所有的行为,统一切入扩展的行为。

  • 注意:如果你某些行为不想注入,请使用原始的对象就可以了。(一般很少这么做)

  1. 使用第三方的代理框架
  • 特点:简单,和.NET自带的代理框架比较,代码量也少(推荐)

  • 其他:和.NET自带的代理框架几乎是一样。

IOC

概念

Iocinversion of control: 控制反转)又称为“依赖注入”(Dependence injection)(DI

控制反转:在我们的开发使用中,对象的创建权,通常是我们开发者自己完成的。

但是现在我们通过容器来完成对象的创建,也就意味着,我们将对象的创建权做了转移。

开发中创建的容器:UnitySpring.Net...

容器 Unity 使用详解

  1. 基础理解:Unity 是一个轻量级的可扩展的依赖注入容器。使用非常方便,开发者可以选择编码方式,也可以

选择配置文件方式,达到对象依赖关系的的建立。在程序运行中,调用 Unity 容器,可以得到我们需要的对象。

  1. 选择建议:小项目可以使用代码方式,较大项目,建议用配置文件。

  2. 应用场景:主要用于核心业务后续增加系统级功能,比如日志、认证、授权、缓存。。。这些业务

  3. IocAOP 的关系理解 :AOP 面向切面编程,是 OOP 思想的扩展和延续。AOP 是以 OOP 为指导,可以更加灵活的

让我们动态扩展业务功能。简单理解。比如做一件事情,我们可以通过 OOP 完成核心业务,其他附属业务(扩展的)可以通

AOP 去实现。

通过编码方式使用 Unity 容器的步骤

  1. 添加基础业务:接口 IOrderService、实现 OrderService、实体类等。
public class CourseOrder
{
  public int OrderId { get; set; }
  public int StudentId { get; set; }
  public int CourseId { get; set; }
  public string CourseName { get; set; }
  public int CoursePrice { get; set; }
  public int SchoolId { get; set; }

  //...
}

public interface IOrderService
{
  //提交订单
  int SubmitOrder(CourseOrder order);

  //查询订单
  List<CourseOrder> QueryOrders();
}

/// <summary>
/// 接口的实现(功能性业务)
/// </summary>
public class OrderService : IOrderService
{
  public List<CourseOrder> QueryOrders()
  {
    //实际项目中,在这里编写具体的查询业务...

    return new List<CourseOrder>
    {
      new CourseOrder {  CourseId=1000, CourseName=".NET 学习", CoursePrice=3500, OrderId=2000, SchoolId=502100, StudentId=293400},
      new CourseOrder {  CourseId=1001, CourseName="JAVA 学习", CoursePrice=4500, OrderId=2001, SchoolId=502101, StudentId=293400},
      new CourseOrder {  CourseId=1002, CourseName="前端学习", CoursePrice=5500, OrderId=2002, SchoolId=502102, StudentId=293400}
    };
  }

  public int SubmitOrder(CourseOrder order)
  {
    //在这里编写具体的查询业务...

    Console.WriteLine("--------------------------------《核心业务》课程订单被正确提交...");

    return 1000;
  }
}
  1. 添加 Unity 容器的引用:Unity 5.5Unity.Interception 5.2.1 (两个引用)。

  2. 给基础业务添加具体的注入类(主要封装扩展的行为,也就是你想要扩展的东西),必须实现 ICallHandler 接口。

    【1】通用的验证类

    【2】通用缓存类

    【3】日志添加类...

//引入的命名空间
using Unity.Interception.PolicyInjection.Pipeline;
using Unity.Interception.PolicyInjection.Policies;

#region 扩展我们需要的行为类

//数据验证类
public class DataValidateHandler : ICallHandler
{
  public int Order { get; set; } //这个是标识注入顺序的熟悉

  /// <summary>
  ///注入的行为
  /// </summary>
  /// <param name="input">输入参数</param>
  /// <param name="getNext">调用下一个行为的委托</param>
  /// <returns></returns>
  public IMethodReturn Invoke(IMethodInvocation input, GetNextHandlerDelegate getNext)
  {
      CourseOrder order = input.Inputs[0] as CourseOrder;
      //实现验证的过程
      if (order.StudentId.ToString().Length < 6)
      {
          return input.CreateExceptionMethodReturn(new Exception("StudentId学号长度不能小于6位!"));
      }
      if (order.CoursePrice < 100)
      {
          return input.CreateExceptionMethodReturn(new Exception("课程价格不能小于100元!"));
      }
      Console.WriteLine("订单数据校验成功!");

      return getNext()(input, getNext);  //跳转到下一个注入的行为,并返回结果
  }
}

//缓存类
public class CacheHandler : ICallHandler
{
  public int Order { get; set; }

  public IMethodReturn Invoke(IMethodInvocation input, GetNextHandlerDelegate getNext)
  {
      CourseOrder order = input.Inputs[0] as CourseOrder;

      //在这里编写缓存写入的具体过程...


      Console.WriteLine("订单数据缓存成功!");

      //跳转到下一个注入的行为,并返回结果
      return getNext()(input, getNext);
  }
}

//日志添加类
public class WriteLogHandler : ICallHandler
{
  public int Order { get; set; }
  public IMethodReturn Invoke(IMethodInvocation input, GetNextHandlerDelegate getNext)
  {
      CourseOrder order = input.Inputs[0] as CourseOrder;

      //在这里编写缓存写入的具体过程...


      Console.WriteLine("日志写入成功!");

      //跳转到下一个注入的行为,并返回结果
      return getNext()(input, getNext);
  }
}

#endregion
  1. 将当前的扩展类,包装为特性类。需要给每一个扩展类都要编程特性类。方便我们注入。
/*
 * 封装特性类
 */
//public abstract class HandlerAttribute : Attribute
//{
//    protected HandlerAttribute();

//    public int Order { get; set; }

//    public abstract ICallHandler CreateHandler(IUnityContainer container);
//}
public class DataValidateHandlerAttribute : HandlerAttribute
{
  public override ICallHandler CreateHandler(IUnityContainer container)
  {
      return new DataValidateHandler { Order = this.Order };
  }
}
public class CacheHandlerAttribute : HandlerAttribute
{
  public override ICallHandler CreateHandler(IUnityContainer container)
  {
      return new CacheHandler { Order = this.Order };
  }
}
public class WriteLogHandlerAttribute : HandlerAttribute
{
  public override ICallHandler CreateHandler(IUnityContainer container)
  {
      return new WriteLogHandler { Order = this.Order };
  }
}
  1. 给接口增加特性类(也就是给 IOrderService 增加特性,并要写上注入的顺序)

    注意:当我们给接口注入这些内容后,所有的接口实现类都会自动具有注入的功能

[WriteLogHandler(Order =10)]
[DataValidateHandler(Order =5)]
[CacheHandler(Order =8)]
public interface IOrderService
{
  //提交订单
  int SubmitOrder(CourseOrder order);

  //查询订单
  List<CourseOrder> QueryOrders();
}
  1. 使用容器。
static void Main(string[] args)
{
  //【1】获取容器
  IUnityContainer container = new Unity.UnityContainer();

  //【2】注册服务
  container.RegisterType<IOrderService, OrderService>();

  //【3】注入业务接口
  container.AddNewExtension<Interception>().Configure<Interception>().SetInterceptorFor<IOrderService>(new InterfaceInterceptor());

  //【4】创建对象
  IOrderService orderService = container.Resolve<IOrderService>();

  //【5】业务调用
  CourseOrder order = new CourseOrder
  {
      CourseId = 1001,
      CourseName = ".NET高级VIP课程",
      CoursePrice = 3500,
      SchoolId = 502102,
      StudentId = 293400
  };
  orderService.SubmitOrder(order);

  Console.Read();
}

通过配置文件使用Unity容器

  1. 添加两个独立的引用(我们已经给到了, 直接使用就可以) + 配置文件读取的引用 System.Configuration
//引入命名空间
using System.Configuration;
using Microsoft.Practices.Unity.Configuration;
using Microsoft.Practices.Unity;
  1. 添加业务接口和实现(和前面的一样)
public class CourseOrder
{
  public int OrderId { get; set; }
  public int StudentId { get; set; }
  public int CourseId { get; set; }
  public string CourseName { get; set; }
  public int CoursePrice { get; set; }
  public int SchoolId { get; set; }

  //...
}

public interface IOrderService
{
  //提交订单
  int SubmitOrder(CourseOrder order);

  //查询订单
  List<CourseOrder> QueryOrders();
}

/// <summary>
/// 接口的实现(功能性业务)
/// </summary>
public class OrderService : IOrderService
{
  public List<CourseOrder> QueryOrders()
  {
    //实际项目中,在这里编写具体的查询业务...

    return new List<CourseOrder>
    {
      new CourseOrder {  CourseId=1000, CourseName=".NET 学习", CoursePrice=3500, OrderId=2000, SchoolId=502100, StudentId=293400},
      new CourseOrder {  CourseId=1001, CourseName="JAVA 学习", CoursePrice=4500, OrderId=2001, SchoolId=502101, StudentId=293400},
      new CourseOrder {  CourseId=1002, CourseName="前端学习", CoursePrice=5500, OrderId=2002, SchoolId=502102, StudentId=293400}
    };
  }

  public int SubmitOrder(CourseOrder order)
  {
    //在这里编写具体的查询业务...

    Console.WriteLine("1--------------------------------《核心业务》课程订单被正确提交...");

    return 1000;
  }
}

/// <summary>
/// 接口的实现2(功能性业务)
/// </summary>
public class OrderService2 : IOrderService
{
  public List<CourseOrder> QueryOrders()
  {
    //实际项目中,在这里编写具体的查询业务...

    return new List<CourseOrder>
    {
      new CourseOrder {  CourseId=2000, CourseName=".NET 学习", CoursePrice=3500, OrderId=2000, SchoolId=502100, StudentId=293400},
      new CourseOrder {  CourseId=2001, CourseName="JAVA 学习", CoursePrice=4500, OrderId=2001, SchoolId=502101, StudentId=293400},
      new CourseOrder {  CourseId=2002, CourseName="前端学习", CoursePrice=5500, OrderId=2002, SchoolId=502102, StudentId=293400}
    };
  }

  public int SubmitOrder(CourseOrder order)
  {
    //在这里编写具体的查询业务...

    Console.WriteLine("2--------------------------------《核心业务》课程订单被正确提交...");

    return 1000;
  }
}
  1. 在配置文件 App.config 中增加我们需要个各种配置节点...
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
  <configSections>
    <section  name="unity" type="Microsoft.Practices.Unity.Configuration.UnityConfigurationSection,
             Microsoft.Practices.Unity.Configuration"/>
  </configSections>
  <unity>
    <!--注册业务:alias是业务名称,type第一部分是完全限定名,第二部分是程序集名称-->
    <aliases>
      <add alias="IOrderService" type="IOrderService,UnityTeach2"/>
      <add alias="OrderService1" type="OrderService1,UnityTeach2"/>
      <add alias="OrderService2" type="OrderService2,UnityTeach2"/>
    </aliases>
    <containers>
      <!--MyContainer是自定义的容器名称-->
      <!--type是接口名称,mapTo是注入的对象名称 name:我们要使用的唯一的,可以自定义的名称-->
      <container name="MyContainer">
        <register  type="IOrderService" mapTo="OrderService1" name="MyOrderService1"/>
        <register  type="IOrderService" mapTo="OrderService2" name="MyOrderService2"/>
      </container>
    </containers>
  </unity>
    <startup> 
        <supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.7.2" />
    </startup>
</configuration>
  1. 使用容器
static void Main(string[] args)
{
  //【1】创建容器对象,并加载配置文件
  IUnityContainer container = new UnityContainer();
  container.LoadConfiguration("MyContainer");

  //【2】获得指定的配置节点,并获得节点的内部详情
  UnityConfigurationSection section =
      (UnityConfigurationSection)ConfigurationManager.GetSection(UnityConfigurationSection.SectionName);

  //【3】根据指定的name字符串返回对象
  IOrderService orderService = container.Resolve<IOrderService>("MyOrderService2");

  //【4】核心业务调用
  orderService.SubmitOrder(new CourseOrder());

  Console.Read();

}

总结

当我们使用配置方式创建对象的时候,容器还是非常给力的,只不过配置文件需要我们提前预定好。这种方式适合于,

我们要创建的这些对象使用,前期不固定的情况。其实这个重点解决的是对象的创建问题。也就是最大限度的解耦。

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions