[入門級]Spring + SpringMVC + Mybatis工程搭建簡單教程

0x0 前言

標題里的這些都是什么我就不細說了,點開這篇博客的你至少應該已經知道了一些。最近工作需要,想學點Spring的東西,看了一些網上關于Spring入門的博客,感覺這些博客面向初學者的話還是有不少沒有講清楚的地方,比如至關重要的配置文件的在工程中的路徑,還有某些配置項在xml文件中的所處的節點。也許大牛們覺得這些都太基礎了沒必要全寫出來占著篇幅,要留給展示操作過程的ide界面截圖。這是很多代碼玩家的另外一個問題,過于依賴ide功能,而不去嘗試脫離ide完成同樣功能的方式,所以為了展示一個操作,不得不放上ide的截圖(如果截圖暴露了作者還用的是xp這種古董操作系統或者vc6.0這種古董ide,那簡直要low穿地殼了)。所以本教程中————雖然內容是入門級的————我采用了一些我認為逼格比較高的方式,首先盡可能的描述清楚每一行代碼所處的文件的在工程中的路徑,和代碼在文件中的位置,當前限于篇幅,我也不可能精確到每個細節,所以我給出了完整工程托管在github上的路徑。另外,盡可能脫離ide,構建和部署均采用命令行和修改配置文件的方式。

工程代碼托管在https://github.com/knightingal/SpringMVCStart.git

數據庫:MySql 5.7

構建工具:apache-maven-3.3.9

容器:apache-tomcat-9.0.0.M4

0x1 還是從最簡單的hello world開始

一個最精簡的基于SpringMVC的java web工程需要以下幾個文件:
首先是導入SpringMVC依賴jar包的pom.xml文件

./pom.xml

<!-- 省略了頭部的名字空間聲明 -->
<project
    ...
    >
<modelVersion>4.0.0</modelVersion>
<groupId>SpringMVCStart</groupId>
<artifactId>SpringMVCStart</artifactId>
<version>1.0-SNAPSHOT</version>
<packaging>war</packaging>

<dependencies>
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-webmvc</artifactId>
        <version>4.2.4.RELEASE</version>
    </dependency>
</dependencies>
</project>

packaging項填寫war才可以最終將工程打包成war包;依賴管理部分,只要導入spring-webmvc,maven就會自動將其依賴的spring-core、spring-context、spring-bean等等一并包含進來。

然后是作為一個java web工程最重要的web.xml文件

./src/main/webapp/WEB-INF/web.xml

<!-- 省略了頭部的名字空間聲明 -->
<web-app
    ...
    >
<display-name>SpringMVCStart</display-name>
<servlet>
    <servlet-name>SpringMVCStart</servlet-name>
    <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
    <load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
    <servlet-name>SpringMVCStart</servlet-name>
    <url-pattern>/</url-pattern>
</servlet-mapping>
</web-app>

這里定義了一個名為SpringMVCStart的servlet,其接收類為org.springframework.web.servlet.DispatcherServlet,容器啟動時加載,然后在servlet-mapping中將這個servlet映射給url/,即所有路徑下的請求均由org.springframework.web.servlet.DispatcherServlet這個類進行分發。于是在這里就引入了spring的webmvc框架。

接下來我們就需要spring的webmvc框架的配置文件。

由于我們的servlet名字叫'SpringMVCStart',在默認情況下,springmvc框架會加載WEB-INF/SpringMVCStart-servlet.xml文件作為配置文件。

./src/main/webapp/WEB-INF/SpringMVCStart-servlet.xml

<!-- 省略了頭部的名字空間聲明 -->
<beans
    ...
    >
<mvc:annotation-driven/>
<context:component-scan base-package="org.home.knightingal.controller" use-default-filters="false">
    <context:include-filter type="annotation" expression="org.springframework.stereotype.Controller"/>
</context:component-scan>
</beans>

其中,<mvc:annotation-driven/>意味著開啟spring mvc相關的注解,<context:component-scan>...</context:component-scan>的意思是,掃描org.home.knightingal.controller路徑下所有帶有org.springframework.stereotype.Controller注解的類,將其作為controller。

那么接下來就是controller類,比如:

./src/main/java/org/home/knightingal/controller/HelloWorldController.java

package org.home.knightingal.controller;

// 省略一堆import

@Controller
@RequestMapping(value="/helloworld")
public class HelloWorldController {

    @RequestMapping(value="/index")
    @ResponseBody
    public String index() {
        return "Hello world";
    }
}

