struts总结完整整理版

来源:互联网 发布:国家的崛起人工智能 编辑:程序博客网 时间:2024/04/30 00:14
1,说明整个Struts2的流程,越详细越好(10分)

初始化:
struts2是由过滤器驱动的:核心过滤器StrutsPrepareAndExecuteFilter在web应用启动的时候被服务器创建,并驻留服务器内存;
并调用init方法进行初始化;
过程包括:
 核心过滤器的init方法主要做的事:
1 初始化配置文件;
  配置文件默认解析顺序为
   default.properties(struts默认的常量信息)--->struts-default.xml--->struts-plugin.xml--->struts.xml;
       如果其中有相同信息则后者覆盖前者,因此我们要覆盖struts默认的常量最好是放在struts.xml中的constant元素中;
       当然一些插件为了覆盖struts的默认常量也会在对应的struts-plugin.xml中定义常量;并最终汇总到struts.xml中;
2 初始化核心分发器:Dispatcher
   Dispatcher dispatcher =init.initDispatcher(config);进行初始化,核心分发器Dispatcher在被创建的过程中
   需要接收两个参数;newDispatcher(filterConfig.getServletContext(),params);和ServletContext;
  dispatcher在初始化的时候做的事情:
    a静态注入:将struts框架工作所需的对象创建并放入到容器中,这个容器的实现类是ContainerImpl
   通过如下方法进行注值;
   org.apache.struts2.config.BeanSelectionProvider.register(ContainerBuilder,LocatableProperties);
   需要注入的是,这些值的注入都有一个特点:比如;
   <beantype="com.opensymphony.xwork2.ActionProxyFactory"name="xwork"
   class="com.opensymphony.xwork2.DefaultActionProxyFactory"/>
   <beantype="com.opensymphony.xwork2.ActionProxyFactory"name="struts"
   class="org.apache.struts2.impl.StrutsActionProxyFactory"/>
   都有一个公共的接口:com.opensymphony.xwork2.ActionProxyFactory,并针对该接口有多个实现;struts内部肯定是通过接口
   引用调用实现类的方法,这又是面向接口编程,大大提高了程序的扩展性;如果你要改写这些配置,
   只要在你的struts.xml配置文件中进行配置,但是前提有一个你必须实现它的接口,并将type定义为该接口,将你的实现类
   定义为class,并取和上面相同的名称,达到覆盖的效果;
3 初始化PrepareOperations:
   他的作用
    a:准备struts的执行环境,具体来说就是创建ActionContext;
    b:对request进行包装;
    c:action路径的寻址和过滤;
4 初始化ExecuteOperations
   他的作用:
   a:执行静态资源请求;
   b:执行action;
    
