Struts 学习笔记

来源:互联网 发布:ibm矩阵式的组织结构 编辑:程序博客网 时间:2024/04/29 14:56

Struts Framework 概览

struts开始于2000年3月,1.0在2001年7月发布。采用Struts能开发出基于MVC(Model-View-Controller)设计模式的应用构架。MVC设计模式把一个系统分成3个相互协调的部分:

Model(模型)
模型用于封装系统的状态

View(视图)
是模型的表示,提供用户交互界面。当模型状态发生变化时,视图应该得到通知,以便更新视图。

Controller(控制器)
接受来自视图的请求,修改模型的状态。

在基于JSP/Servlet的Web应用中要使用MVC模式,需要解决一个问题。我们知道,HTTP的底层是TCP/IP协议,而TCP/IP是一种无状态连接的协议,那么如果我们的模型发生了变化,就无从通知视图。Struts采用了在“JSP规范 v0.92”中提到的MVC Model 2,这是对MVC在Web上应用的修订版。图示如下:


图表 1:MVC Model 2(From Malcolm Davis)

Struts应用有3个主要部件:一个Servlet Controller(由Struts提供,org.apache.action.ActionServlet,以下简称controller)及负责具体业务处理的Action类(org.apache.action.Action的基类);Jsp页面(viewer);应用的业务逻辑封装(model)。Struts 的中心控制器(ActionServlet)接受所有来自客户端的请求,并根据系统的配置(Struts-config.xml)路由HTTP请求到其他Action对象(开发者实现的org.apache.struts.action.Action的子类),在这些Action对象中会进行所有的业务操作,比如插入一条订单,修改一条记录。处理完毕,由Struts的ActionServlet转向到JSP页面,将处理结果返回给客户端。从这儿可以看出在Struts中ActionServlet担任了重要的角色,由它控制所有的程序流转,是MVC三个相对独立的部分协调工作,提供系统的完善功能。从下图可见Struts是MVC Model 2的一个典型应用。
 

图表 2:Struts工作机理(From Malcolm Davis)

在Struts启动时,controller会读入一个配置文件struts-config.xml,其中定义了前端请求的url与相应的Action类及使用的Form类的映射关系,下面是一个struts-config.xml中的片断:

  1.     <action    path="/logon"

  2.                type="org.apache.struts.webapp.example.LogonAction"

  3.                name="logonForm"

  4.               scope="request"

  5.               input="/logon.jsp">

  6.     </action>



从中可以看出,这个action会响应url类似于/logon的请求(在后面我们会看到,这儿的实际的url要根据在web.xml中配置,比如可能为/logon.do或/logon.other之类的带后缀的url),负责处理这个请求的action类是org.apache.struts.webapp.example.LogonAction,使用的FormBean是logonForm,scope界定了这个FormBean存在的范围是只在当前request中存在。
struts会读入这些设置,生成相应的org.apache.action.ActionMapping对象。controller正是使用这些mapping来把HTTP请求转发到应用的actions。一个ActionMapping指定了

  • 一个请求URI

  • 对应的类(Action的子类)

  • 需要的属性


对于一个DB应用,

  • 一个商业逻辑Bean用于连接、查询数据库

  • 这个Bean返回结果到Action

  • Action将这些结果存储到request的form bean中

  • Jsp显示 

ActionServlet深入探讨

java.lang.Object
  |
  +--javax.servlet.GenericServlet
        |
        +--javax.servlet.http.HttpServlet
              |
              +--org.apache.struts.action.ActionServlet

Struts提供了一个缺省版本的ActionServlet类,你可以继承这个类,覆盖其中的一些方法来达到你的特殊处理的需要。ActionServlet继承与javax.servlet.http.HttpServlet,所以在本质上它和一个普通的servlet没有区别,你完全可以把它当做一个servlet来看待,只是在其中完成的功能不同罢了。ActionServlet主要完成如下功能:

将一个来自客户端的URI映射到一个相应的Action类

  • 如果是这个Action类是第一次被调用,那么实例化一个并放入缓存

  • 如果在配置文件(struts-config.xml)中指定了相应的ActionForm,那么从Request中抓取数据填充FormBean

  • 调用这个Action类的perform()方法,传入ActionMapping的一个引用,对应的ActionForm、以及由容器传给ActionServlet的HttpServletRequest、HttpServletResponse对象。



