首页 诗词 字典 板报 句子 名言 友答 励志 学校 网站地图
当前位置: 首页 > 教程频道 > 软件管理 > 软件架构设计 >

Ssi的调整(Struts2+Spring+Ibatis)

2013-07-08 
Ssi的整合(Struts2+Spring+Ibatis)Ssi的整合(Struts2+Spring+Ibatis)1 jar包的下载???????? 本文好用的三

Ssi的整合(Struts2+Spring+Ibatis)

Ssi的整合(Struts2+Spring+Ibatis)

1 jar包的下载

???????? 本文好用的三个框架版本分别为

???????? struts-2.3.8 ,spring-framework-3.0.5.RELEASE,ibatis-2.3.4.726

下载地址:struts

Struts2

http://archive.apache.org/dist/struts/binaries/struts-2.3.8-all.zip

Spring

http://www.springsource.org/spring-community-download(进入后需要录入信息后才能下载)

Ibatis

http://archive.apache.org/dist/ibatis/binaries/ibatis.java/ibatis-2.3.4.726.zip

?

2 程序搭建---Struts2

2.1首先在eclipse新建web工程,新建创建的web目录结构如下:

ssi-release

???????? |__compensate

???????? |___webapps

?????????????????? |___META-INF

??????????????????????????? |___MANIFEST.MF?

?????????????????? |___WEB-INF

??????????????????????????? |___classes

??????????????????????????? |___lib

??????????????????????????? |___web.xml

?????????????????? |___index.jsp

?

2.2拷贝Struts2基本的jar包到工程下的lib目录

commons-fileupload-1.2.2.jar

commons-io-2.0.1.jar

commons-lang3-3.1.jar

freemarker-2.3.19.jar

javassist-3.11.0.GA.jar

ognl-3.0.6.jar

struts2-core-2.3.8.jar

xwork-core-2.3.8.jar

log4j-1.2.15.jar(非必须,便于使用log4j所以此处添加本jar包)

2.3在web.xml中添加Struts2的控制分发的Filter

??? <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>

其中mapping中<url-pattern>/*</url-pattern>表示过滤所有请求

?

2.3编写Struts2测试action

??????? 创建com.jshand.ssi.action包

??????? 在com.jshand.ssi.action包下创建TestAction类实现com.opensymphony.xwork2.Action接口

并实现方法execute方法,代码如下:

package com.jshand.ssi.action;

?

import com.opensymphony.xwork2.Action;

?

/**

?*@file_nameTestAction.java

?*@project?????? ssi-release

?*@author? ?? jshand

?*@createDateJun2,2013? 12:37:18PM

?*@version ?? 1.0

?*http://www.jshand.com

?*

?*/

?

publicclass TestAction implements Action{

???

??? public String actionStr = "com.jshand.ssi.action.TestAction";

???

??? public String execute() throws Exception{

?????? System.out.println("execute Struts2 Action ......");

?????? returnSUCCESS;

??? }

??? public String getActionStr() {

?????? returnactionStr;

??? }

?

??? publicvoid setActionStr(String actionStr) {

?????? this.actionStr = actionStr;

??? }

}

其中actionStr属性定义了一个字符串,并设置了set、get方法目的是讲测试Struts2封装的参数传递,将接收的或者默认的字符串输出到jsp中。

2.3编写Struts2的配置文件struts.xml

在源文件目录(工程下compensate目录)创建struts.xml文件,

<?xml version="1.0" encoding="UTF-8"?>

<!DOCTYPE struts PUBLIC? "-//Apache Software Foundation//DTD Struts Configuration 2.3//EN"

??? "http://struts.apache.org/dtds/struts-2.3.dtd">

?

<struts>

??? <package name="struts2" extends="struts-default" >

??????? <action name="test" class="com.jshand.ssi.action.TestAction">

??????????? <result name="success">/index.jsp</result>

??????? </action>

??? </package>

</struts>

主要配置xml中aciton节点,

name属性为自定义属性,同一个package不允许出现相同name

class属性为之前编写的TestAction的全路径

<action name="test" class="com.jshand.ssi.action.TestAction">

????? <result name="success">/index.jsp</result>

</action>

?

?

?

action子节点中添加一个result节点当执行完Action后跳转到index.jsp中,在index.jsp中添加如下代码${actionStr},使输入的参数或这作用域内的属性输出到index.jsp中

<body>

??? This is my JSP page. <br>

??? ${actionStr}

? </body>

启动中间件,本文用到的是tomcat6,在浏览器中输入

http://localhost:8080/ssi-release/test.action?actionStr=testParameter

控制台输出如下:


Ssi的调整(Struts2+Spring+Ibatis)
?

?

浏览器跳转到index.jsp,并输出接受到的参数


Ssi的调整(Struts2+Spring+Ibatis)
?

说明Struts2配置成功!接下来我们整合Spring到Struts2中

3 程序搭建---整合Spring和Struts2

同Struts2一样大多数框架的第一个步骤都是拷贝jar包到工程中,并配置环境变量?,Spring所需要的包如下,其中struts2-spring-plugin-2.3.8.jar为整合struts2和Spring的包,当然这个包不是必须的,我们可以通过其他方法进行处理。

aopalliance-1.0.jar

aspectj-DEVELOPMENT-20130313082900.jar

aspectjlib-1.5.2.jar

aspectjweaver.jar

commons-attributes-api.jar

commons-attributes-compiler.jar

commons-logging-1.1.1.jar

commons-logging.jar

org.springframework.aop-3.0.5.RELEASE.jar

org.springframework.asm-3.0.5.RELEASE.jar

org.springframework.aspects-3.0.5.RELEASE.jar

org.springframework.beans-3.0.5.RELEASE.jar

org.springframework.context-3.0.5.RELEASE.jar

org.springframework.context.support-3.0.5.RELEASE.jar

org.springframework.core-3.0.5.RELEASE.jar

org.springframework.expression-3.0.5.RELEASE.jar

org.springframework.instrument-3.0.5.RELEASE.jar

