一、概念
Struts是流行和成熟的基于MVC设计模式的Web应用程序框架。
使用Struts的目的:
为了帮助我们减少在运用MVC设计模型来开发Web应用的时间。
MVC模式
MVC是模型视图控制器(Model View Contoller),一种软件设计典范,用一种业务逻辑,数据,界面显示分离的方法组织代码,将业务逻辑聚集到一个部件里面,在改进和个性化定制界面及用户交互的同,不需要重新编写业务逻辑。
二、Struts2环境搭建
- 1.创建Web Project
- 2.导入Jar
要导入的包:(共9个)
commons-fileupload(上传下载包)
commons-io(输入输出包)
commons-lang 3-3.2(基础包)
commons-logging(日志包)
freemarker(模板引擎,通过模板生成文本输出的通用工具)
structs2-core(核心包)
xwork-core(一些类基于xwork)
ognl(表达式)
javassist-3.11.0.GA.jar(解析java类文件的一个包) - 3.在web.xml配置struts2的过滤器
web项目在启动tomcat时第一个启动的文件就是web.xml
首先定义过滤器
<filter>
<filter-name>struct2</filter-name>
<filter-class>org.apache.struts2.dispatcher.ng.filter.StrutsPrepareAndExecuteFilter</filter-class>
</filter>
这里写完后按住ctrl点击鼠标左键如果可以跳转则证明正确
<!--filter的映射-->
<filter-mapping>
<filter-name>struct2</filter-name>
<url-pattern>/*</url-pattern>
<!--/*是所有的都需要过滤-->
</filter-mapping>
映射与文件的filter-name应该保持一致
- 4.创建Struts核心xml文件
<?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" namespace="/" extends="struts-default">
<action name="helloword" class="imooc.acton.StrutsAction">
<result>/result.jsp</result>
</action>
</package>
</struts>
- 5.创建action类继承与ActionSupport
Struts2中有一个默认的方法不指定方法名的话有一个execute()方法 - 6.配置struts.xml
三、工作原理及过程
-
工作原理
在Struts2框架中的处理大概分为以下几个步骤
1 客户端初始化一个指向Servlet容器(例如Tomcat)的请求
2 这个请求经过一系列的过滤器(Filter)(这些过滤器中有一个叫做ActionContextCleanUp的可选过滤器,这个过滤器对于Struts2和其他框架的集成很有帮助,例如:SiteMesh Plugin)
3 接着FilterDispatcher被调用,FilterDispatcher询问ActionMapper来决定这个请是否需要调用某个Action
4 如果ActionMapper决定需要调用某个Action,FilterDispatcher把请求的处理交给ActionProxy
5 ActionProxy通过Configuration Manager询问框架的配置文件,找到需要调用的Action类
6 ActionProxy创建一个ActionInvocation的实例。
7 ActionInvocation实例使用命名模式来调用,在调用Action的过程前后,涉及到相关拦截器(Intercepter)的调用。
8 一旦Action执行完毕,ActionInvocation负责根据struts.xml中的配置找到对应的返回结果。返回结果通常是(但不总是,也可 能是另外的一个Action链)一个需要被表示的JSP或者FreeMarker的模版。在表示的过程中可以使用Struts2 框架中继承的标签。在这个过程中需要涉及到ActionMapper -
工作流程
1、客户端浏览器发出HTTP请求.
2、根据web.xml配置,该请求被FilterDispatcher接收
3、根据struts.xml配置,找到需要调用的Action类和方法, 并通过IoC方式,将值注入给Aciton
4、Action调用业务逻辑组件处理业务逻辑,这一步包含表单验证。
5、Action执行完毕,根据struts.xml中的配置找到对应的返回结果result,并跳转到相应页面
6、返回HTTP响应到客户端浏览器
四、核心配置文件
- web.xml
- struts.xml
struts.xml模板文件
<!DOCTYPE struts PUBLIC "-//Apache Software Foundation//DTD Struts Configuration 2.0//EN" "http://struts.apache.org/dtds/struts-2.0.dtd" >
<struts>
<!-- include节点是struts2中组件化的方式 可以将每个功能模块独立到一个xml配置文件中 然后用include节点引用 -->
<include file="struts-default.xml"></include>
<!-- package提供了将多个Action组织为一个模块的方式
package的名字必须是唯一的 package可以扩展 当一个package扩展自
另一个package时该package会在本身配置的基础上加入扩展的package
的配置 父package必须在子package前配置
name:package名称
extends:继承的父package名称
abstract:设置package的属性为抽象的 抽象的package不能定义action 值true:false
namespace:定义package命名空间 该命名空间影响到url的地址,例如此命名空间为/test那么访问是的地址为http://localhost:8080/struts2/test/XX.action
-->
<package name="com.kay.struts2" extends="struts-default" namespace="/test">
<interceptors>
<!-- 定义拦截器
name:拦截器名称
class:拦截器类路径
-->
<interceptor name="timer" class="com.kay.timer"></interceptor>
<interceptor name="logger" class="com.kay.logger"></interceptor>
<!-- 定义拦截器栈 -->
<interceptor-stack name="mystack">
<interceptor-ref name="timer"></interceptor-ref>
<interceptor-ref name="logger"></interceptor-ref>
</interceptor-stack>
</interceptors>
<!-- 定义默认的拦截器 每个Action都会自动引用
如果Action中引用了其它的拦截器 默认的拦截器将无效 -->
<default-interceptor-ref name="mystack"></default-interceptor-ref>
<!-- 全局results配置 -->
<global-results>
<result name="input">/error.jsp</result>
</global-results>
<!-- Action配置 一个Action可以被多次映射(只要action配置中的name不同)
name:action名称
class: 对应的类的路径
method: 调用Action中的方法名
-->
<action name="hello" class="com.kay.struts2.Action.LoginAction">
<!-- 引用拦截器
name:拦截器名称或拦截器栈名称
-->
<interceptor-ref name="timer"></interceptor-ref>
<!-- 节点配置
name : result名称 和Action中返回的值相同
type : result类型 不写则选用superpackage的type struts-default.xml中的默认为dispatcher
-->
<result name="success" type="dispatcher">/talk.jsp</result>
<!-- 参数设置
name:对应Action中的get/set方法
-->
<param name="url">http://www.sina.com</param>
</action>
</package>
</struts>
- struts.properties文件
五、Struts用法
- 1、访问Servlet API
servlet api:
httpRequest、httpResponse、servletContext
3个api对应jsp面向对象:request、response、application
servlet中可以直接调用servlet api
struts2 Action中execute没有任何参数,也就是不存在servlet api
struts2 访问servlet api方式:
1.ActionContext类
2.实现***Aware接口
3.ServletActionCotext类 - 2、Action搜索顺序
如:http://localhost:8080/struts2/path1/path2/path3/student.action
第一步:判断package是否存在,如:path1/path2/path3/
如果package存在
第二步:则判断该package中action是否存在,如果不存在则去默认namespace的package里面寻找action
第三步:如果没有,则报错
如果package不存在:
第二步:检查上一级路径的package是否存在(直到默认namespace),重复第一步
第三步:如果没有则报错 - 3、动态方法调用
作用:为了解决一个Action对应多个请求的处理,以免Action太多,Action类中又多个方法。
三种方式:
1、指定method属性:Action类中新建多个方法,在配置文件中添加action标签,并在标签中添加method选项来区分调用不同的方法。
<action name="addAction" method="add" class="com.imooc.action.HelloWorldAction">
<result>/add.jsp</result>
2、感叹号方式(不推荐)
<struts>
<package name="default" namespace="/" extends="struts-default">
<action name="helloWorld" class="com.Action.HelloWorldAction">
<result >/result.jsp</result>
<result name="add">/add.jsp</result>
<result name="update">/update.jsp</result>
</action>
</package>
<!--constant标签的value选项设置为true时,表示开启感叹号方式-->
<constant name="struts.enable.DynamicMethodInvocation"value="true"></constant>
</struts>
在浏览器中调用不同方法: http://localhost:8080/HelloWorld/HelloWorld!add(或update).action
3、通配符方式
第一个代替{1},第二个代替{2},result里的name是Action的返回值,action的里method是Action里的方法名,调用某个方法时最后目标就输入 {1}_{2}.action;这样可以访问多个Action里的方法.
<action name="*_*" method="{2}" class="com.imooc.action.{1}Action">
<result >/result.jsp</result>
<result name="add">/{2}.jsp</result>
<result name="update">/{2}.jsp</result>
</action>
在浏览器中调用不同方法:http://localhost:8080/HelloWorld/HelloWorld_add.action
- 4/指定多个配置文件
当一个配置文件中存在多个action标签时,可以新建一个xml文件用来存储不同类型的action,将这些xml添加到一个主配置文件中。
struts.xml主配置文件:
<struts>
<!--当添加不通包的字配置文件时,添加包名<include file="/a/helloword.xml"> -->
<include file="helloword.xml"> </include>
<constant name="struts.i18n.encoding" value="UTF-8"> </constant>
</struts>
helloword配置文件:
<?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" namespace="/" extends="struts-default">
<action name="*_*" method="{2}" class="com.imooc.action.{1}Action">
<result >/result.jsp</result>
<result name="add">/{2}.jsp</result>
<result name="update">/{2}.jsp</result>
</action>
</package>
</struts>
- 5、默认Action
当输入网址找不到对应的action时,跳转到默认的action。
<default-action-ref name="index"></default-action-ref><br>
<action name="index">
<result>/error.jsp</result>
</action>
- 6、struts2后缀
三种方式:
1.struts.properties中:struts.action.extension=action,do,struts2
2.struts.xml中增加常量constant:
<constant name="struts.action.extension" value="action,do,struts2"></constant>
3.在过滤器中配置intt-param参数:
<init-param>
<param-name>struts.action.extension</param-name>
<param-value>do,action,strtus2</param-value>
</init-param>
- 7、接受参数
1、使用Action的属性接受参数,在Action中定义需要接受的属性,并写它的set/get方法。
<!--传递用户登录信息-->
<form action="LoginAction.action" method="post">
用户名:<input type="text" name="name">
密码:<input type="password" name="password">
<input type="submit" value="提交"/>
</form>
当传递的参数比较少是,用此种方法。创建一个登录action类,用来处理页面跳转。
public class LoginAction extends ActionSupport {
//接受页面传来的值
private String name;
private String password;
public String login(){
System.out.println(name);
return SUCCESS;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
}
在配置文件中添加一个action。
<action name="LoginAction" method="login" class="imooc.action.LoginAction">
<result>/success.jsp</result>
</action>
2、使用DomainModel接受参数,创建实体类定义需要接受的属性,并set/get方法,在Action中创建实体类名属性。并在界面进行指定。
当传递的参数过多时,用此种方法。
定义一个User类,用来接受参数。
public class User {
private String name;
private String password;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
}
在Action中创建User类对象来接受参数,此时User不需要实例化。
public class LoginAction extends ActionSupport {
private User user;
public String login(){
System.out.println(user.getName());
return SUCCESS;
}
public User getUser() {
return user;
}
public void setUser(User user) {
this.user = user;
}
}
在登录界面设置参数传递目标。
<form action="LoginAction.action" method="post">
<!--将name值传递给user对象-->
用户名:<input type="text" name="usre.name">
密码:<input type="password" name="user.password">
<input type="submit" value="提交"/>
</form>
3、使用ModelDriver接受参数,在Action中实现ModelDriver<实体类名>接口,并实现方法返回当前需要转换的对象,删除set/get方法,并对 对象 进行实例化,并取消指定。
将LoginAction类继承一个ModelDriven接口,实现接口的getModel()方法,此时User必须实例化。
public class LoginAction extends ActionSupport implements ModelDriven<User>{
private User user=new User();
public String login(){
System.out.println(user.getName());
return SUCCESS;
}
@Override
public User getModel() {
// TODO 自动生成的方法存根
return user;
}
}
登录界面不需要再用user进行传递。
<form action="LoginAction.action" method="post">
用户名:<input type="text" name="name">
密码:<input type="password" name="password">
<input type="submit" value="提交"/>
</form>
实际应用中,最好使用第三种方法,即使user对象改变,也不需要改登录界面的那么值。
- 8、处理结果类型
处理结果类型-result标签下还有param标签:
1)location:该属性定义了该视图对应的实际视图资源
2)parse:该参数指定是否可以再实际视图名字中使用OGNL表达式,默认值为TRUE,支持OGNL(Object-Graph Navigation Language)表达式
ognl表达式可以在jsp页面去写,也可以在struts2页面中去写,在实际开发中是不常用的,默认情况下ognl是允许的,是打开状态
<param name="parse">true</param>
<param name="location">...地址</param>
ognl表达式使用:
<param name="location">/${#request.path}.jsp</param>
在action类中对应的方法中写request.setAttribute("path","返回的jsp页面名称");
- 9、input应用
首先在struts.xml中添加一个input返回结果
<action name="LoginAction" method="login" class="imooc.action.LoginAction">
<result>/success.jsp</result>
<result name="input">login.jsp</result>
</action>
自动跳转到input界面方式有两种,其一是传入的参数类型转换错误,另一种是添加表单错误。
第一种:User类中添加一个int类型的年龄属性,如果在登录界面输入的年龄为字符串类型,点击提交后,页面直接跳转到input设置的登录界面。
第二种:在Action中添加addFieldError()方法,只要此方法获得值机会返回input界面。
public class LoginAction extends ActionSupport implements ModelDriven<User>{
private User user=new User();
public String login(){
if(user.getName()==null||"".equals(user.getName()))
this.addFieldError("name", "用户名不能为空");
return INPUT;
}
@Override
public User getModel() {
// TODO 自动生成的方法存根
return user;
}
}
还可以通过重写validate方法来实现上述功能。
@Override
public void validate() {
if(user.getName()==null||"".equals(user.getName()))
this.addFieldError("name", "用户名不能为空");
}
在登录界面设置
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
<head>
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<!--添加次行代码后才能实现-->
<%@ taglib prefix="s" uri="/struts-tags" %>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>???????</title>
</head>
<body>
<form action="LoginAction.action" method="post">
用户名:<input type="text" name="name"><s:fielderror name="name"></s:fielderror>
密码:<input type="password" name="password">
<input type="submit" value="提交"/>
</form>
</body>
</html>
结果类型input的效果
1.当参数类型转换错误时,如age输入框中的类型是字母等情况,方法自动返回input
2.当action中存在addFiledError时:
1)addFileError放在一般执行方法,addFieldError("", "");语句后面有返回input的语句
2)addFileError放在validate()中
3.FileError的表现形式:
在jsp页面中使用<s:fielderror/>标签,该标签name属性为addFieldError方法中的参数fieldName,在jsp页面中使用struts标签,
需要导入标签库 语句:<%@ taglib prefix="s" uri="/struts-tags" %>
六、拦截器
- 1、拦截器方法在Action执行之前或者之后执行。
拦截器站:由多个拦截器组成,作用与拦截器相同。 - 2、工作原理
类似于web中的过滤器。
- 3、拦截器的定义
/*
* 计算执行Action花费的时间
*/
public class TimerInterceptor extends AbstractInterceptor {
@Override
public String intercept(ActionInvocation invocation) throws Exception {
// 1.执行Action之前
long start = System.currentTimeMillis();
// 2.执行下一个拦截器,如果已经是最后一个拦截器,则执行目标Action
String result = invocation.invoke();
// 3.执行Action之后
long end = System.currentTimeMillis();
System.out.println("执行Action花费的时间:" + (end - start) + "ms");
return result;
}
}
- 4、Struts2内置拦截器