一、Struts2概述
Struts2是一个基于MVC设计模式的web层框架。
1.常见的web层框架
struts2、struts、Webwork、SpringMVC
2.Web层框架基于前端控制器模型设计
3.Struts2的helloworld
(1)下载struts2
http://struts.apache.org
(2)解压压缩包,得到struts2的目录结构
注意:war文件:是web项目打成war包。直接放入到tomcat可以执行
(3)创建动态web项目,导入jar包
(4)创建hello.jsp和success.jsp
hello.jsp
<%@ page language="java" contentType="text/html; charset=utf-8"
pageEncoding="utf-8"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<title>hello struts2</title>
</head>
<body>
<h1>Struts2入门</h1>
<a href="${ pageContext.request.contextPath }/hello.action">hello</a>
</body>
</html>
success.jsp
<%@ page language="java" contentType="text/html; charset=utf-8"
pageEncoding="utf-8"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<title>Insert title here</title>
</head>
<body>
success!!!
</body>
</html>
(5)编写struts的action类
HelloAction.class
package pers.zx.struts.hello;
public class HelloAction {
public String execute() {
System.out.println("动作被调用");
return "sussess";
}
}
(6)在src下创建并配置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>
<!-- Struts2为了管理Action的配置,通过包进行管理。 -->
<!-- 配置Struts2的包 -->
<package name="demo1" extends="struts-default" namespace="/">
<!-- 配置Action================ -->
<action name="hello" class="pers.zx.struts.hello.HelloAction" >
<!-- 配置页面的跳转=========== -->
<result name="success">/hello/success.jsp</result>
</action>
</package>
</struts>
(7)给WEB-INF下的web.xml配置过滤器
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://java.sun.com/xml/ns/javaee" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd" id="WebApp_ID" version="2.5">
<display-name>struts2_day01</display-name>
<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>
<!-- 配置Struts2的核心过滤器 -->
<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>xyz</param-value> -->
<!-- </init-param> -->
</filter>
<filter-mapping>
<filter-name>struts2</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
</web-app>
(8)完成
4.Struts2基本执行过程
当用户执行某个action时,它会先经过核心过滤器,里面会有一组连接器对其进行操作,之后更具struts.xml配置文件找到相应的动作类的方法,在执行方法后会有个返回值,在根据这个返回值跳转到相应的页面。
5.XML提示配置
(1)windows–》preference–》XML–》XML Catalog
(2)复制dtd: http://struts.apache.org/dtds/struts-2.3.dtd
(3)找到dtd文件导入,记住要选URI
6.Struts配置文件加载顺序
注意:在配置常量时要注意文件的加载顺序,后配置的常量会覆盖先配置的常量。
7.Struts2配置文件(struts.xml)的标签详解
(1)package
(2)action
(3)Struts2的常量配置
(4)分模块开发的配置
这时就会用到< include file=”pers/zx/struts/test/struts_test.xml”>标签
即把要包含的xml文件包含到struts.xml中。一般在协同开发中使用。
8.Action类的编写方式
①.POJO类
//Action的编写方式一:Action类是一个POJO的类
public class ActionDemo1 {
public String execute(){
System.out.println("ActionDemo1执行了...");
return null;
}
}
②.实现Action接口
// Action的编写方式二:实现一个Action的接口
// 实现接口的这种方式:提供了五个常量(五个逻辑视图的名称)
// SUCCESS :成功
// ERROR :失败
// LOGIN :登录出错页面跳转
// INPUT :表单校验的时候出错
// NONE :不跳转
public class ActionDemo2 implements Action{
@Override
public String execute() throws Exception {
System.out.println("ActionDemo2执行了...");
return NONE;
}
}
③.继承ActionSupport类(推荐)
//Action的编写方式三:Action类继承ActionSupport类
//推荐使用继承ActionSupport方式
// ActionSupport中提供了数据校验、国际化等一系列操作的方法。
public class ActionDemo3 extends ActionSupport{
@Override
public String execute() throws Exception {
System.out.println("ActionDemo3执行了...");
return NONE;
}
}
9.Action访问方式配置
(1)method配置(在struts.xml中配置)
通过method方式可以获取同一个action类中的不同的方法。
< action name=”user_insert” class=”zx” method=”insert”>< /action>
< action name=”user_delete” class=”zx” method=”delete”>< /action>
(2)通配符的方式
< action name=”user_“ class=”zx” method=”{1}”>< /action>
选取user_开头的所有action,有一个号,所以这个1代表user _后的任意名称的方法。
(3)动态方法访问
二、Struts2访问Servlet API的方式
通过这些访问方式就可以从页面获取数据或者向页面传输数据。
1.完全解耦的方式
该方式用的是和request等方式完全无关的方式,用的是Action类自带的方法。
(1)Action类的代码
@Override
public String execute() throws Exception {
// 一、接收参数:
// 利用Struts2中的对象ActionContext对象.
ActionContext context = ActionContext.getContext();
// 调用ActionContext中的方法。
// 类似于Map<String,String[]> request.getParameterMap();
Map<String,Object> map = context.getParameters();
for (String key : map.keySet()) {
String[] values = (String[]) map.get(key);
System.out.println(key+" "+Arrays.toString(values));
}
// 二、向域对象中存入数据,在页面就可以用EL表达式接收
context.put("reqName", "reqValue");// 相当于request.setAttribute();
context.getSession().put("sessName", "sessValue"); // 相当于session.setAttribute();
context.getApplication().put("appName", "appValue"); // 相当于application.setAttribute();
return SUCCESS;
}
注意:这种方式只能获得代表request、session、application的数据的Map集合,不能操作这些对象本身,比如获取url,它只能获取数据。
2.使用Servlet的API的原生方式
就是原生的request,response等等
(1)Action代码
@Override
public String execute() throws Exception {
// 一、接收数据
// 直接获得request对象,通过ServletActionContext
HttpServletRequest request = ServletActionContext.getRequest();
Map<String, String[]> map = request.getParameterMap();
for (String key : map.keySet()) {
String[] values = map.get(key);
System.out.println(key+" "+Arrays.toString(values));
}
// 二、向域对象中保存数据
// 向request中保存数据:
request.setAttribute("reqName", "reqValue");
// 向session中保存数据
request.getSession().setAttribute("sessName", "sessValue");
// 向application中保存数据
ServletActionContext.getServletContext().setAttribute("appName", "appValue");
return SUCCESS;
}
注意:他就可以获取值和其他属性值
3.使用接口注入的方式
通过实现ServletRequestAware、ServletContextAware等接口来实现。
(1)代码示例
public class RequestDemo3 extends ActionSupport implements ServletRequestAware,ServletContextAware{
private HttpServletRequest request;
private ServletContext context;
public RequestDemo3() {
super();
System.out.println("RequestDemo3被创建了...");
}
@Override
public String execute() throws Exception {
// 一、接收参数
// 通过接口注入的方式获得request对象。
Map<String, String[]> map = request.getParameterMap();
for (String key : map.keySet()) {
String[] values = map.get(key);
System.out.println(key+" "+Arrays.toString(values));
}
// 二、向域对象中保存数据
// 向request域中保存数据
request.setAttribute("reqName", "reqValue");
// 向session中保存数据:
request.getSession().setAttribute("sessName", "sessValue");
// 向application中保存数据:
context.setAttribute("appName", "appValue");
return super.execute();
}
@Override
public void setServletRequest(HttpServletRequest request) {
this.request = request;
}
@Override
public void setServletContext(ServletContext context) {
this.context = context;
}
}
注意:Servlet是单例的,多个程序访问同一个Servlet只会创建一个Servlet的实例。Action是多例的,一次请求,创建一个Action的实(不会出现线程安全的问题)。
三、Struts结果页面配置
1.Struts结果页面配置
(1)全局结果页面配置
任何方法只要返回的结果name是与之相等的,就会执行相应的跳转。
<!--全局-->
<global-results>
<result name="success">/index.jsp</result>
</global-results>
(2)局部结果页面配置
相应的方法只有返回结果name与之相等,则执行相应的跳转
<action name="login" class="pres.zx.test.Login">
<result name="success">/index.jsp</result>
</action>
注意:如果全局结果name=局部结果name则会跳转局部的。
2.result标签的设置
< result name=”” type=””>< /result>
四、Struts数据封装的方式
1.属性驱动:利用在action类中用set方法
(1)编写User类,包含get、set方法
public class User {
private String username;
private String password;
private Integer age;
private Date birthday;
private Double salary;
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;
}
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
public Date getBirthday() {
return birthday;
}
public void setBirthday(Date birthday) {
this.birthday = birthday;
}
public Double getSalary() {
return salary;
}
public void setSalary(Double salary) {
this.salary = salary;
}
@Override
public String toString() {
return "User [username=" + username + ", password=" + password + ", age=" + age + ", birthday=" + birthday+ ", salary=" + salary + "]";
}
}
(2)编写Action类,里面包含相应属性的set方法,此时就可以直接获得表单值
public class UserAction1 extends ActionSupport {
// 提供了对应的属性
private String username;
private String password;
private Integer age;
private Date birthday;
private Double salary;
// 提供属性对应的set方法
public void setUsername(String username) {
this.username = username;
}
public void setPassword(String password) {
this.password = password;
}
public void setAge(Integer age) {
this.age = age;
}
public void setBirthday(Date birthday) {
this.birthday = birthday;
}
public void setSalary(Double salary) {
this.salary = salary;
}
@Override
public String execute() throws Exception {
// 接收数据:
System.out.println(username);
System.out.println(password);
System.out.println(age);
System.out.println(birthday);
System.out.println(salary);
// 封装数据:
User user = new User();
user.setUsername(username);
user.setPassword(password);
user.setAge(age);
user.setBirthday(birthday);
user.setSalary(salary);
return NONE;
}
}
(3)jsp页面
<h3>方式一:属性驱动-提供set方法的方式</h3>
<form action="${ pageContext.request.contextPath }/userAction1.action" method="post">
用户名:<input type="text" name="username"/><br/>
密码:<input type="password" name="password"><br/>
年龄:<input type="text" name="age"/><br/>
生日:<input type="text" name="birthday"/><br/>
工资:<input type="text" name="salary"/><br/>
<input type="submit" value="提交">
</form>
2.属性驱动:页面中提供表达式方式
主要通过在Action类中定义一个私有的对象属性,并设置其get和set方法。在页面中的属性name要以对象的名称.属性名的方式。它主要是利用struts自带的拦截器来实现值的获取的。
(1)JSP
<h3>方式二:属性驱动-在页面中提供表达式方式</h3>
<form action="${ pageContext.request.contextPath }/userAction2.action" method="post">
用户名:<input type="text" name="user.username"/><br/>
密码:<input type="password" name="user.password"><br/>
年龄:<input type="text" name="user.age"/><br/>
生日:<input type="text" name="user.birthday"/><br/>
工资:<input type="text" name="user.salary"/><br/>
<input type="submit" value="提交">
(2)Action类
public class UserAction2 extends ActionSupport {
// 提供一个User对象:
private User user;
// 提供user的set和get方法:一定要提供get方法。
// 因为拦截器完成数据封装,需要创建User对象。通过get方法可以获得同一个对象,将数据封装到同一个对象中。
public User getUser() {
return user;
}
public void setUser(User user) {
this.user = user;
}
@Override
public String execute() throws Exception {
System.out.println(user);
return NONE;
}
}
3.属性驱动:采用模型驱动的方式
该方式是最常用的方式,需要实现ModelDriven< T>接口,以及实现相应的getModel()方法,并且要手动实例化对象。
(1)JSP
<h3>方式三:模型驱动-模型驱动方式</h3>
<form action="${ pageContext.request.contextPath }/userAction3.action" method="post">
用户名:<input type="text" name="username"/><br/>
密码:<input type="password" name="password"><br/>
年龄:<input type="text" name="age"/><br/>
生日:<input type="text" name="birthday"/><br/>
工资:<input type="text" name="salary"/><br/>
<input type="submit" value="提交">
</form>
(2)Action类
public class UserAction3 extends ActionSupport implements ModelDriven<User>{
// 模型驱动使用的对象:前提必须手动提供对象的实例
private User user = new User(); // 手动实例化User.
@Override
// 模型驱动需要使用的方法:
public User getModel() {
return user;
}
@Override
public String execute() throws Exception {
System.out.println(user);
return NONE;
}
}
注意:模型驱动方式最常用的方式,缺点是只能同时向一个对象中封装数据。而使用页面中表达式的方式可以向多个对象中同时封装数据。
不同的方式调用的拦截器是不一样的
五、Input逻辑视图
struts在执行的过程中会执行一系列的拦截器,而拦截器并不能处理所有的错误,所以当出现拦截器以外的错误时,会自动跳转到返回结果含有input逻辑视图的相应的页面。
来看一个图:
所以,在编写程序时,要配置input来处理错误。
六、Struts复杂数据类型的封装
1.封装到List中
(1)实体类
public class Product {
private String name;
private Double price;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Double getPrice() {
return price;
}
public void setPrice(Double price) {
this.price = price;
}
@Override
public String toString() {
return "Product [name=" + name + ", price=" + price + "]";
}
}
(2)Action类
public class ProductAction1 extends ActionSupport {
private List<Product> products;
// 提供集合的set方法:
public void setProducts(List<Product> products) {
this.products = products;
}
public List<Product> getProducts() {
return products;
}
@Override
public String execute() throws Exception {
for (Product product : products) {
System.out.println(product);
}
return NONE;
}
}
(3)JSP
2.封装到Map中
(1)Action类
public class ProductAction2 extends ActionSupport {
private Map<String,Product> map;
public Map<String, Product> getMap() {
return map;
}
public void setMap(Map<String, Product> map) {
this.map = map;
}
@Override
public String execute() throws Exception {
for (String key : map.keySet()) {
Product product = map.get(key);
System.out.println(key+" "+product);
}
return NONE;
}
}
(2)JSP
<h3>封装到Map集合中:批量插入商品</h3>
<form action="${ pageContext.request.contextPath }/productAction2.action" method="post">
商品名称:<input type="text" name="map['one'].name"><br/>
商品价格:<input type="text" name="map['one'].price"><br/>
商品名称:<input type="text" name="map['two'].name"><br/>
商品价格:<input type="text" name="map['two'].price"><br/>
商品名称:<input type="text" name="map['three'].name"><br/>
商品价格:<input type="text" name="map['three'].price"><br/>
<input type="submit" value="提交">
</form>
七、OGNL
OGNL:对象图导航语言,它调用对象的方法,获取Struts2的值栈的数据。
1.OGNL访问对象的方法
在jsp页面
(1)引入struts标签
<%@ taglib uri="/struts-tags" prefix="s"%>
(2)编写代码
<s:property value="'struts'.length()" />
2.OGNL访问对象的静态方法
(1)引入struts标签
<%@ taglib uri="/struts-tags" prefix="s"%>
(2)在struts.xml设置可以访问静态方法(由于struts2默认是不可以访问的)
<constant name="struts.ognl.allowStaticMethodAccess" value="true" />
(3)编写代码:第一个@后是 类名,第二个@后是 方法名(参数)
<s:property value=”@java.lang.Math@random()”/>
3.ValueStack(值栈)
(1)什么是值栈
(2)值栈的内部结构
(3)值栈于ActionContext的关系
(4)获取ValueStack对象
(5)值栈的操作
①.向值栈中存入数据
第一种方法,就是利用在Action中创建实体的私有对象,以及其get方法,并new一个对象设置其属性值,此时值栈中就会有数据了。可以用对象名.属性名来调用。注意要用< s:property>< /s>来获取。
第二种方法,创建valuestack的对象,通过它的put或者set方法,此时值就会进入到值栈中,之后在页面直接取就好了,利用属性名。注意put方法是直接取,set方法是对象名.属性名。
②.值栈获取数据
③.EL获取值栈的值
因为Struts的框架的底层对request.getAttribute()进行了增强。如果在request中没有找到,就回去值栈中找,所以使用EL表达式也可以获得。(从源码中可以知道)
4.OGNL中的特殊符号
(1) #
①.获取context的数据
②.构建map集合
(2) %
%{}可以将字符串强行识别成ognl表达式进行解析。而%{‘ ‘}就不会解析
(3) $
在配置文件中使用ognl
八、拦截器
1.什么是拦截器
Interceptor:拦截器,起到拦截Action的作用,Struts2框架核心的功能都是依赖拦截器实现。
2.拦截器与过滤器的区别
过滤器是过滤从客户端向服务器发送的请求。
拦截器是拦截客户端对Action的访问,更细粒度化的拦截,可以拦截Action的具体方法。
3.拦截器的工作原理
客户端向服务器发送一个Action的请求,init初始化参数,然后执行核心过滤器(doFilter)方法。在这个方法中,调用executeAction()方法,在这个方法内部调用dispatcher.serviceAction();在这个方法内部创建一个Action代理,最终执行的是Action代理中的execute(),在代理中执行的execute方法中调用ActionInvocation的invoke方法。在这个方法内部递归执行一组拦截器(完成部分功能),如果没有下一个拦截器,就会执行目标Action,根据Action的返回的结果进行页面跳转。
4.编写自定义拦截器
(1)编写拦截器类
编写一个类实现Interceptor接口或者继承AbstractInterceptor类。
public class InterceptorDemo1 extends AbstractInterceptor{
@Override
public String intercept(ActionInvocation invocation) throws Exception {
System.out.println("InterceptorDemo1执行了...");
String obj = invocation.invoke();
System.out.println("InterceptorDemo1执行结束了...");
return obj;
}
}
(2)对拦截器进行配置