01-數據庫設計
一弃理、數據庫設計
二屿良、數據庫設計規(guī)約
以下規(guī)約只針對本模塊,更全面的文檔參考《阿里巴巴Java開發(fā)手冊》:五、MySQL數據庫
1按价、庫名與應用名稱盡量一致
2惭适、表名、字段名必須使用小寫字母或數字楼镐,禁止出現數字開頭癞志,
3、表名不使用復數名詞
4框产、表的命名最好是加上“業(yè)務名稱_表的作用”凄杯。如,edu_teacher
5秉宿、表必備三字段:id, gmt_create, gmt_modified
說明:
其中 id 必為主鍵戒突,類型為 bigint unsigned、單表時自增蘸鲸、步長為 1妖谴。
(如果使用分庫分表集群部署窿锉,則id類型為verchar酌摇,非自增,業(yè)務中使用分布式id生成器)
gmt_create, gmt_modified 的類型均為 datetime 類型嗡载,前者現在時表示主動創(chuàng)建窑多,后者過去分詞表示被 動更新。
6洼滚、單表行數超過 500 萬行或者單表容量超過 2GB埂息,才推薦進行分庫分表。 說明:如果預計三年后的數據量根本達不到這個級別遥巴,請不要在創(chuàng)建表時就分庫分表千康。?
7、表達是與否概念的字段铲掐,必須使用 is_xxx 的方式命名拾弃,數據類型是 unsigned tinyint (1 表示是,0 表示否)摆霉。?
說明:任何字段如果為非負數豪椿,必須是 unsigned。?
注意:POJO 類中的任何布爾類型的變量携栋,都不要加 is 前綴搭盾。數據庫表示是與否的值,使用 tinyint 類型婉支,堅持 is_xxx 的 命名方式是為了明確其取值含義與取值范圍鸯隅。?
正例:表達邏輯刪除的字段名 is_deleted,1 表示刪除向挖,0 表示未刪除蝌以。?
8霎奢、小數類型為 decimal,禁止使用 float 和 double饼灿。 說明:float 和 double 在存儲的時候幕侠,存在精度損失的問題,很可能在值的比較時碍彭,得到不 正確的結果晤硕。如果存儲的數據范圍超過 decimal 的范圍,建議將數據拆成整數和小數分開存儲庇忌。
9舞箍、如果存儲的字符串長度幾乎相等,使用 char 定長字符串類型皆疹。?
10疏橄、varchar 是可變長字符串,不預先分配存儲空間略就,長度不要超過 5000捎迫,如果存儲長度大于此值,定義字段類型為 text表牢,獨立出來一張表窄绒,用主鍵來對應,避免影響其它字段索 引效率崔兴。
11彰导、唯一索引名為 uk_字段名;普通索引名則為 idx_字段名敲茄。
說明:uk_ 即 unique key位谋;idx_ 即 index 的簡稱
12、不得使用外鍵與級聯堰燎,一切外鍵概念必須在應用層解決掏父。外鍵與級聯更新適用于單機低并發(fā),不適合分布式爽待、高并發(fā)集群损同;級聯更新是強阻塞,存在數據庫更新風暴的風險鸟款;外鍵影響數據庫的插入速度膏燃。?
02-搭建項目工程(父工程)
一、工程結構介紹
1何什、工程結構
2组哩、模塊說明
guli-parent:在線教學根目錄(父工程),管理四個子模塊:
?canal-client:canal數據庫表同步模塊(統(tǒng)計同步數據)
common:公共模塊父節(jié)點
? ??? ??common-util:工具類模塊,所有模塊都可以依賴于它
? ??? ??service-base:service服務的base包伶贰,包含service服務的公共配置類蛛砰,所有service模塊依賴于它
? ??? ??spring-security:認證與授權模塊,需要認證授權的service服務依賴于它
infrastructure:基礎服務模塊父節(jié)點
? ??? ??api-gateway:api網關服務
service:api接口服務父節(jié)點
service-acl:用戶權限管理api接口服務(用戶管理黍衙、角色管理和權限管理等)
service-cms:cms api接口服務
service-edu:教學相關api接口服務
service-msm:短信api接口服務
service-order:訂單相關api接口服務
service-oss:阿里云oss api接口服務
service-statistics:統(tǒng)計報表api接口服務
service-ucenter:會員api接口服務
service-vod:視頻點播api接口服務
二泥畅、創(chuàng)建父工程
1、創(chuàng)建sprigboot工程guli-parent
在idea開發(fā)工具中琅翻,使用?Spring Initializr?快速初始化一個?Spring Boot 模塊位仁,版本使用:2.2.1.RELEASE
2、配置 pom.xml
<artifactId>?節(jié)點后面添加 pom類型
3方椎、在pom.xml中添加依賴的版本
刪除pom.xml中的<dependencies>內容
<dependencies>
???????????????.......................
</dependencies>
添加?<properties>確定依賴的版本
配置<dependencyManagement>鎖定依賴的版本
03-搭建項目工程(service模塊)
2 講師管理接口開發(fā)
01-講師管理模塊配置和生成代碼
一聂抢、講師管理模塊配置
1、在service下面service-edu模塊中創(chuàng)建配置文件
resources目錄下創(chuàng)建文件?application.properties
# 服務端口
server.port=8001
# 服務名
spring.application.name=service-edu
# 環(huán)境設置:dev棠众、test琳疏、prod
spring.profiles.active=dev
spring.datasource.driver-class-name=com.mysql.jdbc.Driver
spring.datasource.url=jdbc:mysql://localhost:3306/guli?serverTimezone=GMT%2B8&characterEncoding=utf-8&useSSL=false
spring.datasource.username=root
spring.datasource.password=123456
#mybatis日志
mybatis-plus.configuration.log-impl=org.apache.ibatis.logging.stdout.StdOutImpl
或者在resources目錄下創(chuàng)建文件?application.yml
2、創(chuàng)建MP代碼生成器
在test/java目錄下創(chuàng)建包com.atguigu.eduservice闸拿,創(chuàng)建代碼生成器:CodeGenerator.java
package com.atguigu.demo;
import com.baomidou.mybatisplus.annotation.DbType;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.generator.AutoGenerator;
import com.baomidou.mybatisplus.generator.config.DataSourceConfig;
import com.baomidou.mybatisplus.generator.config.GlobalConfig;
import com.baomidou.mybatisplus.generator.config.PackageConfig;
import com.baomidou.mybatisplus.generator.config.StrategyConfig;
import com.baomidou.mybatisplus.generator.config.rules.DateType;
import com.baomidou.mybatisplus.generator.config.rules.NamingStrategy;
import org.junit.Test;
/**
* @author
* @since 2018/12/13
*/
public class CodeGenerator {
@Test
? ? public void run() {
// 1空盼、創(chuàng)建代碼生成器
? ? ? ? AutoGenerator mpg =new AutoGenerator();
// 2、全局配置
? ? ? ? GlobalConfig gc =new GlobalConfig();
String projectPath =System.getProperty("user.dir");
gc.setOutputDir("E:\\blog\\guli_parent\\service\\service_edu" +"/src/main/java");
gc.setAuthor("testjava");
gc.setOpen(false);//生成后是否打開資源管理器
? ? ? ? gc.setFileOverride(false);//重新生成時文件是否覆蓋
? ? ? ? //UserServie
? ? ? ? gc.setServiceName("%sService");//去掉Service接口的首字母I
? ? ? ? gc.setIdType(IdType.ID_WORKER_STR);//主鍵策略
? ? ? ? gc.setDateType(DateType.ONLY_DATE);//定義生成的實體類中日期類型
? ? ? ? gc.setSwagger2(true);//開啟Swagger2模式
? ? ? ? mpg.setGlobalConfig(gc);
// 3胸墙、數據源配置
? ? ? ? DataSourceConfig dsc =new DataSourceConfig();
dsc.setUrl("jdbc:mysql://localhost:3306/guli?serverTimezone=GMT%2B8&characterEncoding=utf-8&useSSL=false");
dsc.setDriverName("com.mysql.jdbc.Driver");
dsc.setUsername("root");
dsc.setPassword("123456");
dsc.setDbType(DbType.MYSQL);
mpg.setDataSource(dsc);
// 4我注、包配置
? ? ? ? PackageConfig pc =new PackageConfig();
pc.setModuleName("eduservice");//模塊名
? ? ? ? //包com.atguigu.eduservice
? ? ? ? pc.setParent("com.atguigu");
//包com.atguigu.eduservice.controller
? ? ? ? pc.setController("controller");
pc.setEntity("entity");
pc.setService("service");
pc.setMapper("mapper");
mpg.setPackageInfo(pc);
// 5、策略配置
? ? ? ? StrategyConfig strategy =new StrategyConfig();
strategy.setInclude("edu_teacher");
strategy.setNaming(NamingStrategy.underline_to_camel);//數據庫表映射到實體的命名策略
? ? ? ? strategy.setTablePrefix(pc.getModuleName() +"_");//生成實體時去掉表前綴
? ? ? ? strategy.setColumnNaming(NamingStrategy.underline_to_camel);//數據庫表字段映射到實體的命名策略
? ? ? ? strategy.setEntityLombokModel(true);// lombok 模型 @Accessors(chain = true) setter鏈式操作
? ? ? ? strategy.setRestControllerStyle(true);//restful api風格控制器
? ? ? ? strategy.setControllerMappingHyphenStyle(true);//url中駝峰轉連字符
? ? ? ? mpg.setStrategy(strategy);
????????// 6迟隅、執(zhí)行
? ? ? ? mpg.execute();
}
}
二、編寫后臺管理api接口
1励七、編寫controller代碼
public class EduTeacherController {
????//把service注入
? ? @Autowired
? ? private EduTeacherService teacherService;
????//1 查詢講師表所有數據
? ? //rest風格
? ? //@GetMapping是一個組合注解智袭,是@RequestMapping(method = RequestMethod.GET)的縮寫
? ? @GetMapping("findAll")
????public List<EduTeacher> findAllTeacher(){
????????//調用service的方法實現查詢所有的操作
? ? ? ? List<EduTeacher> list =teacherService.list(null);
????????return list;
????}
}
2、創(chuàng)建SpringBoot啟動類
創(chuàng)建啟動類 EduApplication.java掠抬,注意啟動類的創(chuàng)建位置
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class EduApplication {
????public static void main(String[] args) {
????????SpringApplication.run(EduApplication.class, args);
????}
}
3吼野、創(chuàng)建SpringBoot配置類
在edu包下創(chuàng)建config包,創(chuàng)建MyBatisPlusConfig.java
@Configuration
@MapperScan("com.atguigu.eduservice.mapper")
public class EduConfig {
}
4两波、配置SQL執(zhí)行性能分析插件
/**
* SQL 執(zhí)行性能分析插件
* 開發(fā)環(huán)境使用瞳步,線上不推薦。 maxTime 指的是 sql 最大執(zhí)行時長
*
*三種環(huán)境
* dev:開發(fā)環(huán)境
* test:測試環(huán)境
* prod:生產環(huán)境
*/
@Bean
@Profile({"dev","test"})// 設置 dev test 環(huán)境開啟
public PerformanceInterceptor performanceInterceptor() {
PerformanceInterceptor performanceInterceptor =new PerformanceInterceptor();
performanceInterceptor.setMaxTime(1000);//ms腰奋,超過此處設置的ms則sql不執(zhí)行
? ? performanceInterceptor.setFormat(true);
return performanceInterceptor;
}
5单起、運行啟動類
訪問http://localhost:8001/eduservice/teacher
得到json數據
成功:
6、統(tǒng)一返回的json時間格式
默認情況下json時間格式帶有時區(qū)劣坊,并且是世界標準時間嘀倒,和我們的時間差了八個小時
在application.properties中設置
#返回json的全局時間格式
spring.jackson.date-format=yyyy-MM-dd HH:mm:ss
spring.jackson.time-zone=GMT+8
三、講師邏輯刪除功能
1、配置邏輯刪除插件
MyBatisPlusConfig中配置
/**
* 邏輯刪除插件
*/
@Bean
public ISqlInjector sqlInjector() {
????return new LogicSqlInjector();
}
邏輯刪除注解:
@ApiModelProperty(value ="邏輯刪除 1(true)已刪除测蘑, 0(false)未刪除")
@TableLogic
private Boolean isDeleted;
2灌危、EduTeacherController添加刪除方法
//2 邏輯刪除講師的方法
@DeleteMapping("{id}")
public boolean removeTeacher(@PathVariable String id){
????return teacherService.removeById(id);
}
02-配置Swagger2生成API接口文檔
一、Swagger2介紹
前后端分離開發(fā)模式中碳胳,api文檔是最好的溝通方式勇蝙。
Swagger 是一個規(guī)范和完整的框架,用于生成挨约、描述浅蚪、調用和可視化 RESTful 風格的 Web 服務。
????1烫罩、及時性?(接口變更后惜傲,能夠及時準確地通知相關前后端開發(fā)人員)
????2、規(guī)范性?(并且保證接口的規(guī)范性贝攒,如接口的地址盗誊,請求方式,參數及響應格式和錯誤信息)
????3隘弊、一致性?(接口信息一致哈踱,不會出現因開發(fā)人員拿到的文檔版本不一致,而出現分歧)
????4梨熙、可測性?(直接在接口文檔上進行測試坤塞,以方便理解業(yè)務)
二、配置Swagger2
1蜘欲、創(chuàng)建common模塊
在guli-parent下創(chuàng)建模塊common
配置:
groupId:com.atguigu
artifactId:common
2洁段、在common中引入相關依賴
<dependencies>
? ? ? ? <dependency>
? ? ? ? ? ? <groupId>org.springframework.boot</groupId>
? ? ? ? ? ? <artifactId>spring-boot-starter-web</artifactId>
? ? ? ? ? ? <scope>provided </scope>
? ? ? ? </dependency>
? ? ? ? <!--mybatis-plus-->
? ? ? ? <dependency>
? ? ? ? ? ? <groupId>com.baomidou</groupId>
? ? ? ? ? ? <artifactId>mybatis-plus-boot-starter</artifactId>
? ? ? ? ? ? <scope>provided </scope>
? ? ? ? </dependency>
? ? ? ? <!--lombok用來簡化實體類:需要安裝lombok插件-->
? ? ? ? <dependency>
? ? ? ? ? ? <groupId>org.projectlombok</groupId>
? ? ? ? ? ? <artifactId>lombok</artifactId>
? ? ? ? ? ? <scope>provided </scope>
? ? ? ? </dependency>
? ? ? ? <!--swagger-->
? ? ? ? <dependency>
? ? ? ? ? ? <groupId>io.springfox</groupId>
? ? ? ? ? ? <artifactId>springfox-swagger2</artifactId>
? ? ? ? ? ? <scope>provided </scope>
? ? ? ? </dependency>
? ? ? ? <dependency>
? ? ? ? ? ? <groupId>io.springfox</groupId>
? ? ? ? ? ? <artifactId>springfox-swagger-ui</artifactId>
? ? ? ? ? ? <scope>provided </scope>
? ? ? ? </dependency>
? ? ? ? <!-- redis -->
? ? ? ? <dependency>
? ? ? ? ? ? <groupId>org.springframework.boot</groupId>
? ? ? ? ? ? <artifactId>spring-boot-starter-data-redis</artifactId>
? ? ? ? </dependency>
? ? ? ? <!-- spring2.X集成redis所需common-pool2
? ? ? ? <dependency>
? ? ? ? ? ? <groupId>org.apache.commons</groupId>
? ? ? ? ? ? <artifactId>commons-pool2</artifactId>
? ? ? ? ? ? <version>2.6.0</version>
? ? ? ? </dependency>-->
? ? </dependencies>
3、在common下面創(chuàng)建子模塊service-base
在模塊service-base中质欲,創(chuàng)建swagger的配置類
創(chuàng)建包com.atguigu.servicebase树埠,創(chuàng)建類SwaggerConfig
import com.google.common.base.Predicates;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import springfox.documentation.builders.ApiInfoBuilder;
import springfox.documentation.builders.PathSelectors;
import springfox.documentation.service.ApiInfo;
import springfox.documentation.service.Contact;
import springfox.documentation.spi.DocumentationType;
import springfox.documentation.spring.web.plugins.Docket;
import springfox.documentation.swagger2.annotations.EnableSwagger2;
@Configuration //配置類
@EnableSwagger2 //swagger注解
public class SwaggerConfig {
? ? @Bean
? ? public Docket webApiConfig(){
? ? ? ? return new Docket(DocumentationType.SWAGGER_2)
? ? ? ? ? ? ? ? .groupName("webApi")
? ? ? ? ? ? ? ? .apiInfo(webApiInfo())
? ? ? ? ? ? ? ? .select()
? ? ? ? ? ? ? ? .paths(Predicates.not(PathSelectors.regex("/admin/.*")))
? ? ? ? ? ? ? ? .paths(Predicates.not(PathSelectors.regex("/error.*")))
? ? ? ? ? ? ? ? .build();
? ? }
? ? private ApiInfo webApiInfo(){
? ? ? ? return new ApiInfoBuilder()
? ? ? ? ? ? ? ? .title("網站-課程中心API文檔")
? ? ? ? ? ? ? ? .description("本文檔描述了課程中心微服務接口定義")
? ? ? ? ? ? ? ? .version("1.0")
? ? ? ? ? ? ? ? .contact(new Contact("java", "http://atguigu.com", "123456@qq.com"))
? ? ? ? ? ? ? ? .build();
? ? }
}
4、在模塊service模塊中引入service-base
????????<dependency>
? ? ? ? ? ? <groupId>com.atguigu</groupId>
? ? ? ? ? ? <artifactId>service_base</artifactId>
? ? ? ? ? ? <version>0.0.1-SNAPSHOT</version>
? ? ? ? </dependency>
5嘶伟、在service-edu啟動類上添加注解怎憋,進行測試
訪問:http://localhost:8001/swagger-ui.html
測試刪除:
定義接口說明和參數說明:
????定義在類上:@Api
????定義在方法上:@ApiOperation
????定義在參數上:@ApiParam
@Api(description="講師管理")
@RestController
@RequestMapping("/eduservice/teacher")
public class EduTeacherController {
? ? //訪問地址http://localhost:8001/eduservice/teacher/findAll
? ? //把service注入
? ? @Autowired
? ? private EduTeacherService teacherService;
? ? //1 查詢講師表所有數據
? ? //rest風格
? ? //@GetMapping是一個組合注解,是@RequestMapping(method = RequestMethod.GET)的縮寫
? ? @ApiOperation(value = "所有講師列表")
? ? @GetMapping("findAll")
? ? public List<EduTeacher> findAllTeacher(){
? ? ? ? //調用service的方法實現查詢所有的操作
? ? ? ? List<EduTeacher> list = teacherService.list(null);
? ? ? ? return list;
? ? }
? ? //2 邏輯刪除講師的方法
? ? @ApiOperation(value = "根據ID刪除講師")
? ? @DeleteMapping("{id}")
? ? public boolean removeTeacher( @ApiParam(name = "id", value = "講師ID", required = true) @PathVariable String id){
? ? ? ? return teacherService.removeById(id);
? ? }
}
6九昧、API模型
可以添加一些自定義設置绊袋,例如:
定義樣例數據
03-統(tǒng)一返回結果對象
一、統(tǒng)一返回數據格式
項目中我們會將響應封裝成json返回铸鹰,一般我們會將所有接口的數據格式統(tǒng)一癌别, 使前端(iOS Android, Web)對數據的操作更一致、輕松掉奄。
一般情況下规个,統(tǒng)一返回數據格式沒有固定的格式凤薛,只要能描述清楚返回的數據狀態(tài)以及要返回的具體數據就可以。但是一般會包含狀態(tài)碼诞仓、返回消息缤苫、數據這幾部分內容
例如,我們的系統(tǒng)要求返回的基本數據格式如下:
列表:
分頁:
沒有返回數據:
失斒谩:
因此活玲,我們定義統(tǒng)一結果
{
????"success":布爾,//響應是否成功
????"code":數字,//響應碼
????"message":字符串,//返回消息
????"data":HashMap//返回數據,放在鍵值對中
}
二谍婉、創(chuàng)建統(tǒng)一結果返回類
1舒憾、在common模塊下創(chuàng)建子模塊common-utils
2、創(chuàng)建接口定義返回碼
創(chuàng)建包com.atguigu.commonutils穗熬,創(chuàng)建接口 ResultCode.java
package com.atguigu.commonutils;
public interface ResultCode {
? ? public static Integer SUCCESS = 20000; //成功
? ? public static Integer ERROR = 20001; //失敗
}
4镀迂、創(chuàng)建結果類
創(chuàng)建類 R.java
package com.atguigu.commonutils;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import java.util.HashMap;
import java.util.Map;
//統(tǒng)一返回結果的類
@Data
public class R {
? ? @ApiModelProperty(value = "是否成功")
? ? private Boolean success;
? ? @ApiModelProperty(value = "返回碼")
? ? private Integer code;
? ? @ApiModelProperty(value = "返回消息")
? ? private String message;
? ? @ApiModelProperty(value = "返回數據")
? ? private Map<String, Object> data = new HashMap<String, Object>();
????//把構造方法私有
????private R(){};
????//成功靜態(tài)方法
? ? public static R ok(){
? ? ? ? R r = new R();
? ? ? ? r.setSuccess(true);
? ? ? ? r.setCode(ResultCode.SUCCESS);
? ? ? ? r.setMessage("成功");
? ? ? ? return r;
? ? }
????//失敗靜態(tài)方法
? ? public static R error(){
? ? ? ? R r = new R();
? ? ? ? r.setSuccess(false);
? ? ? ? r.setCode(ResultCode.ERROR);
? ? ? ? r.setMessage("失敗");
? ? ? ? return r;
? ? }
? ? public R success(Boolean success){
? ? ? ? this.setSuccess(success);
? ? ? ? return this;
? ? }
? ? public R message(String message){
? ? ? ? this.setMessage(message);
? ? ? ? return this;
? ? }
? ? public R code(Integer code){
? ? ? ? this.setCode(code);
? ? ? ? return this;
? ? }
? ? public R data(String key, Object value){
? ? ? ? this.data.put(key, value);
? ? ? ? return this;
? ? }
? ? public R data(Map<String, Object> map){
? ? ? ? this.setData(map);
? ? ? ? return this;
? ? }
}
return this;的目的是為了鏈式編程如R.ok().success().message().code()
三、統(tǒng)一返回結果使用
1唤蔗、在service模塊中添加依賴
????????<dependency>
? ? ? ? ? ? <groupId>com.atguigu</groupId>
? ? ? ? ? ? <artifactId>common_utils</artifactId>
? ? ? ? ? ? <version>0.0.1-SNAPSHOT</version>
? ? ? ? </dependency>
2探遵、修改Controller中的返回結果
列表
????//1 查詢講師表所有數據
? ? //rest風格
? ? //@GetMapping是一個組合注解,是@RequestMapping(method = RequestMethod.GET)的縮寫
? ? @ApiOperation(value = "所有講師列表")
? ? @GetMapping("findAll")
? ? public R findAllTeacher(){
? ? ? ? //調用service的方法實現查詢所有的操作
? ? ? ? List<EduTeacher> list = teacherService.list(null);
? ? ? ? return R.ok().data("items",list);
? ? }
刪除
????//2 邏輯刪除講師的方法
? ? @ApiOperation(value = "根據ID刪除講師")
? ? @DeleteMapping("{id}")
? ? public R removeTeacher( @ApiParam(name = "id", value = "講師ID", required = true) @PathVariable String id){
? ? ? ? boolean flag = teacherService.removeById(id);
? ? ? ? if (flag) {
? ? ? ? ? ? return R.ok();
? ? ? ? }else {
? ? ? ? ? ? return R.error();
? ? ? ? }
? ? }
04-分頁和條件查詢接口開發(fā)
一妓柜、分頁
1箱季、MyBatisPlusConfig中配置分頁插件
????/**
? ? * 分頁插件
? ? */
? ? @Bean
? ? public PaginationInterceptor paginationInterceptor() {
? ? ? ? return new PaginationInterceptor();
? ? }
2、分頁Controller方法
TeacherAdminController中添加分頁方法
????//3 分頁查詢講師的方法
? ? //current 當前頁
? ? //limit 每頁記錄數
? ? @ApiOperation(value = "分頁講師列表")
? ? @GetMapping("pageTeacher/{current}/{limit}")
? ? public R pageListTeacher(@ApiParam(name = "current", value = "當前頁碼", required = true) @PathVariable long current,
? ? ? ? ? ? ? ? ? ? ? ? ? ? @ApiParam(name = "limit", value = "每頁記錄數", required = true) @PathVariable long limit){
? ? ? ? //創(chuàng)建page對象
? ? ? ? Page<EduTeacher> pageTacher = new Page<>(current,limit);
? ? ? ? //調用方法實現分頁
? ? ? ? //調用方法的時候棍掐,底層封裝藏雏,把分頁所有數據封裝到pageTeacher對象里面
? ? ? ? teacherService.page(pageTacher,null);
? ? ? ? long total = pageTacher.getTotal(); //總記錄數
? ? ? ? List<EduTeacher> records = pageTacher.getRecords(); //數據list集合
//? ? ? ? Map map = new HashMap();
//? ? ? ? map.put("total",total);
//? ? ? ? map.put("rows",records);
//? ? ? ? return R.ok().data(map);
? ? ? ? return R.ok().data("total",total).data("rows",records);
3、Swagger中測試
二作煌、條件查詢
根據講師名稱name掘殴,講師頭銜level、講師入駐時間gmt_create(時間段)查詢
1最疆、創(chuàng)建查詢對象
創(chuàng)建com.guli.edu.query包杯巨,創(chuàng)建TeacherQuery.java查詢對象
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
@ApiModel(value = "Teacher查詢對象", description = "講師查詢對象封裝")
@Data
public class TeacherQuery {
? ? @ApiModelProperty(value = "教師名稱,模糊查詢")
? ? private String name;
? ? @ApiModelProperty(value = "頭銜 1高級講師 2首席講師")
? ? private Integer level;
? ? @ApiModelProperty(value = "查詢開始時間",example = "2019-01-01 10:10:10")
? ? private String begin;
? ? @ApiModelProperty(value = "查詢結束時間",example = "2019-12-01 10:10:10")
? ? private String end;
}
2、service
接口
public interface EduTeacherService extends IService<EduTeacher> {
? ? void pageQuery(Page<EduTeacher> pageParam,TeacherQuery teacherQuery);
}
實現
????@Override
? ? public void pageQuery(Page<EduTeacher> pageTacher, TeacherQuery teacherQuery) {
? ? ? ? QueryWrapper<EduTeacher> wrapper = new QueryWrapper<>();
? ? ? ? wrapper.orderByAsc("sort");
? ? ? ? if (teacherQuery == null){
? ? ? ? ? ? baseMapper.selectPage(pageTacher,wrapper);
? ? ? ? ? ? return;
? ? ? ? }
? ? ? ? String name = teacherQuery.getName();
? ? ? ? Integer level = teacherQuery.getLevel();
? ? ? ? String begin = teacherQuery.getBegin();
? ? ? ? String end = teacherQuery.getEnd();
? ? ? ? if (!StringUtils.isEmpty(name)){
? ? ? ? ? ? wrapper.like("name",name);
? ? ? ? }
? ? ? ? if (!StringUtils.isEmpty(level)){
? ? ? ? ? ? wrapper.eq("level",level);
? ? ? ? }
? ? ? ? if (!StringUtils.isEmpty(begin)){
? ? ? ? ? ? wrapper.ge("gmt_create",begin);
? ? ? ? }
? ? ? ? if (!StringUtils.isEmpty(end)){
? ? ? ? ? ? wrapper.le("gmt_modified",end);
? ? ? ? }
? ? ? ? baseMapper.selectPage(pageTacher,wrapper);
? ? }
3努酸、controller
TeacherAdminController中修改 pageList方法:
增加參數TeacherQuery teacherQuery,非必選
????//4 條件查詢帶分頁的方法
? ? @ApiOperation(value = "分頁講師列表")
? ? @GetMapping("pageTeacherCondition/{current}/{limit}")
? ? public R pageTeacherCondition(@ApiParam(name = "current", value = "當前頁碼", required = true) @PathVariable long current,
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? @ApiParam(name = "limit", value = "每頁記錄數", required = true) @PathVariable long limit,
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? TeacherQuery teacherQuery){
? ? ? ? //創(chuàng)建page對象
? ? ? ? Page<EduTeacher> pageTacher = new Page<>(current,limit);
? ? ? ? //構建條件
? ? ? ? QueryWrapper<EduTeacher> wrapper = new QueryWrapper<>();
? ? ? ? //wrapper
? ? ? ? //調用方法實現條件查詢分頁
? ? ? ? teacherService.pageQuery(pageTacher,teacherQuery);
? ? ? ? long total = pageTacher.getTotal(); //總記錄數
? ? ? ? List<EduTeacher> records = pageTacher.getRecords(); //數據list集合
//? ? ? ? Map map = new HashMap();
//? ? ? ? map.put("total",total);
//? ? ? ? map.put("rows",records);
//? ? ? ? return R.ok().data(map);
? ? ? ? return R.ok().data("total",total).data("rows",records);
? ? }
4杜恰、Swagger中測試
http://localhost:8001/swagger-ui.html#/
????//4 條件查詢帶分頁的方法
? ? @ApiOperation(value = "分頁講師列表")
? ? @PostMapping("pageTeacherCondition/{current}/{limit}")
? ? public R pageTeacherCondition(@ApiParam(name = "current", value = "當前頁碼", required = true) @PathVariable long current,
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? @ApiParam(name = "limit", value = "每頁記錄數", required = true) @PathVariable long limit,
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? @RequestBody(required = false) TeacherQuery teacherQuery){
? ? ? ? //創(chuàng)建page對象
? ? ? ? Page<EduTeacher> pageTacher = new Page<>(current,limit);
? ? ? ? //構建條件
? ? ? ? QueryWrapper<EduTeacher> wrapper = new QueryWrapper<>();
? ? ? ? //wrapper
? ? ? ? //調用方法實現條件查詢分頁
? ? ? ? teacherService.pageQuery(pageTacher,teacherQuery);
? ? ? ? long total = pageTacher.getTotal(); //總記錄數
? ? ? ? List<EduTeacher> records = pageTacher.getRecords(); //數據list集合
//? ? ? ? Map map = new HashMap();
//? ? ? ? map.put("total",total);
//? ? ? ? map.put("rows",records);
//? ? ? ? return R.ok().data(map);
? ? ? ? return R.ok().data("total",total).data("rows",records);
? ? }
測試結果:
05-新增和修改講師接口開發(fā)
一获诈、自動填充封裝
1、在service-base模塊中添加
創(chuàng)建包handler心褐,創(chuàng)建自動填充類 MyMetaObjectHandler
@Component
public class MyMetaObjectHandler implements MetaObjectHandler {
? ? @Override
? ? public void insertFill(MetaObject metaObject) {
? ? ? ? this.setFieldValByName("gmtCreate",new Date(),metaObject);
? ? ? ? this.setFieldValByName("gmtModified", new Date(), metaObject);
? ? }
? ? @Override
? ? public void updateFill(MetaObject metaObject) {
? ? ? ? this.setFieldValByName("gmtModified", new Date(), metaObject);
? ? }
}
2舔涎、在實體類添加自動填充注解
????@ApiModelProperty(value = "創(chuàng)建時間", example = "2019-01-01 8:00:00")
? ? @TableField(fill = FieldFill.INSERT)
? ? private Date gmtCreate;
? ? @ApiModelProperty(value = "更新時間", example = "2019-01-01 8:00:00")
? ? @TableField(fill = FieldFill.INSERT_UPDATE)
? ? private Date gmtModified;
二、controller方法定義
1逗爹、新增
????//5 添加講師接口的方法
? ? //通過對象傳遞數據@RequestBody
? ? @ApiOperation(value = "新增講師")
? ? @PostMapping("addTeacher")
? ? public R save(@ApiParam(name = "eduTeacher", value = "講師對象", required = true)
? ? ? ? ? ? ? ? ? @RequestBody EduTeacher eduTeacher){
? ? ? ? boolean save = teacherService.save(eduTeacher);
? ? ? ? if (save) {
? ? ? ? ? ? return R.ok();
? ? ? ? }else {
? ? ? ? ? ? return R.error();
? ? ? ? }
? ? }
2亡嫌、根據id查詢
????//根據講師id查詢
? ? @ApiOperation(value = "根據id查詢講師")
? ? @GetMapping("getTeacher/{id}")
? ? public R getById(@ApiParam(name = "id", value = "講師ID", required = true)
? ? ? ? ? ? ? ? ? ? ? ? @PathVariable String id){
? ? ? ? EduTeacher eduTeacher = teacherService.getById(id);
? ? ? ? return R.ok().data("teacher",eduTeacher);
? ? }
3嚎于、根據id修改
或:
????//講師修改功能
? ? @ApiOperation(value = "根據ID修改講師")
? ? @PostMapping("updateTeacher")
? ? public R updateTeacher(@RequestBody EduTeacher eduTeacher){
? ? ? ? boolean flag = teacherService.updateById(eduTeacher);
? ? ? ? if (flag) {
? ? ? ? ? ? return R.ok();
? ? ? ? }else {
? ? ? ? ? ? return R.error();
? ? ? ? }
? ? }
06-統(tǒng)一異常處理
一、什么是統(tǒng)一異常處理
我們想讓異常結果也顯示為統(tǒng)一的返回結果對象挟冠,并且統(tǒng)一處理系統(tǒng)的異常信息于购,那么需要統(tǒng)一異常處理
二、統(tǒng)一異常處理
1知染、創(chuàng)建統(tǒng)一異常處理器
在service-base中創(chuàng)建統(tǒng)一異常處理類GlobalExceptionHandler.java:
//統(tǒng)一異常處理類
@ControllerAdvice
public class GlobalExceptionHandler {
? ? //指定出現什么異常執(zhí)行這個方法
? ? @ExceptionHandler(Exception.class)
? ? @ResponseBody //為了返回數據
? ? public R error(Exception e){
? ? ? ? e.printStackTrace();
? ? ? ? return R.error().message("執(zhí)行了全局異常處理...");
? ? }
}
在service_base中添加依賴:
刪除service中的
測試:
三肋僧、處理特定異常
1、添加異常處理方法
@ExceptionHandler(Exception.class)
//特定異常
? ? @ExceptionHandler(ArithmeticException.class)
? ? @ResponseBody //為了返回數據
? ? public R error(ArithmeticException e){
? ? ? ? e.printStackTrace();
? ? ? ? return R.error().message("執(zhí)行了ArithmeticException異常處理...");
? ? }
四控淡、自定義異常
1嫌吠、創(chuàng)建自定義異常類
@AllArgsConstructor //生成有參數的構造方法
@NoArgsConstructor //生成無參數的構造方法
@Data
@AllArgsConstructor //生成有參數的構造方法
@NoArgsConstructor //生成無參數的構造方法
public class GuliException extends RuntimeException {
? ? @ApiModelProperty(value = "狀態(tài)碼")
? ? private Integer code;
? ? @ApiModelProperty(value = "異常信息")
? ? private String msg;
}
2、業(yè)務中需要的位置拋出GuliException
????try {
? ? ? ? ? ? int a = 10/0;
? ? ? ? }catch (Exception e){
? ? ? ? ? ? //執(zhí)行自定義異常
? ? ? ? ? ? throw new GuliException(20001,"出現自定義異常");
? ? ? ? }
3掺炭、添加異常處理方法
GlobalExceptionHandler.java中添加
????//自定義異常
? ? @ExceptionHandler(GuliException.class)
? ? @ResponseBody //為了返回數據
? ? public R error(GuliException e){
? ? ? ? e.printStackTrace();
? ? ? ? return R.error().code(e.getCode()).message(e.getMsg());
? ? }
4辫诅、測試
07-統(tǒng)一日志處理
一、日志
1涧狮、配置日志級別
日志記錄器(Logger)的行為是分等級的炕矮。如下表所示:
分為:OFF、FATAL勋篓、ERROR吧享、WARN、INFO譬嚣、DEBUG钢颂、ALL
默認情況下,spring boot從控制臺打印出來的日志級別只有INFO及以上級別拜银,可以配置日志級別
# 設置日志級別
logging.level.root=INFO
這種方式只能將日志打印在控制臺上
二殊鞭、Logback日志
spring boot內部使用Logback作為日志實現的框架。
Logback和log4j非常相似尼桶,如果你對log4j很熟悉操灿,那對logback很快就會得心應手。
logback相對于log4j的一些優(yōu)點:https://blog.csdn.net/caisini_vc/article/details/48551287
1泵督、配置logback日志
刪除application.properties中的日志配置
安裝idea彩色日志插件:grep-console
resources 中創(chuàng)建 logback-spring.xml?
<?xml version="1.0" encoding="UTF-8"?>
<configuration? scan="true" scanPeriod="10 seconds">
? ? <!-- 日志級別從低到高分為TRACE < DEBUG < INFO < WARN < ERROR < FATAL趾盐,如果設置為WARN,則低于WARN的信息都不會輸出 -->
? ? <!-- scan:當此屬性設置為true時小腊,配置文件如果發(fā)生改變救鲤,將會被重新加載,默認值為true -->
? ? <!-- scanPeriod:設置監(jiān)測配置文件是否有修改的時間間隔秩冈,如果沒有給出時間單位本缠,默認單位是毫秒。當scan為true時入问,此屬性生效丹锹。默認的時間間隔為1分鐘稀颁。 -->
? ? <!-- debug:當此屬性設置為true時,將打印出logback內部日志信息楣黍,實時查看logback運行狀態(tài)匾灶。默認值為false。 -->
? ? <contextName>logback</contextName>
? ? <!-- name的值是變量的名稱锡凝,value的值時變量定義的值粘昨。通過定義的值會被插入到logger上下文中。定義變量后窜锯,可以使“${}”來使用變量张肾。 -->
? ? <property name="log.path" value="D:/guli_log/edu" />
? ? <!-- 彩色日志 -->
? ? <!-- 配置格式變量:CONSOLE_LOG_PATTERN 彩色日志格式 -->
? ? <!-- magenta:洋紅 -->
? ? <!-- boldMagenta:粗紅-->
? ? <!-- cyan:青色 -->
? ? <!-- white:白色 -->
? ? <!-- magenta:洋紅 -->
? ? <property name="CONSOLE_LOG_PATTERN"
? ? value="%yellow(%date{yyyy-MM-dd HH:mm:ss}) |%highlight(%-5level) |%blue(%thread) |%blue(%file:%line) |%green(%logger) |%cyan(%msg%n)"/>
? ? <!--輸出到控制臺-->
? ? <appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender">
? ? ? ? <!--此日志appender是為開發(fā)使用,只配置最底級別锚扎,控制臺輸出的日志級別是大于或等于此級別的日志信息-->
? ? ? ? <!-- 例如:如果此處配置了INFO級別吞瞪,則后面其他位置即使配置了DEBUG級別的日志,也不會被輸出 -->
? ? ? ? <filter class="ch.qos.logback.classic.filter.ThresholdFilter">
? ? ? ? ? ? <level>INFO</level>
? ? ? ? </filter>
? ? ? ? <encoder>
? ? ? ? ? ? <Pattern>${CONSOLE_LOG_PATTERN}</Pattern>
? ? ? ? ? ? <!-- 設置字符集 -->
? ? ? ? ? ? <charset>UTF-8</charset>
? ? ? ? </encoder>
? ? </appender>
? ? <!--輸出到文件-->
? ? <!-- 時間滾動輸出 level為 INFO 日志 -->
? ? <appender name="INFO_FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
? ? ? ? <!-- 正在記錄的日志文件的路徑及文件名 -->
? ? ? ? <file>${log.path}/log_info.log</file>
? ? ? ? <!--日志文件輸出格式-->
? ? ? ? <encoder>
? ? ? ? ? ? <pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50} - %msg%n</pattern>
? ? ? ? ? ? <charset>UTF-8</charset>
? ? ? ? </encoder>
? ? ? ? <!-- 日志記錄器的滾動策略驾孔,按日期芍秆,按大小記錄 -->
? ? ? ? <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
? ? ? ? ? ? <!-- 每天日志歸檔路徑以及格式 -->
? ? ? ? ? ? <fileNamePattern>${log.path}/info/log-info-%d{yyyy-MM-dd}.%i.log</fileNamePattern>
? ? ? ? ? ? <timeBasedFileNamingAndTriggeringPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP">
? ? ? ? ? ? ? ? <maxFileSize>100MB</maxFileSize>
? ? ? ? ? ? </timeBasedFileNamingAndTriggeringPolicy>
? ? ? ? ? ? <!--日志文件保留天數-->
? ? ? ? ? ? <maxHistory>15</maxHistory>
? ? ? ? </rollingPolicy>
? ? ? ? <!-- 此日志文件只記錄info級別的 -->
? ? ? ? <filter class="ch.qos.logback.classic.filter.LevelFilter">
? ? ? ? ? ? <level>INFO</level>
? ? ? ? ? ? <onMatch>ACCEPT</onMatch>
? ? ? ? ? ? <onMismatch>DENY</onMismatch>
? ? ? ? </filter>
? ? </appender>
? ? <!-- 時間滾動輸出 level為 WARN 日志 -->
? ? <appender name="WARN_FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
? ? ? ? <!-- 正在記錄的日志文件的路徑及文件名 -->
? ? ? ? <file>${log.path}/log_warn.log</file>
? ? ? ? <!--日志文件輸出格式-->
? ? ? ? <encoder>
? ? ? ? ? ? <pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50} - %msg%n</pattern>
? ? ? ? ? ? <charset>UTF-8</charset> <!-- 此處設置字符集 -->
? ? ? ? </encoder>
? ? ? ? <!-- 日志記錄器的滾動策略,按日期翠勉,按大小記錄 -->
? ? ? ? <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
? ? ? ? ? ? <fileNamePattern>${log.path}/warn/log-warn-%d{yyyy-MM-dd}.%i.log</fileNamePattern>
? ? ? ? ? ? <timeBasedFileNamingAndTriggeringPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP">
? ? ? ? ? ? ? ? <maxFileSize>100MB</maxFileSize>
? ? ? ? ? ? </timeBasedFileNamingAndTriggeringPolicy>
? ? ? ? ? ? <!--日志文件保留天數-->
? ? ? ? ? ? <maxHistory>15</maxHistory>
? ? ? ? </rollingPolicy>
? ? ? ? <!-- 此日志文件只記錄warn級別的 -->
? ? ? ? <filter class="ch.qos.logback.classic.filter.LevelFilter">
? ? ? ? ? ? <level>warn</level>
? ? ? ? ? ? <onMatch>ACCEPT</onMatch>
? ? ? ? ? ? <onMismatch>DENY</onMismatch>
? ? ? ? </filter>
? ? </appender>
? ? <!-- 時間滾動輸出 level為 ERROR 日志 -->
? ? <appender name="ERROR_FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
? ? ? ? <!-- 正在記錄的日志文件的路徑及文件名 -->
? ? ? ? <file>${log.path}/log_error.log</file>
? ? ? ? <!--日志文件輸出格式-->
? ? ? ? <encoder>
? ? ? ? ? ? <pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50} - %msg%n</pattern>
? ? ? ? ? ? <charset>UTF-8</charset> <!-- 此處設置字符集 -->
? ? ? ? </encoder>
? ? ? ? <!-- 日志記錄器的滾動策略妖啥,按日期,按大小記錄 -->
? ? ? ? <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
? ? ? ? ? ? <fileNamePattern>${log.path}/error/log-error-%d{yyyy-MM-dd}.%i.log</fileNamePattern>
? ? ? ? ? ? <timeBasedFileNamingAndTriggeringPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP">
? ? ? ? ? ? ? ? <maxFileSize>100MB</maxFileSize>
? ? ? ? ? ? </timeBasedFileNamingAndTriggeringPolicy>
? ? ? ? ? ? <!--日志文件保留天數-->
? ? ? ? ? ? <maxHistory>15</maxHistory>
? ? ? ? </rollingPolicy>
? ? ? ? <!-- 此日志文件只記錄ERROR級別的 -->
? ? ? ? <filter class="ch.qos.logback.classic.filter.LevelFilter">
? ? ? ? ? ? <level>ERROR</level>
? ? ? ? ? ? <onMatch>ACCEPT</onMatch>
? ? ? ? ? ? <onMismatch>DENY</onMismatch>
? ? ? ? </filter>
? ? </appender>
? ? <!--
? ? ? ? <logger>用來設置某一個包或者具體的某一個類的日志打印級別对碌、以及指定<appender>荆虱。
? ? ? ? <logger>僅有一個name屬性,
? ? ? ? 一個可選的level和一個可選的addtivity屬性朽们。
? ? ? ? name:用來指定受此logger約束的某一個包或者具體的某一個類怀读。
? ? ? ? level:用來設置打印級別,大小寫無關:TRACE, DEBUG, INFO, WARN, ERROR, ALL 和 OFF骑脱,
? ? ? ? ? ? ? 如果未設置此屬性菜枷,那么當前l(fā)ogger將會繼承上級的級別。
? ? -->
? ? <!--
? ? ? ? 使用mybatis的時候叁丧,sql語句是debug下才會打印啤誊,而這里我們只配置了info,所以想要查看sql語句的話拥娄,有以下兩種操作:
? ? ? ? 第一種把<root level="INFO">改成<root level="DEBUG">這樣就會打印sql坷衍,不過這樣日志那邊會出現很多其他消息
? ? ? ? 第二種就是單獨給mapper下目錄配置DEBUG模式,代碼如下条舔,這樣配置sql語句會打印,其他還是正常DEBUG級別:
? ? -->
? ? <!--開發(fā)環(huán)境:打印控制臺-->
? ? <springProfile name="dev">
? ? ? ? <!--可以輸出項目中的debug日志乏矾,包括mybatis的sql日志-->
? ? ? ? <logger name="com.guli" level="INFO" />
? ? ? ? <!--
? ? ? ? ? ? root節(jié)點是必選節(jié)點孟抗,用來指定最基礎的日志輸出級別迁杨,只有一個level屬性
? ? ? ? ? ? level:用來設置打印級別,大小寫無關:TRACE, DEBUG, INFO, WARN, ERROR, ALL 和 OFF凄硼,默認是DEBUG
? ? ? ? ? ? 可以包含零個或多個appender元素铅协。
? ? ? ? -->
? ? ? ? <root level="INFO">
? ? ? ? ? ? <appender-ref ref="CONSOLE" />
? ? ? ? ? ? <appender-ref ref="INFO_FILE" />
? ? ? ? ? ? <appender-ref ref="WARN_FILE" />
? ? ? ? ? ? <appender-ref ref="ERROR_FILE" />
? ? ? ? </root>
? ? </springProfile>
? ? <!--生產環(huán)境:輸出到文件-->
? ? <springProfile name="pro">
? ? ? ? <root level="INFO">
? ? ? ? ? ? <appender-ref ref="CONSOLE" />
? ? ? ? ? ? <appender-ref ref="DEBUG_FILE" />
? ? ? ? ? ? <appender-ref ref="INFO_FILE" />
? ? ? ? ? ? <appender-ref ref="ERROR_FILE" />
? ? ? ? ? ? <appender-ref ref="WARN_FILE" />
? ? ? ? </root>
? ? </springProfile>
</configuration>
2、將錯誤日志輸出到文件
GlobalExceptionHandler.java 中
類上添加注解
異常輸出語句
3摊沉、將日志堆棧信息輸出到文件
定義工具類
guli-framework-common下創(chuàng)建util包狐史,創(chuàng)建ExceptionUtil.java工具類
public class ExceptionUtil {
? ? public static String getMessage(Exception e){
? ? ? ? StringWriter sw =null;
? ? ? ? PrintWriter pw = null;
? ? ? ? try {
? ? ? ? ? ? sw = new StringWriter();
? ? ? ? ? ? pw = new PrintWriter(sw);
? ? ? ? ? ? // 將出錯的棧信息輸出到printWriter中
? ? ? ? ? ? e.printStackTrace(pw);
? ? ? ? ? ? pw.flush();
? ? ? ? ? ? sw.flush();
? ? ? ? }finally {
? ? ? ? ? ? if (sw!= null){
? ? ? ? ? ? ? ? try {
? ? ? ? ? ? ? ? ? ? sw.close();
? ? ? ? ? ? ? ? }catch (IOException e1){
? ? ? ? ? ? ? ? ? ? e1.printStackTrace();
? ? ? ? ? ? ? ? }
? ? ? ? ? ? }
? ? ? ? ? ? if (pw!=null){
? ? ? ? ? ? ? ? pw.close();
? ? ? ? ? ? }
? ? ? ? }
? ? ? ? return sw.toString();
? ? }
}
調用
log.error(ExceptionUtil.getMessage(e));
GuliException中創(chuàng)建toString方法
@Override
? ? public String toString() {
? ? ? ? return "GuliException{" +
? ? ? ? ? ? ? ? "code=" + code +
? ? ? ? ? ? ? ? ", message='" + this.getMessage() + '\'' +
? ? ? ? ? ? ? ? '}';
? ? }