springboot autowired注入为null_net core系列:原生DI+AOP实现spring boot注解式编程

写过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

{

///

/// 开启事务

///

void BeginTransaction();

///

/// 提交

///

void Commit();

///

/// 事物回滚

///

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("回滚事务");

}

}

拦截器

///

/// 事物拦截器

///

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() != 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() != null)

{

Uow.Commit();

}

}

}

用来测试注入效果的接口以及实现类,这里我们定义一辆汽车,汽车拥有一个引擎(属性注入),它能点火启动,点火操作带事物,这里为了演示【接口-实现类】注入和【实现类】注入2种情况,引擎就没添加接口,只有实现类,并且AOP拦截仅有【实现类】的类时,只能拦截虚方法,所以Start和Stop函数为虚函数。

///

/// 汽车引擎

///

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.效果图

39f2a2ba56f354400d436da1cf5a8ac7.png

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

1c35cc87693a623418bb572ba0213aa1.png

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

3.核心代码

第一部分,添加一个扩展类,名叫SummerBootExtentions.cs,代码如下

public static class SummerBootExtentions

{

///

/// 瞬时

///

///

///

///

///

///

public static IServiceCollection AddSbTransient(this IServiceCollection services, params Type[] interceptorTypes)

{

return services.AddSbService(typeof(TService), typeof(TImplementation), ServiceLifetime.Transient, interceptorTypes);

}

///

/// 请求级别

///

///

///

///

///

///

public static IServiceCollection AddSbScoped(this IServiceCollection services, params Type[] interceptorTypes)

{

return services.AddSbService(typeof(TService), typeof(TImplementation), ServiceLifetime.Scoped, interceptorTypes);

}

///

/// 单例

///

///

///

///

///

///

public static IServiceCollection AddSbSingleton(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() != null)

{

var propertyType = info.PropertyType;

var impl = provider.GetService(propertyType);

if (impl != null)

{

info.SetValue(target, impl);

}

}

//配置值注入

if (info.GetCustomAttribute() 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 interceptors = interceptorTypes.ToList()

.ConvertAll(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;

}

///

/// 瞬时

///

///

///

///

///

public static IServiceCollection AddSbTransient(this IServiceCollection services, params Type[] interceptorTypes)

{

return services.AddSbService(typeof(TService), ServiceLifetime.Transient, interceptorTypes);

}

///

/// 请求

///

///

///

///

///

public static IServiceCollection AddSbScoped(this IServiceCollection services, params Type[] interceptorTypes)

{

return services.AddSbService(typeof(TService), ServiceLifetime.Scoped, interceptorTypes);

}

///

/// 单例

///

///

///

///

///

public static IServiceCollection AddSbSingleton(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 interceptors = interceptorTypes.ToList()

.ConvertAll(interceptorType => provider.GetService(interceptorType) as IInterceptor);

var proxy = new ProxyGenerator().CreateClassProxy(serviceType, interceptors.ToArray());

var properties = serviceType.GetTypeInfo().DeclaredProperties;

foreach (PropertyInfo info in properties)

{

//属性注入

if (info.GetCustomAttribute() != null)

{

var propertyType = info.PropertyType;

var impl = provider.GetService(propertyType);

if (impl != null)

{

info.SetValue(proxy, impl);

}

}

//配置值注入

if (info.GetCustomAttribute() 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;

}

///

/// 添加summer boot扩展

///

///

///

public static IMvcBuilder AddSB(this IMvcBuilder builder)

{

if (builder == null)

throw new ArgumentNullException(nameof(builder));

ControllerFeature feature = new ControllerFeature();

builder.PartManager.PopulateFeature(feature);

foreach (Type type in feature.Controllers.Select((Func)(c => c.AsType())))

builder.Services.TryAddTransient(type, type);

builder.Services.Replace(ServiceDescriptor.Transient());

return builder;

}

}

第二部分,添加一个自定义控制器激活类,用以替换掉mvc自带的激活类,这个类命名为SbControllerActivator.cs,代码如下

public class SbControllerActivator : IControllerActivator

{

///

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 info in properties)

{

//属性注入

if (info.GetCustomAttribute() != null)

{

var propertyType = info.PropertyType;

var impl = actionContext.HttpContext.RequestServices.GetService(propertyType);

if (impl != null)

{

info.SetValue(proxy, impl);

}

}

//配置值注入

if (info.GetCustomAttribute() 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;

}

///

public virtual void Release(ControllerContext context, object controller)

{

}

}

第三部分,在Startup.cs中,修改ConfigureServices方法如下

public void ConfigureServices(IServiceCollection services)

{

services.Configure(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(typeof(TransactionalInterceptor));

services.AddScoped();

services.AddScoped(typeof(TransactionalInterceptor));

services.AddSbScoped(typeof(TransactionalInterceptor));

}

从上面代码我们可以看到,在addMvc后加上了我们替换默认控制器的AddSB方法,接管了控制器的生成。AddSbScoped和AddSbScoped这种添加依赖注入的方式也保持了net core自带DI的原滋原味,简洁优雅,并且实现了动态代理,只需要在参数里添加拦截器,就能实时拦截,这里参数为params Type[],可以添加N个拦截器。

原文来源:博客园

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值