org.springframework.instrument.tomcat-3.0.5.RELEASE.jar

org.springframework.jdbc-3.0.5.RELEASE.jar

org.springframework.jms-3.0.5.RELEASE.jar

org.springframework.orm-3.0.5.RELEASE.jar

org.springframework.oxm-3.0.5.RELEASE.jar

org.springframework.test-3.0.5.RELEASE.jar

org.springframework.transaction-3.0.5.RELEASE.jar

org.springframework.web-3.0.5.RELEASE.jar

org.springframework.web.portlet-3.0.5.RELEASE.jar

org.springframework.web.servlet-3.0.5.RELEASE.jar

org.springframework.web.struts-3.0.5.RELEASE.jar

struts2-spring-plugin-2.3.8.jar

?

3.1在web.xml中注册,应用程序启动时,Spring的监听器(Listener),其中context-param节点是初始化监听器的配置文件,即注册的bean从哪里获取,此处使用的是默认文件

WEB-INF

|___applicationContext.xml

?

??? <!-- 指明spring配置文件在何处 -->

<!--

??? <context-param>

?????? <param-name>contextConfigLocation</param-name>

?????? <param-value>classpath*:applicationContext*.xml</param-value>

??? </context-param>

-->

??? <listener>

?????? <listener-class>

?????????? org.springframework.web.context.ContextLoaderListener

?????? </listener-class>

??? </listener>

?

3.2编写测试用bean对象

???????? 此处模仿MVC三层机制,Action中去调用业务辑层bean对象,此对象有Spring创建实例化,Action中只进行声明。

3.2.1编业务逻辑层的接口,遵照面向接口编程的思想,所有业务逻辑层的类,都抽象成接口。

???????? 编写TestService接口如下

package com.jshand.ssi.service;

?

/**

?*@file_nameTestService.java

?*@project?????? ssi-release

?*@author? ?? jshand

?*@createDateJun2,2013? 2:08:53PM

?*@version ?? 1.0

?*http://www.jshand.com

?*

?*/

?

publicinterface TestService {

??? publicvoid service();

}

?

3.2.2编写TestService的实现类TestServiceImpl代码如下:

package com.jshand.ssi.service;

?

/**

?*@file_nameTestServiceImpl.java

?*@project?????? ssi-release

?*@author? ?? jshand

?*@createDateJun2,2013? 2:08:53PM

?*@version ?? 1.0

?*http://www.jshand.com

?*

?*/

?

publicclass TestServiceImpl implements TestService {

???

??? publicvoid service()throws Exception {

?????? System.out.println("this is service function ");

??? }

}

?

3.3 编写Spring配置文件,并在配置文件中注册TestAction和TestService实现类

??? 在WEB-INF目录下创建aplicationContext.xml文件文件内容如下:

<?xml version="1.0" encoding="UTF-8"?>

<beans xmlns="http://www.springframework.org/schema/beans"

??? xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"?

??? xmlns:aop="http://www.springframework.org/schema/aop"

??? xmlns:tx="http://www.springframework.org/schema/tx"?

??? xmlns:context="http://www.springframework.org/schema/context"?

??? xsi:schemaLocation="??

????????? http://www.springframework.org/schema/beans???

????????? http://www.springframework.org/schema/beans/spring-beans-2.0.xsd

????????? http://www.springframework.org/schema/aop???

????????? http://www.springframework.org/schema/aop/spring-aop-2.0.xsd??

????????? http://www.springframework.org/schema/tx???

????????? http://www.springframework.org/schema/tx/spring-tx-2.0.xsd">

??? ?

?

??? <bean id="testAction" class="com.jshand.ssi.action.TestAction">

?????? <property name="testService" ref="testService"></property>

??? </bean>

???

??? <bean id="testService" class="com.jshand.ssi.service.TestServiceImpl" />

???

</beans>

?

?

3.4在TestAction中声明service的应用,并添加set、get方法

声明的属性名称需要跟applicationContext.xml中注入的属性的那么相同

<property name="testService">testService</property>

在TestAction的execute方法中添加service的调用,testService.service();

Execute方法如下:

??? public String execute() throws Exception {

?????? System.out.println("execute Struts2 Action ...... && actionStr = "+actionStr);

??????

???????? testService.service();

?????? returnSUCCESS;

??? }

?

3.5 指定Struts2的Action初始化由Spring接管

由于我们添加Spring依赖的jar包是已经将struts2-spring-plugin-2.3.8.jar添加到应用的环境变量中,该jar包中包含了如下的struts-plugin.xml

<?xml version="1.0" encoding="UTF-8" ?>

<!--

/*

?* $Id: struts-plugin.xml 1221225 2011-12-20 12:22:28Z jogep $

?*

?* Licensed to the Apache Software Foundation (ASF) under one

?* or more contributor license agreements.? See the NOTICE file

?* distributed with this work for additional information

?* regarding copyright ownership.? The ASF licenses this file

?* to you under the Apache License, Version 2.0 (the

?* "License"); you may not use this file except in compliance

?* with the License.? You may obtain a copy of the License at

?*

?*? http://www.apache.org/licenses/LICENSE-2.0

?*

?* Unless required by applicable law or agreed to in writing,

?* software distributed under the License is distributed on an

?* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY

?* KIND, either express or implied.? See the License for the

?* specific language governing permissions and limitations

?* under the License.

?*/

-->

<!DOCTYPE struts PUBLIC

??? "-//Apache Software Foundation//DTD Struts Configuration 2.3//EN"

??? "http://struts.apache.org/dtds/struts-2.3.dtd">

???

<struts>

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

???

??? <!--? Make the Spring object factory the automatic default -->

??? <constant name="struts.objectFactory" value="spring" />

?

??? <constant name="struts.class.reloading.watchList" value="" />

??? <constant name="struts.class.reloading.acceptClasses" value="" />

??? <constant name="struts.class.reloading.reloadConfig" value="false" />

?

??? <package name="spring-default">

??????? <interceptors>

