struts2学习总结(一)
一个struts2的启动流程 ,自己总结:
启动流程:
1、当tomcat服务器启动时,执行了过滤器中的init(初始化)方法,
加载*src根目录下*的3个配置文件,struts-default.xml、struts-plugin.xml、struts.xml。
因为这三个文件的dtd约束是一样的,所以如果这三个文件有相同的项,后面覆盖前面的。
1) 当在现实的项目应用中,程序员只需要关注struts.xml即可。
可以在该xml文件中配置大量的自己需要配置的内容,如普通的action、拦截器、自定义的结果集等。
2、上述内容中,因为在struts.xml文件中, 所有的包都继承了struts-default包(在struts-defult.xml文件中),
所以程序员开发的action具有struts-default包中所有类的功能。(即默认的拦截器和结果集等)
而struts-default.xml文件在web服务器启动的时候就加载了。
在struts-default.xml文件中,定义了struts2容器的核心内容。
命名空间:
1、在说明extends用法的时候,
<package name="base" extends="struts-default" namespace="base"></package>
我们引用了这样一个url:
http://localhost:8080/struts2/base/helloWorld.action。
如果我们把url改成这样:
http://localhost:8080/struts2/base/a/helloWorld.action。
行吗?答案是可以的。
再改成这样的url:
http://localhost:8080/struts2/base/a/b/helloWorld.action
行吗?答案也是可以的。如果这样呢
http://localhost:8080/struts2/helloWorld.action
可以吗?这样就不行了。为什么?
说明:
1.struts2会在当前的命名空间下查找相应的action
2.如果找不到,则会在上级目录中查找,一直查找到根目录
3.如果根目录也找不到,报错。
4.如果直接访问的就是根目录,找不到,这样的情况就会直接报错。不会再去子目录中进行查找。
总结:也就是说,struts寻找action时的寻找方式是从外向内寻找的,只要当前的虚拟路径不是namespace的最底层,
它就会按照一级一级,向最底层寻找,直至达到namespace为止,如果找不到,则会报错。
包的继承机构(extends):
1、在配置package时,通常情况下都是extends="struts-default",而struts-default.xml文件中配置了struts2的核心内容。
所以程序员缩写的package只要是继承自该包,那么就具备了struts2的核心功能。实现了扩展。
需要注意的是,在后期的学习和使用中,需要用到大量的自定义拦截器和自定义结果集,在写package包结构时,
不用忘记至少有一个顶级包是继承至struts-default的,否则会出现struts的功能无法使用的情况。
而拦截器也是一样,在配置时,声明自己的拦截器 ,不要忘记将struts原有的拦截器注入到自己的拦截器栈中,
否则很多功能都会失效,例如:值栈结构(基于属性拦截器)。
写一个最简单的action:
1.写一个类实现ActionSupport接口,复写里面的execute()方法,在该方法中放入需要执行的代码。默认返回success;
在配置文件中,<action>中class属性可以不用书写,默认的就会启动ActionSupport这个类。
结果类型:
1.每个action方法都返回一个String类型的值,struts一次请求返回什么值是由这个值确定的。
2.在配置文件中,每一个action元素的配置都必须有result元素,每一个result对应一个action的返回值。
3.Result有两个属性:
name:结果的名字,和action中的返回值一样,默认值为success;
type:响应结果类型,默认值为dispatcher(转发).
在struts-default.xml中:
<result-types>
<result-type name="chain" class="com.opensymphony.xwork2.ActionChainResult"/>
<result-type name="dispatcher" class="org.apache.struts2.dispatcher.ServletDispatcherResult" default="true"/>
<result-type name="freemarker" class="org.apache.struts2.views.freemarker.FreemarkerResult"/>
<result-type name="httpheader" class="org.apache.struts2.dispatcher.HttpHeaderResult"/>
<result-type name="redirect" class="org.apache.struts2.dispatcher.ServletRedirectResult"/>
<result-type name="redirectAction" class="org.apache.struts2.dispatcher.ServletActionRedirectResult"/>
<result-type name="stream" class="org.apache.struts2.dispatcher.StreamResult"/>
<result-type name="velocity" class="org.apache.struts2.dispatcher.VelocityResult"/>
<result-type name="xslt" class="org.apache.struts2.views.xslt.XSLTResult"/>
<result-type name="plainText" class="org.apache.struts2.dispatcher.PlainTextResult" />
</result-types>
上面列出了struts2的10种基本的结果集。结果集类型可以是这10种的任意一种。
默认的是servletDispatcherResult即转发。
关于配置文件的写法 有2种
第一种写法:
<result name="success">/resulttype/successDispatcher.jsp</result>
第二种写法:
<result name="success">
<param name="location">/resulttype/successDispatcher.jsp</param>
</result>
常用第一种方法,直接在标签体内书写路径即可,使用技巧,可将所有页面放置在web-inf目录下,
而在标签体内直接写入绝对路径访问即可。
第二种写法,是配置param参数,dispatcher的情况下name=“location” 在param标签内配置访问的路径。
除了Dispatcher外,结果集还有Redirect类型,即:重定向。
需要注意的是:Redirect属于重定向。如果用redirect类型,则在reuqest作用域的值不能传递到前台。
更改结果集的类型,只需要在result标签内配置type=“需要定义的类型” 即可。
二:Action原型模式
回顾servlet
在servlet中,一个servlet只有一个对象,也就是说servlet是单例模式。如果把一个集合
写在servlet属性中,则需要考虑线程安全的问题。
Action多例模式
但是在struts2的框架中,并不存在这种情况,也就是说struts2的action,只要访问一次
就要实例化一个对象,这样就不会存在数据共享的问题。这也是struts2框架的一个好处,
也就是说struts2框架中的action是线程安全的。
三、通配符:
execute方法的弊端:
假设有这样的需求:
1、有一个action为personAction。
2、需要在该action中实现增、删、改、查四个方法。
3、但是因为action中的方法入口只有一个execute方法。所以想要完成这样的功能,
有一种方法就是url连接中加参数,那么action中的代码需要实现很多逻辑判断:
例:
public class PatternAction extends ActionSupport{
private String method;
public String execute(){
if(method.equals("add"){
....
}
if(method.equals("update")){
.....
}
.......
return "";
}
}
这样写无疑很麻烦,通配符则解决了这个问题。
解决方式:
1.method的属性:
在一个package中,配置多个action,method用不同的方法名。(配置量大,可读性差)
2.动态调用方法:
在url中通过action名称!方法名称可以动态调用方法。
例:Pattern.jsp
动态调用PatternAction中的add方法:<br>
<a href="${pageContext.request.contextPath}/pattern/patternAction!add.action">测试</a>
struts-pattern.xml
<action name="patternAction"
class="cn.itcast.struts2.action.pattern.PatternAction">
</action>
说明:这样的情况在配置文件中不需要method属性,struts2会自动帮你反射该方法调用。
3.通配符映射:
映射例子一:
需求:a_add.action、b_add.action、c_add.action全部请求PatternAction的add方法
Pattern.jsp
通配符映射实例1:<br>
<a href="${pageContext.request.contextPath}/pattern/a_add.action">测试</a>
<a href="${pageContext.request.contextPath}/pattern/b_add.action">测试</a>
<a href="${pageContext.request.contextPath}/pattern/c_add.action">测试</a>
说明:不管是a_add还是b_add还是c_add的路径都指向PatternAction的add方法。
配置如下:
struts-pattern.xml
<action name="a_add" method="add"
class="cn.itcast.struts2.action.pattern.PatternAction">
</action>
<action name="b_add" method="add"
class="cn.itcast.struts2.action.pattern.PatternAction">
</action>
<action name="c_add" method="add"
class="cn.itcast.struts2.action.pattern.PatternAction">
</action>
上述结构是很差的,经过改进如下:
<action name="*_add" method="add"
class="cn.itcast.struts2.action.pattern.PatternAction">
总结:可以使用*号通配符代替所有。
映射例子二:
请求PersonAction和StudentAction的add方法
Pattern.jsp
通配符映射实例2:
<br>
<a
href="${pageContext.request.contextPath}/pattern/personAction_add.action">请求personAction的add方法</a>
<a
href="${pageContext.request.contextPath}/pattern/studentAction_add.action">请求studentAction的add方法</a>
Struts-pattern.xml
<action name="personAction_add" method="add" class="cn.itcast.struts2.action.pattern.PersonAction"></action>
<action name="studentAction_add" method="add" class="cn.itcast.struts2.action.pattern.StudentAction"></action>
改进如下:
<action name=”*_add” method=”add” class=” cn.itcast.struts2.action.pattern.{1}”/>
说明:*和{1}是相对应的关系。
总结:*号和{1}是对应的关系 {1}即代表这个*号所代表的。
映射实例三:
需求:在TeacherAction中有增、删、改、查的方法。这个时候配置文件怎么写比较简单?
Pattern.jsp
通配符映射实例3:
<a
href="${pageContext.request.contextPath}/pattern/PersonAction_add.action">请求teacherAction的add方法</a>
<a
href="${pageContext.request.contextPath}/pattern/StudentAction_update.action">请求teacherAction的update方法</a>
<a
href="${pageContext.request.contextPath}/pattern/StudentAction_delete.action">请求teacherAction的delete方法</a>
<a
href="${pageContext.request.contextPath}/pattern/StudentAction_query.action">请求teacherAction的query方法</a>
struts-pattern.xml
<action name="teacherAction_*" method="{1}"
class="cn.itcast.struts2.action.pattern.TeacherAction">
</action>
说明:*和method的属性值保持一致。
延伸:
<action name="*_*" method="{2}"
class="cn.itcast.struts2.action.pattern.{1}">
</action>
第一个*匹配{1},第二个*匹配{2}
通配符也可以使用在结果集上,class上 ,method,name中。但是配置越多越复杂,可读性越差。
通常采用普通的配置即可:
例:<struts>
<package name="forward" namespace="/" extends="struts-default">
<action name="forwardAction_*" method="{1}" class="com.itheima03.oa.struts2.action.ForwardAction">
<result name="top">WEB-INF/jsp/frame/top.jsp</result>
<result name="bottom">WEB-INF/jsp/frame/bottom.jsp</result>
<result name="left">WEB-INF/jsp/frame/left.jsp</result>
<result name="right">WEB-INF/jsp/frame/right.jsp</result>
<result name="kynamic">WEB-INF/jsp/kynamic/kynamic.jsp</result>
</action>
</package>
</struts>
四:全局结果类型
说明:当很多提交请求跳转到相同的页面,这个时候,这个页面就可以成为全局的页面。在struts2中提供了全局页面的配置方法。
例:
Struts-pattern.xml
<global-results>
<result name="success">success.jsp</result>
</global-results>
注意:
* 这个配置必须写在action配置的上面。dtd约束的规定。
* 如果在action的result中的name属性也有success值,顺序为先局部后全局。
全局结果集的应用:
例:错误的统一处理
<?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="struts-global" namespace="/" extends="json-default">
<global-results>
<result name="errHandler" type = "chain">
<param name="actionName">errorProcessor</param>
</result>
</global-results>
<global-exception-mappings>
<exception-mapping exception="java.lang.Exception" result="errHandler"></exception-mapping>
</global-exception-mappings>
<action name="errorProcessor" class="cn.itheima01.oa.exception.ErrorProcessor">
<result></result>
</action>
</package>
</struts>
java类定义:
package com.itheima.exception;
import org.apache.struts2.json.annotations.JSON;
import com.opensymphony.xwork2.ActionSupport;
public class ErrorProcessor extends ActionSupport {
private String ss = "";
public String getSs() {
return ss;
}
public void setSs(String ss) {
this.ss = ss;
}
private Exception exception;
@JSON(serialize=false)
public Exception getException() {
return exception;
}
public void setException(Exception exception) {
this.exception = exception;
}
@Override
public String execute() throws Exception {
this.ss = this.exception.getMessage();
return SUCCESS;
}
}
五:struts2与servlet的接口
说明:
通过前面的练习大家都知道,在action的方法中与servlet的所有的API是没有任何关系的。
所以在struts2中做到了aciton与serlvet的松耦合,这点是非常强大的。
但是如果没有HttpServletRequest,HttpServletSession,ServletContext有些功能是没有办法完成的。
例如购物车程序,需要把购买的物品放入session中。所以就得找一些路径使得在struts2中和serlvet的API相结合。
入口类:
1.struts2提供的servlet交互的入口类:ServletActionContext,通过该入口类,
可以获得与servlet相关懂得request对象,servletContext对象,session对象等。
(2.实现ServletContextAware接口、sessionAware、ServletRequesttAware接口等
在类中设置该3个对象的set方法,struts2会自动为其实例化。)(需验证)
六、拦截器
当有一个需求验证的时候,虽然可以在action中进行逻辑判断,但是这样做使得程序的结构不是很好,
如果权限的判断很复杂或者是业务逻辑很复杂会造成后期维护的非常困难。
这种形式只能控制一个action中的一个方法。如果很多action中的很多方法都需要这种控制。会导致大量的重复代码的编写。
在struts2中,用拦截器(interceptor)完美的实现了这一需求。
在struts2中,内置了很多拦截器,在struts-default.xml文件中可以看出。
用户还可以自定义自己的拦截器。自定义拦截器需要以下几点:
在配置文件中:
包括两个部分:声明拦截器栈和使用拦截器栈
示例代码:
struts-interceptor.xml
<!--
声明拦截器
-->
<interceptors>
<!--
定义自己的拦截器
-->
<interceptor name="accessInterceptor"
class="cn.itcast.struts2.action.interceptor.PrivilegeInterceptor">
</interceptor>
<!--
声明拦截器栈
-->
<interceptor-stack name="accessInterceptorStack">
<!--
引用自定义的拦截器
-->
<interceptor-ref name="accessInterceptor"></interceptor-ref>
<!--
引用struts2内部的拦截器栈
-->
<interceptor-ref name="defaultStack"></interceptor-ref>
</interceptor-stack>
</interceptors>
说明:红色部分是struts2内部的拦截器。可以从struts-default.xml文件中得到内容。
自己定义的栈必须引入struts2默认的栈。因为我们在访问action时,属性的赋值等一些工作都是由内部的栈完成的。
如果不怎么写,struts2的功能将发挥不出来。可以用debug进行调试也能得出结论。
<!--
使用拦截器
-->
<default-interceptor-ref name="accessInterceptorStack"></default-interceptor-ref>
说明:使用拦截器栈。从上面声明部分可以看出,accessInterceptorStack栈既包括了自定义的拦截器,
又包括了struts2内部的拦截器栈。
拦截器类的定义:
示例代码:
public class PrivilegeInterceptor implements Interceptor{
public void destroy() {
// 销毁时调用
}
public void init() {
// 初始化时调用
}
public String intercept(ActionInvocation invocation) throws Exception {
System.out.println("aaaa");
//得到当前正在访问的action
System.out.println(invocation.getAction().toString());
//得到Ognl值栈
System.out.println(invocation.getInvocationContext().getValueStack());
//请求路径action的名称,包括方法
System.out.println(invocation.getProxy().getActionName());
//方法名称
System.out.println(invocation.getProxy().getMethod());
//命名空间
System.out.println(invocation.getProxy().getNamespace());
String method = invocation.invoke();
return null;
}
}
说明:
这个类中init、intercept和destroy三个方法说明了一个拦截器的生命周期。
在interceptor方法中的参数invocation是执行action的上下文,可以从
这里得到正在访问的action、Ognl值栈、请求路径、方法名称、命名空间
等信息。以帮助程序员在拦截器中做相应的处理工作。
红色部分是关键部分,就是调用action的方法。这里可以成为目标类的
目标方法。
因为这里配置的拦截器是针对包的,所以只要是包的action都起作用,当然也可以采用继承的方式来实现。
业务需求,表单验证:
ValidateAction.java
public class ValidateAction extends ActionSupport{
private String username;
private String password;
//set和get方法
public String testValidate(){
return "success";
}
public String aaa(){
return "success";
}
public void validate() {
if(this.username.equals("")){
this.addFieldError("username", "用户名不能为空");
}
if(this.password.equals("")){
this.addFieldError("password", "密码不能为空");
}else if(this.password.length()<6){
this.addFieldError("password", "密码不能小于6个字符");
}
}
}
说明:
在ActionSupport类中有这样的描述:
/**
* A default implementation that validates nothing.
* Subclasses should override this method to provide validations.
*/
public void validate() {
}
这是一个默认的实现,子类只需要复写该方法,将验证的内容写在该方法 之中即可。
struts2与json的整合
1、步骤
1、导入jar包
struts2-json-plugin-2.1.8.1
该jar包的根目录有一个struts-plugin.xml文件
<package name="json-default" extends="struts-default">
<result-types>
<result-type name="json" class="org.apache.struts2.json.JSONResult"/>
</result-types>
<interceptors>
<interceptor name="json" class="org.apache.struts2.json.JSONInterceptor"/>
</interceptors>
</package>
从该配置文件可以看出:
1、自定义了一个结果集json-default
2、有一个拦截器json
2、在struts2的配置文件中
<result type="json"></result>
注意:
1、结果集的类型是json
2、因为是ajax请求,所以不需要返回任何页面,所以result中间没有内容
3、在action中,声明一个属性,该属性有get方法,属性有返回值,例如:
public String getXxx(){
return "aaa";
}
那么将以如下的值返回到客户端:'xxx':'aaa'
4、在action中,如果不需要返回客户端值,那么方法最好别以get方法开头
5、在客户端就能接收到服务器端返回的数据