框架整合之SSM——Spring、SpringMVC、Mybaits

    在前面的博文中,依次介绍了Spring、Spring mvc、Mybaits框架的功能及使用。在之前的博文中都是单独的介绍,尤其是Mybaits框架,在例子中跟本就没有用到其它的框架,这说明了他们的独立性,但反观之前的Mybaits的例子,测试的时候总是要先创建sqlSession,再获取mapper接口的动态代理对象,然后才能调用mapper对象的方法,而这些代码都是必须且重复的,十分的不便。这时,Spring的作用就发挥出来了。我们可以实现将所有对象创建和依赖关系维护交给Spring管理,Spring本身就是一个大工厂,负责bean的生产。

一般来说,先整合Spring和SpringMVC,再整合Spring和Mybaits,不过为了方便,我就直接发代码了,不分步骤,xml文件中会有详细的注释说明。以《JavaWeb高级编程》一书中的Discussion-Board工程为例,这个工程本身只是整合了Spring与Spring MVC,我在此工程的基础上添加了MySQL数据库并以Mybaits为框架,有略作修改。

首先是工程的整体结构目录:


    这个工程的结构与普通直接创建的MAVEN工程有些不同,这是因为我在pom.xml文件中做了设置,只是个人喜好,不同也没有关系。

接下来是Maven的pom.xml文件

