之所以启用spring.net,看中的是它的容器功能:一个可以管理对象整个生命周期的容器。在这个容器内,我们加入各种对象的定义信息,让它们自动地装配(类似于乐高积木,定制化的拼合)、实例化、事务协作、回收销毁,以适应系统的需要。
如前所述,spring.net基于配置运作。要让spring.net的容器感知我们编写的对象,需要做的就是在配置文件中声明它们。所谓配置,载体当然是xml文件,定义明晰,清清楚楚。 看一个xml配置片断:
Code
〈?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" > 〈!--controllers--> 〈object id="ArtistController" type ="woodigg.controllers.Controllers.ArtistControllers, woodigg.controllers"> 〈property name="ArtistDaoSpring" ref="ArtistDaoSpring" /> 〈property name="PhotoDaoSpring" ref="PhotoDaoSpring" /> 〈property name="MusicTypeDaoSpring" ref="MusicTypeDaoSpring" /> 〈property name="GenreDaoSpring" ref="GenreDaoSpring" /> 〈property name="StyleDaoSpring" ref="StyleDaoSpring" /> 〈property name="AlbumDaoSpring" ref="AlbumDaoSpring" /> 〈property name="TbrlSimilarDaoSpring" ref="TbrlSimilarDaoSpring" /> 〈property name="TbrlInfluceDaoSpring" ref="TbrlInfluceDaoSpring" /> 〈property name="TbrlFollowDaoSpring" ref="TbrlFollowDaoSpring" /> 〈property name="AreaDaoSpring" ref="AreaDaoSpring" /> 〈property name="TbrlGenreStyleDaoSpring" ref="TbrlGenreStyleDaoSpring" /> 〈property name="CompanyDaoSpring" ref="CompanyDaoSpring" /> 〈/object> 〈/objects>
解释一下:这是一个并不复杂的控制器类ArtistController,我们声明了它的id和type(类型,类在程序集中完整路径及程序集名)。 这样,spring.net的对象工厂(ObjectFactory或者ApplicationContext)在感知到对它的调用请求时,会实例化它并返回给需求方(可以是一个类,一个aspx页面,一个asmx,或者别的什么)——事实比这复杂得多,诸如你可以显式地声明它是单例的(加一个属性 singleton="true",鄙人用的spring.net版本为1.1.0.2),如此一来在spring.net容器生命周期内,它不会再有第二个实例,也许有时这会派上用场,能节省一些开销。 继续观察,〈object>节内会有一些〈property>声明,这是object的属性声明,name即它在内部对外公开的名称,其后可以使用value(值),ref(引用对象)或者expression(表达式)。此例使用的ref引用,意味着容器将干涉内政——把需要的对象注入到object的字段里,前提是:ref指定的对象存在于容器装载的配置文件集合中,并以相同的id声明。 势必存在另一个配置,形如:
Code
〈?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="ArtistDaoSpring" type="woodigg.DAO.ArtistDaoSpring,woodigg.DAO" autowire="byName" /> 〈object id="AdminDaoSpring" type="woodigg.DAO.AdminDaoSpring,woodigg.DAO" autowire="byName" /> 〈object id="CompanyDaoSpring" type="woodigg.DAO.CompanyDaoSpring,woodigg.DAO" autowire="byName" /> 〈object id="AreaDaoSpring" type="woodigg.DAO.AreaDaoSpring,woodigg.DAO" autowire="byName" /> 〈object id="AlbumDaoSpring" type="woodigg.DAO.AlbumDaoSpring,woodigg.DAO" autowire="byName" /> 〈object id="MusicTypeDaoSpring" type="woodigg.DAO.MusicTypeDaoSpring,woodigg.DAO" autowire="byName" /> 〈object id="GenreDaoSpring" type="woodigg.DAO.GenreDaoSpring,woodigg.DAO" autowire="byName" /> 〈object id="StyleDaoSpring" type="woodigg.DAO.StyleDaoSpring,woodigg.DAO" autowire="byName" /> 〈object id="FeelDaoSpring" type="woodigg.DAO.FeelDaoSpring,woodigg.DAO" autowire="byName" /> 〈object id="PhotoDaoSpring" type="woodigg.DAO.PhotoDaoSpring,woodigg.DAO" autowire="byName" /> 〈object id="TbrlSimilarDaoSpring" type="woodigg.DAO.TbrlSimilarDaoSpring,woodigg.DAO" autowire="byName" /> 〈object id="TbrlFollowDaoSpring" type="woodigg.DAO.TbrlFollowDaoSpring,woodigg.DAO" autowire="byName" /> 〈object id="TbrlInfluceDaoSpring" type="woodigg.DAO.TbrlInfluceDaoSpring,woodigg.DAO" autowire="byName" /> 〈object id="TbrlGenreStyleDaoSpring" type="woodigg.DAO.TbrlGenreStyleDaoSpring,woodigg.DAO" autowire="byName" /> 〈/objects>
在这个稍显简单的供求关系中,它们是供方,被需要、被别的对象引用,容器会在那个ref它们的对象(ArtistController)涉及到指定字段被使用时,提供其实例,比如ArtistController有个叫abc的方法中,用到了ArtistDaoSpring字段的defg()方法,容器接收到这一信息,装配ArtistDaoSpring字段对应的woodigg.DAO.ArtistDaoSpring对象——你也许会说,直接实例化不就完了么?呵呵,如果这个对象也是基于别的组件装配出来的,那就不仅仅只是实例化一下,还是需要重复一下这个过程,递归着来,搭积木一般把它装配成功交付需求方(像极了这个商业社会的产业链)。 在这里,我们还看到一个属性autowire,它可以有几种枚举选值(no,byName,byType,constuctor,autodetect,default),而byName的意思就是让容器按名称,自行查找同名的对象,进行装配(id和类中的字段同名)。看到这里,想必有人会说,你的第一个配置ArtistController,简直就是废话连篇,在object上指定autowire="byName"就行了,何必一个个指定property?bingo!如果不是为了说明供求关系,我才懒得写这些property,让容器查找吧。
ok,以上皆为务虚,来务实一把,装配一个简单.net对象(pono?)到一个.aspx页面耍耍。 这个简单对象RouteMap,是为 Asp.net MVC 框架服务的——由于还无福使用WIN2008+IIS7,只得在.net 2.0下奋战,对于new {action="Home",controller="Artist"}这么潇洒的范式自然也是只能眼馋。没关系!自己扩展吧:
Code
using System; /// 〈summary> /// Asp.net mvc RouteMap /// 〈/summary> namespace woodigg.controllers { public class RouteMap { private string _controller; private string _action; private string _id; public string Controller { get { return this ._controller; } set { this._controller = value; } } public string Action { get { return this ._action; } set { this._action = value; } } public string Id { get { return this ._id; } set { this._id = value; } } public RouteMap(string controller, string action, string id) { this._controller = controller; this._action = action; this._id = id; } } }
RouteMap将被一个叫test.aspx页面需要。它长这样:
Code
using System; using System.Data; using System.Configuration; using System.Collections; using System.Web; using System.Web.Security; using System.Web.UI; using System.Web.UI.WebControls; using System.Web.UI.WebControls.WebParts; using System.Web.UI.HtmlControls; public partial class test : System.Web.UI.Page { #region 注入对象 private woodigg.controllers.RouteMap _ArtistRoutmap; public woodigg.controllers.RouteMap ArtistRoutmap { get { return this ._ArtistRoutmap; } set { this._ArtistRoutmap = value; } } #endregion protected void Page_Load(object sender, EventArgs e) { Response.Write(ArtistRoutmap.Controller); } }
它会在页面加载完成时,输出这个RouteMap的控制器名称。至于这东西到底从哪来的值,没有说明。那么,就为这俩构造一个配置,由容器来装配它们。 新建一个routemap.xml文件,放在网站的~/config目录下,形如:
Code
〈?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" > 〈!--routemap--> 〈object id="ArtistRoutmap" type="woodigg.controllers.RouteMap,woodigg.controllers"> 〈constructor-arg name="controller" value="Artist" /> 〈constructor-arg name="action" value="Home" /> 〈constructor-arg name="id" value="3" /> 〈/object> 〈!--页面--> 〈object type="~/test.aspx" autowire="byName" /> 〈/objects>
ArtistRoutmap在被实例化时,我们希望它是有默认值的,在配置中逐一指定它构造函数参数项的值,这很好理解。 页面对象的声明更简单,只需说明它在站点中存在的路径,字段引用按名称查找吧。
累述至此,对象装配已经完成,但要让容器自动的运转起来,还需要在站点层面做些配置: 1、引用spring.net程序集及相关(以1.1.0.2为例,如有出入,稍为调整),包括:Spring.core.dll,Spring.web.dll,Spring.Web.Extensions.dll,log4net.dll; 2、修改web.config,装载Spring模块和处理程序,并指定配置上下文路径,装载日志系统:
Code
〈?xml version="1.0"?> 〈configuration> 〈configSections> 〈sectionGroup name="spring"> 〈section name="context" type="Spring.Context.Support.WebContextHandler, Spring.Web"/> 〈section name="objects" type="Spring.Context.Support.DefaultSectionHandler, Spring.Core"/> 〈/sectionGroup> 〈section name="SpringOverrideProperty" type="System.Configuration.NameValueSectionHandler"/> 〈section name="nhibernate" type="System.Configuration.NameValueSectionHandler, System, Version=1.0.5000.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"/> 〈!--log4net--> 〈section name="log4net" type="log4net.Config.Log4NetConfigurationSectionHandler,log4net"/> 〈/configSections> 〈SpringOverrideProperty> 〈add key="DbProvider.ConnectionString" value="Data Source=(local);Database=Music;User ID=sa;Password=system;Trusted_Connection=False"/> 〈add key="SystemInit.IsDebug" value="true"/> 〈add key="SystemInit.Level" value="4"/> 〈/SpringOverrideProperty> 〈!-- Spirng.Net 配置 --> 〈spring> 〈context> 〈resource uri="config://spring/objects"/> 〈resource uri="~/config/routemap.xml"/> 〈/context> 〈objects xmlns="http://www.springframework.net"/> 〈/spring> 〈log4net> 〈!-- log4net的配置节 --> 〈appender name="rollingFile" type="log4net.Appender.RollingFileAppender,log4net"> 〈param name="File" value="log//log_.txt"/> 〈param name="AppendToFile" value="true"/> 〈param name="RollingStyle" value="Date"/> 〈param name="DatePattern" value="yyyy.MM.dd"/> 〈param name="StaticLogFileName" value="true"/> 〈layout type="log4net.Layout.PatternLayout,log4net"> 〈param name="ConversionPattern" value="%d [%t] %-5p %c [%x] <%X{auth}> - %m%n"/> 〈/layout> 〈/appender> 〈root> 〈priority value="ALL"/> 〈level value="ERROR"/> 〈appender-ref ref="rollingFile"/> 〈/root> 〈/log4net> 〈system.web> 〈httpModules> 〈add name="Spring" type="Spring.Context.Support.WebSupportModule, Spring.Web"/> 〈/httpModules> 〈httpHandlers> 〈add verb="*" path="*.aspx" type="Spring.Web.Support.PageHandlerFactory, Spring.Web"/> 〈add verb="*" path="*.asmx" type="Spring.Web.Services.WebServiceHandlerFactory, Spring.Web"/> 〈/httpHandlers> 〈httpRuntime maxRequestLength="1000000" executionTimeout="300"/> 〈/system.web> 〈/configuration>
添一个全局配置Global.asax,在启动时开启日志记录:
Code
void Application_Start(object sender, EventArgs e) { // log4net log4net.Config.DOMConfigurator.Configure(); }
run一下这个test.aspx,它会输出控制器名称"Artist",平平无奇,却是与一种方式的告别——Craig Walls们在《Action in Spring》的序中这样写道:开发者有一种宝贵的品质,那就是“懒惰”。这种懒惰激励开者努力用最小的开销找到最佳的解决方案。 其实,有时不需要那么勤劳的,有些东西用成熟的框架能很好的解决,何必自己造轮子呢?