[转载]UrlRewrite.dll实现无后缀名的地址映射

mikel阅读(875)

[转载]UrlRewrite.dll实现无后缀名的地址映射 – 哲 思 – 博客园.

1 首先在iis里配置通配符应用程序映射,如下图:

(在通配符应用程序映射中添加.net的处理程序C:\WINDOWS\Microsoft.NET\Framework\v2.0.50727\aspnet_isapi.dll,具体操作请改为自己的文件位置)

2在UrlRewrite配置文件中配置

<RewriterRule>

<LookFor>~/shop</LookFor>

<SendTo>~/product/shop.aspx</SendTo>

</RewriterRule>

3 配置以后就可以实现www.xx.com/shop  映射到   www.xx.com/product/shop.aspx了。

用哲学的头脑去思考问题!

[转载]css中px和em的战争

mikel阅读(1020)

[转载]px和em的战争 – § 薄樱 § – 博客园.

好吧。我承认我标题党了。

其实我就是想说px和em的区别,网上资料其实一堆堆,但是还不如自己试验总结的清楚。前端高手可能觉得这玩意简单,但是作为小猫,还是好好学一下。

下面一点点说~~~

px和em都是长度单位,区别是,px的值是固定的,指定是多少就是多少,计算比较容易。em得值不是固定的,并且em会继承父级元素的字体大小。

任意浏览器的默认字体高都是16px。所以未经调整的浏览器都符合: 1em=16px。那么12px=0.75em, 10px=0.625em。

为了简化计算,在css中的body选择器中声明Font-size=62.5%,这就使em值变为16px*62.5%=10px, 这样12px=1.2em, 10px=1em, 也就是说只需要将你的原来的px数值除以10,然后换上em作为单位就行了。

就像这样:

<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<style>
body{Font-size:62.5%}
.div_px{font-size:10px; border:1px red solid;width:120px; height:20px}
.div_em{font-size:1em; border:0.1em red solid;width:12em; height:2em}

</style>
</head>

<body>
<div class="div_px" >1、这里是div_px</div><br/>

<div class="div_em" >2、这里是div_em</div><br/>

<br/>
</body>
</html>

效果如下:

这两个div基本是一样的。

但是,如果仅仅是这样,那就好办了。直接替换就好了。

‘em会继承父级元素的字体大小’这条,要了亲命了。。。

比如这样的一个页面:

<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<title>无标题文档</title>
<style>
body{Font-size:62.5%}

.div_out{font-size:1em; border:1px red solid;width:12em; height:4em  }
.div_in{ font-size:1em; border:1px blue solid;width:12em; height:4em  }
</style>
</head>

<body>

<div class="div_out" >
    3、这是div_out
    <div class="div_in" >
        4、这是div_in
    </div>
</div>
<br/><br/><br/><br/><br/><br/><br/><br/>
<div class="div_in" >
    5、这是在外面的div_in
</div>

</body>
</html>

这里有3个div,3和4是套在一起的,5是作对比的。

现在的效果是这样的:

恩,看起来这几个div效果是一样的,div_out和div_in的设定,font-size都是1em,所以继承没有表现出来。

通过检测,3和4的字体都是10px,长度都是122px,宽度都是42px。(设置了border,所以多了2px)

现在改一下:

.div_out{font-size:1.5em; border:1px red solid;width:12em; height:4em  }

效果是这样的

很明显,前两个div变大了,说明。。。
红框的div_out,由于font-size设置了1.5em,真实字体大小是10*1.5=15px,宽度12*10*1.5=180px;高度4*10*1.5=60px (没有计算border)

样式div_in没有做更改,font-size仍然是1em,但是嵌套在3里面的4号div和5效果不一样,4继承了div_out。

再来一个:

.div_out{font-size:1.5em; border:1px red solid;width:12em; height:4em  }
.div_in{font-size:1.5em; border:1px blue solid; width:12em; height:4em  }

这次把两个样式的font-size都设置为1.5em。看看效果。


这是为什么呢。。

3号div真实字体大小是10*1.5=15px,宽度12*10*1.5=180px;高度4*10*1.5=60px (没有计算border)

5号div真实字体大小是10*1.5=15px,宽度12*10*1.5=180px;高度4*10*1.5=60px (没有计算border)

4号div真实字体大小是10*1.5*1.5=15px,宽度12*10*1.5*1.5=270px;高度4*10*1.5*1.5=90px (没有计算border)

……………………………………………………………………………………………………………………………………………………………………………………………………………………

如果都用px,那么完全没有问题,你设置多少就是多少,不会让人计算的头晕。我平时都用px作为字号,宽度,高度等的单位,唯一用到em的就是字体缩进。

关于px和em各位还有什么要注意的,如果能告诉我,感激不尽。谢谢!

[转载]创建基于HTTP协议的SVN版本控制服务

mikel阅读(1113)

[转载]创建基于HTTP协议的SVN版本控制服务 – XueM – 博客园.

说明:本文没有讨论什么技术问题,仅仅是自己在使用SVN进行版本控制时的创建过程,或许有人对此不太熟悉,希望对这个感兴趣的有帮助。

虽说现在好多人都已经使用Git作为版本控制,Git在分支管理和性能上着实不错,但是要是办公环境里依然使用SVN,这个可能对新手快速上手就有些用处了。

准备:

操作系统:Debian 6

Web服务器:Apache2

SVN服务端:Subversion

SVN工具:Subversion-tools(可选)

支持SVN的Apache模块:libapache2-svn

1.安装软件:

sudo apt-get install apache2 subversion subversion-tools libapache2-svn

2.检查Apache是否安装好:

浏览器输入:http://localhost:80/ 是否有 It Works显示

3.检查Subversion是否安装成功:

终端进入一个新建的文件夹,运行明令:svnadmin create ./(命令:在当前文件夹下创建版本控制库)

查看文件夹内容是否包含 confdavdbhookslocks文件夹以及format、README.txt文件。

4.检查libapache-svn是否安装成功:

进入/etc/apache2/mods-availables文件夹,查看是否包含dav.load dav_svn.conf dav_svn.load文件。

5.创建版本控制库:

如上(3)中的命令,我们这样就可以创建一个版本库。本文中我们在/home/meego/repo/文件夹创建。

6.配置Apache服务,使用Apache访问版本控制库。

6.1进入/etc/apache2/(Apache2配置文件所在的文件夹),首先检查dav模块以及dav_svn模块是否已经启动。

Debian中管理Apache模块是否启动的方式是:模块是否存在与/etc/apache2/mods-enabled/文件夹中,所以只需在

/etc/apache2/mods-enabled中检查dav.load dav_svn.conf dav_svn.load这三个文件是否存在就可以。