我就不显示我是怎么设置的了,其中有很多不需要的包,各种包有注释说明,依据个人需求吧,并不需要全部都写

 <properties>
		<!--自定义变量-->
		<!-- 编码规则 -->
		<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
		<plugin.version>2.6</plugin.version>
		<project.build.name>tools</project.build.name>
		<!-- Spring与SpringMVC版本号 -->
		<spring.version>4.3.13.RELEASE</spring.version>
		<!-- mybatis版本号 -->
		<mybatis.version>3.4.1</mybatis.version>
		<!-- log4j日志文件管理包版本 -->
		<org.slf4j.version>1.7.25</org.slf4j.version>
		<logback.version>1.0.7</logback.version>
		<!-- Spring安全框架 -->
		<spring-security.version>4.1.2.RELEASE</spring-security.version>
	</properties>
	<dependencies>
		<!-- Spring -->
		<dependency> 
			<groupId>org.springframework</groupId>
			<artifactId>spring-core</artifactId>
			<version>${spring.version}</version>
		</dependency>

		<dependency>
			<groupId>org.springframework</groupId>
			<artifactId>spring-web</artifactId>
			<version>${spring.version}</version>
		</dependency>

		<dependency>
			<groupId>org.springframework</groupId>
			<artifactId>spring-oxm</artifactId>
			<version>${spring.version}</version>
		</dependency>

		<dependency>
			<groupId>org.springframework</groupId>
			<artifactId>spring-tx</artifactId>
			<version>${spring.version}</version>
		</dependency>

		<dependency>
			<groupId>org.springframework</groupId>
			<artifactId>spring-jdbc</artifactId>
			<version>${spring.version}</version>
		</dependency>

		<dependency>
			<groupId>org.springframework</groupId>
			<artifactId>spring-webmvc</artifactId>
			<version>${spring.version}</version>
		</dependency>

		<dependency>
			<groupId>org.springframework</groupId>
			<artifactId>spring-aop</artifactId>
			<version>${spring.version}</version>
		</dependency>

		<dependency>
			<groupId>org.springframework</groupId>
			<artifactId>spring-context-support</artifactId>
			<version>${spring.version}</version>
		</dependency>

		<dependency>
			<groupId>org.springframework</groupId>
			<artifactId>spring-test</artifactId>
			<version>${spring.version}</version>
		</dependency>

		<dependency>
			<groupId>org.springframework</groupId>
			<artifactId>spring-context</artifactId>
			<version>${spring.version}</version>
		</dependency>

		<dependency>
			<groupId>org.springframework</groupId>
			<artifactId>spring-orm</artifactId>
			<version>${spring.version}</version>
		</dependency>

		<!-- Spring安全框架 -->
		<dependency>
			<groupId>org.springframework.security</groupId>
			<artifactId>spring-security-config</artifactId>
			<version>${spring-security.version}</version>
		</dependency>

		<dependency>
			<groupId>org.springframework.security</groupId>
			<artifactId>spring-security-core</artifactId>
			<version>${spring-security.version}</version>
		</dependency>

		<dependency>
			<groupId>org.springframework.security</groupId>
			<artifactId>spring-security-web</artifactId>
			<version>${spring-security.version}</version>
		</dependency>

		<dependency>
			<groupId>org.aspectj</groupId>
			<artifactId>aspectjweaver</artifactId>
			<version>1.8.7</version>
		</dependency>

		<!-- SpringMVC返回json数据 -->
		<dependency>
			<groupId>org.codehaus.jackson</groupId>
			<artifactId>jackson-mapper-asl</artifactId>
			<version>1.9.5</version>
		</dependency>

		<!-- mybatis核心包 -->
		<dependency>
			<groupId>org.mybatis</groupId>
			<artifactId>mybatis</artifactId>
			<version>${mybatis.version}</version>
		</dependency>

		<!-- mybatis/spring包 -->
		<dependency>
			<groupId>org.mybatis</groupId>
			<artifactId>mybatis-spring</artifactId>
			<version>1.3.1</version>
		</dependency>
		
		<!--mybatis 二级缓存-->
		<!-- https://mvnrepository.com/artifact/org.mybatis.caches/mybatis-ehcache -->
		<dependency>
			<groupId>org.mybatis.caches</groupId>
			<artifactId>mybatis-ehcache</artifactId>
			<version>1.1.0</version>
		</dependency>
		<!-- mybaits 支持java8 LocalDate、LocalTime、LocalDateTime  -->
		<dependency>  
		    <groupId>org.mybatis</groupId>  
		    <artifactId>mybatis-typehandlers-jsr310</artifactId>  
		    <version>1.0.1</version>  
		</dependency> 
		
		<!--JPA  -->
		<!-- https://mvnrepository.com/artifact/javax.persistence/persistence-api -->
		<dependency>
			<groupId>javax.persistence</groupId>
			<artifactId>persistence-api</artifactId>
			<version>1.0.2</version>
		</dependency>
		
		<!--通用Mapper插件结合JPA简化Dao层开发-->
		<!-- https://mvnrepository.com/artifact/tk.mybatis/mapper -->
		<dependency>
			<groupId>tk.mybatis</groupId>
			<artifactId>mapper</artifactId>
			<version>3.4.4</version>
		</dependency>
		
		<!--pageHelper 分页插件-->
		<!-- https://mvnrepository.com/artifact/com.github.pagehelper/pagehelper -->
		<dependency>
			<groupId>com.github.pagehelper</groupId>
			<artifactId>pagehelper</artifactId>
			<version>4.2.1</version>
		</dependency>

		<!--mybatis 逆向工程  -->
		<!-- https://mvnrepository.com/artifact/org.mybatis.generator/mybatis-generator-core -->
		<dependency>
			<groupId>org.mybatis.generator</groupId>
			<artifactId>mybatis-generator-core</artifactId>
			<version>1.3.5</version>
		</dependency>
		
		<!--redis 客户端-->
		<!-- https://mvnrepository.com/artifact/redis.clients/jedis -->
		<dependency>
			<groupId>redis.clients</groupId>
			<artifactId>jedis</artifactId>
			<version>2.9.0</version>
		</dependency>
		
		<!--redis 整合spring-->
		<!-- https://mvnrepository.com/artifact/org.springframework.data/spring-data-redis -->
		<dependency>
			<groupId>org.springframework.data</groupId>
			<artifactId>spring-data-redis</artifactId>
			<version>2.0.2.RELEASE</version>
		</dependency>
		

		<!-- 单元测试 -->
		<dependency>
			<groupId>junit</groupId>
			<artifactId>junit</artifactId>
			<version>4.12</version>
			<scope>test</scope>
		</dependency>

		<!-- MySQL驱动 -->
		<dependency>
			<groupId>mysql</groupId>
			<artifactId>mysql-connector-java</artifactId>
			<version>5.1.40</version>
		</dependency>

		<!-- druid连接池 -->
		<dependency>
			<groupId>com.alibaba</groupId>
			<artifactId>druid</artifactId>
			<version>1.1.6</version>
		</dependency>

		<!-- servlet-api -->
		<!-- https://mvnrepository.com/artifact/javax.servlet/javax.servlet-api -->
		<dependency>
			<groupId>javax.servlet</groupId>
			<artifactId>javax.servlet-api</artifactId>
			<version>4.0.0</version>
			<scope>provided</scope>
		</dependency>

		<!-- https://mvnrepository.com/artifact/javax.servlet.jsp/javax.servlet.jsp-api -->
		<dependency>
			<groupId>javax.servlet.jsp</groupId>
			<artifactId>javax.servlet.jsp-api</artifactId>
			<version>2.3.1</version>
			<scope>provided</scope>
		</dependency>

		<!-- 日志文件管理包 -->
		<!-- slfj+logback -->
		<dependency>
			<groupId>org.slf4j</groupId>
			<artifactId>slf4j-api</artifactId>
			<version>${org.slf4j.version}</version>
		</dependency>
		
		<!-- https://mvnrepository.com/artifact/org.slf4j/slf4j-log4j12 -->
		<!-- <dependency>
			<groupId>org.slf4j</groupId>
			<artifactId>slf4j-log4j12</artifactId>
			<version>${org.slf4j.version}</version>
			<scope>test</scope>
		</dependency> -->
		
		<!-- https://mvnrepository.com/artifact/log4j/log4j -->
	<!-- <dependency>
			<groupId>log4j</groupId>
			<artifactId>log4j</artifactId>
			<version>1.2.17</version>
		</dependency> -->

		<dependency>
			<groupId>ch.qos.logback</groupId>
			<artifactId>logback-classic</artifactId>
			<version>${logback.version}</version>
			<type>jar</type>
			<scope>compile</scope>
		</dependency>

		<dependency>
			<groupId>ch.qos.logback</groupId>
			<artifactId>logback-core</artifactId>
			<version>${logback.version}</version>
			<type>jar</type>
			<scope>compile</scope>
		</dependency>
		<!--spring整合logback-->
		<!-- https://mvnrepository.com/artifact/org.logback-extensions/logback-ext-spring -->
		<dependency>
			<groupId>org.logback-extensions</groupId>
			<artifactId>logback-ext-spring</artifactId>
			<version>0.1.4</version>
		</dependency>
		
		<!-- jstl -->
		<dependency>
			<groupId>jstl</groupId>
			<artifactId>jstl</artifactId>
			<version>1.2</version>
		</dependency>

		<!-- https://mvnrepository.com/artifact/org.apache.taglibs/taglibs-standard-impl -->
		<dependency>
			<groupId>org.apache.taglibs</groupId>
			<artifactId>taglibs-standard-impl</artifactId>
			<version>1.2.5</version>
		</dependency>
		
		<!-- commons包 -->
		<dependency>
			<groupId>commons-io</groupId>
			<artifactId>commons-io</artifactId>
			<version>2.4</version>
		</dependency>

		<!-- https://mvnrepository.com/artifact/commons-beanutils/commons-beanutils -->
		<dependency>
			<groupId>commons-beanutils</groupId>
			<artifactId>commons-beanutils</artifactId>
			<version>1.9.3</version>
		</dependency>

		<!-- https://mvnrepository.com/artifact/commons-logging/commons-logging -->
		<dependency>
			<groupId>commons-logging</groupId>
			<artifactId>commons-logging</artifactId>
			<version>1.2</version>
		</dependency>

		<dependency>
			<groupId>commons-codec</groupId>
			<artifactId>commons-codec</artifactId>
			<version>1.9</version>
		</dependency>

		<dependency>
			<groupId>org.apache.commons</groupId>
			<artifactId>commons-lang3</artifactId>
			<version>3.0</version>
		</dependency>
		
		<!-- https://mvnrepository.com/artifact/net.sf.json-lib/json-lib -->
		<dependency>
			<groupId>net.sf.json-lib</groupId>
			<artifactId>json-lib</artifactId>
			<version>2.4</version>
			<classifier>jdk15</classifier><!--指定jdk版本-->
		</dependency>

		<!-- poi文件写入写出 -->
		<dependency>
			<groupId>org.apache.poi</groupId>
			<artifactId>poi</artifactId>
			<version>3.5-FINAL</version>
		</dependency>
		
		<!-- Json工具包 -->
		<dependency>
			<groupId>com.google.code.gson</groupId>
			<artifactId>gson</artifactId>
			<version>2.1</version>
		</dependency>

		<dependency>
			<groupId>org.json</groupId>
			<artifactId>json</artifactId>
			<version>20160212</version>
		</dependency>

		<dependency>
			<groupId>com.alibaba</groupId>
			<artifactId>fastjson</artifactId>
			<version>1.2.17</version>
		</dependency>
		
		<!--百度富文本编辑器  -->
		<dependency>
			<groupId>cn.songxinqiang</groupId>
			<artifactId>com.baidu.ueditor</artifactId>
			<version>1.1.2-offical</version>
		</dependency>
		
		<!--文件上传依赖  -->
		<dependency>
			<groupId>commons-fileupload</groupId>
			<artifactId>commons-fileupload</artifactId>
			<version>1.3.1</version>
			<type>jar</type>
			<scope>compile</scope>
		</dependency>
		
		<!--谷歌二维码依赖-->
		<!-- https://mvnrepository.com/artifact/com.google.zxing/core -->
		<dependency>
			<groupId>com.google.zxing</groupId>
			<artifactId>core</artifactId>
			<version>3.3.0</version>
		</dependency>
		
		<!-- https://mvnrepository.com/artifact/com.google.zxing/javase -->
		<dependency>
			<groupId>com.google.zxing</groupId>
			<artifactId>javase</artifactId>
			<version>3.3.0</version>
		</dependency>
		
		<!--解析xml-->
		<!-- https://mvnrepository.com/artifact/dom4j/dom4j -->
		<dependency>
			<groupId>dom4j</groupId>
			<artifactId>dom4j</artifactId>
			<version>1.6.1</version>
		</dependency>
		
		<!--Java类到xml的转换-->
		<!-- https://mvnrepository.com/artifact/com.thoughtworks.xstream/xstream -->
		<dependency>
			<groupId>com.thoughtworks.xstream</groupId>
			<artifactId>xstream</artifactId>
			<version>1.4.10</version>
		</dependency>
		
		<!-- https://mvnrepository.com/artifact/net.coobird/thumbnailator -->
		<dependency>
			<groupId>net.coobird</groupId>
			<artifactId>thumbnailator</artifactId>
			<version>0.4.8</version>
		</dependency>
		
		<!--图片下载依赖  -->
		<!-- https://mvnrepository.com/artifact/org.apache.httpcomponents/httpcore -->
		<dependency>
			<groupId>org.apache.httpcomponents</groupId>
			<artifactId>httpcore</artifactId>
			<version>4.4.6</version>
		</dependency>
		
		<!-- https://mvnrepository.com/artifact/org.apache.httpcomponents/httpclient -->
		<dependency>
			<groupId>org.apache.httpcomponents</groupId>
			<artifactId>httpclient</artifactId>
			<version>4.5.3</version>
		</dependency>
		
		<!-- https://mvnrepository.com/artifact/org.eclipse.persistence/javax.persistence -->
		<dependency>
			<groupId>org.eclipse.persistence</groupId>
			<artifactId>javax.persistence</artifactId>
			<version>2.1.1</version>
		</dependency>
		
		<!--FreeMaker 依赖 -->
		<!-- https://mvnrepository.com/artifact/org.freemarker/freemarker -->
		<dependency>
			<groupId>org.freemarker</groupId>
			<artifactId>freemarker</artifactId>
			<version>2.3.23</version>
		</dependency>
		
		<!-- demn使用,不一定需要,可以换成其他json工具 -->
		<dependency>
			<groupId>com.ibeetl</groupId>
			<artifactId>btjson</artifactId>
			<version>0.94</version>
		</dependency>

		<!--Beetl依赖  -->
		<dependency>
			<groupId>com.ibeetl</groupId>
			<artifactId>beetl</artifactId>
			<version>2.7.18</version>
		</dependency>
		
		<!--定时作业调度-->
		<!-- https://mvnrepository.com/artifact/org.quartz-scheduler/quartz -->
		<dependency>
			<groupId>org.quartz-scheduler</groupId>
			<artifactId>quartz</artifactId>
			<version>2.3.0</version>
		</dependency>

		<!-- https://mvnrepository.com/artifact/org.quartz-scheduler/quartz-jobs -->
		<dependency>
			<groupId>org.quartz-scheduler</groupId>
			<artifactId>quartz-jobs</artifactId>
			<version>2.3.0</version>
		</dependency>
		
		 
	</dependencies>  