??????????? <interceptor name="autowiring" class="com.opensymphony.xwork2.spring.interceptor.ActionAutowiringInterceptor"/>

??????? </interceptors>

??? </package>???

</struts>

?

?

那么Struts2启动扫描扩展插件时,会把该插件装载。其中如下声明即将Sturts2的创建对象工厂指定给Spring。

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

如果没有导入此jar包则,需要在Struts2配置文件中进行相应的声明和处理。此处就不进行详细的讲述,感兴趣的可以上网搜索。下面打开浏览器重新敲入刚才的请求,


Ssi的调整(Struts2+Spring+Ibatis)
?

后台打印如下:

?


Ssi的调整(Struts2+Spring+Ibatis)
?

?

打印“execute Struts2 Action ...... && actionStr = testParameter”,说明进入到Action的execute方法,执行testService.service();时,testService对象我们在声明的时候并没有实例化,说明此处的testService对象是由Spring帮助我们创建的,至此整合Spring和Struts2的工作就完成了。

4 程序搭建---整合Ibatis到Spring、Struts2

4.1拷贝Ibatis基本的jar包到工程下的lib目录

ibatis-2.3.4.726.jar???? ibatis必备的jar包

commons-dbcp-1.4.jar???? dbcp连接池必备的包(便于整合Spring使用)

commons-pool-1.6.jar???? 便于整合Spring使用同上

mysql-connector-java-5.1.18-bin.jar??? mysql数据库的驱动

classes12.jar???????????????? Oracle数据库的驱动

4.2在前面的步骤没有添加log4j的属性文件,所以,所有的log4j都无法用,下面我们想使用log4j进行控制台信息的输出,所以此处添加log4j的属性文件,文件如下,此处不过多的对log4j进行介绍,感兴趣的朋友可以上网搜索相关内容,也可以参考我之前的一片文章

http://314649444.iteye.com/blog/1874814

#

# Log4J Settings for log4j 1.2.x (via jakarta-commons-logging)

#

# The five logging levels used by Log are (in order):

#

#?? 1. DEBUG (the least serious)

#?? 2. INFO

#?? 3. WARN

#?? 4. ERROR

#?? 5. FATAL (the most serious)

?

?

# Set root logger level to WARN and append to stdout

log4j.rootLogger=DEBUG,stdout

?

log4j.appender.stdout=org.apache.log4j.ConsoleAppender

log4j.appender.stdout.layout=org.apache.log4j.PatternLayout

?

# Pattern to output the caller's file name and line number.

log4j.appender.stdout.layout.ConversionPattern=%d%5p(%c:%L)-%m%n

?

# Print only messages of level ERROR or above in the package noModule.

log4j.logger.noModule=FATAL

?

# OpenSymphony Stuff

log4j.logger.com.opensymphony=INFO

log4j.logger.org.apache.struts2=INFO

?

# Spring Stuff

log4j.logger.org.springframework=INFO

?

?

?

4.3下面首先让我们在数据库中创建一张表,本文采用的是mysql数据库

建表sql如下:

CREATETABLE`person`

(

?

`personid`

VARCHAR(100)

NOT NULL,

`name`

VARCHAR(100)

NOT NULL,

`age`

VARCHAR(100)

NOT NULL,

`sex`

VARCHAR(100)

NOT NULL,

PRIMARYKEY

(`personid`)

?

)

ENGINE=MYISAM

;

4.4.1创建com.jshand.ssi.model包,并在包下创建person表对应的实体类如下:

package com.jshand.ssi.model;

?

/**

?*@file_namePerson.java

?*@project?????? ssi-release

?*@author? ?? jshand

?*@createDateJun26,2013? 10:32:22AM

?*@version ?? 1.0

?*http://www.jshand.com

?*

?*/

?

publicclass Person {

???

??? private String personid="";

??? private String name="";

??? private String age="";

??? private String sex="";

???

???

??? public String getPersonid() {

?????? returnpersonid;

??? }

??? publicvoid setPersonid(String personid) {

?????? this.personid = personid;

??? }

??? public String getName() {

?????? returnname;

??? }

??? publicvoid setName(String name) {

?????? this.name = name;

??? }

??? public String getAge() {

?????? returnage;

??? }

??? publicvoid setAge(String age) {

?????? this.age = age;

??? }

??? public String getSex() {

?????? returnsex;

??? }

??? publicvoid setSex(String sex) {

?????? this.sex = sex;

??? }

}

?

4.4.2添加实体类Person对应的查询语句配置文件

<?xml version="1.0" encoding="UTF-8" ?>

?

<!DOCTYPE sqlMap?????

??? PUBLIC "-//ibatis.apache.org//DTD SQL Map 2.0//EN"?????

??? "http://ibatis.apache.org/dtd/sql-map-2.dtd">

?

<sqlMap namespace="Person">

?

? <!-- Use type aliases to avoid typing the full classname every time. -->

? <typeAlias alias="Person" type="com.jshand.ssi.model.Person"/>

?

? <!-- Result maps describe the mapping between the columns returned

?????? from a query, and the class properties.? A result map isn't

?????? necessary if the columns (or aliases) match to the properties

?????? exactly. -->

? <resultMap id="Person" class="Person">

???? ?? <result property="personid"? column="personid" />

?????? <result property="name"???? column="name" />

?????? <result property="age"? column="age" />

?????? <result property="sex" column="sex" />

? </resultMap>

?

? <!-- A simpler select example without the result map.? Note the

?????? aliases to match the properties of the target result class. -->

? <select id="selectPersonById" parameterClass="String" resultClass="Person">

?? select personid, name, age, sex? from Person where personid= #id#

? </select>

??

? <!-- Insert example, using the Person parameter class -->

? <insert id="insertPerson" parameterClass="Person">

??? insert into Person

? ????? (personid, name, age, sex )

??? values

