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