Struts2是一个基于MVC设计模式的WEB层框架,具有web层框架的特点,基于前端控制器实现的
Struts2的开发环境下载地址,2.3.24
解压,四个目录
创建web项目
导入jar包
将app目录下面的struts2-blank.war包放到tomcat下面,启动tomcat解压war包可以得到struts2框架的基本包
在项目的WEB-INF下创建lib文件夹,将jar包导入lib中
创建web层(controller层)类
public class HelloAction {
public String execute(){
System.out.println("HelloAction执行了....");
return "success";
}
}
配置struts.xml(注意:struts.xml必须配置src的根目录下面,否则不起作用)
<struts>
<package name="demo1" extends="struts-default" namespace="/">
<action name="hello" class="com.struts2.action.HelloAction">
<result name="success">/demo1/success.jspresult>
action>
package>
struts>
前端跳转路径
<h3><a href="${pageContext.request.contextPath}/hello.action">Struts2的入门a>h3>
package标签称为包,这个包与java中的包概念不一样,包是为了更好管理action
在Struts2的框架中,提供了非常多的常量配置,在Struts2源码struts2-core-2.3.24.jar的jar包下面的org.apache.struts2.default.properties配置文件中
default.properties已经是底层了,被人家打成了jar包,所以我们想要修改常量可以到struts.xml或者web.xml中修改
例如:在struts.xml中修改Struts2请求的默认扩展名
<constant name="struts.action.extension" value="action">constant>
在web.xml中修改Struts2请求的默认扩展名
<filter>
<filter-name>strutsfilter-name>
<filter-class>org.apache.struts2.dispatcher.ng.filter.StrutsPrepareAndExecuteFilterfilter-class>
<init-param>
<param-name>struts.action.extensionparam-name>
<param-value>actionparam-value>
init-param>
filter>
在团队开发的时候,个人负责个人的模块,都会来修改同一个配置文件,这样在整合的时候会比较困难,所以,一般都是个人写个人的配置文件,然后有一个总的配置文件来管理这些个人写的配置文件
<include file="com/struts2/action/structs_demno1.xml">include>
<include file="com/struts2/action/structs_demno2.xml">include>
Action类实现接口和继承类,他们的父类都给他们提供了五个常量:
但是继承类还提供了数据校验等一系列操作的功能,所以推荐使用继承ActionSupport
通过method设置
访问路径:
<a href="${pageContext.request.contextPath}/userFind.action">查询用户a>
<a href="${pageContext.request.contextPath}/userUpdate.action">修改用户a>
<a href="${pageContext.request.contextPath}/userDelete.action">删除用户a>
<a href="${pageContext.request.contextPath}/userSave.action">保存用户a>
配置struts.xml
<package name="demo2" extends="struts-default" namespace="/">
<action name="userFind" class="com.struts2.action.demo1.UserAction" method="find">action>
<action name="userUpdate" class="com.struts2.action.demo1.UserAction" method="update">action>
<action name="userDelete" class="com.struts2.action.demo1.UserAction" method="delete">action>
<action name="userSave" class="com.struts2.action.demo1.UserAction" method="save">action>
package>
通过通配符的方式进行设置(要求你访问的路径中包含你要执行的方法)
访问路劲
<a href="${pageContext.request.contextPath}/user_find.action">查询用户a>
<a href="${pageContext.request.contextPath}/user_update.action">修改用户a>
<a href="${pageContext.request.contextPath}/user_delete.action">删除用户a>
<a href="${pageContext.request.contextPath}/user_save.action">保存用户a>
配置struts.xml
<package name="demo2" extends="struts-default" namespace="/">
<action name="user_*" class="com.struts2.action.demo1.UserAction" method="{1}">action>
package>
通配符更加抽象的写法:
<package name="demo2" extends="struts-default" namespace="/">
<action name="*_*" class="com.struts2.action.demo1.{1}" method="{2}">action>
package>
动态方法访问
访问路径:
<a href="${pageContext.request.contextPath}/user!find.action">查询用户a>
<a href="${pageContext.request.contextPath}/user!update.action">修改用户a>
<a href="${pageContext.request.contextPath}/user!delete.action">删除用户a>
<a href="${pageContext.request.contextPath}/user!save.action">保存用户a>
配置struts.xml
<constant name="struts.enable.DynamicMethodInvocation" value="true">constant>
<package name="demo2" extends="struts-default" namespace="/">
<action name="user" class="com.struts2.action.demo1.UserAction">action>
package>
在使用Struts2的框架的过程中,发现Struts2和Servlet的API是解耦合的。在实际开发中,经常会使用到Servlet的API,比如登录,将用户信息保存到session中等
获取页面传递的数据:
ActionContext context = ActionContext.getContext();
//map集合的值是一个数组
Map<String, Object> map = context.getParameters();
for (String key:map.keySet()){
String[] values = (String[])map.get(key);
System.out.println(key+" "+ Arrays.toString(values));
}
将参数传到前端页面
context.put("reqName","李四");//相当于request.setAttribute();
context.getSession().put("age",25);//相当于session.setAttribute();
context.getApplication().put("tel","10086");//相当于application.setAttribute();
这种方式可以操作域对象的数据,但不能获得对象的方法
获取页面传递的数据
javax.servlet.http.HttpServletRequest request = ServletActionContext.getRequest();
Map<String, String[]> map = request.getParameterMap();
for (String key:map.keySet()){
System.out.println(Arrays.toString(map.get(key)));
}
将参数传递到前端
//向request中保存数据
request.setAttribute("reqName","王者荣耀");
//向session中保存数据
request.getSession().setAttribute("age","23");
//向application中保存数据
ServletActionContext.getServletContext().setAttribute("tel","10010");
这种方式可以操作域对象的数据,也可以获得对象的方法
import org.apache.struts2.interceptor.ServletRequestAware;
import org.apache.struts2.util.ServletContextAware;
public class RequestDemo3Action extends ActionSupport implements ServletRequestAware, ServletContextAware {
private HttpServletRequest request;
private ServletContext context;
@Override
public String execute() throws Exception {
Map<String, String[]> map = request.getParameterMap();
for (String key:map.keySet()){
System.out.println(Arrays.toString(map.get(key)));
}
//向request中保存数据
request.setAttribute("reqName","王者荣耀");
//向session中保存数据
request.getSession().setAttribute("age","23");
//向application中保存数据
context.setAttribute("tel","10010");
return SUCCESS;
}
@Override
public void setServletRequest(HttpServletRequest httpServletRequest) {
this.request = httpServletRequest;
}
@Override
public void setServletContext(ServletContext servletContext) {
this.context = servletContext;
}
}
一般情况下,接口注入会写一个基本的BaseAction,然后让其他的Action去继承这个BaseAction
Servlet是单例的,多个程序访问同一个Servlet只会创建一个Servlet实例,Action是多例的,一次请求,创建一个Action实例(不会出现线程安全的问题)
<package name="demo3" extends="struts-default" namespace="/">
<global-results>
<result name="success">/demo1/demo2.jsp<result>
global-results>
<action name="requestDemo1" class="com.struts2.action.demo1.RequestDemo1Action">
<result name="success">/demo1/demo2.jsp<result>
action>
<action name="requestDemo2" class="com.struts2.action.demo1.RequestDemo1Action">
action>
<action name="requestDemo3" class="com.struts2.action.demo1.RequestDemo1Action">
action>
package>
demo
重定向(Action → Action)
不在同一个名称空间下的重定向
<result name="saveSuccess" type="redirectAction">
<param name="namespace">/param>
<param name="actionName">customer_find.actionparam>
result>
处于同一个名称空间下的重定向
<result name="saveSuccess" type="redirectAction">customer_find.actionresult>
在action提供前台所传数据的set方法,即可获取到数据
private String username;
private String password;
private Integer age;
private Date birthday;
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;
}
@Override
public String execute() throws Exception {
System.out.println(username);
System.out.println(password);
System.out.println(age);
System.out.println(birthday);
return NONE;
}
前端:注意看name属性
<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="submit" value="注册">
form>
后端:必须生成对象的get和set方法,不能省略get方法
private User user;
public User getUser() {
return user;
}
public void setUser(User user) {
this.user = user;
}
@Override
public String execute() throws Exception {
System.out.println(user.toString());
return NONE;
}
相较于提供属性set方法的方式的属性驱动而言,属性驱动:页面提供表达式的方式将你获得的数据直接封装到了对象之中。
public class UserAction3 extends ActionSupport implements ModelDriven<User> {
private User user = new User();
@Override
public String execute() throws Exception {
System.out.println(user);
return NONE;
}
@Override
public User getModel() {
return user;
}
}
缺点:一个Action只能向一个对象进行封装
优点:相比于第二种数据封装方法而言,不需要修改页面
在页面显示错误信息:
页面配置:
导包
<%@ taglib uri="/struts-tags" prefix="s"%>
提供错误信息显示的地方
<s:fielderror>s:fielderror>
后端配置
<global-results>
<result name="input">/demo1/demo3.jspresult>
global-results>
页面写法:
<form action="${pageContext.request.contextPath}/productAction1.action" method="post">
商品名称:<input type="text" name="products[0].productName"> <br />
商品价格:<input type="text" name="products[0].price"> <br />
商品名称:<input type="text" name="products[1].productName"> <br />
商品价格:<input type="text" name="products[1].price"> <br />
商品名称:<input type="text" name="products[2].productName"> <br />
商品价格:<input type="text" name="products[2].price"> <br />
<input type="submit" value="提交">
form>
action写法:
private List<Product> products;
public List<Product> getProducts() {
return products;
}
public void setProducts(List<Product> products) {
this.products = products;
}
@Override
public String execute() throws Exception {
System.out.println(products.toString());
return NONE;
}
页面写法:
<form action="${pageContext.request.contextPath}/productAction2.action" method="post">
商品名称:<input type="text" name="map['one'].productName"> <br />
商品价格:<input type="text" name="map['one'].price"> <br />
商品名称:<input type="text" name="map['two'].productName"> <br />
商品价格:<input type="text" name="map['two'].price"> <br />
商品名称:<input type="text" name="map['three'].productName"> <br />
商品价格:<input type="text" name="map['three'].price"> <br />
<input type="submit" value="提交">
form>
action接收:
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()){
System.out.println(map.get(key));
}
return NONE;
}
Interceptor:Struts2拦截器,起到拦截Action的作用
创建一个自定义拦截器类,并继承AbstractInterceptor抽象类,重写AbstractInterceptor的抽象方法
import com.opensymphony.xwork2.ActionInvocation;
import com.opensymphony.xwork2.interceptor.AbstractInterceptor;
public class InterceptorDemo1 extends AbstractInterceptor {
@Override
public String intercept(ActionInvocation actionInvocation) throws Exception {
System.out.println("InterceptorDemo1执行了");
String invoke = actionInvocation.invoke();
System.out.println("InterceptorDemo1执行结束了");
return invoke;
}
}
在需要拦截的方法所配置的struts中配置拦截器,注意配置自定义拦截器后,struts2默认拦截器栈的拦截器就不会执行了,需要自己手动引入
<package name="demo1" extends="struts-default" namespace="/">
<interceptors>
<interceptor name="interceptorDemo1" class="com.ognl.interceptor.InterceptorDemo1">interceptor>
interceptors>
<action name="bbb" class="com.ognl.action.ActionDemo1">
<result name="actionDemo1">/demo1/demo1.jspresult>
<interceptor-ref name="defaultStack">interceptor-ref>
<interceptor-ref name="interceptorDemo1">interceptor-ref>
action>
package>
当拦截器有多个时,一个个引入是很烦的,我们可以定义一个拦截器栈,将需要引用的拦截器放入拦截器栈中,然后引用拦截器栈即可
<interceptors>
<interceptor name="interceptorDemo1" class="com.ognl.interceptor.InterceptorDemo1">interceptor>
<interceptor name="interceptorDemo2" class="com.ognl.interceptor.InterceptorDemo2">interceptor>
<interceptor-stack name="mystack">
<interceptor-ref name="defaultStack">interceptor-ref>
<interceptor-ref name="interceptorDemo1">interceptor-ref>
<interceptor-ref name="interceptorDemo2">interceptor-ref>
interceptor-stack>
interceptors>
<action name="aaa" class="com.ognl.action.ValueStackDemo1">
<result name="success">/demo1/success.jspresult>
action>
<action name="bbb" class="com.ognl.action.ActionDemo1">
<result name="actionDemo1">/demo1/demo1.jspresult>
<interceptor-ref name="mystack">interceptor-ref>
action>
前端登录页面:
<form id=form1 name=form1 action="${pageContext.request.contextPath}/user_login.action" method=post target="_parent">
<s:actionerror>s:actionerror>
<table cellSpacing=0 cellPadding=2 border=0>
<tbody>
<tr>
<td style="height: 28px" width=80>登 录 名:td>
<td style="height: 28px" width=150>
<input id=txtName style="WIDTH: 130px" name="user_code">
td>
tr>
<tr>
<td style="height: 28px">登录密码:td>
<td style="height: 28px">
<input id=txtPwd style="width: 130px" type=password name="user_password">
td>
tr>
tbody>
table>
FORM>
后端action:
import com.opensymphony.xwork2.ActionSupport;
import com.opensymphony.xwork2.ModelDriven;
import org.apache.struts2.ServletActionContext;
public class UserAction extends ActionSupport implements ModelDriven<User> {
private User user = new User();
private UserService userService = new UserServiceImpl();
public String login(){
List<User> login = userService.login(user);
if(login.size() > 0){
//登录成功
//将用户名保存到session
ServletActionContext.getRequest().getSession().setAttribute("username",login.get(0).getUser_code());
return SUCCESS;
}else{
//登录失败,返回错误信息
this.addActionError("用户名或密码错误");
return LOGIN;
}
}
@Override
public User getModel() {
return user;
}
}
拦截器:
import com.opensymphony.xwork2.ActionInvocation;
import com.opensymphony.xwork2.ActionSupport;
import com.opensymphony.xwork2.interceptor.MethodFilterInterceptor;
import org.apache.struts2.ServletActionContext;
public class PrivilegeInterceptor extends MethodFilterInterceptor {
@Override
protected String doIntercept(ActionInvocation actionInvocation) throws Exception {
//获取session中的用户名
Object username = ServletActionContext.getRequest().getSession().getAttribute("username");
if(username == null){
//没有登录
ActionSupport action = (ActionSupport) actionInvocation.getAction();
//返回错误信息
action.addActionError("没有登录!请先登录");
//打回登录页面
return ActionSupport.LOGIN;
}else{
//登录,放行
return actionInvocation.invoke();
}
}
}
xml配置:
<package name="customer" extends="struts-default" namespace="/">
<interceptors>
<interceptor name="privilegeInterceptor" class="com.xuhx.web.interceptor.PrivilegeInterceptor">interceptor>
interceptors>
<global-results>
<result name="login">/login.jspresult>
global-results>
<action name="customer_*" class="com.xuhx.web.action.CustomerAction" method="{1}">
<result name="findSuccess">/jsp/customer/list.jspresult>
<interceptor-ref name="privilegeInterceptor">
<param name="excludeMethods">loginparam>
interceptor-ref>
<interceptor-ref name="defaultStack">interceptor-ref>
action>
<action name="user_*" class="com.xuhx.web.action.UserAction" method="{1}">
<result name="success">/index.jspresult>
<interceptor-ref name="privilegeInterceptor">
<param name="excludeMethods">loginparam>
interceptor-ref>
<interceptor-ref name="defaultStack">interceptor-ref>
action>
package>
客户端向服务器发送一个Action请求,执行核心过滤器(doFilter)方法,在这个方法中,调用executeAction()方法,在这个方法内部调用dispatcher.serviceAction();在这个方法内部创建一个Action代理,最终执行的是Action代理中的execute(),在代理中执行的execute方法中调用Actioninvocation的invoke方法。在这个方法内部递归执行一组拦截器(完成部分功能),如果没有下一个拦截器,就会执行目标Action,根据Action的返回的结果进行页面跳转。
导包:
<%@ taglib prefix="s" uri="/struts-tags" %>
条件判断
<s:set var="i" value="3" scope="request">s:set>
<s:if test="#request.i>3">
i 大于 3
s:if>
<s:elseif test="#request.i<3">
i 小于 3
s:elseif>
<s:else>
i 等于 3
s:else>
循环
遍历List集合:
<s:iterator var="i" value="{'aa','bb','cc'}">
<s:property value="#i" />
s:iterator>
遍历map集合:
<s:iterator var="i" value="#{'aaa':'111','bbb':'222','ccc':'333'}">
<s:property value="#i.key">s:property>--<s:property value="#i.value">s:property>
s:iterator>
遍历1-10的奇数
<s:iterator var="i" begin="1" end="10" step="2" status="status">
<s:property value="#i" />
<s:property value="#status.count" /><br />
s:iterator>
调试用的标签
日期格式化
<s:form action="" method="post">
<s:textfield name="name" label="用户名" />
<s:password name="password" label="密码" />
<s:radio list="{'男','女'}" name="sex" label="性别" />
<s:select list="{'北京','上海','深圳','南京'}" name="city" label="籍贯" headerKey="" headerValue="--请选择--"/>
<s:checkboxlist list="#{'basketball':'篮球','football':'足球','volleyball':'排球','pingpang':'乒乓球'}" name="hobby" label="爱好" />
<s:textarea name="info" cols="20" rows="5" label="介绍" />
<s:submit value="提交" />
s:form>