Java相关课程系列笔记之十三Struts2学习笔记(建议用WPS打开).pdf
Struts2 学习笔记 Java 相关课程系列笔记之十三 笔记内容说明 Struts2(李翊老师主讲,占笔记内容 100%);目 录 一、Struts2 概述.1 为什么要用 Struts.1 什么是 MVC.1 JSP Model 1 和 JSP Model 2.1 Struts2 发展史.2 衡量一个框架的标准.2 Struts2 使用步骤.2 内容详解.3 Struts2 提供的方便之处.4 案例:简单登录(使用 Strust2).4 案例:修改、案例使用户不能绕过前端控制器.6 NetCTOSS 项目:显示资费列表.6 NetCTOSS 项目:资费列表分页显示.8 二、OGNL 技术.8 什么是 OGNL.8 OGNL 基本语法.8 OGNL 表达式中加“#”和不加“#”的区别.8 OGNL 体系结构.8 XWord 框架对 OGNL 进行了改造.9 ValueStack 对象结构.9 ValueStack 结构演示.9 Struts2 标签的使用.10 Struts2 对 EL 表达式的支持.10 案例:修改案例(使用 Struts2 标签和 OGNL 表达式).10 三、Action.11 Struts2 的核心组件.11 Struts2 的工作流程.11 在 Action 中访问 Session 和 Application.11 NetCTOSS 项目:用户登录.12 Action 属性注入.13 案例:重构 NetCTOSS 资费列表分页显示(使用属性注入).13 使用通配符配置 Action.13 案例:通配符配置(资费增、改、查).14 Struts2 中 Action 的设计经验.14 四、Result.15 Result 注册配置.15 Result 组件利用元素的 type 属性指定 result 类型.15 常见的 Result 组件类型.15 NetCTOSS 项目:资费删除.15 NetCTOSS 项目:基于 StreamResult 生成验证码.16 NetCTOSS 项目:基于 JSONResult 进行验证码检验.17 NetCTOSS 项目:添加资费模块中的验证资费名是否重复.19 自定义一个 Result.20 五、Struts2 标签.21 A 开头的标签.21 B 开头的标签.21 C 开头的标签.21 D 开头的标签.21 E 开头的标签.21 F 开头的标签.21 G 开头的标签.21 H 开头的标签.21 I 开头的标签.21 L 开头的标签.21 M 开头的标签.21 O 开头的标签.21 P 开头的标签.21 R 开头的标签.21 S 开头的标签.21 T 开头的标签.21 U 开头的标签.21 所有标签都具备的属性.21 案例:常用标签.21 六、拦截器.21 Struts2 详细流程图.21 拦截器的作用.21 拦截器的常用方法.21 自定义拦截器步骤.21 Struts2 内置的拦截器.21 案例:拦截器入门.22 拦截器栈.24 fileUpload 拦截器原理.24 案例:使用 fileUpload 拦截器实现文件上传.24 NetCTOSS 项目:登录检查拦截器.25 七、Struts2 中如何处理异常.25 异常一般出现在何处.25 如何配置异常.25 八、Struts2 中如何实现国际化.25 i18n.25 如何获得中文的 Unicode 编码.25 浏览器如何决定用哪个资源文件.25 资源文件的命名.25 资源文件的分类.25 实现国际化的步骤.25 九、NetCTOSS 项目.27 DAO 优化、重构、封装!【重要】.27 资费更新.27 导航条.27 十、项目经验.29 主键用 int 还是 Integer.29 “./”表示的意思.29 导入静态页面,样式、JS 失效问题.29 和标签.29 四种情形下的绝对路径写法.29 URL 和 URI.29 和.29 一、Struts2 概述 为什么要用 Struts 1)JSP 用 HTML 与 Java 代码混用的方式开发,把表现与业务逻辑代码混合在一起给前期开发与后期维护带来巨大的复杂度。2)解决办法:把业务逻辑代码从表现层中清晰的分离出来。3)2000 年,Craig McClanahan 采用了 MVC 的设计模式开发 Struts 主流的开发技术,大多数公司在使用。什么是 MVC 1)M-Model 模型 模型(Model)的职责是负责业务逻辑。包含两部分:业务数据和业务处理逻辑。比如实体类、DAO、Service 都属于模型层。2)V-View 视图 视图(View)的职责是显示界面和用户交互(收集用户信息)。属于视图的类是不包含业务逻辑和控制逻辑的 JSP。3)C-Controller 控制器 控制器是模型层 M 和视图层 V 之间的桥梁,用于控制流程。比如我们之前项目中写的 ActionServlet。JSP Model 1 和 JSP Model 2 1)JSP Model 1 最早的 JSP/Servlet 模式被我们称之为 JSP Model 1(JSP 设计模式 1),在其中有模型层 M,但是视图层 V 的 JSP 中包含了业务逻辑或控制逻辑,JSP 和 Servlet 紧密耦合。即:JSP(数据的展现和业务流程的控制)+JavaBean(对数据的访问和运算,Model)。2)JSP Model 2 JSP Model 2(JSP 设计模式 2)和 JSP Model1 不同之处再与将 JSP 中的业务逻辑和控制逻辑全部剔除,并全部放入控制层 C 中,JSP 仅仅具有显示页面和用户交互的功能。编程理念:高内聚低耦合。即:M:Model,用 JavaBean 来做(对数据的访问和运算)V:View,用 JSP 来作(数据的展现)C:Controller,用 Servlet 来作(业务流程的控制)Struts2 发展史 1)最早出现的 Struts1 是一个非常著名的框架,它实现了 MVC 模式。Struts1 简单小巧,其中最成熟的版本是。2)之后出现了 WebWork 框架,其实现技术比 Struts1 先进,但影响力不如 Struts1。3)在框架技术不断发展的过程中,有人在 WebWork 的核心 XWork 的基础上包装了 Struts1(算是两种框架的整合),由此,结合了 Struts1 的影响力和 WebWork 的先进技术,Struts2诞生了。4)Struts2 不是 Struts1 的升级,它更像是 WebWork 的升级版本。衡量一个框架的标准 1)健壮性:不健壮(带 0 的就是实验品),是健壮的(也不健壮,该版本 BUG 较多),3颗星。2)易用性:易用性越好,原理则越复杂,4 颗星。3)扩展性:Struts2,5 颗星。4)侵入性:即对写代码的要求(如:必须继承某个类才能怎么怎么样),越低越好。Struts2 低但也有侵入性,4 颗星。Struts2 使用步骤 step1:创建一个 JavaWeb Project,命名为 struts01(Struts2 只能用在 Web 项目里)step2:拷贝 Struts2 的核心 Jar 包到 WEB-INF/lib/下,基本功能核心 jar 包 5 个:1):Struts2 核心包,是 WebWork 内核。2):Struts2 核心包,是 Struts 框架的“外衣”。3):用来支持 OGNL 表达式的,类似于 EL 表达式,功能比 EL 表达式强大的多。4):freemarker 是比 JSP 更简单好用,功能更加强大的表现层技术,用来替代 JSP 的。在 Struts2 中提倡使用 freemarker 模板,但实际项目中使用 JSP 也很多。5):用于实现文件上传功能的 jar 包。step3:在中配置 Struts2 的前端控制器,Struts2 是用 Filter(过滤器)实现的前端控制器,它只负责根据请求调用不同的 Action 注意事项:原来的项目是用 Servlet 的方式作为控制器。内容:Struts2 Struts2 /*step4:在 WebRoot 下新建 jsp 文件夹,并在其中创建和 1)$error 2)Welcome,$name step5:在包下创建 WelcomeAction 类 private String name;private String error;各自的 get/set 方法 public String execute()().);if(name=null|.equals(name)error=用户名不能为空;return fail;name:+name);qualsIgnoreCase(name)error=不能使用名字 text登录;return fail;else return success;step6:写 Struts2 所需要的控制器配置文件,非常重要!该文件写请求与各个 Action(Java类)的对应关系,前端控制器会根据配置信息调用对应的 Action(Java 类)去处理请求 注意事项:在编写时放在 src 目录中,而部署后该文件位于 WEB-INF/classes/下!同样的,.properties 文件也放 src 下,部署后也在 WEB-INF/classes/下!程序一启动,就会被加载到内存,并不是现读的。内容如下:!DOCTYPE struts PUBLIC-td 格式要求,如:不在第一行可能会报错,标签中随意换行也会引起 404!step7:部署,地址栏输入:,发现一进入页面,则错误提示信息已经显示!为了解决该问题,需要再定义一个用于读取页面,而不是发请求后立即执行 WelcomeAction 类进行处理,处理应当在点击提交后才执行。step8:在中添加新的仅仅用于读取页面 /jsp/step9:重新部署,地址栏输入:,发现问题已经解决,可正常执行 内容详解 1):作用是为分组,标签下可有多个,而标签有如下属性:name=helloworld:唯一的标识,表示包名为 helloworld。注意事项:在相同包里的不能重复,不同包的可以重复!namespace=/day01:用来设置该包中的 action 的地址的命名空间。注意事项:命名空间中的“/”要写!也可以这么写:namespace=/,表示根命名空间,此时 Action 的访问路径为:即:/day01,此时 Action 的访问路径为:即:extends=struts-default:继承的包名,一般继承 Struts2 默认提供的 struts-default包,该包中定义了很多 Struts2 应用必须的组件(比如:拦截器);该 package 声明的位置在文件中。注意事项:appName 是部署时项目的名字!包只能继承包。不能说包继承某个类!2):作用是指明每个请求对应的 Action 类之间的对应关系(一个对应一个请求),下可有多个。而标签有如下属性:name=welcome:表示请求的名字为 welcome(即)class=:格式为“包名.类名”。指定了对应的 Action 类(Java 类)来处理请求。class 属性可以不写,不写则 Struts2 会默认为该添加一个 class(自动创建一个类,该类中的 execute()方法只有一个 return success;语句),作用是转发到对应的中指定的页面。method=xxx:用于指定在对应的 Action 类中要执行的方法名,该方法的返回值必须是 String 类型(这是规定!)例如:public String xxx().return string;.return string。3):作用是指明执行相应的 Action 类之后,显示哪种结果页。而标签有如下属性:name=success:是该 result 的名称,Action 返回哪一个 result 的 name 值,意味着要转发到哪一个 result 所对应的 JSP 地址,不写 name 属性,则默认为success。type=:默认 dispatcher(转发),还可写 json、stream、等 10 种 Struts2 提供的方便之处 1)数据的自动封装(输入属性)根据页面组件的 name 属性,自动封装到 Action 中对应的 name 属性中,即自动调用某属性的 set 方法。例如:在 JSP 页面,而在 Action 中则会自动给属性 private String name 赋值。注意事项:必须有 name 属性对应的 get 和 set 方法。2)数据的自动传递(输出属性)Action 中的属性在 JSP 页面可以直接用 EL 表达式拿到,即自动调用某属性的 get方法。例如:Action 中的属性 private String name;在 JSP 页面上可以直接$name得到对应的值。案例:简单登录(使用 Strust2)step1:新建 Web 工程 struts01,导入 Struts2 的五个基本核心包,并在中配置 Struts2 的前端控制器,代码,见中 step3 step2:在 WebRoot 下创建 jsp 文件夹,并放入和 1)用 户 名:密码:$errorMsg 注意事项:应用名前的“/”要写。标签中的 action 属性只写 action=也可!如果写成 action=day01/,则会出现叠加问题!可参考 Spring 笔记节。2)Welcome,$name step3:在包下新建 LoginAction 类 private String name;private String pwd;private String errorMsg;各自的 get/set 方法 public String execute()if(chang.equals(name)&123.equals(pwd)名-/jsp/jsp/step5:部署,地址栏输入:,发现一进入页面,则错误提示信息已经显示!为了解决该问题,需要再定义一个用于读取页面,而不是发请求后立即执行 LoginAction 类进行处理,处理应当在点击登录后才执行。step6:在中添加新的仅仅用于读取页面 /jsp/注意事项:当前配置指明了 class,则需要自己再写一个 LoginFormAction 类 step7:在包下新建 LoginFormAction 类 public class LoginFormAction public String execute()return success;step8:重新部署,地址栏输入:,发现问题已经解决,可正常执行 step9:优化:step6 也可这么写,此时也不用写 step8 中的 LoginFormAction 类了 /jsp/案例:修改、案例使用户不能绕过前端控制器 1)问题描述:和案例都存在一个问题,用户可以绕过前端控制器,直接访问 JSP 页面,如地址栏直接输入:,可显示页面,只是获取的名字为空。2)如何解决:step1:将 jsp 文件夹放入 WEB-INF 目录下。因为 WEB-INF 目录中的内容是受保护的,不能直接访问!但能转发过来。step2:修改案例中 result 的转发路径,也同理 /WEB-INF/jsp/WEB-INF/jsp/WEB-INF/jsp/step3:测试,访问登录页面 NetCTOSS 项目:显示资费列表 step1:新建 Web 工程:NetCTOSS,导入需要的包:Struts2 核心包、Oracle 数据库驱动包、JSTL 包(若为则不需要导这个包了,若为则需要导入!)step2:创建 COST_CHANG 表,并插入数据,别忘记 commit create table cost_chang(id number(4)constraint cost_chang_id_pk primary key,name varchar2(50)not null,base_duration number(11),base_cost number(7,2),unit_cost number(7,4),status char(1)constraint cost_chang_status_ck check(status in(0,1),descr varchar2(100),creatime date default sysdate,startime date,cost_type char(1);insert into cost_chang values(1,元套餐,20,0,元 20 小时/月,超出部分元/时,default,null,2);insert into cost_chang values(2,元套餐,40,0,元 40 小时/月,超出部分元/时,default,null,2);insert into cost_chang values(3,元套餐,100,0,元 100 小时/月,超出部分元/时,default,null,2);insert into cost_chang values(4,元套餐,200,0,元 200 小时/月,超出部分元/时,default,null,2);insert into cost_chang values(5,计时收费,null,null,0,元/时,不使用不收费,default,null,3);insert into cost_chang values(6,包月,null,20,null,0,每月 20 元,不限制使用时间,default,null,1);注意事项:在 Oracle 数据库中,语句大小写都可,但最终在数据库中显示的是大写形式。step3:新建到 src 目录下,将 username 和 password 修改为自己的数据库的用户名和密码 user=system password=chang url=jdbc:oracle:thin:localhost:1521:dbchang driver=:在包中创建 DBUtils 工具类,用于打开连接接和关闭连接 public class DBUtils private static String driver;private static String url;private static String user;private static String password;static try Properties props=new Properties(););driver=(driver);url=(url);user=(user);password=(password);(driver);catch(Exception e)throw new RuntimeException(数据库驱动加载错误,e);public static Connection openConnection()throws SQLException Connection con=(url,user,password);return con;public static void closeConnection(Connection con)if(con!=null)try ();catch(SQLException e)public static void main(String args)throws Exception Connection con=openConnection();tml”后缀改为动态页面“.jsp”后缀 1)首先在页面顶部添加页面指令:2)然后保存页面,最后再去将后缀改为“.jsp”,如:step15:在页面中保留标题行,使用 JSTL 标签和 EL 表达式修改数据行$小时$元$元/小时$开通 暂停 删除 注意事项:当学习了 Struts2 的标签和 OGNL 表达式后,上面的 JSTL 标签和 EL 表达式可都被替换掉。JSP 页面记得引入:step16:【项目经验】在 src 目录下分别创建和配置文件 1),相当于主配置,分别引入其他配置文件。为何要用这种形式因为当配置文件太大时,采用该方式便于管理 10000,dept);,dept);%7)其他详细用法,可参考节或第五章,只不过我们在 Struts2 标签中直接写 OGNL 表达式(字符串)即可!Struts2 对 EL 表达式的支持 EL表达式默认是访问 page、request、session、application 范围的数据。在 Struts2 中也可以使用 EL 表达式访问 Action 对象的属性。访问顺序是 page、request、root 栈(action)、session、application。当 EL 从 request 中获取不到信息后,会去ValueStack 对象的 root 栈获取,原因是:Struts2 重写了 HttpServletRequest 的方法。案例:修改案例(使用 Struts2 标签和 OGNL 表达式)首页 1上一页 上一页 s:if test=page 下一页 下一页 末页 二、Action Struts2 的核心组件 1)FC:前端控制器 2)VS:ValueStack 3)Action:动作 4)Result:结果 5)Interceptor:拦截器 6)Tags:标记库 Struts2 的工作流程 1)所有请求提交给 FC。2)根据配置信息确定要调用的 Action。3)创建一个 ValueStack 对象(每个请求都有一个独立的 VS 对象)。4)创建 Action 对象,把 Action 对象放到 VS 的栈顶,将 VS 对象存入到 request 中,存储的 key 为“”。5)控制器调用 Action 对象接收请求参数,并在方法中根据输入属性算输出属性。6)在调用 Action 之前或之后调用一系列 Interceptor。7)根据 Action 返回的字符串确定 Result(10 种类型)。8)调用 Result 对象,将 VS 中的数据按照特定的格式输出。9)很多情况下,Result 将转发到 JSP,JSP 页面用 Tags 取出数据并显示。10)请求处理完后,将 ValueStack 对象和 Action 对象销毁。在 Action 中访问 Session 和 Application 1)利用 ActionContext,返回 Map 类型 ActionContext context=();Map session=();(username,chang);Map request=(Map)(request);Map app=();注意事项:不推荐使用 ActionContext 访问 Session 的方式,因为这种方式的“侵入性”较强。ActionContext 是 Struts2 的 API,如果使用其他框架代替目前的 Struts2 框架,而我们实际项目中的 Action 的数量非常庞大,每个类都修改的话,会非常麻烦。推荐使用实现 SessionAware 接口的方式。2)ServletActionContext 返回的是 Servlet 使用类型 HttpServletRequest httpreq=();HttpSession httpsession=();ServletContext httpapp=();3)在 Action 中实现 Aware 接口,由框架底层将对象注入给 Action 变量 RequestAware:Map 类型的 request SessionAware:Map 类型的 session ApplicationAware:Map 类型的 application ServletRequestAware:http 类型的 request ServletResponseAware:http 类型的 response ServletContextAware:http 类型的 application NetCTOSS 项目:用户登录 在原项目的基础上,添加登录模块。step1:创建 ADMIN_CHANG 表,并插入数据,别忘记 commit create table admin_chang(id number(11)constraint admin_info_id_pk_chang primary key,admin_code varchar2(30)unique not null,password varchar2(30)not null,name varchar2(30)not null,telephone varchar2(30),email varchar2(50),enrolldate date not null );insert into admin_chang values(1001,admin,123,管理员,to_date(2013-08-05 15:30:30,yyyy-mm-dd hh24:mi:ss);insert into admin_chang values(1002,user,123,用户,to_date(2013-08-22 08:30:23,yyyy-mm-dd hh24:mi:ss);step2:在包下,新建 Admin.实体类 private Integer id;.step9:为了方便统一管理 KEY,所以在包下新建 Constants 接口,用于定义常量 KEY public interface Constants public static final String ADMIN_KEY=;step10:所以 BaseAction 要再实现 Constants public class BaseAction implements SessionAware,Constants .step11:拷贝登录页面、主页的静态页面片工程中,并修改后缀为“.jsp”,结构如下:step12:修改,添加标签、帐号和密码添加 name 属性,用于提交给服务器、页面引入Struts2 标签:注意事项:在标签中,href=javascript:;一般和 onclick 一起使用,主要用于触发点击事件,单独用相当于空连接:href=#。由于放置位置的原因,中的样式、图片链接,都要加“./”,如:href=./styles/step13:在 src 目录下新建 /WEB-INF/jsp/WEB-INF/jsp/WEB-INF/jsp/step14:在中引入 注意事项:在 xml 文件中,有时多个空行都会报错!step15:部署,测试,地址栏输入:,输入正确则进入主页面,输入错误才出现提示信息:Action 属性注入 在配置中,为 Action 组件的属性指定一个初始值,该值在创建 Action 对象时注入,可以将一些变化的参数值,利用该方法指定。例如:pageSize、password 密码、dir存储路径等。例如:public class ParamAction private String param1;private int param2;各自 get/set ABC 10 案例:重构 NetCTOSS 资费列表分页显示(使用属性注入)一般情况下,为该 Action 定义一些属性,有些是用于输入的(比如表单提交用户名、密码),有些是用于输出的(比如员工列表、当前页数)此外,还有一些属性是 Action 自用的参数,比如节分页案例中,每页显示多少行的行数,我们是写死在程序里的。例如案例 step4:costList=(page,2);1Action method=2 /WEB-INF/jsp/3.jsp 1Action:表示 name 属性中第一个“*”星号匹配的字符串。method=2:表示方法名为 name 属性中第二个“*”星号匹配的字符串。3.jsp:表示显示的页面为 name 属性中第三个“*”星号匹配的字符串。案例:通配符配置(资费增、改、查)step1:新建项目 struts03,导入 Struts2 核心包,配置前端控制器 step2:在 WebRoot 目录下新建 资费查看 资费更新 资费添加 step3:在 action 包中新建 CostAction,并定义三个方法 public String add()资费添加操作);return success;public String list()资费查看操作);return success;public String update()资费更新操作);return success;step4:新建 step5:部署,测试,地址栏输入:,效果如下:step6:点击三个连接,则分别执行了各自的方法:Struts2 中 Action 的设计经验 1)配置文件的拆分。2)控制好 Action 类的粒度(不能太多或就 1 个)。3)Action 和 Servlet API 的耦合度要低,如若非要用底层 API,实现 XXXAware 接口,可以封装拦截器,如:sessionAware。4)适当用通配符。5)根据输入算输出。三、Result Result 组件是一个类,职责是生成视图。该类必须实现 Result 接口,并且将约定的execute 方法实现,在该方法中编写了生成响应输出的逻辑代码。视图可以是多种多样的(比如 JSP、JSON、报表、freeMarker 等),这些视图都可以由Result 负责。Result 注册配置 result-type name=类型名 class=实现类 其它类型的 result Result 组件利用元素的 type 属性指定 result 类型 result 参数指定 常见的 Result 组件类型 1)dispatcher(默认):以请求转发方式调用一个 JSP,生成响应视图。2)redirect:以重定向方式调用一个 JSP,生成响应视图。3)redirectAction:以重定向方式调用一个 Action。参数:actionName:要定向到的 Action 的 name 值。namespace:要定向到的 Action 所在包的命名空间的名字。4)chain:以请求转发方式调用一个 Action。5)stream:以字节流方式响应,将 Action 中指定的一个 InputStream 类型的属性以字节流方式输出。参数:inputName:OGNL 表达式,表示要输出的一个输入流对象(要输出数据的来源)。contentType:用于设置响应中 contentType。6)json:以 json 字符串响应,将 Action 中指定的属性,拼成一个 json 字符串输出。参数:root:OGNL 表达式,要做成 JSON 字符串的对象 NetCTOSS 项目:资费删除 step1:在CostDAO 接口中添加删除 public void delete(int id)throws DAOException;step2:在 CostDAOImpl 类中实现该方法 private static String delete=delete from COST_CHANG where ID=;./注意事项:由于此时还是用的 JSTL 中的 forEach 标签,所以必须要有 var 属性绑定变量,而之前 var 绑定的是 cost,即 var=cost,所以 EL 表达式中要写成$。如果用的是 Struts2 标签进行循环,则 EL 表达式写$id 即可。step6:将中的循环改为 Struts2 标签和 OGNL 表达式:(记得导入 Struts2 标签)小时 元 元/小时 !-s:if test=status=1暂停开通 这些状态也是数据,写这不好,所以采取定义到 Action 或 DAO 中-step7:将状态数据定义到 DAO 中,所以在 CostDAO 接口中添加定义 public Map getCostStatusOptions();step8:在 CostDAOImpl 中实现该 Map 方法 public Map getCostStatusOptions()Map statusOptions;statusOptions=new LinkedHashMap();(0,开通);(1,暂停);(2,删除);return statusOptions;step9:在 ListCostAction 中添加 statusOptions 属性,并计算 private Map statusOptions;get/set 方法 public String execute()throws Exception statusOptions=();step10:部署,测试,地址栏输入:,点击删除,执行成功并重定向到 NetCTOSS 项目:基于 StreamResult 生成验证码 中:step1:在包下,新建 VerifyCodeUtils 类,用于生成验证码 private String code;private byte codeArr;private static char seq=A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R ,S,T,U,V,W,X,Y,Z,0,1,2,3,4,5,6,7,8,9;public String getCode()return code;public byte getCodeArr()return codeArr;public void generate(int width,int height,int num)throws Exception Random r=new Random();etTime();alt=验证 码 title=点击更换/注意事项:onclick=#+(new Date().getTime();,中间的“#”号要加,否则点击图片更换验证码时,会报异常:。step6:部署,测试,地址栏输入:,验证码可正常显示,点击图片也会更换 NetCTOSS 项目:基于 JSONResult 进行验证码检验 Struts2 的中没有提供 json 类型的 Result,所以要扩展 Result。step1:导入 等一系列包到工程中 step2:该 jar 包中有个,里面添加了 json 的配置,并且继承了,所以以后的配置文件只要继承 json-default 包,也就有了 struts-default 中的配置 step3:修改,使包继承 json-default step4:在包下的 VerifyCodeAction 中,添加验证方法 public String verify().step6:部署,测试,地址栏输入:,先产生一个验证码,然后地址栏再输入:,1234 是随便写的,此时页面返回 false。step7:在 WebRoot 目录下新建 js 文件夹,将(jquery 框架)放入。同时新建文件,在其中添加自定义验证方法,可参考 jQuery 笔记案例$.=function(errorMsg,errorCtn)ext();return true;else$(errorCtn).text(errorMsg);return false;$.=function(url,errorMsg,errorCtn)jax(url:url+name+=+value,dataType:json,async:false,success:function(data)if(data)$(errorCtn).text();b=true;else$(errorCtn).text(errorMsg););return b;step8:在中引入这两个 js 文件,并编写验证代码 function validateForm()equired(帐号必须填写,#msg_adminco