确省版本的ActionServlet会从配置文件web.xml中读取如下初始化参数:

  • application
    应用使用的资源包(resources bundle)的基类

  • factory
    用于创建应用的MessageResources对象的MessageResourcesFactory的类名。确省是org.apache.struts.util.PropertyMessageResourcesFactory。

  • config
    Struts的配置文件,确省是/WEB-INF/struts-config.xml。注意这儿是与应用Context关联的相对路径。

  • content
    定义了确省的内容类型和编码格式,它会被自动地被设置到每个response中,如果JSP/Servlet中没有明确的设置。确省是text/html。

  • debug
    调试信息的级别。默认为0,比当前级别高的调试信息会被log到日志文件中。

  • detail
    与debug的作用类似,只是这个detail是initMapping()时专用的。调试信息会被打印到System.out,而不是日志文件。

  • formBean
    ActionFormBean的实现类,确省为org.apache.struts.action.ActionFormBean

  • forward
    应用中使用的ActionForward类,确省是org.apache.struts.action.ActionForward。

  • locale
    指定了确省使用的Locale对象。设为true,当得到一个session时,会自动在session中存储一个以Action.LOCALE_KEY标示的Locale对象,如果session中还没有与Action.LOCALE_KEY绑定的Locale对象。

  • mapping
    应用中使用的ActionMapping类,确省是org.apache.struts.action.ActionMapping。

  • multipartClass
    文件上传使用的MutipartRequestHandler的实现类。确省为org.apache.struts.upload.DiskMultipartRequestHandler

  • nocache
    如果设为true,那么ActionServlet会自动在每个到客户端的响应中添加nocache的HTML头,这样客户端就不会对应用中的页面进行缓存。确省为false

  • null
    如果设置为true,那么应用在得到一个未定义的message资源时,会返回null,而不是返回一个错误信息。确省是true。

  • maxFileSize
    文件上传的大小上限,确省为250M

  • bufferSize
    文件上传时的缓冲区的大小,确省为4M

  • tempDir
    设置用于上传时的临时目录。工作目录会作为一个Servlet环境(Context)的属性提供。

  • validate
    Are we using the new configuration file format?确省为true。

  • validating


在解析配置XML文件是是否进行有效性的验证。确省为true

ActionServlet中应用了命令设计模式。

一个Servlet在由容器生成时,首先会调用init()方法进行初始化,在接到一个HTTP请求时,调用相应的方法进行处理;比如GET请求调用doGet()方法,POST请求调用doPost()方法。所以首先看看ActionServlet的init()方法,你就会很清楚为什么ActionServlet可以完成这些功能了。

init()


