本文不讲这3大框架的内部实现原理,只是提供了一个demo,分别实现了它们实现注入属性、拦截消息的例子,以写日志为例,写日志方式用异步还是同步ILogType作为写日志方式,注入日志存放地方ILogStore来演示基本的aop、ioc功能。spring.net用的1.3.1,官网http://www.springframework.net/,castle windsor用的2.5.2,官网http://www.castleproject.org/,unity用的2.0,它是微软开源项目,可在http://unity.codeplex.com/下载,该demo用的这3大框架里的依赖注入容器均为最新版本,部分配置等实现方式有细微变化,中文资料很少找,我也作为入门学习,做一个技术备份吧。
之前对spring.net熟悉点,中文资料也比较多,比如刘冬的博客http://www.cnblogs.com/GoodHelper。spring.net项目里它封装和扩展了很多其他项目,比如常用的NHibernate,windows消息队列,作业调度,asmx,wcf服务等,castle项目也是相当的庞大,monorail,activerecord,windsor,dynamicproxy等,terrylee好几年前就写过系列博文activerecord和windsor的介绍,http://www.cnblogs.com/Terrylee/archive/2006/04/28/387503.html,unity是微软企业库EntLib发展中演变出来的依赖注入容器,artech的系列文章http://www.cnblogs.com/artech/tag/Unity/ kyo-yo的系列文章 http://www.cnblogs.com/kyo-yo/tag/Entlib/等都是相当有技术含量的。
先看下整个demo的结构:
本程序通过写日志的方式来说明注入过程,ILogType申明写日志的方式,IlogStore申明日志存放方式:
///
<summary>
///
http://lawson.cnblogs.com
///
</summary>
public
interface
ILogType
{
ILogStore Logs {
get
;
set
;}
void
Log(
string
content);
}
///
<summary>
///
http://lawson.cnblogs.com
///
</summary>
public
interface
ILogStore
{
void
Log(
string
content);
}
DirectLogType用来表示日志直接同步存放,ThreadLogType表示日志异步存放(这里简单处理直接new线程处理了),ConsoleLog表示日志存放控制台展现,TextLog表示日志用文本文档存放。
下面看Spring.net的代码,为了直接在Main函数看到所有代码,这里直接在Main里从容器获取对象:
Spring.net
static
void
Main(
string
[] args)
{
IApplicationContext ctx
=
ContextRegistry.GetContext();
ILogType logtype
=
(ILogType)ctx.GetObject(
"
LogType
"
);
//
ILogType logtype = SpringObjectManager.GetObject<ILogType>("LogType");
logtype.Log(
"
log spring test
"
);
Console.ReadKey();
}
输出结果如下:
从代码里看不到多余的代码,它是通过配置文件注入了consoleLog,并且添加了前置后置环绕通知实现的,配置文件如下:
App.config
<
configSections
>
<
sectionGroup name
=
"
spring
"
>
<
section name
=
"
context
"
type
=
"
Spring.Context.Support.ContextHandler, Spring.Core
"
/>
<
section name
=
"
objects
"
type
=
"
Spring.Context.Support.DefaultSectionHandler, Spring.Core
"
/>
</
sectionGroup
>
</
configSections
>
<
spring
>
<
context
>
<
resource uri
=
"
file://objects.xml
"
/>
</
context
>
</
spring
>
objects.xml
<?
xml version="1.0" encoding="utf-8"
?>
<
objects
xmlns
="http://www.springframework.net"
xmlns:xsi
="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation
="http://www.springframework.net
http://www.springframework.net/xsd/spring-objects.xsd"
>
<
object
id
="LogType"
type
="Common.ThreadLogType, Common"
>
<!--
singleton="false" lazy-init="false"
-->
<
property
name
="Logs"
ref
="ConsoleLog"
/>
</
object
>
<
object
id
="ConsoleLog"
type
="Spring.Aop.Framework.ProxyFactoryObject"
>
<
property
name
="Target"
>
<
object
type
="Common.ConsoleLog, Common"
>
</
object
>
</
property
>
<
property
name
="InterceptorNames"
>
<
list
>
<
value
>
beforeAdvice
</
value
>
<
value
>
aroundAdvice
</
value
>
<
value
>
afterAdvice
</
value
>
<
value
>
throwsAdvice
</
value
>
</
list
>
</
property
>
</
object
>
<
object
id
="beforeAdvice"
type
="SpringNetConsole.Advice.BeforeAdvice, SpringNetConsole"
/>
<
object
id
="aroundAdvice"
type
="SpringNetConsole.Advice.InvokeAdvice, SpringNetConsole"
/>
<
object
id
="afterAdvice"
type
="SpringNetConsole.Advice.AfterAdvice, SpringNetConsole"
/>
<
object
id
="throwsAdvice"
type
="SpringNetConsole.Advice.ExceptionAdvice, SpringNetConsole"
/>
</
objects
>
spring.net的环绕通知通过配置后继承AopAlliance.Intercept.IMethodInterceptor接口的public object Invoke(IMethodInvocation invocation)即可拦截,前置后置通知继承Spring.Aop下的IMethodBeforeAdvice,IAfterReturningAdvice接口实现拦截,具体配置文件的说明可以查看相关文档或者spring.net专题的详细说明。
Castle的最新版本和以前有一定的差别,Castle.core已经封装了以前的DynamicProxy的代码,windsor在ioc方面的配置方式差不多,思路都一样的,引用其他对象,它的关键字符是$,如
<
component
id
="LogType"
type
="Common.ThreadLogType, Common"
service
="Common.ILogType, Common"
>
<
parameters
>
<
Logs
>
${Log}
</
Logs
>
</
parameters
>
</
component
>
它也有自己的环绕通知和前置后置通知,环绕通知Castle.DynamicProxy.IInterceptor,前置后置通知的拦截只在一个类里Castle.DynamicProxy.StandardInterceptor。
Unity用起来,和官方资料看起来,感觉更希望大家通过写代码的方式注入对象和拦截对象,但是我更喜欢配置文件注入的方式,因此也用配置文件来注入对象:
objects.xml
<?
xml version="1.0" encoding="utf-8"
?>
<
unity
xmlns
="http://schemas.microsoft.com/practices/2010/unity"
>
<
alias
alias
="singleton"
type
="Microsoft.Practices.Unity.ContainerControlledLifetimeManager, Microsoft.Practices.Unity"
/>
<
sectionExtension
type
="Microsoft.Practices.Unity.InterceptionExtension.Configuration.InterceptionConfigurationExtension,
Microsoft.Practices.Unity.Interception.Configuration"
/>
<
container
name
="abc"
>
<
extension
type
="Interception"
/>
<
register
type
="IInterceptionBehavior"
mapTo
="UnityConsole.Advice.MyInterceptionBehavior,UnityConsole"
name
="MyInterception"
></
register
>
<
register
type
="Common.ILogType, Common"
mapTo
="Common.ThreadLogType, Common"
>
<
property
name
="Logs"
>
<
dependency
name
="regLogs"
></
dependency
>
</
property
>
</
register
>
<
register
name
="regLogs"
type
="Common.ILogStore, Common"
mapTo
="Common.ConsoleLog, Common"
>
<
interceptionBehavior
name
="MyInterception"
/>
<
interceptor
type
="InterfaceInterceptor"
/>
</
register
>
</
container
>
</
unity
>
具体配置方式可以查询相关文档,有意思的是我没有发现它明确的前置后置通知,只有一个通用的环绕通知拦截,Microsoft.Practices.Unity.InterceptionExtension.IInterceptionBehavior接口内的public IMethodReturn Invoke(IMethodInvocation input, GetNextInterceptionBehaviorDelegate getNext),通过注入后,继承它可以拦截到消息的执行。
但可以通过继承它的Microsoft.Practices.Unity.UnityContainerExtension扩展,完成事件的拦截,如artech的文章-IoC+AOP的简单实现 :http://www.cnblogs.com/artech/archive/2010/09/01/1815221.html,或者国外的http://www.machinaaurum.com.br/blog/post/AOP-With-Unity-20.aspx3篇文章,这个我也在学习中,还不是很熟。
这3大框架都是相当庞大的系统,需要慢慢学习,根据项目需要应用他们,也许会发现更合理更合适的实现方式。本篇博文只是简单的对3大容器完成aop、ioc的简单讲解,由于我现在对他们也是了解皮毛,文章或者程序有不对的地方,欢迎提出,我也可以多学习学习。
本demo源代码如下:
/Files/Lawson/IOCTests-lawson.rar