? ????? (#personid#, #name#, #age#, #sex# )

? </insert>

?

? <!-- Update example, using the Person parameter class -->

? <update id="updatePerson" parameterClass="Person">

??? update Person

?? set name = #name#,

?????? age = #age#,

?????? sex = #sex#,

??? where personid = #personid#

? </update>

?

? <!-- Delete example, using an integer as the parameter class -->

? <delete id="deletePersonById" parameterClass="String">

??? delete from Person where personid = #personid#

? </delete>

?

</sqlMap>

?

?

?

4.5 添加一个sqlMapping.properties文件(当然文件名是可以随便起的,只要Spring读取的时候保持一致即可)用于Spring读取配置连接数据库配置包括驱动类,Url、用户名、密码等,内容如下:

#MY SQL

jdbc.driverClassName=com.mysql.jdbc.Driver

jdbc.url=jdbc:mysql://127.0.0.1:3306/test

jdbc.username=root

jdbc.password=root

?

4.6配置sqlMapConfig.xml文件,并在其中注册 TB_Person.xml的SQL

?

<?xml version="1.0" encoding="UTF-8"?>

?

<!DOCTYPE sqlMapConfig PUBLIC "-//iBATIS.com//DTD SQL Map Config 2.0//EN" "http://www.ibatis.com/dtd/sql-map-config-2.dtd">

?

<sqlMapConfig>

?

??? <settings cacheModelsEnabled="true" enhancementEnabled="true"

?????? lazyLoadingEnabled="true" errorTracingEnabled="false" maxRequests="32"

?????? maxSessions="10" maxTransactions="5" useStatementNamespaces="false" />

?

??? <sqlMap resource="com/jshand/ssi/model/TB_Person.xml" />

???

</sqlMapConfig>

?

?

4.7 在Spring配置文件中声明数据源、sqlMapClient操作如下:

在Spring配置文件中添加如下xml节点

?? <!-- 方便查找类路径下 sqlMapping.properties 并读取到内存中-->

??? <bean id="propertyConfigurer"

??? class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">

?????? <property name="locations">

?????????? <list>

????????????? <value>classpath:sqlMapping.properties</value>

?????????? </list>

?????? </property>

??? </bean>

?

??? <!-- 数据源配制 -->

??? <bean id="dataSource"

?????? class="org.apache.commons.dbcp.BasicDataSource">

?????? <property name="driverClassName">

?????????? <value>${jdbc.driverClassName}</value>

?????? </property>

?????? <property name="url">

?????????? <value>${jdbc.url}</value>

?????? </property>

?????? <property name="username">

?????????? <value>${jdbc.username}</value>

?????? </property>

?????? <property name="password">

?????????? <value>${jdbc.password}</value>

?????? </property>

??? </bean>

?

??? <!-- 此处应注入ibatis配置文件,而非sqlMap文件,否则会出现“there is no statement.....异常” -->

??? <bean id="sqlMapClient"

?????? class="org.springframework.orm.ibatis.SqlMapClientFactoryBean">

?????? <property name="configLocation">

?????????? <value>classpath:SqlMapConfig.xml</value>

?????? </property>

?????? <property name="dataSource" ref="dataSource" />

??? </bean>

?

?

4.8 编写PersonDao类,并继承由Spring提供的SqlMapClientDaoSupport类

实现增删改查的操作,PersonDto内容如下:

package com.jshand.ssi.dao;

?

import java.sql.SQLException;

?

import org.springframework.orm.ibatis.support.SqlMapClientDaoSupport;

?

import com.jshand.ssi.model.Person;

?

/**

?*@file_namePersonDao.java

?*@project?????? ssi-release

?*@author? ?? jshand

?*@createDateJun26,2013? 11:05:55AM

?*@version ?? 1.0

?*http://www.jshand.com

?*

?*/

?

publicclass PersonDao extends SqlMapClientDaoSupport{

???

??? /**

??? ?*按照主键查询

??? ?*@paramid

??? ?*@return

??? ?*@throwsSQLException

??? ?*/

??? public? Object selectObjectById? (String id) throws SQLException {

?????? System.out.println("ID = "+id);

?????? return? getSqlMapClientTemplate().queryForObject("selectPersonById", id);

??? }

???

??? /**

??? ?*插入一条数据

??? ?*@paramobject

??? ?*@return

??? ?*@throwsSQLException

??? ?*/

??? public Object insertObject (Person person) throws SQLException {

?????? return getSqlMapClientTemplate().insert("insertPerson", person);

??? }

???

??? /**

??? ?*按主键更新一条记录

??? ?*@paramobject

??? ?*@return

??? ?*@throwsSQLException

??? ?*/

??? public? int updateObject (Person person) throws SQLException {

?????? return getSqlMapClientTemplate().update("updatePerson", person);

??? }

???

??? /**

??? ?*按主键删除一条记录

??? ?*@paramid

??? ?*@return

??? ?*@throwsSQLException

??? ?*/

??? public? int deleteObject (String id) throws SQLException {

?????? return getSqlMapClientTemplate().delete("deletePerson", id);

??? }

}

?

?

?

4.9 将PersonDao类交给Spring管理,即在Spring配置文件中注册PersonDao类

并将PersonDao注入到上面步骤的testService里面去,最后Spring的完整内容如下:

?

<?xml version="1.0" encoding="UTF-8"?>

<beans xmlns="http://www.springframework.org/schema/beans"

??? xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"?

??? xmlns:aop="http://www.springframework.org/schema/aop"

??? xmlns:tx="http://www.springframework.org/schema/tx"?

??? xmlns:context="http://www.springframework.org/schema/context"?

??? xsi:schemaLocation="??

????????? http://www.springframework.org/schema/beans???

????????? http://www.springframework.org/schema/beans/spring-beans-2.0.xsd

????????? http://www.springframework.org/schema/aop???

????????? http://www.springframework.org/schema/aop/spring-aop-2.0.xsd??

????????? http://www.springframework.org/schema/tx???

????????? http://www.springframework.org/schema/tx/spring-tx-2.0.xsd">

?

??? ? <!-- 方便查找类路径下 sqlMapping.properties 并读取到内存中-->

??? <bean id="propertyConfigurer"

??? ??? class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">

?????? <property name="locations">

?????????? <list>

????????????? <value>classpath:sqlMapping.properties</value>

?????????? </list>

?????? </property>

??? </bean>

?

??? <!-- 数据源配制 -->

??? <bean id="dataSource"

?????? class="org.apache.commons.dbcp.BasicDataSource">

?????? <property name="driverClassName">

?????????? <value>${jdbc.driverClassName}</value>

?????? </property>

?????? <property name="url">

?????????? <value>${jdbc.url}</value>

?????? </property>

?????? <property name="username">

?????????? <value>${jdbc.username}</value>

?????? </property>

?????? <property name="password">

?????????? <value>${jdbc.password}</value>

?????? </property>

??? </bean>

?

??? <!-- 此处应注入ibatis配置文件,而非sqlMap文件,否则会出现“there is no statement.....异常” -->

??? <bean id="sqlMapClient"

?????? class="org.springframework.orm.ibatis.SqlMapClientFactoryBean">

?????? <property name="configLocation">

?????????? <value>classpath:SqlMapConfig.xml</value>

?????? </property>

?????? <property name="dataSource" ref="dataSource" />

??? </bean>

??? ?

??? <bean id="testAction" class="com.jshand.ssi.action.TestAction">

?????? <property name="testService" ref="testService"></property>

??? </bean>

???

??? <bean id="testService" class="com.jshand.ssi.service.TestServiceImpl" >

?????????? <property name="personDao" ref="personDao"></property>

??? </bean>

???

??? <bean id="personDao" class="com.jshand.ssi.dao.PersonDao" />

???

</beans>

?

4.10下面就是看效果的时候了,我们在testService中声明一个personDao属性,名称与Spring中注册的com.jshand.ssi.dao.PersonDao类的id保持一致即可并设置setter、getter。personDao属性只需声明即可不用实例化,因为此处验证Spring托管

?

因为之前我们整合Spring、Struts2的时候Struts2的Action接收访问后会调用TestServiceImpl的service方法,所以我们在service方法中调用PersonDao的方法插入和查询方法已验证配置是否成功。

内容如下:

package com.jshand.ssi.service;

?

import java.sql.SQLException;

?

import org.apache.log4j.Logger;

?

import com.jshand.ssi.dao.PersonDao;

import com.jshand.ssi.model.Person;

?

/**

?*@file_nameTestService.java

?*@project?????? ssi-release

?*@author? ?? jshand

?*@createDateJun2,2013? 2:08:53PM

?*@version ?? 1.0

?*http://www.jshand.com

?*

?*/

?

publicclass TestServiceImpl implements TestService {

??? Logger logger = Logger.getLogger(TestServiceImpl.class);

??? private PersonDao personDao = null;

???

??? publicvoid service() throws SQLException{

?????? System.out.println("this is service function ");

?????? Person personInsert? = new Person();

?????? personInsert.setPersonid("001");

?????? personInsert.setName("Jhoon");

?????? personInsert.setAge("23");

?????? personInsert.setSex("男");

?????? personDao.insertObject(personInsert);

??????

?????? logger.debug("插入成功");

?????? Person personQuery = null;

?????? personQuery = (Person) personDao.selectObjectById("001");

??????

?????? logger.debug("查询结果 Name = "+personQuery.getName()+"\t age = "+personQuery.getAge()+"\tsex = "+personQuery.getSex());

??????

??????

??? }

?

??? public PersonDao getPersonDao() {

?????? returnpersonDao;

??? }

?

??? publicvoid setPersonDao(PersonDao personDao) {

?????? this.personDao = personDao;

??? }

}

?

?

?

?

下面我们来发布一下程序,并查看执行结果:


Ssi的调整(Struts2+Spring+Ibatis)
?
?
?

控制台打印如上,说明配置成功

5 将ibatis的事物交给Spring管理,

即将数据源的事物交给Spring处理,然后Spring在一个指定的切面打开事物,切面执行完之后如果产生异常就将事物回滚,否则提交。

具体配置,在Spring配置文件添加如下xml节点,这里的切面为

public * com.jshand.ssi.services.*.*(..)? 即所有Service类的所有方法

<!-- 事务的定义 -->

??? <bean id="transactionManager"

??? ??? class="org.springframework.jdbc.datasource.DataSourceTransactionManager">

?????? <property name="dataSource">

?????????? <ref local="dataSource" />

?????? </property>

??? </bean>

?

??? <aop:config>

?????? <aop:pointcut id="daoMethods" expression="execution(public * com.jshand.ssi.services.*.*(..))" />

?????? <aop:advisor advice-ref="txAdvice" pointcut-ref="daoMethods" />

??? </aop:config>

?

??? <tx:advice id="txAdvice" transaction-manager="transactionManager">

?????? <tx:attributes>

?????????? <tx:method name="*" rollback-for="Exception" propagation="REQUIRED" />

?????? </tx:attributes>

??? </tx:advice>

?

下面我们将TestServiceImpl类的service方法改造一下,因为我们之前已经插入过一条id为001的对象,现在我们先插入一条002的后插入一条001的记录,因为id是主键所以在此插入的时候001是不能插入的,按照事物的一致性原则,如果成功开始事物,并且回滚的话这两条记录都应该插入不成功,下面来执行看下效果

改造后如下:

??? publicvoid service() throws SQLException{

??????

?????? Person personInsert2? = new Person();

?????? personInsert2.setPersonid("002");

?????? personInsert2.setName("Jhoon");

?????? personInsert2.setAge("23");

?????? personInsert2.setSex("男");

?????? personDao.insertObject(personInsert2);

??????

?????? Person personInsert1? = new Person();

?????? personInsert1.setPersonid("001");

?????? personInsert1.setName("Jhoon");

?????? personInsert1.setAge("23");

?????? personInsert1.setSex("男");

?????? personDao.insertObject(personInsert1);

??????

??? }

?

执行后:后台报错如下,

execute Struts2 Action ...... && actionStr = com.jshand.ssi.action.TestAction

2013-06-26 14:30:58,670 DEBUG (java.sql.Connection:27) - {conn-100000} Connection

2013-06-26 14:30:58,701 DEBUG (java.sql.Connection:27) - {conn-100000} Preparing Statement:????? insert into Person???? (personid, name, age, sex )? values???? (?, ?, ?, ? )??

2013-06-26 14:30:58,780 DEBUG (java.sql.PreparedStatement:27) - {pstm-100001} Executing Statement:????? insert into Person???? (personid, name, age, sex )? values???? (?, ?, ?, ? )??

2013-06-26 14:30:58,780 DEBUG (java.sql.PreparedStatement:27) - {pstm-100001} Parameters: [002, Jhoon, 23, 男]

2013-06-26 14:30:58,780 DEBUG (java.sql.PreparedStatement:27) - {pstm-100001} Types: [java.lang.String, java.lang.String, java.lang.String, java.lang.String]

2013-06-26 14:31:21,026 DEBUG (java.sql.Connection:27) - {conn-100002} Connection

2013-06-26 14:31:21,026 DEBUG (java.sql.Connection:27) - {conn-100002} Preparing Statement:????? insert into Person???? (personid, name, age, sex )? values???? (?, ?, ?, ? )??

2013-06-26 14:31:21,041 DEBUG (java.sql.PreparedStatement:27) - {pstm-100003} Executing Statement:????? insert into Person???? (personid, name, age, sex )? values???? (?, ?, ?, ? )??

2013-06-26 14:31:21,041 DEBUG (java.sql.PreparedStatement:27) - {pstm-100003} Parameters: [001, Jhoon, 23, 男]

2013-06-26 14:31:21,058 DEBUG (java.sql.PreparedStatement:27) - {pstm-100003} Types: [java.lang.String, java.lang.String, java.lang.String, java.lang.String]

2013-06-26 14:31:21,214? INFO (org.springframework.beans.factory.xml.XmlBeanDefinitionReader:315) - Loading XML bean definitions from class path resource [org/springframework/jdbc/support/sql-error-codes.xml]

2013-06-26 14:31:21,495? INFO (org.springframework.jdbc.support.SQLErrorCodesFactory:126) - SQLErrorCodes loaded: [DB2, Derby, H2, HSQL, Informix, MS-SQL, MySQL, Oracle, PostgreSQL, Sybase]

2013-06-26 14:31:24,396 ERROR (org.apache.struts2.dispatcher.Dispatcher:38) - Exception occurred during processing request: SqlMapClient operation; SQL [];??

--- The error occurred in com/jshand/ssi/model/TB_Person.xml.?

--- The error occurred while applying a parameter map.?

--- Check the insertPerson-InlineParameterMap.?

--- Check the statement (update failed).?

--- Cause: com.mysql.jdbc.exceptions.jdbc4.MySQLIntegrityConstraintViolationException: Duplicate entry '001' for key 1; nested exception is com.ibatis.common.jdbc.exception.NestedSQLException:??

--- The error occurred in com/jshand/ssi/model/TB_Person.xml.?

--- The error occurred while applying a parameter map.?

--- Check the insertPerson-InlineParameterMap.?

--- Check the statement (update failed).?

--- Cause: com.mysql.jdbc.exceptions.jdbc4.MySQLIntegrityConstraintViolationException: Duplicate entry '001' for key 1

org.springframework.dao.DuplicateKeyException: SqlMapClient operation; SQL [];??

--- The error occurred in com/jshand/ssi/model/TB_Person.xml.?

--- The error occurred while applying a parameter map.?

--- Check the insertPerson-InlineParameterMap.?

--- Check the statement (update failed).?

--- Cause: com.mysql.jdbc.exceptions.jdbc4.MySQLIntegrityConstraintViolationException: Duplicate entry '001' for key 1; nested exception is com.ibatis.common.jdbc.exception.NestedSQLException:??

--- The error occurred in com/jshand/ssi/model/TB_Person.xml.?

--- The error occurred while applying a parameter map.?

--- Check the insertPerson-InlineParameterMap.?

--- Check the statement (update failed).?

--- Cause: com.mysql.jdbc.exceptions.jdbc4.MySQLIntegrityConstraintViolationException: Duplicate entry '001' for key 1

??? at org.springframework.jdbc.support.SQLErrorCodeSQLExceptionTranslator.doTranslate(SQLErrorCodeSQLExceptionTranslator.java:241)

??? at org.springframework.jdbc.support.AbstractFallbackSQLExceptionTranslator.translate(AbstractFallbackSQLExceptionTranslator.java:72)

??? at org.springframework.orm.ibatis.SqlMapClientTemplate.execute(SqlMapClientTemplate.java:203)

??? at org.springframework.orm.ibatis.SqlMapClientTemplate.insert(SqlMapClientTemplate.java:364)

??? at com.jshand.ssi.dao.PersonDao.insertObject(PersonDao.java:39)

??? at com.jshand.ssi.service.TestServiceImpl.service(TestServiceImpl.java:38)

??? at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)

