写过spring
boot之后,那种无处不在的注解让我非常喜欢,比如属性注入@autowire,配置值注入@value,声明式事物@Transactional等,都非常简洁优雅,那么我就在想,这些在net
core里能实现么?经过一番摸索,终于实现并整理成此文。

       IOC方面,个人非常喜欢net
core自带的DI,因为他注册服务简洁优雅,3个生命周期通俗易懂,所以就没使用autofac等其他容器,AOP方面,使用了业内鼎鼎大名的Castle.DynamicProxy(简称DP),所以要在nuget中添加Castle.Core的依赖包,这里大家可能会有疑问,利用mvc的actionFilter不就可以实现了么,为什么还要引用DP呢,因为呀,actionFilter只在controller层有效,普通类他就无能为力了,而DP无所不能。

1.定义注解和需要用到的类

属性注入注解
[AttributeUsage(AttributeTargets.Property)] public class AutowiredAttribute :
Attribute { }
配置值注入注解
[AttributeUsage(AttributeTargets.Property)] public class ValueAttribute :
Attribute {public ValueAttribute(string value = "") { this.Value = value; }
public string Value { get; } }
声明式事物注解
[AttributeUsage(AttributeTargets.Method)] public class TransactionalAttribute
: Attribute { }
工作单元接口及实现类,用来实现事物操作,注入级别为scope,一次请求公用一个工作单元实例
public interface IUnitOfWork : IDisposable { /// <summary> /// 开启事务 ///
</summary> void BeginTransaction(); /// <summary> /// 提交 /// </summary> void
Commit();/// <summary> /// 事物回滚 /// </summary> void RollBack(); } public class
UnitOfWork : IUnitOfWork {public void BeginTransaction() { Console.WriteLine("
开启事务"); } public void Commit() { Console.WriteLine("提交事务"); } public void
Dispose() {//throw new System.NotImplementedException(); } public void
RollBack() { Console.WriteLine("回滚事务"); } }
拦截器
/// <summary> /// 事物拦截器 /// </summary> public class TransactionalInterceptor :
StandardInterceptor {private IUnitOfWork Uow { set; get; } public
TransactionalInterceptor(IUnitOfWork uow) { Uow= uow; } protected override void
PreProceed(IInvocation invocation) { Console.WriteLine("{0}拦截前",
invocation.Method.Name);var method =
invocation.TargetType.GetMethod(invocation.Method.Name);if (method != null &&
method.GetCustomAttribute<TransactionalAttribute>() !=null) {
Uow.BeginTransaction(); } }protected override void PerformProceed(IInvocation
invocation) { invocation.Proceed(); }protected override void
PostProceed(IInvocation invocation) { Console.WriteLine("{0}拦截后, 返回值是{1}",
invocation.Method.Name, invocation.ReturnValue);var method =
invocation.TargetType.GetMethod(invocation.Method.Name);if (method != null &&
method.GetCustomAttribute<TransactionalAttribute>() !=null) { Uow.Commit(); } }
}

用来测试注入效果的接口以及实现类,这里我们定义一辆汽车,汽车拥有一个引擎(属性注入),它能点火启动,点火操作带事物,这里为了演示【接口-实现类】注入和【实现类】注入2种情况,引擎就没添加接口,只有实现类,并且AOP拦截仅有【实现类】的类时,只能拦截虚方法,所以Start和Stop函数为虚函数。
/// <summary> /// 汽车引擎 /// </summary> public class Engine { [Value("HelpNumber"
)]public string HelpNumber { set; get; } public virtual void Start() {
Console.WriteLine("发动机启动"); Stop(); } public virtual void Stop() {
Console.WriteLine("发动机熄火,拨打求救电话" + HelpNumber); } } public interface ICar {
Engine Engine {set; get; } void Fire(); } public class Car : ICar { [Autowired]
public Engine Engine { set; get; } [Value("oilNo")] public int OilNo { set; get
; } [Transactional]public void Fire() { Console.WriteLine("加满" + OilNo + "号汽油,点火
"); Engine.Start(); } }
控制器HomeController
public class HomeController : Controller { [Autowired] public ICar Car{ set;
get; } [Value("description")] public string Description { set; get; } public
IActionResult Index() {var car = Car; Console.WriteLine(Description);
Car.Fire();return View(); } }

 修改appsettings.json,添加一些测试键值对,(如果测试时发现输出的中文乱码,把appsettings.json保存为utf8格式即可),具体代码如下,
{ "Logging": { "LogLevel": { "Default": "Warning" } }, "AllowedHosts": "*", "
oilNo": 95, "HelpNumber": "110", "description": "我要开始飙车了" }
2.效果图



从上图可以看到,正常注入,正常开启拦截器和事务,正确读取配置值。



从上图可以看到,我们的控制器,ICar和Engine全部都是动态代理类,注入正常。

3.核心代码

第一部分,添加一个扩展类,名叫SummerBootExtentions.cs,代码如下
public static class SummerBootExtentions { /// <summary> /// 瞬时 /// </summary>
/// <typeparam name="TService"></typeparam> /// <typeparam
name="TImplementation"></typeparam> /// <param name="services"></param> ///
<param name="interceptorTypes"></param> /// <returns></returns> public static
IServiceCollection AddSbTransient<TService, TImplementation>(this
IServiceCollection services,params Type[] interceptorTypes) { return
services.AddSbService(typeof(TService), typeof(TImplementation),
ServiceLifetime.Transient, interceptorTypes); }/// <summary> /// 请求级别 ///
</summary> /// <typeparam name="TService"></typeparam> /// <typeparam
name="TImplementation"></typeparam> /// <param name="services"></param> ///
<param name="interceptorTypes"></param> /// <returns></returns> public static
IServiceCollection AddSbScoped<TService, TImplementation>(this
IServiceCollection services,params Type[] interceptorTypes) { return
services.AddSbService(typeof(TService), typeof(TImplementation),
ServiceLifetime.Scoped, interceptorTypes); }/// <summary> /// 单例 /// </summary>
/// <typeparam name="TService"></typeparam> /// <typeparam
name="TImplementation"></typeparam> /// <param name="services"></param> ///
<param name="interceptorTypes"></param> /// <returns></returns> public static
IServiceCollection AddSbSingleton<TService, TImplementation>(this
IServiceCollection services,params Type[] interceptorTypes) { return
services.AddSbService(typeof(TService), typeof(TImplementation),
ServiceLifetime.Singleton, interceptorTypes); }public static IServiceCollection
AddSbService(this IServiceCollection services, Type serviceType, Type
implementationType, ServiceLifetime lifetime,params Type[] interceptorTypes) {
services.Add(new ServiceDescriptor(implementationType, implementationType,
lifetime));object Factory(IServiceProvider provider) { var target =
provider.GetService(implementationType);var properties =
implementationType.GetTypeInfo().DeclaredProperties;foreach (PropertyInfo info
in properties) { //属性注入 if (info.GetCustomAttribute<AutowiredAttribute>() !=
null) { var propertyType = info.PropertyType; var impl =
provider.GetService(propertyType);if (impl != null) { info.SetValue(target,
impl); } }//配置值注入 if (info.GetCustomAttribute<ValueAttribute>() is
ValueAttribute valueAttribute) {var value = valueAttribute.Value; if
(provider.GetService(typeof(IConfiguration)) is IConfiguration configService) {
var pathValue = configService.GetSection(value).Value; if (pathValue != null) {
var pathV = Convert.ChangeType(pathValue, info.PropertyType);
info.SetValue(target, pathV); } } } } List<IInterceptor> interceptors =
interceptorTypes.ToList() .ConvertAll<IInterceptor>(interceptorType =>
provider.GetService(interceptorType)as IInterceptor); var proxy = new
ProxyGenerator().CreateInterfaceProxyWithTarget(serviceType, target,
interceptors.ToArray());return proxy; }; var serviceDescriptor = new
ServiceDescriptor(serviceType, Factory, lifetime);
services.Add(serviceDescriptor);return services; } /// <summary> /// 瞬时 ///
</summary> /// <typeparam name="TService"></typeparam> /// <param
name="services"></param> /// <param name="interceptorTypes"></param> ///
<returns></returns> public static IServiceCollection AddSbTransient<TService>(
this IServiceCollection services, params Type[] interceptorTypes) { return
services.AddSbService(typeof(TService), ServiceLifetime.Transient,
interceptorTypes); }/// <summary> /// 请求 /// </summary> /// <typeparam
name="TService"></typeparam> /// <param name="services"></param> /// <param
name="interceptorTypes"></param> /// <returns></returns> public static
IServiceCollection AddSbScoped<TService>(this IServiceCollection services,
params Type[] interceptorTypes) { return services.AddSbService(typeof
(TService), ServiceLifetime.Scoped, interceptorTypes); }/// <summary> /// 单例 ///
</summary> /// <typeparam name="TService"></typeparam> /// <param
name="services"></param> /// <param name="interceptorTypes"></param> ///
<returns></returns> public static IServiceCollection AddSbSingleton<TService>(
this IServiceCollection services, params Type[] interceptorTypes) { return
services.AddSbService(typeof(TService), ServiceLifetime.Singleton,
interceptorTypes); }public static IServiceCollection AddSbService(this
IServiceCollection services, Type serviceType, ServiceLifetime lifetime,params
Type[] interceptorTypes) {if (services == null) throw new
ArgumentNullException(nameof(services));if (serviceType == (Type)null) throw new
ArgumentNullException(nameof(serviceType));object Factory(IServiceProvider
provider) { List<IInterceptor> interceptors = interceptorTypes.ToList()
.ConvertAll<IInterceptor>(interceptorType =>
provider.GetService(interceptorType)as IInterceptor); var proxy = new
ProxyGenerator().CreateClassProxy(serviceType, interceptors.ToArray());var
properties = serviceType.GetTypeInfo().DeclaredProperties; foreach
(PropertyInfo infoin properties) { //属性注入 if
(info.GetCustomAttribute<AutowiredAttribute>() !=null) { var propertyType =
info.PropertyType;var impl = provider.GetService(propertyType); if (impl != null
) { info.SetValue(proxy, impl); } }//配置值注入 if
(info.GetCustomAttribute<ValueAttribute>()is ValueAttribute valueAttribute) {
var value = valueAttribute.Value; if (provider.GetService(typeof
(IConfiguration))is IConfiguration configService) { var pathValue =
configService.GetSection(value).Value;if (pathValue != null) { var pathV =
Convert.ChangeType(pathValue, info.PropertyType); info.SetValue(proxy, pathV);
} } } }return proxy; }; var serviceDescriptor = new
ServiceDescriptor(serviceType, Factory, lifetime);
services.Add(serviceDescriptor);return services; } /// <summary> /// 添加summer
boot扩展/// </summary> /// <param name="builder"></param> /// <returns></returns>
public static IMvcBuilder AddSB(this IMvcBuilder builder) { if (builder == null)
throw new ArgumentNullException(nameof(builder)); ControllerFeature feature =
new ControllerFeature(); builder.PartManager.PopulateFeature<ControllerFeature>
(feature);foreach (Type type in feature.Controllers.Select<TypeInfo,
Type>((Func<TypeInfo, Type>)(c => c.AsType())))
builder.Services.TryAddTransient(type, type);
builder.Services.Replace(ServiceDescriptor.Transient<IControllerActivator,
SbControllerActivator>()); return builder; } } View Code
第二部分,添加一个自定义控制器激活类,用以替换掉mvc自带的激活类,这个类命名为SbControllerActivator.cs,代码如下
public class SbControllerActivator : IControllerActivator { /// <inheritdoc />
public object Create(ControllerContext actionContext) { if (actionContext ==
null) throw new ArgumentNullException(nameof(actionContext)); Type serviceType =
actionContext.ActionDescriptor.ControllerTypeInfo.AsType();var target =
actionContext.HttpContext.RequestServices.GetRequiredService(serviceType);var
properties = serviceType.GetTypeInfo().DeclaredProperties; var proxy = new
ProxyGenerator().CreateClassProxyWithTarget(serviceType, target);foreach
(PropertyInfo infoin properties) { //属性注入 if
(info.GetCustomAttribute<AutowiredAttribute>() !=null) { var propertyType =
info.PropertyType;var impl =
actionContext.HttpContext.RequestServices.GetService(propertyType);if (impl !=
null) { info.SetValue(proxy, impl); } } //配置值注入 if
(info.GetCustomAttribute<ValueAttribute>()is ValueAttribute valueAttribute) {
var value = valueAttribute.Value; if
(actionContext.HttpContext.RequestServices.GetService(typeof(IConfiguration)) is
IConfiguration configService) {var pathValue =
configService.GetSection(value).Value;if (pathValue != null) { var pathV =
Convert.ChangeType(pathValue, info.PropertyType); info.SetValue(proxy, pathV);
} } } }return proxy; } /// <inheritdoc /> public virtual void
Release(ControllerContext context,object controller) { } } View Code
第三部分,在Startup.cs中,修改ConfigureServices方法如下
public void ConfigureServices(IServiceCollection services) { services.Configure
<CookiePolicyOptions>(options => { // This lambda determines whether user
consent for non-essential cookies is needed for a given request.
options.CheckConsentNeeded = context =>true; options.MinimumSameSitePolicy =
SameSiteMode.None; }); services.AddMvc()
.SetCompatibilityVersion(CompatibilityVersion.Version_2_1) .AddSB();
services.AddSbScoped<Engine>(typeof(TransactionalInterceptor));
services.AddScoped<IUnitOfWork,UnitOfWork>(); services.AddScoped(typeof
(TransactionalInterceptor)); services.AddSbScoped<ICar, Car>(typeof
(TransactionalInterceptor)); }
     
 从上面代码我们可以看到,在addMvc后加上了我们替换默认控制器的AddSB方法,接管了控制器的生成。AddSbScoped<Engine>和AddSbScoped<ICar,
Car>这种添加依赖注入的方式也保持了net
core自带DI的原滋原味,简洁优雅,并且实现了动态代理,只需要在参数里添加拦截器,就能实时拦截,这里参数为params Type[],可以添加N个拦截器。

4.写在最后

   在博客园潜水了好几年,见证了net core从1.0到快出3.0,这也是第一次尝试着写博客,为net core的发扬光大尽自己的一份力。

     

友情链接
KaDraw流程图
API参考文档
OK工具箱
云服务器优惠
阿里云优惠券
腾讯云优惠券
华为云优惠券
站点信息
问题反馈
邮箱:ixiaoyang8@qq.com
QQ群:637538335
关注微信