一个计算机专业学生几年的编程经验汇总(四)

Java杂谈(九)--Struts

                J2ee的开源框架很多,笔者只能介绍自己熟悉的几个,其他的目前在中国IT行业应用得不是很多。希望大家对新出的框架不要盲目的推崇,首先一定要熟悉它比旧的到底好在哪里,新的理念和特性是什么?然后再决定是否要使用它。 
              
                这期的主题是Struts,直译过来是支架。Struts的第一个版本是在2001年5月发布的,它提供了一个Web应用的解决方案,如何让Jsp和 servlet共存去提供清晰的分离视图和业务应用逻辑的架构。在Struts之前,通常的做法是在Jsp中加入业务逻辑,或者在Servlet中生成视图转发到前台去。Struts带着MVC的新理念当时退出几乎成为业界公认的Web应用标准,于是当代IT市场上也出现了众多熟悉Struts的程序员。即使有新的框架再出来不用,而继续用Struts的理由也加上了一条低风险,因为中途如果开发人员变动,很容易的招进新的会Struts的IT民工啊, ^_^! 
          
                笔者之前说的都是Struts-1,因为新出了Struts-2,使得每次谈到Struts都必须注明它是Struts-1还是2。笔者先谈比较熟悉的 Struts-1,下次再介绍一下与Struts-2的区别: 
              
                1.   Struts框架整体结构 
                Struts-1的核心功能是前端控制器,程序员需要关注的是后端控制器。前端控制器是是一个Servlet,在中间配置所有 Request都必须经过前端控制器,它的名字是ActionServlet,由框架来实现和管理。所有的视图和业务逻辑隔离都是应为这个 ActionServlet,       它就像一个交通警察,所有过往的车辆必须经过它的法眼,然后被送往特定的通道。所有,对它的理解就是分发器,我们也可以叫做Dispatcher,其实了解Servlet编程的人自己也可以写一个分发器,加上拦截request的Filter,其实自己实现一个struts框架并不是很困难。主要目的就是让编写视图的和后台逻辑的可以脱离紧耦合,各自同步的完成自己的工作。 

                那么有了ActionServlet在中间负责转发,前端的视图比如说是Jsp,只需要把所有的数据Submit,这些数据就会到达适合处理它的后端控制器Action,然后在里面进行处理,处理完毕之后转发到前台的同一个或者不同的视图Jsp中间,返回前台利用的也是Servlet里面的forward 和redirect两种方式。所以到目前为止,一切都只是借用了Servlet的API搭建起了一个方便的框架而已。这也是Struts最显著的特性?? 控制器。 

                那么另外一个特性,可以说也是Struts-1带来的一个比较成功的理念,就是以xml配置代替硬编码配置信息。以往决定Jsp往哪个servlet提交,是要写进Jsp代码中的,也就是说一旦这个提交路径要改,我们必须改写代码再重新编译。而Struts提出来的思路是,编码的只是一个逻辑名字,它对应哪个class文件写进了xml配置文件中,这个配置文件记录着所有的映射关系,一旦需要改变路径,改变xml文件比改变代码要容易得多。这个理念可以说相当成功,以致于后来的框架都延续着这个思路,xml所起的作用也越来越大。 
      
                大致上来说Struts当初给我们带来的新鲜感就这么多了,其他的所有特性都是基于方便的控制转发和可扩展的xml配置的基础之上来完成它们的功能的。 
