本文未經作者允許,禁止內容轉載,引用請標明出處。
需求背景:由于最近總是接到一些需求,需要配合前端團隊快速建設移動端UI應用或web應用及后臺業務邏輯支撐的需求,若每次都復用之前復雜業務應用的項目代碼,總會攜帶很多暫時不會用到的功能或組件,這樣的初始工程就存在冗余代碼。
在本文中,我們將使用Java語言開發集成環境IntelliJ IDEA(其倡言是智能編碼?),應用maven構建SpringMVC整合Mybatis+MySQL5.7(流行框架)的web項目;目的在于快速構建一個簡潔純凈版的web應用工程,將其作為一個基礎web-demo,以便類似的項目都可以復用本demo。本文旨在快速搭建web項目,并未深入研究原理,有興趣的可參考其他文章研究Spring及mybatis原理。
本項目的工程代碼,已經提交到github上,方便每次下載使用。(github源代碼鏈接),新添加了本次demo的war包文件,亦可在github上下載。
一、IDEA下構建maven的web項目
1、新建工程New-->Project,創建maven的web項目,選擇maven-archetype-webapp,并配置Project SDK,本次選用的是本機已安裝的jdk1.8,如下圖所示:

2、下一步,填寫自己項目的GroupId,ArtifactId,如下圖:

3、下一步,設置maven的安裝目錄,代替IDEA自帶的maven版本,我換成了自己手動安裝的maven版本,路徑存在D盤;設置maven本地的倉庫,我配置了本項目單獨的倉庫,但沒去復用之前項目所用的maven倉庫,為的是讓本工程的依賴jar包保持純凈(雖然無關緊要,原諒我有莫名的潔癖?)。

4、下一步,設置工程的Project Name。

5、Finish,maven的web項目就創建完成,接下來maven會下載相應的資源,下載時速度會很慢(maven加載jar包過程,默認的是先掃描本地倉庫,若本地倉庫沒有,則掃描遠程倉庫下載。默認的conf/settings.xml文件沒有配置遠程倉庫,所以掃描的是maven的中央倉庫(資源鏡像一般在國外),所以慢);由于我使用的網絡是FQ外網,直接默認maven下載方式速度還是可以的(或許也是人品好一點?),如果大家想快一點,也可以參考網上配置maven使用國內鏡像的教程。
二、搭建基于SpringMVC整合mybatis的web工程
1、等待maven下載資源完成后,注意到工程目錄中會出現src的文件夾,src下默認會存在main目錄,main下默認存在resources和webapp文件夾,其中resources文件夾主要存放項目的配置或屬性等文件,webapp則是web應用的根目錄,會存放HTML,CSS,js,JSP或資源文件等。我們需要再main目錄下創建java文件夾,java目錄下存放的將是Java程序的package及class文件等。由于我們將搭建springMVC框架,我們可以提前搭建好項目的文件目錄結構,如下圖所示:

