重温SSH小项目实践(1)--框架搭建篇

<!-- @page { margin: 2cm } P { margin-bottom: 0.21cm } -->

       因为在公司用的是内部开发的一套开发框架,虽然原理上和目前主流的框架思想上基本一致,但是用了一年多还是有些晦涩难懂,最近做个小系统玩,就重温一下久违了的struts2,spring2.5,ibatis框架

      先介绍一下这个玩具系统的功能点,系统中有两个核心对象,开发者(Isv)和应用(App),开发者和应用之间是一个一对多的关联关系,而这个系统的核心作用就是让系统管理人员将应用和开发者信息录入到系统中进行管理,然后一个用户(User)登录功能,其实就是3个表增删改查操作

      在这个以技术框架学习为目的的玩具系统里,我们尽量使用开源的框架,我采用的是Struts2+Spring2.5+Ibatis,Sitemesh做页面装饰器,Mysql5做后台数据库,Maven2做系统的构建工作,Junit3+Spring2.5 Test+DbUnit作为系统的单元测试框架,Jetty作为Web容器,好了,是时候动手吧这一坨的东西搞到一起来了

1.项目环境搭建过程

1.1新建Maven工程coreapp

Maven真是一个伟大的项目,有了他,一切都变得简单起来,在Eclipse中创建一个Maven项目--前提是Eclipse中安装了m2clipse插件,有了这个插件,工作起来如虎添异,如图

在下一步的页面中输入要创建应用的名称和工作保存路径,到下一步的时候进入选择Archetype页面,我们的框架用的是Struts,所以在Filter中输入struts关键字,与Struts相关的Archetype就都跑出来了,这里我们选择第一个struts2-archetype-starter,然后继续往下走,是项目的groupidpackage设置,输入一下,点击一下完成,然后就是见证神奇的时刻--Maven自动给我们创建了一个简单的web程序,所有项目的目录都生成好了,包括Struts的配置文件,Spring的配置文件Struts2Spring的集成,Struts2Sitemesh的集成,与DWR的集成--框架都给我们生成出来了!--伟大的maven~,然后我们切换到命令行下运行一个mvn jetty:run命令,maven就会自动打包部署启动jetty,启动完毕后,我我们通过url请球http://localhost:8080/coreapp就能看到Maven给我们的生成的框架Demo

1.2Maven给我们的项目里自动搞进去一些神马东西?

1.2.1pom.xml文件

这个是Maven的核心文件,先从这里面去瞧瞧,可以看到MavenStruts2jar包依赖,Sitemesh的依赖,Spring的依赖,Junit的依赖,DWR的依赖都自动给我们添加进来了,那么生成的这个东西无非就是围绕这些东西展开的

1.2.2web.xml

web程序里的机关的入口,看这里面都搞了些什么东西。,

首先看的是几个filter,都是Struts2相关的,第一个是请求拦截的,统一struts2处理,第二个Filter是请求完毕后对当前请求上下文中的数据做清理

<filter>

<filter-name>action2</filter-name>

<filter-class>org.apache.struts2.dispatcher.FilterDispatcher

</filter-class>

</filter>

<filter>

<filter-name>action2-cleanup</filter-name>

<filter-class>org.apache.struts2.dispatcher.ActionContextCleanUp

</filter-class>

</filter>

这个是Sitemesh的过滤器,要用Sitemesh框架对页面装饰,就必须配置她

<filter>

<filter-name>sitemesh</filter-name>

<filter-class>com.opensymphony.module.sitemesh.filter.PageFilter

</filter-class>

</filter>

看完了这个文件后马上有几个疑问出来了

Q1:SpringStruts2是怎么集成的?

A1:其实Spring一个核心的功能就是依赖注入,在maven提供的空的框架中,也是来这么做的。Struts2Action也可以当成一个bean交给Spring管理吗?可以的,我们来看一下具体是怎么配置实现的。

看一下Springbean配置文件applicationContext.xml—位于src/main/resources下,配置Action Bean的一个Demo--登录响应Action为例

<bean id="loginAction" class="com.taobao.top.coreapp.web.action.LoginAction"

parent="baseAction" scope="prototype">

</bean>

注意这个Action Bean和我们熟悉的DAOManager的配置不太一样,有个bean的属性 scope='prototype',这是因为Spring默认所有的bean都是单例的(singleton),即初始化一次以后,以后所有的请求都会共享这一个实例,这个对于那些没有状态变化的对象是非常有效的,能大大减少对象创建的次数和开销,但是对于Struts2Action,大家都知道,他的Action中是有成员变量的,这些成员变量将会被输出到页面上或从页面参数中直接装配值--这些对象的状态是变化的,如果和其他Bean都配置成一样单例,那么所有的请求都有可能更改这个共享对象的状态,所以这里必须采用prototype的方式来配置--每来一个请求Spring容器都会返回一个新的对象,这样多个请求之间的参数就不会被相互篡改了。

Q2:只要在Spring中配置一下就可以了吗?struts2有自己的那个struts.xml文件里不用配置了吗?

Spring配置文件中只是声明了这个bean,具体Struts2spring bean之间互通的机关还是要在struts.xml中配置的。看一下src/main/resources下的struts.xml文件,里面的内容一般包含以下几个元素(没有列出的元素文档的后方会提到)

<constant name="struts.devMode" value="true" />

<package name="coreapp-common" extends="struts-default" namespace="/">

<action name="login" class="loginAction">

