Struts2第一天
整体课程安排:3天知识点+2天练习
第一天:入门(action和result结果集)--一般的请求+响应
第二天:请求数据处理相关(参数接收、类型转换、合法性校验、国际化)
第三天:拦截器、响应数据处理相关(值栈、OGNL、struts2标签)
第四天、第五天练习。(文件的上传下载+CRUD综合练习)
课程内容:
- Struts2概述(struts2框架、前世今生、开发包下载、开发包结构)
- 快速入门HelloWorld(入门理论、示例、运行流程分析)
- Struts2运行原理(运行原理图、相关概念简介-核心控制器、拦截器、Action、结果集、核心配置文件、DTD约束提示配置)
- Struts常用配置(核心配置文件种类和加载顺序、Action相关映射配置详解、Action默认处理类和默认Action、常量配置、配置文件分离)
- Action的访问相关(Action的三种书写格式、Action的方法执行三种方式)
- Action使用Servlet相关API(直接调用、间接调用)
- result结果集的使用(局部结果集和全局结果集、结果集的类型type)
课程目标:
- 学会使用基本的请求和响应
- 学会action的最主要的编写方式(ActionSupport)
- 学会如何调用action中的指定方法(通配符)
- 获取servelt API(静态方法调用)
- 掌握结果集的使用
-
概述
Struts2是什么
Struts2是一个非常优秀的、免费开源的MVC框架(Model2设计模型),用于创建Web应用。
- 框架
框架是可以重复使用的一些或一整套代码,通常与具体业务无关,也可以认为是软件的半成品。
框架的好处:规范开发流程。
- MVC
全名是Model View Controller,是模型(model)-视图(view)-控制器(controller)的缩写,是一种软件设计模式,或软件设计思想。
- 视图-view:负责数据的显示。
- 模型-model:负责数据的处理。
- 控制器-controller:负责调度不同的逻辑代码(model)并显示到视图(view)。
MVC的设计思想在很多中语言中都有实现,例如java、.net等
JSP规范提出了两种用JSP技术建立应用程序的开发模式,分别称作JSP Model 1 和JSP Model 2:
-
Model1: JSP (控制、显示)+ JavaBean (数据处理)
缺点:页面代码太多,加载速度慢,调试麻烦。
-
Model2 : Servlet (控制)+ JSP(显示) + JavaBean(数据处理)--符合MVC思想架构模式。
适合大型项目
Java web企业应用开发根据又根据Model2(也可以说是根据MVC思想)制定了三层结构体系(来自于JavaEE规范):
- 表现层(页面数据显示、页面跳转调度)jsp/servlet
- 业务层(业务处理和功能逻辑、事务控制)service
- 持久层(数据存取和封装、和数据库打交道)dao
Struts2就是一个表现层框架,可以用来简化表现层代码开发的。
Struts2核心功能:了解
允许POJO对象作为action
Action中的servlet API不再与方法耦合
支持更多视图技术(jsp、freemarker、velocity)
基于Spring AOP思想的拦截器机制
struts2 由来
百度百科:
Struts2由传统Struts1和WebWork两个经典框架发展而来。
历史由来:
Java兴起 98年(最早进行java开发分方向 j2se 、j2ee、 j2me )
J2EE 企业级应用软件开发
2000 年左右出现struts1 (几乎所有开发人员都在用)
随着互联网的发展struts1 设计慢慢开始存有缺陷和不足
Webwork框架的出现
该框架的设计思想比struts1 先进的多,例如:引入拦截器机制、 ognl 表达式语言
struts2是在 struts 1和WebWork的技术基础上进行了合并的全新的Struts 2框架,Struts 2以WebWork为核心,但开发流程类似于struts1。
Struts2的漏洞事件:
软件开发包
官网地址:http://struts.apache.org/
官方网站提供了两大系列版本1.x和2.x,分别成为Struts1和Struts2。
最新版本分别为:1.3.10 和2.5.2。
本课程学习Struts2。
开发包下载:
官方最新版本:struts-2.5.2-all.zip
本次课程使用的版本是:2.3.15.
struts2 开发包结构
使用框架需要导入jar包(也就是人家写好的一些代码)
解压之后,源码和jar包都在里面了。
src文件夹下的是源码
在lib文件夹中有很多的struts需要用到的包,包括和spring的整合包,json的插件包等等
总共126个jar,实际上在我们开发的时候用不到这么多。
docs中的文档几乎看不懂,要快速入门我们可以看一下apps中的一些案例:
解压之后的struts2-blank中就是一些简单的应用,结构如下 ;
打开其中的web.xml:里面就配置了struts2需要用到的过滤器
struts.xml :struts2的灵魂文件
有了web.xml和struts.xml的配置文件,还需要jar包,jar包在lib文件夹中。
总结13个jar包的用途:
-
Struts2快速入门
理论
Struts2和Servlet开发对比
Servlet:
缺点:一个功能对应一个请求,一个请求对应一个serlvet,每一个servlet中都可能有重复的代码。
Struts:
Struts2的基本运行流程图:
Web.xml作用:配置前端控制器
Struts.xml作用:配置请求分发
【小结】
所有的重复代码都交给前端控制器去调用完成和调度,开发者只需要在Aciton中编写业务处理相关代码即可。
入门
创建工程
选择jdk版本,不要选yes,否则就使用了jdk1.5的版本,对后面使用注解支持不好,要用jdk1.5以上的。
当然也可以改成jdk1.7的。
导入jar包
Jar包地址: \Struts_2.3.15.3\struts-2.3.15.3\apps\struts2-blank\WEB-INF\lib下的13个jar包
配置web.xml
Ctrl+shift+T :然后输入StrutsPrepareAndExecuteFilter后可以联想出该文件,然后复制它的绝对路径即可
<?xml
version="1.0"
encoding="UTF-8"?>
<web-app
id="WebApp_9"
version="2.4"
xmlns="http://java.sun.com/xml/ns/j2ee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd">
<display-name>struts</display-name>
<!-- 配置sturts前端控制器 -->
<filter>
<filter-name>struts</filter-name>
<filter-class>org.apache.struts2.dispatcher.ng.filter.StrutsPrepareAndExecuteFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>struts</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
<welcome-file-list>
<welcome-file>index.html</welcome-file>
<welcome-file>index.htm</welcome-file>
<welcome-file>index.jsp</welcome-file>
<welcome-file>default.html</welcome-file>
<welcome-file>default.htm</welcome-file>
<welcome-file>default.jsp</welcome-file>
</welcome-file-list>
</web-app>
编写请求页面
创建页面:
hello.jsp :
success.jsp :
编写action
创建包和action :
编写一个处理具体业务的action,必须实现Action接口
具体内容如下:
public
class HelloAction implements Action{
//exectue为实现Action接口后默认执行的方法,String是返回的结果集视图名称
//通过这个结果集视图名称到struts.xml中可以找到对应跳转的结果集视图
@Override
public String execute() throws Exception {
System.out.println("hello world!");
//响应页面跳转:
//返回的结果集视图名称:名字随便取(必须在struts.xml中能找到相同名称的配置)
return
"success";
}
}
配置struts.xml
1、可以重上述案例的struts.xml中复制过来
2、Struts.xml中的约束路径:在struts2-core核心jar包下的struts-2.3.dtd中
配置完成后如果还是不能联想出struts相关的标签名称,那是因为没有配置相应的dtd约束文件
注意:如果电脑可以上网,就会自动给你下载dtd约束文件。
如果不能上网就需要手动配置dtd约束文件,手动配置如下:
复制:http://struts.apache.org/dtds/struts-2.3.dtd,打开window属性
Laction的文件路径为:之前解压文件struts-2.3.15.3\src\core\src\main\resources下的struts-2.3.dtd
点击确定后,关闭struts.xml并且重新打开,此时用alt+/就可以联想出相关标签.
在工厂的src目录下创建一个struts.xml文件,然后将上述约束复制到struts.xml中
Struts内容如下:
<?xml
version="1.0"
encoding="UTF-8"?>
<!DOCTYPE struts
PUBLIC
"-//Apache Software Foundation//DTD Struts Configuration 2.3//EN"
"http://struts.apache.org/dtds/struts-2.3.dtd">
<struts>
<package
name="default"
extends="struts-default"
namespace="/">
<!—配置action和访问的类之间的对应关系 -->
<!-- name:请求的虚拟路径名称 class:指向的具体路径 -->
<action
name="hello"
class="cn.itcast.struts2.a_hello.HelloAction">
<!-- name:action中通过String值来找到这个标签,并返回相应的结果集视图 -->
<result
name="success">/a_hello/result.jsp</result>
</action>
</package>
</struts>
测试
发布到tomcat :
启动tomcat :
测试访问:
1、控制台打印:
2、显示视图
关联源代码:
选择开发包:
执行流程分析
客户端发送请求,请求路径经过前端控制器,前端控制器读取struts.xml中的配置来分发到HelloAction中,执行默认方法后返回结果集视图名称,在struts.xml中找到相应的结果集视图并显示。
由此可见:struts.xml是这个过程中的核心,通过这个struts.xml才能找到具体的action以及响应结果集视图。
实际上在前端控制器走第三步分发action请求之前,还调用了一组拦截器,由于咱们并没有关于拦截器的业务因此什么都没有感觉到。
Struts运行原理分析
StrutsPrepareAndExecuteFilter (准备 和 执行):它是Struts2的核心控制器,它采用前端控制器模式,对用户的请求进行控制处理。相当于我们的学校的前台。
Interceptor拦截器,Struts2可复用的代码功能都是基于(通过)它来实现的。struts2有很多内置拦截器,用来实现不同的功能。我们看看有哪些默认的拦截器:
参考 struts-core.jar 提供 的struts-default.xml 配置文件
默认情况下, 访问每个Action ,默认拦截器defaultStack会从上往下依次执行。
通过断点调试发现:其中包含的这些拦截器都会依次执行(按照从上倒下的顺序),即可依次实现不同的功能。 而这些功能都是通用的、经常用到的(重复的),都由拦截器来实现。
随便找一个找到具体的类进行断点调试即可
Action是执行具体的业务逻辑代码的,它在拦截器之后执行。(演示。。。)
【查看类型代码】shift+ctrl+t
通过断点调试看到,前端控制器在分发action之前就调用了拦截器。
ConfigBrowser插件(了解)
ConfigBrowser可以用来查看struts2的各类配置。
知识点:如何使用struts2的插件。
Strut22的插件的安装,只需要将插件复制到工程的lib中即可。
导入插件包 ,启动项目 ,访问:
http://IP地址:端口/项目名称/config-browser/index.action
通过路径,查看struts2 Action 配置加载情况
-
常用配置
核心配置文件的分类及读取顺序
关键词:核心配置文件的种类和加载顺序,前端控制器会依次加载依次配置文件。
Struts2的核心配置文件有两大类共6种方式,按照加载顺序分别说明如下:
第一类:框架内置的配置文件:
- Struts2框架内置的常量配置文件:default.properties,位于struts-core.jar包的org.apache.struts2包下。作用:定义了一些常量(键值对)。一些功能开关。
- Struts2框架内置的核心配置文件:struts-default.xml,位于struts-core.jar包下。作用:定义了Bean元素、结果集类型、拦截器等。注意默认包:
struts.xml中extends :
实际就是继承了struts-default
在struts2的框架体系中,struts.xml中的package主要是用来实现一些配置的复用,在使用时编写子包来继承struts-default,如此就可以复用struts-default中的一些功能。
-
Struts2框架插件配置文件:struts-plugin.xml,位于每个插件包的根目录。作用:用于扩展插件的一些配置。
随便复制一个struts的插件包到工程中,里面就有struts-plugin.xml。
每一个插件包中的struts-plugin.xml配置都不一样
第二大类:用户自定义的配置文件:
-
用户自定义核心文件配置:struts.xml,位于用户工程的src下。作用:用于用户开发的相关配置,如配置package、action等。
一般都会继承struts-default,即继承struts-default.xml中的配置,否则就无法使用结果集以及拦截器的配置。
-
用户自定义常量配置文件:struts.properties,一般位于用户工程的src下。只能用于配置一些常量(覆盖内置的default.properties中的常量配置)
一般也不太用,可以在struts.xml中直接配置常量
- web.xml中配置struts2常量(了解)
【注意】
- 配置文件加载的顺序问题:后加载的配置文件的配置内容,可以覆盖先加载的配置文件的配置内容。因此,自定义的可以覆盖默认的配置。
- 开发人员主要采用struts.xml(可配置action映射、常量等) 或者 struts.properties (只能配置常量)
注意:以上第一类的3个配置文件都是不能做修改的,全部配置文件的读取顺序为:1、default.properties---2、 struts-default.xml---3、 struts-plugin.xml---4、struts.xml---5、struts.properties---6、web.xml中的常量配置
当然,常量的配置一般不在struts.properties和web.xml中来配置,在struts.xml中配置即可
核心配置文件struts.xml
关键字:package、action、result、constant(常量的配置)
-
<package>
package标签:用来管理Action和result, 实现包内配置复用 (通过包继承实现 )
- name属性:包的名称,在struts容器中必须具有唯一性
- extends属性:继承父package(中的功能),通常都继承struts-default(默认包内定义大量结果集类型和拦截器)
- namespace属性:名称空间用来标识一个路径,来区分不同action的访问路径。
【示例】
演示不同的包中相同的action通过不同的名称空间来访问。
步骤一:创建新的包和action
创建包:
action:
//编写action需要实现action接口
public
class HelloAction implements Action{
//默认执行的方法
@Override
public String execute() throws Exception {
System.out.println("hello world ......config!");
return
"success";
}
}
步骤二:修改struts.xml
亲:如果HelloAction是复制粘帖的话,记得把class中的绝对路径换成新的HelloAction。
创建一个新的package:
<struts>
<!-- 配置常量 -->
<constant
name="struts.devMode"
value="true"></constant>
<!-- package包 name:包名随便取但是需要保证唯一性 extends:用来继承核心包 -->
<package
name="default"
extends="struts-default"
namespace="/">
<!-- action:具体请求的分发 name:请求名称 class:具体要执行的类的路径 -->
<action
name="hello"
class="cn.itcast.struts.a_hello.HelloAction">
<!-- 配置结果集响应 name:结果集视图名称 /a_hello/result.jsp:需要响应的结果集视图 -->
<result
name="success">/a_hello/result.jsp</result>
</action>
</package>
<package
name="default1"
extends="struts-default"
namespace="/config">
<action
name="hello1"
class="cn.itcast.struts.b_config.HelloAction">
<result
name="success">/a_hello/result.jsp</result>
</action>
</package>
</struts>
注意:在同一个package下,action的name属性也不能重复,必须是唯一的。
步骤三:测试
访问测试:http://127.0.0.1:8080/struts2_day01/config/hello.action
Action的访问路径= IP地址:端口+namespace包名称空间 + action的name属性
-
<action>
action标签:用来管理具体的Action,实现请求和响应相关。
- name属性:action的名字,用于配置请求。
- class属性:action对应的类,该类编写了action具体业务逻辑代码。
-
<result>
result标签:结果集视图,用于配置响应,标签内的值是响应的地址。
- name属性:结果集(结果集视图)的名字,action会根据该名字跳转到对应的地址。
-
配置项的默认值
package-namespace默认值
步骤一:再次创建一个新的package
不写namespace:即为默认值
<!-- 默认值 -->
<package
name="default2"
extends="struts-default"
>
<!-- 配合默认action -->
<default-action-ref
name="errorAction"></default-action-ref>
<!-- 配置默认action的具体映射 -->
<action
name="errorAction"
class="cn.itcast.struts.b_config.HelloAction">
<!-- 配置结果集 -->
<result >/errorPage/errorPage.jsp</result>
</action>
<action
name="hello2"
>
<result >/a_hello/result.jsp</result>
</action>
</package>
步骤二:测试访问
访问无误
注意:不设置namespace的话,期默认值是namespace="",而不是很多人以为的namespace="/"。
由于期访问路径一样是http://127.0.0.1:8080/struts2_day01/hello.action,所以会造成很多人的误解。
步骤四:分析
默认值到底是namespace=""还是namespace="/",从配置第二个package就可以看出来:
如果第二个package配置namespace=""的话就会产生冲突以至于报错,而如果配置成namespace="/"的话就不报错了,由此可见namesapce的默认值应该是namespace=""。
result-name默认值
步骤一:修改Struts.xml
不写namespace:即为默认值
<!-- 默认值 -->
<package
name="default2"
extends="struts-default">
<!-- 配合默认action -->
<default-action-ref
name="errorAction"></default-action-ref>
<!-- 配置默认action的具体映射 -->
<action
name="errorAction"
class="cn.itcast.struts.b_config.HelloAction">
<!-- 配置结果集 -->
<result >/errorPage/errorPage.jsp</result>
</action>
<action
name="hello2"
>
<result >/a_hello/result.jsp</result>
</action>
</package>
步骤二:测试
测试访问hello,执行结果无误
步骤三:分析
如果result标签中的name值不配置的话,其默认值是success,但是如果使用默认值的话需要在action中返回结果集名称为SUCCESS
action-class默认值
步骤一:修改struts.xml
设置默认的action-class(即不在action标签中配置class属性)
<!-- 默认值 -->
<package
name="default2"
extends="struts-default"
>
<!-- 配合默认action -->
<default-action-ref
name="errorAction"></default-action-ref>
<!-- 配置默认action的具体映射 -->
<action
name="errorAction"
class="cn.itcast.struts.b_config.HelloAction">
<!-- 配置结果集 -->
<result >/errorPage/errorPage.jsp</result>
</action>
<action
name="hello2"
>
<result >/a_hello/result.jsp</result>
</action>
</package>
步骤二:测试请求
请求成功,但是后台不打印任何数据
步骤三:分析
如果不指定action标签中的class,那么会默认执行com.opensymphony.xwork2. ActionSupport,并执行其中的execute方法。
由于是使用的默认class—ActionSupport,因此后台没有执行任何逻辑,直接返回了SUCCESS结果集视图名称,跳转到指定页面。
默认action
通常我们的请求路径如果找不到相应的action就会报错如下:
我们可以配置一个默认的action,一旦找不到相应的action就会执行默认action。
【示例】
步骤一:修改struts.xml
配置默认action:
<!-- 配置默认值 -->
<package
name="default2"
extends="struts-default"
>
<!-- 配置默认action -->
<default-action-ref
name="errorAction"/>
<action
name="errorAction"
class="cn.itcast.struts.b_config.HelloAction">
<result>/errorPage/errorPage.jsp</result>
</action>
<!-- action:具体请求的分发 name:请求名称 class:具体要执行的类的路径 -->
<action
name="hello1"
>
<!-- 配置结果集响应 name:结果集视图名称 /result.jsp:需要响应的结果集视图 -->
<result >/a_hello/result.jsp</result>
</action>
</package>
步骤二:创建响应页面
测试访问一个不存在的action:跳转到指定的错误页面-errorPage.jsp
errorpage.jsp需要配置加载所需图片:
<div align="center"><img src="./images/404-xsy.jpg"/></div>
页面内容:
<body>
<div
align="center"><img
alt="图片"
src="b_config/404-xsy.jpg"></div>
</body>
步骤三:测试
【面试知识】
区分Action默认处理类(class)和默认执行的Action
- Action默认处理类:是配置了action,但没有指定action的class,会自动调用执行默认的action类,该action在struts2的struts-default.xml文件中有配置内置默认的,也可以自定义一个进行覆盖(一般很少这样做)。
- 默认执行的action:是用户访问的路径中找不到这个action,那么struts2会去寻找被访问的包中是否有配置一个默认执行的action,如果有配置,则执行默认配置的action,如果没有配置,则显示action找不到的错误信息哦
【扩展了解】
访问action的时候,struts有个内部规律(了解)
如果你访问的路径中,没有定义action,会自动向上层路径寻找。
http://127.0.0.1:8080/struts2_day01/aa/bb/cc/hello.action
先在aa/bb/cc/找helloAction
找到就ok,找不到,继续:
aa/bb/中找hello.action
……..
一直找到hello.action
其实最终会找到根目录下的hello.action:http://127.0.0.1:8080/struts2_day01/aa/bb/cc/hello.action
访问测试:
缺点:代码可读性不好,不建议这样做。
<constant>常量配置
default.properties定义了struts2框架的大量常量,开发者可以通过改变这些常量来满足应用的需求。
要修改这些常量,可以通过自定义核心配置来覆盖默认的值(后加载的配置文件常量,可以对先加载配置文件进行覆盖),有如下三种方式(开启开发者模式):
- 配置src/struts.xml
<constant name = "struts.devMode" value="true"/>
- 配置src/struts.properties
struts.devModel = true
- 配置web.xml
【常用常量分析】
- struts.i18n.encoding=UTF-8
相当于 request.setCharacterEncoding("utf-8"); 解决post 请求乱码问题。
因此,在Struts2开发时,无需关注post 乱码问题。
- struts.devMode=true
开发模式下使用,这样可以打印出更详细、友好的错误信息
当设置开发者模式devMode =true 会激活xml配置文件自动重新加载功能。
- struts.configuration.xml.reload=true :修改struts.xml 配置后,无需重启服务器;
- struts.i18n.reload = true,国际化文件修改之后也不需要重启服务器。会自动加载生效。
- struts.action.extension=action,,
Action请求映射路径 默认扩展名
问题: http://localhost:8080/struts2_day1/hello 也可以访问 HelloAction
如果请求路径 必须以.action结尾
- struts.ui.theme=xhtml
设置页面标签显示样式
- struts.enable.DynamicMethodInvocation=true
访问Action 支持 动态方法调用
- struts.multipart.maxSize=2097152
上传文件的大小限制
小结:
如果需要更改某个常量的值,在default.properties中找到变量,将名字复制到struts.xml中进行覆盖它的值。
配置文件的分离
【开发场景】
在大部分应用里,随着应用规模的增加,系统中Action的数量也会大量增加,导致struts.xml配置文件变得非常臃肿。为了避免struts.xml文件过于庞大、臃肿,提高struts.xml文件的可读性,我们可以将一个struts.xml配置文件分解成多个配置文件,然后在struts.xml文件中包含其他配置文件。下面的struts.xml通过<include>元素指定多个配置文件:
<struts>
<include file="struts-part1.xml"/>
<include file="struts-part2.xml"/>
</struts>
通过这种方式,我们就可以将Struts 2的Action按模块添加在多个配置文件中。
Struts.xml引用其他配置文件
如下:
-
Action的编写和方法访问
Action编写的三种方式
Struts2支持三种书写Action的方式
自定义类实现Action
步骤一:新建包和action
新建一个包:cn.itcast.struts.c_action
Action :
public
class Demo1Action implements Action{
@Override
public String execute() throws Exception {
System.out.println("action的第一中编写方式:实现action接口");
return
NONE;
}
}
步骤二:配置Struts.xml :
不配置结果集视图
<!-- action的第一种编写方式:实现action接口 -->
<action
name="demo1"
class="cn.itcast.struts.c_action.Demo1Action"/>
步骤三:测试
执行结果没有响应,原因是返回了NONE的结果集名称
后台打印:
【内置的视图名称】
Action 接口提供一组 常用的内置的逻辑视图名称:
- SUCCESS 成功视图
- NONE 没有结果视图,用户自己生成响应数据
- ERROR 错误视图
- INPUT 输入视图 (数据输入非法,要求用户重新输入)
- LOGIN 登陆视图 (如果用户未登陆,使用登陆视图)
自定义类继承ActionSupport(重点)
步骤一:编写Action
public
class
Demo2Action
extends ActionSupport{
@Override
public String execute() throws Exception {
System.out.println("action的第二中编写方式:实现ActionSupport");
return
NONE;
}
}
步骤二:配置Struts.xml
<!-- action的第二中编写方式:实现ActionSupport -->
<action
name="demo2"
class="cn.itcast.struts.c_action.Demo2Action"/>
步骤三:测试
这个编写方式是我们最常用的,ActionSupport实现了更多的接口,更加有利于扩展。
自定义POJO(了解)
POJO(Plain Old Java Object简单的Java对象),实际就是普通的JavaBean。
struts2使用反射的机制来进行调用执行。
【注意】关于execute方法的编写要求,必须满足:
- public修饰符
- String返回值
- 无参数方法
步骤一:编写Action
需要手动编写execute方法---固定为 public Stirng execute(){}(还没有讲到方法编写,这是暂时的写法)
public
class Demo3Action {
public String execute(){
System.out.println("action的第三种编写方式:自定义pojo作为action");
return
"none";
}
}
步骤二:配置Struts.xml
<!-- action的第三种编写方式:自定义pojo作为action -->
<action
name="demo3"
class="cn.itcast.struts.c_action.Demo3Action"/>
步骤三:测试
后台打印:
其实,pojo的方式,底层使用反射机制,自动调用execute方法
【小结】
一般情况下都使用继承ActionSupport类的方法。
其他两种,大家了解就行了。
Action的方法编写(重点)
问题:是不是默认只能执行execute这个方法?是不是只能写一个方法呢?
否。
Action访问指定方法的方式:
- execute
- 可以通过method属性来指定需要执行的方法
- 通过通配符来指定需要执行的方法(重点)
- 通过动态方法调用来执行需要执行的方法
一个action类中可以编写N个方法。
但Action类中的可执行的方法必须满足(名字可以任意):
|
通过设置method属性来调用action中的方法
通过action标签中的method属性来指定所要执行的方法
【示例】
步骤一:创建新的包名和Action
新建一个包:cn.itcast.struts.d_method
Action :
public
class
UserAction
extends ActionSupport{
@Override
public String execute() throws Exception {
System.out.println("默认方法执行!");
return
super.execute();
}
public String login(){
System.out.println("登录成功");
return
NONE;
}
public
String register(){
System.out.println("注册成功");
return
NONE;
}
}
步骤二:创建响应页面
Login.jsp:
<body>
<h1>用户登录成功!</h1>
</body>
Register.jsp
<body>
<h1>用户注册成功!</h1>
</body>
Success.jsp:
<body>
<h1>success!</h1>
</body>
步骤三:配置Struts.xml
<!-- action访问指定方法 -->
<!-- 方法login方法 -->
<action
name="user_login"
class="cn.itcast.struts.d_method.UserAction"
method="login">
<result
name="login">/d_method/login.jsp</result>
</action>
<!-- 访问register -->
<action
name="user_register"
class="cn.itcast.struts.d_method.UserAction"
method="register">
<result
name="register">/d_method/register.jsp</result>
</action>
步骤四:测试
执行请求:由于有两个方法,因此需要执行两次请求。
执行注册请求:
步骤五:分析
注意:如果访问路径名称以及返回结果集视图使用通配符以及{1}的话,需要保证,请求路径的名称能在action标签中找到,如果使用{1}来访问指定方法和响应结果集的话,要保证方法名称和响应的结果集视图名称一致,而且必须保证结果集视图页面的名称也要一致
问题:如果要写的业务方法非常多,那是不是每一个方法都要写一个action,感觉非常繁琐。
有没有更好的方法解决?
使用通配符进行方法调用
单纯使用method属性来配置action的方法,如要调用不同的方法,需要配置不同的action。配置较多。
解决方案就是使用通配符,可实现仅通过一个method属性,就可以配置一个action中的N方法的访问。
单个通配符的使用
步骤一:Action
使用之前的例子即可
步骤二:配置Struts.xml
步骤三:测试
执行请求
后台打印:
步骤四:分析
两个通配符的使用
【示例】
步骤一:Action
使用之前的例子即可
步骤二:配置Struts.xml
步骤三:测试
执行请求
后台打印:
一般两个*就差不多是极限了,三个的话就没法看了,可读性非常差。
动态方法调用(了解)
【示例】
步骤一:需要开启常量中的动态方法调用
步骤二:Action
使用之前的例子即可
步骤三:配置Struts.xml
需要开启动态方法调用:
配置action :
步骤四:测试
执行请求
后台打印:
步骤五:分析
方法总结
【小结】
action方法调用:
- 默认的execute方法调用。
- method属性指定调用方法。虽可以任意调用任何的方法,但一个方法需要配置一个action。
- method属性+通配符。可以使用一个配置,调用N方法和结果集。(重点--推荐)
-
动态方法调用,可以不配置method属性也能随意调用aciton中的任意方法。但必须开启动态方法调用,而且不能通过一个result标签随意配置N个结果集。
Action访问Servlet相关API
struts2默认情况下将servlet api都隐藏起来了,为了简化开发。但很多时候,我们还需要调用servlet api,比如,向session中放入一个登录用户。
分析通过3种方式操作servlet的api。
- 解耦方式(了解)
- 接口注入方式(了解)
- 静态方法(重点)
解耦合方式调用(间接调用)-了解
解耦相对于耦合,这里举例:
编写自己的Servlet的时候的调用方法doGet(HttpServletRequest, HttpServletReponse) ,需要直接在代码中依赖servletContext的 request和 response,这就是耦合。
Struts2设计思想就是与Servlet API 解耦合,编写Action 代码中可以不再直接依赖Servlet的任何API,简化开发,也便于测试(方法简单无参)。
那如果我们想调用这些API呢?比如向session中放入登录用户、向request中放入一个值给页面响应用。
struts2为我们提供了一个API,可间接调用servlet api,这个api叫做:ActionContext 类 (Action 上下文 )。(可以理解该类就是一个工具类)
【回顾了解】上下文: 与容器相关,获取容器相关对象、信息 ---- 类似于一个工具类 (如servletcontext,可以拿到一些关于web应用服务范围的一些对象信息,pageContext可以拿到关于jsp页面的一些对象信息)。
该API提供大量间接操作Servlet API (request、 response、 session、application) 方法:
提示:间接操作是指面向map的操作,而不是直接操作相关对象。通过操作map就相当于操作相关对象。
【示例】
步骤一:创建一个新的包和类
//解耦方式调用servlet API
public
class
IndirectAction
extends ActionSupport{
@Override
public
String execute() throws Exception {
Map<String, Object> map = ActionContext.getContext().getParameters();
String[] str = (String[]) map.get("name");
System.out.println(str[1]);
Map requestMap = (Map) ActionContext.getContext().get("request");
requestMap.put("name", str[1]);
Map<String, Object> sessionMap = ActionContext.getContext().getSession();
sessionMap.put("name", "session_name");
return
SUCCESS;
}
}
步骤二:配置struts.xml
<!-- 解耦方式调用servlet API -->
<action
name="indirect"
class="cn.itcast.struts.e_servlet.IndirectAction">
<result>/e_servlet/success.jsp</result>
</action>
步骤三:配置响应页面
结果页面success.jsp:
步骤四:测试
请求url
接口注入方式操作Servlet API(了解)
获取Context需要实现ServletContextAware
获取Request需要实现ServletRequestAware
获取Response需要实现ServletResponseAware
【示例】
步骤一:编写Action
public
class
InjectAction
extends ActionSupport implements ServletRequestAware,
ServletContextAware {
private HttpServletRequest request;
private ServletContext context;
@Override
public String execute() throws Exception {
String name = request.getParameter("name");
request.setAttribute("name", name);
request.getSession().setAttribute("name", "郭芙蓉");
return
super.execute();
}
@Override
public
void setServletRequest(HttpServletRequest request) {
this.request = request;
}
@Override
public
void setServletContext(ServletContext context) {
this.context = context;
}
}
步骤二:Struts.xml
需要创建一个新的结果集视图injectDemo.jsp
<!-- 接口注入方式调用servlet API -->
<action
name="inject"
class="cn.itcast.struts.e_servlet.InjectAction">
<result>/e_servlet/success.jsp</result>
</action>
步骤三:测试
执行请求
通过ServletActionContext 类的静态方法直接获取Servlet API (推荐)
步骤一:编写Action
public
class
DirectAction
extends ActionSupport{
@Override
public String execute() throws Exception {
String name = ServletActionContext.getRequest().getParameter("name");
ServletActionContext.getRequest().setAttribute("name", name);
ServletActionContext.getRequest().getSession().setAttribute("name", "苏菲玛索");
return
super.execute();
}
}
步骤二:配置Struts.xml
配置响应结果集:
<!-- 静态方法直接调用 -->
<action
name="direct"
class="cn.itcast.struts.e_servlet.DirectAction">
<result>/e_servlet/success.jsp</result>
</action>
步骤三:测试
执行请求
【扩展问题】
ServletActionContext 获取 request方法 是 static的, 有没有线程问题 ?
会不会出现,获取错request对象的情况 ---- 不会的 (底层:ThreadLocal)
存储着每个线程的变量副本,每个线程只能取自己的value。
result结果集的使用
该节分为两个知识点:
- 局部结果集和全局结果集。
- 结果集类型。
-
局部结果集和全局结果集
局部结果集
在<action> 标签内部配置的<result>元素。
作用范围:只对当前Action有效
【举例】
步骤一:创建包和类
创建工程:
ProductAction :
public
class
ProductAction
extends ActionSupport {
@Override
public String execute() throws Exception {
System.out.println("默认执行的方法!");
return
super.execute();
}
public String save (){
System.out.println("保存商品成功!");
return
"save";
}
}
步骤二:配置Struts.xml
<!-- 结果集的使用:局部结果集 -->
<action
name="product_save"
class="cn.itcast.struts.f_result.ProductAction"
method="save">
<result
name="save">/f_result/save.jsp</result>
</action>
步骤三:创建响应页面
save.jsp:
步骤四:测试
执行请求:报错,找不到product这个类
原因是:之前的双通配符的配置的作用使我们发送的请求走错地方了,要先把这个配置给注释掉。
再次测试:
注意:如果有用到两个或者两个以上的通配符,此时要注意在这个通配符所在的action标签的下方有没有用到其他通配符的action,如果有,就可能发生不可预知的错误。
问题:如果有几个action中的响应结果集都是指向同一个jsp页面—index.jsp,是不是每一个action都要配置一下?
答 :可以使用全局结果集
全局结果集
在包的标签中的<global-results>中配置
作用范围:对package内所有Action生效
一般是用来对几个不同的action响应相同的结果集视图使用的
【举例】 ProductAction和UserAction都有方法需要响应同一个结果集视图
步骤一:给两个不同的action配置相同的结果集视图
UserAction:使用之间的action即可
public
class
UserAction
extends ActionSupport{
@Override
public
String execute() throws Exception {
System.out.println("默认方法执行!");
return
super.execute();
}
public String login(){
System.out.println("登录成功");
return
"login";
}
public String register(){
System.out.println("注册成功");
return
"register";
}
}
ProductAction :
public
class
ProductAction
extends ActionSupport {
@Override
public String execute() throws Exception {
System.out.println("默认执行的方法!");
return
super.execute();
}
public String save (){
System.out.println("保存商品成功!");
return
"save";
}
}
步骤二:配置Struts.xml
配置全局结果集并把局部结果集注释掉:
提示:该标签有配置顺序,将鼠标悬停在<package >标签上是可见如下提示
注意:局部结果集会覆盖全局结果集
步骤三:创建结果集视图
success.jsp :
步骤四:测试
访问无误
补充
注意:package中的任何标签配置都是有顺序的。
结果集类型
作用:控制响应的方式(转发、重定向)
配置<result> 元素时, name是逻辑视图名称, type是结果集类型。
Struts2提供的常用结果集类型都定义在struts-default.xml 中:
内置的结果集类型:最常用的就是红框中的几个结果集类型
转发-dispatcher
dispatcher(默认值):请求转发。(最常用)
作用:服务器内部是同一次请求,可采用request传递数据,URL不变。
【举例】
步骤一:action
修改ProductAction中的save方法:添加数据
public
class
ProductAction
extends ActionSupport{
@Override
public String execute() throws Exception {
System.out.println("执行默认方法!");
return
SUCCESS;
}
public String save(){
System.out.println("保存商品成功!");
ServletActionContext.getRequest().setAttribute("name","iphone 7 plus");
return
"save";
}
}
步骤二:配置struts.xml
步骤三:修改save.jsp页面,显示数据:
步骤四:测试
执行请求
重定向-redirect
步骤一:配置struts.xml
全局的结果集配置:
还是利用上述的save方法做举例,修改strust.xml中的结果集类型即可。
<!-- 结果集的使用:局部结果集 -->
<action
name="product_*"
class="cn.itcast.struts.f_result.ProductAction"
method="{1}">
<!-- 结果集类型的默认值:转发 -->
<!-- <result name="save" type="dispatcher">/f_result/save.jsp</result> -->
<!-- 结果集类型的重定向 -->
<!-- <result type="redirect">/f_result/success.jsp</result> -->
</action>
步骤二:测试
执行请求
重定向到Action- redirectAction
之前的转发和重定向都是指向页面,但有的时候我们需要执行action,例如:当做完新增商品之后,需要重新查询商品列表,这个时候就需要指向action了
【举例】
商品保存后重定向到查询action
步骤一:action
ProductAction中的save方法保持不变,新增一个findAll方法:
public
class
ProductAction
extends ActionSupport{
@Override
public String execute() throws Exception {
System.out.println("执行默认方法!");
return
SUCCESS;
}
public String save(){
System.out.println("保存商品成功!");
ServletActionContext.getRequest().setAttribute("name","iphone 7 plus");
return
"save";
}
public
String findAll(){
System.out.println("查询所有商品成功!");
return "findAll";
}
}
步骤二:Struts.xml :修改save结果集视图
步骤三:测试
执行请求:发送新增商品的请求,执行完新增商品后重定向到product_findAll这个action,最后转发到index.jsp页面显示hello world
步骤四:分析
【result标签复杂写法】(了解)
其他写法:使用<param>标签传递参数的写法
注意param中的name属性值可以参考结果集相应的类:
-
课程内容整理
示例的编写步骤
创建一个案例需要的步骤:
- 创建项目
- 导包
- 在web.xml中配置前端控制器
- 创建包
- 创建提交数据的页面
- 配置struts.xml
-
测试
常用配置的注意事项
总格6个配置文件,分成两大类:
-
框架内置的配置文件:不能修改
default.properties :常量配置
struts-default.xml :配置了struts需要的bean、各类结果集以及拦截器(默认执行的拦截器栈-defaultStack)
struts-plugin.xml :struts的插件包需要的配置文件
-
用户自定义的配置文件:
struts.xml:前端控制器会读取该配置文件中的标签来判断应该分发给哪个action,并且执行哪个action中的方法,最后响应哪一个结果集视图。
struts.properties:只是用来配置常量的(了解)
web.xml :配置常量(了解)
潜规则:后面配置的常量会覆盖前面配置的常量
加载顺序:1)default.properties 2)struts-default.xml 3)struts-plugin.xml 4)struts.xml 5)struts.properties 6)web.xml配置常量
3、默认的Action :配置错误请求需要默认执行的action,一般用来配置友好提示。
Action的编写方式
- 实现Action接口(掌握)
- 继承ActionSupport(重点,必须掌握)
-
自定义pojo作为action(了解)
Action访问的方法
- execute方法:访问action默认执行
- method属性:访问指定方法
-
通配符的使用:可以通过*表示占位{1}表示第一颗的占位。(重点掌握)
注意事项:如果有两个通配符,先注意下方是不是有其他的通配符的使用,以免造成访问路径的错误。
- 动态方法调用:不需要配置method属性,通过!后面的单词来调用指定的方法,需要配置常量-开启动态方法调用。
Action访问servlet API
- 解耦方式调用servlet API
- 接口注入方式调用servlet API :实现接口方法,并且通过setter方法进行对象的传递。
- 静态方法调用:推荐使用
Result结果集的使用
- 分为全局和局部结果集
- 结果集类型的使用:dispatcher 、redirect、redirectAction、stream
重点和小结
课后作业练习
需求:开发一个登录的小业务。
业务详情:
用户输入的用户名密码错误,跳回登录页面,让用户重新登录。
用户输入的用户名密码正确,重定向到主页,显示成功字样。--SUCCESS
技术选型:Struts2(Web层)