分类 文章随笔 下的文章

树欲静而风不止

纠结.jpg

今天喝了很多酒,说了特别多的话,也许比整个星期说的话的总和还多。

一起共事的毛童鞋要走了,公司换了新的领导,出台了新的政策,要减少我们外包人员, 要么走要么转到本公司。于是,陆陆续续的几个人走了,那些曾经朝夕相处的人,曾经是满不在乎,爱答不理的人, 他们真的走的时候,我的心里竟然充满了忧伤,也许下一个就是我了。

毛童鞋是和我差不多的人,性格内向,不善言谈,但善良明事理,最重要的是对人真诚,似乎有了奇哥的影子,话说回来,奇哥哪去了,奇哥似乎消失了很长一段时间了,不知道在干什么?

肖童鞋走了,刘童鞋走了,张童鞋走了,现在毛童鞋也要走了,而我呢?何去何从是一个问题!

这段时间,似乎我很清闲,但内心却一直纠结于这个问题之中。留在这里继续写那些丑陋而千篇一律的代码,我可以继续过着稳定泰然的生活;离开这里,也许会找到更好的工作,也许找不到,也许还是写这样的代码,但我都将失去这种生活,将会开始早出晚归之旅,而好处是我将找一份可以写优美代码的工作,我热爱的工作。哪一点更重要呢?

Struts 2 的验证框架(一)

之前我们已经学习了如何通过Validateable接口的validate()方法实现动作本地的验证方式。虽然这种方式工作得很好,但是它的某些限制最终会变得难以负担。因此,在本节中我们将引入Struts 2框架的另一个高级机制——验证框架。数据验证框架提供了一个比Validateable接口更通用、更可维护的验证解决方案。验证框架中更强大的一点是Validator(验证器),它是一种可重用的组件,其中实现了特定类型的验证逻辑。

1.熟悉数据验证框架

数据验证框架早已成为了Web应用程序框架的一部分,但是Struts 2将它在优化、模块化、整洁继承方面带到了一个权限的水平。

1.1 验证框架的架构

下图展示了验证框架的主要组件:

Struts 2 验证框架.png

如上图所示,在验证框架中有3个主要的组件:域数据、验证元数据和验证器。在验证工作中每一个组件都扮演了至关重要的角色,我们将一一解释这些内容。

1.域数据

首先,必须有一些验证数据,这些数据以属性的方式驻留在Struts 2的动作中,数据来自对象或者模型驱动对象。

2.验证元数据

验证器和数据属性之间有一个中间组件,这个中间组件就是元数据,这些数据将每一个数据属性与属性运行时数据的合法性验证关联起来。一个属性可以关联多个验证器,也可以不关联任何验证器。元数据层可以使用XML文件或者Java注解将数据属性映射到验证器。

3.验证器

所有这些数据的验证实际上都有验证器完成。验证器是一个包含了执行某种细粒度的验证行动的逻辑的可重用组件。

1.2 Struts 2 工作流中的验证框架

现在看看所有的这些验证工作是如何完成的。验证框架实际上与基本数据验证共享了大部分功能,它使用ValidationAware接口存储错误信息。如果需要,workflow拦截器会将用户带回到input页面。实际上,唯一改变的是验证本身,但是这是一个重大的变化。

不管基本的验证示例还是验证框架都在Struts 2自带的defaultStack环境下工作。下面代码片段是跟当前话题相关的defaultStack部分,来自struts-default.xml文件:

<interceptor-ref name="params"/>
<interceptor-ref name="conversionError"/>
<interceptor-ref name="validation"/>
<interceptor-ref name="workflow"/>

我们需要注意validation拦截器,workflow拦截器调用validate()方法来实施基本验证还没有参与。当validation拦截器触发时,它会通过前一节提到的元数据实施所有已经定义的验证。一下是验证框架的工作流程,数据验证框架在数据转移、类型转换之后,workflow拦截器之前运行:

验证框架的工作流程.png

从图中可以看出整个流程有两个数据验证机制,一种是validation拦截器即数据验证框架的验证,一种是workflow调用validate()方法的验证。我们将会有两种选择,如果能够预测到一个验证逻辑将来还会被重用,那么使用一个自定义验证器来实现会更有意义。然而,如果验证逻辑确实是一个生僻的需求,并且很可能只适用于一次的情况,那么把它放在validate()方法中会更有意义。

2.将动作关联到验证框架

2.1 使用ActionClass-validations.xml声明验证元数据

现在,我们呢使用XML文件声明需要验证的元数据,文件名以:动作类名+-+validations.xml为规范,以下是Register-validation.xml:

