struts是基于MVC的WEB框架,目前我们在所有项目中,均可以看到它的影子。对于这个神秘的框架,它到底长什么样子呢?希望我的这篇文章,能够带你揭开它神秘的面纱。
本文主要内容:
一:struts的基本使用方法,完成前后端交互。
二:struts的配置:(include、namespace、中文问题、上传最大值、通配符)
三:struts的request、response、session的获取,及ajax请求通过response返回的使用。
四:struts的拦截器interceptor 完成登录验证。
五:调试器、客户端跳转、表单验
六:s:标签:s:iterator遍历、s:form提交、s:check多选按钮、s:radio单选按钮、s:select选择列的使用
一:基本结构:
struts的基本结构是怎样?
1. 以在eclipse中搭建struts为例,创建动态WEB项目,导入struts需要使用到的基本jar包。
2. 在web.xml中配置struts过滤,配置了一个filter过滤器,这个过滤器是引入到了struts的class中,也就是告诉服务器,我这里所过滤出来的请求,以下全部交由struts来处理(其实进入struts中,这个过滤器会去找相应的action完成响应)。
<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>
<dispatcher>FORWARD</dispatcher>
<dispatcher>REQUEST</dispatcher>
<url-pattern>/*</url-pattern>
</filter-mapping>
3. 接下来在src目录下(这个目录可以在配置文件中更改,具体如何更改请自行百度,src是struts默认的读取路径)创建struts.xml文件,在该文件中设置action,也就是过滤完的请求来这个配置文件中寻找该何去何从。具体配置如下:例如我们在前端页面访问了localhost:8080/hero这个请求,web.xml过滤给struts.xml来认领,struts.xml去action中匹配hero这个name,然后读取它对应的class类,以及它要访问后台的哪个方法method。
<package>标签制定了该struts的基本情况,name是独一无二的用来区分不同的package,extends默认继承struts-default,
<action>标签代表一个请求过程,包括name代表请求的地址,class和method代表该请求将要访问那个类的那个方法,
<result>代表这个action请求后台完成后,返回的字符也就是对应name中的值,将要跳转到哪个页面中去。
注:一个action可以不用设置result返回值(比如ajax跳转)。
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE struts PUBLIC
"-//Apache Software Foundation//DTD Struts Configuration 2.0//EN"
"http://struts.apache.org/dtds/struts-2.0.dtd">
<struts>
<package name="hero" extends="struts-default">
<action name="showHero" class="action.HeroAction" method="showHero">
<result name="show">showHero.jsp</result>
</action>
</package>
</struts>
4. 编写相应的后台类与方法,客户端请求该action,完成前后端交互。
在客户端浏览器直接请求某个action个,然后通过配置文件,进入到后台方法中。将后台处理的数据和结果返回到前台页面中进行显示,完成简单的struts跳转:
在action包下新建HeroAction类,并编写showHero方法。该方法中:定义变量msg,并给出get、set方法。方法showHero中,给msg赋值,然后返回show,此时struts.xml接收到了返回的show值,然后根据result的配置跳转到showHero.jsp中。
package action;
public class HeroAction {
private String msg;
public String showHero() {
msg = "nihao";
return "show";
}
public String getMsg() {
return msg;
}
public void setMsg(String msg) {
this.msg = msg;
}
}
showHero.jsp中:通过EL表达式
<body>
${msg}
</body>
根据以上配置文件,我们在浏览器中访问:localhost:8080/showHero,就会看到浏览器中显示了nihao。此时struts完成了一次action的请求与响应。这就是简单的struts跳转。
5. 前端到后台:在jsp页面中通过form表单,我们提交一个action请求到后台,struts接收到该请求,并跳转到对应的类方法中,获取到前台传递的数据并进行处理,处理完成后,再通过struts进行跳转。
这里具体以传递对象为例,创建Hero类,设置属性name和price,并设置get、set方法。前台填写name和price,提交到后台,控制台打印前台填写的值。
在addHero.jsp页面中:这里使用的是struts的s:标签。action中addHero!addHero意思是请求addHero这个action中类中的addHero方法。
<%@ taglib prefix="s" uri="/struts-tags"%>
<body>
<s:form action="addHero!addHero" namespace="hero">
<s:textfield name="h1.name" label="英雄名称:"></s:textfield>
<s:textfield name="h1.price" label="英雄血量:"></s:textfield>
<s:submit value="新增"></s:submit>
</s:form>
</body>
struts.xml中,
<struts>
<package name="hero" extends="struts-default">
<action name="addHero" class="action.HeroAction" >
<result name="OK">addSuccess.jsp</result>
<result name="NO">no.jsp</result>
</action>
</package>
</struts>
HeroAction中
package action;
import pojo.Hero;
public class HeroAction {
private Hero h1;
public String addHero() {
System.out.println(h1.getName());
System.out.println(h1.getPrice());
return "OK";
}
public Hero getH1() {
return h1;
}
public void setH1(Hero h1) {
this.h1 = h1;
}
}
此时浏览器访问addHero.jsp,填写内容,点击提交后,可以在后台控制台中看到传递过来的数据。并且跳转到addSuccess.jsp
6. struts运行原理简单介绍:通过以上举例,可以简单认识了struts是如何工作的,它主要的使命就是完成请求和响应,期间带着前后端交互所带的参数或者对象值。
首先是action的跳转,它的发起的每一次请求其实就是一次request请求,例如我们在form中请求的action地址,首先根据、web.xml配置将该请求交给struts处理,它去读取struts.xml配置,寻找有没有对应的action,找到该action后进入对应的后台,在进入后台方法之前,struts会根据这个类中的属性get、set方法将前台中传递过来的对象值进行匹配,如果一致,则实例化该类的同时,给该类中的属性set注入值,例如上例中的form提交的h1.name,struts先实例化HeroAction这个类,然后读取到前台提交过来的值,对h1进行setName(name),然后setH1(hi),此时跳转到方法中时,h1这个对象已经被赋值了。注意struts的每一次请求action都会重新实例化该类,因为struts是多实例的。
然后通过result服务端跳转返回前台页面时,在后台方法中拥有的对象和属性,只要给了get、set方法,在前台页面中均可以获取到该值。同样也是通过get方法获取到的,在跳到jsp页面时,struts会调用实例的get方法, 获取到实例中的属性,作为参数传递到页面中。
值得注意的是:struts的对象属性在进行前后端交互的时候,必须是在一次请求中才可以使用,如果重新发起了action请求或者在result中设置了发起客户端请求(重定向),那该实例中的属性和对象均不可使用。因为struts的跳转其实是一次request请求。在struts.xml文件值配置的<result>addSuccess..jsp</result>此时是struts发起的服务端跳转,并没有发起新的请求。这个请求是从form表单开始的,完整的请求过程如下: form发起请求---struts处理action----后台HeroAction的addHero()---result服务端跳转到addSuccess..jsp---完成响应,请求结束。
二:struts的配置文件(include、namespace、中文问题、上传最大值、通配符)
1. 我们在开发具体的项目时,一般都会需要用到很多实体很多功能实现类,那如果所有的action都在struts.xml文件中配置的话,将会变得特别难维护和修改。此时可以通过将不同的struts文件分散开来,以<include>标签用引入的方式引入到struts.xml来完成使用。我们在src中新建struts(命名可随意)文件夹然后新建hero.xml文件,按照struts的配置文件标准配置完成action,在src下的struts.xml中(struts默认读取路径是src,可以修改)通过include按照在src下的路径引入该hero.xml文件(strtus/hero.xml)。即可使用,struts会先读取struts.xml文件然后碰到include标签,会按照路径在src下寻找并读取该文件。代码如下:
******************struts.xml文件********************
<struts>
<constant name="struts.i18n.encoding" value="UTF-8"/>这里是设置的struts的中文编码
<include file="struts/hero.xml"></include>
</struts>
***********************hero.xml文件**********************
<struts>
<package name="hero" extends="struts-default" namespace="/hero">
<action name="addHero" class="action.HeroAction" >
<result name="OK">addSuccess.jsp</result>
<result name="NO">no.jsp</result>
</action>
</package>
</struts>
2. namespace的使用:项目中会在某个struts的配置文件中添加package的name和package的namespace,name即为该package的唯一标识,不可重复。namespace的使用是用来区分请求地址,添加namespace之后,可以有效地防止action的name命名重复的问题。例如,在本例中,设置了namespace为/hero,则,访问该action的路径就是/hero/addHero。
注1:设置了namespace为/hero之后,result返回时,如果跳转的地址为addSuccess.jsp,默认会去该项目的默认路径下(动态web项目默认的路径就是WebContent)找hero下的addSuccess.jsp。如果不想让它跳转到hero/addSuccess.jsp,可以使用type="redirect"重定向,或者使用绝对路径跳转:/pro/vm/index.jsp,此时会跳转到默认路径(根目录)下的pro下vm下的index.jsp文件。
注2:addHero.jsp在form中action的地址值:<form action:"addHero" namespace:"/hero">此时,提交该form后,struts会首先去namespace为/hero的struts中匹配该action值,如果没有,则再去默认namespace中匹配action的值。而且分为往上寻找的机制,例如action地址为:path1/path2/addHero,则发起请求时,
先去寻找namespace为path1/path2的package,如果有则找addHero,如果没有这个action则404。
如果没找到path1/path2的package,则找path1的namespace,如果有则找action。
如果没有path1,则去未命名(默认的)的namespace中找addHero这个action。找不到的话404。
如果我们在struts.properties设置了只能通过地址栏中的package地址寻找的话,struts只会去namespace为path1/path2的package中找action,找不到则404。
3. struts的中文问题,可以在struts.xml中加上:<constant name="struts.i18n.encoding" value="UTF-8"></constant>
4. 上传文件最大值问题,可在struts.xml中加:
<constant name="struts.multipart.maxSize" value="10240000"/>上传文件最大为:10240KB也就是10M
5. 通配符:可以将struts的action的name值设为通配符,如下,name为*product,如果请求的action为addproduct,会找到该action,并跳转到method为add中。
<action name="*product" class="action.ProductAction" method="{1}">
<result name="input">add.jsp</result>
</action>
三:request、response、session的获取
1. request、response:
可以在后台方法中:通过一下方式获取,通常可以使用response来配合struts完成ajax请求。
HttpServletRequest request = ServletActionContext.getRequest();
HttpServletResponse response = ServletActionContext.getResponse();
2. 通过response返回ajax请求值:
主要为使用response.getWriter().write(checkResult)在ajax请求的后台方法中写入响应参数checkResult,并传递给ajax的响应函数中使用。完成判断。
本篇内容不再讲解,博主会单独写一篇文章,通过hibernate访问数据库,并在前台页面完成注册,验证账号是否存在。通过struts进行跳转。
3. session的获取:
通过map的形式:
Map m = ActionContext.getContext().getSession();
m.put("name", product.getName());
此session会贯穿在整个访问会话中,可以通过session并配合struts的拦截器,设置验证用户是否登录。原理就是用户登录之后,在session中存放一个key,并将用户名存在里面,配置action的拦截器,当访问是,在拦截器中验证session中是该key值是否为空,若为空则表示没有登录过。
四:struts的拦截器interceptor:
此拦截器的功能可以理解为servlet中的filter功能,例如我们在addHero这个action中配置了一个struts的拦截器,则访问该action的请求都会被拦截器拦截,去该拦截器中执行一定的业务逻辑,然后根据逻辑告诉action是否继续往下执行还是直接返回结果。
具体配置拦截器如下:
在struts配置文件中,使用<interceptor>声明一个拦截器,并制定该拦截器类所在的路径。
在action中,<interceptor-ref>标签告诉这个action,我要使用这个拦截器,注意:除了自己配置的拦截器之外,一定要引入struts默认的拦截器defaultStack。否则会报错或者不生效。
<struts>
<package name="basicstruts" extends="struts-default">
<interceptors>
<interceptor name="myInter" class="interceptor.MyInter" />
</interceptors>
<action name="addHero" class="action.HeroAction" method="addHero">
<interceptor-ref name="myInter" />
<interceptor-ref name="defaultStack" />
<result name="list">list.jsp</result>
<result name="login">login.jsp</result>
</action>
</package>
</struts>
在配置的拦截器类中:假设此时我们已经登陆了,并且登录时,在session中设置了map.put("userName",userName)的值,在这个拦截器中,将进行是否已经登录的验证,如果登陆了则放行,否则返回登录页面。
public class DateInterceptor extends AbstractInterceptor {
@Override
public String intercept(ActionInvocation invocation) throws Exception
{
ActionContext context = invocation.getInvocationContext();
Map m = context.getSession();
String userName = (String)m.get("userName");
if(userName!=null)
{
return invocation.invoke(); //表示拦截器放行通过,继续访问action的方法。
}
else
{
context.put("errorMsg","请登录!");
return "login";//返回login,此时struts会执行result中name为login的跳转页面。
}
}
五:调试器、客户端跳转、表单验证
1. 调试:导入struts2-config-browser-plugin-2.2.3.1.jar这个包,然后访问localhost8080/struts/config-browser/actionNames也就是项目地址+config-browser/actionNames就可以看到struts中配置的所有action列表。
或者直接在返回的jsp页面中直接添加s:debug,点击它可以看到struts的情况,包括传到该页面的参数对象,session的值等信息。
2. 客户端跳转:可以在struts的result中添加type="redirect",完成跳转的重定向实现客户端跳转。
<action name="add" class="action.HerotAction" method="add">
<result name="add" type="redirect">add.jsp</result>
</action>
如果需要传递参数的话可以在跳转的地址中添加:add.jsp?name=${name}或者将参数设置在session中,在前台获取。
3. 表单验证:一般很少用。这里不做介绍了,主要是通过action请求的后台中validate()方法进行访问,还可以通过配置xml进行表单的验证,具体请自行百度学习,这不在赘述。
六:s:标签
在使用struts的s:标签时,需要通过指令引入该标签,即可完成使用。<%@ taglib prefix="s" uri="/struts-tags" %>
1. s:iterator遍历,它的使用很类似与JSTL中的fc:orEach标签。用来遍历输出集合中的值或者对象。使用如下:
访问action并返回一个集合值List<Hero> heros
<table align="center">
<tr>
<td>id</td>
<td>name</td>
<td>st.index</td>
<td>st.count</td>
<td>st.first</td>
<td>st.last</td>
<td>st.odd</td>
<td>st.even</td>
</tr>
<s:iterator value="heros" var="p" status="st">
<tr>
<td>${p.id}</td>
<td>${p.name}</td>
<td>${st.index}</td>
<td>${st.count}</td>
<td>${st.first}</td>
<td>${st.last}</td>
<td>${st.odd}</td>
<td>${st.even}</td>
</tr>
</s:iterator>
</table>
s:iterator标签进行遍历
value 表示集合、var 表示遍历出来的元素、st 表示遍历出来的元素状态
st.index 当前行号 基0、st.count 当前行号 基1、st.first 是否是第一个元素、st.last 是否是最后一个元素、st.odd 是否是奇数
st.even 是否是偶数
2. s:form提交,
<s:form action="addHero">
<s:textfield name="hero.name" label="请输入名字:" />
<s:submit value="Submit" />
</s:form>
3.s:check多选按钮,这是一个多选按钮,可以设置默认选中值,
<s:checkboxlist value="selectedHeros" name="hero.id"
list="heros" listValue="name" listKey="id" />
value:哪些被选中、name:提交到服务端用的名称、list:用于遍历的集合、listValue:显示的checkbox的名称
listKey:checkbox的value
解析:在后台中selectedHerds这个集合对象中我们添加id为1,2的hero对象,在heros中添加id为1,2,3的hero对象,然后传递到这个check中,则value中就是id为1,2的集合也就是默认会选中listKey为1,2的按钮,list为heros代表将1,2,3所有的hero作为按钮展示出来,显示的是listValue为hero的name值,值是listKey为hero的id值,name则是提交这个表单时,提交选中的hero的id值。
4. s:radio单选按钮,使用方法基本与check一致,只是他是单选按钮,不可以多选。
<s:radio name="product.id" value="2" list="products" listValue="name"
listKey="id" />
5. s:select选择列的使用,选择框的使用。可以设置默认选择值。
<s:select label="英雄列表"
name="hero.id"
list="heros"
listKey="id"
listValue="name"
multiple="true"
size="3"
value="selectedheros"
/>
name表示:提交到服务端用的名称
list:用于遍历的集合
listKey:每个option的value
listValue:显示的名称
multiple:true表示可以选中多行
size="3"表示默认显示3行
value表示:哪些被选中
有关struts的介绍和使用就到这里
声明:此文章为本人实际学习复习笔记
补充:struts的运行过程分析:
客户端发起一个HTTPServletRequest请求;
这个请求经过WEB.XML配置的filter过滤器进行过滤;
发现FilterDispatcher,然后调用该过滤器,FilterDispatcher询问ActionMapper来决定这个请求是否需要调用具体某个Action;
如果ActionMapper决定需要调用某个Action,FilterDispatcher把处理权限交给ActionProxy来处理;
ActionProxy通过Configuration Manager(适配管理)询问struts的配置文件,找到需要调用的Action类;
找到之后,ActionProxy会创建一个ActionInvocation的实例;
ActionInvocation在调用Action的过程前后,可以进行interceptor拦截器的进行。
当Action执行完毕,ActionInvocation负责根据返回结果寻找struts.xml中的配置中result需要返回的页面。