struts2的执行流程;
1加载并解析配置文件,解析顺序为struts-default.xml,struts-plugin.xml,struts.xml;
 如果这三个文件中有相同内容,则后者覆盖前者;配置文件加载后创建struts基本元素:即<beantype=*,class=*>
 结果集合拦截器(只有被引用到的拦截器才会被创建,即interceptor-ref的)
 需要注意的是<bean>节点对应的元素,type和class的关系,type:
 是父类或者接口,而class是对应的实现类,我们说过配置文件是有加载顺序的,我们要覆盖struts的默认配置
 可以在struts.xml,但是覆盖有一个前提,你的实现类,type必须和默认中的type相同;
 实现类自己定义,因为在struts调用你的实现类是通过type对应的引用来调用的,实际上的实现类是由你提供的'
 这是完全的面向接口(父类)编程,符合开闭原则;比如说
   <beantype="com.opensymphony.xwork2.ObjectFactory" name="struts"
  class="org.apache.struts2.impl.StrutsObjectFactory"/>这是struts中的默认配置,你要修改他的创建模式;
  就必须和他继承相同的父类;比如spring集成struts的插件中的struts-plugin.xml配置文件的配置;
   <bean type="com.opensymphony.xwork2.ObjectFactory"name="spring"
   class="org.apache.struts2.spring.StrutsSpringObjectFactory"/>
   在struts中一定有这么一行代码:
   ObjectFactory objectFactory;并提供相应的set,get方法
   然后在使用到的地方直接就是
   objectFactory.buildBean();//至于这个buildBean怎么执行不管,但是你的实现类一定要有这个方法;
   否则就会导致整个框架的混乱
   开闭原则:
   实现类你爱怎么实现怎么实现,这是开的一方面;
   但是你必须得保证,我父类有的方法一定要有实现;
   或者说我的buildBean一定要能执行;这是闭的一方面;
   同时配置文件中的内容允许覆盖也正是体现了这点,如果你都不允许他人覆盖你的配置,把什么都写死了,三大框架整合就是
   个笑话.
   一句话:开闭原则就是对于扩展性我完全开放,但是对于我struts整个的执行流程,谁都不能动,这是我的根基;
  拦截器的创建时机:
   struts2拦截器的创建机制:只有使用到的拦截器才会被创建,这个很好理解,是为了内存消耗的考虑,你没事叫人过来干嘛,
   叫过来,又叫人回去,啥事也不干,你不是有病吗?
   将解析到的一个个interceptor-ref节点封装成InterceptorMapping,多个InterceptorMapping组成一个集合
   List<InterceptorMapping>interceptors;多个集合构成一个
   InterceptorStackConfig拦截器栈映射对象;
   然后交给ObjectFactory统一创建,具体的是由StrutsObjectFactory进行创建;
    protectedvoid loadInterceptorStacks(Element element, PackageConfig.Buildercontext) throws ConfigurationException {
      //这句代码的意思是,看看配置文件中哪些拦截器栈被引用了,引用了,就进一步的追踪拦截器栈中的拦截器
      NodeList interceptorStackList =element.getElementsByTagName_r("interceptor-stack");
      for (int i = 0; i <interceptorStackList.getLength(); i++) {
         Element interceptorStackElement = (Element)interceptorStackList.item(i);
         InterceptorStackConfig config =loadInterceptorStack(interceptorStackElement, context);
         context.addInterceptorStackConfig(config);
      }
     }
   init的方法执行完成后,所有的对象都会跑到ContainerImpl这个容器中,这是struts2的核心容器;即IOC容器;