如果不存在:将其从mods-availables中拷贝过来,或是创建链接。

创建链接命令:

sudo ln -s /etc/apache2/mods-availables/dav.load /etc/apache2/mods-enabled/dav.load;

sudo ln -s /etc/apache2/mods-availables/dav_svn.load /etc/apache2/mods-enabled/dav_svn.load;

sudo ln -s /etc/apache2/mods-availables/dav_svn.conf /etc/apache2/mods-enabled/dav_svn.conf;

6.2创建Apache站点

编辑/etc/apache2/mods-enabled/dav_svn.conf文件:

<Location /svn>
  DAV svn

  SVNPath /home/meego/repo

  AuthType Basic
  AuthName "Meego SVN Repo"
  AuthUserFile /etc/apache2/dav_svn.passwd

  AuthzSVNAccessFile /etc/apache2/dav_svn.authz

  Require valid-user
</Location>

注意上面的SVNPath要设置成你创建的版本库所在的文件夹。

6.3创建SVN用户、密码、SVN权限设置:

上面的站点文件中AuthUserFile即为SVN用户密码文件,其中密码使用MD5加密,这个文件的生成需要一个命令htpasswd。

htpasswd具体使用,可man一下。

现在创建一个用户:xuem(本文中使用的用户名)

htpasswd -c /etc/apache2/dav_svn.passwd xuem

注意:上面的xuem修改成你自己要创建的用户名。

执行命令后,输入用户密码即可。可以打开dav_svn.passwd文件看一下。

-c 参数表示创建新的dav_svn.passwd,所以创建第2个用户的时候,就不要加此参数。

上面站点配置中dav_svn.authz文件,就是SVN的权限设置,可以手动创建一个。

内容设置跟Subversion中conf文件下的authz文件配置方法一样:

[groups]
admin = xuem

[/]
* = r
@admin = rw

对于Authz文件的详细设置可以参考Subversion的文档。

7.配置完毕。

现在你可以在浏览器中输入:http://localhost:80/svn来访问你的版本库文件。

8.如果访问不成功:

8.1出现403错误:

这种错误主要是你的目录权限以及你的Apache运行用户的权限问题。再此说明Apache2运行用户要有你的版本控制库所在的文件夹的权限。

Debian下的Apache2运行用户是www-data.可以在/etc/apache2/envvars中设置:

分别是APACHE_RUN_USER和APACHE_RUN_GROUP项。

这里你可以设置成你创建版本控制库的用户。

或是将你的版本库的权限赋给www-data用户:

方法是终端运行:

sudo chown -R www-data:www-data /home/meego/repo

注意上面的路径改成你的版本库所在的路径。

8.2出现errorcode=’x’的错误:

这样的错误多数在于站点设置中设置的SVNPath不对,或是你设置了SVNParentPath而不是SVNPath。

SVNParentPath用于支持多个版本库的情况,如果只有一个版本库,只需设置SVNPath就足够了。

9.如果你还没有设置好,可以查看一下Apache的错误日志。

也可以留言,大家一起谈论。这些都不是技术上的问题,花太多的时间研究这个还不如去泡MM呢,设置好能用就行了。

没必要都研究一遍,希望此文可以为你节省一点时间。

XueM:www.cnblogs.com/daydayfree

[转载]ASP.NET MVC权限设计思考

mikel阅读(924)

转载.NET MVC权限设计思考 – 小城岁月 – 博客园.

在WebForm下我们一般会设计个PageBase继承Page,在OnInit方法中实现对基本权限的验证业务,然后所有的页面在继承 PageBase直接继承这项基本权验证业务。而在.NET MVC下我们如何再实现这个业务呢? 其实无非也是要设计一个ExtController基类来实现这个业务,而这个ExtController基类的权 限验证业务切入点选在哪里合适呢? 这个答案还要从前面的 了解.net MVC的实现原理Controller/Action 章节寻找。(标签属性IActionFilter, IAuthorizationFilter暂且不涉及)

一、寻找合适时机的切入点

简单的回顾一下这个过程,首先Controller中的Action要被执行,那Controller就要被实例化,接着才能根据请求的URL,调用对应 的Action。Controller是被MvcHandler的ProcessRequest或BeginProcessRequest方法的这段代码 创建的。

View Code

  private void ProcessRequestInit(HttpContextBase httpContext, out IController controller, out IControllerFactory factory) {
            // If request validation has already been enabled, make it lazy. This allows attributes like [HttpPost] (which looks
            // at Request.Form) to work correctly without triggering full validation.
            bool? isRequestValidationEnabled = ValidationUtility.IsValidationEnabled(HttpContext.Current);
            if (isRequestValidationEnabled == true) {
                ValidationUtility.EnableDynamicValidation(HttpContext.Current);
            }

            AddVersionHeader(httpContext);
            RemoveOptionalRoutingParameters();

            // Get the controller type
            string controllerName = RequestContext.RouteData.GetRequiredString("controller");

            // Instantiate the controller and call Execute
            factory = ControllerBuilder.GetControllerFactory();
            controller = factory.CreateController(RequestContext, controllerName);
            if (controller == null) {
                throw new InvalidOperationException(
                    String.Format(
                        CultureInfo.CurrentCulture,
                        MvcResources.ControllerBuilder_FactoryReturnedNull,
                        factory.GetType(),
                        controllerName));
            }
        }

相信很多人都通IOC容器重新实现过ControllerBuilder的DefaultControllerFactory

View Code

            ControllerBuilder.Current.SetControllerFactory(new UnityControllerFactory(ContainerFactory.GetContainer()));

通过上面代码我们可以知道Controller是在何时被创建的,我们第一次有机会拦截Controller对其做一定处理的时机,就是重新实现 ControllerBuilder的DefaultControllerFactory中的这个方法GetControllerInstance。我们 再继续寻找其它的切入点,当MvcHandler通过ControllerFactory创建了请求对应的Controller的实例后,会继续调用自己 的这个方法

View Code

  protected internal virtual void ProcessRequest(HttpContextBase httpContext) {
            SecurityUtil.ProcessInApplicationTrust(() => {
                IController controller;
                IControllerFactory factory;
                ProcessRequestInit(httpContext, out controller, out factory);

                try {
                    controller.Execute(RequestContext);
                }
                finally {
                    factory.ReleaseController(controller);
                }
            });
        }

执行IController接口的Execute方法,我们来看一下最终是执行的Controller.Execute->ControllerBase.Execute的方法

