我现在每天花一些时间来跟踪其源代码的变化。spring.net代码质量相当令人满意,现在每天 cvs都有一些检入。在cvs中,Spring.Services 项目已经创建 ,Spring.Data项目已经创建,近几天,一个Spring.Pool的namespace已经加入的Spring.Core项目,用来实现对象池的功能。
一个讨论spring.web的实现建议的邮件也相当有意思 sourceforge.net/mailarchive/forum.php?thread_id=5946064&forum_id=40049
很早以前,我在blog上介绍spring container功能,同那时相比,工程有了很大的变化,iesi.collections.dll现在已经合并到Spring.Core项目中,命名空间是Spring.Collections. Spring.Collections 主要解决了.net 中没有Set的缺陷
现在Spring.Context项目已经完成,Spring.Context是Spring.Objects的扩展和延伸,除了①对象工厂(容器)功能外(避免singleton的滥用,实现IListableObjectFactory接口)
Spring.Context更提供以下多个功能
②支持国际化
③资源访问
④事件传播
在使用上,通常建议直接使用IApplicationContext,而不是IObjectFactory,IObjectFactory比IApplicationContext而言主要适用仅使用容器功能的场合
这次的修订以介绍Spring.Context为主,依次介绍其容器、国际化、资源访问,事件传播。这是第一篇,介绍其容器功能。我的文笔很差,如果有行文不通的地方,希望大家指出,我会修改。
下面有些词汇可能会混用,先在这里注明
快速开始
从 cvs中下载源代码, sourceforge.net/projects/springnet/ 或从 .net 评测网 下载
1、编译
2、新建项目,引用log4net.dll,和spring.core.dll,Spring.Context.dll
3、创建一个简单的Person类,具有两个属性 Name,Address;
public class Person { public Person(String name,String address) { this._name=name; this._address=address; } private String _name; private String _address; public String Name { get{return this._name;} set{this._name=value;} } public String Address { set{this._address=value;} get{return this._address;} } }
4、创建applicationcontext配置文件,现在你有多种方案
通过应用程序配置文件app.config
<?xml version="1.0" encoding="utf-8" ?> < <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> <!--您的对象定义 --> <object name="xx" type="xxx"/> </objects> </objects> </configuration>
利用单独的xml 文件
<?xml version="1.0" encoding="utf-8" ?> <objects> <!--您的对象定义 --> <object name="xx" type="xxx"/> </objects> </configuration>
如果你使用应用程序配置文件,一般可如下调用
IApplicationContext context=(IApplicationContext)System.Configuration.ConfigurationSettings.GetConfig("spring/context");
如果你使用单独的xml 文件,可如下调用
IApplicationContext context=(IApplicationContext)new XmlApplicationContext("applicationcontext.xml");
依赖注入
关于依赖注入的进一步信息可参阅这里, www.martinfowler.com/articles/injection.html (有gigix翻译的中文版本)。这里我不再解释其含义。
Spring 主要支持ioc 的两种模式setter和constructor,举例如下
构造函数注入
对象暴露一个或多个构造函数,容器调用相应的构造函数设置属性值
<object name="jjx" type="WindowsApplication1.Person,WindowsApplication1"> <constructor-arg description="Name"><value>jjx</value></constructor-arg> <constructor-arg description="Address"><value>zj</value></constructor-arg> </object>
set属性注入
对象暴露一个或多个setter属性,容器调用相应的属性设置属性值
<object name="jxb" type="WindowsApplication1.Person,WindowsApplication1"> <property name="Name"><value>jjx</value></property> <property name="Address"><value>zj</value></property> </object>
Contextualized Dependency Lookup
这是通过实现IApplicationContextAware 接口,组件一旦实现该接口,就可以得到一个IApplicationContext 接口的实例,则可以通过其获取其它组件
public class SaleOrderService : IApplicationContextAware { private SaleOrderDao dao; public IApplicationContext ApplicationContext { set { dao=(SaleOrderDao)value.GetObject("saleOrderDao"); } } }
建议:当前ApplicationObjectSupport已经加入到Spring.Context中,你可以直接继承它
上下文注入需要依赖于spring的特定接口,因此不建议使用
配置文件提示
关于配置文件的具体写法,请参照src\spring.core\objects\factory\spring-objects.dtd ,配置文件主要就是在描述对象是如何组装的
1、简单属性
一般使用value元素,如
<property name="zip"><value>315400</value></property>
2、Spring 支持的四种复杂的属性类型(map,list,set,name-values)
dictionary - IDictonary,如一个电话属性
代码:
private IDictionary _phone; public IDictionary Phone { get { return _phone; } set { this._phone = value; } }
xml描述:
<property name="Phone"> <dictionary> <entry key="home"> <value>xxxx</value> </entry> <entry key="mobile"> <value>xxxxxx</value> </entry> </dictionary> </property>
list - 映射为IList ,也可以是数组,如一个Childrens属性
代码:
private IList _childrens; public IList Childrens { set { this._childrens = value; } get { return this._childrens; } }
xml 描述
<property name="Childrens"> <list> <ref object="jyf"></ref> </list> </property>
name-values .net 版本没有java 版本中的props,而是使用name-values,其对应的对象是System.Collections.Specialized.NameValueCollection,和map 不同的是其值为字符串
代码:
private NameValueCollection _books; public NameValueCollection Books { get { return this._books; } set { this._books = value; } }
xml 描述
<property name="Books"> <name-values> <add key="7-89494-030-5" value="Visual Basic.Net技术内幕"/> <add key="7-5083-2177-4" value="Essential .net"/> </name-values> </property>
注: spring.net创建了java Properties 对象的等价物Spring.Util.Properties
Set 实现Spring.Collections.ISet ,写法上类似list,但要求其中的对象必须不相同
3、null
使用null元素代表一个null
<property name="SaleOrderDao"><null/></property>
4、 引用对象
使用<ref object="xxx"/>或<ref local="xxx"/>
区别是local只从当前applicationcontext查询<ref local="object name"/>,object在当前ApplicationContext不存在的情况下会查询其父ApplicationContext
进阶话题
singleton
spring 容器中的对象默认是singleton的,如果要非singleton的对象,你需要将singleton attribute设置为false
<object name="xxx" class="xxx" singleton="false"/>
singleton这种设计模式在早先运用的较多,现在则是被批评的较多,因为singleton在多线程中需要注意一些问题(参见我的多线程一文)。而通过spring 的singleton,你可以像平时一样写代码,其它的交由spring去做。
对象生命周期
通过实现IInitializingeObject和IDisposable接口,替代的方法是使用init-method和destroy-method attribute,后者不需要显式实现接口
具体参见 Spring重要接口介绍 一文
lazy-init
仅针对设置了singleton="true"的对象而言,即在需要时才创建该对象,默认的spring在容器初始化时创建对象的实例(singleton)
SpringUtil 类
对于一个依赖于spring的项目而言,你需要为IApplicationContext创建一个全局引用
下面是一个简单实现(这里,我做了一些修改:首先IApplicationContext应该是线程共享的,当然线程不共享可以避免许多问题。第二应该尽量少在项目中引用IApplicationContext.第三:在spring.web项目中,一个WebApplicationContext.Current属性也可以直接使用
public class SpringUtil { private static IApplicationContext context=null; private SpringUtil() { } public static void Close() { if (context!=null) { AbstractApplicationContext c=(context as AbstractApplicationContext); if (c!=null) c.Close(); context=null; } } public static IApplicationContext Context { get { if (context==null) { lock(typeof(SpringUtil) { if (context!=null) context=new XmlApplicationContext("applicationcontext.xml"); } } return context; } } }