1. 整合配置
1.1 配置项目依赖
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.zhexun</groupId>
<artifactId>ssmDemo</artifactId>
<version>1.0-SNAPSHOT</version>
<packaging>war</packaging>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<maven.compiler.source>1.7</maven.compiler.source>
<maven.compiler.target>1.7</maven.compiler.target>
</properties>
<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>5.2.10.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<version>5.2.10.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
<version>5.2.10.RELEASE</version>
</dependency>
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.5.6</version>
</dependency>
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis-spring</artifactId>
<version>1.3.0</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.29</version>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>1.1.16</version>
</dependency>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>3.1.0</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.9.0</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.tomcat.maven</groupId>
<artifactId>tomcat7-maven-plugin</artifactId>
<version>2.1</version>
<configuration>
<port>80</port>
<path>/</path>
</configuration>
</plugin>
</plugins>
</build>
</project>
1.2 创建项目包结构
- config目录存放相关的配置类
- controller编写Controller类
- dao存放Dao接口,因为使用的是Mapper接口代理方式,所以没有实现类impl包
- service存Service接口,impl存放Service实现类
- resources:存入配置文件,如Jdbc.properties
- webapp:目录存放静态资源
- test/java:存放测试类
1.3 创建SpringConfig配置类
@Configuration
@ComponentScan({"com.zhexun.service"})
@PropertySource("classpath:jdbc.properties")
@Import({JdbcConfig.class,MybatisConfig.class})
@EnableTransactionManagement
public class SpringConfig {
}
@configuration
:设置该类为配置类
@ComponentScan
:设置spring配置类扫描路径,用于加载使用注解格式定义的bean
@PropertySource
:加载properties文件中的属性值
@Import
:导入配置类,当配置类有多个时使用数组格式一次性导入多个配置类
@EnableTransactionManagement
:设置当前Spring环境中开启注解式事务支持
1.4 创建Jdbc.properties
jdbc.driver=com.mysql.cj.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3306/ssm_db
jdbc.username=root
jdbc.password=12345678
1.5 创建JdbcConfig配置类
public class JdbcConfig {
@Value("${jdbc.driver}")
private String driver;
@Value("${jdbc.url}")
private String url;
@Value("${jdbc.username}")
private String username;
@Value("${jdbc.password}")
private String password;
@Bean
public DataSource dataSource(){
DruidDataSource dataSource = new DruidDataSource();
dataSource.setDriverClassName(driver);
dataSource.setUrl(url);
dataSource.setUsername(username);
dataSource.setPassword(password);
return dataSource;
}
@Bean
public PlatformTransactionManager transactionManager(DataSource dataSource){
DataSourceTransactionManager ds = new DataSourceTransactionManager();
ds.setDataSource(dataSource);
return ds;
}
}
@Value
:为 基本数据类型 或 字符串类型 属性设置值
@Bean
:设置该方法的返回值作为spring管理的bean
1.6 创建MybatisConfig配置类
public class MybatisConfig {
@Bean
public SqlSessionFactoryBean sqlSessionFactory(DataSource dataSource) {
SqlSessionFactoryBean factoryBean = new SqlSessionFactoryBean();
factoryBean.setDataSource(dataSource);
factoryBean.setTypeAliasesPackage("com.zhexun.domain");
return factoryBean;
}
@Bean
public MapperScannerConfigurer mapperScannerConfigurer() {
MapperScannerConfigurer msc = new MapperScannerConfigurer();
msc.setBasePackage("com.zhexun.dao");
return msc;
}
}
1.7 创建SpringMvcConfig配置类
@Configuration
@ComponentScan({"com.zhexun.controller", "com.zhexun.config"})
@EnableWebMvc
public class SpringMvcConfig {
}
@EnableWebMvc
:开启SpringMVC多项辅助功能
1.8 创建Web项目入口配置类
public class ServletConfig extends AbstractAnnotationConfigDispatcherServletInitializer {
//加载Spring配置类
protected Class<?>[] getRootConfigClasses() {
return new Class[]{SpringConfig.class};
}
//加载SpringMVC配置类
protected Class<?>[] getServletConfigClasses() {
return new Class[]{SpringMvcConfig.class};
}
//设置SpringMVC请求地址拦截规则
protected String[] getServletMappings() {
return new String[]{"/"};
}
//设置post请求中文乱码过滤器
@Override
protected Filter[] getServletFilters() {
CharacterEncodingFilter filter = new CharacterEncodingFilter();
filter.setEncoding("utf-8");
return new Filter[]{filter};
}
}
1.9 创建SpringMvcSupport类
因为添加了静态资源,SpringMVC会拦截,所以需要在SpringConfig的配置类中将静态资源进行放行
-
新建SpringMvcSupport
@Configuration public class SpringMvcSupport extends WebMvcConfigurationSupport { @Override protected void addResourceHandlers(ResourceHandlerRegistry registry) { registry.addResourceHandler("/pages/**").addResourceLocations("/pages/"); registry.addResourceHandler("/css/**").addResourceLocations("/css/"); registry.addResourceHandler("/js/**").addResourceLocations("/js/"); registry.addResourceHandler("/plugins/**").addResourceLocations("/plugins/"); } }
-
在SpringMvcConfig中扫描SpringMvcSupport
@Configuration @ComponentScan({"com.zhexun.controller","com.zhexun.config"}) @EnableWebMvc public class SpringMvcConfig { }
2. 功能模块整合开发
2.1 编写模型类
public class Book {
private Integer id;
private String type;
private String name;
private String description;
//getter...setter...toString省略
}
2.2 编写dao接口
public interface BookDao {
@Insert("insert into tbl_book (type, name, description) values (#{type}, #{name}, #{description})")
int save(Book book);
@Update("update tbl_book set type = #{type}, name = #{name}, description = #{description} where id = #{id}")
int update(Book book);
@Delete("delete from tbl_book where id = #{id}")
int delete(Integer id);
@Select("select * from tbl_book where id = #{id}")
Book getById(Integer id);
@Select("select * from tbl_book")
List<Book> getAll();
}
2.3 编写Service接口和实现类
@Transactional
public interface BookService {
boolean save(Book book);
boolean update(Book book);
boolean delete(Integer id);
Book getById(Integer id);
List<Book> getAll();
}
@Service
public class BookServiceImpl implements com.zhexun.service.BookService {
@Autowired
private BookDao bookDao;
@Override
public boolean save(Book book) {
return bookDao.save(book) > 0;
}
@Override
public boolean update(Book book) {
return bookDao.update(book) > 0;
}
@Override
public boolean delete(Integer id) {
return bookDao.delete(id) > 0;
}
@Override
public Book getById(Integer id) {
return bookDao.getById(id);
}
@Override
public List<Book> getAll() {
return bookDao.getAll();
}
}
@Transactional
:为当前业务层方法添加事务(如果设置在类或接口上方则类或接口中所有方法均添加事务)
@Service
/@Component
/@Controller
/@Repository
:设置该类为spring管理的bean
@Autowired
:为引用类型属性设置值,按照类型注入
说明:
-
bookDao在Service中注入的会提示一个红线提示,为什么呢?
- BookDao是一个接口,没有实现类,接口是不能创建对象的,所以最终注入的应该是代理对象
- 代理对象是由Spring的IOC容器来创建管理的
- IOC容器又是在Web服务器启动的时候才会创建
- IDEA在检测依赖关系的时候,没有找到适合的类注入,所以会提示错误提示
- 但是程序运行的时候,代理对象就会被创建,框架会使用DI进行注入,所以程序运行无影响。
2.4 编写Controller类
@RestController
@RequestMapping("/books")
public class BookController {
@Autowired
private BookService bookService;
@PostMapping
public Result save(@RequestBody Book book) {
boolean flag = bookService.save(book);
return new Result(flag ? Code.SAVE_OK : Code.SAVE_ERR, flag);
}
@PutMapping
public Result update(@RequestBody Book book) {
boolean flag = bookService.update(book);
return new Result(flag ? Code.UPDATE_OK : Code.UPDATE_ERR, flag);
}
@DeleteMapping("/{id}")
public Result delete(@PathVariable Integer id) {
boolean flag = bookService.delete(id);
return new Result(flag ? Code.DELETE_OK : Code.DELETE_ERR, flag);
}
@GetMapping("/{id}")
public Result getById(@PathVariable Integer id) {
Book book = bookService.getById(id);
Integer code = book != null ? Code.GET_OK : Code.GET_ERR;
String msg = book != null ? "" : "查询失败!";
return new Result(code, book, msg);
}
@GetMapping
public Result getAll() {
List<Book> books = bookService.getAll();
Integer code = books != null ? Code.GET_OK : Code.GET_ERR;
String msg = books != null ? "" : "查询失败!";
return new Result(code, books, msg);
}
}
@RestController
:设置当前控制器类为RESTful风格,等同于@Controller与@ResponseBody两个注解组合功能
@RequestMapping
:设置当前控制器方法请求访问路径
@GetMapping
/ @PostMapping
/ @PutMapping
/ @DeleteMapping
:设置当前控制器方法请求访问路径与请求动作,每种对应一个请求动作,例如@GetMapping对应GET请求
以上,对于图书模块的增删改查就已经完成了编写
3. 统一结果封装
后台返回一个统一的数据结果,前端在解析的时候就可以按照一种方式进行解析
3.1 设置统一数据返回Result类
public class Result {
//描述统一格式中的数据
private Object data;
//描述统一格式中的编码,用于区分操作,可以简化配置0或1表示成功失败
private Integer code;
//描述统一格式中的消息,可选属性
private String msg;
public Result() {
}
//构造方法是方便对象的创建
public Result(Integer code,Object data) {
this.data = data;
this.code = code;
}
//构造方法是方便对象的创建
public Result(Integer code, Object data, String msg) {
this.data = data;
this.code = code;
this.msg = msg;
}
//setter...getter...省略
}
3.2 定义返回码Code类
//状态码
public class Code {
public static final Integer SAVE_OK = 20011;
public static final Integer DELETE_OK = 20021;
public static final Integer UPDATE_OK = 20031;
public static final Integer GET_OK = 20041;
public static final Integer SAVE_ERR = 20010;
public static final Integer DELETE_ERR = 20020;
public static final Integer UPDATE_ERR = 20030;
public static final Integer GET_ERR = 20040;
}
3.3 统一controller类的返回值
//统一每一个控制器方法返回值
@RestController
@RequestMapping("/books")
public class BookController {
@Autowired
private BookService bookService;
@PostMapping
public Result save(@RequestBody Book book) {
boolean flag = bookService.save(book);
return new Result(flag ? Code.SAVE_OK:Code.SAVE_ERR,flag);
}
@PutMapping
public Result update(@RequestBody Book book) {
boolean flag = bookService.update(book);
return new Result(flag ? Code.UPDATE_OK:Code.UPDATE_ERR,flag);
}
@DeleteMapping("/{id}")
public Result delete(@PathVariable Integer id) {
boolean flag = bookService.delete(id);
return new Result(flag ? Code.DELETE_OK:Code.DELETE_ERR,flag);
}
@GetMapping("/{id}")
public Result getById(@PathVariable Integer id) {
Book book = bookService.getById(id);
Integer code = book != null ? Code.GET_OK : Code.GET_ERR;
String msg = book != null ? "" : "数据查询失败,请重试!";
return new Result(code,book,msg);
}
@GetMapping
public Result getAll() {
List<Book> bookList = bookService.getAll();
Integer code = bookList != null ? Code.GET_OK : Code.GET_ERR;
String msg = bookList != null ? "" : "数据查询失败,请重试!";
return new Result(code,bookList,msg);
}
}
至此,我们的返回结果就已经能以一种统一的格式返回给前端。前端根据返回的结果,先从中获取code
,根据code判断,如果成功则取data
属性的值,如果失败,则取msg
中的值做提示。
4. 统一异常处理
5. 前后台协议联调
books.html
<!DOCTYPE html>
<html>
<head>
<!-- 页面meta -->
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<title>SpringMVC案例</title>
<meta content="width=device-width,initial-scale=1,maximum-scale=1,user-scalable=no" name="viewport">
<!-- 引入样式 -->
<link rel="stylesheet" href="../plugins/elementui/index.css">
<link rel="stylesheet" href="../plugins/font-awesome/css/font-awesome.min.css">
<link rel="stylesheet" href="../css/style.css">
</head>
<body class="hold-transition">
<div id="app">
<div class="content-header">
<h1>图书管理</h1>
</div>
<div class="app-container">
<div class="box">
<div class="filter-container">
<el-input placeholder="图书名称" v-model="pagination.queryString" style="width: 200px;"
class="filter-item"></el-input>
<el-button @click="getAll()" class="dalfBut">查询</el-button>
<el-button type="primary" class="butT" @click="handleCreate()">新建</el-button>
</div>
<el-table size="small" current-row-key="id" :data="dataList" stripe highlight-current-row>
<el-table-column type="index" align="center" label="序号"></el-table-column>
<el-table-column prop="type" label="图书类别" align="center"></el-table-column>
<el-table-column prop="name" label="图书名称" align="center"></el-table-column>
<el-table-column prop="description" label="描述" align="center"></el-table-column>
<el-table-column label="操作" align="center">
<template slot-scope="scope">
<el-button type="primary" size="mini" @click="handleUpdate(scope.row)">编辑</el-button>
<el-button type="danger" size="mini" @click="handleDelete(scope.row)">删除</el-button>
</template>
</el-table-column>
</el-table>
<!-- 新增标签弹层 -->
<div class="add-form">
<el-dialog title="新增图书" :visible.sync="dialogFormVisible">
<el-form ref="dataAddForm" :model="formData" :rules="rules" label-position="right"
label-width="100px">
<el-row>
<el-col :span="12">
<el-form-item label="图书类别" prop="type">
<el-input v-model="formData.type"/>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="图书名称" prop="name">
<el-input v-model="formData.name"/>
</el-form-item>
</el-col>
</el-row>
<el-row>
<el-col :span="24">
<el-form-item label="描述">
<el-input v-model="formData.description" type="textarea"></el-input>
</el-form-item>
</el-col>
</el-row>
</el-form>
<div slot="footer" class="dialog-footer">
<el-button @click="dialogFormVisible = false">取消</el-button>
<el-button type="primary" @click="handleAdd()">确定</el-button>
</div>
</el-dialog>
</div>
<!-- 编辑标签弹层 -->
<div class="add-form">
<el-dialog title="编辑检查项" :visible.sync="dialogFormVisible4Edit">
<el-form ref="dataEditForm" :model="formData" :rules="rules" label-position="right"
label-width="100px">
<el-row>
<el-col :span="12">
<el-form-item label="图书类别" prop="type">
<el-input v-model="formData.type"/>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="图书名称" prop="name">
<el-input v-model="formData.name"/>
</el-form-item>
</el-col>
</el-row>
<el-row>
<el-col :span="24">
<el-form-item label="描述">
<el-input v-model="formData.description" type="textarea"></el-input>
</el-form-item>
</el-col>
</el-row>
</el-form>
<div slot="footer" class="dialog-footer">
<el-button @click="dialogFormVisible4Edit = false">取消</el-button>
<el-button type="primary" @click="handleEdit()">确定</el-button>
</div>
</el-dialog>
</div>
</div>
</div>
</div>
</body>
<!-- 引入组件库 -->
<script src="../js/vue.js"></script>
<script src="../plugins/elementui/index.js"></script>
<script type="text/javascript" src="../js/jquery.min.js"></script>
<script src="../js/axios-0.18.0.js"></script>
<script>
var vue = new Vue({
el: '#app',
data: {
pagination: {},
dataList: [],//当前页要展示的列表数据
formData: {},//表单数据
dialogFormVisible: false,//控制表单是否可见
dialogFormVisible4Edit: false,//编辑表单是否可见
rules: {//校验规则
type: [{required: true, message: '图书类别为必填项', trigger: 'blur'}],
name: [{required: true, message: '图书名称为必填项', trigger: 'blur'}]
}
},
//钩子函数,VUE对象初始化完成后自动执行
created() {
this.getAll();
},
methods: {
//列表
getAll() {
axios.get("/ssmDemo_war/books").then((res) => {
this.dataList = res.data.data;
});
},
//弹出添加窗口
handleCreate() {
this.dialogFormVisible = true
this.resetForm()
},
//重置表单
resetForm() {
this.formData = {}
},
//添加
handleAdd() {
axios.post("/ssmDemo_war/books", this.formData).then(res => {
if (res.data.code === 20011) {
this.dialogFormVisible = false
this.$message.success("添加成功!")
} else if (res.data.code === 20010) {
this.$message.error("添加失败!")
} else {
this.$message.error(res.data.msg)
}
}).finally(() => {
this.getAll()
})
},
//弹出编辑窗口
handleUpdate(row) {
axios.get("/ssmDemo_war/books/" + row.id).then((res) => {
if (res.data.code === 20041) {
this.formData = res.data.data
this.dialogFormVisible4Edit = true
} else {
this.$message.error(res.data.msg)
}
})
},
//编辑
handleEdit() {
axios.put("/ssmDemo_war/books", this.formData).then(res => {
if (res.data.code === 20031) {
this.dialogFormVisible4Edit = false
this.$message.success("修改成功!")
} else if (res.data.code === 20030) {
this.$message.error("修改失败!")
} else {
this.$message.error(res.data.msg)
}
}).finally(() => {
this.getAll()
})
},
// 删除
handleDelete(row) {
//1.弹出提示框
this.$confirm("此操作永久删除当前数据,是否继续?", "提示", {
type: 'info'
}).then(() => {
//2.做删除业务
axios.delete("/ssmDemo_war/books/" + row.id).then((res) => {
if (res.data.code === 20021) {
this.$message.success("删除成功");
} else {
this.$message.error("删除失败");
}
}).finally(() => {
this.getAll();
});
}).catch(() => {
//3.取消删除
this.$message.info("取消删除操作");
});
}
}
})
</script>
</html>