View Code

  protected virtual void Execute(RequestContext requestContext) {
            if (requestContext == null) {
                throw new ArgumentNullException("requestContext");
            }
            if (requestContext.HttpContext == null) {
                throw new ArgumentException(MvcResources.ControllerBase_CannotExecuteWithNullHttpContext, "requestContext");
            }

            VerifyExecuteCalledOnce();
            Initialize(requestContext);

            using (ScopeStorage.CreateTransientScope()) {
                ExecuteCore();
            }
        }

注意ControllerBase中Execute本身这个方法,以及其中的Initialize方法,ExecuteCore方法均是 Action被执行前的实现基本权限验证业务的切入点。

另外在Controller中ExecuteCore 方法被重载实现

View Code

 protected override void ExecuteCore() {
            // If code in this method needs to be updated, please also check the BeginExecuteCore() and
            // EndExecuteCore() methods of AsyncController to see if that code also must be updated.

            PossiblyLoadTempData();
            try {
                string actionName = RouteData.GetRequiredString("action");
                if (!ActionInvoker.InvokeAction(ControllerContext, actionName)) {
                    HandleUnknownAction(actionName);
                }
            }
            finally {
                PossiblySaveTempData();
            }
        }

其中ActionInvoker.InvokeAction方法是反射执行对应URL请求的Action,在这个方法,我们可以清楚看到当在Action被执行前,又分别先执行了Controller中的这个两个方法

View Code

 protected override void OnActionExecuting(System.Web.Mvc.ActionExecutingContext filterContext)
        {
            base.OnActionExecuting(filterContext);
        }

        protected override void OnAuthorization(System.Web.Mvc.AuthorizationContext filterContext)
        {
            base.OnAuthorization(filterContext);
        }

因此Controller的OnActionExecuting,OnAuthorization也可以做为Action 被执行前的权限验证切入点。好了,通过源码找到了这么多的切入点,那我们就分别来实现一下试试。

二、权限验证切入点的尝试

首先设计个继承Controller的基类BaseController,详细设计请参照注释

View Code

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace Demo.Mvc.Core
{
    /// <summary>
    /// 登录成功的时候被放入SESSION中
    /// </summary>
    public class UserState
    {
        public string PassCode { set; get; }
        public string UserId { set; get; }
        public string UserName { set; get; }

        //public List<Role> RoleCollection { set; get; }   //角色集
        //public List<Auth> AuthCollection { set; get; }   //权限集
    }

    public class BaseController:System.Web.Mvc.Controller 
    {
        /// <summary>
        /// 用户信息
        /// </summary>
        public UserState UserState { set; get; }

        /// <summary>
        /// 微软设计这个无参的构造的Controller 有利于使用IOC容器提高对象的创建效率
        /// 如果设计了System.Web.Routing.RequestContext参数,由于每次来的RequestContext都不相同
        /// 则Controller 就要不停的动态创建
        /// </summary>
        public BaseController()
        {
            //无参的构造
        }

        /// <summary>
        /// 改造一个构造函数切入点
        /// 这种方式虽然使得切入机会早,并且可以较早的构造中对业务层注入一些用户信息。
        /// 但是缺点就是每次都要动态反射(因为每次来的HttpContext请求都不相同)
        /// </summary>
        /// <param name="requestContext"></param>
        public BaseController(System.Web.Routing.RequestContext requestContext)
        {
            this.OnInit(requestContext);  //这样可以在构造的时候就切入了
        }

        /// <summary>
        /// 比较早的切入点 在ControllerFactory被创建的时候顺便就实现权限验证
        /// </summary>
        /// <param name="requestContext"></param>
        public virtual void OnInit(System.Web.Routing.RequestContext requestContext)
        {
            //这里实现用户信息的相关验证业务
            if (requestContext.HttpContext.Session["UserState"] != null)
            {
                UserState userState = requestContext.HttpContext.Session["UserState"] as UserState;
                string passCode = requestContext.HttpContext.Request.Cookies["UserState"].Value.Trim();

                string controllerName =  requestContext.RouteData.Values["controller"].ToString()+"Controller";
                string actionName = requestContext.RouteData.Values["action"].ToString();

               //判断有没有Action操作权限
               //userState.AuthCollection.Contains(controllerName + "/" + acitonName);
            }
            else
            {
                //非登录用户跳转
                //requestContext.HttpContext.Response.Redirect("/html/complex.html");
            }            
        }

        /// <summary>
        /// 比较晚的切入点 IController在执行Execute之后,Action被执行之前使用的
        /// </summary>
        public virtual void OnInit()
        {
            //这里实现用户信息的相关验证业务
            if (this.HttpContext.Session["UserState"] != null)
            {
                UserState userState = this.HttpContext.Session["UserState"] as UserState;
                string passCode = this.HttpContext.Request.Cookies["UserState"].Value.Trim();

                string controllerName = this.RouteData.Values["controller"].ToString() + "Controller";
                string actionName = this.RouteData.Values["action"].ToString();

                //实现Action操作权限验证业务
                //userState.AuthCollection.Contains(controllerName + "/" + acitonName);
            }
            else
            {
                //非登录用户跳转
                //requestContext.HttpContext.Response.Redirect("/html/complex.html");
            }
        }       
        

        protected override void Execute(System.Web.Routing.RequestContext requestContext)
        {             
            base.Execute(requestContext);
            //this.OnInit();//---------------------------------------------切入点
        }

        protected override void ExecuteCore()
        {            
            base.ExecuteCore();
            //this.OnInit();//---------------------------------------------切入点
        }

        protected override void Initialize(System.Web.Routing.RequestContext requestContext)
        {         
            base.Initialize(requestContext);
            this.OnInit();  //---------------------------------------------切入点 
        }

        //除上述的方式以下方式 
        //我们还可以使用IActionFilter, IAuthorizationFilter标签属性的方式实现权限验证 (这个不在本次讨研究范围内)
        protected override void OnActionExecuting(System.Web.Mvc.ActionExecutingContext filterContext)
        {            
            base.OnActionExecuting(filterContext);
            //this.OnInit();//---------------------------------------------切入点
        }

        protected override void OnAuthorization(System.Web.Mvc.AuthorizationContext filterContext)
        {
            base.OnAuthorization(filterContext);
            //this.OnInit();//---------------------------------------------切入点
        }
        
    }
}

里面列出了实现基类的中的多个切入点,其中第一个OnInit 方法的设计 是由于Controller在构造实例时 并没有合适的切入的点,所以通过RequestContext的注入可以使我们可以将切入点提到ControllerFactory中。 第二个OnInit的切入比较晚,都是在IController的 Execute被执行后,对应的Action被执行前。当然第二种情况已经完全满足了我们的业务需要,为什么还要第一种OnInit的设计?我们先来重构 一下ControllerFactory,改变默认的Controller的创建方式。

View Code

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Web.Mvc;
using System.Web.Routing;
using Microsoft.Practices.Unity;

