基于 AOP 的 Unity 容器的使用
回顾
AOP 本质是在 OOP 基础上,解决项目动态扩展的问题。
使用场景
我们如果把项目的业务,分为功能性业务,和系统级业务两部分的话,功能性业务通常是我们开发某些模块最主要的,
是第一时间要开发完成的。所以,我们对功能性业务的设计是第一位的。但是系统级业务,往往是在功能性业务的基
础上的扩展。这种扩展可以延迟到项目运营后期,也可以在项目功能性业务完成后,独立完成,再集合到一起。
AOP的本质:就是希望在不破坏原有对象的封装前提下,动态的给对象增加额外行为的能力。
实现
- 基于装饰器设计模式,对于小项目或者小的功能性业务扩展,还是非常灵活的。
- 框架的代理
- 使用第三方的代理框架
IOC
概念
Ioc (inversion of control: 控制反转)又称为“依赖注入”(Dependence injection)(DI)
控制反转:在我们的开发使用中,对象的创建权,通常是我们开发者自己完成的。
但是现在我们通过容器来完成对象的创建,也就意味着,我们将对象的创建权做了转移。
开发中创建的容器:Unity、Spring.Net...
容器 Unity 使用详解
- 基础理解:
Unity 是一个轻量级的可扩展的依赖注入容器。使用非常方便,开发者可以选择编码方式,也可以
选择配置文件方式,达到对象依赖关系的的建立。在程序运行中,调用 Unity 容器,可以得到我们需要的对象。
-
选择建议:小项目可以使用代码方式,较大项目,建议用配置文件。
-
应用场景:主要用于核心业务后续增加系统级功能,比如日志、认证、授权、缓存。。。这些业务
-
Ioc 和 AOP 的关系理解 :AOP 面向切面编程,是 OOP 思想的扩展和延续。AOP 是以 OOP 为指导,可以更加灵活的
让我们动态扩展业务功能。简单理解。比如做一件事情,我们可以通过 OOP 完成核心业务,其他附属业务(扩展的)可以通
过 AOP 去实现。
通过编码方式使用 Unity 容器的步骤
- 添加基础业务:接口
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;
}
}
-
添加 Unity 容器的引用:Unity 5.5 和 Unity.Interception 5.2.1 (两个引用)。
-
给基础业务添加具体的注入类(主要封装扩展的行为,也就是你想要扩展的东西),必须实现 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
- 将当前的扩展类,包装为特性类。需要给每一个扩展类都要编程特性类。方便我们注入。
/*
* 封装特性类
*/
//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 };
}
}
-
给接口增加特性类(也就是给 IOrderService 增加特性,并要写上注入的顺序)
注意:当我们给接口注入这些内容后,所有的接口实现类都会自动具有注入的功能
[WriteLogHandler(Order =10)]
[DataValidateHandler(Order =5)]
[CacheHandler(Order =8)]
public interface IOrderService
{
//提交订单
int SubmitOrder(CourseOrder order);
//查询订单
List<CourseOrder> QueryOrders();
}
- 使用容器。
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容器
- 添加两个独立的引用(我们已经给到了, 直接使用就可以) + 配置文件读取的引用
System.Configuration
//引入命名空间
using System.Configuration;
using Microsoft.Practices.Unity.Configuration;
using Microsoft.Practices.Unity;
- 添加业务接口和实现(和前面的一样)
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;
}
}
- 在配置文件
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>
- 使用容器
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();
}
总结
当我们使用配置方式创建对象的时候,容器还是非常给力的,只不过配置文件需要我们提前预定好。这种方式适合于,
我们要创建的这些对象使用,前期不固定的情况。其实这个重点解决的是对象的创建问题。也就是最大限度的解耦。
基于
AOP的Unity容器的使用回顾
AOP本质是在OOP基础上,解决项目动态扩展的问题。使用场景
我们如果把项目的业务,分为功能性业务,和系统级业务两部分的话,功能性业务通常是我们开发某些模块最主要的,
是第一时间要开发完成的。所以,我们对功能性业务的设计是第一位的。但是系统级业务,往往是在功能性业务的基
础上的扩展。这种扩展可以延迟到项目运营后期,也可以在项目功能性业务完成后,独立完成,再集合到一起。
AOP的本质:就是希望在不破坏原有对象的封装前提下,动态的给对象增加额外行为的能力。
实现
局限性:只能针对某一个功能性业务类实现“装饰”。受制于特定的接口。
试想,如果我们想给不同的对象,扩展相同的系统级业务,如何做到?
特点:就是通过泛型类的引入,可以对任意需要扩展的类实现动态行为的注入。
好处:可以针对所有的行为,统一切入扩展的行为。
注意:如果你某些行为不想注入,请使用原始的对象就可以了。(一般很少这么做)
特点:简单,和.NET自带的代理框架比较,代码量也少(推荐)
其他:和.NET自带的代理框架几乎是一样。
IOC概念
Ioc(inversion of control: 控制反转)又称为“依赖注入”(Dependence injection)(DI)控制反转:在我们的开发使用中,对象的创建权,通常是我们开发者自己完成的。
但是现在我们通过容器来完成对象的创建,也就意味着,我们将对象的创建权做了转移。
开发中创建的容器:
Unity、Spring.Net...容器
Unity使用详解Unity是一个轻量级的可扩展的依赖注入容器。使用非常方便,开发者可以选择编码方式,也可以选择配置文件方式,达到对象依赖关系的的建立。在程序运行中,调用
Unity容器,可以得到我们需要的对象。选择建议:小项目可以使用代码方式,较大项目,建议用配置文件。
应用场景:主要用于核心业务后续增加系统级功能,比如日志、认证、授权、缓存。。。这些业务
Ioc和AOP的关系理解 :AOP面向切面编程,是OOP思想的扩展和延续。AOP是以OOP为指导,可以更加灵活的让我们动态扩展业务功能。简单理解。比如做一件事情,我们可以通过
OOP完成核心业务,其他附属业务(扩展的)可以通过
AOP去实现。通过编码方式使用
Unity容器的步骤IOrderService、实现OrderService、实体类等。添加
Unity容器的引用:Unity 5.5和Unity.Interception 5.2.1(两个引用)。给基础业务添加具体的注入类(主要封装扩展的行为,也就是你想要扩展的东西),必须实现
ICallHandler接口。【1】通用的验证类
【2】通用缓存类
【3】日志添加类...
给接口增加特性类(也就是给
IOrderService增加特性,并要写上注入的顺序)注意:当我们给接口注入这些内容后,所有的接口实现类都会自动具有注入的功能
通过配置文件使用Unity容器
System.ConfigurationApp.config中增加我们需要个各种配置节点...总结
当我们使用配置方式创建对象的时候,容器还是非常给力的,只不过配置文件需要我们提前预定好。这种方式适合于,
我们要创建的这些对象使用,前期不固定的情况。其实这个重点解决的是对象的创建问题。也就是最大限度的解耦。