下面将分别介绍Action和FormBean,       这两个是Struts中最核心的两个组件。 

                    2.   后端控制器Action 
                Action就是我们说的后端控制器,它必须继承自一个Action父类,Struts设计了很多种Action,例如DispatchAction、 DynaValidationAction。它们都有一个处理业务逻辑的方法execute(),传入的request,       response,       formBean和actionMapping四个对象,返回actionForward对象。到达Action之前先会经过一个 RequestProcessor来初始化配置文件的映射关系,这里需要大家注意几点: 

                    1)   为了确保线程安全,在一个应用的生命周期中,Struts框架只会为每个Action类创建一个Action实例,所有的客户请求共享同一个Action 实例,并且所有线程可以同时执行它的execute()方法。所以当你继承父类Action,并添加了private成员变量的时候,请记住这个变量可以被多个线程访问,它的同步必须由程序员负责。(所有我们不推荐这样做)。在使用Action的时候,保证线程安全的重要原则是在Action类中仅仅使用局部变量,谨慎的使用实例变量。局部变量是对每个线程来说私有的,execute方法结束就被销毁,而实例变量相当于被所有线程共享。 
  
                    2)   当ActionServlet实例接收到Http请求后,在doGet()或者doPost()方法中都会调用process()方法来处理请求。 RequestProcessor类包含一个HashMap,作为存放所有Action实例的缓存,每个Action实例在缓存中存放的属性key为 Action类名。在RequestProcessor类的processActionCreate()方法中,首先检查在HashMap中是否存在 Action实例。创建Action实例的代码位于同步代码块中,以保证只有一个线程创建Action实例。一旦线程创建了Action实例并把它存放到 HashMap中,以后所有的线程会直接使用这个缓存中的实例。 

                    3)   <action>       元素的   <roles>   属性指定访问这个Action用户必须具备的安全角色,多个角色之间逗号隔开。RequestProcessor类在预处理请求时会调用自身的 processRoles()方法,检查配置文件中是否为Action配置了安全角色,如果有,就调用HttpServletRequest的 isUserInRole()方法来判断用户是否具备了必要的安全性角色,如果不具备,就直接向客户端返回错误。(返回的视图通过   <input>   属性来指定) 

                3.   数据传输对象FormBean 
                Struts并没有把模型层的业务对象直接传递到视图层,而是采用DTO(Data       Transfer       Object)来传输数据,这样可以减少传输数据的冗余,提高传输效率;还有助于实现各层之间的独立,使每个层分工明确。Struts的DTO就是 ActionForm,即formBean。由于模型层应该和Web应用层保持独立。由于ActionForm类中使用了Servlet       API,       因此不提倡把ActionForm传递给模型层,       而应该在控制层把ActionForm       Bean的数据重新组装到自定义的DTO中,       再把它传递给模型层。它只有两个scope,分别是session和request。(默认是session)一个ActionForm标准的生命周期是: 
                1)   控制器收到请求       ->     
                2)   从request或session中取出ActionForm实例,如不存在就创建一个       -> 
                3)   调用ActionForm的reset()方法       ->     
                4)   把实例放入session或者request中       ->     
                5)   将用户输入表达数据组装到ActionForm中       ->     
                6)   如眼张方法配置了就调用validate()方法           -> 
                7)   如验证错误就转发给   <input>   属性指定的地方,否则调用execute()方法 

                validate()方法调用必须满足两个条件: 
                1)   ActionForm       配置了Action映射而且name属性匹配 
                2)   <aciton>       元素的validate属性为true 
      
                如果ActionForm在request范围内,那么对于每个新的请求都会创建新的ActionForm实例,属性被初始化为默认值,那么reset ()方法就显得没有必要;但如果ActionForm在session范围内,同一个ActionForm实例会被多个请求共享,reset()方法在这种情况下极为有用。 

                4.   验证框架和国际化 
                Struts有许多自己的特性,但是基本上大家还是不太常用,说白了它们也是基于JDK中间的很多Java基础包来完成工作。例如国际化、验证框架、插件自扩展功能、与其他框架的集成、因为各大框架基本都有提供这样的特性,Struts也并不是做得最好的一个,这里也不想多说。Struts的验证框架,是通过一个的配置文件读入验证规则,然后在里面找到验证实现通过自动为Jsp插入 Javascript来实现,可以说做得相当简陋。弹出来的JavaScript框不但难看还很多冗余信息,笔者宁愿用formBean验证或者 Action的saveErrors(),验证逻辑虽然要自己写,但页面隐藏/浮现的警告提示更加人性化和美观一些。 

                至于Struts的国际化,其实无论哪个框架的国际化,类是最重要的Java       I18N类。在Java语言中,几乎所有的对国际化和本地化的支持都依赖于这个类。如果Java类库中的某个类在运行的时候需要根据Locale对象来调整其功能,那么就称这个类是本地敏感的(Locale-Sensitive),       例如类就是,依赖于特定Locale。 

                创建Locale对象的时候,需要明确的指定其语言和国家的代码,语言代码遵从的是ISO-639规范,国家代码遵从ISO-3166规范,可以从 
               
               

                Struts的国际化是基于properties的message/key对应来实现的,笔者曾写过一个程序,所有Jsp页面上没有任何Text文本串,全部都用的是   <bean:message>   去Properties文件里面读,这个时候其实只要指定不同的语言区域读不同的Properties文件就实现了国际化。需要注意的是不同语言的字符写进Properties文件的时候需要转化成Unicode码,JDK已经带有转换的功能。JDK的bin目录中有native2ascii这个命令,可以完成对*.txt和*.properties的Unicode码转换。 

                OK,今天就说到这里,本文中的很多内容也不是笔者的手笔,是笔者一路学习过来自己抄下来的笔记,希望对大家有帮助!Java杂谈一路走来,感谢大家持续的关注,大概再有个2到3篇续篇就改完结了!笔者尽快整理完成后续的写作吧……^_^