<result>/index.jsp</result>

<interceptor-ref name="defaultStack"/>

</action>

</package>

先看一下<constant>这个元素,这个是做什么用的呢?他和struts.properties文件里的那些参数貌似key值是一样的嘛?

是的,struts2的系统级变量设置都是放在struts.properties这个文件中的,里面的配置项都是以key=valuea方式出现的,这个struts.properties文件一般也是放置在classpath下的,Struts2读取这些系统变量的顺序是这样的,现从struts自身的jar包中读取自带的默认配置(org.apache.struts2.default.properties),然后再到应用的classpath下查找struts.properties文件,如果有这个文件,则读取文件中配置项的值,覆盖jar包中的默认配置,如果没有这个文件还是用系统默认,找完struts.properties文件后,继续解析这个struts.xml文件中的constant变量,然后将constant元素的key,value覆盖默认配置,所以更改struts2系统配置项目放在properties文件或xml中还是有这么一点区别的。至于每个常量参数代表什么意思就不再阐述了,struts文档已经有很详细的注释了。

绕了一圈一直在说配置项的加载顺序问题了,看一下具体AAction的配置

<action name="login" class="loginAction">

这个Action元素的配置有些奇怪,那个class元素的值应该是指向一个具体的java类才对的啊,这个loginAction是从哪里蹦出来的呢?首先不是蹦出来的,是刚才在我们Q1里通过Spring配置出来的一个BeanId!是的,这个actionclass要和在spring中为这个action配置的bean ID保持一致

<bean id="loginAction" class="com.taobao.top.coreapp.web.action.LoginAction"

parent="baseAction" scope="prototype">

</bean>

等等,那struts2又是怎么做到通过普通的acton配置能访问,然后直接写个spring bean id也能找到的呢?

我们看一下,maven给我们自动生成的这个框架程序中添加了一个struts2-spring-plugin的依赖,是不是这个东西在搞怪呢?是的,这个jar包很简单,只有一个类和一个配置文件truts-plugin.xml,仔细看一下这个文件中的配置,这个文件中也有一个常量定义<constant name="struts.objectFactory" value="spring" />原来struts2自身也是一个小的容器,用来管理那些Action,Interceptor的创建和销毁,默认struts2使用自己的ObjectFactory来管理这些对象的,SpringStruts集成插件对这个ObjectFactory类做了扩展,可以看到,在这个文件中还有个配置项

<bean type="com.opensymphony.xwork2.ObjectFactory" name="spring" class="org.apache.struts2.spring.StrutsSpringObjectFactory" />

<contant>元素里的那个value=spring就是定位到这个 name=springbean上,这样通过扩展ObjectFactory的方式来达到将struts自身的beanspring bean打通的效果,通过一种很优雅的方式就将action的代理权交给了spring--针对接口编程的魅力,

Q2:Struts2是怎么用到Sitemesh达到装饰效果的?

网上可以搜到很多关于sitemesh的资料,在这个小项目里,用的也是很通用的方式,在web.xml中配置的sitemeshFilter对所有请求拦截,然后判断该请求是否满足被装饰的条件—WEB-INF/decoretor.xml文件中有配置<excludes>即不需要被装饰的页面在这里配置,支持正则表达式,然后判断当前请求满足哪个装饰器

<decorator name="main" page="main.jsp">

<pattern>/*</pattern>

</decorator>

然后到具体的装饰器页面main.jsp中,这个装饰页面定义了页面大的框架逻辑,如下图,一个页面header,footer,left,content几个部分组成,只有content是变化的,其他的都是固定的,就可以通过这个页面来定义好整个站点的规则

一个典型的装饰页面的代码实例

<htm>

<head>

<title><decorator:title default="企业信息管理系统" /></title>

<link src=”main.css”/>

</head>

<body

<decorator:getProperty property="body.onload" writeEntireProperty="true"/>>

<div id='header'>

这里是公共头的内容

</div>

<div id=”left”>

这里是左侧菜单栏

</div>

<div id=”content”>

<decorator:body/>这个是具体页面的body部分

</div>

<div id=”footer”>

页脚部分

</div>

<body>

</htm>

 

<decorator:title>标签

用于获取被装饰页面<head>元素中<title>子元素的值,如果没有,则设置为默认值 default

<decorator:getProperty property="body.onload" writeEntireProperty="true"/>

body上一般都会加上这行代码,因为具体子页面可能会为<body>元素添加onload时间,而装饰页面必须获取这个事件脚本

<decorator:body/>这个元素的作用就是获取被装饰页面的body部分内容

Sitemesh是怎样处理这些页面然后输出最终的结果的呢?

装修页面提供一个打的html框架,然后分别解析被装饰页面的head,通过<decorator:head>元素读取出来,然后添加到框架的head元素后面,然后通过<decorator:title>读取页面名字,通过<decorator:body>元素读取被装饰页面的body部分,然后sitemesh把各个部分解析后的html组装成最终的html大字符串,输出到页面,可以看出,这种处理方式对于大请求的应用还是有性能问题的,这么大个一坨字符串处理起来可不是个小的开销

 

简单总结一下,到目前这个阶段一切还是比较顺利的,很大部分归功与maven的自动化,对一堆的框架各自的职责和运行机制有所了解,接下来的几篇文章将后介绍在实际开发过程中遇到的一些问题和解决思路

 

posted @ 2010-12-25 17:41  java简单例子  阅读(340)  评论(0编辑  收藏  举报