老司机带你玩Spring.Net -入门篇
网上有 Spring.Net 的相关的很多介绍的文章还有实践例子,推荐个还不错的博客 Spring.Net 学习笔记 。以前对 Spring.Net 算是有过一面之缘,但却迟迟未真正相识。在网上有太多的五花八门的文章来介绍它,讴歌它,也有很多的在线学习文档。很多人也尝试去学习这个神奇的框架,你会发现网上的资料虽然很多,但有些很枯燥的术语频繁的出现在文章中导致理解起来很吃力,这也让很多只是希望入门的人难以掌握而无奈半途而废。刚好在一个项目中需要整合这个框架,抽了点时间研究下,也算小有成果,今天偷着闲写个简单的示例来介绍下什么是 Spring.Net 、怎么去用它。
关于什么是 Spring.Net ,这里直接引用一段网上的介绍:
Spring.NET是一个应用程序框架,其目的是协助开发人员创建企业级的.NET应用程序。它提供了很多方面的功能,比如依赖注入、面向方面编程(AOP)、数据访问抽象及ASP.NET扩展等等。Spring.NET以Java版的Spring框架为基础,将Spring.Java的核心概念与思想移植到了.NET平台上。
企业级应用一般由多个物理层组成,每个物理层也经常划分为若干功能层。不同层次之间需要相互协作,例如,业务服务层一般需要使用数据访问层的对象来实现某个用例。不管应用程序如何构建,最终都会表现为一系列相互协作的对象,这些对象一起组成了完整的应用程序。所以我们说,应用程序中的对象之间相互具有依赖性。
.NET平台为构建应用程序提供了丰富的功能,从非常基础的基元类型和基础类库(以及定义新类的方法),到功能完善的应用程序服务器和Web框架,都有很好的支持。但.NET平台本身并没有提供任何方式来管理基础的应用模块并将它们组合为一个相互协作的整体,只能依靠架构师或开发人员去创建(一系列)应用程序。诚然,目前有很多设计模式可用于业务系统的设计,我们可以使用这些模式将各种类或对象组合成能够正常工作的完整应用。工厂、抽象工厂、Builder、装饰及服务定位器(Service Locator)等模式已被现今的软件开发行业广泛接受和采用(这也许正是这些模式最早被定型为模式的原因)。这些模式都非常好,但也不过是些已命名的最佳编程方法,在对这些模式的介绍中一般还会说明它们是作什么用的、最好应用到什么场合、可以解决什么问题等等。我们可以从许多书籍和wiki上找到这些模式,然后仔细研读,然后实现在我们自己的应用中。
Spring.NET的IoC容器所解决的,正是如何在企业应用中将类、对象和服务组合成应用程序的问题。IoC容器通过很正统(按:formalized,言下之意是这些方式都是已经定型了的、经过了业界多年考验的)的方式将分散的组件组合成完整的应用程序。Spring.NET框架所采用的,都是被业界无数应用程序考验多年的、已经被定型为设计模式的最佳编程方式,实际上,这些模式已经成为我们架构和开发时的法典,而通过Spring.NET,我们可以直接将它们整合到自己的应用程序中。目前已有很多组织和机构用Spring框架开发出了强壮的、维护性好的应用程序,这确实是一件非常好的事情。
上面这一坨其实阅读起来不会那么晦涩难懂,讲白了就是在说(吹) Spring.Net 是如何去实现业务层之间的解耦, Spring.Net 提供的功能(比如 DI 、IoC),其实从刚入门来说 DI、和 IoC 就是 Spring.Net 的核心体现,知道怎么用,就算是可以入门了,这个也是我写这篇文章的目的。
使用 Spring.Net 时需要去在配置文件(App.config)上进行配置,然后跟其它第三方框架一样需要引入相关的程序集(类库)。这里我创建一个 WinForm 项目来作为本次 Demo 的示例演示。创建完成项目之后,会产生一个 App.config 配置文件,把这个配置内容放到 <configuration></configuration> 节点之间
<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="config://spring/objects"/> </context> <objects xmlns="http://www.springframework.net"> <description>An example that demonstrates simple IoC features.</description> </objects> </spring>
这个配置主要是初始化 IApplicationContext ,其实就是初始化容器让我们可以根据这个容器通过代码获取到指定的对象。这里主要提下 Spring.Core ,Spring.Core 程序集是 Spring.Net 控制反转 (IoC,也叫依赖注入)功能实现的基础。
这里我们就把配置文件的内容给完成了,简单吧!现在我们引入 Common.Logging.dll / Spring.Core.dll 这两个程序集,然后就可以开始进行代码的实现了。
首先我们在 WinForm 窗体中添加一个按钮,双击按钮在后台代码加入创建容器的代码
IApplicationContext ctx = ContextRegistry.GetContext();
容器创建完成,接着我们分别创建 PersonService 类 和 接口类 IPersonService
PersonService 继承 IPersonService 接口,并实现内部的一个 Show() 方法,代码如下 :
public class PersonService:IPersonService { public void Show() { Console.WriteLine("Spring.Net Demo.."); } }
public interface IPersonService { void Show(); }
在没有使用 Spring.Net 之前我们在窗体后台代码调用 PersonService 都是需要先(New)实例化对象,再调用内部的方法
PersonService p = new PersonService(); p.Show();
现在我们使用 Spring.Net 的容器来创建对象。创建对象之前需要在配置文件中加入 <object></object> 节点对象
<objects xmlns="http://www.springframework.net"> <description>An example that demonstrates simple IoC features.</description> <object name="PersonService" type="SpringNetDemo.PersonService, SpringNetDemo"> </object> </objects>
加入这个对象节点之后,我们可以就可以通过以下代码来获取这个对象:
IPersonService personService =(IPersonService)ctx.GetObject("PersonService"); personService.Show();
从这段代码来看,你会发现其实这就是一种抽象工厂的实现。当然这个时候你可能会十分的好奇后面这种方式和实例化对象有什么本质上的区别,其实一开始我也是懵逼的。 先不急,我们先把这个问题留着,接着去探索下如何在类中实现属性注入。
我们在 PersonService 类中加入这些自定义属性
private PersonDal PersonDal { get; set; } private string ServerName { get; set; } private int Age { get; set; }
然后在配置文件 PersonService 的对象节点加入自定义属性
<property name="ServerName" value="许大虾"/>
这个时候 ServerName 这个属性值会经过容器初始化成 "许大虾" ,这样就完成了属性注入。那么问题就来了,在类中经常会使用构造函数来初始化值,这个时候不是很别扭了吗?别慌, Spring.Net 绝对的强大,现在我们来看下构造函数的实现。首先我们还是一样的在 App.config 中加入构造函数的属性
<constructor-arg name="age" value="18"/>
然后在 PersonService 类中加入一个构造函数
public PersonService(int age) { Age = age; }
这个时候就可以跟普通的属性注入一样对 age 的值进行初始化。到这边我们就可以把 Spring.Net 的容器 、IoC、 DI 都用了一遍了。最后我们还需要解决一个问题,如何进行复杂的属性注入。比如传统的三层架构都有 UI 层 、业务层,UI层一般都是通过依赖业务层对业务层的方法进行实例化调用,那能否直接通过 Spring.Net 的依赖注入把这两个业务层都进行彻底解耦呢?答案当然是可以的, Spring.Net 真是神一样的框架,实现这个自然是没问题。
我们建立一个新的类 PersonDal
public class PersonDal { public string Uname { get; set; } }
然后在 App.config 中加入这个对象
<object name="PersonDal" type="SpringNetDemo.PersonDal, SpringNetDemo"> <property name="Uname" value="许大虾666"/> </object>
这个时候还需要在 App.config 中的 PersonService 对象节点去关联 PersonDal
<property name="PersonDal" ref="PersonDal"/>
<object name="PersonService" type="SpringNetDemo.PersonService, SpringNetDemo"> <constructor-arg name="age" value="18"/><!--构造函数注入--> <property name="ServerName" value="许大虾"/><!--属性注入--> <property name="PersonDal" ref="PersonDal"/> </object>
接着在 PersonService 中去访问 PersonDal 里面这个自定义属性
Console.WriteLine(PersonDal.Uname);
最后来看下实际的 Demo 效果
这样就能够把这个复杂注入的问题给实现了!返回最初的问题,这个时候再去看这个问题就不是问题了。在刚入门的时候并不需要你能很深的把 Spring.Net 所有原理都掌握了,毕竟这也不太现实,只要把 Spring.Net 的容器,IoC,DI 都拿来实现一遍并且能体会出一些实际的应用场景那就是这篇文章的目的了。