什么是Struts
是流行和成熟的基于MVC设计模式的Web应用程序开源框架。Struts使用目的
为了帮助我们减少在运用MVC设计模型来开发Web应用时间。MVC模式
- JSP+JavaBean = Model1:适用于小型网站的开发
- JSP+Servlet+JavaBean = Model2:最典型的MVC模式
- MVC是模型(Model)、视图(View)和控制器(Controller)的缩写;MVC是一种软件设计典范,用一种业务逻辑、数据、界面显示分离的方法组织代码,将业务逻辑聚集到一个部件里面,在改进和个性化定制界面及用户交互的同时,不需要重新编写业务逻辑。
MVC流程图
Struts2发展历史
Struts工作原理
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
Struts2工作流程
1、客户端浏览器发出HTTP请求.2、根据web.xml配置,该请求被FilterDispatcher接收
3、根据struts.xml配置,找到需要调用的Action类和方法, 并通过IoC方式,将值注入给Aciton
4、Action调用业务逻辑组件处理业务逻辑,这一步包含表单验证。
5、Action执行完毕,根据struts.xml中的配置找到对应的返回结果result,并跳转到相应页面
6、返回HTTP响应到客户端浏览器
Struts2核心文件
核心文件一:web.xml
核心文件二:struts.xml
核心文件三:struts.properties
Struts2入门
1.使用步骤:
(1)下载相关jar包(2)创建Web项目
(3)创建并完善相关配置文件
(4)创建Action并测试启动
2.Structs官方网站
参考:
http://www.yiibai.com/struts_2/struts_examples.html
http://struts.apache.org/http://people.apache.org/builds/struts/
3.导入的包:(共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类文件的一个包)
引用包
4.配置web.xml文档
<!-- 定义过滤器 -->
<filter>
<filter-name>struts2</filter-name>
<filter-class>org.apache.struts2.dispatcher.ng.filter.StrutsPrepareAndExecuteFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>struts2</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
5.创建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="helloworld" class="com.czq.action.HelloWorldAction">
<result>/result.jsp</result>
</action>
</package>
</struts>
6.创建action类
public class HelloWorldAction extends ActionSupport {
@Override
public String execute() throws Exception {
// TODO Auto-generated method stub
System.out.println("执行Action");
return SUCCESS;
}
}
7.创建result.jsp页面
Action搜索顺序
Eg:http://localhost:8080/struts2/path1/path2/path3/student.action第一步:判断package是否存在,如:path1/path2/path3/
如果package存在
第二步:则判断该package中action是否存在,如果不存在则去默认namespace的package里面寻找action
第三步:如果没有,则报错
如果package不存在:
第二步:检查上一级路径的package是否存在(直到默认namespace),重复第一步
第三步:如果没有则报错
动态方法调用
动态方法调用是为了解决一个Action对应多个请求的处理,以免Action太多。三种方式:指定method属性(少用)、感叹号方式(不推荐)、通配符方式(建议使用)
第1种(指定method属性):
struts.xml
<action name="addAction" method="add" class="com.czq.action.HelloWorldAction">
<result>/add.jsp</result>
</action>
<action name="updateAction" method="update" class="com.czq.action.HelloWorldAction">
<result>/update.jsp</result>
</action>
第2种(感叹号方式):
<constant name="struts.enable.DynamicMethodInvocation" value="true"></constant>
<action name="helloworld" class="com.czq.action.HelloWorldAction">
<result>/result.jsp</result>
<result name="add">/add.jsp</result>
<result name="update">/update.jsp</result>
</action>
public String add(){
request.setAttribute("path", "update");
return "add";
}
public String update(){
return "update";
}
访问地址:http://localhost:8080/HelloWorld/helloworld!add(或update).action
第3种(通配符方式):
一个*代替
<action name="helloworld_*" method="{1}" class="com.czq.action.HelloWorldAction">
<result>/result.jsp</result>
<result name="add">/{1}.jsp</result>
<result name="update">/{1}.jsp</result>
</action>
访问地址:http://localhost:8080/HelloWorld/helloworld_add(或update).action
两个*代替
<action name="*_*" method="{2}" class="com.czq.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_update(或update).action
三个*代替
<action name="*_*_*" method="{2}" class="com.czq.{3}.{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.action
默认Action
<default-action-ref name="index"></default-action-ref>
<action name="index">
<result>/error.jsp</result>
</action>
访问地址:http://localhost:8080/HelloWorld/aaas.action(随意的action)
Struts2后缀
1.strts.xml配置
<constant name = "strts.action.extension" value = "xxx"></constant>//name是固定的,value是指定的后缀,可以是空值
<constant name="struts.action.extension" value="html"></constant>
2.web.xml配置
<!-- 定义过滤器 -->
<filter>
<filter-name>struts2</filter-name>
<filter-class>org.apache.struts2.dispatcher.ng.filter.StrutsPrepareAndExecuteFilter</filter-class>
<init-param>
<param-name>struts.action.extension</param-name>
<param-value>do</param-value>
</init-param>
</filter>
3.struts.properties配置
struts.action.extension = xxxx,xxx,xx//可以配置多个,以,号分隔访问地址:http://localhost:8080/HelloWorld/HelloWorld_add_action.html
接收参数
1、使用Action属性接受参数
login.jsp页面:
Action类:
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;
}
}
2, 使用DomainModel接受参数
login.jsp页面
Action类
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;
}
}
3, 使用ModelDriver接受参数
login.jsp页面
User类
public class User {
private String username;
private String password;
private List<User> bookList;
public List<User> getBookList() {
return bookList;
}
public void setBookList(List<User> bookList) {
this.bookList = bookList;
}
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;
}
}
Action类
public class LoginAction extends ActionSupport implements ModelDriven<User>{
private User user=new User();
public String login(){
System.out.println(user.getUsername());
System.out.println(user.getBookList().get(0));
System.out.println(user.getBookList().get(1));
return SUCCESS;
}
public User getModel() {
// TODO Auto-generated method stub
return user;
}
}
Login.jsp页面
Action类
public class LoginAction extends ActionSupport implements ModelDriven<User>{
private User user=new User();
public String login(){
System.out.println(user.getUsername());
System.out.println(user.getBookList().get(0).getUsername());
System.out.println(user.getBookList().get(1).getUsername());
return SUCCESS;
}
public User getModel() {
// TODO Auto-generated method stub
return user;
}
}
处理结果类型
处理流程
Struts2返回String,提供代码复用性,有利于框架分离
Action五种内置属性
Input结果类型
1.当参数类型转换错误时,如:age输入框中的类型是字母等情况,方法自动返回input年龄:
<input type="text" name="age">
<result name="input">/login.jsp</result>
2.当action中存在addFiledError时:
1)addFileError放在一般执行方法,addFieldError("", "");
语句后面有返回input的语句
if (user.getUsername()==null||"".equals(user.getUsername())) {
this.addFieldError("username", "用户名不能为空");
return INPUT;
}
2)addFileError放在validate()中
public void validate() {
// TODO Auto-generated method stub
if (user.getUsername()==null||"".equals(user.getUsername())) {
this.addFieldError("username", "用户名不能为空");
// return INPUT;
}
}
注:login.jsp页面
导入标签库:
<%@ taglib prefix="s" uri="/struts-tags" %>
用户名:
<input type="text" name="username">
<s:fielderror name="username"></s:fielderror>
处理结果的两种类型(位置):
1.局部结果:将<result/>作为<action/>元素的子元素配置
2.全局结果:将<result/>作为<global-result/>元素的子元素配置
result子标签
例:
<result name="add">
<param name="location">/${#request.path}.jsp</param>
<param name="parse">true</param>
</result>
private HttpServletRequest request=ServletActionContext.getRequest();
public String add(){
request.setAttribute("path", "update");
return "add";
}
public String update(){
return "update";
}
Result的属性type类型
type的默认值为dispatcher(转发),这个类型支持JSP视图技术
常用三个:chain,redirect,plaintext。
1、chain:将action和另外一个action链接起来。
2、redirect:重定向(会丢失请求参数)。
3、plaintext:返回网页源代码。
4、stream:返回inputstream用于文件下载。
什么是拦截器
Struts2大多数核心功能是通过拦截器实现的,每个拦截器完成某项功能。拦截器方法在Action执行之前或者之后执行。
拦截器栈
从结构上看,拦截器栈相当于多个拦截器的组合。从功能上看,拦截器栈也是拦截器。
拦截器的工作原理
拦截器的执行过程是一个递归的过程
自定义拦截器
拦截器示例
计算Action的执行时间:思路:执行之后的时间-执行之前的时间= 执行Action消耗的时间
实现步骤:
创建拦截器
在配置文件中定义拦截器并引用它
1.定义拦截器
1.1.创建一个拦截器类继承自AbstractInterceptor类
1.2.实现intercept方法
public class TimerInterceptor extends AbstractInterceptor{
//自动调用此方法,进行拦截操作
@Override
public String intercept(ActionInvocation invocation) throws Exception {
// TODO Auto-generated method stub
//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;
}
}
2.<!-- 注册拦截器 -->
<interceptors>
<interceptor name="mytimer" class="com.czq.interceptor.TimerInterceptor"></interceptor>
</interceptors>
<action name="timer" class="com.czq.action.TimerAction">
<result>/success.jsp</result>
3.<!-- 引用拦截器 -->
<interceptor-ref name="mytimer"></interceptor-ref>
</action>
struts2内建拦截器
默认拦截器栈
注:默认的拦截器写在自定义拦截器前面
<!-- 为Action显示引用拦截器后,默认的拦截器defaultStack不再生效,需手工引用 -->
<interceptor-ref name="defaultStack"></interceptor-ref>
<!-- 引用拦截器 -->
<interceptor-ref name="mytimer"></interceptor-ref>
开发模式常用配置
<!-- 开启使用开发模式,详细错误提示 -->
<constant name="struts.devMode" value="true"/>
<!-- 指定每次请求到达,重新加载资源文件 -->
<constant name="struts.i18n.reload" value="true"/>
<!-- 指定每次配置文件更改后,自动重新加载 -->
<constant name="struts.configuration.xml.reload" value="true"/>
<!-- 指定XSLT Result使用样式表缓存 -->
<constant name="struts.xslt.nocache" value="true"/>
开发权限验证拦截器
1. 创建被访问的资源(如后台管理页面manager.jsp),将其放到WEB-INF下的page文件夹中2. 创建auth的Action将请求转发到被访问资源(manager.jsp)
<action name="auth">
<result>/WEB-INF/page/manager.jsp</result>
<result name="login">/login.jsp</result>
<!-- 引用自定义的拦截器栈 -->
<interceptor-ref name="myStack"></interceptor-ref>
</action>
3. 创建登陆页面login.jsp接收用户登录信息
<%@ page language="java" import="java.util.*" pageEncoding="utf-8"%>
<%
String path = request.getContextPath();
String basePath = request.getScheme() + "://"
+ request.getServerName() + ":" + request.getServerPort()
+ path + "/";
%>
<%@ taglib prefix="s" uri="/struts-tags" %>
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
<head>
<base href="<%=basePath%>">
<title>用户登陆</title>
<meta http-equiv="pragma" content="no-cache">
<meta http-equiv="cache-control" content="no-cache">
<meta http-equiv="expires" content="0">
<meta http-equiv="keywords" content="keyword1,keyword2,keyword3">
<meta http-equiv="description" content="This is my page">
<!--
<link rel="stylesheet" type="text/css" href="styles.css">
-->
</head>
<body>
${loginError}
<form action="LoginAction.action" method="post">
用户名:<input type="text" name="username">
<s:fielderror name="username"></s:fielderror>
密码:<input type="password" name="password">
<input type="submit" value="提交">
</form>
</body>
</html>
4.创建loginAction处理登陆请求,校验登录信息并将有效登陆信息或错误信息放到session中
public class LoginAction extends ActionSupport implements SessionAware{
private User user=new User();
private Map<String, Object> session;
public void setSession(Map<String, Object> session) {
// TODO Auto-generated method stub
this.session=session;
}
//处理登陆请求
public String login(){
if ("admin".equals(user.getUsername())&&"123".equals(user.getPassword())) {
session.put("loginInfo", user.getUsername());
return SUCCESS;
} else {
session.put("loginError", "用户名或密码不正确!");
return ERROR;
}
}
}
<action name="LoginAction" method="login" class="com.czq.action.LoginAction">
<result>/WEB-INF/page/manager.jsp</result>
<result name="error">/login.jsp</result>
</action>
5.创建拦截器authInterceptor对访问authAction的请求进行拦截处理,通过ActionContext获取会话session,并校验session的登陆信息,如果不为空,即获取权限,放行,否则拦截并转发到登陆界面
<!-- 注册拦截器 -->
<interceptors>
<interceptor name="auth" class="com.czq.interceptor.AuthInterceptor"></interceptor>
<interceptor-stack name="myStack">
<!-- 自定义拦截器栈myStack,组合了default和auth -->
<interceptor-ref name="defaultStack"></interceptor-ref>
<interceptor-ref name="auth"></interceptor-ref>
</interceptor-stack>
</interceptors>
public class AuthInterceptor extends AbstractInterceptor {
@Override
public String intercept(ActionInvocation invocation) throws Exception {
// TODO Auto-generated method stub
ActionContext context=ActionContext.getContext();
Map<String, Object> session=context.getSession();
if (session.get("loginInfo")!=null) {
String result=invocation.invoke();
return result;
} else {
return "login";
}
}
}
本文章的案例源码:链接: https://pan.baidu.com/s/1qXItba8 密码: i4d4