Skip to content

基于Struts2 + Struts Convention的Struts插件,无需配置任何action即可享用类似"namespace/action/method.suffix"(后缀可省略)格式的URL访问方式。并集成文件下载、JSON输出等多种常用的Web功能,大幅减少冗余的配置和编码工作。

Notifications You must be signed in to change notification settings

CodePlayer/struts-ready-plugin

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

70 Commits
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

struts-ready-plugin

基于Struts2 + Struts Convention的Struts插件,无需配置任何action即可享用类似"namespace/action/method.suffix"(后缀可省略)格式的URL访问方式。并集成文件下载、权限控制等多种常用的Web功能,大幅减少冗余的配置和编码工作。

Struts 2是非常流行的开源 MVC 框架,熟练使用 Struts 也是一个Java EE开发人员的必备技能,因此此处不再赘述 Struts 2 的用法细则。你可以直接参考 Struts 的官方文档。

众所周知,Struts 的请求映射一般都是在 struts.xml 文件中配置的。我们每新增一个业务请求方法,都需要在 struts.xml 中进行相应的配置(例如:package、action、result的配置)。不过,这样的配置方式将会带来以下问题:

  • 每次新增业务请求方法,都需要进行手动配置,略显繁琐;无意义的重复劳动,浪费时间(尽管 Struts 也支持通配符来简化配置)。
  • 如果URI的命名没有严格的约定,那么用户访问的URL将没有统一的风格。此外,在排查问题时,都需要根据URL在配置文件中定位对应的类和方法。
  • 团队合作时,多个开发人员对同一配置文件进行修改,极可能引起文件冲突(尽管 Struts 配置文件也支持使用 include 指令将其拆分为多个配置文件,但这会增加上一问题的定位查找负担)。

为了解决上述问题,提高开发效率,我们对 Struts 底层代码进行了深入研究,并自行研发了Struts Ready插件(Struts Ready 插件自身又依赖于Struts 官方的另一个插件:Struts Convention)。使用该插件,我们在每次新增业务请求方法时,将无需在 struts.xml 配置文件中添加任何配置,真正的实现了“零配置”。

以我们项目中的 me.codeplayer.action.UserAction 类的info()方法为例,我们只需要为该方法添加一个 @Ready 注解,即可通过URL "/user/info" 来访问该请求方法:

package me.codeplayer.action;

import me.codeplayer.annotation.Ready;

public class UserAction {

	@Ready
	public String info() {
		// 这里是控制器层的处理代码
		return "user_info";
	}

}

问:为什么一定要给方法加上 @Ready 注解? 答:因为在Action类中一般还有供Struts进行属性注入的 getter/setter 方法。例如,UserAction类具有 user 属性,以及对应的 getUser() 和 setUser() 方法。如果没有@Ready加以区别的话,用户甚至可以通过URL "/user/getUser" 来访问getUser()方法。这将产生一些不必要的问题,并有一定的安全隐患。 因此,我们定义了@Ready注解,只有标注了该注解的方法才允许外部访问,否则显示HTTP 404。

Struts Ready 插件的Action匹配机制

###一般匹配机制

基于“约定大于配置”的思想,Struts Ready插件重写了 Struts 底层的 Action 映射机制,并能够自动建立请求URL到Action类以及方法之间的映射。 Struts Ready插件认为,一个Action方法的完全限定名称(例如me.codeplayer.action.admin.UserAction.hello())主要由以下几个部分组成:

  • action根包名(me.codeplayer.action):用于存放所有Action类的根包
  • action子包名(admin):用于按类别存放Action类的包,也可以没有
  • 类名(UserAction):类名必须以Action作为后缀。
  • 方法名(hello):处理当前请求的具体方法

Struts Ready将基于以下规则为该方法建立与之对应的URL之间的映射:

方法组成 映射规则 对应的URL部分示例
action根包名 对应项目的根URL me.codeplayer.action -> localhost:8080/p2p/(视实际情况而定)
action子包名 对应名称的URL父目录(也可以没有) admin -> admin/ 或 mobile.admin -> mobile/admin/
类名 去掉Action后缀,然后全部转小写。如果Action前面由多个单词组成,则多个单词之间以"-"连接 UserAction -> user/ 或 HelloWorldAction -> hello-world/
方法名 保持不变(大小写也保持不变) hello() -> hello 或 sayHi() -> sayHi

Struts Ready是如何知道哪些包下面存放的是Action类呢?此外,由于包名需要区分为根包和子包两部分,Struts Ready是如何知道哪一部分是根包的呢?这是因为我们在 struts.xml 中进行了如下配置:

<!-- 在哪些包中扫描action类(无需匹配locators) -->
<constant name="struts.convention.action.packages" value="me.codeplayer.action" />