然后是web.xml文件,主要是配置Spring和Spring MVC,我还配置了druid连接池监控

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://java.sun.com/xml/ns/javaee" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd" id="WebApp_ID" version="3.0">
  <display-name>Disscussion-Board</display-name>
  <welcome-file-list>
    <welcome-file>index.html</welcome-file>
    <welcome-file>index.jsp</welcome-file>
    
  </welcome-file-list>
  
 	<welcome-file-list>
		<welcome-file>index.html</welcome-file>
	</welcome-file-list>
	
	<!--如果bean需要以session , request的等作为作用域需要配置  -->
	<listener>
		<listener-class>org.springframework.web.context.request.RequestContextListener</listener-class>
	</listener>
	<!--专门用来处理Introspector内存泄漏问题的辅助类。 -->
	<listener>
		<listener-class>org.springframework.web.util.IntrospectorCleanupListener</listener-class>
	</listener>
	
	<!--Spring的初始化,交由Web容器来负责  -->
	<!--配置spring的全局初始化参数  -->
	<context-param>
		<param-name>contextConfigLocation</param-name>
		<!--配置全局参数,用于指定spring配置文件的位置  -->
		<!--可以有多个value  -->
		<param-value>classpath:spring/applicationContext.xml</param-value>
	</context-param>
	
	<!--配置核心监听器,让spring随着tomcat的启动而创建容器  springListener的启动有顺序关系,spring的容器启动放在最后 -->
	<listener>
		<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
	</listener>
	
	<!--spring编码过滤  -->
	<filter>
		<filter-name>encodingFilter</filter-name>
		<filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
		<init-param>
			<param-name>encoding</param-name>
			<param-value>UTF-8</param-value>
		</init-param>
		<init-param>
			<param-name>forceEncoding</param-name>
			<param-value>true</param-value>
		</init-param>
	</filter>
	<filter-mapping>
		<filter-name>encodingFilter</filter-name>
		<url-pattern>/*</url-pattern>
	</filter-mapping>

	<!--将所有请求交由springMVC请求分发器处理  -->
	<servlet>
		<servlet-name>SpringMVC</servlet-name>
		<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
		<init-param>
		<!-- 初始化请求分发器  -->
			<param-name>contextConfigLocation</param-name>
			<param-value>classpath:spring/applicationContext-mvc.xml</param-value>
		</init-param>
	</servlet>
	<servlet-mapping>
		<servlet-name>SpringMVC</servlet-name>
		<url-pattern>/</url-pattern>
	</servlet-mapping>
	
	<!--错误代码响应界面-->
	<!-- <error-page>
		<error-code>404</error-code>
		<location>/404.html</location>
	</error-page>
	<error-page>
		<error-code>500</error-code>
		<location>/404.html</location>
	</error-page> -->
	
	<!--session失效时间 单位为分钟  -->
	<session-config>
		<session-timeout>1440000</session-timeout>
	</session-config>

	<!--druid连接池监控-->
	<servlet>
		<servlet-name>DruidStatView</servlet-name>
		<servlet-class>com.alibaba.druid.support.http.StatViewServlet</servlet-class>
	</servlet>

	<servlet-mapping>
		<servlet-name>DruidStatView</servlet-name>
		<url-pattern>/druid/*</url-pattern>
	</servlet-mapping>

	<filter>
		<filter-name>druidWebStatFilter</filter-name>
		<filter-class>com.alibaba.druid.support.http.WebStatFilter</filter-class>
		<init-param>
			<param-name>exclusions</param-name>
			<param-value>/public/*,*.js,*.css,/druid*,*.jsp,*.swf</param-value>
		</init-param>
		<init-param>
			<param-name>principalSessionName</param-name>
			<param-value>sessionInfo</param-value>
		</init-param>
		<init-param>
			<param-name>profileEnable</param-name>
			<param-value>true</param-value>
		</init-param>
	</filter>

	<filter-mapping>
		<filter-name>druidWebStatFilter</filter-name>
		<url-pattern>/*</url-pattern>
	</filter-mapping>
</web-app>

接下来是Spring的配置文件applicationContext.xml(这个是默认的名字,如果需要更改成其他名字,需要在web.xml文件中说明,怎么说明就请你自行百度了),需要注意的是,文件配置中取消了对Controller层的扫描加载bean,因为控制将有SpringMVC控制,如果Spring又扫描的话,控制层的bean将会被扫描两遍,会引发一些莫名奇妙的错误。

<?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:context="http://www.springframework.org/schema/context"
	xmlns:mvc="http://www.springframework.org/schema/mvc"
	xmlns:tx="http://www.springframework.org/schema/tx"
	xmlns:mybatis-spring="http://mybatis.org/schema/mybatis-spring"
	xsi:schemaLocation="http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-4.3.xsd
		http://mybatis.org/schema/mybatis-spring http://mybatis.org/schema/mybatis-spring-1.2.xsd
		http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
		http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.3.xsd
		http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-4.3.xsd
		http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-4.3.xsd">

    <!--启用注解  -->
	<context:annotation-config />
	
	<!-- 启动组件扫描,排除@Controller组件,该组件 由SpringMVC配置文件扫描-->
	<context:component-scan base-package="com.wrox">
		<context:exclude-filter type="annotation" expression="org.springframework.stereotype.Controller"/>
	</context:component-scan>
	
	<!-- 链接dbcongif.properties文件 -->
	<bean id="propertyConfigurer" class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
		<property name="locations">
			<list>
				<value>classpath:config/dbconfig.properties</value>
			</list>
		</property>
	</bean>
	
	<!-- 阿里 druid数据库连接池 -->
	 <bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource"
		destroy-method="close">  
        <!--  数据库基本信息配置 -->
		<property name="url" value="${url}" />
		<property name="username" value="${username}" />
		<property name="password" value="${password}" />
		<property name="driverClassName" value="${driverClassName}" />
		<property name="filters" value="${filters}" />  
   		<!--  最大并发连接数 -->
		<property name="maxActive" value="${maxActive}" />
         <!-- 初始化连接数量 -->
		<property name="initialSize" value="${initialSize}" />
         <!-- 配置获取连接等待超时的时间 -->
		<property name="maxWait" value="${maxWait}" />
         <!-- 最小空闲连接数 -->
		<property name="minIdle" value="${minIdle}" />  
   		<!--  配置间隔多久才进行一次检测,检测需要关闭的空闲连接,单位是毫秒 -->
		<property name="timeBetweenEvictionRunsMillis" value="${timeBetweenEvictionRunsMillis}" />
       <!--   配置一个连接在池中最小生存的时间,单位是毫秒 -->
		<property name="minEvictableIdleTimeMillis" value="${minEvictableIdleTimeMillis}" />
		<property name="validationQuery" value="${validationQuery}" />
		<property name="testWhileIdle" value="${testWhileIdle}" />
		<property name="testOnBorrow" value="${testOnBorrow}" />
		<property name="testOnReturn" value="${testOnReturn}" />
		<property name="maxOpenPreparedStatements" value="${maxOpenPreparedStatements}" />
        <!--  打开removeAbandoned功能 -->
		<property name="removeAbandoned" value="${removeAbandoned}" />
         <!-- 1800秒,也就是30分钟 -->
		<property name="removeAbandonedTimeout" value="${removeAbandonedTimeout}" />
        <!--  关闭abanded连接时输出错误日志 -->
		<property name="logAbandoned" value="${logAbandoned}" />
	</bean>
	
	<!-- 事务管理 -->
	<bean name="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
		<property name="dataSource" ref="dataSource"></property>
	</bean>
	<!-- 事物处理 service及其子包中任意方法-->
	<aop:config>
		<aop:pointcut id="pc"
			expression="execution(* com.wrox.*.service..*.*(..))" />
		<aop:advisor pointcut-ref="pc" advice-ref="txAdvice" />
	</aop:config>
	<!-- 当方法错误发生事务返回错误配置 -->
	 <tx:advice id="txAdvice" transaction-manager="transactionManager">
		<tx:attributes>
			<tx:method name="delete*" propagation="REQUIRED" read-only="false"
				rollback-for="java.lang.Exception" />
			<tx:method name="insert*" propagation="REQUIRED" read-only="false"
				rollback-for="java.lang.Exception" />
			<tx:method name="update*" propagation="REQUIRED" read-only="false"
				rollback-for="java.lang.Exception" />
			<tx:method name="save*" propagation="REQUIRED" read-only="false"
				rollback-for="java.lang.Exception" />
			<tx:method name="*Service" propagation="REQUIRED"
				read-only="false" rollback-for="java.lang.Exception" />
		</tx:attributes>
	</tx:advice>
	
	<!--开启AOP注解扫描-->
	<aop:aspectj-autoproxy proxy-target-class="true" />
	
	<!-- 配置mybatis -->
	 <bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
		<property name="dataSource" ref="dataSource" />
		<property name="configLocation" value="classpath:mybatis/sqlMapConfig.xml"></property>
        <!-- mapper扫描 -->
		<property name="mapperLocations" value="classpath*:mybatis/mapper/**/*.xml"></property>
	</bean>
	
	<!--注册mybatis模板为原型-->
	<!-- SqlSessionTemplate 通常是被用来替代默认的 MyBatis 实现的 DefaultSqlSession , 因为模板可以参与到 Spring 的事务中并且被多个注入的映射器类所使 用时也是线程安全的。相同应用程序中两个类之间的转换可能会引起数据一致性的问题。 -->
	<!-- <bean id="sqlSessionTemplate" class="org.mybatis.spring.SqlSessionTemplate"
		scope="prototype">
		<constructor-arg ref="sqlSessionFactory" />
	</bean> -->
	
	<!-- mapper接口所在包名,Spring会自动查找其下的类,并动态生成其代理子类对象 -->
	<!-- <bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
		<property name="basePackage" value="com.wrox.*.mapper" />
		<property name="sqlSessionFactoryBeanName" value="sqlSessionFactory"></property>
	</bean> -->
	
	<!-- 上面注释的配置启动不成功,尝试下面这种自动的 -->
	<!-- 扫描对应的包,生成代理对象 -->
	<mybatis-spring:scan base-package="com.wrox.*.mapper"/>