namespace Demo.Mvc
{
    public class UnityControllerFactory : DefaultControllerFactory
    {
        private readonly IUnityContainer container;

        public UnityControllerFactory(IUnityContainer container)
        {
            //要做异常处理
            this.container = container;
        }

        protected override IController GetControllerInstance(RequestContext requestContext, Type controllerType)
        {             
            if (controllerType.IsSubclassOf(typeof(Core.BaseController)))
            {              
                //早期切入点 方式1,使用默认的无构造方式
                //目的必免直接反射Controller,提高效率
                Core.BaseController controller = container.Resolve(controllerType) as Core.BaseController;
                controller.OnInit(requestContext);  //切入点 
                
                //假如这个位置使用构造注入那OnInit就会构造函数执行时就实现相关验证业务包括UserState已经创建
                //这样做导致Controller每次都要被动态反射创建,但好处是可以使用权限切入点更早
                //这样权限业务可以更早的注入业务层中(如UserState传递至业务层)
                //代码省略
                
                return controller;
            }
            else
                return container.Resolve(controllerType) as IController;
        }

        public override IController CreateController(RequestContext requestContext, string controllerName)
        {            
            return base.CreateController(requestContext, controllerName);
        }
      
    }


}

我们再来实现个MenuController ,详细参照注释

View Code

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Web;
using System.Web.Mvc;
using System.ComponentModel;
using Demo.Model;
using Demo.Service.IService;
using Demo.Mvc.ViewObject;

namespace Demo.Mvc.Controller
{    
          
     
    [HandleError]
    public class MenuController:Core.BaseController
    {
        private IMenuService service;

        //如果我们ControllerFactory中使用构造生成Controller,那OnInit在构造的同时也成功获得了UserState
        //并且可以在这个时候将UserState注入到业务层中
        public MenuController(IMenuService service)
        {
             
            this.service = service;
            //this.service.UserState = this.UserState;   
            //这样做的优点就是不用再重载基类的Initialize方法,前提ControllerFactory中使用构造生成Controller,这样可以过早的拿到Session.
        }

        //由于在ControllerFactory无构造生成Controller,就会导OnInit的切入时机比较晚,这个时候UserState还是NULL(即还没有拿到HttpContext中的Session)
        //所以我们只能通过在重载一次基类的方式,才能获得已经创建的UserState信息
        protected override void Initialize(System.Web.Routing.RequestContext requestContext)
        {
            base.Initialize(requestContext);
            //this.service.UserState = this.UserState;    
        }

        public MenuController()
        { }

        public ActionResult ShowContent(object model)
        {
          //service.Update(model,this.UserState)   //用户信息传至业务层 通过业务层方法参数注入
            return View();
        }

        public ActionResult Index(UserVO userVo)
        {
            if (ModelState.IsValid)
            {
                ViewData["Key1"] = "TEST1";
                TempData["Key2"] = "TEST2";
            }
            else
            {
                 
            }
            return View();   //默认封装ViewResult返回   
        }

        public ActionResult DownLoadFile(string fileName)
        {
            return File(Server.MapPath(@"/Images/view.jpg"), @"image/gif");
        }

        public ActionResult ToOther(string fileName)
        {
            return Redirect(@"http://localhost:1847/Menu/ShowContent");
        }
       
    }    
     
}

以上就是对.NET MVC 基本权限验证实现的分析和模拟实现。如果.net mvc 的controller不用来实现业务,还要另外提取实现业务层的情况下,那本身对Controller的Action拦截是否还有意义呢? 因为对具体 业务方法拦截才是最终目的,这导致类似AOP的拦截框架是与业务层绑定的,因此只要针对业务层实现即可了,并且还可以重用。而此时.net mvc 在提供Controller对Action 的拦截机制似乎就变得多余了。我没必要对Action拦截一次,还要对业务层具体业务再拦截一次。大家对这个问题怎么看?希望分享一下自己的经验。

[转载]Lucene.Net, SQL Server 2008全文检索, Like模糊查询的一点心得

mikel阅读(1255)

[转载]Lucene.Net, SQL Server 2008全文检索, Like模糊查询的一点心得 – BobLiu – 博客园.

1.Like 模糊查询

例如: select * from table where productdes like ‘%cad%’

它不能利用索引查询, 只能是全表扫描, 效率较低, 但查询结果准确.

2.SQL Server 2008全文检索

为了提高效率, 换用SQL server的全文检索, 怎么建全文检索就此略去, 不提, 只看查询方法.

例如: select * from table where contains(ProductDesc, ‘*cad*’)

它查询的效率很高, 中文分词没有问题, 但缺点竟然是在英文查询上, 如果想模糊查询带cad三个字母的数据, 它只能搜索出以cad为完整单词的数据, 例如: 它能查出abc cad , cad def, 或者cad, 它不能查出autocad这种字母连在一起的数据, 也就是说, SQL server的全文检索的英文分词是空格.

3.Lucene全文检索

SQL server全文检索不灵了, 只能找第三方的方案了, 首当其冲的就是Lucene了, 但在.net下, Lucene却很不顺当. NLucene是将 Lucene 从 Java 移植到 .NET 的一个 SourceForge 项目,它从 Lucene 1.2 版本转化而来,
但2002年就停止更新了. 因为 NLucene 项目到2002年就没有再推出新的版本,可Lucene 却一直在发展,于是有人把Lucene 1.3版移植到.NET就成了Lucene .NET,但是Lucene .Net发展到2.0版的时候变成了商业化的产品,脱离了开源项目. 受到Lucene.Net脱离开源项目的影响,有人为了继续发展开源.Net搜索引擎,于是在Lucene.Net的原有基础上继续发展该项目,但是名字 改成了DotLucene以区别于Lucene.Net。但现在打开官网一看, 得, 又停止了. 看来, 只能用Lucene.Net2.0这最后一个开源版本了.

建索引, 查索引的代码如下:

private void btnCreateIndex_Click(object sender, EventArgs e)
{
StringBuilder sbSQL = new StringBuilder();
sbSQL.AppendLine(” Select ProductName, ProductDesc from Tbl_Product  “);
dt = DataHelper.RetrieveData(sbSQL.ToString());

//建立索引
IndexWriter writer = CreateIndex(dt);
}

public IndexWriter CreateIndex(DataTable dt)
{
IndexWriter writer = new IndexWriter(“c:/index/”, new StandardAnalyzer(), true);
try
{
//建立索引字段
foreach(DataRow dr in dt.Rows)
{
Document doc=new Document();
doc.Add(new Field(“ProductName”, dr[0].ToString(), Field.Store.YES, Field.Index.TOKENIZED));
doc.Add(new Field(“ProductDesc”, dr[1].ToString(), Field.Store.YES, Field.Index.TOKENIZED));
writer.AddDocument(doc);
}
writer.Optimize();
writer.Close();
}
catch(Exception e)
{
MessageBox.Show(e.Message);
}
return writer;
}