??? at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)

??? at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)

??? at java.lang.reflect.Method.invoke(Method.java:597)

??? at org.springframework.aop.support.AopUtils.invokeJoinpointUsingReflection(AopUtils.java:309)

??? at org.springframework.aop.framework.ReflectiveMethodInvocation.invokeJoinpoint(ReflectiveMethodInvocation.java:183)

??? at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:150)

??? at org.springframework.transaction.interceptor.TransactionInterceptor.invoke(TransactionInterceptor.java:110)

??? at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:172)

??? at org.springframework.aop.interceptor.ExposeInvocationInterceptor.invoke(ExposeInvocationInterceptor.java:89)

??? at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:172)

??? at org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:202)

??? at $Proxy4.service(Unknown Source)

??? at com.jshand.ssi.action.TestAction.execute(TestAction.java:24)

??? at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)

??? at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)

??? at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)

??? at java.lang.reflect.Method.invoke(Method.java:597)

??? at com.opensymphony.xwork2.DefaultActionInvocation.invokeAction(DefaultActionInvocation.java:446)

??? at com.opensymphony.xwork2.DefaultActionInvocation.invokeActionOnly(DefaultActionInvocation.java:285)

??? at com.opensymphony.xwork2.DefaultActionInvocation.invoke(DefaultActionInvocation.java:248)