2当请求到来的时候,首先经过核心过滤器的doFilter方法;这个方法实现了以下功能;
  a:初始化action执行环境,ActionContext;
    对请求url进行解析,并判断你的url是否是需要经过struts的核心;
   如果需要做进一步的处理,如果不需要执行chain.doFilter放行;
   当然这一步是粗粒度的进行判断,接下来对请求进行包装,解析出对应的action;
   并在ActionMapping中找看是否存在对应的action映射;
   如果不存在,继续判断是否是要使用到struts的静态资源,即路径中有
   "/struts/":对应于org.apache.struts2.template中的静态资源;
   "/static/"对应于org.apache.struts2下的static包中的静态资源;
    如果有调用静态资源处理器进行处理,如果没有直接放行;
    如果actionMapping中有对应的action;
    执行execute.executeAction(request, response,mapping);方法,进入struts的核心
    调用核心分发器的serviceAction方法创建值栈对象ValueStack和contextMap;
    并创建出ActionProxy,由ActionProxy创建出ActionInvocation,即核心控制单元,他采用的是command设计模式进行设计;;
    由ActionInvocation控制拦截器和action,
    和result的执行;
    在执行proxy.invoke方法就回到ActionInvocation,由ActionInvocation
    负责拦截器的执行,拦截器执行完成后,执行目标action的目标方法,
    并返回一个路由串,即结果视图,然后又回到ActionInvocation,继续执行Result中的代码,
    result根据配置文件中配置的结果视图类型和页面跳转地址,
    这时候通常还得调用jsp等模板对结果进行处理,---是否使用到标签用OGNL解析器进行处理,
    形成最终的数据;
    然后重新回到ActionInvocation,继续调用
    拦截器的清理工作,拦截器都执行完成后,响应消息写入到response缓冲区,由服务器取出,
    以http响应格式输出到浏览器供浏览器解析,
    核心过滤器的doFilter方法执行结束,
    本次请求结束;ActionContext中的数据被清空;

    我们发现核心过滤器和其他过滤器共同构成一个职责链设计模式,整个职责链由tomcat服务器进行控制;
    每个过滤器完成自己的功能:1要么直接返回数据到页面2要么执行完自己的功能后通知下一个过滤器执行;
    而在核心过滤器中又有一个action核心控制单元,ActionInvocation,他控制着拦截器栈,action,及结果视图Result;
    的执行.
   对框架的理解;
   为什么要有框架,框架帮我们解决了什么问题,好比如说我们为什么要学struts2,不用struts2,我们用servlet,
   也可以开发,struts2号称是严格遵循MVC设计模式的web层框架,他帮我们解决了什么问题?这个我们得回过头来看
   MVC设计模式;一个Controller要完成如下工作:
    1客户端提交的数据的封装及校验;
    2调用service层处理业务逻辑;
    3根据业务逻辑的处理情况,跳转到相应的页面;
    4异常的处理;
   在servlet中我们队数据的封装是通过对request中获取请求参数,用beanutils将数据注入到formbean中,并对formbean进行校验;
   然后将formbean中的数据copy到我们的数据bean中,即这里说的Model;
   而关于页面的跳转我们用的是request,或者response直接跳转;
   异常处理我们是手动的try catch;
   这样做下来我们发现,我们在做很多很多重复性的工作,每次请求都得封装数据并校验,处理业务逻辑
   然后跳转页面周而复始,真正不同的是我们的业务逻辑处理代码,而这正是我们真正关心的;
   那么我们可以回过头来看struts帮我们解决的问题:
    1数据的封装以及检验,错误消息提示及数据回显:DefaultActionValidatorManager
    2异常处理:ExceptionMappingInterceptor
    3页面跳转:Result
    4文件上传:
    5国际化:
    6类型转换;
   而且实现形式还多样化,可以手动,可以用配置文件,可以用注解;
   这些功能的实现都是通过拦截器进行配置的,可见struts2的核心是拦截器;
   我们只要写配置文件就可以完成和之前手动写N行
   代码的效果,而且高手写的代码从效率和安全性来将都超过我们,可以称得上是最佳实践;
   大大的提高了开发效率;
   使得程序员有更多的时间去处理业务逻辑;
   说道这里框架的本质也就说完了
   总结:框架是一组程序的集合,包含了一系列的最佳实践,作用是解决某个领域的问题;本质是对jdk的扩展;(来源于网络)
   我的理解:框架就是来帮我们干活的,简化了我们的开发;

2,说明你对Struts-plugin.xml 文件的理解(5分)
  1是struts插件的配置文件,充分体现了struts的扩展性,是开闭原则(OCP,OpenClose Principle,是指一个程序应该是可扩展
 而不可修改的,即对扩展开发,对修改封闭的原则)的体现;
 开的一方面体现在:你可以通过插件及插件配置文件覆盖struts默认的配置,闭的原则是你不能改变struts的执行流程,这是他的核心
 谁都不能动,其实我觉得还体现在面向接口编程,也就是你覆盖可以,但是你的实现类必须实现我给你提供的指定接口;
 因为框架内部是通过接口的引用调用实现类的方法的;必须实现指定的接口---闭,
 实现类开放:你爱怎么实现怎么实现---开;
  2 分离关注;separateaware;好比如说集成spring,你只要把对spring支持的插件包和具体的实现类加到classpath;
 在struts.xml文件中根本就没有spring的影子;因为str-plu.xml中已经配置好了,当然这依赖于struts配置文件的加载顺序;
 也就是将插件和struts核心分开处理;使得逻辑更加的清晰
 如果没有这个插件你还得在struts.xml中配置或者导入;东西一多,嘿嘿,你懂的!!