private void Search_Click(object sender, EventArgs e)
{
string key = this.textBox1.Text;

IndexSearcher search = new IndexSearcher(@”c:/index/”); //把刚才建立的索引取出来

Term t = new Term(“ProductDesc”, “*” + key + “*”);
WildcardQuery query = new WildcardQuery(t);

Hits hit = search.Search(query);

DataTable dt = new DataTable();
dt.Columns.Add(“ProductDesc”);

for (int i = 0; i <= hit.Length() – 1; i++)
{
DataRow dr = dt.NewRow();
dr[0] = hit.Doc(i).GetField(“ProductDesc”).StringValue();
dt.Rows.Add(dr);
}
this.dataGridView1.DataSource = dt;
}

关键是下面这两句:

Term t = new Term(“ProductDesc”, “*” + key + “*”);
WildcardQuery query = new WildcardQuery(t);

它通过WildcardQuery(通配符查询, 包括?和*) 实现了类似like的模糊查询, 这样, 就解决了上面like查得出但慢, SQLServer full-text search虽快但查不出来的问题.

但据说lucene建(更新)索引时不能查, 查时则不能建(更新)索引, 还没细看,如果是这样, 还是有些不爽.

4.全文索引和like语句比较

全文索引的执行效率高.一般全文索引使用的是倒排索引,能够支持多关键字的索引,而LIKE只有前缀匹配时才能使用索引,否则就是全表扫描,效率当然很低

当然是全文索引效率高。但全文索引存在填充问题,需要在增加内容后进行增量填充,否则检索不到新增的内容的。可以设置调度来自动运行。

5.倒排索引 inverted index

为什么Sql server全文检索和Lucune全文检索速度快呢, 因为它和大多数搜索引擎一样, 都使用了倒排索引 inverted index

从别人那里抄了例子, 如下:

假设有3篇文章:

文章1的内容为:I was a student, i came from chengdu.

文章2的内容为:My father is a teacher, and i am a student.

文章3的内容为:Chengdu is my hometown. my father’s hometown is wuhan.

Lucene是基于关键字索引和查询的。首先取得这3篇文章的关键字,通常需要如下处理措施(在Lucene中以下措施由Analyzer类完成):

1)需要把3篇文章的内容切分成一个个单词,作为索引关键字的候选集。英文中单词与单词之间有空格,分词比较好处理,但中文字与字之间没有空隔,是连在一起的,所以中文分词处理要麻烦。

2)需要将文章中分离出来的没有实际意义的单词过滤掉。比如a、from、and,中文中的“的”、“也”、“啊”等通常无具体含义的词可以去掉。还要过滤掉标点符号。

3)单词需要统一大小写。

4)查找与当前关键字相关联的其他关键字,即统一单词不同的时态。如“came”、“coming”统一成“come”,再进行查找。

经过上面处理后,3篇文章的所有关键字为:

文章1:[i] [am] [student] [i] [come] [chengdu]

文章2:[my] [father] [is] [teacher] [i] [am] [student]

文章3:[chengdu] [is] [my] [hometown] [my] [father] [hometown] [is] [wuhan]

有了关键字后,就可以建立倒排索引了。上面的对应关系是顺排的,即“文章号→关键字”,倒排处理为“关键字→文章号”。文章1、2、3经过倒排后变成如表5-1所示。

表5-1  倒排文件

i

12

am

12

student

12

come

1

chengdu

1,3

my

2,3

father

2,3

is

2,3

teacher

2

hometown

3

wuhan

3

最后推荐几个学习资料:

1.全文检索与Lucene学习
http://www.cnblogs.com/keith2011/archive/2011/09/12/2173984.html

2.Lucene:WildcardQuery
http://hi.baidu.com/lewutian/blog/item/2f5adafb89cac468034f56ed.html

3.lucene.net 查询与添加索引的同步问题
http://blog.csdn.net/poson/article/details/2201880

4.倒排文件索引(Inverted File Index)的建立(2)
http://book.51cto.com/art/200906/128320.htm

[转载]来源于WCF的设计模式:可扩展对象模式

mikel阅读(992)

[转载]来源于WCF的设计模式:可扩展对象模式 – Artech – 博客园.

我一直很喜欢剖析微软一些产品、框架的底层实现。在我看来,这不但让我们可以更加深入地 了解其运作的原理,同时也能提高设计/架构的技能。因为对于这些框架或者产品来说,高质量的设计是它们能够成功的一个最基本的因素。比如说比如 ASP.NET,不但能够支持传统的Web Form应用,MVC同样建立在它基础之上。比如说WCF,从其诞生的那一天开始,真个架构体系就从未改变。这些应用在这些产品和框架上的设计其实是最值 得我们学习的设计案例。比如说,今天我们介绍的“可扩展对象模式(Extensible Object Pattern)”就来源于WCF。[源代码从这里下载]

一、一个简单的“可扩展对象模式”的实现

为 了让这种所谓的“可扩展对象模式”有一个大概的了解,我们先来演示一个简单的例子。现在有一个表示房间的类型Room,它具有几个基本的属性Walls、 Windows和Door分别表示房间的墙、窗户和门。现在我们要来创建一个Room对象,即分别创建组成这个Room对象的各个构成元素。按照“大事化 小”这个基本的设计原则,我们分别创建相应的Builder来分别为Room构建相应的元素。按照“可扩展对象模式”的原理,Room对象就是一个可扩展 对象,而相应的Builder实现了对它的扩展。现在我们将Room这个类型定义在实现了接口 IExtensibleObject<Room>的可扩展对象。

   1: public class Room : IExtensibleObject<Room>
   2: {
   3:     public Room()
   4:     {
   5:         this.Extensions = new ExtensionCollection<Room>(this);
   6:     }
   7:     public Door Door { get; set; }
   8:     public IList<Wall> Walls { get; set; }
   9:     public Window Window { get; set; }
  10:     public IExtensionCollection<Room> Extensions { get; private set; }
  11: }
  12: public class Door{}
  13: public class Wall{}
  14: public class Window{}

