前言
其實好多項目中,做一些數據攔截、數據緩存都有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在做一些業務前置或后置處理上時很有用的,使用比較靈活,無需修改原有代碼邏輯,比起修改原有代碼維護相對好多啦!!!