3,说明struts2的属性驱动和模型驱动的实现原理,并把关键代码写出来(5分)
 模型驱动是将你的action实现ModelDriven接口,并覆盖对应的getModel方法,该方法返回的是你定义的模型;
 如果你的模型类已经创建则经过ModelDrivenInterceptor的时候
  if (action instanceof ModelDriven) {
           ModelDriven modelDriven = (ModelDriven) action;
           ValueStack stack = invocation.getStack();
           Object model =modelDriven.getModel();//获得模型类即User;
           if (model !=  null) {
              stack.push(model);//将model压入栈顶;
           }
           if (refreshModelBeforeResult) {
               invocation.addPreResultListener(newRefreshModelBeforeResult(modelDriven, model));
           }
       }
 将你的模型类压入栈顶,然后经过params拦截器,他有一个valuestack.setValue();//将参数压入栈顶对象中;
  从而实现对模型的注值;
 如果模型类一开始没有值将会调用ObjectFactory.buildBean方法创建,然后继续这样的操作;这样效率更低;
 因此如果使用模型驱动,最好对其进行显示初始化;这应该做为一条最佳实践;

 属性驱动用的是利用ParameterInterceptor拦截器
 这个方法会用反射把Action中有set方法的属性创建出对象;
 ReflectionContextState.setCreatingNullObjects(contextMap,true);

  接着会调用setParameters(action, stack,parameters)方法将参数中的值注入到action中的属性中
 最终调用的是ValueStack.setValue();方法---真正的为属性注值
 他是利用反射机制,调用对象属性的set方法,即method.invoke;

 其实两种方式都得使用到parameterinterceptor拦截器,为action的属性注值;
 由于action会默认的压到栈顶属性随之也到了栈顶,模型驱动也就是多了这么个压栈的方法,因此作用不是很大;
 --依据:将params拦截器注释掉,action中的exec方法引用user的属性报空指针异常
         s:debug标签中的内容;
4,配置一个自己的拦截器,实现的功能:如果session中的user为Null,则不会跳转到相应的action,
如果不为null,则跳转到相应的action(3分)
       Object user =ServletActionContext.getRequest().getSession().getAttribute("user");
   if(user!=null&&user.getClass()==User.class){
      //加上类型判断更加安全;因为只有user类对象才能做为标记;
      return invocation.invoke();
    }
    returnnull;
   拦截器配置;省略interceptors中的配置;
       <action name="login"class="com.itheima.web.struts.actions.LoginAction"
         method="login">
         <interceptor-refname="loginInterceptor"/>
         <interceptor-refname="defaultStack"/>
         <result>/index.jsp</result>
   </action>
   注意:如果你的拦截器依赖于默认的拦截器请将你的拦截器定义在默认拦截器栈后,反之亦然,
   在这里由于我的是判断是否登录,不依赖于默认,因此配置在之前,提高效率,即你都没有登录,其他的操作都是枉然;
   这也应该成为一条最佳实践:看自己的拦截器是否依赖默认拦截器栈,合理规划配置;
