本篇在数据库表建立完毕的基础之上, 总结一下EZShare项目的技术选型(后续可能会进行修改和添加), 以及后端的一些配置, 最后总结一下Mybatis-Plus提供的代码生成技术
具体代码见: https://github.com/JasonkayZK/EZShare
欢迎PR❤
EZShare项目总结-2
项目采用前后端分离架构, 后端采用SSM(Spring Boot 2.2.4 + Spring MVC + Mybatis)
项目拟采用的技术栈和功能有:
| 名称 |
说明 |
| JDK 11 |
/ |
| Lombok |
/ |
| Spring Boot Actuator |
Spring应用程序监控 |
| Spring Boot AOP |
自定义切面 |
| Redis |
缓存 |
| Dynamic Datasource |
动态数据源 |
| Mybatis-Plus |
多表关联查询, 分页等ORM相关操作 |
| Mybatis-Plus-Generator & Freemarker |
基于Freemarker的代码生成技术 |
| p6spy |
数据库跟踪, 慢查询监控等 |
| Shro & JWT |
权限管理, 身份校验等 |
| Guava & Apache Commons: lang3、io、fileupload |
字符串、文件上传等工具类 |
| ExcelKit |
Excel等文件导出 |
| quartz |
任务计划 |
| ip2region |
ip地址定位库 |
| Swagger |
RESTful接口文档生成 |
下面对个别功能和配置进行说明
项目总体配置
application.yml
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133
| server: port: 8848
spring: datasource: dynamic: p6spy: true
hikari: connection-timeout: 30000 max-lifetime: 1800000 max-pool-size: 15 min-idle: 5 connection-test-query: SELECT 1 FROM DUAL pool-name: HikariCP
primary: primary datasource: primary: username: root password: 123456 driver-class-name: com.p6spy.engine.spy.P6SpyDriver url: jdbc:p6spy:mysql://127.0.0.1:3306/ezshare?useUnicode=true&characterEncoding=UTF-8&useJDBCCompliantTimezoneShift=true&useLegacyDatetimeCode=false&serverTimezone=UTC
test: username: root password: 123456 driver-class-name: com.p6spy.engine.spy.P6SpyDriver url: jdbc:p6spy:mysql://127.0.0.1:3306/ezshare_test?useUnicode=true&characterEncoding=UTF-8&useJDBCCompliantTimezoneShift=true&useLegacyDatetimeCode=false&serverTimezone=UTC
aop: proxy-target-class: true
messages: encoding: utf-8
jackson: date-format: yyyy-MM-dd HH:mm:ss time-zone: GMT+8
redis: host: 127.0.0.1 port: 6379 password: jedis: pool: min-idle: 8 max-idle: 500 max-active: 2000 max-wait: 10000 timeout: 0
servlet: multipart: max-file-size: 1099511627776 max-request-size: 1099511627776
main: banner-mode: off
logging: config: classpath:logback.xml
management: endpoints: web: exposure: include: ['httptrace', 'metrics', 'caches']
mybatis-plus: type-aliases-package: top.jasonkayzk.ezshare.system.entity,top.jasonkayzk.ezshare.job.entity,top.jasonkayzk.ezshare.file.entity mapper-locations: classpath:mapper/*/*.xml configuration: jdbc-type-for-null: null global-config: banner: true
swagger: enabled: true title: EZShare Application API basePackage: top.jasonkayzk.ezshare basePath=/**: description: upload, download, share, file-sharing system version: 1.0 author: Jasonkay url: https://github.com/JasonkayZK/EZShare email: jasonkayzk@gmail.com license: Apache 2.0 licenseUrl: https://www.apache.org/licenses/LICENSE-2.0.html exclude-path: error, /ops/**
shiro: anonUrl: - /login - /logout/** - /regist - /user/check/** - /swagger-resources/** - /webjars/** - /v2/** - /swagger-ui.html/** - /favicon.ico jwtTimeOut: 3600
ezshare: openAopLog: true
max: batch: insert: num: 2000
|
说明: 其中包括了本项目中绝大多数的配置
日志记录Logback配置
在application.yml中配置配置文件的地址:
1 2 3
| logging: config: classpath:logback.xml
|
logback.xml文件内容:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54
| <?xml version="1.0" encoding="UTF-8"?> <configuration scan="true" scanPeriod="60 seconds" debug="false"> <contextName>ezshare</contextName> <property name="log.path" value="log" /> <property name="log.maxHistory" value="15" /> <property name="log.colorPattern" value="%d{yyyy-MM-dd HH:mm:ss} | %highlight(%-5level) | %boldYellow(%thread) | %boldGreen(%logger) | %msg%n"/> <property name="log.pattern" value="%d{yyyy-MM-dd HH:mm:ss.SSS} %contextName [%thread] %-5level %logger{36} - %msg%n" />
<appender name="console" class="ch.qos.logback.core.ConsoleAppender"> <encoder> <pattern>${log.colorPattern}</pattern> </encoder> </appender>
<appender name="log_info" class="ch.qos.logback.core.rolling.RollingFileAppender"> <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy"> <fileNamePattern>${log.path}/info/info.%d{yyyy-MM-dd}.log</fileNamePattern> <MaxHistory>${log.maxHistory}</MaxHistory> </rollingPolicy> <encoder> <pattern>${log.pattern}</pattern> </encoder> <filter class="ch.qos.logback.classic.filter.LevelFilter"> <level>INFO</level> <onMatch>ACCEPT</onMatch> <onMismatch>DENY</onMismatch> </filter> </appender>
<appender name="log_error" class="ch.qos.logback.core.rolling.RollingFileAppender"> <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy"> <fileNamePattern>${log.path}/error/error.%d{yyyy-MM-dd}.log</fileNamePattern> </rollingPolicy> <encoder> <pattern>${log.pattern}</pattern> </encoder> <filter class="ch.qos.logback.classic.filter.LevelFilter"> <level>ERROR</level> <onMatch>ACCEPT</onMatch> <onMismatch>DENY</onMismatch> </filter> </appender>
<root level="debug"> <appender-ref ref="console" /> </root>
<root level="info"> <appender-ref ref="log_info" /> <appender-ref ref="log_error" /> </root> </configuration>
|
logback.xml配置文件说明:
在Spring Boot 2.x中Logback是默认的日志方式, 所以无需引入依赖即可使用
参数校验提示信息配置
Spring Boot中可在resources路径下创建ValidationMessages.properties(文件名称不可更改!), 来提供参数校验时的提示信息
ValidationMessages.properties
1 2 3 4 5 6
| required=内容不能为空 range=输入有效长度{min}到{max}个字符 email=邮箱输入格式不合法 mobile=手机号输入不合法 noMoreThan=输入长度不能超过{max}个字符 invalid=输入值不合法
|
注意:
Spring Boot在解析.properties文件时默认采用Unicode编码, 此时中文会产生乱码
解决方法:
在application.yml配置:
1 2 3 4
| ># Spring 配置内容编码(ValidationMessages.properties) >spring: messages: encoding: utf-8
|
p6spy数据库
① 修改driver和url
如下
1 2 3 4 5 6 7
| datasource: primary: username: root password: 123456 driver-class-name: com.p6spy.engine.spy.P6SpyDriver url: jdbc:p6spy:mysql://127.0.0.1:3306/ezshare?useUnicode=true&characterEncoding=UTF-8&useJDBCCompliantTimezoneShift=true&useLegacyDatetimeCode=false&serverTimezone=UTC
|
② 创建spy.properties配置
spy.properties
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
|
appender=com.p6spy.engine.spy.appender.Slf4JLogger
logMessageFormat=top.jasonkayzk.ezshare.common.config.P6spySqlFormatConfig
outagedetection=true
outagedetectioninterval=3
filter=true
exclude=QRTZ
|
③ 创建P6spySqlFormatConfig配置类
P6spySqlFormatConfig.java
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
|
public class P6spySqlFormatConfig implements MessageFormattingStrategy {
@Override public String formatMessage(int connectionId, String now, long elapsed, String category, String prepared, String sql, String url) { return StringUtils.isNotBlank(sql) ? DateUtil.formatFullTime(LocalDateTime.now(), DateUtil.FULL_TIME_SPLIT_PATTERN) + " | 耗时 " + elapsed + " ms | SQL 语句:" + StringUtils.LF + sql.replaceAll("[\\s]+", StringUtils.SPACE) + ";" : ""; }
}
|
更多详细内容见: https://p6spy.readthedocs.io/en/latest/configandusage.html
Mybatis-Plus提供的代码生成
① 引入依赖
字节码生成采用Freemarker模板, 所以还应当加入Freemarker的依赖
1 2 3 4 5 6 7 8 9 10 11 12 13
| <dependency> <groupId>com.baomidou</groupId> <artifactId>mybatis-plus-generator</artifactId> <version>${mybatis-plus.version}</version> </dependency>
<dependency> <groupId>org.freemarker</groupId> <artifactId>freemarker</artifactId> <version>${freemarker.version}</version> </dependency>
|
② 创建模板
1 2 3 4 5 6 7 8 9 10 11
| . ├── application.yml ├── generator │ └── templates │ ├── controller.java.ftl │ ├── entity.java.ftl │ ├── mapper.java.ftl │ ├── mapper.xml.ftl │ ├── serviceImpl.java.ftl │ └── service.java.ftl └── ...
|
模板内容见: https://github.com/JasonkayZK/EZShare/tree/master/backend/src/main/resources/generator/templates
其实模板网上一搜也有一大堆
③ 创建生成器类
Mybatis-Plus官方也是提供了这个生成器类的, 我的生成器内容如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156
| package top.jasonkayzk.ezshare.common.generator;
import com.baomidou.mybatisplus.core.exceptions.MybatisPlusException; import com.baomidou.mybatisplus.generator.AutoGenerator; import com.baomidou.mybatisplus.generator.config.*; import com.baomidou.mybatisplus.generator.config.rules.NamingStrategy; import com.baomidou.mybatisplus.generator.engine.FreemarkerTemplateEngine; import org.apache.commons.lang3.StringUtils;
import java.util.Scanner;
public class CodeGenerator {
private static final String URL = "jdbc:mysql://127.0.0.1:3306/ezshare?useUnicode=true&characterEncoding=UTF-8&useJDBCCompliantTimezoneShift=true&useLegacyDatetimeCode=false&serverTimezone=UTC";
private static final String DRIVER_NAME = "com.mysql.cj.jdbc.Driver";
private static final String USERNAME = "root";
private static final String PASSWORD = "123456";
private static final String AUTHOR = "Jasonkay";
private static final String BASE_PACKAGE_URL = "top.jasonkayzk.ezshare";
private static final String XML_MAPPER_TEMPLATE_PATH = "generator/templates/mapper.xml";
private static final String TABLE_PREFIX = "t_";
private static final String MAPPER_TEMPLATE_PATH = "generator/templates/mapper.java";
private static final String ENTITY_TEMPLATE_PATH = "generator/templates/entity.java";
private static final String SERVICE_TEMPLATE_PATH = "generator/templates/service.java";
private static final String SERVICE_IMPL_TEMPLATE_PATH = "generator/templates/serviceImpl.java";
private static final String CONTROLLER_TEMPLATE_PATH = "generator/templates/controller.java";
public static void main(String[] args) { AutoGenerator generator = new AutoGenerator();
GlobalConfig globalConfig = new GlobalConfig(); String projectPath = System.getProperty("user.dir"); globalConfig.setOutputDir(projectPath + "/src/main/java"); globalConfig.setAuthor(AUTHOR); globalConfig.setOpen(false); globalConfig.setFileOverride(false); generator.setGlobalConfig(globalConfig);
DataSourceConfig dataSourceConfig = new DataSourceConfig(); dataSourceConfig.setUrl(URL); dataSourceConfig.setDriverName(DRIVER_NAME); dataSourceConfig.setUsername(USERNAME); dataSourceConfig.setPassword(PASSWORD); generator.setDataSource(dataSourceConfig);
PackageConfig packageConfig = new PackageConfig(); packageConfig.setModuleName("gen"); packageConfig.setParent(BASE_PACKAGE_URL); generator.setPackageInfo(packageConfig);
TemplateConfig templateConfig = new TemplateConfig(); templateConfig.setXml(XML_MAPPER_TEMPLATE_PATH); templateConfig.setMapper(MAPPER_TEMPLATE_PATH); templateConfig.setEntity(ENTITY_TEMPLATE_PATH); templateConfig.setService(SERVICE_TEMPLATE_PATH); templateConfig.setServiceImpl(SERVICE_IMPL_TEMPLATE_PATH); templateConfig.setController(CONTROLLER_TEMPLATE_PATH); generator.setTemplate(templateConfig);
StrategyConfig strategy = new StrategyConfig(); strategy.setNaming(NamingStrategy.underline_to_camel); strategy.setColumnNaming(NamingStrategy.underline_to_camel); strategy.setEntityLombokModel(true); strategy.setRestControllerStyle(true); strategy.setInclude(scanner()); strategy.setControllerMappingHyphenStyle(true); strategy.setTablePrefix(TABLE_PREFIX); generator.setStrategy(strategy); generator.setTemplateEngine(new FreemarkerTemplateEngine()); generator.execute(); }
private static String[] scanner() { Scanner scanner = new Scanner(System.in); System.out.println(("请输入表名(多个表使用空格分开)" + ":")); if (scanner.hasNextLine()) { String ipt = scanner.nextLine(); if (StringUtils.isNotBlank(ipt)) { return ipt.split("\\s"); } } throw new MybatisPlusException("请输入正确的" + "表名" + "!"); }
}
|
输入表名, 即可根据模板生成代码
补充:
① 去掉表前缀:
在我的数据表中有类似t_tablename的前缀, 可以通过策略配置去掉:
1 2
| > >strategy.setTablePrefix("t_");
|
② 生成实体entity没有id
删去: strategy.setSuperEntityColumns("id");即可
源代码见: https://github.com/JasonkayZK/EZShare/tree/master/backend/src/main/java/top/jasonkayzk/ezshare/common/generator/
更多内容见: AutoGenerator官方文档
ip转地区
将数据放在resources目录下即可
可见: https://github.com/lionsoul2014/ip2region
启动类
一般情况下, 都是使用SpringApplication.run(Application.class,args);启动Spring Boot应用
但是使用SpringApplicationBuilder可以在启动时增加更多的配置信息(如自定义横幅和记录器等), 启动方式更为灵活
1 2 3 4 5 6 7 8 9 10 11 12 13
| @SpringBootApplication @EnableTransactionManagement @EnableScheduling @EnableAsync public class EzShareApplication {
public static void main(String[] args) { new SpringApplicationBuilder(EzShareApplication.class) .bannerMode(Banner.Mode.OFF) .run(args); }
}
|
补充:
- EnableTransactionManagement: 开启事务支持, 在访问数据库的Service方法上添加注解
@Transactional便可(无需Spring中的配置)
- EnableScheduling: 启动定时任务, 可与quartz配合使用
- EnableAsync: 开启多线程进行异步调用执行, 让@Async注解生效