scope="singleton">
...
...
...
UserService.queryUser(String scope,Collection[String] paras,int begin,int max)
...
通过上面的结构可以看见,每一个EasyJWeb配置文件的根元素均为
,在
元素下面可以包含0或一个
元素、0或1个
元素、0或1个
元素、0或1个
元素、0或1个
元素,根据项目的性质可以选择使用这些元素,下面将分别对这些元素作简单的介绍。关于EasyJWeb的配置文档的详细定义,请参看DTD和schemle。
这个元素主要用来定义EasyJWeb的一些基本信息及全局信息,包括工作模式、模板位置、附件最大值、初始应用程序,全局拦截器等。其下\包括
等子元素。
用来定义EasyJWeb的基本运行参数,如下面的配置信息:
用来配置伴随EasyJWeb启动而启动执行的应用程序,这些程序一般可以作额外的初始工作,或者是以一个新线程的形式在系统中运行。如下面是EasyJF开源Blog系统中,自动生成静态html文件的程序:
元素下面是EasyJWeb的模块信息,在EasyJWeb中,可以把功能相关或形式的相近的请求处理程序封装到一个模块Module中,元素下面包含0或多个元素,在中将包含具体的Module配置信息,Module是EasyJWeb应用程序中重要的概念,我们将在后面详细介绍元素的详细配置。
元素下面包含0或多个
元素用来定义前台页面表单的信息。在普通的应用中,一般不使用这个配置选项。
元素下面包含0或多个元素,元素一般为业务层对象,关于的配置将在后面专门介绍。
元素主要用来定义应用程序中支持远程javascript脚本访问的业务对象,下面包含0或多个
元素,0或多个
元素,0或多个
元素,我们将在Ajax一章中专门介绍。
Module标签定义了一个Module对象。根据前文对Module对象的讲解,应该能够很直观的了解Module的配置。Module中包含了Page对象(Page一节),控制器(Module一节),拦截器(AOP和拦截器一章),注入的bean(容器一章)分别介绍其配置。
Tip
在一个配置文件中,并不要求包含所有的以上的元素,请根据实际情况配置最小需要的元素。比如如果按照我们推荐的mvc文件分布,则在主文件mvc.xml中,只需要配置包含文件、
、错误处理器以及和Spring等容器集成的基础配置。而在各个子配置文件中只需要
或者
标签。
WebForm是EasyJWeb的一个重要的组件,在这一小节中,将介绍PO和WebForm的toPo方法。在验证一小节中介绍toPO方法中的验证。
PO
PersistenceObject,持久化对象,VO value Object,值对象,TO Translate Object 传输对象,在EasyJWeb中,WebForm扮演了VO和TO,而PO直接由Domain扮演。
toPo方法
在一个带参数的请求中,或者是在一个有表单域的请求中,如何从中取得值,用于保存对象或者处理?在Struts1.x中,使用了一个ActionForm来承载这些数据;struts2.x中,使用Controller作为Command对象来承载这些数据;springMVC中,可以使用带Command对象Controller,使用一个Command对象来承载这些数据;而在EasyJWeb中,是WebForm承载了这些数据,并且使用一个toPo方法来直接将这些数据拷贝到需要持久化的对象中。比如下面一个代码片段演示了保存一个论坛帖子的功能:
public Page doSave(WebForm form, Module module) {
BBSDoc doc = form.toPo(BBSDoc.class);
List attachs = ActionUtil.saveFile(form, webConfig
.getImageDirPath(), false);
doc.setIp(ActionContext.getContext().getRequest(). getRemoteAddr());
List attachList = this.binaryService
.addAttachMent(attachs);
doc.setFiles(attachList);
this.service.addBBSDoc(doc);
return this.doBbsList(form, module);
}
在WebForm中,有两种toPo方法:
WebForm.toPo(Object obj):传入一个对象,直接从WebForm中拷贝值到改对象中。
WebForm.toPo(Class clazz):传入一个类型,WebForm会初始化这个类型的实例再将WebForm中的值拷贝。关于属性的拷贝,有几点注意:1,只会拷贝属性相同的值,比如提交的表单中包括一个name,那么,在持久化对象或者Command对象中,只会拷贝String、Integer的name属性。
使用@FormPO标签来规定拷贝的规则:
inject:
指定可通过toPo自动注入的属性,默认为全部可自动注入;如果设置了该值,则表示除指定可注入的属性以外,其它所有属性都为不能自动注入;在EasyJWeb中,可以直接通在模型(域)对象上通过标签来指定只允许注入哪些属性值。如果一个属性值为可注入的,则可以使用WebForm.toPo(obj)方法,把WebForm中与obj属性名称相同的属性值设置到obj中。需要注入的属性使用逗号(,)作为分逗符。比如: @FormPO(inject="name,bornDate")
public class Person
即在Person类(或者Command对象)中,当使用toPO方法拷贝值的时候,只有name和bornDate属性被拷贝,其余如果有符合的属性,都不准被拷贝。这在很大的层度上提高了应用的安全性。
disInject:
指定不可通过toPO自动注入的属性,当试图通过toPO更新该属性时,将会被忽略,并在日志中提示相关信息。在EasyJWeb中,一个模型(域)对象的属性默认情况下全部都是可注入的,我们可以通过disInject来指定不可注入的属性。多个不可注入的属性使用逗号(,)作为分隔。如上面inject示例,也可以写成下面的形式:
@FormPO(disInject="id,loginTimes")
public class Person
Tip:
在实际应用中,可以根据实际情况选择使用inject及disInject,如果需要拷贝的属性较多,则选择disInject;而拷贝的属性较少,则选择inject。
控制addPo属性的可见性:
前文在介绍WebForm的时候,提到了addPo方法,在这个方法中,其实也有控制标签:
disRead:
指定为disRead的属性在addPo方法中不会被添加到Velocity上下文中。
下面,给一个这些标签的使用的一个范例:
@Entity
@FormPO(name = "person",inject="name,sex,mail,intro",disInject="age",disRead="serialVersionUID,id")
public class Person implements Serializable {
@Id
@GeneratedValue(strategy = GenerationType.TABLE)
private Long id;
@Column(length = 32)
private String name;
@Column(length = 6)
private String sex;
@Column(length = 40)
private String mail;
private int age;
@Column(length = 200)
public String intro;
}
在这个例子中,如果调用WebForm.toPo(Person.class),那么Person中的name、sex、mail将会拷贝,而age不会被拷贝,并且调用WebForm.addPo()方法时,Person的serialVersionUID和id将不会被添加到Velocity上下文中。关于更多@FormPO标签的用法,请参见API doc。
在EasyJWeb中,最常用的方法是通过WebForm.toPo方法在拷贝属性的过程中执行验证,同时,也可以调用FramewokEngine.form2Obj方法执行验证。
EasyJWeb不使用传统的XML配置来完成验证,我们认为,验证应该是和PO或者Command对象紧密相连的,为了避免大量的XML文件的存在,采用了Annotation来完成验证。EasyWeb同时也不推荐使用过多的Annotation,从而造成Annotation泛滥,因此EasyJWeb的验证标签只有一个,即@Validator。
EasyJWeb完善了验证系统,使验证变得更加容易、灵活、统一控制。你可以使用非常简单的标签或配置就能使系统拥有服务器端及客户端验证的功能。
下面以一个简单的示例:
有一个Person模型,如下所示:
包含id、name、sex、borndate、height、mail、homepage等几个属性。
假如我们要让name、sex、heigth、borndate必填,并且borndate必须在1908到2008年之间, mail属性只接收正确的email信息,homepage必须接收url信息。则我们只需要在Domain对象(可以是Entity对象,也可以是传输Command对象)中加入下面的配置信息,即可。
@FormPO(name="person",validators={@Validator(name="required",field="name,sex,heigth,borndate"),@Validator(name="range",field="borndate",value="min:1908-01-01;max:2008-01-01"),@Validator(name="email",field="mail"),@Validator(name="url",field="homepage")})
public class Person{
private Long id;
private String name;
private String sex;
private String mail;
private Integer heigth;
private Date borndate;
public String homepage;
…//setter及getter方法
}
不需要进行复杂的配置,只需要使用符合人类语言习惯的简单标注,就能实现所需要的验证业务逻辑。
@Validator验证标签的使用非常灵活,你只要具有充分的想像力,就能描述出符合特定需要的验证逻辑。比如上面的例子中,我们规定name不允许为空,字符数最小不能少于5个,最大不能超过10个,在进行字符验证前需要清除掉前后的空格。则我们可以使用下面的验证标签:
@Validator(name=”string”,value=”blank;trim;required;min:5;max:10;minMsg:最少不能少于5个字符;maxMsg:最大不能超过10字符”)
private String name;
统一的验证标签@Validator
EasyJWeb中只有一个用于验证的Annotation,即@Validator,通过一个@Validator,就能标签任意类型的验证,使用起来非常简单。@Validator的代码如下所示:
public @interface Validator {
public String name();// 验证器的名称,如required,string,range等
public String value() default "";// 验证器的值,使用;号作为分隔符存放各个参数。如value="required;min:5;max:20"
public String msg() default "";// 默认错误提示信息,当验证无法通过时显示的提示信息
public String field() default "";// 字段名称,对于property及field类型的校验均可用,也是错误对象的主属性名称。可以用于多个字段,此时需要使用,隔开
public String displayName() default "";// 定义对象的显示名称,默认情况下为field的名称,可以通过@Field中的name属性定义。
public ValidateType type() default ValidateType.Property;// 校验类型,默认是对属性进行校验
public boolean required() default false;// 是否必填字段,每个验证器都可以通过设置属性required=true来指定该属性为必填项
public String key() default "";// 多国语言显示值的编码
}
Validators.RequiredValidator-用来定义必填属性,预定义名称required。
Validators.StringValidator-字符串验证器,定义字符串的属性,预定义名称string。
Validators.URLValidator-URL字符串验证器,匹配一个合法的URL,预定义名称url。
Validators.RegexpValidator-正则表达式验证器,匹配指定条件的正则表达式,预定义名称regex。
Validators.EmailValidator-Email字符验证器,匹配正确的email字符串,预定义名称email。
Validators.RangeValidator-范围验证器,用来限制属性必须在指定的范围之内,预定义名称range。
验证器引擎的触发也是非常灵活的,如果是普通的CRUD应用或者是基于普通CRUD应用基础上扩展的应用,则在基本的添删改查中会自动调用验证逻辑。
如果是自定义的Action,可通过调用form.toPo等方法触法验证逻辑。如果是使用IDAO接口进行的调用,则在进行数据持久化之前会调用验证逻辑。
关于内置验证器的更多细节,请参看API doc。
要自定义验证器很简单,只需要实现Validator接口,即可注册到系统中使用。在实际应用中,一般通过使用继承抽象类AbstractValidator来实现自定义的验证器。如下面是最简单的验证器Required的实现:
public class RequiredValidator extends AbstractValidator {
public RequiredValidator() {}
public void validate(TargetObject obj, Object value, Errors errors) {
if (value == null)
addError(obj, value, errors);
else if (value instanceof String) {
}
}
public String getDefaultMessage() {
return "{%0}不能为空!";
}
}
在完成了自定义的验证器后,只需要在容器中声明该bean,并将bean的名字设置为需要调用的验证器的名字即可。比如如果要将上面的RequiredValidator作为notNull验证器使用,则在容器中配置:
即可,然后在Domain或Command对象中可以通过@Validator(name="notNull")来标注使用该验证器。
EasyJWeb中使用了Errors对象来包装在验证过程中出现的错误,该对象提供了三个有用的方法:
int getErrorCount():用来显示出错的个数;
boolean hasError():用来显示是否有错;
String getMessage(String fieldName):用来显示某一个属性的错误;
在页面中显示错误:
$!errors-显示全部验证错误信息。
$!errors.name-显示name属性(字段)的错误信息。
如下面的的Form
请输入姓名:$!errors.name
电子邮箱:$!errors.mail
在代码中获得错误:
在Action中,如果要捕获错误,即得到Errors对象,使用:
FrameworkEngine.getValidatorManager().getErrors()方法来得到一个验证错误对象Errors。通过Errors可以检查是否包含未通过的验证逻辑,从则作相应的处理。
匹配错误的处理:
如果发生匹配错误,有三种处理方式:
1,忽略,在toPo方法中,设置validateRollback为false,则发生匹配错误继续匹配下面的属性。
2,回滚,在toPo方法中,设置validateRollback为true,那么在发生匹配错误时,将已经拷贝了的属性还原到拷贝之前。这在使用JPA作为持久层的时候特别有用,避免了设置flushMode。
3,强制性出错,在中设置validate为true,则在匹配出错时,马上抛出一个框架错误,不会继续向下匹配。
关于错误显示的更多细节,请参看相关API doc。
在EasyJWeb中提供了自定义错误处理机制。如果在控制器中发生了错误,那么可以根据配置,来完成对特定错误的特定处理过程。
要实现错误处理器,需要实现com.easyjweb.interceptor.ExceptionInterceptor接口,该接口定义了一个方法:
boolean handle(Throwable e, Object target, Method method, Object[] args) throws Exception;
其中,e是出现的异常,在自定义的错误处理中,可以判断这个e是否instanceof你需要处理的Excepion,而选择是否处理。Target是异常出现的对象,一般来说会是控制器对象。Method是异常抛出时调用的方法,args是相关参数,这几个参数都是AOP拦截器规定的参数。该方法返回了一个boolean值,如果返回true,则表示异常已经成功处理,不再需要作其它的处理,返回false则,表示把异常交给下一级异常处理器进行处理,如果抛出异常,则表示将直接把异常交给外部程序或发给用户。可以在自定义的错误处理器中完成自定义的错误处理规则。比如根据错误的不同,显示不同的错误页面。如果配合target和method参数,甚至可以将出错页面的导向细化到每一个Action的每一个Method上。
在完成了自定义的错误处理器后,可以直接配置一个Bean,这样EasyJWeb会自动使用该异常处理器来处理在系统运行过程中出现的特定的异常。所有实现ExceptionInterceptor的异常处理器会会构成一个错误处理器链,来协作处理Action中抛出的错误。在这条错误处理器链的最后,是系统级别的DefaultExceptionHandle,可以在容器中配置该处理器,在这个处理器中,如果不配置这个处理器,或者在容器中没有对这个处理器做特别的配置,将显示默认的错误提示页面。在这个处理器中,可以通过配置errorPage属性,来定义自己的出错页面。
EasyJWeb提供了一些有用的工具类来辅助开发,提高开发效率。
CommUtil提供了很多常用的方法,其中分页是重要的一个。CommUtil不光只用在后台控制器中,而且EasyJWeb内置的将CommUtil的一个实例放入了Velocity上下文中,使得可以直接在前台使用$CommUtil来调用。下面介绍CommUtil的几个常用方法:
l $!CommUtil.formatDate(formatStr,Date):
按照规则格式化时间。Date为传入的类型为Date的时间对象,formatStr是格式化样式,使用的是SimpleDateFormat 格式化,比如$!CommUtil.formatDate("yyyy-mm-dd",$date)就可以将date显示为2000-12-29类似的样式,关于格式化字符请参见SimpleDateFormat。
l $!CommUtil.longDate(Date):
完整的时间格式化,样式为yyyy-MM-dd H:m:s。
还有更多的时间格式化样式,比如显示中文时间的等等,请参见CommUtil的API文档。
l CommUtil.null2String(nameStr):
在Action中常用的方法,一般用于从WebForm取得字符串性的属性:
String idStr=CommUtil.null2String(form.get("id"));
if(!"".equals(idStr)){
//...
该方法避免对null的验证。如果是null,返回"";
同时,也提供了一个null2int方法,如果属性为null,返回0。
l $CommUtil.toRowChildList($List,sizePerRow):
将一个List转换成sizePerRow为每行的一个List的List:List
在页面显示的时候特别有用,我们经常遇到在页面上需要把一个list分成几行来显示,不需要太多的VTL,也不需要vmacro,只需要:
$foreach($subList in $!CommUtil.toRowChildList($!infoList,num))
$foreach($info in $!subList)
$info.show()
$end
$end
更多的CommUtil方法请参见API 文档。
EasyJWeb提供了一个比较完整的分页接口和相关工具。IPageList接口定义了一个分页对象的信息,IQuery接口定义了分页查询的方法。
在IPageList中定义了比如当前页,所有页面数,每页大小等信息。
常用的分页组件:
EasyJWeb提供了一个为List对象分页的组件,只需要象下面这样使用:
IPageList pList=new PageList(new ListQuery(resultList));
pList.doList(pageSize,currentPage,null,null);
这样就得到了一个IPageList对象了,这里面有完整的分页信息。同时EasyJWeb提供了CommUtil.saveIPageList2WebForm(IPageList,WebForm),使用该方法可以快速的将分页信息放入Velocity上下文中,并且自动生成翻页需要的html。通用的在页面的分页使用为:
在列表页面上放入一个Form,添加两个hidden域:
第一个是用于保存每页的记录数,第二个保存当前页面,
在需要放置分页的位置添加:
共$!rows条 第$!page/$!pages页 每页$!pageSize条 $!pagination
其中,$rows是所有的条数,$page是当前页数,$pages是总共条数,$pageSize是每页的条数,$pageination是EasyJWeb生成的分页HTML,如下图:

同时,EasyJWeb为JPA+Spring+EasyJWeb的应用提供了一个JPA的分页组件:QueryUtil。只需要把查询的条件包装为IQueryObject的子类,就可以使用
public static IPageList query(IQueryObject queryObject,Class entityType,GenericDAO dao)来完成整个分页过程。
关于更多分页和实现自定义分页请参见相关文档。
tagUtil也是EasyJWeb内置的加入到了Velocity上下文中的用于生成页面标签的工具类。下面列举几个常用的方法:
l String options(final List list,final String valuePoperty,final String titlePoperty,final String value):
用于根据一个list生成多个选择项,其中,list是需要用于生成选择项的对象的列表,比如List;valueProperty是用于生成选择项的value的属性,比如要使用User的id生成,那么就填id;titleProperty:用于生成选择项的名字的属性,比如要使用User的name生成,这里就填name;value,根据value来设置选择项,比如在选择总经理的一个页面中,已经选择了某个user为总经理,那么在修改的时候,要选中这个user,这里只需要传入已经选中的user的id就可以在确定的user的选择项中添加selected="selected"。
在tagUtil里面还有生成分页HTML的方法,在自定义分页HTML样式时,可以参考这些方法。
更多的关于tagUtil的用法请参看API doc。
EasyJWeb内置了一个验证码生成和检查器,如果你的应用需要加入一个验证码,只需要:
1,在mvc.xml里面添加:
action="com.easyjf.cms.mvc.RandomImgCode" defaultPage="" />
2,在页面上需要显示验证码的位置添加类似:
style="cursor:pointer;" alt="点击这里换一个"
onclick="this.src='randomCode.ejf?'+Math.random()";>
3,在接受这个验证码的Action中添加类似代码:
String randomCode = (String) ActionContext.getContext().getSession()
.getAttribute("rand");
if (!randomCode.equals((String) form.get("randCode"))) {
form.addResult("msg", "验证码不正确,请重新输入!");
return this.doRegisterPU(form, module);
}
即可完成整个验证。
如果要生成自定义的验证码,可以参看ImageCode和RandomImgCode的实现。
EasyJWeb提供了一个简单,但功能完整的IoC容器,并且该容器能很好的和其他容器并肩作战,形成一个超级IoC容器。在本章中,我们将详细介绍EasyJWeb容器的使用,并介绍容器间的整合。
EasyJWeb提供了一个内置的IoC容器,该容器不但能做为一个IoC容器配合EasyJWeb一起使用,而且还能通过简单的配置集成Spring、Guice等其他容器,借用其他容器强大的功能,比如事务管理等。同时,EasyJWeb使用@Inject标签等,使得使用容器中的bean更加简单和透明。