??? at org.apache.struts2.interceptor.debugging.DebuggingInterceptor.intercept(DebuggingInterceptor.java:256)

??? at com.opensymphony.xwork2.DefaultActionInvocation.invoke(DefaultActionInvocation.java:242)

??? at com.opensymphony.xwork2.interceptor.DefaultWorkflowInterceptor.doIntercept(DefaultWorkflowInterceptor.java:176)

??? at com.opensymphony.xwork2.interceptor.MethodFilterInterceptor.intercept(MethodFilterInterceptor.java:98)

??? at com.opensymphony.xwork2.DefaultActionInvocation.invoke(DefaultActionInvocation.java:242)

??? at com.opensymphony.xwork2.validator.ValidationInterceptor.doIntercept(ValidationInterceptor.java:265)

??? at org.apache.struts2.interceptor.validation.AnnotationValidationInterceptor.doIntercept(AnnotationValidationInterceptor.java:68)

??? at com.opensymphony.xwork2.interceptor.MethodFilterInterceptor.intercept(MethodFilterInterceptor.java:98)

??? at com.opensymphony.xwork2.DefaultActionInvocation.invoke(DefaultActionInvocation.java:242)

??? at com.opensymphony.xwork2.interceptor.ConversionErrorInterceptor.intercept(ConversionErrorInterceptor.java:138)