为Room对象构建门、窗和墙的Builder(DoorBuilder、WindowBuilder和WallBuilder)均实现了相同的接口IExtension<Room>,表明它们都是针对Room的扩展。

   1: public class DoorBuilder : IExtension<Room>
   2: {
   3:     public Door Door { get; private set; }
   4:     public void Attach(Room owner)
   5:     {
   6:         owner.Door = this.Door = new Door();
   7:     }
   8:     public void Detach(Room owner)
   9:     {
  10:         if (this.Door == owner.Door)
  11:         {
  12:             owner.Door = null;
  13:             this.Door = null;
  14:         }
  15:     }
  16: }
  17:
  18: public class WindowBuilder : IExtension<Room>
  19: {
  20:     public Window Window { get; private set; }
  21:     public void Attach(Room owner)
  22:     {
  23:         owner.Window = this.Window = new Window();
  24:     }
  25:     public void Detach(Room owner)
  26:     {
  27:         if (this.Window == owner.Window)
  28:         {
  29:             owner.Window = null;
  30:             this.Window = null;
  31:         }
  32:     }
  33: }
  34:
  35: public class WallBuilder : IExtension<Room>
  36: {
  37:     public Wall[] Walls { get; private set;}
  38:     public void Attach(Room owner)
  39:     {
  40:         owner.Walls = this.Walls = new Wall[] { new Wall(), new Wall(), new Wall(), new Wall() };
  41:     }
  42:     public void Detach(Room owner)
  43:     {
  44:         if (null == owner.Walls || null== this.Walls)
  45:         {
  46:             this.Walls = null;
  47:             return;
  48:         }
  49:         Array.ForEach(this.Walls, wall =>
  50:             {
  51:                 if (owner.Walls.Contains(wall))
  52:                 {
  53:                     owner.Walls.Remove(wall);
  54:                 }
  55:             });
  56:         this.Walls = null;
  57:     }
  58: }

现在我们真正创建Room对象的程序写成如下形式:先创建可扩展对象Room,并将用于用于构建相应元素的三个Builder添加到以 Extensions属性表示的扩展集合中。经过这个简单的过程,一个完整的Room对象就已经被正常的构建了。这个简单的应用体现和很好的可扩展性:任 何针对Room对象的扩展都可以通过相应扩展对象(IExtension<Room>)来实现,如果我们需要,只需要将这些扩展添加到它的 Extensions集合中就可以了。

   1: Room room = new Room();
   2: room.Extensions.Add(new DoorBuilder());
   3: room.Extensions.Add(new WindowBuilder());
   4: room.Extensions.Add(new WallBuilder());
   5:
   6: Debug.Assert(room.Door != null, "Door has not been built!");
   7: Debug.Assert(room.Window != null, "Window has not been built!");
   8: Debug.Assert(room.Walls != null, "Walls have not been built!");

二、IExtensibleObject<T>和IExtension<T>

这个可扩展对象模式涉及到两个基本的接口,即IExtensibleObject<T>和IExtension<T>,前 者代表可扩展对象,后者代表对这个可扩展对象的扩展,而这个泛型参数T则代表定义成可扩展对象的类型。我想如果你是第一个接触者两个接口,看到这个介绍你 可能会觉得很晕,尤其是“可扩展对象类型T实现接口IExtensibleObject<T>”,不过多想想,应该会很快绕过弯子来的。

IExtensibleObject<T>接口仅仅定义了一个唯一的属性Extensions,而它类型是 IExteniosnCollection<T>代表针对可扩展对象类型T的扩展的集合。System.ServiceModel定义了具体 的集合类型ExteniosnCollection<T>实现了IExteniosnCollection<T>接口

   1: public interface IExtensibleObject<T> where T: IExtensibleObject<T>
   2: {
   3:     IExtensionCollection<T> Extensions { get; }
   4: }

而所谓的“针对可扩展对象类型T的扩展”就是实现了第二个接口IExtension<T>的对象。该接口具有两个方法Attach和 Detach,具体相同的参数owner。当我们将某个扩展对象添加到某个可扩展对象的Extensions集合中的时候,Attach方法会自动被调 用,而传入的参数owner就是这个需要被扩展的对象。与此相对,Detach则在当该扩展对象从可扩展对象的Extensions集合中移出的时候被调 用。

   1: public interface IExtension<T> where T: IExtensibleObject<T>
   2: {
   3:     void Attach(T owner);
   4:     void Detach(T owner);
   5: }

在前面的例子中,我们将DoorBuilder、WindowBuilder和WallBuilder实现 IExtension<Room>接口,将相应的针对门、窗和墙的构建实现在Attach方法中。所以当它们被作为对Room对象的扩展添加 到一个具体的Room对象的Extensions集合上的时候,这个Romm对象就分别有了一道门、一扇窗和四面墙。此外,由于被添加的Builder有 可能被移除,如果被移除后,先前被创建的门、窗和墙应该也一并移掉,而这些操作被定义在Detach方法中。

三、总结

在这里,我们将围绕着IExtensibleObject<T>和IExtension<T>这两个接口的设计方式说成一 种“设计模式”,可能不太妥当。实际上,任何存在扩展可能的类型都可以按照这样的方式来设计。而我们熟悉的一些设计模式都可以按照“可扩展对象”的方式来 设计。文中Room采用的涉及模式可以看成是Builder模式。

注:关于“可扩展对象模式”,李会军同学写了一篇很好的文章《技巧:使用可扩展对象模式扩展HttpApplication

[转载]Anthologize:利用WordPress轻松制作电子书!

mikel阅读(979)

[转载]Anthologize:利用Wordpress轻松制作电子书!.

WordPress 3.0有一个专门用来选辑文章制作电子书的plugin叫作Anthologize
Anthologize这个字,原意就是汇编、选辑成册的意思。
而支援WordPress 3.0的这个Anthologize外挂,正如它字面上的意思,就是用来装置在WordPress 3.0博客平台上,供作者从自己的博客里挑选文章,制作属于自己的电子书。
安装
Anthologize的安装,就跟其他所有WordPress外挂一样。
你可以在WordPress的管理介面里,选择「插件」>「新增插件」>「安装插件」,搜寻Anthologize之后自动安装。
或者,你也可以到Anthologize官网下载最新版本的压缩档,然后,再把解压之后的档案上传到「wp-content/plugins」手动安装。
使用
一旦在WordPress的管理介面里启用Anthologize这个插件,就可以开始使用Anthologize来制作电子书。
基本上,Anthologize把「Project」视为一本电子书,所以,使用Anthologize来制作电子书的第一个步骤就是「New Project」
同理,在Add New Project这个步骤必须填写的「Project Title」就是书名,「Subtitle」就是副标。
编辑
前面提到Anthologize把单本书称作「Project」,而每一本书里的章节,Anthologize则称之为「Part」。
在Anthologize的Project管理介面里,你博客的所有文章都被称作「Items」,你可以把它当作是「Part」的素材原料。
一如建立新书的第一个步骤是「New Project」,建立一个新章节的第一个步骤就是「New Part」。
「Part」建好之后,你就可以利用Anthologize所提供简单易用的拖拉介面,直接把「Items」里的文章拖拉到「Part」里。
管理
「Project」建立完成后,你随时可以到「My Project」去管理你已经建好的电子书专案。
输出
Anthologize提供输出4种电子书格式:
ePub
PDF
TEI (plus HTML)
RTF
以及Letter(215.9×279.4 mm)与A4(210×297 mm)两种尺寸。
由于Anthologize目前还在Alpha,很难说它是否会成为具影响力的电子书工具,但,至少它提供并实现了博客直接出书的便利可能。