<!DOCTYPE validators PUBLIC "-//OpenSymphony Group//XWork Validator 1.0.2//
    EN" "http://www.opensymphony.com/xwork/xwork-validator-1.0.2.dtd">
<validators>
    <field name="password">
        <field-validator type="requiredstring">
        <message>You must enter a value for password.</message>
        </field-validator>
    </field>
    <field name="username">
        <field-validator type="stringlength">
        <param name="maxLength">8</param>
        <param name="minLength">5</param>
        <message>While ${username} is a nice name, a valid username must
                be between ${minLength} and ${maxLength} characters long.
        </message>
        </field-validator>
    </field>
    <field name="portfolioName">
        <field-validator type="requiredstring">
        <message key="portfolioName.required"/>
        </field-validator>
    </field>
    <field name="email">
        <field-validator type="requiredstring">
        <message>You must enter a value for email.</message>
        </field-validator>
        <field-validator type="email">
        <message key="email.invalid"/>
        </field-validator>
    </field>
    <validator type="expression">
        <param name="expression">username != password</param>
        <message>Username and password can't be the same.</message>
    </validator>
</validators>

1.字段验证器

字段验证器是用来操作一个独立字段的验证器。这里的字段(field)与数据属性意思相同,字段验证器中的“字段”的含义是它们来源于请求的HTML表单的字段。

例如第一个字段password字段,一旦为数据声明了一个field元素,我们只需在field元素内部放入field-validator元素来声明哪些验证器用来验证此数据。对于password,我们只声明了一个requiredstring验证器。message元素包含验证失败时显示的消息的文本。一个field元素可以声明任意多个验证器。

2.非字段验证器

你也可以声明验证逻辑不是适用在某个特定字段的验证器,这些验证器适用于整个动作,通常包含对多个字段值的检查。例如:expression验证器就是如此。这个验证器使用OGNL比较其他两个字段是否相等,如果不相等表达式返回true,验证通过,否则,验证失败最终用户会返回输入页面并显示message元素中的内容。

3.消息元素的选择

message元素用来指定验证错误的情况下用户应该看到的消息,我们可以使用OGNL动态的组织消息。例如username字段的声明那样。XML中OGNL使用$作为转义字符,而不是OGNL通常使用的%符号。

message元素的另一个选择是将这些消息抽出到外部的资源文件中以实现验证错误的提示信息的国际化,portfolioName字段的验证方式是一个很好的示例,message 的 key 属性指向的正式国际化文件中的引用。如下:

user.exists=This user ${username} already exists.
portfolioName.required=You must enter a name for your initial portfolio.
email.invalid=Your email address was not a valid email address.

2.2 内建的验证器

框架自带了一系列功能强大的验证器以满足常见的验证需求,以下列出了全部的内建验证器:

数据验证器 参数 功能 类型
required 没有 检验值非空 字段
requiredstring trim(默认为true) 验证值非空,并且不是空字符串 字段
stringlength trim(默认值为true)、minLenth、maxLength 验证字符床的长度在参数指定的范围内。不指定的参数不做检查。 字段
int min、max 验证这个证书值在参数指定的最小值和最大值之间 字段
double minInclusive、maxInclusive、minExclusive、maxExclusive 验证浮点值在参数指定的范围内 字段
date min、max 验证日期值在指定的最小值和最大值之间。日期格式MM/DD/YYYY 字段
email 没有 验证电子邮件地址格式 字段
url 没有 验证URL格式 字段
fieldexpression expression(必须) 根据当前ValueStack解析OGNL表达式。表达式必须返回true或者false以决定验证是否成功 字段
expression expression(必须) 与fieldexpression相同,但用在动作级别 动作
visitor context、appendPrefix 将域对象属性的验证转交给域对象本地的验证声明 字段
regx expression(必须)、caseSensitive、trim 验证String遵循给定的正则表达式 字段

这些数据验证器中的大部分功能都很简单,唯一需要深入讨论的是visitor验证器,这个验证器允许你为每一个域模型(ModelDriven)类定义验证元素据。

3.编写自定义验证器

编写自定义验证器与编写任何其他的Struts 2组件都不同,下面我们将通过编写一个检查密码完整性的自定义验证器。

3.1 检查密码强度的自定义验证器

所有验证器必须实现Validator接口或者FieldValidator接口,前者将实现非字段验证器,后者将实现字段验证器。通常情况下我们会扩展对应的ValidatorSupport或者FieldValidatorSupport这两个类。

我们设计的密码验证器将做一下3项检查:

  • 密码必须包含一个大写字母或者小写字母;
  • 密码必须包含0~9的一个数字;
  • 密码必须至少包含一套特殊字符中的一个字符。