##############################################################################################################
Java杂谈(九)--Struts2


        最近业余时间笔者一直Java       Virtual       Machine的研究,由于实习分配到项目组里面,不想从前那么闲了,好不容易才抽出时间来继续这个话题的帖子。我打算把J2ee的部分结束之后,再谈谈 JVM和JavaScript,只要笔者有最新的学习笔记总结出来,一定会拿来及时和大家分享的。衷心希望与热爱Java的关大同仁共同进步…… 

        这次准备继续上次的话题先讲讲Struts-2,手下简短回顾一段历史:随着时间的推移,Web应用框架经常变化的需求,产生了几个下一代       Struts的解决方案。其中的Struts       Ti       继续坚持       MVC模式的基础上改进,继续Struts的成功经验。       WebWork项目是在2002年3月发布的,它对Struts式框架进行了革命性改进,引进了不少新的思想,概念和功能,但和原Struts代码并不兼       容。WebWork是一个成熟的框架,经过了好几次重大的改进与发布。在2005年12月,WebWork与Struts       Ti决定合拼,       再此同时,       Struts       Ti       改名为       Struts       Action       Framework       2.0,成为Struts真正的下一代。 

                看看Struts-2的处理流程: 
                1)   Browser产生一个请求并提交框架来处理:根据配置决定使用哪些拦截器、action类和结果等。 
                2)   请求经过一系列拦截器:根据请求的级别不同拦截器做不同的处理。这和Struts-1的RequestProcessor类很相似。 
                3)   调用Action:       产生一个新的action实例,调用业务逻辑方法。 
                4)   调用产生结果:匹配result       class并调用产生实例。 
                5)   请求再次经过一系列拦截器返回:过程也可配置减少拦截器数量 
                6)   请求返回用户:从control返回servlet,生成Html。 

                这里很明显的一点是不存在FormBean的作用域封装,直接可以从Action中取得数据。       这里有一个Strut-2配置的文件: 
                <filter>     
                        <filter-name>   controller   </filter-name>     
                        <filter-class>      </filter-class> 
                </filter> 
                <filter-mapping>     
                        <filter-name>   cotroller   </filter-name>     
                        <url-pattern>   /*   </url-pattern> 
                </filter-mapping> 

        注意到以往的servlet变成了filter,ActionServlet变成了FilterDispatcher,*.do变成了/*。filter 配置定义了名称(供关联)和filter的类。filter       mapping让URI匹配成功的的请求调用该filter。默认情况下,扩展名为   ".action   "。这个是在文件里的   "   "属性定义的。 

        是属性定义文件,通过在项目classpath路径中包含一个名为“”的文件来设置不同的属性值。而Struts-2的默认配置文件名为。由于1和2的action扩展名分别为.do和.action,所以很方便能共存。我们再来看一个Struts-2的action代码: 
        public   class   MyAction   { 
              public   String   execute()   throws   Exception   {     
                      //do   the   work     
                      return   "success   "; 
              } 
        } 

        很明显的区别是不用再继承任何类和接口,返回的只是一个String,无参数。实际上在Struts-2中任何返回String的无参数方法都可以通过配置来调用action。所有的参数从哪里来获得呢?答案就是Inversion       of       Control技术(控制反转)。笔者尽量以最通俗的方式来解释,我们先试图让这个Action获得reuqest对象,这样可以提取页面提交的任何参数。那么我们把request设为一个成员变量,然后需要一个对它的set方法。由于大部分的action都需要这么做,我们把这个set方法作为接口来实现。 
        public   interface   ServletRequestAware   { 
                public   void   setServletRequest(HttpServletRequest   request); 
        } 

        public   class   MyAction   implements   ServletRequestAware   {     
                private   HttpServletRequest   request;     

                public   void   setServletRequest(HttpServletRequest   request)   {     
                           =   request;     
                } 
                              
                public   String   execute()   throws   Exception       { 
                          //     do   the   work   directly   using   the   request     
                          return       ;     
                }     
        } 

        那么谁来调用这个set方法呢?也就是说谁来控制这个action的行为,以往我们都是自己在适当的地方写上一句 (…),也就是控制权在程序员这边。然而控制反转的思想是在哪里调用交给正在运行的容器来决定,只要利用Java反射机制来获得Method对象然后调用它的invoke方法传入参数就能做到,这样控制权就从程序员这边转移到了容器那边。程序员可以减轻很多繁琐的工作更多的关注业务逻辑。Request可以这样注入到action中,其他任何对象也都可以。为了保证action的成员变量线程安全, Struts-2的action不是单例的,每一个新的请求都会产生一个新的action实例。 

        那么有人会问,到底谁来做这个对象的注入工作呢?答案就是拦截器。拦截器又是什么东西?笔者再来尽量通俗的解释拦截器的概念。大家要理解拦截器的话,首先一定要理解GOF23种设计模式中的Proxy模式。 

        A对象要调用f(),它希望代理给B来做,那么B就要获得A对象的引用,然后在B的f()中通过A对象引用调用A对象的f()方法,最终达到A的f()被调用的目的。有没有人会觉得这样很麻烦,为什么明明只要A.f()就可以完成的一定要封装到B的f()方法中去?有哪些好处呢? 

        1)   这里我们只有一个A,当我们有很多个A的时候,只需要监视B一个对象的f()方法就可以从全局上控制所有被调用的f()方法。 
        2)   另外,既然代理人B能获得A对象的引用,那么B可以决定在真正调A对象的f()方法之前可以做哪些前置工作,调完返回前可有做哪些后置工作。 

        讲到这里,大家看出来一点拦截器的概念了么?它拦截下一调f()方法的请求,然后统一的做处理(处理每个的方式还可以不同,解析A对象就可以辨别),处理完毕再放行。这样像不像对流动的河水横切了一刀,对所有想通过的水分子进行搜身,然后再放行?这也就是AOP(Aspect       of       Programming面向切面编程)的思想。 

        Anyway,Struts-2只是利用了AOP和IoC技术来减轻action和框架的耦合关系,力图到最大程度重用action的目的。在这样的技术促动下,Struts-2的action成了一个简单被框架使用的POJO(Plain       Old       Java       Object)罢了。实事上AOP和IoC的思想已经遍布新出来的每一个框架上,他们并不是多么新的技术,利用的也都是JDK早已可以最到的事情,它们代表的是更加面向接口编程,提高重用,增加扩展性的一种思想。Struts-2只是部分的使用这两种思想来设计完成的,另外一个最近很火的框架 Spring,更大程度上代表了这两种设计思想,笔者将于下一篇来进一步探讨Spring的结构。 

        PS:       关于Struts-2笔者也没真正怎么用过,这里是看了网上一些前辈的帖子之后写下自己的学习体验,不足之处请见谅!
##################################################################################################################


 

你可能感兴趣的:(计算机)