前言
其實好多項目中,做一些數據攔截、數據緩存都有Aop的概念,只是實現方式不一樣;之前大家可能都會利用過濾器來實現Aop的功能,如果是Asp.NetCore的話,也可能會使用中間件; 而這種實現方式都是在請求過程中進行攔截,如果我們想在服務層中做切面的話,那種方式顯然不好使了,需要用到“真正的Aop”。
直接開始
其實我們常說的“真正的Aop”其實就是動態代理,理論知識我這里就不記錄了,自己也寫不好,大家自行找度娘,我們這直接上代碼:
正常搭建一個控制台項目,目錄結構如下:
這里就是模擬一個簡單的用戶維護,代碼內容如下:
定義Model
public class User { public string Name { get; set; } public int Age { get; set; } }
定義接口
public interface IUserService { bool AddUser(User user); }
實現接口
public class UserService : IUserService { public bool AddUser(User user) { Console.WriteLine("用戶添加成功"); return true; } }
main方法
class Program { static void Main(string[] args) { User user = new User {Name="Zoe", Age=18 }; IUserService userService = new UserService(); userService.AddUser(user); //Console.ReadLine(); } }
項目很簡單,正常運行就行;
新需求,如果我們想在用戶增加前和增加后都做點其他事,怎么做呢?
解決方案:
1. 直接修改服務層代碼,執行存儲前后分別處理相關業務就行了;
2. 使用Aop,不修改原有方法。
方案1我們就不說了,肯定大家都知道,功能肯定能實現,但需要原有代碼,加入好多接口都要處理類似的事情怎么辦? 如果加好了,下個版本針對某些功能不需要了怎么辦?顯然不是很靈活,而且這樣重復改,出Bug的幾率很大哦!!!
直接動態代理:
1. 增加一個類,進行業務操作;
using System; using System.Collections.Generic; using System.Reflection; using System.Text; namespace Aop { public class MyDecorator : DispatchProxy { //具體類型 public object TargetClass { get; set; } protected override object Invoke(MethodInfo targetMethod, object[] args) { Console.WriteLine("增加用戶前執行業務"); //調用原有方法 targetMethod.Invoke(TargetClass, args); Console.WriteLine("增加用戶后執行業務"); return true; } } }
優化 Main()函數的代碼
class Program { static void Main(string[] args) { User user = new User {Name="Zoe", Age=18 }; IUserService userService = new UserService(); userService.AddUser(user); //動態代理 //1. 創建代理對象 IUserService userService1 = DispatchProxy.Create<IUserService, MyDecorator>(); //2. 因為調用的是實例方法,需要傳提具體類型 ((MyDecorator)userService1).TargetClass = new UserService(); userService1.AddUser(user); Console.ReadLine(); } }
看動態代理部分,這樣就統一實現了用戶維護服務層的Aop編程,看運行結果:
這樣是不是比較靈活了,自己不需要在一個個業務層中進行處理,而且取舍也很簡單,不要就不適用此類就行了。
引用第三方庫
原生的這種方式使用感覺有點麻煩,還有什么強制轉換啊,傳類型啊等這些,Castle.Core就幫我們把事都做好了,接下來我們看看怎么用。
1. 首先引入Castle.Core;
2. 新增攔截器類,做業務擴展;
using Castle.DynamicProxy; using System; using System.Collections.Generic; using System.Text; namespace Aop { class MyIntercept : IInterceptor { public void Intercept(IInvocation invocation) { //執行原有方法之前 Console.WriteLine("增加用戶前執行業務"); //執行原有方法 invocation.Proceed(); //執行原有方法之后 Console.WriteLine("增加用戶后執行業務"); } } }
3. Main函數增加Castle.Core的用法
using AopModel; using AopService; using Castle.DynamicProxy; using System; using System.Reflection; using System.Reflection.Metadata; namespace Aop { class Program { static void Main(string[] args) { User user = new User {Name="Zoe", Age=18 }; IUserService userService = new UserService(); userService.AddUser(user); Console.WriteLine("=============動態代理=============="); //動態代理 //1. 創建代理對象 IUserService userService1 = DispatchProxy.Create<IUserService, MyDecorator>(); //2. 因為調用的是實例方法,需要傳提具體類型 ((MyDecorator)userService1).TargetClass = new UserService(); userService1.AddUser(user); Console.WriteLine("=============Castle.Core=============="); ProxyGenerator generator = new ProxyGenerator(); var u = generator.CreateInterfaceProxyWithTarget<IUserService>(new UserService(),new MyIntercept()); u.AddUser(user); Console.ReadLine(); } } }
這樣就行了,看運行結果:
綜上,第三方使用相對簡單,而且封裝了好多方法,不僅僅以上的使用方式。 以下舉例集成Autofac和Castle.Core在Asp.NetCore中的應用(用Asp.NetCore項目,是因為好多真實項目都是API或Web項目,所以比較符合實際),僅供給大家提供思路。如下:
1. 首先我們創建一個Asp.NetCore項目,這里我創建的是API項目,正常運行即可,項目結構如下;
2. 引入三個包,通過Nuget安裝,Autofac開頭,如下
注: 其中Autofac.Extras.DynamicProxy就是AOP相關組件,其中包含了Castle.Core,所以不用單獨安裝Castle.Core.
3.模擬編寫用戶維護相關邏輯,代碼如下:
接口:
public interface IUserService { int AddUser(string strName, int nAge); }
實現:
public class UserService : IUserService { /// <summary> /// 模擬新增用戶,這里沒有寫數據處理層 /// </summary> public int AddUser(string strName, int nAge) { Console.WriteLine("新增用戶到數據庫中"); return 1; } }
4. 編寫攔截器邏輯,即代理:
using Castle.DynamicProxy; using System; using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; namespace WebAop.Aop { public class UserAop : IInterceptor {
//關鍵所在,在執行方法前后進行相關邏輯處理 public void Intercept(IInvocation invocation) { Console.WriteLine("新增用戶前進行其他處理"); //調用原有方法 invocation.Proceed(); Console.WriteLine("新增用戶后進行其他處理"); } } }
5. 集成Autofac將用戶維護服務這塊進行注冊到容器中:
- 首先在Startup中增加方法,如下:
public void ConfigureContainer(ContainerBuilder builder) { //注冊用戶維護業務層 var basePath = AppContext.BaseDirectory; var serviceDll = Path.Combine(basePath, "AopService.dll"); if(!File.Exists(serviceDll)) { throw new Exception("找不到程序集"); } //注冊AOP攔截器 builder.RegisterType(typeof(UserAop)); builder.RegisterAssemblyTypes(Assembly.LoadFrom(serviceDll)) .AsImplementedInterfaces() .EnableInterfaceInterceptors()//開啟切面,需要引入Autofac.Extras.DynamicProxy .InterceptedBy(typeof(UserAop));//指定攔截器,可以指定多個 }
- 然后在program中添加Autofac的工廠,如下:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Autofac.Extensions.DependencyInjection;
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
namespace WebAop
{
public class Program
{
public static void Main(string[] args)
{
CreateHostBuilder(args).Build().Run();
}
public static IHostBuilder CreateHostBuilder(string[] args) =>
Host.CreateDefaultBuilder(args)
//需要引入Autofac.Extensions.DependencyInjection, 這里重要,不然Autofac不管用 .UseServiceProviderFactory(new AutofacServiceProviderFactory())
.ConfigureWebHostDefaults(webBuilder =>
{
webBuilder.UseStartup<Startup>();
});
}
}
6. 增加UserController方法,如下:
using System; using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; using AopService.Interface; using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Mvc; namespace WebAop.Controllers { [Route("api/[controller]")] [ApiController] public class UserController : ControllerBase { private IUserService _userService; //這里已經通過Autofac 注冊過了,直接通過構造函數注入即可 public UserController(IUserService userService) { _userService = userService; } [HttpGet] [Route("AddUser")] public IActionResult AddUser(string name,int age) { //正常調用用戶新增操作 _userService.AddUser(name, age); return Ok("Success!!"); } } }
7. 運行走起,為了方便看見控制台打印,用項目啟動方式進行運行,結果如下:
直接在瀏覽器中輸入http://localhost:5000/api/User/AddUser?name=sss&age=12,然后回車,然后看控制台打印:
總結:
AOP在做一些業務前置或后置處理上時很有用的,使用比較靈活,無需修改原有代碼邏輯,比起修改原有代碼維護相對好多啦!!!