特殊字符有一个默认值,但可以通过一个参数配置,这与之前使用的stringlength参数很类似。以下是自定义验证器类代码:

public class PasswordIntegrityValidator extends FieldValidatorSupport {
    static Pattern digitPattern = Pattern.compile( "[0-9]");
    static Pattern letterPattern = Pattern.compile( "[a-zA-Z]");
    static Pattern specialCharsDefaultPattern = Pattern.compile( "!@#$");

    public void validate(Object object) throws ValidationException {
        String fieldName = getFieldName();
        String fieldValue = (String) getFieldValue(fieldName, object );
        fieldValue = fieldValue.trim();
        Matcher digitMatcher = digitPattern.matcher(fieldValue);
        Matcher letterMatcher = letterPattern.matcher(fieldValue);
        Matcher specialCharacterMatcher;
        if ( getSpecialCharacters() != null ){
            Pattern specialPattern = Pattern.compile("[" + getSpecialCharacters() + "]" );
            specialCharacterMatcher = specialPattern.matcher( fieldValue );
        } else{
            specialCharacterMatcher = specialCharsDefaultPattern.matcher( fieldValue );
        }
        if ( !digitMatcher.find() ) {
            addFieldError( fieldName, object );
        }else if ( !letterMatcher.find() ) {
            addFieldError( fieldName, object );
        }else if ( !specialCharacterMatcher.find() ) {
            addFieldError( fieldName, object );
        }
    }
    private String specialCharacters;
    //省略get和set方法
}

作为一个开发人员,只需要关注验证逻辑的细节,这个逻辑放在validate()方法中,这个方法是Validator接口定义的入口方法并且在扩展类的抽象支持类中没有实现。此外,还需要创建JavaBean属性,这些属性应该与所有公开给用户的参数匹配。以下代码片段展示了一个参数如何从XML文件传递到这个属性:

<field-validator type="passwordintegrity">
    <param name="specialCharacters">$!@#?</param>
    <message>Your password must contain one letter, one number, and one
        of the following "${specialCharacters}".
    </message>
</field-validator>

我们通过两个辅助方法 getFieldName()和getFieldValue()获取字段的值,这里注意,validate()方法接收了需要被验证的对象,由于我们在动作级别通过Register-validation.xml文件定义了验证,所以传入validate()方法的对象是动作本身。获取字段值后,我们进行了各种检查,最终将错误信息添加到存储的错误消息集中,当然还是使用从支持类继承的辅助方法。这就是全部内容了。

3.2 使用自定义数据验证器

我们在应用程序的类路径下的validators.xml文件中声明自定义的验证器,也就是src文件夹下,一下是这个文件的所有代码:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE validators PUBLIC
        "-//OpenSymphony Group//XWork Validator Config 1.0//EN"
        "http://www.opensymphony.com/xwork/xwork-validator-config-1.0.dtd">

<validators>
   <validator name="passwordintegrity" class="manning.utils.PasswordIntegrityValidator"/>
</validators>

然后在Register-validation.xml文件中把这个验证器添加到password字段对应的验证器中,以下是代码片段:

<field name="password">
  <field-validator type="requiredstring">
     <message >Password is required.</message>
  </field-validator>
  <field-validator type="stringlength">
     <param name="maxLength">10</param>
     <param name="minLength">6</param>
     <message>Your password should be 6-10 characters.</message>
  </field-validator>
  <field-validator type="passwordintegrity">
      <param name="specialCharacters">$!@#?</param>
      <message>Your password must contain one letter, one number, and one of the following "${specialCharacters}".</message>
  </field-validator>
</field>

小年小记——2012年终总结

眼看着又是挥之不去的一年过去了,而我只能在这里以此种方式深切的缅怀一下。

时间飞快,当我还没来的急感受下冬天寒冷的气息时,小年已然就在眼前了;当我还沉浸在2012世界末日没有发生的失望中时,2012已然飘向身后远去了。

这一年对于我来说之所以快,也许是因为我的生活太稳定了,毕业前是每天上课下课,毕业后是每天上班下班,工作和上学几乎没有什么区别,有时候我甚至恍然分不清楚,这既是我的幸运又是我的不幸。稳定的生活是我想要的,但是一成不变的稳定让人看不到希望。

工作

自从2011年中来到北京就一直在泰岳上班,只是2011时人在北京但在客户现场开发,而2012年则终于安稳的在公司上班了,生活也因此稳定下来。之前在霍营住,现在在北苑,上班都是走着去,非常之近。