Struts Ready将会在该配置所指定的包下面扫描所有的 Action 类和方法,并将该包视作action的根包。

特殊匹配机制

在正式服务器运行环境中,如果用户访问首页时,实际上调用的是 HomeAction.index() 这个方法。那么,用户是不是必须输入类似如下URL "http://example.com/home/index" 才能访问首页呢? 很明显,这并不是一个好的主意。一般而言,用户更希望直接输入 http://example.com 就能够访问首页。因此,Struts Ready插件也支持缺省的URL路径。

默认情况下:

  • 如果方法名的映射名称为缺省方法名(默认为index),则URL的方法名部分可以省略。
  • 如果action类的映射名称为缺省action名(默认为default),并且方法名的映射名称为缺省方法名,则URL的action类名和方法名部分都可以省略。

例如:

me.codeplayer.action.DefaultAction.index() 方法,我们可以直接通过 http://example.com 进行访问。 me.codeplayer.action.admin.DefaultAction.index() 方法,我们可以直接通过 http://example.com/admin/ 进行访问。 me.codeplayer.action.HelloAction.index() 方法,我们可以直接通过 http://example.com/hello/ 进行访问。

注意:出于性能和区分考虑,除了根URL外,缺省形式的URL必须以"/"号结尾。

你也可以在 struts.xml 中通过以下配置更改缺省的action名称和方法名称:

<!-- 默认的action名称(默认为default) -->
<constant name="struts.ready.default.action" value="default" />
<!-- 默认的method名称(默认为index) -->
<constant name="struts.ready.default.method" value="index" />

此外,你也可以通过如下配置启用/禁用该缺省匹配机制:

<!-- 是否启用默认的action和method(默认为true) -->
<constant name="struts.ready.enable.default" value="false" />

Struts Ready插件的result匹配机制

一般匹配机制

一般情况下,当我们执行请求方法,并处理完对应的业务逻辑后,我们还需要输出响应页面,从而完成与用户之间的请求交互。

在原始的Struts Action类中,执行方法需要返回一个字符串值。这个字符串就对应了我们在 struts.xml 中配置的 节点的 name 属性值,Struts 据此找到对应的JSP页面(或其他模板页面)并进行渲染输出显示。 在Struts Ready中,我们无须在 struts.xml 中定义result的配置,因为它能够根据约定,自动建立 result 字符串到对应页面路径的映射。 以 me.codeplayer.action.UserAction.hello() 返回的 result 为例,其具体映射规则如下:

result组成 映射规则
result字符串 对应的显示页面路径为(映射名称即是Action URL匹配的映射名称,没有的部分可省略):{页面文件根目录}/{action子包映射名称}/{action类名的映射名称}/{result字符串}.jsp

例如:

  • me.codeplayer.action.UserAction.hello() 方法返回"test",则对应的页面路径为:{页面文件根目录}/user/test.jsp
  • me.codeplayer.action.UserAction.hello() 方法返回"user_login",则对应的页面路径为: {页面文件根目录}/user/user_login.jsp
  • me.codeplayer.action.admin.DefaultAction.index() 方法返回"error",则对应的页面路径为: {页面文件根目录}/admin/default/error.jsp
  • me.codeplayer.action.admin.HelloWorldAction.hello() 方法返回"sayHi",则对应的页面路径为:{页面文件根目录}/admin/hello-world/sayHi.jsp

页面文件的根目录默认为 "/WEB-INF/content/" 。当然,这也是可以通过配置文件进行修改的:

<!-- result文件的所在路径(默认为:"/WEB-INF/content/") -->
<constant name="struts.convention.result.path" value="/WEB-INF/page/" />

此外,result的映射文件,不仅支持常用的JSP格式,也支持Freemarker、Velocity等模板文件。在查找匹配时,Struts Ready将默认按照以下顺序依次查找对应文件后缀(result type)的页面文件:

jsp -> htm/html -> vm -> ftl

你也可以使用以下设置自定义查找的result的type属性值(多个以英文逗号隔开)以及查找顺序:

<!-- result的type类型(默认为:"dispatcher,velocity,freemarker") -->
<constant name="struts.convention.relative.result.types" value="dispatcher" />

特殊匹配机制

为了更加便于常规使用,对于 Struts 的 result 常量 Action.SUCCESS(即字符串"success"),Struts Ready插件进行了相应的特殊处理:以JSP为例,它会先查找对应路径下的 success.jsp 文件,然后再查找 index.jsp 文件,并以第一个找到的文件输出响应。

About

基于Struts2 + Struts Convention的Struts插件,无需配置任何action即可享用类似"namespace/action/method.suffix"(后缀可省略)格式的URL访问方式。并集成文件下载、JSON输出等多种常用的Web功能,大幅减少冗余的配置和编码工作。

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published

Languages