作者:聂勇 欢迎转载,请保留作者信息并说明文章来源!
已经近三年没有使用Spring,IBatis,Hibernate等框架,同部门的另一个项目组赶进度,我们整个项目组都加入开发的行列。Spring,IBatis如何配置,如何使用等都忘光了,上网找一找资料,看看以前写的项目,花了近半天,完成了 Spring + IBatis + Struts2 的集成。好脑袋不如写博客,记录下来,备用。
集成Spring IBatis 的整个过程如下:
引入所有需要的*.jar文件。
- 配置web.xml。
- 配置log4j。
- 配置C3P0数据库连接池。
- 配置Spring以及与IBatis集成。
- 建立数据库表结构及其domain类和配置
- 编写dao类。
- 编写单元测试类。
一、所需类库 | Required jar list
c3p0-0.9.1.2.jar // 连接池实现类库
commons-logging-1.0.4.jar
ibatis-2.3.4.726.jar
log4j-1.2.15.jar
ojdbc14-10g.jar // Oracle JDBC驱动类库
spring-2.5.6.jar
spring-test.jar // 单元测试需要用到的类库
junit-4.4.jar // 单元测试需要用到的类库
1、在web.xml中增加如下内容:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| <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> <listener> <listener-class>org.springframework.web.util.IntrospectorCleanupListener</listener-class> </listener>
|
1、在src/main/resouces 目录下建立 log4j.xml,其内容如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| <?xml version="1.0"?> <!DOCTYPE log4j:configuration SYSTEM "log4j.dtd"> <log4j:configuration xmlns:log4j="http://jakarta.apache.org/log4j/"> <appender name="sis_log_file" class="org.apache.log4j.DailyRollingFileAppender"> <param name="File" value="log/sis.log" /> <param name="DatePattern" value=".yyyyMMddHH" /> <layout class="org.apache.log4j.PatternLayout"> <param name="ConversionPattern" value="%d %p %t %C{3} %m%n" /> </layout> </appender> <root> <level value="info" /> <appender-ref ref="sis_log_file" /> </root> </log4j:configuration>
|
1、在src/main/resouces 目录下建立 jdbc.properties,其内容如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| #c3p0 数据源配置; jdbc.driverClassName=oracle.jdbc.OracleDriver jdbc.url=jdbc:oracle:thin:@192.168.0.8:1521:orcl10 jdbc.username=aofeng jdbc.password=aofeng # 连接池完成初始化后建立的连接数量 initialPoolSize=0 # 连接池的最小连接数量 minPoolSize=2 # 连接池的最大连接数量 maxPoolSize=10 # 连接的最大空闲时间(单位:秒),当连接空闲超时此时间后,连接将被回收 maxIdleTime=1000 idleConnectionTestPeriod=1000
|
2、然后在Spring的配置ApplicationContext.xml中添加如下配置:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| <context:property-placeholder location="classpath:jdbc.properties"/> <bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource" destroy-method="close"> <property name="driverClass" value="${jdbc.driverClassName}"/> <property name="jdbcUrl" value="${jdbc.url}"/> <property name="user" value="${jdbc.username}"/> <property name="password" value="${jdbc.password}"/> <property name="maxIdleTime" value="${maxIdleTime}"/> <property name="maxPoolSize" value="${maxPoolSize}"/> <property name="minPoolSize" value="${minPoolSize}"/> <property name="initialPoolSize" value="${initialPoolSize}"/> <property name="idleConnectionTestPeriod" value="${idleConnectionTestPeriod}"/> </bean>
|
1、在src/main/resouces 目录下建立IBatis的SQL映射配置文件SqlMapConfig.xml,其内容如下:
1 2 3 4 5 6 7 8 9 10
| <?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE sqlMapConfig PUBLIC "-//ibatis.apache.org//DTD SQL Map Config 2.0//EN" "http://ibatis.apache.org/dtd/sql-map-config-2.dtd"> <sqlMapConfig> <settings cacheModelsEnabled="true" useStatementNamespaces="true"/> </sqlMapConfig>
|
2、在Spring的配置ApplicationContext.xml中添加如下配置:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| <bean id="sqlMapClient" class="org.springframework.orm.ibatis.SqlMapClientFactoryBean"> <property name="dataSource" ref="dataSource" /> <property name="configLocation" value="classpath:SqlMapConfig.xml" /> </bean> <bean id="sqlMapClientTemplate" class="org.springframework.orm.ibatis.SqlMapClientTemplate"> <property name="sqlMapClient" ref="sqlMapClient"></property> </bean> <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"> <property name="dataSource" ref="dataSource" /> </bean> <tx:annotation-driven transaction-manager="transactionManager" />
|
六、建立数据表结构及Domain类 | Create table and its corresponding domain class
1、测试Spring与IBatis集成的表结构如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| create table USER ( USER_ID NUMBER(10) not null, USER_TYPE NUMBER(2), USER_STATUS NUMBER(1) not null, USER_NAME VARCHAR2(11) not null, USER_PASSWD VARCHAR2(20) not null, CREATE_TIME DATE not null, UPDATE_TIME DATE not null, LAST_LOGIN_TIME DATE ); alter table USER add constraint PK_USER primary key (USER_ID) using index;
|
2、建立User表对应的domain类:User.java,其内容如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199
| * 建立时间:2011-3-19 */ package cn.aofeng.sis.domain; import java.util.Date; * 表USER对应的持久层POJO. * * @author 傲风 <a href="mailto:aofengblog@163.com">aofengblog@163.com</a> */ public class User { * This field corresponds to the database column USER.USER_ID */ private Long userId; * This field corresponds to the database column USER.USER_TYPE */ private Short userType; * This field corresponds to the database column USER.USER_STATUS */ private Short userStatus; * This field corresponds to the database column USER.USER_NAME */ private String userName; * This field corresponds to the database column USER.USER_PASSWD */ private String userPasswd; * This field corresponds to the database column USER.CREATE_TIME */ private Date createTime; * This field corresponds to the database column USER.UPDATE_TIME */ private Date updateTime; * This field corresponds to the database column USER.LAST_LOGIN_TIME */ private Date lastLoginTime; * This method returns the value of the database column USER.USER_ID * * @return the value of USER.USER_ID */ public Long getUserId() { return userId; } * This method sets the value of the database column USER.USER_ID * * @param userId the value for USER.USER_ID */ public void setUserId(Long userId) { this.userId = userId; } * This method returns the value of the database column USER.USER_TYPE * * @return the value of USER.USER_TYPE */ public Short getUserType() { return userType; } * This method sets the value of the database column USER.USER_TYPE * * @param userType the value for USER.USER_TYPE */ public void setUserType(Short userType) { this.userType = userType; } * This method returns the value of the database column USER.USER_STATUS * * @return the value of USER.USER_STATUS */ public Short getUserStatus() { return userStatus; } * This method sets the value of the database column USER.USER_STATUS * * @param userStatus the value for USER.USER_STATUS */ public void setUserStatus(Short userStatus) { this.userStatus = userStatus; } * This method returns the value of the database column USER.USER_NAME * * @return the value of USER.USER_NAME */ public String getUserName() { return userName; } * This method sets the value of the database column USER.USER_NAME * * @param userName the value for USER.USER_NAME */ public void setUserName(String userName) { this.userName = userName; } * This method returns the value of the database column USER.USER_PASSWD * * @return the value of USER.USER_PASSWD */ public String getUserPasswd() { return userPasswd; } * This method sets the value of the database column USER.USER_PASSWD * * @param userPasswd the value for USER.USER_PASSWD */ public void setUserPasswd(String userPasswd) { this.userPasswd = userPasswd; } * This method returns the value of the database column USER.CREATE_TIME * * @return the value of USER.CREATE_TIME */ public Date getCreateTime() { return createTime; } * This method sets the value of the database column USER.CREATE_TIME * * @param createTime the value for USER.CREATE_TIME */ public void setCreateTime(Date createTime) { this.createTime = createTime; } * This method returns the value of the database column USER.UPDATE_TIME * * @return the value of USER.UPDATE_TIME */ public Date getUpdateTime() { return updateTime; } * This method sets the value of the database column USER.UPDATE_TIME * * @param updateTime the value for USER.UPDATE_TIME */ public void setUpdateTime(Date updateTime) { this.updateTime = updateTime; } * This method returns the value of the database column USER.LAST_LOGIN_TIME * * @return the value of USER.LAST_LOGIN_TIME */ public Date getLastLoginTime() { return lastLoginTime; } * This method sets the value of the database column USER.LAST_LOGIN_TIME * * @param lastLoginTime the value for USER.LAST_LOGIN_TIME */ public void setLastLoginTime(Date lastLoginTime) { this.lastLoginTime = lastLoginTime; } }
|
3、在与User.java的同一个package下建立User表的SqlMap映射配置文件:USER_SqlMap.xml。其内容如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34
| <?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="USER" > <resultMap id="BaseResultMap" class="cn.aofeng.sis.domain.User" > <result column="USER_ID" property="userId" jdbcType="DECIMAL" /> <result column="USER_TYPE" property="userType" jdbcType="DECIMAL" /> <result column="USER_STATUS" property="userStatus" jdbcType="DECIMAL" /> <result column="USER_NAME" property="userName" jdbcType="VARCHAR" /> <result column="USER_PASSWD" property="userPasswd" jdbcType="VARCHAR" /> <result column="CREATE_TIME" property="createTime" jdbcType="DATE" /> <result column="UPDATE_TIME" property="updateTime" jdbcType="DATE" /> <result column="LAST_LOGIN_TIME" property="lastLoginTime" jdbcType="DATE" /> </resultMap> <select id="selectByUserIdOrUserName" resultMap="BaseResultMap" parameterClass="cn.aofeng.sis.domain.User" > select USER_ID, USER_TYPE, USER_STATUS, USER_NAME, USER_PASSWD, CREATE_TIME, UPDATE_TIME, LAST_LOGIN_TIME from TEST_PTL_USER <dynamic prepend=" where" > <isNotNull prepend="or" property="userId" > USER_ID = #userId:DECIMAL# </isNotNull> <isNotNull prepend="or" property="userName" > USER_NAME= #userName:VARCHAR# </isNotNull> </dynamic> </select> <delete id="deleteByUserId" parameterClass="Long" > delete from TEST_PTL_USER where USER_ID = #value# </delete> </sqlMap>
|
4、在IBatis的配置文件SqlMapConfig.xml中加入USER_SqlMap.xml:
1
| <sqlMap resource ="cn/aofeng/sis/domain/USER_SqlMap.xml" />
|
七、编写DAO类 | Write dao class
1、编写DAO接口类:UserDAO.java,其内容如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39
| * 建立时间:2011-3-19 */ package cn.aofeng.sis.dao; import cn.aofeng.sis.domain.User; * 账号表User DAO接口定义. * * @author 傲风 <a href="mailto:aofengblog@163.com">aofengblog@163.com</a> */ public interface UserDAO { * 根据账号ID删除账号. * * @param userId 账号ID. * @return 返回1表示成功,返回0表示失败. */ int deleteByUserId(Long userId); * 根据账号ID查询账号信息. * * @param userId 账号ID. * @return 如果查询成功返回一个@{link com.ailk.dm.odomain.testportal.domain.TestPtlUser}实例;查找不到返回null. */ User selectByUserId(Long userId); * 根据账号名称询账号信息. * * @param userName 账号名称. * @return 如果查询成功返回一个@{link com.ailk.dm.odomain.testportal.domain.TestPtlUser}实例;查找不到返回null. */ User selectByUserName(String userName); }
|
2、编写DAO实现类:UserDAOImpl.java,其内容如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56
| * 建立时间:2011-3-19 */ package cn.aofeng.sis.dao; import cn.aofeng.sis.domain.User; import javax.annotation.Resource; import org.springframework.orm.ibatis.SqlMapClientTemplate; import org.springframework.stereotype.Repository; import org.springframework.transaction.annotation.Propagation; import org.springframework.transaction.annotation.Transactional; * 账号表User DAO IBatis实现. * * @author 傲风 <a href="mailto:aofengblog@163.com">aofengblog@163.com</a> */ @ Repository("userDAO") @ Transactional(propagation = Propagation.SUPPORTS, readOnly = true) public class UserDAOImpl implements UserDAO { @ Resource(name = "sqlMapClientTemplate") private SqlMapClientTemplate _sqlMapClientTemplate; protected SqlMapClientTemplate getSqlMapClientTemplate() { return _sqlMapClientTemplate; } protected String getNamespace() { return "USER"; } @ Transactional(propagation = Propagation.REQUIRED, readOnly = false) public int deleteByUserId(Long userId) { int result = getSqlMapClientTemplate().delete (getNamespace() + ".deleteByUserId", userId); return result; } public User selectByUserId(Long userId) { User param = new User(); param.setUserId(userId); return (User)getSqlMapClientTemplate().queryForObject(getNamespace() + ".selectByUserIdOrUserName", param); } public User selectByUserName(String userName) { User param = new User(); param.setUserName(userName); return (User)getSqlMapClientTemplate().queryForObject(getNamespace() + ".selectByUserIdOrUserName", param); } }
|
八、编写JUnit单元测试 | Write junit testcase class
1、编写JUnit公用类:DaoBaseTestCase.java,其内容如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51
| * 建立时间:2011-3-19 */ package cn.aofeng.sis.dao; import java.util.Date; import org.springframework.test.context.junit4.AbstractTransactionalJUnit4SpringContextTests; import cn.aofeng.sis.domain.UserStatus; import cn.aofeng.sis.domain.UserType; * DAO单元测试公用类. * * @author 傲风 <a href="mailto:aofengblog@163.com">aofengblog@163.com</a> */ public class DaoBaseTestCase extends AbstractTransactionalJUnit4SpringContextTests { * Default constructor. */ public DaoBaseTestCase() {} * 向数据库插入一条账号记录. * * @param userId 账号ID. * @return 插入记录成功返回1;插入记录失败返回0. */ public int insertUser(Long userId) { final String sql = "insert into USER(USER_ID, USER_TYPE, USER_STATUS, USER_NAME, USER_PASSWD, CREATE_TIME, UPDATE_TIME) values(?, ?, ?, ?, ?, ?, ?)"; int result = simpleJdbcTemplate.update(sql, userId, UserType.OPERATOR, UserStatus.NORMAL, "aofeng" + userId, "aofeng", new Date(), new Date()); return result; } * 删除指定账号ID的账号记录. * * @param userId 账号ID. * @return 删除记录成功返回1;删除记录失败返回0. */ public int deleteUserByUserId(Long userId) { final String sql = "delete from USER where USER_ID = ?"; int result = simpleJdbcTemplate.update(sql, userId); return result; } }
|
2、编写UserDAOImpl的单元测试类:UserDaoImplTest,其内容如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115
| * 建立时间:2011-3-19 */ package cn.aofeng.sis.dao; import static org.junit.Assert. * ; import javax.annotation.Resource; import org.junit.After; import org.junit.Before; import org.junit.Test; import org.springframework.test.context.ContextConfiguration; import cn.aofeng.sis.domain.User; * {@link cn.aofemg.sis.dao.UserDAOImpl}的单元测试代码. * * @author 傲风 <a href="mailto:aofengblog@163.com">aofengblog@163.com</a> */ @ ContextConfiguration(locations = { "/ApplicationContext.xml" }, inheritLocations = false) public class UserDAOImplTest extends DaoBaseTestCase { protected Long _userId = 1L; @ Resource(name = "userDAO") protected UserDAO _userDAO; * @throws java.lang.Exception */ @ Before public void setUp()throws Exception { int result = super.insertUser(_userId); assertEquals(1, result); } * @throws java.lang.Exception */ @ After public void tearDown()throws Exception { @ SuppressWarnings("unused") int result = super.deleteUserByUserId(_userId); } * Test method for {@link cn.aofeng.sis.dao.UserDAOImpl#deleteByUserId(java.lang.Long)}. */ @ Test public void testDeleteByUserIdForExist() { int result = _userDAO.deleteByUserId(_userId); assertEquals(1, result); } * Test method for {@link cn.aofeng.sis.dao.UserDAOImpl#deleteByUserId(java.lang.Long)}. */ @ Test public void testDeleteByUserIdForNotExist() { int result = _userDAO.deleteByUserId(100L); assertEquals(0, result); } * Test method for {@link cn.aofeng.sis.dao.UserDAOImpl#selectByUserId(java.lang.Long)}. */ @ Test public void testSelectByUserIdForExist() { User record = _userDAO.selectByUserId(_userId); assertNotNull(record); assertEquals(1, record.getUserId().longValue()); assertEquals("aofeng" + _userId, record.getUserName()); } * Test method for {@link cn.aofeng.sis.dao.UserDAOImpl#selectByUserId(java.lang.Long)}. */ @ Test public void testSelectByUserIdForNotExist() { User record = _userDAO.selectByUserId(100L); assertNull(record); } * Test method for {@link cn.aofeng.sis.dao.UserDAOImpl#selectByUserName(java.lang.String)}. */ @ Test public void testSelectByUserNameForExist() { User record = _userDAO.selectByUserName("aofeng" + _userId); assertNotNull(record); assertEquals(1, record.getUserId().longValue()); assertEquals("aofeng" + _userId, record.getUserName()); } * Test method for {@link cn.aofeng.sis.dao.UserDAOImpl#selectByUserName(java.lang.String)}. */ @ Test public void testSelectByUserNameForNotExist() { User record = _userDAO.selectByUserName("userNotExists"); assertNull(record); } }
|
3、运行单元测试,结果如下:
至此,完成Spring与IBatis的集成。
附录I:项目结构及完整配置 | Appendix I: Project structure and complete configuration
1、项目结构如下图所示:
2、Spring 配置文件ApplicationContext.xml的完整内容:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52
| <?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:context="http://www.springframework.org/schema/context" xmlns:tx="http://www.springframework.org/schema/tx" xmlns:jee="http://www.springframework.org/schema/jee" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-2.5.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-2.5.xsd http://www.springframework.org/schema/jee http://www.springframework.org/schema/jee/spring-jee-2.0.xsd"> <context:property-placeholder location="classpath:jdbc.properties"/> <context:component-scan base-package="cn.aofeng.sis" /> <context:annotation-config /> <bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource" destroy-method="close"> <property name="driverClass" value="${jdbc.driverClassName}"/> <property name="jdbcUrl" value="${jdbc.url}"/> <property name="user" value="${jdbc.username}"/> <property name="password" value="${jdbc.password}"/> <property name="maxIdleTime" value="${maxIdleTime}"/> <property name="maxPoolSize" value="${maxPoolSize}"/> <property name="minPoolSize" value="${minPoolSize}"/> <property name="initialPoolSize" value="${initialPoolSize}"/> <property name="idleConnectionTestPeriod" value="${idleConnectionTestPeriod}"/> </bean> <bean id="sqlMapClient" class="org.springframework.orm.ibatis.SqlMapClientFactoryBean"> <property name="dataSource" ref="dataSource" /> <property name="configLocation" value="classpath:SqlMapConfig.xml" /> </bean> <bean id="sqlMapClientTemplate" class="org.springframework.orm.ibatis.SqlMapClientTemplate"> <property name="sqlMapClient" ref="sqlMapClient"></property> </bean> <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"> <property name="dataSource" ref="dataSource" /> </bean> <tx:annotation-driven transaction-manager="transactionManager" /> </beans>
|
3、IBatis 配置文件SqlMapConfig.xml的完整内容:
1 2 3 4 5 6 7 8 9 10
| <?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE sqlMapConfig PUBLIC "-//ibatis.apache.org//DTD SQL Map Config 2.0//EN" "http://ibatis.apache.org/dtd/sql-map-config-2.dtd"> <sqlMapConfig> <settings cacheModelsEnabled="true" useStatementNamespaces="true"/> <sqlMap resource ="cn/aofeng/sis/domain/USER_SqlMap.xml" /> </sqlMapConfig>
|
附录II:Spring配置项说明 | Appendix II: Spring configuration items description
1、<context:component-scan/>说明。
<context:component-scan/> 配置项不但启用了对类包进行扫描以实施注释驱动 Bean 定义的功能,同时还启用了注释驱动自动注入的功能(即还隐式地在内部注册了 AutowiredAnnotationBeanPostProcessor 和 CommonAnnotationBeanPostProcessor),因此当使用 <context:component-scan/> 后,就可以将 <context:annotation-config/> 移除了。
2、<context:annotationconfig/>说明。
<context:annotationconfig/> 将隐式地向 Spring 容器注册 AutowiredAnnotationBeanPostProcessor、CommonAnnotationBeanPostProcessor、PersistenceAnnotationBeanPostProcessor 以及 equiredAnnotationBeanPostProcessor 这 4 个 BeanPostProcessor。
3、<tx:annotation-driven /> 说明。
<tx:annotation-driven /> 表示使用声明式事务。如果用 ‘transactionManager’ 来定义 PlatformTransactionManager bean的名字的话,你就可以忽略 <tx:annotation-driven/> 标签里的 ‘transaction-manager’ 属性。 如果 PlatformTransactionManager bean你要通过其它名称来注入的话,你必须用 ‘transaction-manager’ 属性来指定它。