其中,java下創建項目的package com.charlie,其下可以再分類不同的package,包括controller,service,service-->impl,entity,dao,dao-->mapper,dao-->xml,common,util等,這些包是基于springmvc+mybatis所需的,還有可以存放一些公共類或工具類。
webapp下可創建css,js,image,WEB-INF-->views等。
src可創建main的同級目錄test,可以用于后續項目開發過程中的Junit程序測試。
2、配置pom.xml文件,maven添加web項目必需的以及SpringMVC所必需的dependencies包。
2.1、以下是引入Junit,日志,MySQL驅動的依賴包,注意此次引入的是最新的mysql-connector-java-6.0.6,會影響后續的SpringMVC及jdbc等的特殊配置,這將和舊版本的mysql驅動包有區別。
1 <!-- junit測試包--> 2 <dependency> 3 <groupId>junit</groupId> 4 <artifactId>junit</artifactId> 5 <version>4.12</version> 6 <scope>test</scope> 7 </dependency> 8 <!--日志包--> 9 <dependency> 10 <groupId>org.slf4j</groupId> 11 <artifactId>slf4j-log4j12</artifactId> 12 <version>1.8.0-alpha2</version> 13 </dependency> 14 <!--mysql驅動包--> 15 <dependency> 16 <groupId>mysql</groupId> 17 <artifactId>mysql-connector-java</artifactId> 18 <version>6.0.6</version> 19 </dependency>
這樣,已經支持了日志的輸出,我們仍然需要日志的屬性文件,在resources目錄下,新增日志屬性文件log4j.properties,并配置如下(配置方式很多種,大家選擇一種即可):
#配置根Logger 后面是若干個Appender
log4j.rootLogger=DEBUG,A1,R
#log4j.rootLogger=INFO,A1,R
# ConsoleAppender 輸出
log4j.appender.A1=org.apache.log4j.ConsoleAppender
log4j.appender.A1.layout=org.apache.log4j.PatternLayout
log4j.appender.A1.layout.ConversionPattern=%-d{yyyy-MM-dd HH:mm:ss,SSS} [%c]-[%p] %m%n
# File 輸出 一天一個文件,輸出路徑可以定制,一般在根路徑下
log4j.appender.R=org.apache.log4j.DailyRollingFileAppender
log4j.appender.R.File=log.txt
log4j.appender.R.MaxFileSize=500KB
log4j.appender.R.MaxBackupIndex=10
log4j.appender.R.layout=org.apache.log4j.PatternLayout
log4j.appender.R.layout.ConversionPattern=%d{yyyy-MM-dd HH:mm:ss,SSS} [%t] [%c] [%p] - %m%n
其中,開發者可設置rootLogger,開發調試環境時,可設置為DEBUG模式,輸出系統的詳細日志;生產環境時,可設置為INFO模式。
2.2、以下是引入SpringMVC所必需的依賴包,由于maven自動依賴功能,引入以下包時,會自動引入其他相關的依賴包,其中就包括spring-core,spring-context等重要的包(注意只要能夠引入所有的Spring依賴包即可)。
1 <!--spring相關包--> 2 <dependency> 3 <groupId>org.springframework</groupId> 4 <artifactId>spring-web</artifactId> 5 <version>4.3.8.RELEASE</version> 6 </dependency> 7 <dependency> 8 <groupId>org.springframework</groupId> 9 <artifactId>spring-webmvc</artifactId> 10 <version>4.3.8.RELEASE</version> 11 </dependency> 12 <dependency> 13 <groupId>org.springframework</groupId> 14 <artifactId>spring-context-support</artifactId> 15 <version>4.3.8.RELEASE</version> 16 </dependency> 17 <dependency> 18 <groupId>org.springframework</groupId> 19 <artifactId>spring-oxm</artifactId> 20 <version>4.3.8.RELEASE</version> 21 </dependency> 22 <dependency> 23 <groupId>org.springframework</groupId> 24 <artifactId>spring-tx</artifactId> 25 <version>4.3.8.RELEASE</version> 26 </dependency> 27 <dependency> 28 <groupId>org.springframework</groupId> 29 <artifactId>spring-test</artifactId> 30 <version>4.3.8.RELEASE</version> 31 </dependency> 32 <dependency> 33 <groupId>org.springframework</groupId> 34 <artifactId>spring-jdbc</artifactId> 35 <version>4.3.8.RELEASE</version> 36 </dependency> 37 <!--aspectj start--> 38 <dependency> 39 <groupId>org.aspectj</groupId> 40 <artifactId>aspectjweaver</artifactId> 41 <version>1.8.10</version> 42 </dependency> 43 <dependency> 44 <groupId>org.aspectj</groupId> 45 <artifactId>aspectjrt</artifactId> 46 <version>1.8.10</version> 47 </dependency>
2.3、以下是引入JSP的包,以支持jsp視圖的功能。
1 <!-- 支持jsp --> 2 <dependency> 3 <groupId>javax.servlet</groupId> 4 <artifactId>jstl</artifactId> 5 <version>1.2</version> 6 </dependency> 7 <dependency> 8 <groupId>taglibs</groupId> 9 <artifactId>standard</artifactId> 10 <version>1.1.2</version> 11 </dependency> 12 <!--servlet/jsp api start--> 13 <dependency> 14 <groupId>javax.servlet</groupId> 15 <artifactId>servlet-api</artifactId> 16 <version>3.0-alpha-1</version> 17 </dependency> 18 <dependency> 19 <groupId>javax.servlet.jsp</groupId> 20 <artifactId>jsp-api</artifactId> 21 <version>2.2.1-b03</version> 22 </dependency>
2.4、以下是引入datasource數據源的包,引入了2種方式,但本次項目中,我們使用的是alibaba的Druid DataSource。
1 <!-- JDBC連接池 --> 2 <dependency> 3 <groupId>com.mchange</groupId> 4 <artifactId>c3p0</artifactId> 5 <version>0.9.5.2</version> 6 </dependency> 7 <!-- DruidDataSource,本工程的dataSource配置使用的Druid --> 8 <dependency> 9 <groupId>com.alibaba</groupId> 10 <artifactId>druid</artifactId> 11 <version>1.0.29</version> 12 </dependency>
到此,我們已經引入了mysql驅動包,DataSource連接池,我們還需要配置jdbc屬性文件,在resources下創建jdbc.properties文件,內容如下:
#JDBC Global Setting #jdbc.driver=com.mysql.jdbc.Driver jdbc.driver=com.mysql.cj.jdbc.Driver jdbc.url=jdbc:mysql://localhost:3306/charlie_web_demo?useUnicode=true&characterEncoding=utf-8&useSSL=true&serverTimezone=GMT%2B8 jdbc.username=root jdbc.password=p@ssw0rd ##DataSource Global Setting #配置初始化大小、最小、最大 ds.initialSize=1 ds.minIdle=1 ds.maxActive=20 #配置獲取連接等待超時的時間 ds.maxWait=60000 #配置間隔多久才進行一次檢測,檢測需要關閉的空閑連接,單位是毫秒 ds.timeBetweenEvictionRunsMillis=60000 #配置一個連接在池中最小生存的時間,單位是毫秒 ds.minEvictableIdleTimeMillis=300000
其中,配置了mysql數據庫的連接,用戶名,密碼等信息。由于使用了最新版本的mysql-connector-java-6.0.6驅動,這要求driver和url的特殊配置,否則項目啟動時會報1個警告和2個異常。jdbc.driver配置為com.mysql.cj.jdbc.Driver,可以避免出現警告信息(具體警告信息沒有記錄下來,大家配置過程中可能會遇到);jdbc.url里添加useSSL屬性---配置為true;若出現The server time zone value ‘?й???????’ is unrecognized or represents more than one time zone的錯誤,則需添加serverTimezone屬性,設置為GMT%2B8即可解決。
2.5、以下是引入一些其他可能需要的包,包括文件上傳等。
1 <!--其他需要的包--> 2 <dependency> 3 <groupId>org.apache.commons</groupId> 4 <artifactId>commons-lang3</artifactId> 5 <version>3.5</version> 6 </dependency> 7 <dependency> 8 <groupId>commons-fileupload</groupId> 9 <artifactId>commons-fileupload</artifactId> 10 <version>1.3.2</version> 11 </dependency>
3、配置pom.xml文件,設置項目的編譯屬性,編譯后的war包名稱(即finalName),存放配置或屬性文件到resources目錄下。
1 <build> 2 <finalName>CharlieWebDemo</finalName> 3 <resources> 4 <!--表示把java目錄下的有關xml文件,properties文件編譯/打包的時候放在resource目錄下--> 5 <resource> 6 <directory>${basedir}/src/main/java</directory> 7 <includes> 8 <include>**/*.properties</include> 9 <include>**/*.xml</include> 10 </includes> 11 </resource> 12 <resource> 13 <directory>${basedir}/src/main/resources</directory> 14 </resource> 15 </resources> 16 </build>
經過以上的pom.xml的配置,項目及springmvc所需的依賴包就已經基本引入了,通過IDEA右側欄中的maven projects的reimport按鈕實現依賴包的引入下載(并且,其中常用的是clean,package,install等,clean可以清除生成的target目錄,install可以重新生成target目錄),若pom.xml種引入/修改了新的依賴包/插件或者Dependencies存在紅色提示,需要重新reimport以下。