[转载]10个最好的wordpress搜索引擎优化(SEO)插件

mikel阅读(915)

[转载]10个最好的wordpress搜索引擎优化(SEO)插件 – 孟晨 – 博客园.

搜索引擎喜欢博客,对于wordpress博客平台而言,你不需要具备一 些基本的SEO技能,wordpress对搜索引擎非常友好,它天生就是为不懂SEO的bloggers而设计的。本文不是为那些搜索引擎优化专家准备 的,如果你拥有自己的博客,你只希望专心的投入到博客的文章创新和写作中去,又不想在搜索引擎优化上面耗费你宝贵的时间和精力,那么本文为你介绍的20个wordpress SEO插件可以帮助你获得更多的流量和读者。

什么是Wordpress SEO插件?

WordPress SEO插件是简单的搜索引擎优化工具,它可以帮助你选择正确的tags,告诉搜索引擎蜘蛛如何在你的博客上工作,优化你的文章标题,以及优化你的博客内部链接……使用SEO插件可以让你无须考虑过多的搜索引擎优化细节和SEO技巧,同时它们能让你的博客构架对搜索引擎更加友好,从而让你的博客文章更快被搜索引擎索引,并获得不错的搜索引擎排名。

10个最好的WordPress搜索引擎优化插件

All in One SEO Pack – wordpress平台上最受欢迎的插件之一,该WordPress 插件可以帮助你选择最好的文章标题和关键词,以及让你避免发布重复内容。利用该插件,你可以给每篇页面添加独立的关键词和摘要,还可以防止存档页面被抓取以免被 Google 降权。

Automatic SEO Links – Automatic SEO Links允许你选择一个关键词或者文章标题来自动进行链接,比如我们可以设置“网络营销博客” 的自动链接URL为“http://zhengyong.net”,那么在你post的文章中出现“网络营销博客”这个词的时候,插件会将该关键词自动添 加链接。你可以为关键词设置为内部或者外部链接,设置锚文本,选择是否为链接添加 “nofollow” 等等。该插件还有个特色就是一篇文章中只会出现一次相同的自动链接,避免过度优化而造成搜索引擎惩罚。

Google XML Sitemaps – 个人认为这个是每个wordpress博客都必须的SEO插件工具。虽然说的是google xml站点地图生成工具,但实际上该插件生成的XML-sitmap也可以被ask,msn,yahoo等搜索引擎蜘蛛读取。

更多站点地图工具参考:Sitemap Tools – 免费的站点地图生成工具及插件介绍Google站点地图生成工具BETA版

HeadSpace2 – 该插件让你可以安装各种类型的meta-data,为页面添加特别的JavaScript和css代码,以及tags提示等,也是一个非常有用的SEO插件。

Meta Robots WordPress plugin – 可以为你博客的任何页面添加针对搜索引擎机器人的metadata。为页面和目录添加nofollow属性,这对一些利用web service的会员计划来建立的博客商店来说非常重要,可以避免搜索引擎索引重复内容。你还可以防止登陆页,归档页等被索引。

Nofollow Case by Case – 该插件让你去除在文章评论中的nofollow属性,你可以选择在哪个文章中支持评论的dofollow,避免全站都采用dofollow的评论,防止垃圾评论。

Platinum SEO Plugin – Platinum SEO Plugin 支持永久链接的自动301重定向,自动生成meta tags,以及post slug优化等,还可以帮助你避免副本内容。

Redirection – 在很多时候,你因为一些不同的原因需要对博客文章进行移动或者修改,这很容易造成原有页面在搜索引擎的排名下降。Redirection插件可以通过301重定向帮助你捕获404错误日志,因此你可以发现并修改这些错误,你还可以设置一个错误RSS feed。

SEO Blogroll – 如果你担心你的blogroll中的博客链接分享你的PageRank,那么使用SEOBlogroll,你可以对链接分组进行分别设置,并为一些链接组分配nofollow属性,避免该链接组中的博客分享你的网站权重。

SEO for Paged Comments– WordPress 2.7评论分页功能有效的降低了页面体积,增加页面载入速度,尤其对于留言较多的博客确实很实用,但WordPress默认的评论分页随着带来的SEO问 题–内容重复,正文页面与评论分页页面内容一样。该插件将评论分页页面的post正文显示摘要,再加上一个至post的permalink链接,从而区分 了正文页面,避免重复内容,完全达到了SEO的要求。

[转载]系统设计与架构笔记:键值对在架构设计里的应用

mikel阅读(753)

[转载]系统设计与架构笔记:键值对在架构设计里的应用 – 夏天的森林 – 博客园.

系统设计与架构笔记:键值对在架构设计里的应用

  1. 谈谈我对程序的理解

作为程序员你对程序是如何理解的?写这篇文章的时候,我认真思考了下,发现我对程序的理解不是和教科书一样的,我每次听到程序二字我想到的只有两个东西:代码和数据,而每次写程序的时候也就是写代码操作数据的过程。

程序开发和做菜很像,数据就是食材,代码就是厨艺,做出的软件就是一道菜了,至于这个菜好不好吃,到底是看食材还是看厨艺了?呵呵,当我抛出 这个问题的时候,我的第一反应是菜不好吃当然是手艺不好了,不知道其他童鞋是不是这么想的。认真想下,一道好菜一般都是二者必须兼备,当然不排除某一项突 出也可以达到同样的效果,但这种情况毕竟不是大众化,而是属于少数精英的,做软件也是如此,代码与数据是不可偏废的。这里我要提的是数据。

根据我的经验和知识(分类标准我一直想不太好,所以说是自己的经验和知识),我把数据分为两类:落地数据和不落地数据。

  1. 落地数据:就是被持久化的数据,这种数据一般放在硬盘或是其他的持久化存储设备里,例如:图片、系统日志、在页面上显示的数据以及保存在关系数据库里的数据等等,落地数据一定会有一个固定的载体,他们不会瞬时消失的。
  2. 不落地数据:一般指存储在内存或者是网络传输里的数据,这些数据是瞬时,使用完毕就会消失,例如:我们在浏览器发送给服务器的请求;从数据库读取出来的一直到页面展示前的数据等等。