之前给人寿开发的小程序虽然模块少,但业务逻辑,功能俱全,使我学到了不少东西,尤其是LDAP和Webservice;到了公司开始开发4A的相关项目,主要是做一些模块的开发(集团自注册和业支应用改造),后期参与到了Portal的改造,“有幸”去了两次广东,在4A项目中学到的最多的只是竟然是jquery和dom,而不是java相关的。

对于人寿的项目我需要小小的“吐槽”下:

  • 项目开始没人引导。开始时我和张敏两个人对于LDAP一无所知,以至于我不得不去Orcal官网看英文文档,Google与LDAP相关的知识,可以说LDAP这些东西是我一点点的啃出来的,数据结构;还有后来的LDAP的连接池,也是我一点点的从dpp里抽取有用的核心的部分。当时如果有个对LDAP熟悉的人来支持的话,对于LDAP这边的开发就会快很多。

  • 项目的分歧。也许是我太追求完美主义,在项目中很想遵循一些规范,将程序写的尽量漂亮,于是就和张敏产生了一些分歧。也许我是错误的,我的想法虽好,但会花费更长的时间,而张敏的做法是用尽量少的时间去完成需求即可;孰对孰错?不得而知,也许这两种方式达到一种平衡才是最正确的吧。

对于4A项目我也想小小的“吐槽”下:

  • 业务逻辑不可控。这表现在两个方面,一个是代码方面,一个是源头方面。先说代码方面,一般来讲,j2ee项目都是MVC模式分层,业务逻辑放在service层,但是当代码写的多了,人员组织多了之后,往往会把业务逻辑放在action层,甚至在V层js/jsp中也会有。源头方面,需求总是在变化的,开发前没有预估到的扩展性会导致后续的很多问题,既费时又费力。

  • 该重构的要重构,且重构的一定要彻底。这一点尤其表现在视图层,即页面及脚本。单是页面的插件就有很多个版本互相混杂(jquery就有很多个);完成同样功能的插件会使用两套(弹出窗口用了三种方式)等,更别说页面脚本(js)了,那叫一个鱼龙混杂!

  • 评审到更细的模块。对于一个模块的开发或者对于一个系统的改造计划,都是应该评审的,一个人的决策和考虑必定有限;评审应该更细,细到这个模块的具体实现方式即可。比如改造Portal时,其实不用写那么多的js,有很多更友好(对于开发人员来说)的实现方式没有选择而是使用了最笨的方法,可怜我的腰就是在那时整合系统资源列表多维框弄坏了。

  • 请关注下用户体验。这个不用我细说了吧,用户体验差的产品是没有竞争力的。

PS:上述吐槽系本人半夜睡不着在意识还未清醒之下所发,若相关人士看到有异议及肤浅之处请忽略之。

除了上述工作内容,在业余时间里,我还主要学习了以下内容:

  • Android:研究了一番activity、常见控件、布局初步、数据库sqlite、广播机制和intent,并编写了两个小app:“拼图游戏应用”和“顾客信息管理系统”,后来荒废了;

  • PHP:研究了下php的语法,会写简单的脚本,知道几个简单的函数,会从数据库查询东西并输出,会搭建lamp,并为typecho博客系统写了3款皮肤:NewGreen,Moodpo,Moodpo-bootstrap

  • Ubuntu:依稀记不清了从那个系统换到12.04的,反正从8.04开始就一直在用,电脑也一直是双系统或多系统,写了两篇Ubuntu的文章:关于ATI显卡配置的(貌似是博客里点击率最高的)和装完系统需要的一些基本配置,类似装机必备吧。后续,还会写一篇关于字体设置的,Ubuntu下字体设置好了完胜M$啊,有木有!!!

  • Git/Github:学会使用git这一强大的分布式版本控制工具,个人感觉是完胜SVN神马的,不信看这里;当然学会git后就自然而然的注册github了,fork了几个项目,后来感觉做的不是很好又删除了,呃!

  • Python:大蜥蜴,学习了基本的语法和函数,目前还在学习,期望以后学会它,学到学java这种程度。

  • ssh2:就是spring/struts2/hibernate啦,话说这几天spring框架爆出一个0 day漏洞,汗!学ssh2是看着几本书学的,后续会发部相关的笔记。

好了,就这些吧!总的来说,工作上马马虎虎,有的没的学的不少,但都没“精”,主要原因还是没有实践,唉,这只能寄希望于未来了;而以后要勤写笔记,没实践的东西没笔记是万万不可的。

生活

曾经梦想着08年去看奥运,曾经梦想着和一个知己的人一同上路去北京,曾经梦想着自己只身前往西藏,曾经梦想着徒步在横断山脉的原始森林里徜徉恣邃...这许许多多的梦想在时间飞掠而过后终于变成一个个“梦”,而我已不再“想”。也许我真的老了,在现实的打磨下变的不可方物,一天天过去,不是忙着生就是忙着死,我徘徊在其中,恍恍不可终日。