??? at com.opensymphony.xwork2.DefaultActionInvocation.invoke(DefaultActionInvocation.java:242)

??? at com.opensymphony.xwork2.interceptor.ParametersInterceptor.doIntercept(ParametersInterceptor.java:238)

??? at com.opensymphony.xwork2.interceptor.MethodFilterInterceptor.intercept(MethodFilterInterceptor.java:98)

??? at com.opensymphony.xwork2.DefaultActionInvocation.invoke(DefaultActionInvocation.java:242)

??? at com.opensymphony.xwork2.interceptor.ParametersInterceptor.doIntercept(ParametersInterceptor.java:238)

??? at com.opensymphony.xwork2.interceptor.MethodFilterInterceptor.intercept(MethodFilterInterceptor.java:98)

??? at com.opensymphony.xwork2.DefaultActionInvocation.invoke(DefaultActionInvocation.java:242)

??? at com.opensymphony.xwork2.interceptor.StaticParametersInterceptor.intercept(StaticParametersInterceptor.java:191)

??? at com.opensymphony.xwork2.DefaultActionInvocation.invoke(DefaultActionInvocation.java:242)

??? at org.apache.struts2.interceptor.MultiselectInterceptor.intercept(MultiselectInterceptor.java:73)

??? at com.opensymphony.xwork2.DefaultActionInvocation.invoke(DefaultActionInvocation.java:242)

??? at org.apache.struts2.interceptor.CheckboxInterceptor.intercept(CheckboxInterceptor.java:91)

??? at com.opensymphony.xwork2.DefaultActionInvocation.invoke(DefaultActionInvocation.java:242)

??? at org.apache.struts2.interceptor.FileUploadInterceptor.intercept(FileUploadInterceptor.java:252)

??? at com.opensymphony.xwork2.DefaultActionInvocation.invoke(DefaultActionInvocation.java:242)

??? at com.opensymphony.xwork2.interceptor.ModelDrivenInterceptor.intercept(ModelDrivenInterceptor.java:100)

??? at com.opensymphony.xwork2.DefaultActionInvocation.invoke(DefaultActionInvocation.java:242)

??? at com.opensymphony.xwork2.interceptor.ScopedModelDrivenInterceptor.intercept(ScopedModelDrivenInterceptor.java:141)

??? at com.opensymphony.xwork2.DefaultActionInvocation.invoke(DefaultActionInvocation.java:242)

??? at com.opensymphony.xwork2.interceptor.ChainingInterceptor.intercept(ChainingInterceptor.java:145)

??? at com.opensymphony.xwork2.DefaultActionInvocation.invoke(DefaultActionInvocation.java:242)

??? at com.opensymphony.xwork2.interceptor.PrepareInterceptor.doIntercept(PrepareInterceptor.java:171)

??? at com.opensymphony.xwork2.interceptor.MethodFilterInterceptor.intercept(MethodFilterInterceptor.java:98)

??? at com.opensymphony.xwork2.DefaultActionInvocation.invoke(DefaultActionInvocation.java:242)

??? at com.opensymphony.xwork2.interceptor.I18nInterceptor.intercept(I18nInterceptor.java:176)

??? at com.opensymphony.xwork2.DefaultActionInvocation.invoke(DefaultActionInvocation.java:242)

??? at org.apache.struts2.interceptor.ServletConfigInterceptor.intercept(ServletConfigInterceptor.java:164)

??? at com.opensymphony.xwork2.DefaultActionInvocation.invoke(DefaultActionInvocation.java:242)

??? at com.opensymphony.xwork2.interceptor.AliasInterceptor.intercept(AliasInterceptor.java:193)

??? at com.opensymphony.xwork2.DefaultActionInvocation.invoke(DefaultActionInvocation.java:242)

??? at com.opensymphony.xwork2.interceptor.ExceptionMappingInterceptor.intercept(ExceptionMappingInterceptor.java:187)

??? at com.opensymphony.xwork2.DefaultActionInvocation.invoke(DefaultActionInvocation.java:242)

??? at org.apache.struts2.impl.StrutsActionProxy.execute(StrutsActionProxy.java:54)

??? at org.apache.struts2.dispatcher.Dispatcher.serviceAction(Dispatcher.java:544)

??? at org.apache.struts2.dispatcher.ng.ExecuteOperations.executeAction(ExecuteOperations.java:77)

