1. 前情回顾
之前我们已经用SpringMVC+JSP+Boostrap+原生JDBC实现过博客系统。
然后我们将其改为了前后端分离的Spring Restful+jQuery+Bootstrap+原生JDBC实现的博客系统。
本篇我们将完整的实现一个Spring+SpringMVC+SpringJDBC+jQuery+Bootstrap的博客系统,说的很复杂,实际上后端还是Restful风格的API,前端还是通过jQuery调用后端,Bootstrap仅负责页面样式。
OK,通过本篇希望大家将之前的知识点都串起来,同时我们的项目规范上也更加贴近项目实战多一些。
OK,前情回顾完毕,开整~~
2. 概述
功能:很简单,就是实现博客的增删改查。
数据库:MySQL,表结构如下:
CREATE TABLE `blog` (
`id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '唯一标志',
`title` varchar(255) DEFAULT '' COMMENT '标题',
`author` varchar(255) DEFAULT '' COMMENT '作者姓名',
`content` longtext COMMENT '内容',
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=35 DEFAULT CHARSET=utf8;
项目分层:
视图层(Html+Bootstrap+jQuery),负责页面显示
控制层(Controller),负责接受页面请求,并调用服务层完成业务逻辑,最后返回数据
服务层(Service),负责调用数据访问层,完成对数据库的增删改查操作,并封装业务逻辑
数据访问层(DAO),负责操作数据库
3. 新建项目
新建Dynamic web project,项目名称【springjdbcblog】,然后将Spring相关jar包以及下面几个jar包放入lib目录下:
commons-logging-1.2.jar 日志相关
jackson-annotations-2.8.0.jar json相关
jackson-core-2.8.0.jar json相关
jackson-databind-2.8.0.jar json相关
mysql-connector-java-5.1.48.jar mysql驱动
druid-1.1.21.jar 数据库连接池
4. 新建包及目录、文件
建立如下图项目结构:
5. 完整开发过程
本次我们按照从底层到外层的开发方式,也就是从数据对象–数据访问层–服务层–控制器层–视图页面的顺序。
5.1 封装数据对象
数据对象与数据库表是一一对应关系,根据表中的列设计数据对象类的属性即可,代码如下:
package org.maoge.sjblog.xdo;
/**
* @theme 数据对象--博客
* @author maoge
* @date 2020-01-29
*/
public class BlogDo {
private Long id;
private String title;
private String author;
private String content;
// 省略get get
}
5.2 封装数据访问层,并注册为bean
我们将博客数据访问层封装为BlogDao,并通过@Repositoy注册为bean,然后通过@Autowired注入jdbcTemplate对象。
package org.maoge.sjblog.dao;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.maoge.sjblog.xdo.BlogDo;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.RowMapper;
import org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate;
import org.springframework.stereotype.Repository;
/**
* @theme DAO--博客
* @author maoge
* @date 2020-01-29
*/
@Repository // 注册为组件
public class BlogDao {
@Autowired // 自动注入
private NamedParameterJdbcTemplate namedTemplate;
/**
* 新增
*/
public void insert(BlogDo blog) {
Map map = new HashMap<>();
map.put("author", blog.getAuthor());
map.put("content", blog.getContent());
map.put("title", blog.getTitle());
// 注意使用:xxx占位
namedTemplate.update("insert into blog(author,content,title)values(:author,:content,:title)", map);
}
/**
* 删除
*/
public void delete(Long id) {
Map map = new HashMap<>();
map.put("id", id);
namedTemplate.update("delete from blog where id =:id", map);
}
/**
* 更新
*/
public void update(BlogDo blog) {
Map map = new HashMap<>();
map.put("author", blog.getAuthor());
map.put("content", blog.getContent());
map.put("title", blog.getTitle());
map.put("id", blog.getId());
namedTemplate.update("update blog set author=:author,content=:content,title=:title where id=:id", map);
}
/**
* 按id查询
*/
public BlogDo getById(Long id) {
Map map = new HashMap<>();
map.put("id", id);
return namedTemplate.queryForObject("select * from blog where id=:id", map, new RowMapper() {
@Override
public BlogDo mapRow(ResultSet rs, int rowNum) throws SQLException {
BlogDo blog = new BlogDo();
blog.setAuthor(rs.getString("author"));
blog.setContent(rs.getString("content"));
blog.setId(rs.getLong("id"));
blog.setTitle(rs.getString("title"));
return blog;
}
});
}
/**
* 查询列表
*/
public List getList() {
return namedTemplate.query("select * from blog", new RowMapper() {
@Override
public BlogDo mapRow(ResultSet rs, int rowNum) throws SQLException {
BlogDo blog = new BlogDo();
blog.setAuthor(rs.getString("author"));
blog.setContent(rs.getString("content"));
blog.setId(rs.getLong("id"));
blog.setTitle(rs.getString("title"));
return blog;
}
});
}
}
5.3 编写服务类,封装对博客的操作方法
直接我们是在控制器中直接调用数据接口层,这种封装会导致当业务逻辑比较复杂时,控制器层会有很多操作代码。
实际上控制器层应该只负责接受输入,调用方法完成业务逻辑,然后返回结果。
DAO层方法只是简单的对数据源完成增删改查等操作,显然不足以支撑业务逻辑。
所以一般在控制器层和DAO层之间,有一个服务层,封装业务逻辑和对数据源的操作,此处编写一个BlogService类实现该部分功能。
package org.maoge.sjblog.service;
import java.util.List;
import org.maoge.sjblog.dao.BlogDao;
import org.maoge.sjblog.xdo.BlogDo;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
/**
* @theme 服务--博客
* @author maoge
* @date 2020-01-27
*/
@Service//替代@Component
public class BlogService {
@Autowired // 自动注入BlogDao组件
private BlogDao blogDao;
/**
* 获取博客列表
*/
public List getBlogList() {
return blogDao.getList();
}
/**
* 按id获取博客信息
*/
public BlogDo getBlogById(Long id) {
return blogDao.getById(id);
}
/**
* 新增博客
*/
public void addBlog(BlogDo blog) {
blogDao.insert(blog);
}
/**
* 根据博客id更新博客信息
*/
public void updateBlog(BlogDo blog) {
blogDao.update(blog);
}
/**
* 根据博客id删除对应博客
*/
public void deleteBlog(Long id) {
blogDao.delete(id);
}
}
5.4 编写控制器,提供http接口
然后我们编写BlogController,按照Restful风格提供http接口,代码如下:
package org.maoge.sjblog.controller;
import java.util.List;
import org.maoge.sjblog.service.BlogService;
import org.maoge.sjblog.xdo.BlogDo;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.PutMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RestController;
/**
* @theme 控制器--博客
* @author maoge
* @date 2020-01-28
*/
@RestController // 通过该注解,第一将BlogController注册为控制器,第二将其中方法返回值转换为json
public class BlogController {
@Autowired // 自动装配blogService
private BlogService blogService;
/**
* 查询博客信息
* 1、@GetMapping表示可以使用get方法请求该api
* 2、"/blog/{id}"表示请求路径为/blog/{id}的形式,其中{id}为占位符
* 3、@PathVariable("id")表示将占位符{id}的值传递给id
* 4、也就是说/blog/123请求的话,会将123传递给参数id
*/
@GetMapping(value="/blog/{id}")
public BlogDo getOne(@PathVariable("id") long id) {
return blogService.getBlogById(id);
}
/**
* 查询博客列表,使用get方法
*/
@GetMapping("/blog")
public List getList(){
return blogService.getBlogList();
}
/**
* 新增博客
* 1、@PostMapping表示使用post方法
* 2、@RequestBody表示将请求中的json信息转换为BlogDo类型的对象信息,该转换也是由SpringMVC自动完成的
*/
@PostMapping("/blog")
public void add(@RequestBody BlogDo blog) {
blogService.addBlog(blog);
}
/**
* 修改博客
* 实际上此处也可以不在路径中传递id,而是整个使用json传递对象信息,但是我查询了一些文档,貌似使用路径传递id更加规范一些,此处不用纠结
*/
@PutMapping("/blog/{id}")
public void update(@PathVariable("id") long id,@RequestBody BlogDo blog) {
//修改指定id的博客信息
blog.setId(id);
blogService.updateBlog(blog);
}
/**
* 删除博客
*/
@DeleteMapping("/blog/{id}")
public void delete(@PathVariable("id") long id) {
blogService.deleteBlog(id);
}
}
5.5 编写html页面,调用api接口完成操作
编写html页面,通过jQuery的ajax调用BlogController里面的api方法,代码如下:
integrity="sha384-BVYiiSIFeK1dGmJRAkycuHAHRg32OmUcww7on3RYdg4Va+PmSTsz/K68vbdEjh4u" crossorigin="anonymous">
<div data-lake-id="8d84cce1d2d73cda9a0c92b50d1457d1"> integrity="sha384-Tc5IQib027qvyjSMfHjOMaLkfuWVxZxUPnCJA7l2mCWNIpG9mGCD8wGNIcPD7Txa" crossorigin="anonymous"></div><div data-lake-id="1e4744a3d77b91e92090251b05c740db">
</div><div data-lake-id="df4dccd8f0b00975076ca0b1c0159675"> //浏览博客</div><div data-lake-id="362df7f6715f96bd70acb9c2d04297df"> function viewBlogs() {</div><div data-lake-id="86d1b35904b82dffc404252d92dc3d2c"> var row = "";</div><div data-lake-id="16ce686e9734201f86af1a59752416e3"> //先清空表格</div><div data-lake-id="a778976977ad6bb0fa51569be9be63c8"> $('#blogTable').find("tr:gt(0)").remove();</div><div data-lake-id="01655a041ba4e3702a4b50cecead2a67"> $.ajax({</div><div data-lake-id="d3abd510e751ba4a303b05a3ae12a808"> type: "GET",</div><div data-lake-id="307e172b6eb415c09d5c280049099f8f"> url: "/restfulblog/blog",</div><div data-lake-id="1fbcd2ff429fd19be3fffbdd736ad8e7"> dataType: "json",</div><div data-lake-id="97bdffb2e0475a7dcbd1d6f75006073d"> contentType: "application/json; charset=utf-8",</div><div data-lake-id="4077b87a8785f2dafe784d470cc63da6"> success: function (res) {</div><div data-lake-id="75b9301abe37257725df3f142fdd5b88"> $.each(res, function (i, v) {</div><div data-lake-id="e3c2b88f03525f2a5333febc537a6777"> row = "<tr>";</div><div data-lake-id="92dbb591768c8854bc09838aeb411c2b"> row += "<td>" + v.id + "</td>";</div><div data-lake-id="bb755a3c5f03e508a23286e2b4821210"> row += "<td>" + v.title + "</td>";</div><div data-lake-id="97ff4a98d61e4869c7d9c0221cc7963c"> row += "<td>" + v.author + "</td>";</div><div data-lake-id="57aeb3cb9cd3c32b1d960875d0049a02"> row +=</div><div data-lake-id="644b1b84846cb7bcf90ec10e8e61a854"> "<td><a class='btn btn-primary btn-sm' href='#' οnclick='editBlog(" + v.id +</div><div data-lake-id="6713c7747ad4f23f413824d5b6612596"> ")'>编辑</a>";</div><div data-lake-id="e4ec78052fcf6424ff83748a6bd6f9d9"> row +=</div><div data-lake-id="cc8676ec47b88d7aef1d83c59fb37448"> "<a class='btn btn-danger btn-sm' href='#' οnclick='deleteBlog(" + v.id +</div><div data-lake-id="70f33cce11ffe6be6948d5afea77d49c"> ")'>删除</a></td>";</div><div data-lake-id="2378c649c3e253df0fe3c839f6086549"> row += "</tr>";</div><div data-lake-id="8e212ab99ddbab0145fb359d18e225c2"> $("#blogTable").append(row);</div><div data-lake-id="a34ae617d8ab021e198e9c017e2efdbf"> });</div><div data-lake-id="e41d1a1cc9c73391c14f908ff1b78789"> },</div><div data-lake-id="d3cbc05a3432ce6e54529a2e3b89f3f3"> error: function (err) {</div><div data-lake-id="dab5149ac0ef53fdef6ffea3e527feec"> console.log(err);</div><div data-lake-id="da22821ff3907af70d3bd4b26d89130c"> }</div><div data-lake-id="2d39541c45210f5447b283a8a9d88ffb"> });</div><div data-lake-id="503deeeb75ddbc98703d2a72bc500271"> }</div><div data-lake-id="742d87d69e858c96f33e6cf2720219f7"> //新增</div><div data-lake-id="ecd49182034852e2e5594e2d29641d9b"> function addBlog() {</div><div data-lake-id="335aa32ef7282caf6e47f0536229fee2"> $('#blogAddModal').modal('show');</div><div data-lake-id="66b24ff76a1b1fb9eb4e3df518c6732e"> }</div><div data-lake-id="c85456257bfbd45276cb86a610335fff"> //新增提交</div><div data-lake-id="b2c95880c661d41ab51a53600e4a3eb6"> function addBlogSubmit() {</div><div data-lake-id="2c43754e053ac98cc84a857ea872983e"> var data = {</div><div data-lake-id="9a46f738e42fab907738a69f1b61f092"> id: '',</div><div data-lake-id="d8e6c76f3e8102e70d45a2c526ae461b"> title: $("#blogAddModal input[name='title']").val(),</div><div data-lake-id="629ddd60886feb998ea798453dac9661"> author: $("#blogAddModal input[name='author']").val(),</div><div data-lake-id="afdb8fee5ec04b0de1c0af69ca1ba142"> content: $("#blogAddModal textarea[name='content']").val()</div><div data-lake-id="2fb100e149e63adde3b393a4a45bae8e"> };</div><div data-lake-id="23c364f864f0be89125782e00a3a5543"> $.ajax({</div><div data-lake-id="4447ab0c0b9eef82b6eca12e15350c6d"> type: "POST",</div><div data-lake-id="4fd820a32117b4872b6242336aa5c594"> url: "/restfulblog/blog",</div><div data-lake-id="4d8a9857940cac3ad5bd0542c5f75453"> //dataType: "json",</div><div data-lake-id="424ea2740a92a9d1815835f9794f985a"> contentType: "application/json; charset=utf-8",</div><div data-lake-id="dcf9037056052477ae252f56fe440c43"> data: JSON.stringify(data), //需要将对象转换为字符串提交</div><div data-lake-id="3524fd66846962dc6e3b61c30ef5ea24"> success: function () {</div><div data-lake-id="abc0fb5723192d11ebd5ea56cc72dac1"> //新增后重新加载</div><div data-lake-id="bf3402e2edc667e4d4a6b46a80377c08"> viewBlogs();</div><div data-lake-id="12afc8fe231d52811d3e78a498cd1f81"> //关闭弹窗</div><div data-lake-id="afe557c7bc61b5a51bb7ed0f560c9fc3"> $('#blogAddModal').modal('hide');</div><div data-lake-id="42f2de88dac6fb87efa91682c5e0913c"> },</div><div data-lake-id="a0447c0e8d0fa3fa228f9213efe47b7d"> error: function (err) {</div><div data-lake-id="8f4ac7d05bcad0954ba0f0c5a9d07f46"> console.log(err);</div><div data-lake-id="3bcaeaed59595e57e05b60898701077e"> }</div><div data-lake-id="bfb465a472b6da4ff976de455ff5d9c1"> });</div><div data-lake-id="bf17f9cd291c91c18f748be8aaa4331d"> }</div><div data-lake-id="10d8b6659941ce749113598b98b9e10d"> //编辑</div><div data-lake-id="0e7845c8ee686bc8659c98496fd442ab"> function editBlog(id) {</div><div data-lake-id="dcfd4c4475a4d2f61c0b586baa6dcf02"> //查询博客信息</div><div data-lake-id="fa3a88d714d98255f03f3a6738ad5c3a"> $.ajax({</div><div data-lake-id="269de9badad4e9933889230ec247af51"> type: "GET",</div><div data-lake-id="73a9a89f4fc5568915994154882026d5"> url: "/restfulblog/blog/" + id,</div><div data-lake-id="cc2342d6efc17bd6142190e1f2d2b104"> dataType: "json",</div><div data-lake-id="c0957374189595a3a5ead20c87adb034"> contentType: "application/json; charset=utf-8",</div><div data-lake-id="e9ea35e34033d805229329ced906c2cf"> success: function (res) {</div><div data-lake-id="7438c63f63856a43df9de8c1bfa7c3b1"> console.log(res);</div><div data-lake-id="723fb0bdc7380d71be5c0ca6db44f543"> //为编辑框赋值</div><div data-lake-id="fed562aeffd5487ba4e55330ba8675ea"> $("#blogEditModal input[name='id']").val(res.id);</div><div data-lake-id="85ccfecfb2f7208cff77ca5710c95a93"> $("#blogEditModal input[name='title']").val(res.title);</div><div data-lake-id="8f6e284394f40cfb44af76113a86da49"> $("#blogEditModal input[name='author']").val(res.author);</div><div data-lake-id="a855488d4ceaaae6f1023ce689d041cd"> $("#blogEditModal textarea[name='content']").val(res.content);</div><div data-lake-id="aea437e3911d2ef021ec46ad5afb096c"> //显示编辑弹窗</div><div data-lake-id="f264d3220fcd1ba6a1e8b1aade42d7f3"> $('#blogEditModal').modal('show');</div><div data-lake-id="7613d06c88b6731e3e23d5762c61c3bd"> },</div><div data-lake-id="27579461ba76e1196a86d5ae2df71205"> error: function (err) {</div><div data-lake-id="ade906ad360ad7bc799c88178f33d43d"> console.log(err);</div><div data-lake-id="f6a36cd1bbc41ba0905540b50c91df95"> }</div><div data-lake-id="8c32940b6754018499c247479a0c8f76"> });</div><div data-lake-id="3216a5bf0cef334a63b32eb60e724110"> }</div><div data-lake-id="0122d71cb87a5a7af380b590e3123b38"> //编辑提交</div><div data-lake-id="6ecb20a27672cf3b016c7071ca717b8b"> function editBlogSubmit() {</div><div data-lake-id="f0cc3a2671faf21eb4bc34bde3e55f31"> var data = {</div><div data-lake-id="acd186fe44069133223687c9016390b4"> id: $("#blogEditModal input[name='id']").val(),</div><div data-lake-id="1cdadc5a1ef5c1cf016d83e6b3bfc2bf"> title: $("#blogEditModal input[name='title']").val(),</div><div data-lake-id="9e9dd2a4de87f8324eee18f16e478caa"> author: $("#blogEditModal input[name='author']").val(),</div><div data-lake-id="9e9adcb650c1f7c574027e797a8ee841"> content: $("#blogEditModal textarea[name='content']").val()</div><div data-lake-id="de6b70919abeed987fbb5d9a5e5d14b0"> };</div><div data-lake-id="7c57ac6709b6b0f1b318e68ea7eb84db"> $.ajax({</div><div data-lake-id="55fdffac93edf6f921faf18c1f2d0dd0"> type: "PUT",</div><div data-lake-id="ae0c0e8839e925a5624114ab22e799a4"> url: "/restfulblog/blog/" + data.id,</div><div data-lake-id="9119d11d8c377bb91166e32cab9da8dd"> //dataType: "json",</div><div data-lake-id="deb6d83fbee93c5f4afd50264e39d320"> contentType: "application/json; charset=utf-8",</div><div data-lake-id="935e9f9d9fcf935363538d3e60b50429"> data: JSON.stringify(data), //需要将对象转换为字符串提交</div><div data-lake-id="075733e3f789ce12fe5dfc9ac0f333ee"> success: function () {</div><div data-lake-id="d6b28577b0659b9acf2f164177a259ed"> //新增后重新加载</div><div data-lake-id="2cb0528220ff134180d30808173cc410"> viewBlogs();</div><div data-lake-id="b54a7cad8ed330ef08fe1b6212f7bf44"> //关闭弹窗</div><div data-lake-id="0f016e9f2ebce0286585de30436f5034"> $('#blogEditModal').modal('hide');</div><div data-lake-id="1eb30ca1c5ba80f8a50b1f29fb98bcc7"> },</div><div data-lake-id="2f919090af9674d722a6d36787e41e58"> error: function (err) {</div><div data-lake-id="653e5d344ff383e9b314626544b66384"> console.log(err);</div><div data-lake-id="6abfff97e759b02707cbff677854127e"> }</div><div data-lake-id="5435dd5969c698654faf49221d5579da"> });</div><div data-lake-id="82c5e6ad0625af92436596ee04aa1971"> }</div><div data-lake-id="bf0d022319681560373e11fe51b4c56b"> //删除</div><div data-lake-id="dd6ca7d2f378188ffbdb5cb7c3870cd8"> function deleteBlog(id) {</div><div data-lake-id="075d3be4f739e635b4d7ff507685276e"> $.ajax({</div><div data-lake-id="5825b200cd410d9c3832a78fbf8813f1"> type: "DELETE",</div><div data-lake-id="e5969c93e25ef91704689c5a714c01e2"> url: "/restfulblog/blog/" + id,</div><div data-lake-id="7c43dc83293ccd618edd977ecab47944"> //dataType: "json",//由于删除方法无返回值,所以此处注释掉</div><div data-lake-id="c1a98fea960cdb7755b4c92c62310969"> contentType: "application/json; charset=utf-8",</div><div data-lake-id="76f1e50dd2390bf93fd3e6bd653f51fc"> success: function () {</div><div data-lake-id="bd24701b8d3423812cc4982dd363e4d3"> //删除后重新加载</div><div data-lake-id="78819c3d19d73be06f5527066cc7209c"> viewBlogs();</div><div data-lake-id="a385a6df64489d789e6c8f16dec3875c"> },</div><div data-lake-id="cfd6c8e8b411ec36fc78a6d435f1bf46"> error: function (err) {</div><div data-lake-id="c6f29c9611cb955de780c9634c44fcdb"> console.log(err);</div><div data-lake-id="0697b60f0c3b09dec621bc623a84a7c2"> }</div><div data-lake-id="f25774ce8ca790e5b6b6b90f4f0df86f"> });</div><div data-lake-id="e7f3f1c7697f9e81614b9cbb58e00b0a"> }</div><div data-lake-id="28221a67677735e12ae5ed120ecabcbb">
5.6 编写springmvc-config.xml
上面我们使用到的bean包括:namedParameterJdbcTemplate、blogDao、blogService、blogController,此外namedParameterJdbcTemplate还需要注入dataSource。
其中blogDao、blogService、blogController是使用注解标识的,我们可以直接开启扫描即可。namedParameterJdbcTemplate和dataSource是Spring提供的类,我们需要在xml手工配置。
所以springmvc-config.xml编写如下:
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.0.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-4.0.xsd
http://www.springframework.org/schema/mvc
http://www.springframework.org/schema/mvc/spring-mvc-4.0.xsd">
base-package="org.maoge.sjblog" />
class="com.alibaba.druid.pool.DruidDataSource">
value="com.mysql.jdbc.Driver">
value="jdbc:mysql://127.0.0.1:3306/myblog?useUnicode=true&characterEncoding=utf-8">
class="org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate">
5.7 编写web.xml
最后我们实现下web.xml,当web服务启动时配置好加载的DispatcherServlet及对应的容器配置信息。
xmlns="http://xmlns.jcp.org/xml/ns/javaee"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd"
id="WebApp_ID" version="3.1">
springjdbcblog
springmvc
org.springframework.web.servlet.DispatcherServlet
contextConfigLocation
/WEB-INF/springmvc-config.xml
1
springmvc
/*
6. 测试
部署到Tomcat上启动,界面如下,还是不错滴。
哈哈,虽然跟之前一版的博客系统从界面上讲没任何差别,但是后端代码变化不少啊,数据库操作引擎已经由自定义的DbHelper变为SpringJDBC封装的NamedParameterJdbcTemplate,而且还使用了Druid这样先进滴数据库连接池,稳啊!