使用Gradle 构建一个SpringBoot + Spring Security + Mybatisplus的多模块系统
## 目标
- 使用 Gradle 构建一个多模块系统,熟悉脚本
- 搭建 SpringBoot ,Spring Security ,Mybatis子模块,实现基本的 Hello World。可以独立运行。
总体步骤:新建父工程(gradle) - 配置maven - 新建多个子工程(SpringBoot ,Spring Security ,Mybatis) - 修改父子的build.gradle & 父的settings.gradle - 将无用的文件进行删除 - 写相关测试类 - 测试
Pre1: 建本地数据库
Pre2: 把一个empty项目变成gradle项目: gradle init build
Pre3: git 从分支拉取:git clone -b brachname XXX
git push指定分支:git push origin brachname
1 搭建父工程:父工程没什么要求,直接new一个gradle的空工程就行。
父模块建好之后,多了下面红色框标出的东西,我们需要修改的有build.gradle(配置构建gradle项目时的一些东西,后面会细说,搭建多模块项目,主要就是在配置各个模块的这个文件) 和 settings.gradle (配置父子模块间的依赖关系)。
build.grale文件详细解释:https://www.cnblogs.com/caesar-the-great/p/12671179.html
配置父模块的build.gradle文件
1 /* 2 * This file was generated by the Gradle 'init' task. 3 * 4 * This is a general purpose Gradle build. 5 * Learn how to create Gradle builds at https://guides.gradle.org/creating-new-gradle-builds 6 */ 7 buildscript { 8 repositories { 9 maven { 10 url 'https://maven.aliyun.com/repository/gradle-plugin' 11 } 12 } 13 } 14 plugins { 15 id 'org.springframework.boot' version '2.2.5.RELEASE' apply false 16 id 'io.spring.dependency-management' version '1.0.9.RELEASE' 17 } 18 description="flight-sample" 19 20 ext { 21 22 23 //================= datasource ================ 24 mysqlConnectorJavaVersion = "8.0.19" 25 druidStarterVersion = "1.1.18" 26 druidVersion = "1.0.31" 27 28 mybatisPlusStarterVersion = "3.3.1" 29 30 //=================json======================= 31 fastjsonVersion = "1.2.62" 32 gsonVersion = "2.8.6" 33 jjwtVersion = "0.9.1" 34 35 //=================cache======================= 36 ehcacheVersion = "2.10.6" 37 38 //=================httpclient================= 39 httpclientVersion = "4.5.10" 40 OkHttpVersion = "3.14.4" 41 42 //================= tools==================== 43 GuavaVersion = "26.0-jre" 44 validationApiVersion = "2.0.1.Final" 45 protostuffVersion = "1.6.0" 46 curatorVersion = "4.0.1" 47 jacksonDataformatYamlVersion="2.9.8" 48 49 //================= test ==================== 50 h2Version = "1.4.197" 51 52 } 53 54 configure(allprojects ) { abjects -> 55 apply plugin: 'java' 56 apply plugin: 'maven-publish' 57 repositories { 58 59 maven { 60 url 'https://maven.aliyun.com/nexus/content/groups/public/' 61 } 62 63 maven { 64 url = 'https://repo.spring.io/libs-milestone' 65 } 66 67 maven { 68 url 'https://repo.spring.io/snapshot/' 69 } 70 71 maven { 72 url = 'https://oss.sonatype.org/content/repositories/snapshots/' 73 } 74 75 mavenLocal() 76 77 } 78 } 79 80 configure(subprojects){subjects -> 81 apply plugin: 'java' 82 apply plugin: 'maven' 83 apply plugin: 'maven-publish' 84 apply plugin: 'io.spring.dependency-management' 85 86 group = 'com.td.flight' 87 version = '0.0.1' 88 89 repositories { 90 91 maven { 92 url 'https://maven.aliyun.com/nexus/content/groups/public/' 93 } 94 95 maven { 96 url = 'https://repo.spring.io/libs-milestone' 97 } 98 99 maven { 100 url 'https://repo.spring.io/snapshot/' 101 } 102 103 maven { 104 url = 'https://oss.sonatype.org/content/repositories/snapshots/' 105 } 106 107 mavenLocal() 108 } 109 110 dependencyManagement { 111 imports { 112 mavenBom org.springframework.boot.gradle.plugin.SpringBootPlugin.BOM_COORDINATES 113 } 114 } 115 116 dependencies{ 117 compile "org.springframework.boot:spring-boot-starter-aop" 118 compile "org.springframework.boot:spring-boot-autoconfigure" 119 compile "org.springframework.boot:spring-boot-configuration-processor" 120 compile "org.springframework.retry:spring-retry" 121 testImplementation 'org.junit.platform:junit-platform-commons:1.5.2' 122 testCompile "org.springframework.boot:spring-boot-starter-test" 123 testCompile "org.assertj:assertj-core:3.15.0" 124 } 125 test { 126 useJUnitPlatform() 127 } 128 129 130 compileJava { 131 sourceCompatibility = 1.8 132 targetCompatibility = 1.8 133 options.encoding = "UTF-8" 134 } 135 136 task sourcesJar(type: Jar) { 137 from sourceSets.main.allJava 138 archiveClassifier = 'sources' 139 } 140 141 task javadocJar(type: Jar) { 142 from javadoc 143 archiveClassifier = 'javadoc' 144 } 145 146 }
2 搭建子模块:boot-demo
type选择gradle project
这里勾选web依赖(方便后面的测试),然后直接next->finish
这个时候,IDEA一直提示在importing gradle project,仿佛卡死在了这里
因为对于子项目xxx而言,IDEA为它自动生成了一个默认的build.gradle,这句mavenCentral()意思是从maven中心仓库下载相关依赖,大家都知道去国外的网站下载这些资源肯定是慢的一批
这里我们手动停止build的动作,删除其他无用配置(上面的父工程的build.gradle已经对所有的子项目做了配置,所以各个子工程里不需要再配了),只留下一句话
然后reimport all gradle projects
配置application.yaml
IDEA为我们默认生成的是application.properities文件,修改后缀为.yaml,然后添加port号
配置controller:添加@RestController 和 @RequestMapping注解,并写一个方法
启动测试
处理父子模块依赖:
修改父工程的settings.gradle,使子模块boot-demo属于父模块,多个子模块可以使用下面的逗号隔开的书写格式。写完include后,IDEA应该会自动重新import, 如果没有,则按上面的步骤执行reimport all gradle projects
如果gradle项目呈现出下图所示的所属结构,则证明子父模块依赖弄好了。
PS: 每新建一个module,都会生成一个和父工程同级的item (如下图所示),我们需要选中它,点击减号清理掉它(不然会有一些不必要的麻烦)
3 搭建子模块:mybatisplus-demo
参考:https://www.cnblogs.com/happy4java/p/11206801.html
对于Mybatisplus-demo模块的yaml文件有详细注释,方便以后学习:https://www.cnblogs.com/lgg20/archive/2019/10/28/11752946.html
和第2步搭建的boot-demo模块步骤基本一致,唯一的区别在于build.gradle文件中多引入了一些依赖(红框中的两个是必须的,其他的是我为了测试弄的)
build.gradle的脚本
1 configurations { 2 mybatisGenerator 3 } 4 dependencies { 5 implementation 'org.springframework.boot:spring-boot-starter-web' 6 compile "com.baomidou:mybatis-plus-boot-starter:${mybatisPlusStarterVersion}" 7 implementation "mysql:mysql-connector-java:${mysqlConnectorJavaVersion}" 8 //implementation 'org.projectlombok:lombok:1.18.2' 9 implementation 'org.mybatis.generator:mybatis-generator-core:1.3.5' 10 //mybatisGenerator 'mysql:mysql-connector-java:${mysqlConnectorJavaVersion}' 11 //implementation 'tk.mybatis:mapper:3.3.9' 12 //implementation 'tk.mybatis:mapper-spring-boot-starter:2.0.0' 13 annotationProcessor 'org.projectlombok:lombok:1.18.2' 14 compileOnly 'org.projectlombok:lombok:1.18.2' 15 }
配置application.yaml
1 spring: 2 datasource: 3 driver-class-name: com.mysql.cj.jdbc.Driver 4 url: jdbc:mysql://localhost:3306/test?useUnicode=true&characterEncoding=UTF-8&serverTimezone=GMT%2B8&useSSL=false 5 username: root 6 password: root 7 mybatis-plus: 8 mapper-locations: classpath:/mybatis/*Dao.xml 9 #实体扫描,多个package用逗号或者分号分隔 10 type-aliases-package: com.example.entity 11 global-config: 12 #主键类型 0:"数据库ID自增", 1:"用户输入ID",2:"全局唯一ID (数字类型唯一ID)", 3:"全局唯一ID UUID"; 13 id-type: 3 14 #字段策略 0:"忽略判断",1:"非 NULL 判断"),2:"非空判断" 15 field-strategy: 2 16 #驼峰下划线转换 17 db-column-underline: true 18 logic-delete-value: 1 19 logic-not-delete-value: 0 20 configuration: 21 map-underscore-to-camel-case: true 22 cache-enabled: false 23 server: 24 port: 8097
新建controller/mapper/entity/config类
controller
1 @RestController 2 public class UserController { 3 4 @Autowired 5 private UserDao UserDao; 6 7 @RequestMapping("/getUserInfoById") 8 public User getUserInfoById(){ 9 //List<User> users = UserDao.selectAll(); 10 User user = UserDao.selectById("1"); 11 return user; 12 } 13 @RequestMapping("/getUserList") 14 public List<User> getUserList(){ 15 List<User> users = UserDao.selectList(new QueryWrapper<>()); 16 return users; 17 } 18 @RequestMapping("/hello") 19 public String hello(){ 20 return "hello"; 21 } 22 @RequestMapping("/insertUserInfo") 23 public String insertUserInfo(){ 24 String result = ""; 25 User user = new User(); 26 user.setName("飞飞飞"); 27 user.setEmail("121213@163.com"); 28 user.setAge(21); 29 int count = UserDao.insert(user); 30 if(count > 0){ 31 result = "插入成功!"; 32 } else { 33 result = "失败!"; 34 } 35 return result; 36 } 37 }
mapper
1 @Mapper 2 public interface UserDao extends BaseMapper<User> { 3 //User findById(String id); 4 }
entity
1 @Data 2 @TableName("user") 3 public class User{ 4 @TableId 5 private String id; 6 private String name; 7 private int age; 8 private String email; 9 }
config
1 @Configuration 2 @MapperScan("com.example.dao")//扫描文件 3 public class MybatisPlusConfig { 4 5 /* 6 * 分页插件,自动识别数据库类型 7 * 多租户,请参考官网【插件扩展】 8 */ 9 @Bean 10 public PaginationInterceptor paginationInterceptor() { 11 return new PaginationInterceptor(); 12 } 13 }
项目结构
测试
查询(根据id查询,为了方便,id在代码中写死了)
DB中的数据
插入数据(为了方便,要插入的User对象直接在代码中写死了)
DB中的数据
4 搭建子模块:security-demo
build.gradle文件
1 dependencies { 2 compile project(":mybatisplus-demo") 3 implementation 'org.springframework.boot:spring-boot-starter-security' 4 implementation 'org.springframework.boot:spring-boot-starter-web' 5 annotationProcessor 'org.projectlombok:lombok:1.18.2' 6 compileOnly 'org.projectlombok:lombok:1.18.2' 7 }
测试用的类ref: https://www.cnblogs.com/telwanggs/p/10802851.html
这里说一下我对springSecurity工作原理的初步认识:
shiro架构初步解读:
解释:user想和application交互之前,必须先问问security manager同不同意,security manager需要去咨询一下realm,最终做出决定。
PS: 在搭建mybatisplus-demo模块的过程中,踩了很多坑,在这里Mark一下
坑1:在build gradle项目的时候,一直提示cannot find implement(),IDEA把问题定位到了build.gradle的文件中的这些位置,做法:在gradle中直接移除子模块,再reimport (ref步骤2中的PS部分)
坑2:在所有的配置项,测试类写好之后,开始测试的时候,web页面一直提示:Whitelabel Error Page,百度了一下:一般出现这个问题的原因就是目录结构不正确,导致主应用程序类(Main application class)扫描不到controller类。默认情况下主应用程序类(Main application class)只会扫描同一包下的Class。对照了一下自己的,我是把启动类放到了config包下,所以导致扫描不到其他包,做法:把启动类拖出来,和com.example同级。
坑3:web页面访问查数据的url (getUserInfoById),在web页面死活不返回数据,只有一个大大的 {}, 里面什么都没有;同时类里面的user.setXXXX() 等方法报一系列乱码,当时我就懵逼了,反思了一下:我user的entity用的lombok,然后百度了一下lombok的gradle依赖引入,原来是我的引用方式不对。在gradle4.7版本以后甚至现在gradle5.0了里面这种方式会产生警告,在gradle5.0里面会直接报编译错误。正确使用可以采用如下方式:
1 annotationProcessor 'org.projectlombok:lombok:1.18.2' 2 compileOnly 'org.projectlombok:lombok:1.18.2'
注释掉的是我写的错的。
Lombok的使用:主要是entity中少些了一些setter 和getter等方法。
坑4:本来用的是mybatisplus框架,因为在搭模块的时候参考了网上的一些资料,竟然阴差阳错的又引入了另外一个mybatis框架:tk.mybatis,然后就遇到了下面的问题:java.lang.NoSuchMethodException: tk.mybatis.mapper.provider.base.BaseSelectProvider.<init>(),使用selectByPrimaryKey无法根据主键查找获取结果,解决方法:在写实体类时,引入@Id注解时,导入正确的包:import javax.persistence.Id
而我导成了:org.springframework.data.annotation.Id (当然最后注释掉了tk.mybatis相关的依赖,不然会冲突,这里只是mark一下遇到这个问题的解决方法)
同时应该只保留一种Mybatis相关的框架依赖,我原来是两个共存(不知道是抽什么风加进去的。。。)
PS: 其他细节,后续再补上,今天先写到这里 --- 20200408-23:03
补充(2020.4.22)
1 删除项目中的无用文件:
删除所有子模块中的gradlew文件
删除所有子模块中的gradlew.bat文件
删除所有子模块中的gradle文件夹
删除所有子模块中的.gitignore文件,在项目最外层配置.gitignore文件,做为整个项目的git提交忽略配置
github: 暂时未公开
PS: 第一次写博客,难免有不妥的地方,希望理解。