首先是@Controller注解使得spring mvc框架可以掃描到這個類,將其作為一個controller,接下來兩個@RequestMapping注解將index方法綁定給url路徑/helloworld/index@ResponseBody表示將返回值直接作為響應的消息體,否則框架會將方法返回的字符串值理解為響應頁面的名字。新版的springmvc框架對于將方法的返回值直接作為響應體的場景有了更簡便的注解類型,這里還是沿用老辦法。

到目前為止一個基于springmvc的java web項目就搭建完成了,雖然我們只有mvc中的c部分。

我假定你已經配置好了jdk和maven的環境變量,接下來我們在cmd或者shell中輸入命令

mvn package

maven就會執行打包程序,它會首先去maven中央倉庫下載必要的jar包和插件,首次運行這部分過程可能會持續比較長的時間,取決于你和maven中央倉庫的連接網速,必要時請自行科學上網。當jar包和插件都下載完畢,它會執行javac去編譯java源碼,如果有單元測試的話此編譯完成后它還會執行單元測試,最后它會將編譯后的class文件,以及java web需要的各類配置文件一并打成war包,放入target目錄下。

如果一切正常,那么此時你應該可以看到

[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time: 1.286 s
[INFO] Finished at: 2016-05-12T00:20:08+08:00
[INFO] Final Memory: 12M/147M
[INFO] ------------------------------------------------------------------------

類似字樣。盡管不是必須的,但我還是建議你把maven輸出的完成記錄稍微過目一下,了解一下maven構建和打包一個java工程的打包過程。

下面是部署這個web工程,這里我就不說怎么用eclipse全家桶自動部署了,單單說一下手動部署。

打開tomcat路徑下的/conf/server.xml文件,找到<Host>節點,在這個節點下添加下面的內容

<Context path="/SpringMVCStart" docBase="D:\SpringMVCStart\target\SpringMVCStart-1.0-SNAPSHOT" reloadable="true" >
</Context>

docBase屬性的值替換成你自己的版本。

改好了以后啟動tomcat,待啟動成功之后,假設你沒有改tomcat的監聽端口,那么在瀏覽器地址欄輸入http://localhost:8080/SpringMVCStart/helloworld/index,應該就可以看到"hello world"字樣了。

0x2 數據庫支持

在./pom.xml里面添加mysql-connector和jdbc相關依賴

<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-jdbc</artifactId>
    <version>4.2.4.RELEASE</version>
</dependency>

<dependency>
    <groupId>mysql</groupId>
    <artifactId>mysql-connector-java</artifactId>
    <version>5.1.38</version>
</dependency>

<dependency>
    <groupId>commons-dbcp</groupId>
    <artifactId>commons-dbcp</artifactId>
    <version>1.4</version>
</dependency>

./src/main/webapp/WEB-INF/SpringMVCStart-servlet.xml中配置datasource連接參數

<bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close">
    <property name="driverClassName" value="com.mysql.jdbc.Driver" />
    <property name="url" value="jdbc:mysql://127.0.0.1:3306/world"/>   <!-- 修改為你自己的數據庫地址-->
    <property name="username" value="Knightingal"/>                    <!-- 修改為你自己的用戶名 -->
    <property name="password" value="123456"/>                         <!-- 修改為你自己的密碼 -->
</bean>

這里配置了一個org.apache.commons.dbcp.BasicDataSource類型的實例,以及該實例中driverClassName,url,username,password成員變量的值。后續配置中可使用dataSource這個id引用該實例。

以及配置jdbcTemplate。

<bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
    <property name="dataSource" ref="dataSource"/>
</bean>

稍后在Controller中便可通過@Autowired注解將org.springframework.jdbc.core.JdbcTemplate類型的實例反向注入到名為jdbcTemplate的成員變量上,并且該實例中的dataSource成員變量已經注入了之前配置的id為dataSourceorg.apache.commons.dbcp.BasicDataSource類型實例。

至此數據源的配置就完成了。

0x3 在Controller中使用JdbcTemplate進行數據庫操作

現在我們配置一個新的Controller進行數據庫操作。在此之前我們先了解一下需要查詢的數據庫的表結構及數據。

這里我們使用MySql預裝的Demo庫World,其中有city,country,countrylanguage三張表,從名字就能猜出里面的數據是什么。本教程中只需要其中的city就可以了。也許你的MySql里面沒有預裝這個Demo庫,沒關系,我把city的表結構和一部分數據導出來了放到這里了https://github.com/knightingal/SpringMVCStart/blob/master/dbsetup/city.sql

簡單的說,city表的表結構如下:

Field Type Null Key Default Extra
ID int(11) NO PRI auto_increment
Name char(35) NO
CountryCode char(3) NO MUL
District char(20) NO
Population int(11) NO 0

根據這表結構,我們可以建立一個名為City的bean

./src/main/java/org/home/knightingal/bean/City.java

public class City {

    private Integer id;

    private String name;

    private String countryCode;

    private String district;

    private Integer population;

    // getter and setter...

}    

接下來新建一個Controller,接受一個請求來查詢這張表,將查詢結果放入City的列表作為查詢請求的響應。

./src/main/java/org/home/knightingal/controller/CityController.java

//省略package和import

@Controller
@RequestMapping(value="/city")
public class CityController {

    @Autowired
    JdbcTemplate jdbcTemplate;

    @RequestMapping(value="/simpleQueryCities")
    @ResponseBody
    public List<City> simpleQueryCities() {
        final List<City> cities = new ArrayList<City>();
        jdbcTemplate.query("select id, name, countryCode, district, population from city limit 0, 10", new RowCallbackHandler() {
            public void processRow(ResultSet resultSet) throws SQLException {
                System.out.println(resultSet.getString(2));

                City city = new City();
                city.setId(resultSet.getInt(1));
                city.setName(resultSet.getString(2));
                city.setCountryCode(resultSet.getString(3));
                city.setDistrict(resultSet.getString(4));
                city.setPopulation(resultSet.getInt(5));

                cities.add(city);
            }
        });
        return cities;
    }
}

重新構建之后在瀏覽器中輸入http://localhost:8080/SpringMVCStart/city/simpleQueryCities,返回了500錯誤。但是在Tomcat的運行窗口中我們看到打印出了查詢到的城市名字,說明查詢是成功的。

那我們回過頭看看500錯誤的描述,可以看到這樣的信息:

No converter found for return value of type: class java.util.ArrayList

問題出在simpleQueryCities的返回值,這個方法返回的是一個ArrayList類型的對象,框架無法對這個對象進行轉換,成為具備良好閱讀性的響應體。

這時我們需要引入jackson作為converter。在./pom.xml里面添加jackson的依賴

<dependency>
    <groupId>com.fasterxml.jackson.core</groupId>
    <artifactId>jackson-databind</artifactId>
    <version>2.7.3</version>
</dependency>

SpringMVC框架會自動識別出jackson并作為其converter,不需要做另行配置。

重新打包部署后,再次刷新http://localhost:8080/SpringMVCStart/city/simpleQueryCities頁面,就可以看到處理成json格式的城市信息了。

0x4 引入Mybatis進行數據庫操作

在./pom.xml中添加如下依賴

<dependency>
    <groupId>org.mybatis</groupId>
    <artifactId>mybatis</artifactId>
    <version>3.4.0</version>
</dependency>

<dependency>
    <groupId>org.mybatis</groupId>
    <artifactId>mybatis-spring</artifactId>
    <version>1.3.0</version>
</dependency>

第一個是mybatis的主包,另一個mybatis-spring可以將mybatis的代碼無縫整合到spring中。

接下來配置一個簡單的dao接口。由于只是個簡單的演示項目,所以不打算添加常見的service層了,controller直接調用dao層。

./src/main/java/org/home/knightingal/dao/CityDao.java

public interface CityDao {
    List<City> queryCities();
}

CityController中加入對queryCities的調用

./src/main/java/org/home/knightingal/controller/CityController.java


@Controller
@RequestMapping(value="/city")
public class CityController {

    //略...

    @Autowired
    CityDao cityDao;

    @RequestMapping(value="/queryCities")
    @ResponseBody
    public List<City> queryCities() {
        return cityDao.queryCities();
    }
}

有了接口,總得有實現吧,還有那個cityDao總不能就這樣放著等著運行的時候給你來個空指針異常吧。下面是通過mybatis做的CityDao的實現

./src/main/resources/org/home/knightingal/dao/CityDao.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="org.home.knightingal.dao.CityDao">
    <select id="queryCities" resultType="org.home.knightingal.bean.City">
        select id, name, countryCode, district, population from city
        limit 0, 10
    </select>
</mapper>

以及cityDao變量的裝配。
./src/main/webapp/WEB-INF/SpringMVCStart-servlet.xml

<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
    <property name="dataSource" ref="dataSource"/>
</bean>

<bean id="cityDao" class="org.mybatis.spring.mapper.MapperFactoryBean">
    <property name="mapperInterface" value="org.home.knightingal.dao.CityDao" />
    <property name="sqlSessionFactory" ref="sqlSessionFactory" />
</bean>
<bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
    <property name="basePackage" value="org.home.knightingal.dao" />
</bean>

在配置文件中,我們可以看到cityDao的實際父類型是org.mybatis.spring.mapper.MapperFactoryBean<property name="mapperInterface" value="org.home.knightingal.dao.CityDao" />這行配置了cityDao的接口。<property name="sqlSessionFactory" ref="sqlSessionFactory" />這行裝配了MapperFactoryBean中的sqlSessionFactory成員變量,該變量中裝配了dataSource,即我們之前配置的dataSource。由于@Autowired注解的作用,這個bean會自動注入給變量CityDao cityDao

org.mybatis.spring.mapper.MapperScannerConfigurer這個bean配置則指定了mybatis去那個路徑下搜索dao接口關聯的sql配置文件。

重新編譯運行后輸入地址http://localhost:8080/SpringMVCStart/city/queryCities即可看到運行結果。

0x5 加入jsp頁面

目前為止mvc架構中我們已經有了controller和model部分,對于很多純后臺系統而言已經夠了,并不是每個系統都一定要有一個前臺展示的view。但是呢,既然說到這了,再把view的部分加上也不是什么難事。

首先是引入jsp依賴的jstl庫

<dependency>
    <groupId>javax.servlet</groupId>
    <artifactId>jstl</artifactId>
    <version>1.2</version>
</dependency>

jsp的配置,就不啰嗦是在哪個文件里面配的了。

<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
    <property name="viewClass" value="org.springframework.web.servlet.view.JstlView"/>
    <property name="contentType" value="text/html"/>
    <property name="prefix" value="/WEB-INF/views/"/>
    <property name="suffix" value=".jsp"/>
</bean>

queryCities的請求中返回jsp頁面queryCities,注意,去掉了@ResponseBody注解,否則一會兒你在頁面上只能看到"queryCities"幾個字,是不是頓時覺得自己萌萌噠?

@RequestMapping(value="/queryCities")
public String queryCities(
        @RequestParam(value="countryCode", required=false) String countryCode,
        Model model
) {
    City param = new City();
    param.setCountryCode(countryCode);

    List<City> cities = cityDao.queryCities(param);
    model.addAttribute("cities", cities);
    return "queryCities";  
}

這里我還稍微修改了CityDao.queryCities()接口,讓它可以接受一個City類型的入參,帶入查詢條件,

public interface CityDao {
    List<City> queryCities(City param);
}

這么做的目的是驗證mybatis動態sql的特性。

<mapper namespace="org.home.knightingal.dao.CityDao">
    <select id="queryCities" resultType="org.home.knightingal.bean.City">
        select id, name, countryCode, district, population from city
        where
            1 = 1
            <if test="countryCode != null">
                and countryCode = #{countryCode}
            </if>
        limit 0, 10
    </select>
</mapper>

最后為了完成查詢結果的展示,現學現賣的寫一個queryCities.jsp頁面,根據之前的配置,放在/WEB-INF/views/目錄下。

./src/main/webapp/WEB-INF/views/queryCities.jsp

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"
    import="java.util.List, org.home.knightingal.bean.City"
    %>
<!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>queryCities</title>
</head>
<body>
    <table border="1">
        <tr>
            <th>ID</th>
            <th>NAME</th>
            <th>COUNTRY CODE</th>
            <th>DISTRICT</th>
            <th>POPULATION</th>
        </tr>
        <%
        List<City> cities = ((List<City>)request.getAttribute("cities"));
        for (int i = 0; i < cities.size(); i++) {
        %>
        <tr>
            <td>
            <%= cities.get(i).getId() %>
            </td>
            <td>
            <%= cities.get(i).getName() %>
            </td>
            <td>
            <%= cities.get(i).getCountryCode() %>
            </td>
            <td>
            <%= cities.get(i).getDistrict() %>
            </td>
            <td>
            <%= cities.get(i).getPopulation() %>
            </td>
        </tr>
        <% } %>
    </table>
</body>
</html>

關于這個jsp頁面我就不做講解了,我所有的前端水平只夠寫出這些來,一個樣式看起來很乏味的表格,顯示了一些從數據庫里查出來的數據。

0x6 結語

到目前為止,這個簡單的spring+spring-mvc+mybatis工程的搭建演示過程的講解就算完成了。整個工程開發從無到有的過程都在我之前給出的github倉庫里,每一次提交我都打了tag,如果我有沒講明白的地方,就去那里看吧。

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 227,797評論 6 531
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 98,179評論 3 414
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 175,628評論 0 373
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 62,642評論 1 309
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 71,444評論 6 405
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 54,948評論 1 321
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,040評論 3 440
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,185評論 0 287
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 48,717評論 1 333
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 40,602評論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 42,794評論 1 369
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,316評論 5 358
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,045評論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,418評論 0 26
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,671評論 1 281
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,414評論 3 390
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 47,750評論 2 370

推薦閱讀更多精彩內容