??? at org.apache.struts2.dispatcher.ng.filter.StrutsPrepareAndExecuteFilter.doFilter(StrutsPrepareAndExecuteFilter.java:91)

??? at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:235)

??? at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:206)

??? at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:233)

??? at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:191)

??? at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:127)

??? at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:103)

??? at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:109)

??? at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:293)

??? at org.apache.coyote.http11.Http11Processor.process(Http11Processor.java:861)

??? at org.apache.coyote.http11.Http11Protocol$Http11ConnectionHandler.process(Http11Protocol.java:606)

??? at org.apache.tomcat.util.net.JIoEndpoint$Worker.run(JIoEndpoint.java:489)

??? at java.lang.Thread.run(Thread.java:619)

Caused by: com.ibatis.common.jdbc.exception.NestedSQLException:??

--- The error occurred in com/jshand/ssi/model/TB_Person.xml.?

--- The error occurred while applying a parameter map.?

--- Check the insertPerson-InlineParameterMap.?

--- Check the statement (update failed).?

--- Cause: com.mysql.jdbc.exceptions.jdbc4.MySQLIntegrityConstraintViolationException: Duplicate entry '001' for key 1

??? at com.ibatis.sqlmap.engine.mapping.statement.MappedStatement.executeUpdate(MappedStatement.java:107)

??? at com.ibatis.sqlmap.engine.impl.SqlMapExecutorDelegate.insert(SqlMapExecutorDelegate.java:393)

??? at com.ibatis.sqlmap.engine.impl.SqlMapSessionImpl.insert(SqlMapSessionImpl.java:82)

??? at org.springframework.orm.ibatis.SqlMapClientTemplate$8.doInSqlMapClient(SqlMapClientTemplate.java:366)

??? at org.springframework.orm.ibatis.SqlMapClientTemplate.execute(SqlMapClientTemplate.java:200)

??? ... 82 more

Caused by: com.mysql.jdbc.exceptions.jdbc4.MySQLIntegrityConstraintViolationException: Duplicate entry '001' for key 1

??? at sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method)

??? at sun.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:39)

??? at sun.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:27)

??? at java.lang.reflect.Constructor.newInstance(Constructor.java:513)

??? at com.mysql.jdbc.Util.handleNewInstance(Util.java:411)

??? at com.mysql.jdbc.Util.getInstance(Util.java:386)

??? at com.mysql.jdbc.SQLError.createSQLException(SQLError.java:1039)

??? at com.mysql.jdbc.MysqlIO.checkErrorPacket(MysqlIO.java:3609)

??? at com.mysql.jdbc.MysqlIO.checkErrorPacket(MysqlIO.java:3541)

??? at com.mysql.jdbc.MysqlIO.sendCommand(MysqlIO.java:2002)

??? at com.mysql.jdbc.MysqlIO.sqlQueryDirect(MysqlIO.java:2163)

??? at com.mysql.jdbc.ConnectionImpl.execSQL(ConnectionImpl.java:2624)

??? at com.mysql.jdbc.PreparedStatement.executeInternal(PreparedStatement.java:2127)

??? at com.mysql.jdbc.PreparedStatement.execute(PreparedStatement.java:1362)

??? at org.apache.commons.dbcp.DelegatingPreparedStatement.execute(DelegatingPreparedStatement.java:172)

??? at org.apache.commons.dbcp.DelegatingPreparedStatement.execute(DelegatingPreparedStatement.java:172)

??? at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)

??? at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)

??? at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)

??? at java.lang.reflect.Method.invoke(Method.java:597)

??? at com.ibatis.common.jdbc.logging.PreparedStatementLogProxy.invoke(PreparedStatementLogProxy.java:62)

??? at $Proxy7.execute(Unknown Source)

??? at com.ibatis.sqlmap.engine.execution.SqlExecutor.executeUpdate(SqlExecutor.java:80)

??? at com.ibatis.sqlmap.engine.mapping.statement.MappedStatement.sqlExecuteUpdate(MappedStatement.java:216)

??? at com.ibatis.sqlmap.engine.mapping.statement.MappedStatement.executeUpdate(MappedStatement.java:94)

??? ... 86 more

?

表中的数据还是之前插入的一条,新插入的两条都没成功。


Ssi的调整(Struts2+Spring+Ibatis)
?

下面我们将service方法再次改造一下,将001的id换成003,我们DEBUG执行下

改造后的service方法如下:

??? ??? publicvoid service() throws SQLException{

??????

?????? Person personInsert2? = new Person();

?????? personInsert2.setPersonid("002");

?????? personInsert2.setName("Jhoon");

?????? personInsert2.setAge("23");

?????? personInsert2.setSex("男");

?????? personDao.insertObject(personInsert2);

??????

?????? Person personInsert3? = new Person();

?????? personInsert3.setPersonid("003");

?????? personInsert3.setName("Jhoon");

?????? personInsert3.setAge("23");

?????? personInsert3.setSex("男");

?????? personDao.insertObject(personInsert3);

??????

??? }

我们在Person personInsert3? = new Person();句话上打一个端点,当我们执行到这酷话的时候如果没有开启事物那么id为002的记录应该是插入到数据库中的,在这里测试事物的正确与否:

开始执行:


Ssi的调整(Struts2+Spring+Ibatis)
?

说明插入002的操作已经执行这个时候我们看一下数据库:


Ssi的调整(Struts2+Spring+Ibatis)
?

发现记录中不存在002的id所以证明事物成功开启未提交,当把service全部执行完毕后,在查看数据库发现,id为002、003的记录都已经成功插入,说明数据库事物是正确的。

?

?

总结:至此整合Struts2、Spring、Ibatis已经结束了,需要值得注意的是,

mysql中表创建时需要选择存储引擎,本文中用的是mysql5.4提供七种种Storage Engine

,而其中MyISAM对事物的支持比较特殊,所以没有使用个,本文中建表使用?? InnoDB作为存储引擎的。

?

?

?

本文中搭建好的应用可以到下面的地址下载:

http://test-jshand.googlecode.com/files/ssi-release.rar

热点排行