和Struts2框架的初体验

Struts2的基础知识

问题一:什么是框架

框架是一个基本概念上的结构用于解决复杂的问题,应用在特定的领域内。使用框架可以使代码的复用大大提高,开发效率和质量也得到提高;他提供统一的标准,使后期维护的时间大大降低。归根到底框架就是用来提高开发效率的一种工具。

问题二:什么是Struts2框架

简单来说Struts2框架是基于MVC设计模式的Web应用框架,他本质相当于一个Servlet,所以他在MVC设计模式中相当于一个控制器,用来建立模型和视图的数据交互。

问题三:为什么使用Struts2而不使用Servlet作为MVC设计模式中的控制器(重点)

①Struts2本质相当于Servlet但他并不是一个Servlet,Servlet是使用Filter过滤器作为控制器,Struts2使用了一个个拦截器,组成一个强大的拦截器栈,相当于对Filter的改善,封装和简化

②Servlet使用Filetr过滤器拦截客户端的请求,代码冗余而且耦合性太高

③Struts2使用了一个个拦截器,使代码简单了而且类之间的耦合性大大降低了

问题四:Struts2框架的运行流程(重点)

①浏览器向服务器发送一个Http请求

②StrutsPrepareAndExecuteFilter(前端控制器)过滤器拦截

③StrutsPrepareAndExecuteFilter过滤器执行doFilter

④执行execute.executeAction(request, response, mapping)接受请求

⑤执行dispatcher.serviceAction(request, response, mapping)转发请求

⑥创建Action代理对象ActionProxy proxy = getContainer().getInstance(ActionProxyFactory.class).createActionProxy( namespace, name, method, extraContext, true, false)

⑦请求代理对象proxy执行proxy.execute()方法

⑧execute方法return invocation.invoke()

⑨invocation它是ActionInvocation一个对象,invoke()加载我们的配置文件,将配置文件中所有的interceptor得到进行遍历

⑩在struts-default.xml文件中定义了默认加载的拦截器栈 defaultStack,在每一个拦截器的interceptor方法内,又调用了DefaultActionInvocation的invoke方法,其实就是递归调用,根据Action中方法的执行结果来选择来跳转页面Resutl视图

问题五:如何Struts2运行环境

①导入Jar包括:struts-2.3.37\apps\struts2-blank\WEB-INF\lib和mysql-connector-java-5.1.5-bin.jar数据库包

②配置Struts2web.xml文件 struts-2.3.37\apps\struts2-blank\WEB-INF\web.xml