也许在外人看来,2012年我的生活是翻天覆地的,变化最大的一年:2012年我结婚了,退出了单身的行列。其实,在我看来,生活还是生活,我还是我:看新闻,上网络,翻翻墙,熬熬夜,看dota;生活在继续,我只想说:我和豆子都还是孩子,给我们点糖果好不好...

2012年初,豆子从江苏过来和我住在了一起,下班买菜做饭吃饭睡觉。生活是琐碎的,就像春雨,润物细无声,慢慢我就习惯了并乐在其中。虽然也有各种各样吵架的时候,但日子倒也过的滋润。北京的合租房状况是我无力吐槽的,之前是公司给租房子,现在是我自己租房,问公司是否有补助,罗嗦了半天最后不了了之了。自己租房顿感压力山大,但还在承受范围内。现在回想那些往事,曾经那么多梦想怎也猜不到自己未来是这样一种境遇,真是造化不弄人,人自醉。

阳历的年终前,我从广州回来,房子正好到期,又在附近找了一间租下,一个月要1200块大洋,然后又回家,回了家又去豆子家,然后再回家领了结婚证,又回北京,待了没几天,又回家结婚,2012年的最后一个月真是祸不单行,忙得不可开交啊!唯一的收获就是,在我眼里,终于有了结婚的概念——就是请上一堆堆的人在某个地方吃上两三天,而你还要在其中一天起个大早,穿上新衣服,却在众人的注视下,对着家里那再熟悉不过的北墙磕N个头。

生活其实再简单不过了,只需要坦然的面对每天新升起的太阳,认真对待每一天,生活的滋润,其他都与你无关或者无关紧要了。

2013

需要对2013有所计划了,再怎么说世界末日也过去了,可惜我还没想好,先到此为止啦。

搬窝事宜

博客从IDCsoft搬到了Hostigation,从限制较多的虚拟主机上搬到了自由自在的VPS。对于VPS,因为一直使用Ubuntu的缘故,所以VPS装的系统是Debain,轻车熟路的就配置好了mysql,nginx和php,再装上typecho,用了不到半天功夫,速度还算可以,毕竟开始VPS上纯的一塌糊涂,初始内存使用只有10m。

本来已经把以前的文章导了过来,但后来浏览了下,代码和图片都乱了,而且还有以前wordpress上加的音乐,改来改去,最后不耐烦了,把数据库清空了,So,打算重新开始吧!

PS:当然一些浏览量大的有用的文章我还是会陆续导过来的。

下午三点钟的...

电影《海云台》中妍熙说万植:“你就像下午三点钟的太阳,想做点什么,可是时间总是不够;而不做什么,就会觉得时间很漫长。”

加班其实是好事,星期六加班,星期天就过的比较充实了;如果星期六不加班,我肯定又会宅在家里了。今天醒来的时候才七点多,没办法,已经养成了这样的习惯。但是懒得没起,一直到十点半,洗了澡,出去买饭,之后已到了中午。很快时间就到了下午三点。

没办法豆子走了,只有我一个人,于是开始打扫房间。衣柜下面脏的要死,这都是拜豆子的乌龟所赐,把那地方当成了自己的小窝。我把桌子架走,不知为什么桌子里没什么东西,但是却很重(也许是我很长时间没有锻炼的原因,感觉自己也没什么劲了),再把衣柜挪走,用拖把把里面拖了两遍,重新把床铺了一下;最后把找到的脏衣服全洗了,把整个房间拖了一遍,感觉清爽整齐了许多。这是落日的余辉斜斜照了进来,心情大爽,遂找了电影《海云台》开始看。

以上即本人的星期天。下午三点钟才开始干的活,也干完了,虽然不多,但是却是差点就要否决的地步。经常会形成“下午三点钟”的情况,然后放弃,但是放弃之后又懊恼时间如此漫长,大学的时候一天天就是这样过去的。

北京的天气终于好起来了,豆子离开之后,变成我一个人生活,和以前一样,不再做饭了。几点一线,周而复始,宇宙中的一切真是都在周而复始。不要问为什么,终极的为什么都只能归诸于哲学去回答,那很没意思。想到以前在学校,为了写道家的哲学理论研究的那些大道运行论,在现在的社会之中,被充斥的纸醉金迷,灯红酒绿所代替。

豆子回来,也不再做饭了吧,我想!而且我要带她去吃美味的食物,看美妙的风景,去迷人的地方...否则不如此,人生又作何意义呢?