4、配置項目的web.xml文件,包括首頁,異常跳轉,會話超時,字符編碼過濾器CharacterEncodingFilter,監聽器ContextLoaderListener,前置控制器DispatcherServlet,靜態文件單獨處理等等。代碼及解釋如下:
<?xml version="1.0" encoding="UTF-8"?> <web-app xmlns="http://java.sun.com/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd" version="3.0"> <display-name>Archetype Created Web Application</display-name> <!--welcome pages--> <welcome-file-list> <welcome-file>index.jsp</welcome-file> </welcome-file-list> <!--當系統出現404錯誤,跳轉到頁面NoPage.html--> <error-page> <error-code>404</error-code> <location>/WEB-INF/NoPage.html</location> </error-page> <!--當系統出現java.lang.NullPointerException,跳轉到頁面error.html--> <error-page> <exception-type>java.lang.NullPointerException</exception-type> <location>/WEB-INF/error.html</location> </error-page> <!--會話超時配置,單位分鐘--> <session-config> <session-timeout>360</session-timeout> </session-config> <!--Spring框架給我們提供過濾器CharacterEncodingFilter 這個過濾器就是針對于每次瀏覽器請求進行過濾的,然后再其之上添加了父類沒有的功能即處理字符編碼。 其中encoding用來設置編碼格式,forceEncoding用來設置是否理會 request.getCharacterEncoding()方法,設置為true則強制覆蓋之前的編碼格式。--> <filter> <filter-name>characterEncodingFilter</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>characterEncodingFilter</filter-name> <url-pattern>/*</url-pattern> </filter-mapping> <!-- 項目中使用Spring時,applicationContext.xml配置文件中并沒有BeanFactory,要想在業務層中的class文件中直接引用Spring容器管理的bean可通過以下方式--> <!--1、在web.xml配置監聽器ContextLoaderListener。ContextLoaderListener的作用就是啟動Web容器時,自動裝配ApplicationContext的配置信息。因為它實現了ServletContextListener這個接口,在web.xml配置這個監聽器,啟動容器時,就會默認執行它實現的方法。 在ContextLoaderListener中關聯了ContextLoader這個類,所以整個加載配置過程由ContextLoader來完成。--> <listener> <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class> </listener> <!--2、部署applicationContext的xml文件。如果在web.xml中不寫任何參數配置信息,默認的路徑是"/WEB-INF/applicationContext.xml, 在WEB-INF目錄下創建的xml文件的名稱必須是applicationContext.xml。 如果是要自定義文件名可以在web.xml里加入contextConfigLocation這個context參數: 在<param-value> </param-value>里指定相應的xml文件名,如果有多個xml文件,可以寫在一起并以“,”號分隔,也可以這樣applicationContext-*.xml采用通配符,匹配的文件都會一同被載入。 在ContextLoaderListener中關聯了ContextLoader這個類,所以整個加載配置過程由ContextLoader來完成。--> <context-param> <param-name>contextConfigLocation</param-name> <param-value>classpath:applicationContext.xml</param-value> </context-param> <!--如果你的DispatcherServlet攔截"/",為了實現REST風格,攔截了所有的請求,那么同時對*.js,*.jpg等靜態文件的訪問也就被攔截了。--> <!--方案一:激活Tomcat的defaultServlet來處理靜態文件--> <!--要寫在DispatcherServlet的前面,讓defaultServlet先攔截請求,這樣請求就不會進入Spring了--> <servlet-mapping> <servlet-name>default</servlet-name> <url-pattern>*.css</url-pattern> </servlet-mapping> <servlet-mapping> <servlet-name>default</servlet-name> <url-pattern>*.swf</url-pattern> </servlet-mapping> <servlet-mapping> <servlet-name>default</servlet-name> <url-pattern>*.gif</url-pattern> </servlet-mapping> <servlet-mapping> <servlet-name>default</servlet-name> <url-pattern>*.jpg</url-pattern> </servlet-mapping> <servlet-mapping> <servlet-name>default</servlet-name> <url-pattern>*.png</url-pattern> </servlet-mapping> <servlet-mapping> <servlet-name>default</servlet-name> <url-pattern>*.js</url-pattern> </servlet-mapping> <servlet-mapping> <servlet-name>default</servlet-name> <url-pattern>*.html</url-pattern> </servlet-mapping> <servlet-mapping> <servlet-name>default</servlet-name> <url-pattern>*.xml</url-pattern> </servlet-mapping> <servlet-mapping> <servlet-name>default</servlet-name> <url-pattern>*.json</url-pattern> </servlet-mapping> <servlet-mapping> <servlet-name>default</servlet-name> <url-pattern>*.map</url-pattern> </servlet-mapping> <!--使用Spring MVC,配置DispatcherServlet是第一步。DispatcherServlet是一個Servlet,,所以可以配置多個DispatcherServlet--> <!--DispatcherServlet是前置控制器,配置在web.xml文件中的。攔截匹配的請求,Servlet攔截匹配規則要自已定義,把攔截下來的請求,依據某某規則分發到目標Controller(我們寫的Action)來處理。--> <!--配置SpringMVC DispatcherServlet--> <servlet> <!--在DispatcherServlet的初始化過程中,框架會在web應用的 WEB-INF文件夾下尋找名為[servlet-name]-servlet.xml 的配置文件,生成文件中定義的bean。--> <servlet-name>springMVC</servlet-name> <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class> <init-param> <!--指明了配置文件的文件名,不使用默認配置文件名,而使用spring-mvc.xml配置文件。--> <param-name>contextConfigLocation</param-name> <!--其中<param-value>**.xml</param-value> 這里可以使用多種寫法--> <!--1、不寫,使用默認值:/WEB-INF/<servlet-name>-servlet.xml--> <!--2、<param-value>/WEB-INF/classes/dispatcher-servlet.xml</param-value>--> <!--3、<param-value>classpath*:dispatcher-servlet.xml</param-value>--> <!--4、多個值用逗號分隔--> <param-value>classpath:spring-mvc.xml</param-value> </init-param> <!--是啟動順序,讓這個Servlet隨Servlet容器一起啟動。--> <load-on-startup>1</load-on-startup> <async-supported>true</async-supported> </servlet> <servlet-mapping> <!--這個Servlet的名字是dispatcher,可以有多個DispatcherServlet,是通過名字來區分的。每一個DispatcherServlet有自己的WebApplicationContext上下文對象。同時保存的ServletContext中和Request對象中.--> <!--ApplicationContext是Spring的核心,Context我們通常解釋為上下文環境,我想用“容器”來表述它更容易理解一些,ApplicationContext則是“應用的容器”了,Spring把Bean放在這個容器中,在需要的時候,用getBean方法取出--> <servlet-name>springMVC</servlet-name> <!--Servlet攔截匹配規則可以自已定義,當映射為@RequestMapping("/user/add")時,為例,攔截哪種URL合適?--> <!--1、攔截*.do、*.htm, 例如:/user/add.do,這是最傳統的方式,最簡單也最實用。不會導致靜態文件(jpg,js,css)被攔截。--> <!--2、攔截/,例如:/user/add,可以實現現在很流行的REST風格。很多互聯網類型的應用很喜歡這種風格的URL。弊端:會導致靜態文件(jpg,js,css)被攔截后不能正常顯示。 --> <url-pattern>/</url-pattern><!--會攔截URL中帶“/”的請求。--> </servlet-mapping> </web-app>
5、由web.xml的配置中看出,我們還需要配置applicationContext.xml和spring-mvc.xml,我們在spring-mvc.xml中配置springmvc的相關配置,如下:
<?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/beans http://www.springframework.org/schema/beans/spring-beans-4.3.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.3.xsd http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc.xsd"> <!--啟用spring的一些annotation --> <context:annotation-config/> <!-- 自動掃描該包,使SpringMVC認為包下用了@controller注解的類是控制器 --> <context:component-scan base-package="com.charlie.controller"> <context:include-filter type="annotation" expression="org.springframework.stereotype.Controller"/> </context:component-scan> <!--掃描service--> <context:component-scan base-package="com.charlie.service"/> <!--HandlerMapping 無需配置,SpringMVC可以默認啟動--> <!--靜態資源映射--> <!--本項目把靜態資源放在了WEB-INF的子目錄下,資源映射如下--> <mvc:resources mapping="/css/**" location="/css/"/> <mvc:resources mapping="/js/**" location="/js/"/> <mvc:resources mapping="/image/**" location="/image/"/> <!-- 配置注解驅動 可以將request參數與綁定到controller參數上 --> <mvc:annotation-driven/> <!-- 對模型視圖名稱的解析,即在模型視圖名稱添加前后綴(如果最后一個還是表示文件夾,則最后的斜杠不要漏了) 使用JSP--> <!-- 默認的視圖解析器在上邊的解析錯誤時使用 (默認使用html)- --> <bean id="defaultViewResolver" class="org.springframework.web.servlet.view.InternalResourceViewResolver"> <property name="viewClass" value="org.springframework.web.servlet.view.JstlView"/> <!--設置JSP文件的目錄位置--> <property name="prefix" value="/WEB-INF/views/"/> <property name="suffix" value=".jsp"/> </bean> <!-- SpringMVC文件上傳需要配置的節點--> <bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver"> <property name="maxUploadSize" value="20971500"/> <property name="defaultEncoding" value="UTF-8"/> <property name="resolveLazily" value="true"/> </bean> <!-- 支持返回json,用來處理json格式轉換,避免IE執行ajax時,返回json出現下載文件 --> <bean class="org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter"> <property name="messageConverters"> <list > <ref bean="mappingJacksonHttpMessageConverter" /> </list> </property> </bean> <bean id="mappingJacksonHttpMessageConverter" class="org.springframework.http.converter.json.MappingJackson2HttpMessageConverter"> <property name="supportedMediaTypes"> <list> <value>application/json;charset=UTF-8</value> </list> </property> </bean> </beans>
其中:
注解配置:<context:annotation-config/>;
注解驅動:<mvc:annotation-driven/>;
自動掃描controller類,自動掃描service類:context:component-scan;
靜態資源映射(我們springmvc配置的是攔截“/”,例如:/user/getUserInfo,REST風格,但會導致靜態文件(jpg,js,css等)被攔截后不能正常顯示):mvc:resources;
模型視圖名稱的解析(默認路徑及后綴):InternalResourceViewResolver;
支持返回json格式(前后端分離場景下,返回數據格式):AnnotationMethodHandlerAdapter,MappingJackson2HttpMessageConverter。通常在前后端分離項目中,后臺要向前端返回json格式的相應數據,以上已經在spring-mvc.xml中配置了json格式轉換的處理,我們仍需要引入json支持的依賴包,在pom.xml文件中添加以下:
<!-- 支持json,舊包 --> <dependency> <groupId>org.codehaus.jackson</groupId> <artifactId>jackson-core-asl</artifactId> <version>1.9.13</version> </dependency> <dependency> <groupId>org.codehaus.jackson</groupId> <artifactId>jackson-mapper-asl</artifactId> <version>1.9.13</version> </dependency> <!-- 使用MappingJackson2HttpMessageConverter,加入最新的FastJackson依賴 --> <dependency> <groupId>com.fasterxml.jackson.core</groupId> <artifactId>jackson-core</artifactId> <version>2.9.0.pr3</version> </dependency> <dependency> <groupId>com.fasterxml.jackson.core</groupId> <artifactId>jackson-databind</artifactId> <version>2.9.0.pr3</version> </dependency> <dependency> <groupId>com.fasterxml.jackson.core</groupId> <artifactId>jackson-annotations</artifactId> <version>2.9.0.pr3</version> </dependency> <!-- google的json格式支持 --> <dependency> <groupId>com.google.code.gson</groupId> <artifactId>gson</artifactId> <version>2.8.0</version> </dependency>
我們注意到,json依賴包存在舊包和新包,由于本項目使用的spring4.3.8,所以mappingJacksonHttpMessageConverter使用的是MappingJackson2HttpMessageConverter,代替了MappingJacksonHttpMessageConverter,否則項目啟動時會報錯;而使用MappingJackson2HttpMessageConverter時,我們就需要引入新的json依賴包了,如以上程序注釋所示(jackson-core,jackson-databind,jackson-annotations)。我們還添加了google的json格式依賴包Gson,Gson提供了fromJson() 和toJson() 兩個直接用于解析和生成的方法,前者實現反序列化,后者實現了序列化,后續的json數據返回程序示例中會再次提及。
6、配置applicationContext.xml文件,在這里我們配置數據源以及與mybatis的整合配置,內容如下所示:
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:context="http://www.springframework.org/schema/context" 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:p="http://www.springframework.org/schema/p" xsi:schemaLocation=" http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd"> <!-- 自動掃描SpringMVC包 ,將帶有注解的類 納入spring容器管理 --> <context:component-scan base-package="com.charlie"/> <!-- 引入jdbc配置文件 --> <bean id="propertyConfigurer" class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer"> <property name="locations"> <list> <value>classpath*:jdbc.properties</value> </list> </property> </bean> <!-- dataSource 配置 --> <bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource" init-method="init" destroy-method="close"> <!-- 基本屬性 url、user、password --> <property name="driverClassName" value="${jdbc.driver}"/> <property name="url" value="${jdbc.url}"/> <property name="username" value="${jdbc.username}"/> <property name="password" value="${jdbc.password}"/> <!-- 配置初始化大小、最小、最大 --> <property name="initialSize" value="${ds.initialSize}"/> <property name="minIdle" value="${ds.minIdle}"/> <property name="maxActive" value="${ds.maxActive}"/> <!-- 配置獲取連接等待超時的時間 --> <property name="maxWait" value="${ds.maxWait}"/> <!-- 配置間隔多久才進行一次檢測,檢測需要關閉的空閑連接,單位是毫秒 --> <property name="timeBetweenEvictionRunsMillis" value="${ds.timeBetweenEvictionRunsMillis}"/> <!-- 配置一個連接在池中最小生存的時間,單位是毫秒 --> <property name="minEvictableIdleTimeMillis" value="${ds.minEvictableIdleTimeMillis}"/> <property name="validationQuery" value="SELECT 'x'"/> <property name="testWhileIdle" value="true"/> <property name="testOnBorrow" value="false"/> <property name="testOnReturn" value="false"/> <!-- 打開PSCache,并且指定每個連接上PSCache的大小 --> <property name="poolPreparedStatements" value="false"/> <property name="maxPoolPreparedStatementPerConnectionSize" value="20"/> <!-- 配置監控統計攔截的filters --> <property name="filters" value="stat"/> </bean> <!-- mybatis文件配置,掃描所有mapper.xml文件 --> <!-- 配置mybatisSqlSessionFactoryBean --> <bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean"> <property name="dataSource" ref="dataSource" /> <property name="configLocation" value="classpath:mybatis.xml"/> <property name="mapperLocations" value="classpath*:com/charlie/dao/xml/*Mapper.xml"/> </bean> <!-- 配置SqlSessionTemplate --> <bean id="sqlSessionTemplate" class="org.mybatis.spring.SqlSessionTemplate"> <constructor-arg name="sqlSessionFactory" ref="sqlSessionFactory" /> </bean> <!-- 配置mybatis mapper接口,掃描所有dao --> <bean class="org.mybatis.spring.mapper.MapperScannerConfigurer"> <property name="basePackage" value="com.charlie.dao"/> <property name="sqlSessionTemplateBeanName" value="sqlSessionTemplate"/> </bean> <!-- 事務管理 通知 --> <tx:advice id="txAdvice" transaction-manager="transactionManager"> <tx:attributes> <!-- 對insert,update,delete 開頭的方法進行事務管理,只要有異常就回滾 --> <tx:method name="insert*" propagation="REQUIRED" rollback-for="java.lang.Throwable"/> <tx:method name="update*" propagation="REQUIRED" rollback-for="java.lang.Throwable"/> <tx:method name="delete*" propagation="REQUIRED" rollback-for="java.lang.Throwable"/> <!-- select,count開頭的方法,開啟只讀,提高數據庫訪問性能 --> <tx:method name="select*" read-only="true"/> <tx:method name="count*" read-only="true"/> <!-- 對其他方法 使用默認的事務管理 --> <tx:method name="*"/> </tx:attributes> </tx:advice> <aop:config> <aop:pointcut id="serviceMethods" expression="execution(* com.charlie.service..*(..))"/> <aop:advisor advice-ref="txAdvice" pointcut-ref="serviceMethods"/> </aop:config> <!-- 配置使Spring采用CGLIB代理 --> <aop:aspectj-autoproxy proxy-target-class="true"/> <!-- 對dataSource 數據源進行事務管理 --> <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager" p:dataSource-ref="dataSource"/> <!-- 使用annotation注解方式配置事務,啟用對事務注解的支持 --> <tx:annotation-driven transaction-manager="transactionManager"/> </beans>
在這里,我們引入了之前配置的jdbc.properties屬性文件來配置我們的DataSource;配置了對dataSource 數據源進行事務管理;使用annotation注解方式配置事務,啟用對事務注解的支持;
注意在這里我們添加了對mybatis的設置,實現springmvc與mybatis的整合,配置可掃描所有mapper.xml和mapper dao文件以及對事務的管理。
Springmvc整合mybatis,需要引入mybatis包,在pom.xml中添加以下依賴包:
<!--mybatis--> <dependency> <groupId>org.mybatis</groupId> <artifactId>mybatis</artifactId> <version>3.4.4</version> </dependency> <!--mybatis spring整合--> <dependency> <groupId>org.mybatis</groupId> <artifactId>mybatis-spring</artifactId> <version>1.3.1</version> </dependency>
我們如果想使用mybatis逆向工程插件生成mybatis的mapper,entity等代碼,則需要在之前的<build>標簽下添加mybatis-generator插件:
<build> <plugins> <!--mybatis 逆向工程插件--> <plugin> <groupId>org.mybatis.generator</groupId> <artifactId>mybatis-generator-maven-plugin</artifactId> <version>1.3.5</version> <configuration> <verbose>true</verbose> <overwrite>true</overwrite> </configuration> </plugin> </plugins> </build>
添加后,我們reimport下,會在maven project下新增出相應的插件,我們之后會使用到mybatis-generator:generate自動生成實體類,mapper文件,如下圖:

7、在applicationContext.xml的配置中看出,我們仍需要配置mybatis.xml的文件,進行mybatis相關的配置,如下所示:
<?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> <!-- 配置mybatis的緩存,延遲加載等等一系列屬性 --> <settings> <!-- 該配置影響的所有映射器中配置的緩存的全局開關。默認true --> <setting name="cacheEnabled" value="true" /> <!-- 延遲加載的全局開關。當開啟時,所有關聯對象都會延遲加載。 特定關聯關系中可通過設置fetchType屬性來覆蓋該項的開關狀態。默認false --> <setting name="lazyLoadingEnabled" value="true" /> <!-- 是否允許單一語句返回多結果集(需要兼容驅動)。 默認true --> <setting name="multipleResultSetsEnabled" value="true" /> <!-- 使用列標簽代替列名。不同的驅動在這方面會有不同的表現, 具體可參考相關驅動文檔或通過測試這兩種不同的模式來觀察所用驅動的結果。默認true --> <setting name="useColumnLabel" value="true" /> <!-- 允許 JDBC 支持自動生成主鍵,需要驅動兼容。 如果設置為 true 則這個設置強制使用自動生成主鍵,盡管一些驅動不能兼容但仍可正常工作(比如 Derby)。 默認false --> <setting name="useGeneratedKeys" value="false" /> <!-- 指定 MyBatis 應如何自動映射列到字段或屬性。 NONE 表示取消自動映射;PARTIAL 只會自動映射沒有定義嵌套結果集映射的結果集。 FULL 會自動映射任意復雜的結果集(無論是否嵌套)。 默認 PARTIAL --> <setting name="autoMappingBehavior" value="PARTIAL" /> <setting name="autoMappingUnknownColumnBehavior" value="WARNING" /> <!-- 配置默認的執行器。SIMPLE 就是普通的執行器;REUSE 執行器會重用預處理語句(prepared statements); BATCH 執行器將重用語句并執行批量更新。默認SIMPLE --> <setting name="defaultExecutorType" value="SIMPLE" /> <!-- 設置超時時間,它決定驅動等待數據庫響應的秒數。Not Set (null) --> <setting name="defaultStatementTimeout" value="25" /> <!-- 為驅動的結果集獲取數量(fetchSize)設置一個提示值。此參數只可以在查詢設置中被覆蓋。 --> <setting name="defaultFetchSize" value="100" /> <!-- 允許在嵌套語句中使用分頁(RowBounds)。 If allow, set the false. --> <setting name="safeRowBoundsEnabled" value="false" /> <!-- 是否開啟自動駝峰命名規則(camel case)映射,即從經典數據庫列名 A_COLUMN 到經典 Java 屬性名 aColumn 的類似映射。 --> <setting name="mapUnderscoreToCamelCase" value="false" /> <!-- MyBatis 利用本地緩存機制(Local Cache)防止循環引用(circular references)和加速重復嵌套查詢。 默認值為 SESSION,這種情況下會緩存一個會話中執行的所有查詢。 若設置值為 STATEMENT,本地會話僅用在語句執行上,對相同 SqlSession 的不同調用將不會共享數據。 --> <setting name="localCacheScope" value="SESSION" /> <!-- 當沒有為參數提供特定的 JDBC 類型時,為空值指定 JDBC 類型。 某些驅動需要指定列的 JDBC 類型,多數情況直接用一般類型即可,比如 NULL、VARCHAR 或 OTHER。 --> <setting name="jdbcTypeForNull" value="OTHER" /> <!-- 指定哪個對象的方法觸發一次延遲加載。 --> <setting name="lazyLoadTriggerMethods" value="equals,clone,hashCode,toString" /> </settings> </configuration>
OK,到目前為止,該web-demo項目的環境搭建就基本完成了。
三、web實例
webdemo環境搭建完成后,我們進行程序的實例,實現springmvc+mybatis的邏輯層級,數據庫的訪問及視圖展示等。
示例數據準備:在數據庫中創建表user,并插入一條數據:
CREATE TABLE `user` ( `id` int(11) NOT NULL AUTO_INCREMENT, `username` varchar(32) NOT NULL COMMENT '用戶名稱', `birthday` date DEFAULT NULL COMMENT '生日', `sex` char(1) DEFAULT NULL COMMENT '性別', `address` varchar(256) DEFAULT NULL COMMENT '地址', PRIMARY KEY (`id`) ) ENGINE=InnoDB AUTO_INCREMENT=27 DEFAULT CHARSET=utf8;

1、通過mybatis-generator插件,生成User表相應的實體類,mapper文件,我們首先需要在resources下創建一個generatorConfig.xml文件,內容如下(注意xml中&特殊字符的轉義,使用&表示):
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE generatorConfiguration PUBLIC "-//mybatis.org//DTD MyBatis Generator Configuration 1.0//EN" "http://mybatis.org/dtd/mybatis-generator-config_1_0.dtd"> <generatorConfiguration> <classPathEntry location="F:\IdeaProjects\MavenRepositoryForCharlie\mysql\mysql-connector-java\6.0.6\mysql-connector-java-6.0.6.jar"/> <context id="testTables" targetRuntime="MyBatis3" > <commentGenerator> <!-- 是否去除自動生成的注釋 true:是 : false:否 --> <property name="suppressAllComments" value="true" /> </commentGenerator> <!--數據庫連接的信息:驅動類、連接地址、用戶名、密碼 --> <jdbcConnection driverClass="com.mysql.cj.jdbc.Driver" connectionURL="jdbc:mysql://localhost:3306/charlie_web_demo?characterEncoding=UTF-8&useSSL=true&serverTimezone=GMT%2B8" userId="root" password="p@ssw0rd"> </jdbcConnection> <!-- 默認false,把JDBC DECIMAL 和 NUMERIC 類型解析為 Integer,為 true時把JDBC DECIMAL 和 NUMERIC 類型解析為java.math.BigDecimal --> <javaTypeResolver> <property name="forceBigDecimals" value="false" /> </javaTypeResolver> <!-- targetProject:生成PO類的位置 --> <javaModelGenerator targetPackage="com.charlie.entity" targetProject="src\main\java"> <!-- enableSubPackages:是否讓schema作為包的后綴 --> <property name="enableSubPackages" value="false" /> <!-- 從數據庫返回的值被清理前后的空格 --> <property name="trimStrings" value="true" /> </javaModelGenerator> <!-- targetProject:mapper映射文件生成的位置 --> <sqlMapGenerator targetPackage="com.charlie.dao.xml" targetProject="src\main\java"> <!-- enableSubPackages:是否讓schema作為包的后綴 --> <property name="enableSubPackages" value="false" /> </sqlMapGenerator> <!-- targetPackage:mapper接口生成的位置 --> <javaClientGenerator type="XMLMAPPER" targetPackage="com.charlie.dao.mapper" targetProject="src\main\java"> <!-- enableSubPackages:是否讓schema作為包的后綴 --> <property name="enableSubPackages" value="false" /> </javaClientGenerator> <!-- 指定數據庫表 --> <table tableName="user"></table> <!-- 有些表的字段需要指定java類型 <table schema="" tableName=""> <columnOverride column="" javaType="" /> </table> --> </context> </generatorConfiguration>
我們需要設置相應的mysql驅動包的路徑,本demo中使用的是mysql-connector-java-6.0.6.jar;設置數據庫連接信息;指定mybatis-generator生成的實體類、mapper映射文件、mapper接口文件的包路徑;并指定需要生成的數據庫表,本示例演示的是user表。我們通過執行maven projects的mybatis-generator:generate來生成相應的程序,如下圖所示:

執行完畢,會生成相應的實體類及dao文件,如下目錄結構所示:

2、在controller包下,編寫UserController.java類,代碼如下:
package com.charlie.controller; import com.charlie.common.GenericController; import com.charlie.entity.User; import com.charlie.service.UserService; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMethod; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.ResponseBody; import org.springframework.web.servlet.ModelAndView; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.util.List; @Controller @RequestMapping(value = "/user") public class UserController extends GenericController { private static final Logger logger = LoggerFactory.getLogger(UserController.class); @Autowired private UserService userService; //返回jsp視圖展示 @RequestMapping(value = "/getUserModel",method = RequestMethod.GET) public ModelAndView getUsers1(@RequestParam Integer userId) { ModelAndView modelAndView = new ModelAndView(); //調用service方法得到用戶列表 List<User> users = userService.getUsers(userId); //將得到的用戶列表內容添加到ModelAndView中 modelAndView.addObject("users",users); //設置響應的jsp視圖 modelAndView.setViewName("getUsers"); logger.info("===============================成功查詢用戶列表!"); return modelAndView; } //返回json格式數據,形式1 @RequestMapping(value = "/getUserJson1",method = RequestMethod.GET) @ResponseBody public List getUsers2(@RequestParam Integer userId, HttpServletRequest request, HttpServletResponse response) { //調用service方法得到用戶列表 List<User> users = userService.getUsers(userId); logger.info("===============================成功查詢用戶列表!"); return users; } //返回json格式數據,形式2(自定義了返回的格式) @RequestMapping(value = "/getUserJson2",method = RequestMethod.GET) public void getUsers3(@RequestParam Integer userId, HttpServletRequest request, HttpServletResponse response) { //調用service方法得到用戶列表 List<User> users = userService.getUsers(userId); logger.info("===============================成功查詢用戶列表!"); renderSuccessString(response, users); } }
其中,@controller注解標注本類為controller類;
@RequestMapping注解實現web REST風格請求的映射;
@Autowired用來綁定service實現類;
方法getUsers1實現了JSP視圖的解析和展示,由于在spring-mvc.xml中已經設置了jsp視圖解析的配置,這里modelAndView.setViewName("getUsers")就直接響應的jsp視圖為/WEB-INF/views下的getUsers.jsp文件(詳見github項目代碼)。
由于在spring-mvc.xml中已經配置了支持json數據的處理,這里我們可直接使用@ResponseBody注解就可實現json格式數據的返回,如方法getUsers2所示。
方法getUsers3則使用的Gson的json格式支持,我們在程序中,通過common包下的GenericController類的renderSuccessString等方法結合Result自定義類自定義了返回數據的json內容。
以上3種方法的效果會在后續介紹中展示。
3、在dao-->mapper包下,編寫UserMapper.java,@Repository注解標注本類為mapper dao類,本次示例我們直接使用selectByExample方法。
package com.charlie.dao.mapper; import com.charlie.entity.User; import com.charlie.entity.UserExample; import java.util.List; import org.apache.ibatis.annotations.Param; import org.springframework.stereotype.Repository; @Repository public interface UserMapper { long countByExample(UserExample example); int deleteByExample(UserExample example); int insert(User record); int insertSelective(User record); List<User> selectByExample(UserExample example); int updateByExampleSelective(@Param("record") User record, @Param("example") UserExample example); int updateByExample(@Param("record") User record, @Param("example") UserExample example); }
dao文件對應的mapper映射文件UserMapper.xml,使用的是mybatis-generator默認生成的,此處不再描述。
4、在service包下編寫UserService.java類,并在service-impl包下編寫UserServiceImpl.java實現此類的方法,如下:
UserService.java
package com.charlie.service; import com.charlie.entity.User; import java.util.List; public interface UserService { List<User> getUsers(Integer userId); }
UserServiceImpl.java(使用@Service注解,標注為service實現類,并@Autowired綁定相應的UserMapper)
package com.charlie.service.impl; import com.charlie.entity.User; import com.charlie.dao.mapper.UserMapper; import com.charlie.entity.UserExample; import com.charlie.service.UserService; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import java.util.List; @Service public class UserServiceImpl implements UserService { @Autowired private UserMapper userMapper; public List<User> getUsers(Integer userId) { UserExample example = new UserExample(); UserExample.Criteria criteria = example.createCriteria(); if (!"".equals(userId == null ? "" : userId)) { criteria.andIdEqualTo(userId); } return userMapper.selectByExample(example); } }
至此,我們的程序示例編寫完成,下面我們以REST訪問方式,展示下訪問的效果。
①測試JSP視圖展示效果,訪問http://localhost:8080/user/getUserModel?userId=1,效果如下:

②測試原始的@ResponseBody注解的json數據返回格式效果,訪問http://localhost:8080/user/getUserJson1?userId=1,效果如下:

注意到,使用這種json方式,birthday將數據庫的日期格式轉化為了時間戳,通常這不是我們所期望的,我們可以通過特定的方法調整json返回的日期格式轉換;也可以通過一下的Gson的方式處理json數據。
③測試Gson自定義了具體的json數據返回格式效果,訪問http://localhost:8080/user/getUserJson2?userId=1,效果如下:

提示:以上2種瀏覽器中格式化的json數據是由JSON View插件實現的。若用戶在調試過程中想要達到格式化的json數據展示,可以通過安裝Chrome瀏覽器的插件JSON View實現。
本文,旨在快速搭建一個springmvc+mybatis整合的通用的web-demo,其中springmvc或mybatis深入的使用方法,需要大家再去研究,這里不再贅述。
在github源代碼鏈接里,新添加了本次demo的war包文件CharlieWebDemo.war,感興趣也可以下載下來運行測試。
浙公網安備 33010602011771號