Struts学习总结 学习
ContextMap 包含值栈包含 root(list结构)和context(map结构) 值栈包含contextMap的引用. Actioncontext是工具类 可以获取他们
Struts2拥有优良的设计和功能,其优势具体如下: 优点
l 项目开源,使用及拓展方便,天生优势。
l 提供Exception处理机制。
l Result方式的页面导航,通过Result标签很方便的实现重定向和页面跳转。 全局结果视图
l 通过简单、集中的配置来调度业务类,使得配置和修改都非常容易。
l 提供简单、统一的表达式语言来访问所有可供访问的数据。
l 提供标准、强大的验证框架和国际化框架。
l 提供强大的、可以有效减少页面代码的标签。
l 提供良好的Ajax支持。
l 拥有简单的插件,只需放入相应的JAR包,任何人都可以扩展Struts2框架,比如自定义拦截器、自定义结果类型、自定义标签等,为Struts2定制需要的功能,不需要什么特殊配置,并且可以发布给其他人使用。
l 拥有智能的默认设置,不需要另外进行繁琐的设置。使用默认设置就可以完成大多数项目程序开发所需要的功能。
上面列举的就是Struts2的一系列技术优势,只需对它们简单了解即可,在学习了后面的知识后,会慢慢对这些技术优势有更
好的理解和体会。
请简述下Struts2 的执行流程.
首先是,启动tomcat服务器,这时候会加载web.xml,当读到filter标签时,会创建过滤器对象。struts2的核心过滤器(StrutsPrepareAndExecuteFilter)会负责加载类路径下的struts.xml配置文件。接下来,从客户端发送请求过来 先经过前端控制器(核心过滤器StrutsPrepareAndExecuteFilter),前端控制器会根据请求的名称在struts.xml中找到对应的配置,创建我们的动作类对象(每次访问时都会创建新的Action对象),然后执行指定的方法,根据方法的返回值找到Result的配置进行页面的跳转.最后响应浏览器。
加载struts2的配置文件,他们的加载顺序及作用.
default.properties
struts-default.xml
strtuts-plugin.xml
struts.xml
struts.properties
web.xml
当多个配置文件中,有相同的参数,后加载的配置文件中的值会把前面加载的配置文件的值给覆盖了。
实现一下动作类的三种创建方式.
第一种:无侵入式创建一个action实现一个跳转(实际开发中基本不用)
第二种:实现Action接口的定义方式 (实际开发中用的也不多)
第三种:继承ActionSupport (们实际开发中采用的方式)
1. Struts2与Struts1的联系与区别是什么?为什么要用Struts2?
答案:
struts1与struts2都是mvc框架的经典实现模式。
Struts2不是从Struts1升级而来,而是有WebWork改名而来,Struts 2以WebWork为核心,采用拦截器的机制来处理用户的请求
区别:(了解几条即可)
1.核心控制器改成了过滤器(过滤器比Servlet的级别要高,因为程序运行时是先进入过滤器再进入Servlet)
2.struts1要求业务类必须继承Action或dispatchAction,struts2不强制这么做,只需要提供一个pojo类。
3.绑定值到业务类时struts1是通过ActionForm,struts2是通过模型或属性驱动直接绑定到Action属性。
4.struts1严重依赖于Servlet(因为太过于依赖于api的HttpServletRequest与HttpServletResponse的两个参数),
struts2就则脱离了Servlet的API。
5.管理Action时struts1是单例模式,struts2是每个请求产生一个实例。
6.在表达式的支持上struts2不仅有jstl,还有功能更加强大的ognl表达式。
7.struts1的类型转换是单向的(页面到ActionForm),struts2是双向的(页面到Action再到页面回显)
8.校验,struts1没有针对具体方法的校验,struts2提供了指定某个方法进行效验,还有框架校验。
请求(.action)---->经过StrutsPrepareAndExecuteFilter 核心控制器---->进入到Struts2的拦截器Interceptor(实现代码功能)----->通过action的名称找对应的Action类----->执行Action类的execute方法----->通过execute方法中返回的字符串,在Struts.xml中找对应的结果页面(result)【在action执行之前,执行了defaultStack拦截器栈】
* 拦截器 在 struts-default.xml定义 【它位于sruts2-core-xxx.jar目录下】
* 执行拦截器 是 defaultStack 中引用拦截器
获取域对象的两种方式 例如获取request
1.1 使用ServletActionContext
为了直接访问Servlet API,Struts2框架提供了ServletActionContext类,在实际开发中,这也是我们采用最多的方式。该类包含了几个常用的静态方法,具体如下:
l static HttpServletRequest getRequest():获取Web应用的HttpServletRequest对象。
l static HttpServletResponse getResponse():获取Web应用的HttpServletResponse对象。
l static ServletContext getServletContext():获取Web应用的ServletContext对象。
l static PageContext getPageContext():获取Web应用的PageContext对象。
接下来,讲解如何通过ServletActionContext访问Servlet API。使用方式如下:
/**
* 在struts2框架中有三种方式可以获取到ServletAPI对象
* ServletAPI对象包含了:
* request,response,HttpSession,ServletContext
* 第一种方式:
* 使用的是ServletActionContext对象,它里面提供了一些静态方法,可以让我们获取到ServletAPI
* 此种方式,使我们实际开发中采用最多的方式
* 此时我们还要记住一件事:
* 这四个对象,有一个已经变了,和其他三个不一样,找出来,记住。
*
* org.apache.struts2.dispatcher.StrutsRequestWrapper@1577071 它是那个不一样的
org.apache.catalina.connector.ResponseFacade@1973ca4
org.apache.catalina.session.StandardSessionFacade@e4d72
org.apache.catalina.core.ApplicationContextFacade@fe7e93
*
*/
public class HelloAction extends ActionSupport{
private HttpServletRequest request;
private HttpServletResponse response;
private HttpSession session;
private ServletContext application;
/**
* @return
*/
public String sayHello(){
request = ServletActionContext.getRequest();
response = ServletActionContext.getResponse();
session = request.getSession();
application = ServletActionContext.getServletContext();
System.out.println(request);
System.out.println(response);
System.out.println(session);
System.out.println(application);
return SUCCESS;
}
}
1.2 通过实现接口的方式
Struts2还提供了一系列接口,具体如下:
l ServletRequestAware:实现该接口的Action可以直接访问Web应用的HttpServletRequest实例。
l ServletResponseAware:实现该接口的Action可以直接访问Web应用的HttpServletResponse实例。
l SessionAware:实现该接口的Action可以直接访问Web应用的HttpSession实例。
l ServletContextAware:实现该接口的Action可以直接访问Web应用的ServletContext实例。
/**
* 在struts2框架中有三种方式可以获取到ServletAPI对象
* ServletAPI对象包含了:
* request,response,HttpSession,ServletContext
* 第三种方式:
* 采用实现接口的方式,来获取对象
* 能够使用对象的原因:
* 是因为在我们的sayHello方法执行之前,已经有一些拦截器执行了。
* 通过struts-default.xml我们能知道默认的拦截器栈defaultStack,
* 在我们的动作配置时,默认就会有该拦截器栈中的所有拦截器执行。先正序,再倒序。
*
* 帮我们给对象赋值全靠一个名称为servletConfig的拦截器
*
* 拦截器的重要性:
* 他可以在不改变我们动作方法的代码基础上,对我们的动作方法进行增强。
*/
public class Hello3Action extends ActionSupport
implements ServletRequestAware,ServletResponseAware,ServletContextAware{
private HttpServletRequest request;
private HttpServletResponse response;
private HttpSession session;
private ServletContext application;
/**
* @return
*/
public String sayHello(){
session = request.getSession();
System.out.println(request);
System.out.println(response);
System.out.println(session);
System.out.println(application);
return SUCCESS;
}
@Override
public void setServletRequest(HttpServletRequest request) {
this.request = request;
}
@Override
public void setServletResponse(HttpServletResponse response) {
this.response = response;
}
@Override
public void setServletContext(ServletContext application) {
this.application = application;
}
}
1. 值栈ValueStack的原理与生命周期?
答案:
1)ValueStack贯穿整个 Action 的生命周期,保存在request域中,所以ValueStack和request的生命周期一样。当Struts2接受一个请求时,会迅速创建ActionContext工具类,
ValueStack,action。然后把action存放进ValueStack,所以action的实例变量可以被OGNL访问。请求来的时候,action、ValueStack的生命开始,请求结束,action、ValueStack的生命结束;
2)action是多例的,和Servlet不一样,Servelt是单例的;
3)每个action的都有一个对应的值栈,值栈存放的数据类型是该action的实例,以及该action中的实例变量,Action对象默认保存在栈顶;
4)ValueStack本质上就是一个ArrayList;
5)关于ContextMap,Struts 会把下面这些映射压入 ContextMap 中: 只能用%{#取出}
parameters : 该 Map 中包含当前请求的请求参数
request : 该 Map 中包含当前 request 对象中的所有属性 session :该 Map 中包含当前 session 对象中的所有属性
application :该 Map 中包含当前 application 对象中的所有属性
attr:该 Map 按如下顺序来检索某个属性: request, session, application
6)使用OGNL访问值栈的内容时,不需要#号,而访问request、session、application、attr时,需要加#号;
7)注意: Struts2中,OGNL表达式需要配合Struts标签才可以使用。如:<s:property value="name"/>
8)在struts2配置文件中引用ognl表达式 ,引用值栈的值 ,此时使用的"$",而不是#或者%;
OGNL表达式和EL表达式的区别
OGNL可以访问静态属性和静态方法
随机数:<s:property value="@java.lang.Math@random()"/>
1.1.1 OGNL表达式创建集合
1.1.1.1 list集合
用HTML在浏览器上输出一个单选性别:
<input type="radio" name="gender" value="男"/>男
<input type="radio" name="gender" value="女"/>女
<br/>
用Struts2的单选按钮标签输出一个单选
<%--s:radio用于在浏览器上显示一个单选按钮
list属性取值是一个OGNL表达式
{}就表示创建了一个List集合 List list = new ArrayList();
{'男','女'}
list.add("男");list.add("女");
--%>
<s:radio name="gender" list="{'男','女'}" label="性别"/>
1.1.1.2 map集合
用HTML在浏览器上输出一个单选性别:Map结构
<input type="radio" name="gender" value="male"/>男
<input type="radio" name="gender" value="female"/>女
<br/>
<%--#{}就表示创建了一个Map
里面的写法
#{'key':'value','key':'value'......}
--%>
<s:radio name="gender" list="#{'male':'男','female':'女'}" label="性别"/>
1.1 ActionContext
1.1.1 ActionContext对象概述
它是一个工具类,是struts2框架提供给我们的,可以让我们调用其中的方法,快速的操作ContextMap。用它操作OGNL上下文对象,比直接操作ContextMap要方便很多。
1.1.2 ActionContext对象以及和ContextMap的关系
ActionContext就相当于对ContextMap进行了一次再封装。
1.1.3 ActionContext何时创建
由于ActionContext是操作的ContextMap,而ContextMap中封了我们一次请求的所有数据,所以它的创建应该是每次请求访问Action时,即核心控制器(StrutsPrepareAndExecuteFilter)的doFilter方法执行时,下图是代码截取:
1.1.1 ValueStack对象概述
ValueStack是Struts的一个接口,字面意义为值栈,OgnlValueStack是ValueStack的实现类,客户端发起一个请求struts2架构会创建一个action实例同时创建一个OgnlValueStack值栈实例,OgnlValueStack贯穿整个 Action 的生命周期。
它是ContextMap中的一部分,里面的结构是一个List,是我们可以快速访问数据一个容器。它的封装是由struts2框架完成的。
通常情况下我们是从页面上获取数据。它实现了栈的特性(先进后出)。
1.1.2 ValueStack的内部结构
在 OnglValueStack 中包含了一个CompoundRoot的对象和context上下文对象,该对象继承了ArrayList,并且提供了只能操作集合第一个元素的方法,所以我们说它实现了栈的特性。同时,它里面定义了一个ContextMap的引用,也就是说,我们有值栈对象,也可以通过值栈来获取ContextMap
1.1.1.1 如何让Action中定义的成员出现在值栈中
在Action定义一个私有属性,并且提供公有get/set方法,那么该属性就会出现在值栈的Property Name中。显示的名称是根据get/set方法后面的内容决定的,与私有成员变量名称叫什么无关。
EL表达式原来的搜素顺序:
page Scope——>request Scope——>sessionScope——>application Scope
EL表达式改变后的搜索顺序:
page Scope—>request Scope—>valueStack—>contextMap—>sessionScope—>application Scope
在不修改源码的基础上,已有的方法进行动态增强。
在struts2中,拦截器它就是对我们的动作方法进行增强。(其实就是把重复性的代码提取出来,然后放到拦截器中,统一管理,统一调用)
1.1 自定义拦截器
在程序开发过程中,如果需要开发自己的拦截器类,就需要直接或间接的实现com.opensymphony.xwork2.interceptor.Interceptor接口。其定义的代码如下:
public interface Interceptor extends Serializable {
void init();
void destroy();
String intercept(ActionInvocation invocation) throws Exception;
}
该接口提供了三个方法,其具体介绍如下。
l void init():该方法在拦截器被创建后会立即被调用, 它在拦截器的生命周期内只被调用一次. 可以在该方法中对相关资源进行必要的初始化。
l void destroy():该方法与init方法相对应,在拦截器实例被销毁之前,将调用该方法来释放和拦截器相关的资源。它在拦截器的生命周期内,也只被调用一次。
l String intercept(ActionInvocation invocation) throws Exception:该方法是拦截器的核心方法,用来添加真正执行拦截工作的代码,实现具体的拦截操作。它返回一个字符串作为逻辑视图,系统根据返回的字符串跳转到对应的视图资源。每拦截一个动作请求, 该方法就会被调用一次。该方法的ActionInvocation参数包含了被拦截的Action的引用,可以通过该参数的invoke()方法,将控制权转给下一个拦截器或者转给Action的execute()方法。
如果需要自定义拦截器,只需要实现Interceptor接口的三个方法即可。然而在实际开发过程中,除了实现Interceptor接口可以自定义拦截器外,更常用的一种方式是继承抽象拦截器类AbstractIntercepter。该类实现了Interceptor接口,并且提供了init()方法和destroy()方法的空实现。使用时,可以直接继承该抽象类,而不用实现那些不必要的方法。拦截器类AbstractInterceptor中定义的方法如下所示:
public abstract class AbstractInterceptor implements Interceptor {
public void init() {}
public void destroy() {}
public abstract String intercept(ActionInvocation invocation)
throws Exception;
}
从上述代码中可以看出,AbstractInterceptor类已经实现了Interceptor接口的所有方法,一般情况下,只需继承AbstractInterceptor类,实现interceptor()方法就可以创建自定义拦截器。
只有当自定义的拦截器需要打开系统资源时,才需要覆盖AbstractInterceptor类的init()方法和destroy()方法。与实现Interceptor接口相比,继承AbstractInterceptor类的方法更为简单。
当然还有更简单的,AbstractInterceptor还有一个子类,MethodFilterInterceptor,该类中提供了两个属性,可以告知拦截器对哪些方法进行拦截或者对哪些方法排除。
1.1.1 自定义步骤
通过在拦截器类视图上我们可以得知,我们定义拦截器可以有三种办法:
第一种:定义一个类,实现Interceptor接口
第二种:定义一个类,继承AbstractInterceptor
第三种:定义一个类,继承MethodFilterInterceptor
看完两个类之后,我们有了结论。即:选择第三种方式,比第二种多了一个功能,就是告知拦截器哪些方法我们需要拦截,哪些方法我们不需要拦截。(注意:不要想着很傻的问题,在需要拦截和不需要拦截的属性中提供同一个方法)
1.1.1 配置拦截器
<!-- 一个公共包 -->
<package name="myDefault" extends="struts-default" abstract="true">
<!-- 声明拦截器 -->
<interceptors>
<interceptor name="checkLogin"
class="com.itheima.web.interceptors.CheckLoginInterceptor"/>
<!-- 定义一个拦截器栈 -->
<interceptor-stack name="myDefaultStack">
<interceptor-ref name="checkLogin">
<!-- 由于我们使用了继承MethodFilterInterceptor,
此时我们可以告知拦截器,排除掉登录方法 -->
<param name="excludeMethods">login</param>
</interceptor-ref>
<interceptor-ref name="defaultStack"></interceptor-ref>
</interceptor-stack>
</interceptors>
<!-- 修改默认拦截器栈,设置我们自定义的拦截器栈,
这样的话我们写的所有动作都有了检查登录功能。并且排除了登录方法-->
<default-interceptor-ref name="myDefaultStack"/>
<!-- 全局结果视图 -->
<global-results>
<result name="login">/login.jsp</result>
</global-results>
</package>
1.1.1 @InterceptorRef
出现的位置:
它可以出现在动作类上或者Action注解中。
作用:
用于配置要引用的拦截器或者拦截器栈
属性:
value:用于指定拦截器或者拦截器栈
示例:
出现在动作方法上:
/**
* 查询所有客户
* @return
*/
@Action(value="findAllCustomer",results={
@Result(name="findAllCustomer",location="/jsp/customer/list.jsp")
},interceptorRefs={
@InterceptorRef("myDefaultStack")
})
public String findAllCustomer(){
customers = customerService.findAllCustomer();
return "findAllCustomer";
}
出现在动作类上:
@InterceptorRef("myDefaultStack")
public class CustomerAction extends ActionSupport implements ModelDriven<Customer> {
private Customer customer = new Customer();
@Override
public Customer getModel() {
return customer;
}
}
OgnlValueStack贯穿整个 Action 的生命周期。
它是ContextMap中的一部分,里面的结构是一个List,是我们可以快速访问数据一个容器。它的封装是由struts2框架完成的。
通常情况下我们是从页面上获取数据。
- 请阐述一下ValueStack的内部结构
在 OnglValueStack 中包含了一个CompoundRoot的对象,该对象继承了ArrayList,并且提供了只能操作集合第一个元素的方法,所以我们说它实现了栈的特性。同时,它里面定义了一个ContextMap的引用,也就是说,我们有值栈对象,也可以通过值栈来获取ContextMap。
1. 继承AbstractInterceptor和继承MethodFilterInterceptor的区别
答:MethodFilterInterceptor多了一个功能,就是告知拦截器哪些方法我们需要拦截,哪些方法我们不需要拦截。推荐使用。
1. 【简答题】拦截器和过滤器的区别
答:
拦截器:
拦截器是struts2框架自己的,只有使用了struts2框架的工程才能用。
拦截器它是只有进入struts2核心内部之后,才会起作用,如果访问的是jsp, html,css,image或者js是不会进行拦截的。
过滤器:
过滤器是servlet规范中的一部分,任何java web工程都可以使用。
过滤器在url-pattern中配置了/*之后,可以对所有要访问的资源拦截。
1、①拦截器是基于java的反射机制的,而过滤器是基于函数回调
2、②过滤器依赖与servlet容器,而拦截器不依赖与servlet容器
3、③拦截器只能对action请求起作用,而过滤器则可以对几乎所有的请求起作用
4、④拦截器可以访问action上下文、值栈里的对象,而过滤器不能
5、⑤在action的生命周期中,拦截器可以多次被调用,而过滤器只能在容器初始化时被调用一次
拦 拦截器 :是在面向切面编程的就是在你的service或者一个方法,前调用一个方法,或者在方法后调用一个方法比如动态代理就是拦截器的简单实现,在你调用方法前打印出字符串(或者做其它业务逻辑的操作),也可以在你调用方法后打印出字符串,甚至在你抛出异常的时候做业务逻辑的操作。
配置拦截器 并不拦截login方法
<package name="myDefault" extends="struts-default">
<!-- 声明拦截器 -->
<interceptors>
<interceptor name="checkLogin"
class="cn.itcast.action.CheckUserInterceptor"/>
<!-- 定义一个拦截器栈 -->
<interceptor-stack name="myDefaultStack">
<interceptor-ref name="checkLogin">
<!-- 由于我们使用了继承MethodFilterInterceptor, 此时我们可以告知拦截器,排除掉登录方法 -->
<param name="excludeMethods">login</param>
</interceptor-ref>
<interceptor-ref name="defaultStack"></interceptor-ref>
</interceptor-stack>
</interceptors>
<!-- 修改默认拦截器栈,设置我们自定义的拦截器栈, 这样的话我们写的所有动作都有了检查登录功能。并且排除了登录方法 -->
<default-interceptor-ref name="myDefaultStack" />
1. 举例Struts2中通过拦截器实现了哪些功能?
答案:
参数绑定、参数校验、类型转换等
1. struts2有哪些优点?
答案:
1)在软件设计上Struts2的应用可以不依赖于Servlet API和struts API。 Struts2的这种设计属于无侵入式设计;
2)拦截器,实现如参数拦截注入等功能;
3)类型转换器,可以把特殊的请求参数转换成需要的类型;
4)多种表现层技术,如:JSP、freeMarker、Velocity等;
5)Struts2的输入校验可以对指定某个方法进行校验;
6)提供了全局范围、包范围和Action范围的国际化资源文件管理实现
支持全局结果视图
支持Ognl