LightURL——打造零配置的Struts2开发方式

[b]背景[/b]
Struts2已经日益成为Web层比较主流的开发框架,它来源于Webwork2,是一个非常优秀的MVC框架。在Webwork2设计之处,Annotation和Ruby on Rails还没有像现在那么火,所以整个框架在配置方面还是沿用了Web框架惯用的XML作为主要的配置方式。

随着时代的发展,对于Web程序员来说,如何简化配置成了一个很重要的课题。在这方面,Struts2也有一些探索。在Struts2的官方网站上,我们可以找到一些优秀的plugin来做这些工作:

[url=http://cwiki.apache.org/S2PLUGINS/codebehind-plugin.html]http://cwiki.apache.org/S2PLUGINS/codebehind-plugin.html[/url]

[url=http://cwiki.apache.org/S2PLUGINS/smarturls-plugin.html]http://cwiki.apache.org/S2PLUGINS/smarturls-plugin.html[/url]
LightURL的目的是为了吸取这些优秀的plugin的优点,并支持更方便的配置方式。

[b]版本提示[/b]

[b][size=medium][color=red]目前LightURL是基于struts2.0.X的版本写出来的。由于struts2.1.X尚属于beta版本,而且与之对应的XWork在IoC这块做了重大的修改,所以,我也暂不打算支持到struts2.1.X。如果使用struts2.1.X结合lighturl发生问题的朋友,请退回到struts的稳定版本。[/color][/size][/b]

[b]安装[/b]
1. 将struts2-lighturl-plugin.jar加入到classpath下

2. 配置你的web.xml
        
<!-- Struts Filter -->
<filter>
<filter-name>struts</filter-name>
<filter-class>com.demo2do.lighturl.LightURLFilter</filter-class>
</filter>

<!-- Struts URL Definition -->
<filter-mapping>
<filter-name>struts</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>

3. 需要指定你的action package所在位置和一些基本配置

这一步一般在struts.properties中完成。你可以建一个struts.properties的配置文件,并放到classpath下,并指定如下配置


## action package config
lighturl.action.packages=com.demo2do.lighturl.action

## action mapping implementation class
struts.mapper.class=com.demo2do.lighturl.LightURLActionMapper

## default parent package
lighturl.action.default.parent.package=lighturl

## define your entity package (optional)
# lighturl.entity.package=com.demo2do.lighturl.entity


在完成以上的步骤后,lighturl的所有配置即告完成,现在你已经可以使用所有lighturl所提供的特性。

[b]Namespace, ActionName和URL映射[/b]

lighturl所提供的最基本的特性是根据你的Action所在的package,确定namespace和actionName,并进行对应的URL映射。

举例说明,你在上面所讲述的struts.properties中,已经指定了你的action package


lighturl.action.packages=com.demo2do.lighturl.action


那么,如果你有以下的Action类,那么lighturl将根据如下的规则来确定每个Action类所对应的namespace,actionName和url映射关系

1. 根据lighturl.action.packages的配置的package到你的Action类的相对package来确定namespace,如果其中有驼峰法命名,那么转化成"-"连接的单词
[list]
[*]com.demo2do.lighturl.action.Index ----> /
[*]com.demo2do.lighturl.action.user.Search ----> /user
[*]com.demo2do.lighturl.action.blog.category.Index ----> /blog/category
[*]com.demo2do.lighturl.action.accoutDetail.View ----> /account-detail
[/list]
2. 将Action类的类名转化成actionName。如果碰到的Action类以"Action"结尾,则去掉末尾的"Action",如果其中有驼峰法命名,那么转化成"-"连接的单词
[list]
[*]com.demo2do.lighturl.action.Index ----> index
[*]com.demo2do.lighturl.action.user.SearchAction ----> search
[*]com.demo2do.lighturl.action.blog.CategoryBlog ----> category-blog
[/list]
3. 将namespace与actionName拼起来,就成构成映射到具体Action类的url
[list]
[*]http://host:port/app/user/index ---> com.demo2do.lighturl.action.user.Index
[*]http://host:port/app/blog/category-blog ---> com.demo2do.lighturl.action.blog.CategoryBlog
[/list]
上面的这种URL匹配方式,我称之为:[b]package匹配[/b]

[b]特殊形式的URL[/b]

应该说根据package来进行Action映射,可以解决绝大多数从url到action的映射配置问题。不过有的时候,我们可能需要支持一些特殊形式的url。LightURL在默认情况下,支持下列2种特殊形式的URL

1. 支持将名为Index的Action直接映射到package上

这种匹配我称之为:[b]Namespace匹配[/b]。这一个特性很直观。如果你在某个Action的package下面有一个名为Index的Action类,那么如果你直接访问这个package,那么你可以访问到这个类:
[list]
[*]com.demo2do.lighturl.action.Index ---> http://host:port/app/
[*]com.demo2do.lighturl.action.user.Index ---> http://host:port/app/user
[*]com.demo2do.lighturl.action.blog.category.Index ---> http://host:port/app/blog/category
[/list]
2. 支持类似: /entity/${id}形式的URL

这种匹配我通常称之为:[b]entity匹配[/b]。这个特性也比较简单,如果你有某个entity,并且在你的Action package下有一个与entity同名的package。同时在这个package下有一个叫View的Action,那么上述形式的URL会被映射到该Action。
[list]
[*]com.demo2do.lighturl.action.user.View ---> http://host:port/app/user/3456
[*]com.demo2do.lighturl.action.blog.View ---> http://host:port/app/blog/1113
[/list]
你可以通过在struts.properties中指定你entity所在的package来对哪些url可以具备这些特性,如果你输入的url不在你所指定的package中含有entity,那么这个url将无法被识别。
针对有些情况,数据库的主键可能不是数字。此时,你可以通过自己实现com.demo2do.lighturl.config.EntityPrimaryKeyIdentifier的接口来指定你的url中id具备什么特点。默认的实现是将主键识别为数字。

不过一般而言,典型的entity匹配是无法满足我们的要求的。因为我们往往不会针对某个entity直接为action建立package,而是会对某个entity,对应到某个Action,它位于某个具体的package。所以针对这样的需求,我对[b]entity匹配[/b]做了一定的扩展:

[list]
[*]com.demo2do.lighturl.action.user.admin.View ---> http://host:port/app/user/user/3456
[*]com.demo2do.lighturl.action.blog.category.View ---> http://host:port/app/blog/category/1113
[/list]
也就是说,某个具体的package中,名字为View的Action,会对应到类似相同package下增加${id}的url。

[b]使用Annotation来指定映射[/b]
除了上述这些基本特性以外,还可以通过Annotaion来指定URL映射。目前情况下,LightURL所支持的Annotation有两种类型:

1. URL完整匹配

URL完整匹配是指如果某个url完整匹配于Annotation中所指定的内容,那么这个URL将被映射到Annotation所在的Action类的method


package com.demo2do.lighturl.action.user;

import com.demo2do.lighturl.annotation.Action;
import com.opensymphony.xwork2.ActionSupport;

/**
* @author Downpour
*
*/
public class Search extends ActionSupport {

private static final long serialVersionUID = -1728616675239859226L;
/* (non-Javadoc)
* @see com.opensymphony.xwork2.ActionSupport#execute()
*/
@Override
@Action("/all/search-user")
public String execute() throws Exception {
return super.execute();
}
}


例如,上述的Action类有一个Annotation,那么这个Action和method将被映射到对应的URL:http://host:port/app/all/search-user。
注意,此时,虽然从url上来看,这是一个没有什么规则的url,但是其所对应的namespace和actionName还是根据com.demo2do.lighturl.action.user.Search来进行计算的。

2. URL Template

URL Template是指,url可以匹配Annotaion中指定的某种URL Template,并将其中的可变部分作为参数映射到Action中。


package com.demo2do.lighturl.action.blog;

import com.demo2do.lighturl.annotation.Action;
import com.opensymphony.xwork2.ActionSupport;

/**
* @author Downpour
*
*/
public class Category extends ActionSupport {

private static final long serialVersionUID = -1535992103374733252L;

private Long id;
private int year;
private int month;
private int day;

/* (non-Javadoc)
* @see com.opensymphony.xwork2.ActionSupport#execute()
*/
@Override
@Action("/blogs/${year}/${month}/${day}")
public String execute() throws Exception {
return super.execute();
}

@Action("/blog/${id}/edit")
public String edit() throws Exception {
return super.execute();
}

@Action("/blogs/common/*")
public String common() throws Exception {
return super.execute();
}
// setters and getters

}


在上述的例子中,可以发现,在Annotation中所指定的内容是一个URL Template,如果你有一个url,可以匹配上面的URL Template,那么url将匹配到这个Action的method,并且将对应位置的值注入到Action中的同名参数中。

不仅如此,LightURL还能够支持AntPath的URL匹配。这样可以通过书写/blogs/common/*或者/blogs/**来匹配多个不同的URL映射。

针对上面的例子:
http://host:port/app/blogs/2008/08/07将被映射到这个Action的execute,并且2008,08和07分别映射到year,month和day中。
http://host:port/app/blog/2345/edit将被映射到Action的edit方法,并且2345映射到id中作为参数
http://host:port/app/blogs/common/abcd将被映射到Action的common方法。


[b]URL的匹配顺序与重复配置的校验[/b]

在上面的例子中,介绍了那么多的url映射到Action中的方式。他们之间可能会出现冲突,有些冲突,LightURL会在系统启动时为你检查出来,并强制要求你纠正它,而有些冲突,则通过优先级匹配的方式进行。

下列冲突将被认为是你必须在系统启动前就进行纠正的:
1. Annotation中定义的URL Template互相之间冲突

例如:/blogs/${year}/${month}/${day}和/blogs/${category}/${id}/edit就是冲突的。

2. Annotation中定义的URL Template与其他Annotation中定义的完全匹配URL冲突

例如:/blogs/${year}/${month}和/blogs/category/index就是冲突的。

3. Annotation中定义的URL Template与形如/entity/${id}的url定义冲突

例如:/user/${id}形式的Annotation定义可能会与系统默认支持的冲突。

在其他情况下,如果你定义的URL映射互相直接有冲突,那么LightURL将根据某个顺序进行URL匹配,并找到第一个匹配的映射方式,然后放弃查找。这个顺序为:
1. 首先进行[b]Namespace匹配[/b],如果url恰好能匹配某个namespace,并且其对应的package下有Index作为Action,那么直接进行匹配。
2. 其次查看所有的Annotation定义中,是否存在[b]完整的URL匹配[/b],如果找到,那么进行直接匹配。
3. 接着进行[b]package匹配[/b],将url分解成相应的namespace和actionName,与已有的配置进行匹配,如果找到,那么直接匹配。
4. 然后进行[b]entity匹配[/b],看看url是否形如:/entity/${id},如果是,那么直接匹配。
5. 最后进行Annotation定义的[b]Url Template匹配[/b]。

如果所有的五种情况都无法进行匹配,那么这个URL将无法被LightURL识别,继续交由Struts2进行后续处理。


上面所描述的内容都是Url到Action的映射。下面的部分,描述的是如何在Action执行完毕之后,转到相应的结果view。

[b]Codebehind[/b]

LightURL支持codebehind。有关codebehind的相关知识,可以参考struts2的相关文档:
[url]http://struts.apache.org/2.x/docs/codebehind-plugin.html[/url]

有了codebehind的支持,那么从Action转到类似jsp,ftl或者vm的view层组件就不需要任何配置,只要符合一定的命名规范,就可以直接进行转向。

在这里,LightURL不仅实现了Codebehind的所有功能。而且,LightURL还对Codebehind的规则做出了扩展。

Codebehind在寻址JSP等资源时,顺序按照以下的规则进行:

1. /namespace/actionName-<resultCode>.<ext>

2. /namespace/actionName.<ext>


而LightURL在寻址JSP等资源时,增加了一定的规则,并且按照如下顺序进行:

1. /namespace/actionName-<resultCode>.<ext>

2. /namespace/<resultCode>.<ext>

3. /namespace/actionName.<ext>

4. /namespace/namespace-actionName-<resultCode>.<ext>

5. /namespace/namespace-<resultCode>.<ext>

6. /namespace/namespace-actionName.<ext>

这样一来,大家就有更加宽广的选择范围,进行默认资源的寻址匹配了。

[b]ResultCode的识别[/b]

但是在很多情况下,我们需要的是全方位的Result类型的支持。例如,有的时候我们需要返回JSON Result,有的时候,我们可能需要Redirect到一个新的Action。此时,我们不得不为此增加一些配置,或者借助Annotation来完成。

为此,LightURL提供了一种根据ResultCode进行识别的命名方式来匹配你所指定的Result。

以JSON Result为例,你可以这么写:


package com.demo2do.lighturl.action.user;

import com.demo2do.lighturl.entity.User;
import com.opensymphony.xwork2.ActionSupport;

/**
* @author Downpour
*
*/
public class Index extends ActionSupport {

private static final long serialVersionUID = -5017825114664788765L;

private User user;

/* (non-Javadoc)
* @see com.opensymphony.xwork2.ActionSupport#execute()
*/
@Override
public String execute() throws Exception {
return "j:user";
}

// setters and getters
}


在这里,你可以发现,resultCode变成了j:user。那么此时,LightURL会将这个resultCode识别成:请返回JSON Result,并且JSON的Result的root为user对象。

再例如:


package com.demo2do.lighturl.action.user;

import com.demo2do.lighturl.entity.User;
import com.opensymphony.xwork2.ActionSupport;

/**
* @author Downpour
*
*/
public class Index extends ActionSupport {

private static final long serialVersionUID = -5017825114664788765L;

private User user;

/* (non-Javadoc)
* @see com.opensymphony.xwork2.ActionSupport#execute()
*/
@Override
public String execute() throws Exception {
return "r:/user/add-user";
}

// setters and getters
}


在这里,"r:/user/add-user"会被识别成:请返回Redirect Result。Redirect的URL为/user/add-user。

LightURL默认情况下,实现了对Redirect Result和JSON Result的ResultCode识别,并将他们的前缀分别定制为:"r:"("redirect:")或者"j:"("json:")。

当然,你可以根据你自己的情况,实现你自己的ResultCode的识别程序,你只需要实现com.demo2do.lighturl.result.ResultCodeConfig接口即可。并在struts.properties中指定你希望LightURL进行ResultCode匹配的顺序。


lighturl.result.code.config=yourpackage.ResultCodeConfigImpl1,yourpackage.ResultCodeConfigImpl2


这样,LightURL会根据你所指定的顺序,依次进行ResultCode的匹配,直到找到第一个匹配的Result Type为之。

当然,默认情况下,LightURL的匹配顺序为:

codebehind -> 你自定义的ResultCode识别实现 -> json -> redirect
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
完整版:https://download.csdn.net/download/qq_27595745/89522468 【课程大纲】 1-1 什么是java 1-2 认识java语言 1-3 java平台的体系结构 1-4 java SE环境安装和配置 2-1 java程序简介 2-2 计算机中的程序 2-3 java程序 2-4 java类库组织结构和文档 2-5 java虚拟机简介 2-6 java的垃圾回收器 2-7 java上机练习 3-1 java语言基础入门 3-2 数据的分类 3-3 标识符、关键字和常量 3-4 运算符 3-5 表达式 3-6 顺序结构和选择结构 3-7 循环语句 3-8 跳转语句 3-9 MyEclipse工具介绍 3-10 java基础知识章节练习 4-1 一维数组 4-2 数组应用 4-3 多维数组 4-4 排序算法 4-5 增强for循环 4-6 数组和排序算法章节练习 5-0 抽象和封装 5-1 面向过程的设计思想 5-2 面向对象的设计思想 5-3 抽象 5-4 封装 5-5 属性 5-6 方法的定义 5-7 this关键字 5-8 javaBean 5-9 包 package 5-10 抽象和封装章节练习 6-0 继承和多态 6-1 继承 6-2 object类 6-3 多态 6-4 访问修饰符 6-5 static修饰符 6-6 final修饰符 6-7 abstract修饰符 6-8 接口 6-9 继承和多态 章节练习 7-1 面向对象的分析与设计简介 7-2 对象模型建立 7-3 类之间的关系 7-4 软件的可维护与复用设计原则 7-5 面向对象的设计与分析 章节练习 8-1 内部类与包装器 8-2 对象包装器 8-3 装箱和拆箱 8-4 练习题 9-1 常用类介绍 9-2 StringBuffer和String Builder类 9-3 Rintime类的使用 9-4 日期类简介 9-5 java程序国际化的实现 9-6 Random类和Math类 9-7 枚举 9-8 练习题 10-1 java异常处理 10-2 认识异常 10-3 使用try和catch捕获异常 10-4 使用throw和throws引发异常 10-5 finally关键字 10-6 getMessage和printStackTrace方法 10-7 异常分类 10-8 自定义异常类 10-9 练习题 11-1 Java集合框架和泛型机制 11-2 Collection接口 11-3 Set接口实现类 11-4 List接口实现类 11-5 Map接口 11-6 Collections类 11-7 泛型概述 11-8 练习题 12-1 多线程 12-2 线程的生命周期 12-3 线程的调度和优先级 12-4 线程的同步 12-5 集合类的同步问题 12-6 用Timer类调度任务 12-7 练习题 13-1 Java IO 13-2 Java IO原理 13-3 流类的结构 13-4 文件流 13-5 缓冲流 13-6 转换流 13-7 数据流 13-8 打印流 13-9 对象流 13-10 随机存取文件流 13-11 zip文件流 13-12 练习题 14-1 图形用户界面设计 14-2 事件处理机制 14-3 AWT常用组件 14-4 swing简介 14-5 可视化开发swing组件 14-6 声音的播放和处理 14-7 2D图形的绘制 14-8 练习题 15-1 反射 15-2 使用Java反射机制 15-3 反射与动态代理 15-4 练习题 16-1 Java标注 16-2 JDK内置的基本标注类型 16-3 自定义标注类型 16-4 对标注进行标注 16-5 利用反射获取标注信息 16-6 练习题 17-1 顶目实战1-单机版五子棋游戏 17-2 总体设计 17-3 代码实现 17-4 程序的运行与发布 17-5 手动生成可执行JAR文件 17-6 练习题 18-1 Java数据库编程 18-2 JDBC类和接口 18-3 JDBC操作SQL 18-4 JDBC基本示例 18-5 JDBC应用示例 18-6 练习题 19-1 。。。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值