最近在研究ABP項目,有關ABP的介紹請看陽光銘睿 博客,ABP的DI和AOP框架用的是Castle Windsor
下面就對Castle Windsor項目常用方法介紹和關於ABP的使用總結
1、下載Castle.Windsor所需要的dll,在程序包管理器控制台 運行Install-Package Castle.Windsor
下面先看個簡單的例子
var container = new WindsorContainer(); container.Register( Component.For(typeof(IMyService) .ImplementedBy(typeof(MyServiceImpl) ); //控制反轉 得到實例 var myService= container.Resolve<IMyService>();
我們首先創建了WindsorContainer然后注冊了MyServiceImpl以及它的接口,然后我們用容器創建了一個MyServiceImpl的實例
2、注冊 Castle.Windsor有很多方法來注冊你的類,下面一一介紹幾種注冊方法
常規注冊
我們可以使用Castle.MicroKernel.Registration.Component這個靜態類,的For方法進行注冊,返回一個 ComponentRegistration,就可以用他來進一步注冊
注冊一個類到容器,默認的注冊類型是Singleton也就是單例
container.Register(
Component.For<MyServiceImpl>()
);
給接口注冊一個默認實例,這種做abp項目中應用很多
container.Register(
Component.For(typeof(IMyService)
.ImplementedBy(typeof(MyServiceImpl)
);
當然我們也可以指定注冊的實例方式,主要有Transient和Singleton,Transient是每次請求都創建一個新實例,Singleton是單例,他們都是LifeStyle的屬性
container.Register(
Component.For<IMyService>()
.ImplementedBy<MyServiceImpl>()
.LifeStyle.Transient
);
當注冊一個接口有多個實例的時候,我們可以以命名的方式來注冊,下面這個是沒有重命名的情況下,默認是注冊第一個MyServiceImpl的
container.Register(
Component.For<IMyService>().ImplementedBy<MyServiceImpl>(),
Component.For<IMyService>().ImplementedBy<OtherServiceImpl>()
);
比如Nop項目中的緩存,但是Nop項目是用Autofac,那么你反轉的時候就可以根據名字進行反轉了
builder.RegisterType<MemoryCacheManager>().As<ICacheManager>().Named<ICacheManager>("nop_cache_static").SingleInstance();
builder.RegisterType<PerRequestCacheManager>().As<ICacheManager>().Named<ICacheManager>("nop_cache_per_request").InstancePerLifetimeScope();
在Castle Windsor我們可以
container.Register(
Component.For<IMyService>().Named("OtherServiceImpl").ImplementedBy<OtherServiceImpl>()
);
以上講到了windsor項目中常用的最簡單的注冊方式,那么我們也可以按照程序集進行注冊,比如根據當前程序集注冊以IController為接口的實例
public WindsorControllerFactory(IWindsorContainer container)
{
this.container = container;
var controllerTypes =
from t in Assembly.GetExecutingAssembly().GetTypes()
where typeof(IController).IsAssignableFrom(t)
select t;
foreach (var t in controllerTypes)
container.Register(Component.For(t).LifeStyle.Transient);
}
Assembly.GetExecutingAssembly()是獲取當前運行的程序集,或者你也可以這樣,下面是ABP代碼
context.IocManager.IocContainer.Register(
Classes.FromAssembly(context.Assembly)
.IncludeNonPublicTypes()
.BasedOn<ISingletonDependency>()
.WithService.Self()
.WithService.DefaultInterfaces()
.LifestyleSingleton()
);
自定義注冊
你也可以重寫IWindsorInstaller方法Install進行統一注冊
public class RepositoriesInstaller : IWindsorInstaller
{
public void Install(IWindsorContainer container, IConfigurationStore store)
{
container.Register(Classes.FromThisAssembly()
.Where(Component.IsInSameNamespaceAs<King>())
.WithService.DefaultInterfaces()
.LifestyleTransient());
}
}
構造函數&屬性注入
構造函數和屬性注入是項目開發的最佳實踐,你可以使用去獲取你的類的依賴關系。
public class PersonAppService
{
public ILogger Logger { get; set; }
private IPersonRepository _personRepository;
public PersonAppService(IPersonRepository personRepository)
{
_personRepository = personRepository;
Logger = NullLogger.Instance;
}
public void CreatePerson(string name, int age)
{
Logger.Debug("Inserting a new person to database with name = " + name);
var person = new Person { Name = name, Age = age };
_personRepository.Insert(person);
Logger.Debug("Successfully inserted!");
}
}
IPersonRepository 從構造函數注入, ILogger 實例從公共屬性注入。這樣, 你的代碼不會體現依賴注入系統。這是使用 DI 系統最適當的方式。
一般的控制器的話我們會統一注冊,下面是ABP注冊控制器的代碼
public class WindsorControllerFactory : DefaultControllerFactory
{
private readonly IKernel kernel;
public WindsorControllerFactory(IKernel kernel)
{
this.kernel = kernel;
}
public override void ReleaseController(IController controller)
{
kernel.ReleaseComponent(controller);
}
protected override IController GetControllerInstance(RequestContext requestContext, Type controllerType)
{
if (controllerType == null)
{
throw new HttpException(404, string.Format("The controller for path '{0}' could not be found.", requestContext.HttpContext.Request.Path));
}
return (IController)kernel.Resolve(controllerType);
}
}
采用構造函數的注入模式是一個完美的提供類的依賴關系的方式。通過這種方式, 只有提供了依賴你才能創建類的實例。 同時這也是一個強大的方式顯式地聲明,類需要什么樣的
依賴才能正確的工作。但是,在有些情況下,該類依賴於另一個類,但也可以沒有它。這通常是適用於橫切關注點(如日志記錄)。一個類可以沒有工作日志,但它可以寫日志如果你提供一個日志對象。
在這種情況下, 你可以定義依賴為公共屬性,而不是讓他們放在構造函數。---摘自abp中文文檔
好了,到了終於把Castle Windsor一些常用的注冊寫完了,上面主要還是講依賴注入,下面開始ABP的相關介紹
ABP定義了一個統一的注冊類IocManager,主要提供注冊、反轉、注銷等操作
public class IocManager : IIocManager
{
public static IocManager Instance { get; private set; }
public IWindsorContainer IocContainer { get; private set; }
private readonly List<IConventionalDependencyRegistrar> _conventionalRegistrars;
static IocManager()
{
Instance = new IocManager();
}
public IocManager()
{
IocContainer = new WindsorContainer();
_conventionalRegistrars = new List<IConventionalDependencyRegistrar>();
//Register self!
IocContainer.Register(
Component.For<IocManager, IIocManager, IIocRegistrar, IIocResolver>().UsingFactoryMethod(() => this)
);
}
/// <summary>
/// Registers types of given assembly by all conventional registrars. See <see cref="AddConventionalRegistrar"/> method.
/// </summary>
/// <param name="assembly">Assembly to register</param>
/// <param name="config">Additional configuration</param>
public void RegisterAssemblyByConvention(Assembly assembly, ConventionalRegistrationConfig config)
{
var context = new ConventionalRegistrationContext(assembly, this, config);
//這個循環還是要進行四個注冊,
foreach (var registerer in _conventionalRegistrars)
{
registerer.RegisterAssembly(context);
}
if (config.InstallInstallers)
{
IocContainer.Install(FromAssembly.Instance(assembly));
}
}
有個重要的方法是RegisterAssemblyByConvention會循環遍歷繼承IConventionalDependencyRegistrar接口的所有類,並進行注冊
查看ABP的代碼發現,實現該接口的主要有四個類,分別注冊DbContex類型,控制器和ApiController和注冊基於接口ITransientDependency和ISingletonDependency和IInterceptor 實現的類
由於篇幅關系 我就只展示注冊控制器
/// <summary>
/// Registers all MVC Controllers derived from <see cref="Controller"/>.
/// </summary>
public class ControllerConventionalRegistrar : IConventionalDependencyRegistrar
{
/// <inheritdoc/>
public void RegisterAssembly(IConventionalRegistrationContext context)
{
context.IocManager.IocContainer.Register(
Classes.FromAssembly(context.Assembly)
.BasedOn<Controller>()
.LifestyleTransient()
);
}
}
關於Castle.Windsor注冊有一個實體,一般我們常用的有Singleton(單例)和Transient(每次創建新對象)那么我們看下ABP是怎么做的吧
public enum DependencyLifeStyle
{
/// <summary>
/// Singleton object. Created a single object on first resolving
/// and same instance is used for subsequent resolves.
/// </summary>
Singleton,
/// <summary>
/// Transient object. Created one object for every resolving.
/// </summary>
Transient
}
定義了一個關於LifeStyle的枚舉進行相關注冊
public void Register(Type type, DependencyLifeStyle lifeStyle = DependencyLifeStyle.Singleton)
{
IocContainer.Register(ApplyLifestyle(Component.For(type), lifeStyle));
}
public class LoggerInstaller : IWindsorInstaller
{
public void Install(IWindsorContainer container, IConfigurationStore store)
{
container.AddFacility<LoggingFacility>(f => f.UseLog4Net());
}
}
第三配置下log4net.config文件,下面是我的簡單配置
<?xml version="1.0" encoding="utf-8"?>
<configuration>
<configSections>
<!--日志配置部分-->
<section name="log4net" type="log4net.Config.Log4NetConfigurationSectionHandler,log4net" />
</configSections>
<log4net>
<root>
<priority value="All" />
<appender-ref ref="FileAppender" />
<appender-ref ref="InfoLoging" />
</root>
<appender name="FileAppender" type="log4net.Appender.RollingFileAppender">
<file value="log\\log.txt" />
<appendToFile value="true" />
<maxSizeRollBackups value="10" />
<maximumFileSize value="10000KB" />
<rollingStyle value="Size" />
<staticLogFileName value="true" />
<filter type="log4net.Filter.LevelRangeFilter">
<levelMin value="ERROR" />
<levelMax value="ERROR" />
</filter>
<lockingModel type="log4net.Appender.FileAppender+MinimalLock" />
<layout type="log4net.Layout.PatternLayout">
<conversionPattern value="%date [%thread] %-5level %logger [%property{NDC}] - %message%newline" />
</layout>
</appender>
<appender name="InfoLoging" type="log4net.Appender.RollingFileAppender">
<file value="log\\logData.txt" />
<appendToFile value="true" />
<maxSizeRollBackups value="10" />
<maximumFileSize value="10000KB" />
<rollingStyle value="Size" />
<staticLogFileName value="true" />
<filter type="log4net.Filter.LevelRangeFilter">
<levelMin value="INFO" />
<levelMax value="INFO" />
</filter>
<lockingModel type="log4net.Appender.FileAppender+MinimalLock" />
<layout type="log4net.Layout.PatternLayout">
<conversionPattern value="%date [%thread] %-5level %logger [%property{NDC}] - %message%newline" />
</layout>
</appender>
</log4net>
</configuration>
文章最后會附源碼,包含配置等
最后我們就可以使用了,下面我用的是屬性注入方式
public class AccountController : Controller
{
public ILogger Logger { get; set; }
public AccountController()
{
Logger = NullLogger.Instance;
}
public ActionResult LogOn()
{
Logger.Error("test");
return View();
}
}
當然ABP也是提供這種方式用log4net日志的,但是它驅動的時候是在Global中配置
上面是Castle Windsor的IOC的應用,當然在ABP中也有用到其AOP方法,我們可以繼承IInterceptor的Intercept方法來進行攔截
namespace Castle.DynamicProxy
{
using System;
public interface IInterceptor
{
void Intercept(IInvocation invocation);
}
}
下面看下ABP最重要的一個攔截方法
internal class UnitOfWorkInterceptor : IInterceptor
{
private readonly IUnitOfWorkManager _unitOfWorkManager;
public UnitOfWorkInterceptor(IUnitOfWorkManager unitOfWorkManager)
{
_unitOfWorkManager = unitOfWorkManager;
}
/// <summary>
/// Intercepts a method.
/// </summary>
/// <param name="invocation">Method invocation arguments</param>
public void Intercept(IInvocation invocation)
{
if (_unitOfWorkManager.Current != null)
{
//Continue with current uow
invocation.Proceed();
return;
}
var unitOfWorkAttr = UnitOfWorkAttribute.GetUnitOfWorkAttributeOrNull(invocation.MethodInvocationTarget);
if (unitOfWorkAttr == null || unitOfWorkAttr.IsDisabled)
{
//No need to a uow
invocation.Proceed();
return;
}
//No current uow, run a new one
PerformUow(invocation, unitOfWorkAttr.CreateOptions());
}
它的注冊是在模塊中進行注冊的,注冊主要包含IRepository和IApplicationService和UnitOfWorkAttribute,所以包含[UnitOfWork]的方法和繼承IRepository和IApplicationService的方法都會被攔截
/// <summary>
/// 攔截注冊事件
/// </summary>
/// <param name="key"></param>
/// <param name="handler"></param>
private static void ComponentRegistered(string key, IHandler handler)
{
if (UnitOfWorkHelper.IsConventionalUowClass(handler.ComponentModel.Implementation))
{
//Intercept all methods of all repositories.
handler.ComponentModel.Interceptors.Add(new InterceptorReference(typeof(UnitOfWorkInterceptor)));
}
else if (handler.ComponentModel.Implementation.GetMethods(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic).Any(UnitOfWorkHelper.HasUnitOfWorkAttribute))
{
//Intercept all methods of classes those have at least one method that has UnitOfWork attribute.
//TODO: Intecept only UnitOfWork methods, not other methods!
handler.ComponentModel.Interceptors.Add(new InterceptorReference(typeof(UnitOfWorkInterceptor)));
}
}
以上就把Castle Windsor的常用功能和ABP項目中的使用簡單的講完了。主要參考的幾個項目ABP、NOP、Prodinner
簡單的源碼地址:http://pan.baidu.com/s/1kTCNpQZ
參考文章:
https://github.com/ABPFrameWorkGroup/AbpDocument2Chinese
https://github.com/castleproject/Windsor/blob/master/docs/README.md
http://www.cnblogs.com/wucg/archive/2012/03/09/2387946.html