5,说明s:iterator迭代器的用法(10分)
   1迭代List集合;每次迭代的都是list中的一个元素:并用一个变量存储,压入栈顶;
    提问:为什么不能是set集合呢?这是因为iterator是需要角标滴;
    用法:
    <s:iterator value="{'a','b','c'}"var="xx">
   <s:propertyvalue="xx"/>
    </s:iterator>
    这里会报错;说是ClassCastExc; Character cant't cast toString
    而 <s:iteratorvalue="{'a','b','c'}">
     <s:property/>--默认取栈顶元素
       </s:iterator>确没有丝毫问题;我认为这是他的一个bug;至于原因,找了好久没有找到根源;
   解决方案:用单引号套双引号:
    即:
    <s:iterator value='{"a","b","c"}'var="xx">
      <s:property value="xx"/>
        </s:iterator>
   2 迭代Map集合;
    <s:iteratorvalue="#{'name':'xxx','age':'111'}">
      <s:property value="key"/>
      <s:property value="value"/>
    </s:iterator>
    注意#号不能丢,他是构建一个map集合放入OGNL上下文中,迭代时取出每一个map.entry,放到栈顶;
    取的时候用key,value取对应的值,否则默认取得的一个map.entry;
    因为他底层会去调用entry.getKey,getValue方法

    还可以取得ActionContext中的六个map中的数据;取的时候注意用#号;

    应用场景:隔行间色效果实现,用statu,类似于jstl的foreach标签;


6,写一个自定义的结果集,实现转发(3分)
思路:写一个DispatcherResult,继承自ServletDispatcherResult
    只要我的结果集直接或者间接继承了Result接口即可,struts的ActionInvocation
    会自动调用我的结果集并根据具体的路由串,跳转到指定页面;
       public void execute(ActionInvocation invocation) throws Exception{
         HttpServletRequest request =ServletActionContext.getRequest();
         HttpServletResponse response =ServletActionContext.getResponse();
         //location即我们的跳转页面,就是下面配置的param.jsp;
         request.getRequestDispatcher(super.getLocation()).forward(request,
               response);
      }

   <result-types>
      <result-type name="mydispatcher"class="com.itheima.results.DispatcherResult">
      </result-type>
   </result-types>
   <global-results>
      <resulttype="mydispatcher">
         /param.jsp
      </result>
   </global-results>
   <action name="login"class="com.itheima.web.struts.actions.LoginAction">
   </action>
 PS:大部分的view层插件都是以结果集的形式和struts2进行整合的;比如,json,ITEXTS,JFreeChart等;
 其实我们完全可以抛开struts不管,只要结果视图返回一个NONE;
 然后用传统的request,response,照样可以做开发;尤其是文件下载的时候,由于要配置多个什么项的;
 因为之前用习惯了response输出,然后就是手动用
 struts输出验证码也可以用这种方式实现----
7,说明Struts2怎样做到定制action(15分):
 所谓的定制action其实就是你自己定义一个类继承自ObjectFactory,因为这个类是struts中创建对象的核心工厂类;
  并覆盖对应是buildAction方法即可;
 因此所谓的SSH整合,其实就是将struts中action的创建策略由默认的org.apache.struts2.impl.StrutsObjectFactory改为Spring提供的
 org.apache.struts2.spring.StrutsSpringObjectFactory类,但是不管怎么样
 他们都得实现或者继承自com.opensymphony.xwork2.ObjectFactory,因为在struts内部,肯定是通过父类或者接口的引用调用子类的方法;
 不管你怎么实现,怎么覆盖,你必须得有对应的方法,即可以通过父类的引用.buildBean();进行调用;
 
 如果我们真的要这样做的话,可以做成一个插件的形式,写一个类继承org.apache.struts2.impl.StrutsObjectFactory,覆盖其中的buildAction方法即可;
  然后在struts-plugin.xml中配置
  <beantype="com.opensymphony.xwork2.ObjectFactory" name="myActionFactory"class="com.itheima.factory.MyObjectFactory" />
  <constantname="struts.objectFactory" value="spring" />
 然后打成jar包,放到classpath构建路径下即可,就将struts创建action方式变成了自己的,这是其开闭原则的体现;
所谓的开闭原则,就是对扩展性开发,对修改封闭的原则...具体的说就是,在一个现有的框架下...如果要对现有的功能进行扩展,
应该建立在不修改源代码的基础之上.
 
阅读(67)|评论(0)
0 0
原创粉丝点击