在它的init()方法中,ActionServlet依次调用如下protected的方法完成初始化:

  • initActions() -     大家可能还曾有这个疑问:Struts为什么可以找到一个请求URI对应的action类呢?答案就在这儿,ActionServlet有一个actions属性,类型为org.apache.struts.util.FastHashMap,用于存储以类的全名为key的已实例化的Action类。在init()时首先调用的就是initActions()方法,在这个方法中只是简单的清除map中的所有的名值对,

    1.         synchronized (actions) {

    2.             actions.setFast(false);

    3.             actions.clear();

    4.             actions.setFast(true);

    5.         }


    首先把actions设为slow模式,这时对FastHashMap的访问是线程同步的,然后清除actions中的所有的已存在的名/值对,最后再把actions的模式设为fast。由于FastHashMap是struts在java.util.HashMap的基础上的一个扩展类,是为了适应多线程、并且对HashMap的访问大部分是只读的特殊环境的需要。大家知道java.util.HashMap是非线程安全的,所以HashMap一般适用于单线程环境下。org.apache.struts.FastHashMap就是继承于java.util.HashMap,在其中添加多线程的支持产生的。在fast模式下的工作方式是这样的:读取是非线程同步的;写入时首先克隆当前map,然后在这个克隆上做写入操做,完成后用这个修改后的克隆版本替换原来的map。那么在什么时候会把Actions类添加到这个map中呢?我们已经提到了struts是动态的生成Action类的实例的,在每次ActionServlet接收到一个GET或POST的HTTP请求时,会在这个map中查找对应的Action类的实例,如果不存在,那么就实例化一个,并放入map中。可见这个actions属性起到了对Action类实例的缓存的作用。

  • initInternal() -     初始化ActionServlet内部使用的资源包MessageResources,使用MessageResources.getMessageResources(internalName)得到    internalName为"org.apache.struts.action.ActionResources"对应的ActionResources.properties文件。这个资源包主要用于ActionServlet处理过程中的用到的提示信息,这儿不展开讨论。

  • initDebug() -     从web.xml中读取本应用的debug级别参数getServletConfig().getInitParameter("debug"),然后赋给debug属性。

  • initApplication()-    初始化应用资源包,并放置入ServletContext中。

    1.         String factory =getServletConfig().getInitParameter(“factory”);    

    2.         String oldFacory = MessageResourcesFactory.getFactoryClass();

    3.         if (factory !=null)

    4.                 MessageResourcesFactory.setFactoryClass(factory);

    5.         String value = getServletConfig().getInitParameter("application");

    6.         MessageResourcesFactory factoryObject =

    7.                 MessageResourcesFactory.createFactory();

    8.         application = factoryObject.createResources(value);

    9.         MessageResourcesFactory.setFactory(oldFactory); 

    10.         getServletContext().setAttribute(Action.MESSAGES_KEY, application);


    说明:文中引用的代码片断可能会省略了一些例外检查等非主线的内容,敬请注意。

    首先从配置文件中读取factory参数,如果这个参数不为空,那么就在MessageResourcesFactory中使用这个指定的Factory类;否则,使用默认的工厂类org.apche.struts.util.PropertyMessageResourceFactory。然后调用MessageResourcesFactory的静态createFactory()方法,生成一个具体的MessageResourceFactory对象(注意:MessageResourcesFactory是抽象类)。这样就可以调用这个具体的MessageResourceFactory的createResource()方法得到配置文件(web.xml)中定义的资源文件了。
    上面的application对象类型为MessageResources。在web.xml中在配置ActionServlet时可以指定一个特定的工厂类。不能直接MessageResourcesFactory的createResources()方法,因为这个方法是abstract的。创建factoryObject的过程如下:

    1.     

    2.         MessageResourceFactory factoryObject=

    3.                 MessageResourcesFactory.createFactory();

    4.         application = factoryObject.createResources(value);


    <li>initMapping() -    为应用初始化mapping信息ActionServlet有一个protected的属性:mapping,封装了一个ActionMapping的对象集合,以便于管理、查找ActionMapping。mappings是org.apache.struts.action.ActionMappings类的实例。主要有两个方法:addMapping(ActionMapping mapping)和findMapping(String path)。ActionMapping也是使用上面提到的org.apache.struts.util.FastHashMap类来存储所有的ActionMapping对象。

    1.         mappings.setServlet(this);

    2.         ……

    3.         // Initialize the name of our ActionFormBean implementation class

    4.         value = getServletConfig().getInitParameter("formBean");

    5.         if (value != null)

    6.             formBeanClass = value;

    7.     

    8.         // Initialize the name of our ActionForward implementation class

    9.         value = getServletConfig().getInitParameter("forward");

    10.         if (value != null)

    11.                 forwardClass = value;

    12.         // Initialize the name of our ActionMapping implementation class

    13.         value = getServletConfig().getInitParameter("mapping");

    14.         if (value != null)

    15.                 mappingClass = value;


    在initMapping()中,首先链接mappings对象到本servlet实例。其实这句话的作用很简单,在ActionMappings中会有一个ActionServlet类型的属性,这个属性就界定了这个ActionMappings对象所属的ActionServlet。Struts的实现比较灵活,其中的ActionFormBean、ActionForward、ActionMapping类你完全可以使用自己实现的子类,来定制Struts的工作方式。上面的代码就从配置文件(web.xml)中读取formBean、forward、mapping参数,这些参数就是你定制的ActionFormBean、ActionForward、ActionMapping类名。

    1.         // Initialize the context-relative path to our configuration resources

    2.         value = getServletConfig().getInitParameter("config");

    3.         if (value != null)

    4.                 config = value;

    5.                 // Acquire an input stream to our configuration resource

    6.         InputStream input = getServletContext().getResourceAsStream(config);

    7.         Digester digester = null;

    8.                 digester = initDigester(detail);

    9.         try {

    10.                 formBeans.setFast(false);

    11.                 forwards.setFast(false);

    12.                 mappings.setFast(false);

    13.                 digester.parse(input);

    14.                 mappings.setFast(true);

    15.                 forwards.setFast(true);

    16.                 formBeans.setFast(true);

    17.         } catch (SAXException e) {

    18.                 throw new ServletException

    19.         (internal.getMessage("configParse", config), e);

    20.         } finally {

    21.                 input.close();

    22.         }


    从web.xml读取Struts的配置文件的位置。使用org.apache.struts.digester.Digester解析config参数标示的配置文件,通常为“/WEB-INF/struts-config.xml”,解析出所有的data-source、form-bean、action-mapping、forward。从上面的程序片断看到,Digester仅仅调用了一个parse()方法,那么,Digester是怎样把解析struts-config.xml文件并把解析的结果form-bean等信息存储到属性变量formBeans等中的呢?你可以注意到在调用digester.parse(InputStream)之前,首先调用了initDigester()方法:

    1.         Digester digester = new Digester();

    2.         digester.push(this);

    3.         digester.addObjectCreate("struts-config/action-mappings/action",

    4.                                  mappingClass, "className");

    5.         digester.addSetProperties("struts-config/action-mappings/action");

    6.         digester.addSetNext("struts-config/action-mappings/action",

    7.                             "addMapping",

    8.                             "org.apache.struts.action.ActionMapping");

    9.         digester.addSetProperty

    10.             ("struts-config/action-mappings/action/set-property",

    11.              "property", "value");


    在这个方法中首先生成一个Digester对象,然后设置解析的规则和回调,如果你对XML、SAX不是很熟,这儿不必纠缠太深。要注意的是addSetNext()方法,设置了每一个要解析元素的Set Next回调方法,而这个方法就是由digester解析器的父提供的。上面的片断中的“addMapping”就是ActionServlet本身定义的一个方法,将由Digester回调。Digester就是籍此把解析出的每一个FormBean、ActionForward、ActionMapping等存储到属性变量formBeans、forwards、mappings等中的。

  • initUpload() -    初始化有关Upload的一些参数,比如:bufferSize、tempDir。

  • initDataSource() -取出在initMapping()中从配置文件中读取的每一个DataSource,设置LogWriter,如果为GenericDataSource的实例,则打开数据源。然后,把每个dataSource放入Context中。
    dataSource.setLogWriter(scw);
    ((GenericDataSource)dataSource).open();
    getServletContext().setAttribute(key,dataSource);

  • initOther() -        设置其它尚未初始化的的参数(content、locale、nocache),并发布formBeans、forwards、mappings到Context:
    getServletContext().setAttribute(Action.FORM_BEANS_KEY, formBeans);
    getServletContext().setAttribute(Action.FORWARDS_KEY, forwards);
    getServletContext().setAttribute(Action.MAPPINGS_KEY, mappings);

  • initServlet() -    初始化Controller Servlet的Servlet Mapping。这儿也使用了Digester工具,扫描web.xml所有的<web-app/servlet-mapping>,寻找servlet-name与当前Servlet相同的mapping,置入Context。代码如下;

    1.         Digester digester = new Digester();

    2.         digester.push(this);

    3.         digester.setDebug(debug);

    4.         digester.setValidating(validating);

    5.         digester.addCallMethod(“web-appservlet-mapping”,“addServletMapping”, 2);

    6.         digester.addCallParm(“web-appservlet-mappingservlet-name”, 0);

    7.         digester.addCallParm(“web-appservlet-mappingurl-pattern”, 1);

    8.         InputStream is = getServletContext().getResourceAsStream(“/WEB-INFweb.xml”);

    9.         digester.parse(is); 

    10.         getServletContext().setAttribute(Action.SERVLET_KEY,servletMapping);

 

原创粉丝点击