- Spring:Spring是一個輕量級的控制反轉(IOC)和麪向切面(AOP)的容器框架。用來裝JavaBean(java對象),中間層框架(萬能膠)。
提供了展現層 SpringMVC和持久層 Spring JdbcTemplate以及業務層事務管理等衆多的企業級應用技術,還能整合開源世界衆多著名的第三方框架和類庫,逐漸成爲使用最多的Java EE 企業應用開源框架。- SpringMVC: SpringMvc是Spring的一個模塊,基於MVC的一個框架,無需中間整合層來整合 .
什麼是MVC? : 首先請求發送request請求到C(control 接收用戶請求響應用戶) 然後控制器到M模型(pojo、action、service、dao)層處理 處理結果完了返回控制器 控制器要經過視圖渲染 最後返回終端(response)
- MyBatis: MyBatis 是一款優秀的(ORM)持久層框架,它支持定製化 SQL、存儲過程以及高級映射。MyBatis 避免了幾乎所有的 JDBC 代碼和手動設置參數以及獲取結果集。MyBatis 可以使用簡單的 XML 或註解來配置和映射原生類型、接口和 Java 的 POJO(Plain Old Java Objects,普通老式 Java 對象)爲數據庫中的記錄。
- MyBatis-Plus(簡稱 MP)是一個 MyBatis 的增強工具,在 MyBatis 的基礎上只做增強不做改變,爲簡化開發、提高效率而生。
- 強大的 CRUD 操作:內置通用 Mapper、通用 Service,僅僅通過少量配置即可實現單表大部分 CRUD 操作,更有強大的條件構造器,滿足各類使用需求
- 支持 Lambda 形式調用:通過 Lambda 表達式,方便的編寫各類查詢條件,無需再擔心字段寫錯
- 支持主鍵自動生成:支持多達 4 種主鍵策略(內含分佈式唯一 ID 生成器 - Sequence),可自由配置,完美解決主鍵問題
- 內置代碼生成器:採用代碼或者 Maven 插件可快速生成 Mapper 、 Model 、 Service 、 Controller 層代碼,支持模板引擎,更有超多自定義配置等您來使用
- 內置分頁插件:基於 MyBatis 物理分頁,開發者無需關心具體操作,配置好插件之後,寫分頁等同於普通 List 查詢
- 當前框架整合使用的Spring版本是
5.1.5.RELEASE
,MyBatisPlus版本爲:3.1.0
文章目錄
- 1. 創建對應的數據庫以及數據表
- 2. 創建web工程
- 3. 導入所需要的jar包
- 4. 通過MP代碼生成器生成代碼
- 5. application-core.xml配置
- 6 通用返回接口編寫
- 6.1 定義通過的返回編碼及提示信息
- 6.2 定義通用的返回實體,並提供對應的常用方法
- 7. application-mvc.xml配置
- 8. web.xml配置
- 9. 集成Swagger構建restful API
- 10. Controller 編寫
- 11. logback日誌
- 12. java8日期注入全局處理
- 13. 日期類JSON序列化和反序列化全局處理
- 14. 自定義異常以及異常處理
- 15. 攔截器配置
- 16. MP lambda表達式使用
- 17. MP 使用Wrapper 自定義SQL
- 18. 結束
1. 創建對應的數據庫以及數據表
--創建數據庫表
create database ssm_db;
-- 使用數據庫
use ssm_db;
--創建用戶表
create table tbl_user(
id int primary key auto_increment ,
rel_name varchar(25) comment '真實名',
log_name varchar(25) comment '登陸名',
log_pwd varchar(50) comment '密碼',
log_status int(2) default 0 comment '登陸狀態,0啓用,1禁用',
sex varchar(4) comment '性別',
birthday date comment '生日',
create_time timestamp default current_timestamp
);
--默認管理員數據
insert into tbl_user(rel_name,log_name,log_pwd,sex,birthday) values('管理員','admin','123','男','1991-11-12');
--查詢數據
select id,rel_name,log_name,log_pwd,log_status,sex,birthday,create_time from tbl_user;
2. 創建web工程
使用IDEA新建一個Maven Web項目,並構建相應的項目架構,config(全局配置),util(幫助工具類),exception(自定義異常類) ,interceptor(自定義攔截器),剩下的(controller,entity,mapper,service)這些目錄不用自己定義,MP代碼構建工具可以幫我們完成。
3. 導入所需要的jar包
初始構建jar包,後續集成其它框架或工具會陸續加入。
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<maven.compiler.source>1.8</maven.compiler.source>
<maven.compiler.target>1.8</maven.compiler.target>
<spring.version>5.1.7.RELEASE</spring.version>
<jackson.verion>2.9.0</jackson.verion>
<swagger2.version>2.7.0</swagger2.version>
</properties>
<dependencies>
<!--spring架構基礎包-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context-support</artifactId>
<version>${spring.version}</version>
</dependency>
<!--spring 數據庫支持-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.46</version>
</dependency>
<!--數據源-->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>1.1.14</version>
</dependency>
<!--
myBatisPlus : myBatis 增強包,封裝了Mapper,Service,Page 等常用操作,提供了簡潔的lambda條件表達式
myBatisPlus依賴於mybatis於mybatis-spring包,這裏它會依賴添加,不用顯示添加這兩個jar包
`使用注意不要使用3.1.1版本。否則java8日期序列化會報如下錯誤:Error attempting to get column 'birthday' from result set. `
-->
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus</artifactId>
<version>3.1.0</version>
</dependency>
<!-- mybatis 對象java8日期處理的輔助類 -->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis-typehandlers-jsr310</artifactId>
<version>1.0.2</version>
</dependency>
<!--mybatis-plus代碼生成器工具包-->
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-generator</artifactId>
<version>3.1.1</version>
</dependency>
<!-- mybatis-plus 代碼生成需要的靜態模板工具包-->
<dependency>
<groupId>org.apache.velocity</groupId>
<artifactId>velocity-engine-core</artifactId>
<version>2.1</version>
</dependency>
<!--spring整合junit4測試包-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
<scope>test</scope>
</dependency>
<!--springMVC-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>${spring.version}</version>
</dependency>
<!--json序列化-->
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>${jackson.verion}</version>
</dependency>
<!--json序列化java8日期輔助包-->
<dependency>
<groupId>com.fasterxml.jackson.datatype</groupId>
<artifactId>jackson-datatype-jsr310</artifactId>
<version>${jackson.verion}</version>
</dependency>
<!--
快速生成Getting,Setting,ToString
`使用需要注意:maven tomcat7插件啓動項目,不支持lombok1.16.18以上的版本,否則會報 Unable to process Jar entry [module-info.class] from Jar`
-->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.16.16</version>
</dependency>
<!--日誌包-->
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>1.7.26</version>
</dependency>
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
<version>1.2.3</version>
</dependency>
<!--字符串常用工具包-->
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
<version>3.8.1</version>
</dependency>
<!-- Swagger2 構建Restful API -->
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger2</artifactId>
<version>${swagger2.version}</version>
</dependency>
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger-ui</artifactId>
<version>${swagger2.version}</version>
</dependency>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>4.0.1</version>
<scope>provided</scope>
</dependency>
</dependencies>
<build>
<finalName>ssm</finalName>
<plugins>
<!--tomcat7 maven插件-->
<plugin>
<groupId>org.apache.tomcat.maven</groupId>
<artifactId>tomcat7-maven-plugin</artifactId>
<version>2.2</version>
<configuration>
<port>80</port>
<path>/</path>
<uriEncoding>UTF-8</uriEncoding>
<!--
<url>http://localhost/manager</url>
<server>tomcat7</server>
<username>tomcat</username>
<password>tomcat</password>
-->
</configuration>
</plugin>
</plugins>
</build>
4. 通過MP代碼生成器生成代碼
AutoGenerator 是 MyBatis-Plus 的代碼生成器,通過 AutoGenerator 可以快速生成 Entity、Mapper、Mapper XML、Service、Controller 等各個模塊的代碼,極大的提升了開發效率。
4.1 加入數據庫屬性配置文件
把常用的數據源連接數據庫操作的常用字段信息提取爲一個properties文件方便後續修改和重複使用。
driver=com.mysql.jdbc.Driver
url=jdbc:mysql://localhost:3306/ssm_db?useSSL=false&useUnicode=true&characterEncoding=utf8
# mybatis中數據源連接使用username,報 Access denied for user 'CDHong'@'localhost' (using password: YES)
user=root
password=root
initialSize=5
maxActive=10
minIdle=4
maxWait=3000
代碼生成配置啓動類,配置官網參考地址
package mybaits.plus.code.generator;
import com.baomidou.mybatisplus.core.toolkit.StringPool;
import com.baomidou.mybatisplus.generator.AutoGenerator;
import com.baomidou.mybatisplus.generator.InjectionConfig;
import com.baomidou.mybatisplus.generator.config.*;
import com.baomidou.mybatisplus.generator.config.po.TableInfo;
import com.baomidou.mybatisplus.generator.config.rules.NamingStrategy;
import org.junit.Test;
import java.util.ArrayList;
import java.util.List;
import java.util.ResourceBundle;
/**
* @description
* @auther: CDHong
* @date: 2019/6/24-10:18
**/
public class MyBatisPlusCodeGenerator {
//讀取屬性配置文件
private ResourceBundle rb = ResourceBundle.getBundle("druid");
@Test
public void codeGenerator() {
// 代碼生成器
AutoGenerator mpg = new AutoGenerator();
// 全局配置
GlobalConfig gc = new GlobalConfig();
String projectPath = System.getProperty("user.dir");
gc.setOutputDir(projectPath + "/src/main/java");
gc.setAuthor("CDHong");
gc.setOpen(false);
gc.setSwagger2(true); //實體屬性 Swagger2 註解
mpg.setGlobalConfig(gc);
// 數據源配置
DataSourceConfig dsc = new DataSourceConfig();
dsc.setUrl(rb.getString("url"));
// dsc.setSchemaName("public");
dsc.setDriverName(rb.getString("driver"));
dsc.setUsername(rb.getString("user"));
dsc.setPassword(rb.getString("password"));
mpg.setDataSource(dsc);
// 包配置
PackageConfig pc = new PackageConfig();
pc.setParent("org.itcast.demo"); //父級公用包名,就是自動生成的文件放在項目路徑下的那個包中
mpg.setPackageInfo(pc);
// 自定義配置
InjectionConfig cfg = new InjectionConfig() {
@Override
public void initMap() {
// to do nothing
}
};
String templatePath = "/templates/mapper.xml.vm";
// 自定義輸出配置
List<FileOutConfig> focList = new ArrayList<>();
// 自定義配置會被優先輸出
focList.add(new FileOutConfig(templatePath) {
@Override
public String outputFile(TableInfo tableInfo) {
// 自定義輸出文件名 , 如果你 Entity 設置了前後綴、此處注意 xml 的名稱會跟着發生變化!!
return projectPath + "/src/main/resources/mappers/" + tableInfo.getEntityName() + "Mapper" + StringPool.DOT_XML;
}
});
cfg.setFileOutConfigList(focList);
mpg.setCfg(cfg);
// 配置模板
TemplateConfig templateConfig = new TemplateConfig();
templateConfig.setXml(null); //是否在mapper接口處生成xml文件
mpg.setTemplate(templateConfig);
// 策略配置
StrategyConfig strategy = new StrategyConfig();
strategy.setNaming(NamingStrategy.underline_to_camel); //Entity文件名稱命名規範
strategy.setColumnNaming(NamingStrategy.underline_to_camel); //Entity字段名稱命名規範
strategy.setEntityLombokModel(true); //是否使用lombok完成Entity實體標註Getting Setting ToString 方法
strategy.setRestControllerStyle(true); //Controller註解使用是否RestController標註,否則是否開啓使用Controller標註
strategy.setControllerMappingHyphenStyle(true); //Controller註解名稱,不使用駝峯,使用連字符
strategy.setTablePrefix("tbl_"); //表前綴,添加該表示,則生成的實體,不會有表前綴,比如sys_dept 生成就是Dept
//strategy.setFieldPrefix("sys_"); //字段前綴
mpg.setStrategy(strategy);
mpg.execute();
}
}
5. application-core.xml配置
Spring 與 MyBatis 整合,導入屬性配置文件,配置數據眼,配置SqlSessionFactory , 配置MapperScanner , 配置事務管理,掃描Service註解
- applicationContext-core.xml
<?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:tx="http://www.springframework.org/schema/tx"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd">
<!--掃描Services-->
<context:component-scan base-package="org.itcast.demo.service.impl" />
<!--導入配置文件-->
<context:property-placeholder location="classpath:druid.properties" />
<!--配置數據源-->
<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource" destroy-method="close">
<property name="driverClassName" value="${driver}" />
<property name="url" value="${url}" />
<property name="username" value="${user}" />
<property name="password" value="${password}" />
<property name="initialSize" value="${initialSize}" />
<property name="maxWait" value="${maxWait}" />
<property name="maxActive" value="${maxActive}" />
<property name="minIdle" value="${minIdle}" />
</bean>
<!--配置MybatisSqlSessionFactoryBean-->
<bean id="sqlSessionFactory" class="com.baomidou.mybatisplus.extension.spring.MybatisSqlSessionFactoryBean">
<!--數據源注入-->
<property name="dataSource" ref="dataSource" />
<!--別名配置-->
<property name="typeAliasesPackage" value="org.itcast.demo.entity" />
<!--SQL映射文件地址-->
<property name="mapperLocations" value="classpath:mappers/*Mapper.xml" />
<!--分頁插件配置-->
<property name="plugins">
<bean class="com.baomidou.mybatisplus.extension.plugins.PaginationInterceptor" />
</property>
</bean>
<!--配置MapperScan-->
<bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
<!--Mapper接口掃描-->
<property name="basePackage" value="org.itcast.demo.mapper" />
<!--SqlSession注入,在mybatis plus中可以省略-->
<property name="sqlSessionFactoryBeanName" value="sqlSessionFactory" />
</bean>
<!--事務處理-->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource" />
</bean>
<!--開啓事務註解-->
<tx:annotation-driven transaction-manager="transactionManager" />
</beans>
6 通用返回接口編寫
隨着互聯網的高速發展,前端頁面的展示、交互體驗越來越靈活、炫麗,響應體驗也要求越來越高,後端服務的高併發、高可用、高性能、高擴展等特性的要求也愈加苛刻,從而導致前後端研發各自專注於自己擅長的領域深耕細作。
然而帶來的另一個問題:前後端的對接界面雙方卻關注甚少,沒有任何接口約定規範情況下各自擼起袖子就是幹,導致我們在產品項目開發過程中,前後端的接口聯調對接工作量佔比在30%-50%左右,甚至會更高。往往前後端接口聯調對接及系統間的聯調對接都是整個產品項目研發的軟肋。
因此我們需要一個統一的返回接口樣式,這樣,前後端交互就較爲融洽。
6.1 定義通過的返回編碼及提示信息
後臺所有的返回數據都有一個狀態碼,這樣定義好,後續生成文檔,前端人員看到對應的Code值就知道對應是什麼錯誤,使用對應的樣式來顯示對應的錯誤。這樣統一定義也方便管理,統一規範。
package org.itcast.demo.util;
/**
* @description
* @auther: CDHong
* @date: 2019/5/2-13:56
**/
public enum ResponseCode {
SUCCESS(0,"SUCCESS"),
ERROR(1,"ERROR"),
PARAME_ERROR(2,"PARAME_ERROR"),
NEED_LOGIN(10,"NEED_LOGIN"),
SYS_ERROR(11,"系統錯誤~");
private final int code;
private final String desc;
ResponseCode(int code,String desc){
this.code = code;
this.desc = desc;
}
public int getCode() {
return code;
}
public String getDesc() {
return desc;
}}
6.2 定義通用的返回實體,並提供對應的常用方法
前後端分離,肯定需要返回統一格式的JSON數據,我們這裏就是定義一個返回給前端的數據實體,包括狀態碼,顯示信息,數據,以及分頁需要展示的數據信息,都有定義,基本上能夠滿足所有需求。
package org.itcast.demo.util;
import com.fasterxml.jackson.annotation.JsonIgnore;
import com.fasterxml.jackson.annotation.JsonInclude;
import lombok.Data;
import java.io.Serializable;
/**
* @description
* @auther: CDHong
* @date: 2019/5/2-13:52
**/
@Data
@JsonInclude(JsonInclude.Include.NON_NULL) //JSON序列化的是否,如果值爲NULL則不返回到前端
public class ResponseData implements Serializable{
private Integer status;
private Long total;
private String msg;
private Object data;
private ResponseData(int status) {
this.status = status;
}
private ResponseData(int status, String msg) {
this.status = status;
this.msg = msg;
}
private ResponseData(int status, Object data) {
this.status = status;
this.data = data;
}
private ResponseData(int status, String msg, Object data) {
this.status = status;
this.msg = msg;
this.data = data;
}
private ResponseData(int status, Long total, Object data) {
this.status = status;
this.total = total;
this.data = data;
}
public static ResponseData ok(){
return new ResponseData(ResponseCode.SUCCESS.getCode());
}
public static ResponseData ok(String msg){
return new ResponseData(ResponseCode.SUCCESS.getCode(),msg);
}
public static ResponseData ok(Object data){
return new ResponseData(ResponseCode.SUCCESS.getCode(),data);
}
public static ResponseData okPage(Long total, Object data){
return new ResponseData(ResponseCode.SUCCESS.getCode(),total,data);
}
public static ResponseData fail(String msg){
return new ResponseData(ResponseCode.ERROR.getCode(),msg);
}
public static ResponseData error(ResponseCode responseCode){
return new ResponseData(responseCode.getCode(),responseCode.getDesc());
}
public static ResponseData exception(String msg){
return new ResponseData(ResponseCode.ERROR.getCode(),msg);
}
public static ResponseData ok(String msg, Object data) {
return new ResponseData(ResponseCode.SUCCESS.getCode(),msg,data);
}
@JsonIgnore
public boolean isSuccess() {
return this.status == ResponseCode.SUCCESS.getCode();
}
}
7. application-mvc.xml配置
集成SpringMVC 模塊,配置JSON類型轉化器,開啓靜態資源處理,掃描Controller註解
<?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.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc.xsd">
<!--掃描Controller註解-->
<context:component-scan base-package="org.itcast.demo.controller" />
<!--配置一個JSON數據類型轉化器-->
<bean class="org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter">
<property name="messageConverters">
<list>
<bean class="org.springframework.http.converter.json.MappingJackson2HttpMessageConverter" >
<property name="supportedMediaTypes">
<list>
<!--設置返回格式,防止瀏覽器下載和數據亂碼-->
<value>application/json;charset=UTF-8</value>
<value>text/html;charset=UTF-8</value>
</list>
</property>
<property name="objectMapper">
<bean class="org.itcast.demo.config.JavaTimeJsonMapper" />
</property>
</bean>
</list>
</property>
</bean>
<!--開啓註解驅動,JSON處理,上傳下載..-->
<mvc:annotation-driven />
<!--將靜態資源交由默認的servlet處理-->
<mvc:default-servlet-handler />
<!--向容器自動注入配置-->
<context:annotation-config />
</beans>
8. web.xml配置
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
version="4.0">
<!--IOC容器 -->
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:applicationContext-core.xml</param-value>
</context-param>
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
<!--前端控制器-->
<servlet>
<servlet-name>springMVC</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:applicationContext-mvc.xml</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>springMVC</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
<!--字符編碼處理-->
<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>
</web-app>
9. 集成Swagger構建restful API
Swagger 是什麼?
*1、是一款讓你更好的書寫API文檔的規範且完整框架。
*2、提供描述、生產、消費和可視化RESTful Web Service。
*3、是由龐大工具集合支撐的形式化規範。這個集合涵蓋了從終端用戶接口、底層代碼庫到商業API管理的方方面面。
Swagger 主要提供了三個功能:
*Swagger Editor: Swagger提供的一個編輯器,用來通過Swagger提供的特定的YAML語法來編寫API文檔
*Swagger Codegen: 代碼生成器
*Swagger UI: YAML語法定義我們的RESTful API,然後它會自動生成一篇排版優美的API文檔,並且提供實時預覽。
9.1 Swagger2註解說明
在前面MP代碼生成器中我們已經配置了自動生成Swagger註解,相信也有同學看到了,但是對他不是很瞭解,接下來我們就來看看這些常用的註解
@Api:用在請求的類上,表示對類的說明
tags="說明該類的作用,可以在UI界面上看到的註解"
value="該參數沒什麼意義,在UI界面上也看到,所以不需要配置"
@ApiOperation:用在請求的方法上,說明方法的用途、作用
value="說明方法的用途、作用"
notes="方法的備註說明"
@ApiModel:用於響應類上,表示一個返回響應數據的信息,MP代碼生成器Entity中有配置
(這種一般用在post創建的時候,使用@RequestBody這樣的場景,
請求參數無法使用@ApiImplicitParam註解進行描述的時候)
@ApiModelProperty:用在屬性上,描述響應類的屬性
@ApiImplicitParams:用在請求的方法上,表示一組參數說明
@ApiImplicitParam:用在@ApiImplicitParams註解中,指定一個請求參數的各個方面
name:參數名
value:參數的漢字說明、解釋
required:參數是否必須傳
paramType:參數放在哪個地方
· header --> 請求參數的獲取:@RequestHeader
· query --> 請求參數的獲取:@RequestParam
· path(用於restful接口)--> 請求參數的獲取:@PathVariable
· body(不常用)
· form(不常用)
dataType:參數類型,默認String,其它值dataType="Integer"
defaultValue:參數的默認值
@ApiResponses:用在請求的方法上,表示一組響應
@ApiResponse:用在@ApiResponses中,一般用於表達一個錯誤的響應信息
code:數字,例如400
message:信息,例如"請求參數沒填好"
response:拋出異常的類
9.2 SpringMVC集成springfox-swagger2構建restful API
- 9.2.1 在pom.xml文件中添加swagger相關依賴
第一個是API獲取的包,第二是官方給出的一個ui界面。這個界面可以自定義,默認是官方的,對於安全問題,以及ui路由設置需要着重思考
<!-- swagger2 : swagger本身不支持spring mvc的,SpringFox把swagger包裝了一下,讓他可以支持Spring mvc-->
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger2</artifactId>
<version>${swagger2.version}</version>
</dependency>
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger-ui</artifactId>
<version>${swagger2.version}</version>
</dependency>
- 配置Swagger2的Config,生成相應的API
需要特別注意的是swagger scan base package,這是掃描註解的配置,即你的API接口位置。
Swagger2配置類 通過@Configuration註解,讓spring來加載該配置 再通過@EnableSwagger2註解來啓動Swagger2, 一定要jdk1.8,不然跑不起swagger
package org.itcast.demo.config;
import io.swagger.annotations.ApiOperation;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.stereotype.Component;
import org.springframework.test.context.web.WebAppConfiguration;
import org.springframework.web.servlet.config.annotation.EnableWebMvc;
import springfox.documentation.builders.ApiInfoBuilder;
import springfox.documentation.builders.PathSelectors;
import springfox.documentation.builders.RequestHandlerSelectors;
import springfox.documentation.service.ApiInfo;
import springfox.documentation.spi.DocumentationType;
import springfox.documentation.spring.web.plugins.Docket;
import springfox.documentation.swagger2.annotations.EnableSwagger2;
/**
* @description swagger的configuration
* @auther: CDHong
* @date: 2019/6/25-10:14
**/
@Configuration
@EnableSwagger2
@EnableWebMvc //非springboot框架需要引入且需要在配置文件中配置該類的Bean對象
@ComponentScan(basePackages = {"org.itcast.demo.controller"}) //掃描包
public class Swagger2Config {
/**
* 創建API應用
* appinfo()增加API相關信息
* 通過select()函數返回一個ApiSelectorBuilder實例,用來控制那些接口暴露給Swagger來展現
* 本例採用置頂掃描的包路徑來定義指定要建立API的目錄
* @return
*/
@Bean
public Docket createRestApi() {
return new Docket(DocumentationType.SWAGGER_2)
.groupName("")
.select()// 選擇哪些路徑和API會生成document
.apis(RequestHandlerSelectors.withMethodAnnotation(ApiOperation.class))// 對所有api進行監控
.paths(PathSelectors.any())// 對所有路徑進行監控
.build()
.apiInfo(apiInfo());
}
/**
* 創建改API的基本信息(這些基本信息會展示在文檔頁面中)
* 訪問地址: http://項目實際地址/swagger-ui.html
* @return
*/
private ApiInfo apiInfo() {
return new ApiInfoBuilder()
.title("簡單的SSM框架搭建練習案例")
.description("使用MyBatisPlus與VUE來擴展一下前後端")
.termsOfServiceUrl("http://localhost:80/swagger-ui.html")// 將“url”換成自己的ip:port
.version("1.0")
.build();
}
}
- 修改applicaitonContext-mvc.xml文件;添加關於swagger的配置,內容如下:
<mvc:default-servlet-handler />
<!--掃描Config-->
<context:component-scan base-package="org.itcast.demo.config" />
<!-- 將swaggerconfig配置類注入 -->
<bean class="org.itcast.demo.config.Swagger2Config"/>
<!--
<mvc:resources location="classpath:/META-INF/resources/" mapping="swagger-ui.html"/>
<mvc:resources location="classpath:/META-INF/resources/webjars/" mapping="/webjars/**"/>
-->
集成完畢,接下來在代碼中使用。
10. Controller 編寫
package org.itcast.demo.controller;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiImplicitParam;
import io.swagger.annotations.ApiImplicitParams;
import io.swagger.annotations.ApiOperation;
import lombok.extern.slf4j.Slf4j;
import org.itcast.demo.entity.User;
import org.itcast.demo.exception.MyException;
import org.itcast.demo.service.IUserService;
import org.itcast.demo.util.ConstUtils;
import org.itcast.demo.util.ResponseCode;
import org.itcast.demo.util.ResponseData;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import javax.servlet.http.HttpSession;
import java.util.List;
import java.util.Objects;
/**
* @author CDHong
* @since 2019-06-24
*/
@Slf4j
@RestController
@RequestMapping("/user")
@Api(value = "用戶管理接口Api",tags = "用戶管理接口Api")
public class UserController {
@Autowired
private IUserService userService;
@ApiOperation(value = "添加用戶",notes = "用戶註冊,狀態默認0啓用1禁用,生日格式:yyyy-MM-dd")
@PostMapping("/save")
public ResponseData save(User user){
if(Objects.isNull(user)){
return ResponseData.error(ResponseCode.PARAME_ERROR);
}
boolean flg = userService.save(user);
if(flg){
return ResponseData.ok("添加用戶成功~");
}
return ResponseData.fail("添加用戶失敗~");
}
@ApiOperation("根據ID刪除用戶")
@ApiImplicitParam(name = "id",value = "用戶ID",dataType = "Integer" ,required = true,paramType = "path")
@DeleteMapping("/del/{id}")
public ResponseData delById(@PathVariable("id") Integer id){
if(Objects.isNull(id)){
return ResponseData.error(ResponseCode.PARAME_ERROR);
}
boolean flg = userService.removeById(id);
if(flg){
return ResponseData.ok("刪除用戶成功~");
}
return ResponseData.fail("刪除用戶失敗~");
}
@ApiOperation("根據ID編輯用戶")
@PostMapping("/edit")
public ResponseData edit(User user){
if(Objects.isNull(user)){
return ResponseData.error(ResponseCode.PARAME_ERROR);
}
boolean flg = userService.updateById(user);
if(flg){
return ResponseData.ok("修改用戶成功~");
}
return ResponseData.fail("修改用戶失敗~");
}
@ApiOperation("顯示用戶列表")
@GetMapping("/list")
public ResponseData list(){
List<User> list = userService.list();
return ResponseData.ok(list);
}
@ApiOperation("根據ID獲取用戶信息")
@ApiImplicitParam(name = "id",value = "用戶ID",dataType = "Integer" ,required = true,paramType = "path")
@GetMapping("/find/{id}")
public ResponseData findById(@PathVariable("id") Integer id){
User user = userService.getById(id);
return ResponseData.ok(user);
}
@ApiOperation("分頁顯示用戶信息")
@ApiImplicitParams({
@ApiImplicitParam(name = "pageIndex",value = "頁碼",dataType = "Integer",required = true,paramType = "path"),
@ApiImplicitParam(name = "pageSize",value = "每頁顯示條數",dataType = "Integer",required = true,paramType = "path")
})
@GetMapping("/page/{pageIndex}/{pageSize}")
public ResponseData page(@PathVariable("pageIndex") Integer pageIndex,@PathVariable("pageSize") Integer pageSize){
Page<User> page = new Page<>(pageIndex,pageSize);
IPage<User> userPage = userService.page(page);
return ResponseData.okPage(userPage.getTotal(), userPage.getRecords());
}
}
11. logback日誌
需要在pom文件中配置兩個座標:slf4j-api,logback-classic
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>1.7.26</version>
</dependency>
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
<version>1.2.3</version>
</dependency>
配置文件名稱必須爲
logback.xml
,且需要放在資源目錄的根路徑下。
<?xml version="1.0" encoding="UTF-8"?>
<configuration scan="true" scanPeriod="60 seconds" debug="false">
<!-- magenta:洋紅 -->
<!-- boldMagenta:粗紅-->
<!-- cyan:青色 -->
<!-- white:白色 -->
<!-- magenta:洋紅 -->
<!--控制檯日誌配置-->
<property name="CONSOLE_LOG_PATTERN" value="%yellow(%date{yyyy-MM-dd HH:mm:ss}) |%highlight(%-5level)|%green(%logger) |%cyan(%msg%n)"/>
<property name="FILE_PATH" value="/root/sys/logs/member"/>
<appender name="console" class="ch.qos.logback.core.ConsoleAppender">
<encoder>
<pattern>${CONSOLE_LOG_PATTERN}</pattern>
</encoder>
<filter class="ch.qos.logback.classic.filter.ThresholdFilter">
<level>debug</level>
</filter>
</appender>
<!-- log 日誌 要強制打印的日誌信息,否則按root的級別打印 -->
<logger name="org.springframework.web.servlet.DispatcherServlet" level="debug" additivity="false">
<appender-ref ref="debug" />
</logger>
<root level="debug">
<appender-ref ref="console"/>
</root>
</configuration>
12. java8日期注入全局處理
在實體中使用java8的日期,導致SpringMVC數據無法注入,需要一序列的配置,在這裏,我使用一個全局的配置,統一處理,該文件需要註解掃描,所以需要在配置文件中添加掃描路徑。
package org.itcast.demo.config;
import org.itcast.demo.util.ConstUtils;
import org.springframework.web.bind.WebDataBinder;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.InitBinder;
import java.beans.PropertyEditorSupport;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.time.format.DateTimeFormatter;
/**
* @description
* @auther: CDHong
* @date: 2019/6/23-19:50
**/
@ControllerAdvice
public class GlobalDataInitBinder {
@InitBinder
protected void initBinder(WebDataBinder binder) {
binder.registerCustomEditor(LocalDate.class, new PropertyEditorSupport() {
@Override
public void setAsText(String text) {
setValue(LocalDate.parse(text, DateTimeFormatter.ofPattern(ConstUtils.DEFAULT_DATE_FORMAT)));
}
});
binder.registerCustomEditor(LocalDateTime.class, new PropertyEditorSupport() {
@Override
public void setAsText(String text) throws IllegalArgumentException {
setValue(LocalDateTime.parse(text, DateTimeFormatter.ofPattern(ConstUtils.DEFAULT_DATE_TIME_FORMAT)));
}
});
binder.registerCustomEditor(LocalTime.class, new PropertyEditorSupport() {
@Override
public void setAsText(String text) throws IllegalArgumentException {
setValue(LocalTime.parse(text, DateTimeFormatter.ofPattern(ConstUtils.DEFAULT_TIME_FORMAT)));
}
});
}
}
13. 日期類JSON序列化和反序列化全局處理
Spring集成Jackson來完成JSON序列化操作,但是日期的返回信息不是我們需要的格式,這個時候,我們同樣使用一個全局的配置文件來完成序列化操作。同樣需要註解掃描。
package org.itcast.demo.config;
import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.databind.*;
import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
import com.fasterxml.jackson.datatype.jsr310.deser.LocalDateDeserializer;
import com.fasterxml.jackson.datatype.jsr310.deser.LocalDateTimeDeserializer;
import com.fasterxml.jackson.datatype.jsr310.deser.LocalTimeDeserializer;
import com.fasterxml.jackson.datatype.jsr310.ser.LocalDateSerializer;
import com.fasterxml.jackson.datatype.jsr310.ser.LocalDateTimeSerializer;
import com.fasterxml.jackson.datatype.jsr310.ser.LocalTimeSerializer;
import org.itcast.demo.util.ConstUtils;
import java.io.IOException;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.time.format.DateTimeFormatter;
import java.util.Date;
/**
* @description javaTime JSON序列化和反序列化
* @auther: CDHong
* @date: 2019/6/23-19:56
**/
public class JavaTimeJsonMapper extends ObjectMapper {
/** Date日期格式化 **/
private SimpleDateFormat formatter = new SimpleDateFormat(ConstUtils.DEFAULT_DATE_TIME_FORMAT);
public JavaTimeJsonMapper(){
this.disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS);
this.disable(DeserializationFeature.ADJUST_DATES_TO_CONTEXT_TIME_ZONE);
//LocalDateTime系列序列化和反序列化模塊,繼承自jsr310,我們在這裏修改了日期格式
JavaTimeModule javaTimeModule = new JavaTimeModule();
javaTimeModule.addSerializer(LocalDateTime.class, new LocalDateTimeSerializer(DateTimeFormatter.ofPattern(ConstUtils.DEFAULT_DATE_TIME_FORMAT)));
javaTimeModule.addSerializer(LocalDate.class,new LocalDateSerializer(DateTimeFormatter.ofPattern(ConstUtils.DEFAULT_DATE_FORMAT)));
javaTimeModule.addSerializer(LocalTime.class, new LocalTimeSerializer(DateTimeFormatter.ofPattern(ConstUtils.DEFAULT_TIME_FORMAT)));
javaTimeModule.addDeserializer(LocalDateTime.class,new LocalDateTimeDeserializer(DateTimeFormatter.ofPattern(ConstUtils.DEFAULT_DATE_TIME_FORMAT)));
javaTimeModule.addDeserializer(LocalDate.class,new LocalDateDeserializer(DateTimeFormatter.ofPattern(ConstUtils.DEFAULT_DATE_FORMAT)));
javaTimeModule.addDeserializer(LocalTime.class,new LocalTimeDeserializer(DateTimeFormatter.ofPattern(ConstUtils.DEFAULT_TIME_FORMAT)));
//Date序列化和反序列化
javaTimeModule.addSerializer(Date.class, new JsonSerializer<Date>() {
@Override
public void serialize(Date date, JsonGenerator jsonGenerator, SerializerProvider serializerProvider) throws IOException {
String formattedDate = formatter.format(date);
jsonGenerator.writeString(formattedDate);
}
});
javaTimeModule.addDeserializer(Date.class, new JsonDeserializer<Date>() {
@Override
public Date deserialize(JsonParser jsonParser, DeserializationContext deserializationContext) throws IOException {
String date = jsonParser.getText();
try {
return formatter.parse(date);
} catch (ParseException e) {
e.printStackTrace();
}
return null;
}
});
this.registerModule(javaTimeModule);
}
}
14. 自定義異常以及異常處理
在項目開發中我們總是無法避免各種錯誤,如果都是使用try…catch來預防,總歸不是完美的辦法,這個對我們查找錯誤信息修復bug也不利,這時候我們需要配置一個異常全局處理,給前端提供一個統一的數據格式信息。
14.1 自定義異常類
這裏需要注意一點,自定義異常繼承RuntimeException即可,不需要繼承Excpeiton,不然拋出自定義異常的時候,不是很方便。
package org.itcast.demo.exception;
import org.itcast.demo.util.ResponseCode;
/**
* @description
* @auther: CDHong
* @date: 2019/6/25-18:01
**/
public class MyException extends RuntimeException {
public MyException(String message) {
super(message);
}
public MyException(ResponseCode code) {
super(code.getDesc());
}
}
14.2 全局異常處理
package org.itcast.demo.config;
import org.itcast.demo.exception.MyException;
import org.itcast.demo.util.ResponseCode;
import org.itcast.demo.util.ResponseData;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;
import org.springframework.web.servlet.mvc.method.annotation.ResponseEntityExceptionHandler;
/**
* @description 全局異常處理
* @auther: CDHong
* @date: 2019/6/24-11:30
**/
@RestControllerAdvice
public class GlobalExceptionResolver{
@ExceptionHandler(MyException.class)
public ResponseData myException(MyException e){
return ResponseData.exception(e.getMessage());
}
@ExceptionHandler(Exception.class)
public ResponseData exception(Exception e) {
return ResponseData.exception(e.getMessage());
}
}
14.3 在controller中定義兩個異常來測試一下
@ApiOperation("自定義異常")
@GetMapping("/exception")
public ResponseData exception(){
throw new MyException("異常測試");
}
@ApiOperation("系統異常")
@GetMapping("/sys-error")
public ResponseData sysException(){
int i = 1/0;
return ResponseData.ok();
}
15. 攔截器配置
SpringMVC攔截器配置比較簡單,只需要實現HandlerInterceptor,重寫preHandle,添加相應的攔截器處理,在配置文件中註冊即可。
package org.itcast.demo.interceptor;
import lombok.extern.slf4j.Slf4j;
import org.itcast.demo.exception.MyException;
import org.itcast.demo.util.ConstUtils;
import org.itcast.demo.util.ResponseCode;
import org.springframework.web.servlet.HandlerInterceptor;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.Objects;
/**
* @description
* @auther: CDHong
* @date: 2019/6/27-13:57
**/
@Slf4j
public class LoginInterceptor implements HandlerInterceptor {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws IOException {
Object obj = request.getSession().getAttribute(ConstUtils.CURRENT_LOGIN_USER);
String requestURI = request.getRequestURI();
if(Objects.nonNull(obj)){
return true;
}
log.debug("請求路徑:【{}】被攔截了",requestURI);
throw new MyException(ResponseCode.NEED_LOGIN);
//response.sendRedirect("/login");
//return false;
}
}
配置文件中註冊: 可以指定要攔截的表達式和排除的表達式
<!--攔截器配置-->
<mvc:interceptors>
<mvc:interceptor>
<mvc:mapping path="/user/**"/> <!--攔截所有以/user/開頭的請求-->
<mvc:exclude-mapping path="/user/login" /> <!--登錄請求和其他非/user開頭的請求不攔截-->
<bean class="org.itcast.demo.interceptor.LoginInterceptor" />
</mvc:interceptor>
</mvc:interceptors>
16. MP lambda表達式使用
@ApiOperation("用戶登錄")
@PostMapping("/login")
public ResponseData login(HttpSession session,String logName, String logPwd){
//LambdaQueryWrapper<User> lambda = new QueryWrapper<User>().lambda();
//LambdaQueryWrapper<User> lambdaQueryWrapper = new LambdaQueryWrapper<>();
//LambdaQueryWrapper<User> queryWrapper = Wrappers.<User>lambdaQuery().eq(User::getLogName, logName).eq(User::getLogPwd, logPwd);
//User user = userService.getOne(queryWrapper);
ResponseData responseData = userService.login(logName, logPwd);
if(responseData.isSuccess()){
session.setAttribute(ConstUtils.CURRENT_LOGIN_USER,responseData.getData());
}
return responseData;
}
17. MP 使用Wrapper 自定義SQL
在使用了mybatis-plus之後, 自定義SQL的同時也想使用Wrapper的便利應該怎麼辦? 在mybatis-plus版本3.0.7得到了完美解決 版本需要大於或等於3.0.7, 以下兩種方案取其一即可
- 方案一 註解方式 Mapper.java
@Select("select * from mysql_data ${ew.customSqlSegment}")
List<MysqlData> getAll(@Param(Constants.WRAPPER) Wrapper wrapper);
- 方案二 XML形式 Mapper.xml
<select id="getAll" resultType="MysqlData">
SELECT * FROM mysql_data ${ew.customSqlSegment}
</select>
- 具體使用如下:
Mapper接口中定義如下:
package org.itcast.demo.mapper;
import com.baomidou.mybatisplus.core.conditions.Wrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.core.toolkit.Constants;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import org.apache.ibatis.annotations.Param;
import org.apache.ibatis.annotations.Select;
import org.itcast.demo.entity.User;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import java.util.List;
/**
* <p>
* Mapper 接口
* </p>
*
* @author CDHong
* @since 2019-06-24
*/
public interface UserMapper extends BaseMapper<User> {
@Select("SELECT id,rel_name,log_pwd,log_name,log_status,sex,birthday,create_time from tbl_user ${ew.customSqlSegment}")
List<User> findAll(@Param(Constants.WRAPPER) Wrapper<User> wrapper);
IPage<User> pageInfo(Page<User> page,@Param(Constants.WRAPPER) Wrapper<User> wrapper);
}
XMLSQL映射文件配置如下:
<?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.itcast.demo.mapper.UserMapper">
<select id="pageInfo" resultType="org.itcast.demo.entity.User">
SELECT id,rel_name,log_pwd,log_name,log_status,sex,birthday,create_time from tbl_user ${ew.customSqlSegment}
</select>
</mapper>
18. 結束
到此講解完畢,最後附上一個Swaager測試的ResultFul API
任意點開一個請求,輸入對應的數據,執行Try it out!按鈕就可以看到對應的結果信息
返回信息如下:
最後附上案例源碼地址,希望大家喜歡!!