net core系列:一个接口多个实现类,利用mixin实现属性注入

哈哈哈哈,大家好,我就是高产似母猪的三合。日常开发中,我们常会遇到这样的场景,一个接口,有多个实现类,在某个业务中,我们希望指定某个实现类,如今网络上常见的解决方案,就是注入一个委托或者利用工厂模式,这些方式虽然能实现功能,但使用起来还是不够优雅,如何才称得上优雅呢?自然是在添加服务的时候给服务指定名称,注入的时候根据名称来进行注入,没错,就是类似spring boot里的@Qualifier注解,那么net core能实现这个注解么?要知道net core里的原生DI并不支持在注册的时候给服务添加名称,所以实现起来也是难上加难,但是,利用动态代理,我们就能完美解决这个问题,没错,他Lei了,net core版[QualifierAttribute],实现这个注解用到的最核心的技术,就是动态代理中非常重要的mixin,什么是mixin呢?玩过less的小伙伴可能会知道,这玩意可以把2个css样式混合成一个,而在net core中,DP(castle.dynamicproxy)提供的mixin技术,可以在程序运行时,把多个类混合成一个动态代理类,这个混合类既可以用作类1,又可以用作类2,那么思路就很清晰了,类1就是我们注入的服务类,类2需要我们自定义,在我们的这个自定义类中,我们可以添加一个Name的属性,这样在注册服务的时候给服务命名,到了注入的时候,我们就可以根据名称来进行精准地的属性注入。BTW,动态代理,注解式编程,AOP贯穿本系列始终,no bb,正文开始。

1.定义用到的类

定义QualifierAttribute注解

<code> 

public

class

QualifierAttribute

:

Attribute

{

public

string

Name {

get

; }

public

QualifierAttribute

(

string

name

)

{

if

(

string

.IsNullOrWhiteSpace(name)) {

throw

new

ArgumentException(

"服务名称不能为空"

); }

this

.Name = name; } }/<code>

定义用来进行混合的附加类接口IDependencyAddition和他的实现类DependencyAddition,类里面只有一个名称的属性。

<code>

public

interface

IDependencyAddition

{

string

Name {

set

;

get

; } }/<code>
<code> 

public

class

DependencyAddition

:

IDependencyAddition

{

public

string

Name {

set

;

get

; } }/<code>

第一篇文章里,讲过有一辆汽车,汽车里有引擎,我们在这基础上进行扩展,汽车总是有轮胎的,我们定义一个轮胎接口IWheel和他的2个实现类WheelA和WheelB,类里只有一个Scroll方法,表示轮胎在滚,同时向控制台输出他们是哪种轮胎。

<code> 

public

interface

IWheel

{

void

Scroll

(

)

; }/<code>
<code>  

public

class

WheelA

:

IWheel {

public

void

Scroll

()

{ Console.WriteLine(

"我是A轮胎,我正在滚"

); } }/<code>
<code> 

public

class

WheelB

:

IWheel {

public

void

Scroll

()

{ Console.WriteLine(

"我是B轮胎,我正在滚"

); } }/<code>

修改汽车类接口ICar和他的实现类Car,主要就是添加了轮胎的属性注入,并且用[Qualifier("B轮胎")]注解指定使用哪种轮胎,然后调用Scroll方法,让轮胎滚起来。

<code>   

public

interface

ICar

{ Engine Engine {

set

;

get

; } IWheel Wheel {

set

;

get

; }

void

Fire

(

)

; }/<code>
<code>   

public

class

Car

:

ICar

{ [ ]

public

Engine Engine {

set

;

get

; } [ ]

public

int

OilNo {

set

;

get

; } [ ] [ ]

public

IWheel Wheel {

set

;

get

; } [ ]

public

void

Fire

(

)

{ Console.WriteLine(

"加满"

+ OilNo +

"号汽油,点火"

); Wheel.Scroll(); Engine.Start(); } }/<code>

IServiceCollection的静态扩展类SummerBootExtentions,和上一篇相比,主要是添加了AddSbService的新重载,使注册服务的时候能够为服务添加名称,在这基础上进一步封装了AddSbSingleton,AddSbScoped和AddSbTransient方法。还有在工厂委托函数里进行属性注入的时候,添加了支持[QualifierAttribute]注解的逻辑,完整类代码如下:

<code>

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

AddSbTransient

(

this

IServiceCollection services, Type serviceType, Type implementationType,

params

Type[] interceptorTypes

)

{

return

services.AddSbService(serviceType, implementationType, 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

AddSbScoped

(

this

IServiceCollection services, Type serviceType, Type implementationType,

params

Type[] interceptorTypes

)

{

return

services.AddSbService(serviceType, implementationType, 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

AddSbSingleton

(

this

IServiceCollection services, Type serviceType, Type implementationType,

params

Type[] interceptorTypes

)

{

return

services.AddSbService(serviceType, implementationType, 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;

object

impl =

null

;

var

qualifierAttribute = info.GetCustomAttribute();

if

(qualifierAttribute !=

null

) {

var

serviceName = qualifierAttribute.Name;

var

implList = provider.GetServices(propertyType);

foreach

(

var

tmpImpl

in

implList) {

if

(ProxyUtil.IsProxy(tmpImpl)) {

var

addition = tmpImpl

as

IDependencyAddition;

if

(addition?.Name == serviceName) { impl = tmpImpl;

break

; ; } } } }

else

{ 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

proxyGenerator = provider.GetService();

var

proxy = 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

AddSbTransient

(

this

IServiceCollection services, Type serviceType,

params

Type[] interceptorTypes

)

{

return

services.AddSbService(serviceType, ServiceLifetime.Transient, interceptorTypes); }

public

static

IServiceCollection AddSbScoped(

this

IServiceCollection services,

params

Type[] interceptorTypes) {

return

services.AddSbService(

typeof

(TService), ServiceLifetime.Scoped, interceptorTypes); }

public

static

IServiceCollection

AddSbScoped

(

this

IServiceCollection services, Type serviceType,

params

Type[] interceptorTypes

)

{

return

services.AddSbService(serviceType, ServiceLifetime.Scoped, interceptorTypes); }

public

static

IServiceCollection AddSbSingleton(

this

IServiceCollection services,

params

Type[] interceptorTypes) {

return

services.AddSbService(

typeof

(TService), ServiceLifetime.Singleton, interceptorTypes); }

public

static

IServiceCollection

AddSbSingleton

(

this

IServiceCollection services, Type serviceType,

params

Type[] interceptorTypes

)

{

return

services.AddSbService(serviceType, 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

proxyGenerator = provider.GetService();

var

proxy = proxyGenerator.CreateClassProxy(serviceType, interceptors.ToArray());

var

properties = serviceType.GetTypeInfo().DeclaredProperties;

foreach

(PropertyInfo info

in

properties) {

if

(info.GetCustomAttribute() !=

null

) {

var

propertyType = info.PropertyType;

object

impl =

null

;

var

qualifierAttribute = info.GetCustomAttribute();

if

(qualifierAttribute !=

null

) {

var

serviceName = qualifierAttribute.Name;

var

implList = provider.GetServices(propertyType);

foreach

(

var

tmpImpl

in

implList) {

if

(ProxyUtil.IsProxy(tmpImpl)) {

var

addition = tmpImpl

as

IDependencyAddition;

if

(addition?.Name == serviceName) { impl = tmpImpl;

break

; ; } } } }

else

{ 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; }

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; }

public

static

IServiceCollection

AddSbRepositoryService

(

this

IServiceCollection services,

params

Type[] interceptorTypes

)

{

var

types = AppDomain.CurrentDomain.GetAssemblies().SelectMany(it => it.GetTypes());

var

tableType = types.Where(it => it.GetCustomAttribute() !=

null

);

foreach

(

var

type

in

tableType) {

var

injectServiceType =

typeof

(IRepository<>).MakeGenericType(type);

var

injectImplType =

typeof

(BaseRepository<>).MakeGenericType(type); services.AddSbScoped(injectServiceType, injectImplType, interceptorTypes); }

return

services; }

public

static

IServiceCollection AddSbTransient(

this

IServiceCollection services,

string

name =

""

,

params

Type[] interceptorTypes) {

return

services.AddSbService(

typeof

(TService),

typeof

(TImplementation), ServiceLifetime.Transient, name, interceptorTypes); }

public

static

IServiceCollection

AddSbTransient

(

this

IServiceCollection services, Type serviceType, Type implementationType,

string

name =

""

,

params

Type[] interceptorTypes

)

{

return

services.AddSbService(serviceType, implementationType, ServiceLifetime.Transient, name, interceptorTypes); }

public

static

IServiceCollection AddSbScoped(

this

IServiceCollection services,

string

name =

""

,

params

Type[] interceptorTypes) {

return

services.AddSbService(

typeof

(TService),

typeof

(TImplementation), ServiceLifetime.Scoped, name, interceptorTypes); }

public

static

IServiceCollection

AddSbScoped

(

this

IServiceCollection services, Type serviceType, Type implementationType,

string

name =

""

,

params

Type[] interceptorTypes

)

{

return

services.AddSbService(serviceType, implementationType, ServiceLifetime.Scoped, name, interceptorTypes); }

public

static

IServiceCollection AddSbSingleton(

this

IServiceCollection services,

string

name =

""

,

params

Type[] interceptorTypes) {

return

services.AddSbService(

typeof

(TService),

typeof

(TImplementation), ServiceLifetime.Singleton, name, interceptorTypes); }

public

static

IServiceCollection

AddSbSingleton

(

this

IServiceCollection services, Type serviceType, Type implementationType,

string

name =

""

,

params

Type[] interceptorTypes

)

{

return

services.AddSbService(serviceType, implementationType, ServiceLifetime.Singleton, name, interceptorTypes); }

public

static

IServiceCollection

AddSbService

(

this

IServiceCollection services, Type serviceType, Type implementationType, ServiceLifetime lifetime,

string

name =

""

,

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;

object

impl =

null

;

var

qualifierAttribute = info.GetCustomAttribute();

if

(qualifierAttribute !=

null

) {

var

serviceName = qualifierAttribute.Name;

var

implList = provider.GetServices(propertyType);

foreach

(

var

tmpImpl

in

implList) {

if

(ProxyUtil.IsProxy(tmpImpl)) {

var

addition = tmpImpl

as

IDependencyAddition;

if

(addition?.Name == serviceName) { impl = tmpImpl;

break

; ; } } } }

else

{ 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

proxyGenerator = provider.GetService();

var

options =

new

ProxyGenerationOptions(); options.AddMixinInstance(

new

DependencyAddition() { Name = name });

var

proxy = proxyGenerator.CreateInterfaceProxyWithTarget(serviceType, target, options, interceptors.ToArray());

return

proxy; };

var

serviceDescriptor =

new

ServiceDescriptor(serviceType, Factory, lifetime); services.Add(serviceDescriptor);

return

services; } }/<code>

修改第一篇中自定义的控制器激活类SbControllerActivator,添加支持[QualifierAttribute]注解的逻辑。

<code> 

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

proxyGenerator = actionContext.HttpContext.RequestServices.GetService();

var

proxy = proxyGenerator.CreateClassProxyWithTarget(serviceType, target);

foreach

(PropertyInfo info

in

properties) {

if

(info.GetCustomAttribute() !=

null

) {

var

propertyType = info.PropertyType;

object

impl =

null

;

var

qualifierAttribute = info.GetCustomAttribute();

if

(qualifierAttribute !=

null

) {

var

serviceName = qualifierAttribute.Name;

var

implList = actionContext.HttpContext.RequestServices.GetServices(propertyType);

foreach

(

var

tmpImpl

in

implList) {

if

(ProxyUtil.IsProxy(tmpImpl)) {

var

addition = tmpImpl

as

IDependencyAddition;

if

(addition?.Name == serviceName) { impl = tmpImpl;

break

; ; } } } }

else

{ 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

)

{ } }/<code>

修改Startup.cs类里的ConfigureServices方法,可以看到,我们在注册WheelA和WheelB的时候,为服务添加了名称,并且随手添加了个拦截器,代码如下:

<code> 

public

void

ConfigureServices(IServiceCollection services) { services.Configure(

options

=>

{ options.CheckConsentNeeded =

context

=>

true

; options.MinimumSameSitePolicy = SameSiteMode.None; }); services.AddMvc() .SetCompatibilityVersion(CompatibilityVersion.Version_2_1) .AddSB(); services.AddSingleton(); services.AddSbScoped(

typeof

(TransactionalInterceptor)); services.AddSbScoped(); services.AddScoped(

typeof

(TransactionalInterceptor)); services.AddSbScoped(

typeof

(TransactionalInterceptor)); services.AddSbScoped(); services.AddSbRepositoryService(

typeof

(TransactionalInterceptor)); services.AddSbScoped(

typeof

(TransactionalInterceptor)); services.AddSbScoped(

"A轮胎"

,

typeof

(TransactionalInterceptor)); services.AddSbScoped(

"B轮胎"

,

typeof

(TransactionalInterceptor)); }/<code>

控制器类HomeController,代码如下:

<code>

public

class

HomeController

:

Controller

{ [ ]

public

ICar Car {

set

;

get

; } [ ]

public

IDistributedCache Cache {

set

;

get

; } [ ]

public

string

Description {

set

;

get

; } [ ]

public

IAddOilService AddOilService {

set

;

get

; }

public

IActionResult

Index

(

)

{

var

car = Car; AddOilService.AddOil(); Car.Fire(); Console.WriteLine(Description);

return

View(); } }/<code>

核心原理,从SummerBootExtentions类完整源码里单独抽取mixin服务注册和属性注入的代码,可以看到在生成动态代理的时候,在option里添加了mixin实例,也就是我们自定义的DependencyAddition,里面包含了这个服务的名称,在注入时,判断是否有[QualifierAttribute]注解,如果有,就获取整个接口的实现类列表,在循环里,判断是否为动态代理类,如果是,就把每个类实例强转为附加类接口IDependencyAddition,然后获取名称,如果名称和我们[QualifierAttribute]注解指定的名称相同,则注入。

<code> 

var

proxyGenerator = provider.GetService();

var

options =

new

ProxyGenerationOptions(); options.AddMixinInstance(

new

DependencyAddition() { Name = name });

var

proxy = proxyGenerator.CreateInterfaceProxyWithTarget(serviceType, target, options, interceptors.ToArray());/<code>
<code> 
                    

if

(info.GetCustomAttribute() !=

null

) {

var

propertyType = info.PropertyType;

object

impl =

null

;

var

qualifierAttribute = info.GetCustomAttribute();

if

(qualifierAttribute !=

null

) {

var

serviceName = qualifierAttribute.Name;

var

implList = provider.GetServices(propertyType);

foreach

(

var

tmpImpl

in

implList) {

if

(ProxyUtil.IsProxy(tmpImpl)) {

var

addition = tmpImpl

as

IDependencyAddition;

if

(addition?.Name == serviceName) { impl = tmpImpl;

break

; ; } } } }

else

{ impl = provider.GetService(propertyType); }

if

(impl !=

null

) { info.SetValue(target, impl); } }/<code>

2.效果图

先注入A轮胎

net core系列:一个接口多个实现类,利用mixin实现属性注入

从上图可以看到,生成的动态代理类里包含了Name这个属性,并且名称为A轮胎。

net core系列:一个接口多个实现类,利用mixin实现属性注入

控制台输出,也显示了“我是A轮胎,我正在滚”,符合预期。

注入B轮胎

net core系列:一个接口多个实现类,利用mixin实现属性注入

从上图可以看到,生成的动态代理类里包含了Name这个属性,并且名称为B轮胎。

net core系列:一个接口多个实现类,利用mixin实现属性注入

控制台输出,也显示了“我是B轮胎,我正在滚”,符合预期。

3. 写在最后

通过[Autowired]注解和[Qualifier("B轮胎")]注解联合使用,我们就实现了一个接口多个实现类的精准属性注入,代码看起来是不是舒服多了呢~,我始终相信,如果一件事我讲的别人听不明白,那一定是我讲的不够有趣,不够通俗易懂,所以大白话是必须的,如果文章还有哪里说得太深奥,欢迎给我指出来,同时net core里还有啥功能让你觉得用起来特别别扭的,请!一定!告诉我!,i will try it,因为这特别有趣,因为我一直践行的编程理念就是万物靠注入,万物可拦截,万物皆注解,始终相信IOC和AOP是构建软件的基石,哈哈哈哈哈。


原文来源:博客园

原文地址:https://www.cnblogs.com/hezp/p/11495147.html


分享到:


相關文章: