[入门级]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阅读 220,367评论 6 512
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 93,959评论 3 396
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 166,750评论 0 357
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 59,226评论 1 295
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 68,252评论 6 397
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,975评论 1 308
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,592评论 3 420
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,497评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 46,027评论 1 319
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 38,147评论 3 340
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 40,274评论 1 352
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,953评论 5 347
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,623评论 3 331
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 32,143评论 0 23
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 33,260评论 1 272
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,607评论 3 375
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 45,271评论 2 358

推荐阅读更多精彩内容