<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>
<url-pattern>/*</url-pattern>
</filter-mapping>

③配置struts.xml文件 struts-2.3.37\apps\struts2-blank\WEB-INF\src\java\struts.xml

问题六:如何配置struts.xml文件

①package配置strust.xml只存在一个package相当于一个struts的项目

name属性作用定义一个包的名称,它必须唯一用于其他包应用当前包

namespace属性作用主要是与action标签的name属性联合使用来确定一个 action的访问路径默认为/

extends属性作用:指定继承自哪个包一般值是struts-default,struts-default包是在struts-default.xml文件中声明

②action配置对应相应的以action结尾的类,一个package可以有多个action,一个struts请求就是一个action

name属性作用主要是与package的namespace联合使用来确定action的访问路径

class属性作用用于指示当前的action类进行类反射对action类的属性进行赋值

method属性作用用于指示当前的action类中执行哪个方法

③result配置用于显示视图的结果

name属性作用与action类的method方法的返回值进行匹配,来确定跳转路径

type属性作用用于指定跳转方式

问题七:如何区别请求类和请求

①Action请求类相当于javaWeb阶段下的Servlet类,做着调用service层的关系,实现页面的跳转完成业务逻辑操

②一个http请求就是一个struts2中的请求

问题八:Struts2中如何访问web资源

①和servletAPI解耦的方式

获取ActionContext对象    ActionContext.getContext()

实现XxxAware接口(依赖注入方式)

②和servletAPI耦合的方式

获取ServletActionContext对象

实现ServletXxxAware接口

问题九:如何修改Struts2默认配置

①找到需配置的默认属性struts-core-2.3.37.jar/org.apache.struts2/static/default.properties

②在struts.xml中配置<constant name="" value=""></constant>

name属性作用定义默认属性

value属性作用修改默认属性的值

问题十:如何在struts2中使用通配符

简化的 action 访问方式可以使用*和?和{1}{2}通配符来访问使用*和?和{1}{2}来简化操作方案它对名称规范必须进行一个统一

问题十一:如何理解值栈(重点)

①值栈相当于一个数据中转站,在其中保存当前Action对象和其他相关对象(域对象)

②值栈包括:ContextMap和ObjectStack

③ValueStack存储数据

手动向valueStack存储数据

ActionContext.getContext()

ValueStack  vs =  ActionContext.getServletContext()getValueStack();

vs.push(String str);

vs.set(Object obj  String str);

自动向valueStack中存储数据

每次请求访问action这个对象会存储到valueStack中

问题十二:如何理解对象图形导航语言(重点)

①OGNL表达式可以存取对象的任意属性,调用对象的方法,遍历整个对象的结构图,实现字段类型转化

②OGNL表达式支持静态成员访问(<s:property value="@[email protected]">),支持赋值操作与表达串联,访问OGNL上下文,访问ActionContext,操作集合对象

③OGNL表达式的作用是从valueStack中获取数据

④从值栈中获取数据

1.访问ContextMap数据:在域对象面前加上#

2.访问ObjectStack数据:利用s:property标签和ognl表达式读取或者用EL表达式

问题十三:Struts2的标签有哪些,如何使用标签

①Struts2标签有:通用标签;表单标签;

②通用标签:

1.<s:property> 用于访问值栈属性值

访问域对象属性值<s:property value=#request.user.userName></property>

访问对象栈属性值<s:property value="[x]userName"></s:property>

访问静态字段和方法<s:property value="@java.lang.Math @PI"> <s:property value="@java.lang.Math @cos(0)">

访问数组对象属性<s:property value="#attr.names.length"></s:property>

2.<s:url><s:param>

创建一个URL字符串,构建一个action请求地址

<s:url  value="/testurl" var="url" includeparam="post/get/all">

<s:param name="productId" value="productId"></s:param>

</s:url>

注意:对于value值会自动进行ognl解析,若不希望进行ognl解析则使用单引号引起来

3.<s:set>

向page,request,session,application域对象加个属性

<s:set name="productName" value="productName" scope="request"></s:set>

注意:对于value值会自动进行ognl解析,若不希望进行ognl解析则使用单引号引起来

4.<s:push>

把一个对象在标签开始后压入值栈,标签结束后弹出值栈

<s:push value="#request.person"></s:push>

注意:对于value值会自动进行ognl解析,若不希望进行ognl解析则使用单引号引起来

5.<s:if><s:else if><s:else>

6.<s:iterator>

遍历集合并把这个可遍历的集合里的每个属性一次压入值栈和弹出值栈

<s:iterator value="#request.persons">

<s:property value="name"></s:property>

<s:property value="id"></s:property>

</s:iterator>

7.<s:sort>

用来对一个可遍历对象里的属性进行排序

8.<s:date>

对Date对象进行指定的排版

<s:date name="request.date" format="yyyy-MM-dd hh:mm:ss" var="date"></s:date>

${date}

③表单标签(重点)

1.<s:checkbox>

创建一个配对不可见的字段.解决如果该复选框未被选中,在请求就不会增加一个请求参数

<s:checkbox name="agree" label="是否同意入职"></s:checkbox>

2.<s:checkboxlist>

<s:checkboxlist name="hobby" label="兴趣爱好" list="#{‘篮球‘:‘篮球‘,‘足球‘:‘足球‘,‘排球‘:‘排球‘,‘羽毛球‘:‘羽毛球‘}"></s:checkboxlist>

3.<s:radio>

<s:radio name="sex" label="性別" list="#{‘男‘:‘男‘,‘女‘:‘女‘}"></s:radio>

4.<s:select><s:optgroup>

<s:select name="age" label="年龄" list="#{0:0,1:1,2:2,3:3,4:4,5:5,6:6,7:7,8:8,9:9,10:10,11:11,12:12,13:13,14:14,15:15,16:16,17:17}" headerKey="" headerValue="请选择你的年龄">
<s:optgroup label="18-24" list="#{18:18,19:19,20:20,21:21,22:22,23:23,24:24}"></s:optgroup>
<s:optgroup label="25-31" list="#{25:25,26:26,27:27,28:28,29:29,30:30,31:31}"></s:optgroup>
</s:select>

问题十四:如何解决类型转换失败的消息提示(重点)

①方法一:默认拦截器CoversionError <result name="input"></result>

对于theme主题:<s:fileError fieldName="">

②方法二:配置文件覆盖错误消息

1.在对于action包下,新建action名.properties

2.内容:invalid.fieldvalue.变量名=错误提示信息

对于theme主题:在src下创建tempplate.simple包新建fielderror.ftl文件把原生复制新建的fielderror.ftl文件中,让后剔除ul,li,span部分

问题十五:如何解决类型转换问题(重点)

解决方法:自定义类型转换器

①配置类型转换器的类实现TypeCoverter接口或者继承DefaultTypeConverter实现类

②配置类型转换器

1.全局配置在src根下建立xwork-conversion.properties文件配置好实体类与转换类的二者完全限定名对应关系

内容:要转换的类型包路径=转换器包路径  com.thxy.model.Student=com.thxy.Converter.DateTypeConverter

2.局部配置在action的包里面建立Action类名-conversion.properties文件(StudentAction-conversion.properties)

内容:模型属性名=转换器包路径 date=com.thxy.Converter.DateTypeConverter
问题十六:如何理解ModelDriven接口(难点)

①为什么使用ModelDriven接口

原因1:直接在Action中定义所有需要的属性,然后在JSP中直接用属性名称来提交数据;如果实体类的属性非常多那么Action中也要定义相同的属性

原因2:User对象定义到UserAction中,然后在JSP中通过user属性来给user赋值;JSP页面上表单域中的命名变得太长

原因3:ModelDriven机制让UserAction实现一个ModelDriven接口同时实现接口中的方法getModel();Action和JSP写起来都比较简单

②ModelDriven接口背后机制是什么?

1.ModelDriven背后的机制就是ValueStack

2.ModelDrivenInterceptor是缺省的拦截器链的一部分,当一个请求经过ModelDrivenInterceptor的时候,在这个拦截器中,会判断当前要调用的Action对象是否实现了ModelDriven接口,如果实现了这个接口,则调用getModel()方法,并把返回值压入ValueStack (可以说ModelDriven接口是手动将对象压入值栈)

3.ModelDrivenInterceptor源代码

 1 public class ModelDrivenInterceptor extends AbstractInterceptor {
 2
 3     protected boolean refreshModelBeforeResult = false;
 4
 5     public void setRefreshModelBeforeResult(boolean val) {
 6         this.refreshModelBeforeResult = val;
 7     }
 8
 9     @Override
10     public String intercept(ActionInvocation invocation) throws Exception {
11         Object action = invocation.getAction();
12
13         if (action instanceof ModelDriven) {
14             ModelDriven modelDriven = (ModelDriven) action;
15             ValueStack stack = invocation.getStack();
16             Object model = modelDriven.getModel();
17             if (model !=  null) {
18                 stack.push(model);
19             }
20             if (refreshModelBeforeResult) {
21                 invocation.addPreResultListener(new RefreshModelBeforeResult(modelDriven, model));
22             }
23         }
24         return invocation.invoke();
25     }
由intercept方法可以看出ModelDriven接口将返回对象值压入值栈

问题十六:如何理解Preparable接口(难点)

①为什么使用paramsPrepareParamsStack拦截器栈

原因1:每次请求,访问action调用ModelDrivern的getModel()方法出现了值栈栈顶是getModel()方法返回的对象值,之后手动的把数据库的对象也放入值栈值,这时值栈的栈顶对象就是数据库中返回的对象,值栈的第一个对象和第二个对象都是同一特这的对象,造成了内存空间的浪费

原因2:paramsPrepareParamsStack拦截器栈可以实现params拦截器->modelDriven拦截器->params拦截器判断action请求的参数来执行逻辑业务

②为什么使用Preparable接口

原因:在加载数据库时进行查询和删除操作时会根据参数id加载多一次数据库,效率和空间会造成浪费,使用Preparable接口可以根据需求将对象压入值栈

③Preparable接口背后机制

1.首先了解paramsPrepareParamsStack拦截器栈的运行流程

exception->params->prepare->modelDriven->params->conversionError

用文字表述为:

params拦截器首先给action中的相关参数赋值

prepare拦截器执行prepare方法,prepare方法中会根据参数,如id,去调用业务逻辑,设置model对象

modelDriven拦截器将model对象压入value stack,这里的model对象就是在prepare中创建的

params拦截器再将参数赋值给model对象

2.PrepareInterceptor运行机制

PrepareInterceptor源代码

 1 public class PrepareInterceptor extends MethodFilterInterceptor {
 2
 3     private static final long serialVersionUID = -5216969014510719786L;
 4
 5     private final static String PREPARE_PREFIX = "prepare";
 6     private final static String ALT_PREPARE_PREFIX = "prepareDo";
 7
 8     private boolean alwaysInvokePrepare = true;
 9     private boolean firstCallPrepareDo = false;
10
11     /**
12      * Sets if the <code>preapare</code> method should always be executed.
13      * <p/>
14      * Default is <tt>true</tt>.
15      *
16      * @param alwaysInvokePrepare if <code>prepare</code> should always be executed or not.
17      */
18     public void setAlwaysInvokePrepare(String alwaysInvokePrepare) {
19         this.alwaysInvokePrepare = Boolean.parseBoolean(alwaysInvokePrepare);
20     }
21
22     /**
23      * Sets if the <code>prepareDoXXX</code> method should be called first
24      * <p/>
25      * Default is <tt>false</tt> for backward compatibility
26      *
27      * @param firstCallPrepareDo if <code>prepareDoXXX</code> should be called first
28      */
29     public void setFirstCallPrepareDo(String firstCallPrepareDo) {
30         this.firstCallPrepareDo = Boolean.parseBoolean(firstCallPrepareDo);
31     }
32
33     @Override
34     public String doIntercept(ActionInvocation invocation) throws Exception {
35         Object action = invocation.getAction();
36
37         if (action instanceof Preparable) {
38             try {
39                 String[] prefixes;
40                 if (firstCallPrepareDo) {
41                     prefixes = new String[] {ALT_PREPARE_PREFIX, PREPARE_PREFIX};
42                 } else {
43                     prefixes = new String[] {PREPARE_PREFIX, ALT_PREPARE_PREFIX};
44                 }
45                 PrefixMethodInvocationUtil.invokePrefixMethod(invocation, prefixes);
46             }
47             catch (InvocationTargetException e) {
48                 /*
49                  * The invoked method threw an exception and reflection wrapped it
50                  * in an InvocationTargetException.
51                  * If possible re-throw the original exception so that normal
52                  * exception handling will take place.
53                  */
54                 Throwable cause = e.getCause();
55                 if (cause instanceof Exception) {
56                     throw (Exception) cause;
57                 } else if(cause instanceof Error) {
58                     throw (Error) cause;
59                 } else {
60                     /*
61                      * The cause is not an Exception or Error (must be Throwable) so
62                      * just re-throw the wrapped exception.
63                      */
64                     throw e;
65                 }
66             }
67
68             if (alwaysInvokePrepare) {
69                 ((Preparable) action).prepare();
70             }
71         }
72
73         return invocation.invoke();
74     }
75
76 }

