.net企业级架构实战之2——Spring.net对象装配【精华】

之所以启用spring.net,看中的是它的容器功能:一个可以管理对象整个生命周期的容器。在这个容器内,我们加入各种对象的定义信息,让它们自动地装配(类似于乐高积木,定制化的拼合)、实例化、事务协作、回收销毁,以适应系统的需要。

  如前所述,spring.net基于配置运作。要让spring.net的容器感知我们编写的对象,需要做的就是在配置文件中声明它们。所谓配置,载体当然是xml文件,定义明晰,清清楚楚。
看一个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"
>
  
<!--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声明。
势必存在另一个配置,形如:

 


<?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"}这么潇洒的范式自然也是只能眼馋。没关系!自己扩展吧:

 


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页面需要。它长这样:

 


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目录下,形如:

 


<?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模块和处理程序,并指定配置上下文路径,装载日志系统:

 


<?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] &lt;%X{auth}&gt; - %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,在启动时开启日志记录:

 


    void Application_Start(object sender, EventArgs e) 
    {
        
// log4net
        log4net.Config.DOMConfigurator.Configure();
    }


run一下这个test.aspx,它会输出控制器名称"Artist",平平无奇,却是与一种方式的告别——Craig Walls们在《Action in Spring》的序中这样写道:开发者有一种宝贵的品质,那就是“懒惰”。这种懒惰激励开者努力用最小的开销找到最佳的解决方案。
其实,有时不需要那么勤劳的,有些东西用成熟的框架能很好的解决,何必自己造轮子呢?当然,我并不是来搞推销的,只是由衷的喜欢spring,仅此而已。

 

  至于web service页.asmx的注入,将会在以后交待,或者有兴趣也可以参看一下这个不错的中文文档:
http://files.cnblogs.com/moye/SpringNet_document.rar

  也可以常上官网看看:http://www.springframework.net/
抑或与同好们交流:http://www.springframework.cn/index.php

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值