</beans>

    注意最后mybaits配置时,对mapper文件的自动加载生成代理对象的两种方式,理论上应该是两种方式都可以,但是我只能用没有被注释掉的一种,可能是前面那种发式哪里写错了,欢迎大家指出纠正。

接下来是Springmvc的配置文件applicationContext-mvc.xml(这个名字没有要求,自己爱怎么起就怎么起,但是要跟web.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:context="http://www.springframework.org/schema/context"
	xmlns:mvc="http://www.springframework.org/schema/mvc"
	xsi:schemaLocation="http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-4.3.xsd
		http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.2.xsd
		http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.3.xsd">

    <!-- 当在web.xml 中   DispatcherServlet使用 <url-pattern>/</url-pattern> 映射时,能映射静态资源 -->
	<mvc:default-servlet-handler />  
	
	<!-- 静态资源映射,不拦截的部分 -->
	<!-- <mvc:resources mapping="/static/**" location="/static/" /> -->	
	
	<!-- 只扫描Controller 注解 -->
	<context:component-scan
		base-package="com.wrox.*.controller"
		use-default-filters="false">
		<context:include-filter type="annotation"
			expression="org.springframework.stereotype.Controller" />
		<context:include-filter type="annotation"
			expression="org.springframework.web.bind.annotation.ControllerAdvice" />
	</context:component-scan>

	<mvc:annotation-driven>
		<mvc:message-converters register-defaults="true">
			<!-- StringHttpMessageConverter编码为UTF-8,防止乱码 -->
			<bean class="org.springframework.http.converter.StringHttpMessageConverter">
				<constructor-arg value="UTF-8" />
				<property name="supportedMediaTypes">
					<list>
						<bean class="org.springframework.http.MediaType">
							<constructor-arg index="0" value="text" />
							<constructor-arg index="1" value="plain" />
							<constructor-arg index="2" value="UTF-8" />
						</bean>
						<bean class="org.springframework.http.MediaType">
							<constructor-arg index="0" value="*" />
							<constructor-arg index="1" value="*" />
							<constructor-arg index="2" value="UTF-8" />
						</bean>
					</list>
				</property>
			</bean>
			<!-- 避免IE执行AJAX时,返回JSON出现下载文件 -->
			<bean id="fastJsonHttpMessageConverter"
				class="com.alibaba.fastjson.support.spring.FastJsonHttpMessageConverter">
				<property name="supportedMediaTypes">
					<list>
						<value>application/json;charset=UTF-8</value>
					</list>
				</property>
			</bean>
		</mvc:message-converters>
	</mvc:annotation-driven>
	
	<!--国际化请求拦截器处理-->
	<mvc:interceptors>
		<bean class="org.springframework.web.servlet.i18n.LocaleChangeInterceptor" />
	</mvc:interceptors>
	
	<!-- 内容协商管理器  -->
    <!--1、首先检查路径扩展名(如my.pdf);2、其次检查Parameter(如my?format=pdf);3、检查Accept Header-->
	<bean id="contentNegotiationManager"
		class="org.springframework.web.accept.ContentNegotiationManagerFactoryBean">
        <!-- 扩展名至mimeType的映射,即 /user.json => application/json -->
		<property name="favorPathExtension" value="true" />
        <!-- 用于开启 /userinfo/123?format=json 的支持 -->
		<property name="favorParameter" value="true" />
		<property name="parameterName" value="format" />
        <!-- 是否忽略Accept Header -->
		<property name="ignoreAcceptHeader" value="false" />

		<property name="mediaTypes"> <!--扩展名到MIME的映射;favorPathExtension, favorParameter是true时起作用  -->
			<value>
				json=application/json
				xml=application/xml
				html=text/html
			</value>
		</property>
        <!-- 默认的content type -->
		<property name="defaultContentType" value="text/html" />
	</bean>
	
	<!--默认的JSP视图解析器-->
	<!-- 默认的视图解析器 在上边的解析错误时使用 (默认使用html)- -->
	<bean id="defaultViewResolver"
		class="org.springframework.web.servlet.view.InternalResourceViewResolver">
		<property name="order" value="1" />
		<property name="viewClass"
			value="org.springframework.web.servlet.view.JstlView" />
		<property name="contentType" value="text/html" />
		<property name="prefix" value="/WEB-INF/jsp/" />
		<property name="suffix" value=".jsp" />
	</bean>
	
	<!-- 文件上传配置 -->
	<bean id="multipartResolver"
		class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
		<property name="defaultEncoding" value="UTF-8" /> <!-- 默认编码 (ISO-8859-1) -->
		<property name="maxInMemorySize" value="10240" /> <!-- 最大内存大小 (10240) -->
		<property name="maxUploadSize" value="-1" /> <!-- 最大文件大小,-1为无限止(-1) -->
	</bean>
</beans>

这样Spring 、SpringMVC和Mybaits就整合完毕了,但是还确实Mybait的主配置文件,sqlMapConfig.xml(名字也随便你起,但要跟Spring配置文件中写的一致),事实上什么都没有,因为没用上它的功能,比如开启缓存啊、setting设置啊之类的,主要是省了我们写mapper映射文件加载,Spring已经自动帮我们完成了。

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration
  PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
  "http://mybatis.org/dtd/mybatis-3-config.dtd ">
  <configuration>
  	<!-- <properties resource=""></properties> -->
  	<typeAliases>
		<package name="com.wrox.discussionboard.pojo"/>
	</typeAliases> 
  </configuration>

配置文件就是这样的了,上面的所有东西就已经将Spring、SpringMVC、Mybaits整合完成了,接下是Discussion-Board功能的具体实现。

首先是POJO类,结构如下:


只列出属性,注意一定要有get、set方法

public class Reply implements Serializable{

	private static final long serialVersionUID = 1L;
	private Integer id;
	private Integer discussionId;
	private String user;
	private String message;
	private LocalDateTime created;
public class Discussion implements Serializable {

	private static final long serialVersionUID = 1L;
	private Integer id;
	private String user;
	private String subject;
	private String uriSafeSubject;
	private String message;
	private LocalDateTime created;
	private LocalDateTime lastUpdated;
	private Set<String> subscribedUsers = new HashSet<>();
public class DiscussionForm implements Serializable{

	private static final long serialVersionUID = 1L;
	private String user;
	private String subject;
	private String message;
public class ReplyForm implements Serializable{

	private static final long serialVersionUID = 1L;
	
	private String user;
	private String message;

接下来时mapper接口类,也就是在平常的Dao层:

public interface DiscussionMapper {

	List<Discussion> getAll();
	Discussion get(int id);
	void add(Discussion discussion);
	void update(Discussion discussion);
}
public interface ReplyMapper {

	List<Reply> getForDiscussion(int id);
	void add(Reply reply);
	void update(Reply reply);
}

mapper映射文件代码如下:

ReplyMapper.xml

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.wrox.discussionboard.mapper.ReplyMapper">
<select id="getForDiscussion" parameterType="int" resultType="com.wrox.discussionboard.pojo.Reply">
	select * from reply where discussionId = #{discussionId}
</select>
<insert id="add" parameterType="com.wrox.discussionboard.pojo.Reply">
	insert into reply(discussionId,user,message,created) 
	values (#{discussionId},#{user},#{message},#{created})
</insert>

<update id="update" parameterType="com.wrox.discussionboard.pojo.Reply">
	update reply set message = #{message}, created = #{created} where id = #{id}
</update>

</mapper>

DiscussionMapper.xml:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.wrox.discussionboard.mapper.DiscussionMapper">

<select id="getAll" resultType="com.wrox.discussionboard.pojo.Discussion">
	select * from discussion
</select>
<select id="get" resultType="com.wrox.discussionboard.pojo.Discussion" parameterType="int">
	select * from discussion where id = #{id}
</select>
<insert id="add" parameterType="com.wrox.discussionboard.pojo.Discussion">
	insert into discussion(user,subject,uriSafeSubject,message,created,lastUpdated) 
	values(#{user},#{subject},#{uriSafeSubject},#{message},#{created},#{lastUpdated}) 
</insert>

<update id="update" parameterType="com.wrox.discussionboard.pojo.Discussion">
	update discussion set subject = #{subject} ,uriSafeSubject = #{uriSafeSubject} , lastUpdated= #{lastUpdated} where id = #{id}
</update>

</mapper>

然后是Service层,采用面向接口编程的方式,结构如下:


接口文件如下:

public interface DiscussionService {

	List<Discussion> getAllDiscussion();
	Discussion getDiscussion(int id);
	void saveDiscussion(Discussion discussion);
}
public interface ReplyService {
	
	List<Reply> getRepliesForDiscussion(int discussionId);
	void saveReply(Reply reply);
}

接口实现类如下:

@Service
public class DefaultDiscussionService implements DiscussionService{

	@Autowired
	DiscussionMapper discussionMapper;
	
	@Override
	public List<Discussion> getAllDiscussion() {
		List<Discussion> list = this.discussionMapper.getAll();
		list.sort((d1,d2) -> d1.getLastUpdated().compareTo(d2.getLastUpdated()));
		return list;
	}

	@Override
	public Discussion getDiscussion(int id) {
		// TODO Auto-generated method stub
		return this.discussionMapper.get(id);
	}

	@Override
	public void saveDiscussion(Discussion discussion) {
		// TODO Auto-generated method stub
		String subject = discussion.getSubject();
		subject = Normalizer.normalize(subject.toLowerCase(), Normalizer.Form.NFD)
                .replaceAll("\\p{InCombiningDiacriticalMarks}+", "")
                .replaceAll("[^\\p{Alnum}]+", "-")
                .replace("--", "-").replace("--", "-")
                .replaceAll("[^a-z0-9]+$", "")
                .replaceAll("^[^a-z0-9]+", "");
		discussion.setUriSafeSubject(subject);
		LocalDateTime now = LocalDateTime.now();
		discussion.setLastUpdated(now);
		
		if(discussion.getId() ==  null){
			discussion.setCreated(now);
			discussion.getSubscribedUsers().add(discussion.getUser());
			this.discussionMapper.add(discussion);
		}else
			this.discussionMapper.update(discussion);
	}

}

@Service
public class DefaultReplyService implements ReplyService{

	@Autowired
	ReplyMapper replyMapper;
	@Autowired
	DiscussionService discussionService;
	
	@Override
	public List<Reply> getRepliesForDiscussion(int discussionId) {
		List<Reply> list = this.replyMapper.getForDiscussion(discussionId);
		list.sort((r1,r2) -> r1.getId()<r2.getId() ? -1:1);
		return list;
	}

	@Override
	public void saveReply(Reply reply) {
		// TODO Auto-generated method stub
		Discussion discussion = this.discussionService.getDiscussion(reply.getDiscussionId());
		if(reply.getId() == null){
			discussion.getSubscribedUsers().add(reply.getUser());
			reply.setCreated(LocalDateTime.now());
			this.replyMapper.add(reply);
		}
		else{
			this.replyMapper.update(reply);
		}
		this.discussionService.saveDiscussion(discussion);
	}

}

最后是逻辑控制层,其实现如下:

@Controller
@RequestMapping("discussion")
public class BoradController {

	@Autowired
	DiscussionService discussionService;
	
	@RequestMapping(value={"","list"},method = {RequestMethod.GET,RequestMethod.POST})
	public String listDiscussions(Map<String,Object> model,HttpServletRequest request){
		model.put("discussions",this.discussionService.getAllDiscussion());
		return "discussion/list";
	}
	@RequestMapping(value="create",method = RequestMethod.GET)
	public String createDiscussion(Map<String,Object> model,HttpServletRequest request){
		model.put("discussionForm", new DiscussionForm());
		return "discussion/create";
	}
	@RequestMapping(value="create",method = RequestMethod.POST)
	public String createDiscussion(DiscussionForm from){
		Discussion discussion = new Discussion();
		discussion.setUser(from.getUser());
		discussion.setSubject(from.getSubject());;
		discussion.setMessage(from.getMessage());
		this.discussionService.saveDiscussion(discussion);
		
		return "forward:/discussion/list";
	}
}
@Controller
@RequestMapping("discussion/{discussionId:\\d+}")
public class DiscussionController {

	@Autowired
	DiscussionService discussionService;
	@Autowired
	ReplyService replyService;
	
	@RequestMapping(value ={"","*"},method = RequestMethod.GET)
	public String viewDiscussion(Map<String,Object> model,@PathVariable("discussionId") int id){
		Discussion discussion = this.discussionService.getDiscussion(id);
		if(discussion != null){
			model.put("discussion", discussion);
			model.put("replies", this.replyService.getRepliesForDiscussion(id));
			model.put("replyForm", new ReplyForm());
			return "discussion/view";
		}
		return "discussion/errorNoDiscussion";
	}
	@RequestMapping(value="reply",method=RequestMethod.POST)
	public ModelAndView reply(ReplyForm form,@PathVariable("discussionId") int id){
		Discussion discussion = this.discussionService.getDiscussion(id);
		if(discussion != null){
			Reply reply = new Reply();
			reply.setDiscussionId(id);
			reply.setUser(form.getUser());
			reply.setMessage(form.getMessage());
			this.replyService.saveReply(reply);
			
			return new ModelAndView(new RedirectView("/discussion/"+id+"/"+discussion.getUriSafeSubject(),true,false));
		}
		return new ModelAndView("discussion/errorNoDiscussion");
	}
	
}

最后是视图层,其结构如果:


create.jsp文件代码如下:

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<%@ taglib prefix="form" uri="http://www.springframework.org/tags/form" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Insert title here</title>
</head>
<body>
	<h2>Create Discussion</h2>
	<a href="list">返回列表界面</a>
	<br>
	<form:form method="post" modelAttribute="discussionForm">
		<form:label path="user">邮箱:</form:label><br>
		<form:input path="user" type="email"/><br>
		<br>
		<form:label path="subject">Subject</form:label><br>
		<form:input path="subject" /><br>
		<br>
		<form:label path="message">Message</form:label><br />
        <form:textarea path="message" cols="40" rows="5" /><br />
        <br />
        <input type="submit" value="Submit" />
		
	</form:form>
</body>
</html>

errorNoDiscussion.jsp:

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Insert title here</title>
</head>
<body>
        <h2>Error: Discussion Not Found</h2>
      	  你输入的讨论不存在!<br />
        <br />
        [<a href="<c:url value="discussion/list" />">返回列表</a>]
</body>
</html>

list.jsp:

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
    <%@ taglib prefix="fn" uri="http://java.sun.com/jsp/jstl/functions" %>
    <%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
    <%--@elvariable id="discussions" type="java.util.List<com.wrox.discussionboard.pojo.Discussion>"--%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Insert title here</title>
</head>
<body>
        <h2>Discussions</h2>
        [<a href="<c:url value="/discussion/create" />">new discussion</a>]<br />
        <br />
        <c:choose>
            <c:when test="${fn:length(discussions) > 0}">
                <c:forEach items="${discussions}" var="discussion">
                    <a href="<c:url value="/discussion/${discussion.id}/${discussion.uriSafeSubject}" />">
                        <c:out value="${discussion.subject}" /></a><br />
                    (<c:out value="${discussion.user}" />)<br/>
                </c:forEach>
            </c:when>
            <c:otherwise>
                <i>There are no discussions yet.</i>
            </c:otherwise>
        </c:choose>
</body>
</html>

view.jsp

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<%@ taglib prefix="fn" uri="http://java.sun.com/jsp/jstl/functions" %>
<%@ taglib prefix="form" uri="http://www.springframework.org/tags/form" %>
<%--@elvariable id="discussion" type="com.wrox.discussionboard.pojo.Discussion"--%>
<%--@elvariable id="replies" type="java.util.List<com.wrox.discussionboard.pojo.Reply>"--%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Insert title here</title>
</head>
<body>
        <h2>Discussion: <c:out value="${discussion.subject}" /></h2>
        [<a href="<c:url value="/discussion/list" />">return to list</a>]<br />
        <i>User: <c:out value="${discussion.user}" /> / Created: <c:out value="${discussion.created}" /></i>
        <br />
        <c:out value="${discussion.message}" /><br />
        <br />
        <c:choose>
            <c:when test="${fn:length(replies) > 0}">
                <c:forEach items="${replies}" var="reply" varStatus="i">
                    <b>Reply #${i.count}</b><i> - <c:out value="${reply.user}" /> -
                    <c:out value="${reply.created}" /></i><br />
                    <c:out value="${reply.message}" /><br />
                    <br />
                </c:forEach>
            </c:when>
            <c:otherwise>
                <i>There are no replies yet.</i><br />
                <br />
            </c:otherwise>
        </c:choose>
        <b>Post Reply</b><br />
        <br />
        <c:set var="action"><c:url value="/discussion/${discussion.id}/reply" /></c:set>
        <form:form method="post" action="${action}" modelAttribute="replyForm">
            <form:label path="user">Your Email</form:label><br />
            <form:input path="user" type="email" /><br />
            <br />
            <form:label path="message">Message</form:label><br />
            <form:textarea path="message" cols="40" rows="5" /><br />
            <br />
            <input type="submit" value="Submit" />
        </form:form>
</body>
</html>

启动Tomcat,在浏览器地址中输入:http://localhost/Discussion-Board/discussion/list因有如下显示:


我是因为数据库中已经存有了信息才会有这些显示,如果数据库为空,应显示There are no discussions yet.


    这篇博文提醒了我要搞个github账户来玩一玩,否则的话非重点的代码实在是太多了!!






猜你喜欢

转载自blog.csdn.net/m0_37673753/article/details/80216291