由doIntercept方法可以看出

1.获取Action实例 Object action = invocation.getAction();

2.判断Action是否实现了Preparable接口  action instanceof Preparable

3.根据当前拦截器的firstCallPrepareDo属性确定Prefixes

4.根据alwaysInvokePrepare决定是否调用Action的Prepare方法,若Action请求类实现了PreParebale接口,则Struts2将尝试实现Prepare[ActionMethodName]方法,若Prepare[ActionMethodName]方法不存在,则尝试执行

PrePareDo[ActionMethodName]方法,若不存在则就不执行

注意:alwaysInvokePrepare属性是False,则Struts2将不会调用实现Prepareable接口Action类的Prepare方法,可以为每个actionMethod准备Prepare[ActionMethodName],而抛弃原来Prepare方法

备注:本人是在校的大二学生自学框架,功力尚浅不能很好分析源码有分析到不到位的地方请指正。我相信通过你们的指正之后我们都会变得更好。

原文地址:https://www.cnblogs.com/KYAOYYW/p/10355712.html

时间: 2024-10-06 08:58:25

和Struts2框架的初体验的相关文章

基于Scala的Actor之上的分布式并发消息驱动框架Akka初体验

学习了基于Scala的Actor之上的分布式并发消息驱动框架Akka初体验,应用actor模型,位置透明,做到高并发.可伸缩.容错.单机也可以用,水平扩展.垂直扩展.容错都有很好的表现,spark中的例子如下: private def initializeEventProcessActor(){ implicat val timeout=Timeout( 30 seconds) val initEventActorReply= dagSchedulerActorSupervisor ? Prop

Net Core平台灵活简单的日志记录框架NLog初体验

Net Core平台灵活简单的日志记录框架NLog初体验 前几天分享的"[Net Core集成Exceptionless分布式日志功能以及全局异常过滤][https://www.cnblogs.com/yilezhu/p/9339017.html]" 有人说比较重量,生产环境部署也比较麻烦.因此就有了今天的这篇文章.如果你的项目(网站或者中小型项目)不是很大,日志量也不多的话可以考虑NLog+Mysql的组合.因为NLog具有高性能,易于使用,易于扩展和灵活配置的特点能够让你快速集成日

《java.util.concurrent 包源码阅读》22 Fork/Join框架的初体验

JDK7引入了Fork/Join框架,所谓Fork/Join框架,个人解释:Fork分解任务成独立的子任务,用多线程去执行这些子任务,Join合并子任务的结果.这样就能使用多线程的方式来执行一个任务. JDK7引入的Fork/Join有三个核心类: ForkJoinPool,执行任务的线程池 ForkJoinWorkerThread,执行任务的工作线程 ForkJoinTask,一个用于ForkJoinPool的任务抽象类. 因为ForkJoinTask比较复杂,抽象方法比较多,日常使用时一般不

Python爬虫框架--pyspider初体验

之前接触scrapy本来是想也许scrapy能够让我的爬虫更快,但是也许是我没有掌握scrapy的要领,所以爬虫运行起来并没有我想象的那么快,看这篇文章就是之前使用scrapy的写得爬虫.然后昨天我又看到了pyspider,说实话本来只是想看看,但是没想到一看就让我喜欢上了pyspider. 先给大家看一下pyspider的后台截图:  pyspider是国人写的一款开源爬虫框架,个人觉得这个框架用起来很方便,至于如何方便可以继续看下去. 作者博客:http://blog.binux.me/ 这

前端工业化框架Grund初体验

提示:Grunt基于Node.js,安装之前要先安装Node.js 第一步:安装 grunt-cli npm install -g grunt-cli 第二步:用以下命令创建一个基本的package.json文件 npm init 第三步: npm install 第四步:安装 Grunt 插件 npm install grunt --save-dev 同样方法按需安装常用Grunt插件如: npm install grunt-contrib-concat --save-dev grunt-co

Scala 深入浅出实战经典 第90讲:基于Scala的Actor之上的分布式并发消息驱动框架Akka初体验

akka提供了可伸缩的实时事务处理功能. akka基于actor,并提供了位置透明. 1GB的heap可以有2500000个actor. 水平扩展,垂直扩展,容错3个方面的解决方式. 树形结构的actor,每个actor都有状态和行为. DT大数据梦工厂微信公众账号:DT_Spark. DT大数据梦工厂的微信公众号是DT_Spark,每天都会有大数据实战视频发布,请您持续学习. 王家林DT大数据梦工厂scala的所有视频.PPT和代码在百度云盘的链接:http://pan.baidu.com/s

C#代码生成工具:文本模板初体验 使用T4批量修改实体框架(Entity Framework)的类名

转自:http://www.cnblogs.com/huangcong/archive/2011/07/20/1931107.html 在之前的文本模板(T4)初体验中我们已经知道了T4的用处,下面就看看如何用它来实现批量修改实体框架(Entity Framework)中的类名.我们都知道ADO.NET 实体数据模型中有一种方式是以数据库模型来生成数据模型的,这是个很简便的实体数据模型生成的方式,但是因为微软提供的自定义接口不足,我们无法实现对生成的数据模型实体类批量进行修改(至少我上网找了很久

软件测试学习笔记week 3 --- 测试框架初体验

测试框架初体验 在这周的软件测试课上,第一次了解了软件测试框架的概念.软件测试框架包含的范围非常广,从自动化测试框架到单元测试框架以及性能测试框架.在上个寒假中,在学习Coursera的在线课程时发现普林斯顿的单元测试做得非常强大,从程序正确性到Time consuming甚至Memory consuming,几乎能发现程序中的每一处错误或者缺陷.因此,在上完了这周的课程后,我查阅了一些资料,做了这篇随笔记录了解到的单元测试的知识. 一.什么是测试框架 要认识测试框架,首先要对所谓框架有概念.框

【Spark深入学习 -15】Spark Streaming前奏-Kafka初体验

----本节内容------- 1.Kafka基础概念 1.1 出世背景 1.2 基本原理 1.2.1.前置知识 1.2.2.架构和原理 1.2.3.基本概念 1.2.4.kafka特点 2.Kafka初体验 2.1 环境准备 2.2 Kafka小试牛刀 2.2.1单个broker初体验 2.2.2 多个broker初体验 2.3 Kafka分布式集群构建 2.3.1 Kafka分布式集群构建 2.3.2 Kafka主题创建 2.3.3 生产者生产数据 2.3.4消费者消费数据 2.3.5消息的