访问Servlet API
关于Servlet,里边会有doGet和doPost两个方法,两个方法体里会有两个参数,一个是HttpServletRequest类型,一个是HttpServletResponse类型,这两个参数其实就是Servlet API,还有一个类型ServletContext。也就是说在Servlet可以直接调用Servlet API。
然后在看看Struts2中Action的execute方法
@Override
public String execute() throws Exception{
System.out.println("执行Action");
return SUCCESS;
}
里边没有任何参数,不存在HttpServletRequest和HttpServletResponse,所以是不存在Servlet API。不和Servlet API进行组合,execute方法可以轻松地进行测试,不需要存入参数。但是还是需要去访问Servlet API的。
Struts2提供了三种方式去访问Servlet API:
1.ActionContext(上下文的类,通过它可以获得相关对象,在这里所有对象都是以Map方式进行存储)
2.实现**Aware接口
3.ServletActionContext
Action搜索顺序
比如要访问
http://localhost:8080/struts2/path1/path2/path3/test.action
在struts.xml文件中package有属性namespace。
第一步,先是判断package是否存在,如path1/path2/path3/是否有包存在
如果存在:
第二步,判断action是否存在,如果包下有这个action就直接执行,如果不存在就去默认的namespace的package里面寻找action。
第三步,如果没有就报错。
如果不存在:
第二步,检查上一级路径的package是否存在(直到默认namespace),重复第一步。
第三步,如果没有,就报错。
补充:默认的命名空间namespace=""
, 根命名空间namespace="/"
。
<package name="test" extends="struts-default">
,如果未指定命名空间,则命名空间默认为namespace=""
解释更详细一点:
1.获得请求路径的URI,例如url是:http://server/struts2/path1/path2/path3/test.action
2.首先寻找namespace为/path1/path2/path3的package,如果存在这个package,则在这个package中寻找名字为test的action,如果不存在这个package则转步骤3;
3.寻找namespace为/path1/path2的package,如果存在这个package,则在这个package中寻找名字为test的action,如果不存在这个package,则转步骤4;
4.寻找namespace为/path1的package,如果存在这个package,则在这个package中寻找名字为test的action,如果仍然不存在这个package,就去默认的namaspace的package下面去找名字为test的action,如果还是找不到,页面提示找不到action。
动态方法调用
动态方法调用就是为了解决一个Action对应多个请求的处理,一面Action太多。
如果一个action里边有一个可执行方法execute,当我们访问这个action的时候,不指定这个方法,但是如果这个方法是不包含任何参数的,这个方法默认调用。如果每个模块写一个action并且写一个默认方法,那么会给开发造成麻烦。所以需要动态方法调用来解决。
Struts2提供了三种方式来访问方法:
指定method属性
感叹号方式
通配符方式
比如我们的原本配置文件如下
<struts>
<package name="default" namespace="/" extends="struts-default">
<action name="helloword" class="com.imooc.action.HelloWorldAction">
<result>/result.jsp</result>
</action>
</package>
</struts>
在HelloWordAction中有execute方法,又新增了add()和update()方法。
指定method方法
<struts>
<package name="default" namespace="/" extends="struts-default">
<action name="helloword" class="com.imooc.action.HelloWorldAction">
<result>/result.jsp</result>
</action>
<action name="addAction" method="add" class="com.imooc.action.HelloWorldAction">
<result>/add.jsp</result>
</action>
<action name="updateAction" method="update" class="com.imooc.action.HelloWorldAction">
<result>/update.jsp</result>
</action>
</package>
</struts>
如果我们这个时候访问addAction这个action的话,就会找到HelloWorldAction这个action,同时会找到add这个方法,,然后就去执行add这个方法的逻辑。
在url中输入http://localhost:8080/HelloWord/addAction.action
,就可以调到add.jsp页面。
也就是说现在我们通过配置解决了我们通过网页来动态调用执行方法。但是如果方法特别多,那么我们就要写很多个action,开发量不小。
感叹号方式
<struts>
<package name="default" namespace="/" extends="struts-default">
<action name="helloword" class="com.imooc.action.HelloWorldAction">
<result>/result.jsp</result>
<result name="add">/add.jsp</result>
<result name="update">/update.jsp</result>
</action>
</package>
<constant name="struts.enable.DynamicMethodInvocation" value="true"></constant>
</struts>
这种方式官方并不推荐。
要使用这种方式,要在struts.xml文件里开启一个功能。
在url中访问的时候采用如下写法http://localhost:8080/HelloWord/helloword!add.action
,就可以访问到add.jsp页面。
通配符方式
<struts>
<package name="default" namespace="/" extends="struts-default">
<action name="helloword_*" method="{1}" class="com.imooc.action.HelloWorldAction">
<result>/result.jsp</result>
<result name="add">/{1}.jsp</result>
<result name="update">/{1}.jsp</result>
</action>
</package>
</struts>
这种方式官方是推荐使用的。
把method当成一个参数,动态传递。如何传递,就是在action的name后面加_*。星号对应的就是{1},如果想再加上一个下划线和星号,method里边可以写为{1}{2}。
在使用的时候,在url输入http://localhost:8080/HelloWord/helloword_add.action
,输入了helloword_add.action,就可以执行add方法,同时返回add.jsp页面
指定多个配置文件
<include file="login.xml"></include>
如果一个项目中有特别多action,要想把它们全部配置进一个xml文件中,文件会非常大,可以用上边的方法,将每个模块的xml文件包含进来。
但是要注意添加dtd这样一个文档描述的开头。
<!DOCTYPE struts PUBLIC
"-//Apatch Software Foundation//DTD Struts Configuration 2.3//EN"
"http://struts.apache.org/dtds/struts-2.3.dtd">
要注意每个配置文件的字符类型,我们也可以在总的配置文件里加上编码来统一
<constant name="struts.i18n.encoding" value="UTF-8"></constant>
默认Action
当用户访问action的时候,如果找不到这个action,默认的action就会顶替用户需要找的action,提升了用户体验。
默认action是在一个package下边的,要写在package标签里。
<default-action-ref name="index"></default-action-ref>
<action name="index">
<result>/error.jsp</result>
</action>
default-action-ref中的name要和下边的action的name相对应。
Struts2后缀
前边访问的时候,后缀都是.action,有的时候我们想让后缀名不是.action,比如.html,.do。这个时候可以写一个常量。
<constant name="struts.action" value="html"></constant>
这样后缀就变为了html。
如果没有上一行代码那样的配置,或者value为空,那么访问的时候,可以不加后缀。
接收参数
如何在action里边接收参数?常用三种方式:
1.使用Action的属性接收参数
2.使用DomainModel接收参数
3.使用ModelDriven接收参数
使用Action的属性接收参数
首先我们在登录页面先建一个form表单,然后要有action和提交方式method。里边有两个字段用户名和密码。
<form action="LoginAction.action" method="post">
用户名:<input type="text" name="username">
密码:<input type="password" name="password">
<input type="submit" value="提交"/>
</form>
然后我们要创建我们的action,新建一个类LoginAction,选择它的父类ActionSupport,为什么要继承ActionSupport,因为像返回的SUCCESS这样的常量就在ActionSupport的父类Action中,当然也有一些其他的属性。
public class LoginAction extends ActionSupport{
public String login(){
return SUCCESS;
}
}
我们在LoginAction里边创建一个方法,返回SUCCESS。
然后需要在配置文件去配置这个action。
<action name="LoginAction" method="login" class="com.imooc.action.LoginAction">
<result>/success.jsp</result>
</action>
还需要新建一个页面success.jsp。
建好了之后,下一步就是要考虑怎么通过action的属性来获取用户名和密码。
要在LoginAction里边建两个属性username和password,然后就是它们的set和get方法。
然后在login方法里打印输出一下就可以了。
public class LoginAction extends ActionSupport{
private String username;
private String password;
public String login(){
System.out.println(username);
return SUCCESS;
}
public String getUsername(){
return username;
}
public void setUsername(String username){
this.username=username;
}
public String getPassword(){
return password;
}
public void setPassword(String password){
this.password=password;
}
}
在登录页面输入用户名和密码,会跳转到success.jsp,后台会输出刚刚输入的用户名。
使用DomainModel接收参数
前边的简单登录有两个文本框,但是如果页面很大,有非常多的文本框,那么建很多个属性就显得很麻烦。
Java是一种面向对象的语言,那么是否可以将这些属性放到一个对象里来进行开发?答案是肯定的,这就需要第二种方式,使用DomainModel接收参数。
新建一个类User,将属性都放在里边,在里边实现各个属性的get和set方法。
public class User{
private String username;
private String password;
public String getUsername(){
return username;
}
public void setUsername(String username){
this.username=username;
}
public String getPassword(){
return password;
}
public void setPassword(String password){
this.password=password;
}
}
然后要在LoginAction类中声明一个属性user,并将它的get和set方法加进来。然后打印一下,里边的参数为user.getUsername()。
public class LoginAction extends ActionSupport{
private User user;
public String login(){
System.out.println(user.getUsername());
return SUCCESS;
}
public User getUser(){
return user;
}
public void setUser(User user){
this.user=user;
}
}
只是这样还不够,假如有多个对象,每个里边都有一下属性,当参数传过来的时候,很容易乱,所以在登录页面的表单里也需要修改。
<form action="LoginAction.action" method="post">
用户名:<input type="text" name="user.username">
密码:<input type="password" name="user.password">
<input type="submit" value="提交"/>
</form>
指定属性名称传入哪一个对象里边,这样就可以避免混乱。
使用ModelDriven接收参数
这种方法要求实现ModelDriven接口,里边的泛型输入我们需要转换的类。
这里我们的类型就为User。
既然是实现接口,那么就要实现里边的方法,getModel,返回我们当前需要转换的对象。
public class LoginAction extends ActionSupport implements ModelDriven<User>{
private User user = new User();
public String login(){
System.out.println(user.getUsername());
return SUCCESS;
}
@Override
public User getModel(){
return user;
}
}
要注意,我们的user对象一定要进行实例化,并且使用了ModelDriven方法的话,get和set方法是不需要的。
实例化之后,登录页面的form表单里,那些name就不需要指定属于哪个对象了
之前讲到的都是传入的String类型的参数,那么如果是传入一个数据集要怎么办?
在User类中有一个List
private List<String> bookList;
同时也给了它set和get方法,这里没写出。
那么在form表单里要怎么写?
<form action="LoginAction.action" method="post">
用户名:<input type="text" name="username">
密码:<input type="password" name="password">
书籍1:<input type="text" name="bookList[0]">
书籍2:<input type="text" name="bookList[1]">
<input type="submit" value="提交"/>
</form>
用这种方法就可以。
处理结果类型
首先看一下Struts2的处理流程
用户请求通过路径到达Struts框架来解析,然后找到控制器(Action),Action将处理结果(处理结果是字符串)返回给Struts框架,然后会把一些数据传给视图资源。
<result name="success">/success.jsp</result>
result元素中的name就是result元素的逻辑视图名称。如果省略了name属性,系统将采用默认的name属性值,默认的name值是success。
SUCCESS是系统内置的,我们还有没有其他的一些系统内置的属性?
com.opensymphony.xwork2.Action中有五个内置属性:
SUCCESS:Action正确地执行完成,返回相应的视图,success是name属性的默认值
NONE:表示Action正确地执行完成,但是并不返回任何视图
ERROR:表示Action执行失败,返回到错误处理视图
LOGIN:Action因为用户没有登录的原因没有正确执行,将返回该登录视图,要求用户进行登录验证
INPUT:Action的执行,需要从前端界面获取参数,INPUT就是代表这个参数输入的界面,一般在应用中,会对这些参数进行验证,如果验证没有通过,将自动返回到该视图
处理结果是通过struts.xml使用<result/>
标签配置结果。
根据位置不同,分为两种结果:
1.局部结果,将<result/>
作为<action/>
元素的子元素配置
2.全局结果,将<result/>
作为<global-result/>
元素的子元素配置
result标签还有子标签param,param标签有两个属性
1.location:该属性定义了该视图对应的实际视图资源
2.parse:该参数指定是否可以在实际视图名字中使用OGNL表达式