最近在研究ABP项目,有关ABP的介绍请看阳光铭睿 博客,ABP的DI和AOP框架用的是Castle Windsor
下面就对Castle Windsor项目常用方法介绍和关于ABP的使用总结
1、下载Castle.Windsor所需要的dll,在程序包管理器控制台 运行Install-Package Castle.Windsor
下面先看个简单的例子
1
2
3
4
5
6
7
8
|
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也就是单例
1
2
3
|
container.Register(
Component.For<MyServiceImpl>()
);
|
给接口注册一个默认实例,这种做abp项目中应用很多
1
2
3
4
|
container.Register(
Component.For( typeof (IMyService)
.ImplementedBy( typeof (MyServiceImpl)
);
|
当然我们也可以指定注册的实例方式,主要有Transient和Singleton,Transient是每次请求都创建一个新实例,Singleton是单例,他们都是LifeStyle的属性
1
2
3
4
5
|
container.Register(
Component.For<IMyService>()
.ImplementedBy<MyServiceImpl>()
.LifeStyle.Transient
);
|
当注册一个接口有多个实例的时候,我们可以以命名的方式来注册,下面这个是没有重命名的情况下,默认是注册第一个MyServiceImpl的
1
2
3
4
|
container.Register(
Component.For<IMyService>().ImplementedBy<MyServiceImpl>(),
Component.For<IMyService>().ImplementedBy<OtherServiceImpl>()
);
|
比如Nop项目中的缓存,但是Nop项目是用Autofac,那么你反转的时候就可以根据名字进行反转了
1
2
|
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我们可以
1
2
3
|
container.Register(
Component.For<IMyService>().Named( "OtherServiceImpl" ).ImplementedBy<OtherServiceImpl>()
);
|
以上讲到了windsor项目中常用的最简单的注册方式,那么我们也可以按照程序集进行注册,比如根据当前程序集注册以IController为接口的实例
1
2
3
4
5
6
7
8
9
10
|
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代码
1
2
3
4
5
6
7
8
|
context.IocManager.IocContainer.Register(
Classes.FromAssembly(context.Assembly)
.IncludeNonPublicTypes()
.BasedOn<ISingletonDependency>()
.WithService.Self()
.WithService.DefaultInterfaces()
.LifestyleSingleton()
);
|
自定义注册
你也可以重写IWindsorInstaller方法Install进行统一注册
1
2
3
4
5
6
7
8
9
10
|
public class RepositoriesInstaller : IWindsorInstaller
{
public void Install(IWindsorContainer container, IConfigurationStore store)
{
container.Register(Classes.FromThisAssembly()
.Where(Component.IsInSameNamespaceAs<King>())
.WithService.DefaultInterfaces()
.LifestyleTransient());
}
}
|
构造函数&属性注入
构造函数和属性注入是项目开发的最佳实践,你可以使用去获取你的类的依赖关系。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
|
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注册控制器的代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
|
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,主要提供注册、反转、注销等操作
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
|
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 实现的类
由于篇幅关系 我就只展示注册控制器
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
|
/// <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是怎么做的吧
1
2
3
4
5
6
7
8
9
10
11
12
|
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的枚举进行相关注册
1
2
3
4
|
public void Register(Type type, DependencyLifeStyle lifeStyle = DependencyLifeStyle.Singleton)
{
IocContainer.Register(ApplyLifestyle(Component.For(type), lifeStyle));
}
|
另外Castle Windsor还支持日志,这点在ABP也有应用到,支持log4net等
首先需要下载相应的dll ,Install-Package Castle.Windsor-log4net
其次自动注册一个Log实例
1
2
3
4
5
6
7
|
public class LoggerInstaller : IWindsorInstaller
{
public void Install(IWindsorContainer container, IConfigurationStore store)
{
container.AddFacility<LoggingFacility>(f => f.UseLog4Net());
}
}
|
第三配置下log4net.config文件,下面是我的简单配置
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
|
<?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>
|
文章最后会附源码,包含配置等
最后我们就可以使用了,下面我用的是属性注入方式
1
2
3
4
5
6
7
8
9
10
11
12
13
|
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方法来进行拦截
1
2
3
4
5
6
7
8
9
|
namespace Castle.DynamicProxy
{
using System;
public interface IInterceptor
{
void Intercept(IInvocation invocation);
}
}
|
下面看下ABP最重要的一个拦截方法
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
|
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的方法都会被拦截
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
|
/// <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
时间: 2024-12-21 19:42:58