写过程序的人都知道,程序里对这两种类型数据操作是有很大的不同的。

2.由javaEE的mvc设计模式谈起

java一个很重要的贡献就是推出了mvc设计模式,mvc其实应该按vcm顺序读最好。v及view,主要 是前台展示的页面;c及controller,控制层主要作用是接受前台页面数据,根据数据的不同调用后台不同的业务模型,同时业务模型处理好的数据也要 发送到controller,controller再分配给相应的前台页面;m及model,模型层专门负责操作业务模型。下图很好的表达了mvc的理 念:

随着mvc模式的发展,现在流行的架构:view(视图层)+controller(也叫action,控制层)+service(业务模型层)+DAO(数据访问对象层)+数据库的多层结构,如下图:

红线是用户的请求;蓝线是服务器响应用户的请求。红色和蓝线也代表各层数据传输的流向。

不管是传统的mvc模式还是现在流行的多层架构, 各个逻辑层传输的数据都是不落地数据。

在java项目里,一般我们都是传输javabean,而这些javabean都是程序员根据实际业务需求自己定义的,例如下面一个典型的javabean:

public class User {
    
    private String name = "";
    private String sex = "";
    private String age = "";
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    public String getSex() {
        return sex;
    }
    public void setSex(String sex) {
        this.sex = sex;
    }
    public String getAge() {
        return age;
    }
    public void setAge(String age) {
        this.age = age;
    }
    
    

}

各个逻辑层传输javabean对象的观念一定会深入很多java程序员的人心,当一个项目开始,程序员都会吭哧吭哧的创建javabean对象, 比如数据库表映射的javabean,在struts1.2里还有页面表达对应的javabean,做了这么多年这样的javabean,现在我就思考, 使用javabean真是最好的选择吗?

3.Nojavabean

我这里借用现在很流行的NoSQL的定义作为我文章第三部分的标题。javabean已经深入很多java程序员心理,甚至是很多程序员的习惯,其实它真的那么好用吗?真的那么包治百病吗?其实不尽然,下面我要列举它的缺点:

1.javabean的文件太多。一个java企业项目,都会使用orm,将数据库映射为java对象,一般一张表对应一个javabean,甚至 有些特殊情况还会归并一些表做一个统一的javabean,如果这个系统有1000张表也就会有不少于1000个javabean,在原始的 struts1.2里面还有和页面表单字段一一对应的javabean,大伙可以想象下,这些毫无技术含量的java类也许会像病毒一样去繁殖。

2.javabean会增强各层之间的耦合度。每个层里运算结束,业务数据大多会被封装成javabean对象,不同的业务封装的javabean 是不同的,mvc本来就是为逻辑层解耦做的设计,而传输介质却是包含业务逻辑,如果碰到业务层级的修改,各个层之间都会有相应的结构性修改,增加了维护的 成本。

3.我们至少会多维护一份数据字典。数据库里的某张表一个字段叫USER_NAME,那么到了javabean里可能就变成了userName,当 然我们在orm会有一个映射模型,但是随着数据一层层传递,离数据库越远的数据,数据的本质也就越模糊,特别会给新手产生一定误解,增加了软件开发和维护 成本。

4.这个是缺点是我的在实际项目里碰到的一个难题,我还没把它总结成一个概括性的缺陷,但我相信会有童鞋碰到类似的问题。我在一个项目里做权限管 理,因为某些数据的敏感性我们要做数据集的权限,比如某些人只能查看当天的数据,还有些人只能看当天数据前10条,另一些人只能看最近一个星期的数据,而 且只有三个字段能让他查看。由于权限管理是在该项目三期的任务,我们的权限设计是在原有的系统上进行开发,而且包含两个不同项目,其中一个项目是使用 javabean做传输介质,另一个我们用了另外的方式,最后这个javabean成了我们的梦魇,为了做一个通用解决方案,我们将用javabean作 为传输介质的项目重构了一下,增加了不少工作量。

5.mvc分层思想,其实是为了让专业的人做专业的事,擅长做页面的做页面,擅长逻辑的做逻辑,按mvc思想项目开发往往是横向分组的,但是实际开发中,没人敢这么分配工作,究其原因就是层与层直接传输的介质并不统一。

既然我的标题是Nojavabean,not only javabean,那么就像NoSQL能弥足SQL数据库的不足,以上的不足我们用什么技术来弥补了?

答案就是:键值对。

4.万能的MAP(键值对)

google的三篇论文带来了云时代,用博大的胸怀改变了世界,而这里面最关键的东西就是map(键值对),key-value模式(后面简称 kv)+数组我个人觉得基本可以表述这个世界的90%了。而且基本所有的语言都支持kv的数据结构,这件kv是多么强大的数据结构,世界就是被这个简单方 式而改变的。(本来我想谈谈kv在云计算存储和计算的作用,作为本文主题的补充,但是时间限制,大伙可以自己查阅下相关资料)。

我的替代数据模型就是用map作为数据传输介质,让我产生这个想法的源头是三个技术:mapreduce,ibatis和json;我们中心很重视 hadoop技术,我们这里经常做hadoop相关技术分享,听来听去都有什么mapreduce,都是map,map;而我们现在公司的项目都是用 ibatis做orm,当时表很多,我一时偷懒不是所有的ibatis配置文件都和javabean一一对应,只是返回一个map对象,如果是列表查询就 是List<Map>,没想这么做居然有意外的收获,因为页面数据我们都是封装成json对象,json其实也是一个键值对的模型,数据到了 action也是被传化成了map,所有返回值一样,居然提升了整个开发的效率,而且项目里面少了javabean对象,显的清爽多了。

下面我总结下map的好处,在文章第三部分写道了javabean的缺点,这些缺点倒过来看就是map的优点了,我这里就不再复述了:

1.不管什么技术从map取值的方式是统一的:例如在JavaScript里面json:

obj.XXX或者obj[XXX]

java里的map:

map.get(XXX)或map.put(XXX)

map取值方式是用key,key是字符串,是可以随时定义,而javabean是通过定义的方法读取,如果使用map会将传输数据的业务属性封闭在map内部,这样带来了操作的方便。

2.map技术几乎在所有的主流技术里面都有,那么用这种数据结构作为传输介质是跨平台的

3.map数据结构算法很成熟,做复杂计算是十分方便。

ok,写道这里吧,还有很多东西没讲清楚,写的太累了,明天还要起早赶火车,我的这个想法抛出来就是想听听大伙的意见,现在有点着迷map了,不知道将我们把各个逻辑层用map这种数据结构做传输是不是会带来更多的开发便利,很希望听到大家的意见。