文章目录
父项目microcloud
添加common-api子模块
添加 provider-dept-8001 子模块
添加 consumer-springboot-80 子模块
New Module
Gradle版本:
版本组合:
springboot : ‘2.2.5.RELEASE’, // SpringBoot版本号
springcloud : ‘Hoxton.SR3’, // SpringCloud版本号
alibabacloud : ‘2.2.1.RELEASE’, // SpringCloudAlibaba版本号
gradle.properties
project_group=com.yootk
project_version=1.0.0
project_jdk=11
dependencies.gradle
ext.versions = [ // 定义全部的依赖库版本号
springboot : '2.2.5.RELEASE', // SpringBoot版本号
springcloud : 'Hoxton.SR3', // SpringCloud版本号
alibabacloud : '2.2.1.RELEASE', // SpringCloudAlibaba版本号
lombok : '1.18.20', // Lombok版本号
junit : '5.6.3', // 配置JUnit测试工具的版本编号
junitPlatformLauncher: '1.6.3', // JUnit测试工具运行平台版本编号
mybatisPlus : '3.4.3', // MyBatisPlus的版本号
mysql : '8.0.25', // MySQL数据库驱动版本
druid : '1.2.6', // Druid版本号
swagger : '3.0.0', // Swagger版本号
nacos : '2.0.2', // Nacos版本号
]
ext.libraries = [ // 依赖库引入配置
'spring-boot-gradle-plugin' :
"org.springframework.boot:spring-boot-gradle-plugin:${versions.springboot}",
'spring-cloud-dependencies' :
"org.springframework.cloud:spring-cloud-dependencies:${versions.springcloud}",
'spring-cloud-alibaba-dependencies':
"com.alibaba.cloud:spring-cloud-alibaba-dependencies:${versions.alibabacloud}",
// 以下的配置为与项目用例测试有关的依赖
'junit-jupiter-api' :
"org.junit.jupiter:junit-jupiter-api:${versions.junit}",
'junit-vintage-engine' :
"org.junit.vintage:junit-vintage-engine:${versions.junit}",
'junit-jupiter-engine' :
"org.junit.jupiter:junit-jupiter-engine:${versions.junit}",
'junit-platform-launcher' :
"org.junit.platform:junit-platform-launcher:${versions.junitPlatformLauncher}",
'junit-platform-engine' :
"org.junit.platform:junit-platform-engine:${versions.junitPlatformLauncher}",
'junit-jupiter-params' :
"org.junit.jupiter:junit-jupiter-params:${versions.junit}",
'junit-bom' : "org.junit:junit-bom:${versions.junit}",
'junit-platform-commons' :
"org.junit.platform:junit-platform-commons:${versions.junitPlatformLauncher}",
// 以下的配置为Lombok组件有关的依赖
'lombok' : "org.projectlombok:lombok:${versions.lombok}",
// 以下的配置为数据库开发有关的依赖
'mybatis-plus-boot-starter' : "com.baomidou:mybatis-plus-boot-starter:${versions.mybatisPlus}",
'mysql-connector-java' : "mysql:mysql-connector-java:${versions.mysql}",
'druid' : "com.alibaba:druid:${versions.druid}",
// 以下的配置为Swagger有关的依赖库
'springfox-boot-starter' : "io.springfox:springfox-boot-starter:${versions.swagger}",
// 以下的配置为Nacos有关的依赖库
'nacos-client' : "com.alibaba.nacos:nacos-client:${versions.nacos}"
]
build.gradle
buildscript { // 定义脚本使用资源
apply from: 'dependencies.gradle' // 引入所需要的依赖库文件
repositories { // 脚本资源仓库
maven { url 'https://maven.aliyun.com/repository/public' }
}
dependencies { // 依赖库
classpath libraries.'spring-boot-gradle-plugin' // SpringBoot插件
}
}
group project_group // 组织名称
version project_version // 项目版本
apply from: 'dependencies.gradle' // 导入依赖配置
def env = System.getProperty("env") ?: 'dev' // 获取env环境属性
subprojects { // 配置子项目
apply plugin: 'java' // 子模块插件
apply plugin: 'org.springframework.boot' // 引入SpringBoot插件
apply plugin: 'io.spring.dependency-management' // 版本号管理
sourceCompatibility = project_jdk // 源代码版本
targetCompatibility = project_jdk // 生成类版本
repositories { // 配置Gradle仓库
mavenLocal()
maven { url 'http://maven.aliyun.com/nexus/content/groups/public/' }
mavenCentral()
jcenter()
}
dependencyManagement {// 版本控制插件
imports {
mavenBom libraries.'spring-cloud-dependencies' // SpringCloud依赖管理
mavenBom libraries.'spring-cloud-alibaba-dependencies' // SpringCloudAlibaba依赖管理
}
}
dependencies { // 公共依赖库管理
compile('org.springframework.boot:spring-boot-devtools') // 项目热部署
// 以下为测试环境的相关依赖配置
testImplementation('org.springframework.boot:spring-boot-starter-test') {
exclude group: 'junit', module: 'junit' // 移除Junit4
}
testImplementation(enforcedPlatform(libraries.'junit-bom')) // 绑定为JUnit5运行
testImplementation(libraries.'junit-platform-commons') // Junit5测试组件
testImplementation(libraries.'junit-platform-engine') // Junit5测试组件
testImplementation(libraries.'junit-jupiter-api') // Junit5测试组件
testImplementation(libraries.'junit-vintage-engine') // Junit5测试组件
testImplementation(libraries.'junit-jupiter-engine') // Junit5测试组件
testImplementation(libraries.'junit-platform-launcher') // Junit5测试组件
// 以下为Lombok插件的相关依赖配置
compileOnly(libraries.'lombok') // 编译时生效
annotationProcessor(libraries.'lombok') // 注解时生效
}
sourceSets { // 源代码目录配置
main { // main及相关子目录配置
java { srcDirs = ['src/main/java'] }
resources { srcDirs = ['src/main/resources', "src/main/profiles/$env"] }
}
test { // test及相关子目录配置
java { srcDirs = ['src/test/java'] }
resources { srcDirs = ['src/test/resources'] }
}
}
test { // 配置测试任务
useJUnitPlatform() // 使用JUnit测试平台
}
task sourceJar(type: Jar, dependsOn: classes) { // 源代码的打包任务
archiveClassifier = 'sources' // 设置文件的后缀
from sourceSets.main.allSource // 所有源代码的读取路径
}
task javadocTask(type: Javadoc) { // JavaDoc文档打包任务
options.encoding = 'UTF-8' // 设置文件编码
source = sourceSets.main.allJava // 定义所有的Java源代码
}
task javadocJar(type: Jar, dependsOn: javadocTask) { // 先生成JavaDoc再打包
archiveClassifier = 'javadoc' // 文件标记类型
from javadocTask.destinationDir // 通过JavadocTask任务中找到目标路径
}
tasks.withType(Javadoc) { // 文档编码配置
options.encoding = 'UTF-8' // 定义编码
}
tasks.withType(JavaCompile) { // 编译编码配置
options.encoding = 'UTF-8' // 定义编码
}
artifacts { // 最终的打包的操作任务
archives sourceJar // 源代码打包
archives javadocJar // javadoc打包
}
gradle.taskGraph.whenReady { // 在所有的操作准备好后触发
tasks.each { task -> // 找出所有的任务
if (task.name.contains('test')) { // 如果发现有test任务
// 如果将enabled设置为true表示要执行测试任务,如果设置为false表示不执行测试任务
task.enabled = true
}
}
}
[compileJava, compileTestJava, javadoc]*.options*.encoding = 'UTF-8'// 编码配置
}
project(":common-api") { // 进行子模块的配置
dependencies { // 配置模块所需要的依赖库
compile("org.springframework.boot:spring-boot-starter-web") // SpringBoot依赖
compile('org.springframework.cloud:spring-cloud-starter-openfeign')
}
}
project(":provider-dept-8001") { // 部门微服务
dependencies {
implementation(project(":common-api")) // 导入公共的子模块
implementation(libraries.'mybatis-plus-boot-starter')
implementation(libraries.'mysql-connector-java')
implementation(libraries.'druid')
implementation(libraries.'springfox-boot-starter')
// 以下的依赖库为Nacos注册中心所需要的依赖配置
implementation('com.alibaba.cloud:spring-cloud-starter-alibaba-nacos-discovery') {
exclude group: 'com.alibaba.nacos', module: 'nacos-client' // 移除旧版本的Nacos依赖
}
implementation('com.alibaba.cloud:spring-cloud-starter-alibaba-nacos-config') {
exclude group: 'com.alibaba.nacos', module: 'nacos-client' // 移除旧版本的Nacos依赖
}
implementation(libraries.'nacos-client') // 引入与当前的Nacos匹配的依赖库
}
}
project(":consumer-springboot-80") { // 消费端模块
dependencies {
implementation(project(":common-api")) // 导入公共的子模块
implementation('com.alibaba.cloud:spring-cloud-starter-alibaba-nacos-discovery') {
exclude group: 'com.alibaba.nacos', module: 'nacos-client' // 移除旧版本的Nacos依赖
}
implementation(libraries.'nacos-client') // 引入与当前的Nacos匹配的依赖库
}
}
本地模拟RPC调用
配置文件路径:C:\Windows\System32\driversletc\hosts
服务提供者主机名称: 127.0.0.1 provider-dept-8001
服务消费者主机名称: 127.0.0.1 consumer-springboot-80
common-api子模块
创建dto类
package com.yootk.common.dto;
import lombok.Data;
import java.io.Serializable;
// 该类主要实现部门数据结构的映射,实现数据的远程传输
@Data // Lombok注解,自动生成所需要的类结构
public class DeptDTO implements Serializable { // 定义数据传输类
private Long deptno; // 部门编号
private String dname; // 部门名称
private String loc; // 部门位置
}
创建服务接口
package com.yootk.service;
import com.yootk.common.dto.DeptDTO;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestParam;
import java.util.List;
import java.util.Map;
@FeignClient("dept.provider") // 定义要访问的微服务实例名称
public interface IDeptService { // 业务接口
/**
* 根据部门的编号获取部门的完整信息
* @param id 要查询的部门编号
* @return 编号存在则以DTO对象的形式返回部门数据,如果不存在返回null
*/
@GetMapping("/provider/dept/get/{deptno}") // 远程REST接口
public DeptDTO get(@PathVariable("deptno") long id);
/**
* 增加部门对象
* @param dto 保存要增加部门的详细数据
* @return 增加成功返回true,否则返回false
*/
@PostMapping("/provider/dept/add")
public boolean add(DeptDTO dto);
/**
* 列出所有的部门数据信息
* @return 全部数据的集合, 如果没有任何的部门数据则集合为空(size() == 0)
*/
@GetMapping("/provider/dept/list")
public List<DeptDTO> list();
/**
* 进行部门的分页数据加载操作
* @param currentPage 当前所在页
* @param lineSize 每页加载的数据行数
* @param column 模糊查询的数据列
* @param keyword 模糊查询关键字
* @return 部门集合数据以及统计数据,返回的数据项包括:
* 1、key = allDepts、value = List集合(部门的全部数据对象)
* 2、key = allRecorders、value = 总记录数;
* 3、key = allPages、value = 页数。
*/
@GetMapping("/provider/dept/split")
public Map<String, Object> split(
@RequestParam("cp") int currentPage,
@RequestParam("ls") int lineSize,
@RequestParam("col") String column,
@RequestParam("kw") String keyword);
}
创建一个对象拷贝的工具类
在Spring 开发框架内部提供有一个BeanUJtils工具类,这个工具类有一个最大的特点就是可以直接实现对象的数据的拷贝操作,可是这个拷贝操作不包含有集合数据的拷贝处理。
package com.yootk.common.util;
import org.springframework.beans.BeanUtils;
import java.util.ArrayList;
import java.util.List;
import java.util.function.Supplier;
public class DeepBeanUtils extends BeanUtils { // 扩充已有的Bean工具类
private DeepBeanUtils() {}
/**
* 实现List集合对象的拷贝处理
* @param sources 原始对象集合
* @param target 目标对象集合
* @param <S> 源对象类型
* @param <T> 目标对象类型
* @return 拷贝后的List集合
*/
public static <S, T> List<T> copyListProperties(List<S> sources, Supplier<T> target) {
List<T> list = new ArrayList<>(sources.size()); // 开辟一个新的List集合
for (S source : sources) { // 集合迭代
T obj = target.get(); // 获取数据
copyProperties(source, obj); // 由父类所提供的拷贝方法
list.add(obj); // 增加集合数据
}
return list; // 返回集合
}
}
provider-dept-8001 子模块
bootstrap.yml
spring: # Spring配置项
cloud: # SpringCloud配置项
nacos: # Nacos注册中心的配置
config: # gRPC通讯配置
server-addr: nacos-server:8848 # Nacos地址 代理nacos-proxy:8848
namespace: 96c23d77-8d08-4648-b750-1217845607ee # 命名空间ID
group: MICROCLOUD_GROUP # 一般建议大写
cluster-name: MuyanCluster # 配置集群名称
application.yml
server: # 服务端配置
port: 8001 # 8001端口
mybatis-plus: # MyBatisPlus配置
type-aliases-package: com.yootk.provider.vo # 别名配置
spring:
application: # 配置应用信息
name: dept.provider # 是微服务的名称
cloud: # Cloud配置
nacos: # Nacos注册中心配置
discovery: # 发现服务
service: ${spring.application.name} # 使用微服务的名称作为注册的服务名称
server-addr: nacos-server:8848 # Nacos服务地址 代理nacos-proxy:8848
namespace: 96c23d77-8d08-4648-b750-1217845607ee # 命名空间ID
group: MICROCLOUD_GROUP # 一般建议大写
cluster-name: MuyanCluster # 配置集群名称
metadata: # 根据自身的需要配置元数据
version: 1.0 # 自定义元数据项
company: 沐言科技 # 自定义元数据项
url: www.yootk.com # 自定义元数据项
author: 李兴华(爆可爱的小李老师) # 自定义元数据项
datasource: # 数据源配置
type: com.alibaba.druid.pool.DruidDataSource # 数据源类型
driver-class-name: com.mysql.cj.jdbc.Driver # 驱动程序类
url: jdbc:mysql://localhost:3306/yootk8001 # 连接地址
username: root # 用户名
password: mysqladmin # 连接密码
druid: # druid相关配置
initial-size: 5 # 初始化连接池大小
min-idle: 10 # 最小维持连接池大小
max-active: 50 # 最大支持连接池大小
max-wait: 60000 # 最大等待时间
time-between-eviction-runs-millis: 60000 # 关闭空闲连接间隔
min-evictable-idle-time-millis: 30000 # 连接最小生存时间
validation-query: SELECT 1 FROM dual # 状态检测
test-while-idle: true # 空闲时检测连接是否有效
test-on-borrow: false # 申请时检测连接是否有效
test-on-return: false # 归还时检测连接是否有效
pool-prepared-statements: false # PSCache缓存
max-pool-prepared-statement-per-connection-size: 20 # 配置PS缓存
filters: stat, wall, slf4j # 开启过滤
stat-view-servlet: # 监控界面配置
enabled: true # 启用druid监控界面
allow: 127.0.0.1 # 访问白名单
login-username: muyan # 用户名
login-password: yootk # 密码
reset-enable: true # 允许重置
url-pattern: /druid/* # 访问路径
web-stat-filter:
enabled: true # 启动URI监控
url-pattern: /* # 跟踪全部服务
exclusions: "*.js,*.gif,*.jpg,*.bmp,*.png,*.css,*.ico,/druid/*" # 跟踪排除
filter:
slf4j: # 日志
enabled: true # 启用SLF4j监控
data-source-log-enabled: true # 启用数据库日志
statement-executable-sql-log-enable: true # 执行日志
result-set-log-enabled: true # ResultSet日志启用
stat: # SQL监控
merge-sql: true # 合并统计
log-slow-sql: true # 慢执行记录
slow-sql-millis: 1 # 慢SQL执行时间
wall: # SQL防火墙
enabled: true # SQL防火墙
config: # 防火墙规则
multi-statement-allow: true # 允许执行批量SQL
delete-allow: false # 禁止执行删除语句
aop-patterns: "com.yootk.provider.action.*,com.yootk.provider.service.*,com.yootk.provider.dao.*" # Spring监控
logback-spring.xml
<?xml version="1.0" encoding="UTF-8"?>
<configuration scan="true" scanPeriod="60 seconds" debug="false">
<contextName>logback</contextName>
<!-- 定义控制台输出匹配格式 -->
<substitutionProperty name="logging.pattern.console"
value="%clr(%d{${LOG_DATEFORMAT_PATTERN:-yyyy-MM-dd HH:mm:ss.SSS}}){faint} %clr(${LOG_LEVEL_PATTERN:-%5p}) %clr([%X{requestId}]) %clr(${PID:- }){magenta} %clr(---){faint} %clr([%15.15t]){faint} %clr(%-40.40logger{39}){cyan} %clr(:){faint} %m%n${LOG_EXCEPTION_CONVERSION_WORD:-%ewtpc}"/>
<!-- 定义日志文件输出匹配格式 -->
<substitutionProperty name="logging.pattern.file"
value="%d{${LOG_DATEFORMAT_PATTERN:-yyyy-MM-dd HH:mm:ss.SSS}} ${LOG_LEVEL_PATTERN:-%5p} %clr([%X{requestId}]) ${PID:- } --- [%t] %-40.40logger{39} : %m%n${LOG_EXCEPTION_CONVERSION_WORD:-%ewtpc}"/>
<conversionRule conversionWord="clr" converterClass="org.springframework.boot.logging.logback.ColorConverter"/>
<conversionRule conversionWord="wtpc" converterClass="org.springframework.boot.logging.logback.WhitespaceThrowableProxyConverter"/>
<conversionRule conversionWord="ewtpc" converterClass="org.springframework.boot.logging.logback.ExtendedWhitespaceThrowableProxyConverter"/>
<appender name="console" class="ch.qos.logback.core.ConsoleAppender"> <!-- 控制台输出 -->
<layout class="ch.qos.logback.classic.PatternLayout"> <!-- 使用layout节点 -->
<pattern>${logging.pattern.console}</pattern> <!-- 格式引用 -->
</layout>
</appender>
<!-- 将每天的日志保存在一个文件之中 -->
<appender name="file" class="ch.qos.logback.core.rolling.RollingFileAppender">
<Prudent>true</Prudent>
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<!-- 设置日志保存路径,本次按照月份创建日志目录,而后每天的文件归档到一组 -->
<FileNamePattern>muyan-logs/%d{yyyy-MM}/yootk_%d{yyyy-MM-dd}.log</FileNamePattern>
<MaxHistory>365</MaxHistory><!-- 删除超过365天的日志文件 -->
</rollingPolicy>
<filter class="ch.qos.logback.classic.filter.ThresholdFilter">
<level>INFO</level> <!-- 保存ERROR及以上级别的日志 -->
</filter>
<encoder>
<Pattern>${logging.pattern.file}</Pattern> <!-- 格式引用 -->
</encoder>
</appender>
<appender name="druidSqlFile" class="ch.qos.logback.core.rolling.RollingFileAppender">
<Prudent>true</Prudent>
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<!-- 设置日志保存路径,本次按照月份创建日志目录,而后每天的文件归档到一组 -->
<FileNamePattern>druid-logs/%d{yyyy-MM}/yootk_druid_slow_sql_%d{yyyy-MM-dd}.log</FileNamePattern>
<MaxHistory>365</MaxHistory><!-- 删除超过365天的日志文件 -->
</rollingPolicy>
<filter class="ch.qos.logback.classic.filter.ThresholdFilter">
<level>ERROR</level> <!-- 保存ERROR及以上级别的日志 -->
</filter>
<encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
<pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} %-5level %logger Line:%-3L - %msg%n</pattern>
<charset>utf-8</charset>
</encoder>
</appender>
<logger name="com.alibaba.druid.filter.stat.StatFilter" level="ERROR">
<appender-ref ref="druidSqlFile"/>
</logger>
<logger name="com.yootk.provider.dao" level="DEBUG"/> <!-- 局部日志级别 -->
<root level="INFO"> <!-- 全局日志级别 -->
<appender-ref ref="console"/>
<appender-ref ref="file"/>
</root>
</configuration>
Swagger配置
package com.yootk.provider.config;
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.builders.RequestHandlerSelectors;
import springfox.documentation.service.ApiInfo;
import springfox.documentation.service.Contact;
import springfox.documentation.spi.DocumentationType;
import springfox.documentation.spring.web.plugins.Docket;
@Configuration
public class SwaggerConfig { // Swagger配置类
private ApiInfo getApiInfo() { // 文档之中的头部的信息项
return new ApiInfoBuilder().title("【沐言科技】部门微服务")
.description("实现部门数据的统一管理,包括:增加部门信息、查询部门信息、部门列表显示等,此处省略5000字...")
.termsOfServiceUrl("https://www.yootk.com")
.contact(new Contact("爆可爱的小李老师", "edu.yootk.com", "784420216@qq.com"))
.license("沐言科技 - 授权管理").version("1.0.0").build();
}
@Bean
public Docket getDocker() { // 所有的详细描述在此类中定义
return new Docket(DocumentationType.SWAGGER_2) // 使用的文档版本类型
.apiInfo(this.getApiInfo())
.select() // 所有的接口一定要放在指定的包中
.apis(RequestHandlerSelectors.basePackage("com.yootk.provider.action"))
.paths(PathSelectors.any()).build();
}
}
swagger安全配置
虽然当前已经成功的实现了所需要的REST接口描述文档显示,但是这个文档显示本身会存在有一些设计上的缺陷,他没有提供安全保护,只要是个人,可以访问到你的服务器,那么就都可以进行服务调用。.
如果要想解决这种文档的安全访问的问题,唯一的做法就是使用SpringSecurity 来实现开发,因为这个框架是与SpringBoot深入绑定在一起的,那么使用它进行开发是最佳的做法。
父项目的build.gradle配置
project(":provider-dept-8001") { // 部门微服务
dependencies {
implementation(project(":common-api")) // 导入公共的子模块
implementation(libraries.'mybatis-plus-boot-starter')
implementation(libraries.'mysql-connector-java')
implementation(libraries.'druid')
implementation(libraries.'springfox-boot-starter')
implementation('org.springframework.boot:spring-boot-starter-security')
// 以下的依赖库为Nacos注册中心所需要的依赖配置
implementation('com.alibaba.cloud:spring-cloud-starter-alibaba-nacos-discovery') {
exclude group: 'com.alibaba.nacos', module: 'nacos-client' // 移除旧版本的Nacos依赖
}
implementation('com.alibaba.cloud:spring-cloud-starter-alibaba-nacos-config') {
exclude group: 'com.alibaba.nacos', module: 'nacos-client' // 移除旧版本的Nacos依赖
}
implementation(libraries.'nacos-client') // 引入与当前的Nacos匹配的依赖库
}
}
启用安全配置之前需要进行密码的加密处理,本次使用的密码明文“yootk"
package com.yootk.test;
import org.springframework.security.crypto.factory.PasswordEncoderFactories;
public class TestCreatePassword {
public static void main(String[] args) {
String pwd = PasswordEncoderFactories.createDelegatingPasswordEncoder().encode("yootk");
System.out.println(pwd);
}
}
SwaggerWebSecurityConfig
如果要想进行安全的启用,肯定要配置访问路径以及账户信息。
package com.yootk.provider.config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.crypto.factory.PasswordEncoderFactories;
import org.springframework.security.crypto.password.PasswordEncoder;
@Configuration
public class SwaggerWebSecurityConfig extends WebSecurityConfigurerAdapter { // Swagger安全配置
// 如果有其他的需要,你可以继续进行数据库的连接配置,具体的讲解已经提供过了
private static final String DEFAULT_PASSWORD =
"{bcrypt}$2a$10$bvOY6ixvY5DmgiNW.Z79qeV9abQM9a6NbM1n9sejeUnB98C0kKAMu";
@Bean
public PasswordEncoder getPasswordEncoder() {
return PasswordEncoderFactories.createDelegatingPasswordEncoder();
}
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.inMemoryAuthentication()
.withUser("swagger") // 默认用户名
.password(DEFAULT_PASSWORD) // 默认密码
.roles("USER", "ADMIN");
}
@Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests()
.antMatchers("/swagger-ui/**", "/v2/api-docs").hasRole("ADMIN")
.and().httpBasic().and().formLogin()
.permitAll().and().csrf().disable();
}
}
Swagger所提供的地址
http://provider-dept-8001:8001/swagger-ui
创建MyBatisPlus配置类
package com.yootk.provider.config;
import com.baomidou.mybatisplus.annotation.DbType;
import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor;
import com.baomidou.mybatisplus.extension.plugins.inner.PaginationInnerInterceptor;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class MyBatisPlusConfig { // MybatisPlus配置类
@Bean
public MybatisPlusInterceptor getMybatisPlusInterceptor() {
MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor(); // 拦截器
interceptor.addInnerInterceptor(
new PaginationInnerInterceptor(DbType.MYSQL)); // 分页处理
return interceptor;
}
}
部门 数据库创建脚本
DROP DATABASE IF EXISTS yootk8001;
CREATE DATABASE yootk8001 CHARACTER SET UTF8;
USE yootk8001;
CREATE TABLE dept (
deptno BIGINT AUTO_INCREMENT,
dname VARCHAR(50),
loc VARCHAR(50),
CONSTRAINT pk_deptno PRIMARY KEY(deptno)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
INSERT INTO dept(dname,loc) VALUES ('开发部', database());
INSERT INTO dept(dname,loc) VALUES ('财务部', database());
INSERT INTO dept(dname,loc) VALUES ('市场部', database());
INSERT INTO dept(dname,loc) VALUES ('后勤部', database());
INSERT INTO dept(dname,loc) VALUES ('公关部', database());
COMMIT;
创建Dept映射类
package com.yootk.provider.vo;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.Data;
@TableName("dept")
@Data // Lombok代码生成
public class Dept { // 这个类所需要追加MBP所需要的注解
@TableId(type = IdType.AUTO) // 采用自动增长列配置
private Long deptno; // 与deptno字段映射
private String dname;
private String loc;
}
创建IDeptDAO数据接口
package com.yootk.provider.dao;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.yootk.provider.vo.Dept;
import org.apache.ibatis.annotations.Mapper;
@Mapper
public interface IDeptDAO extends BaseMapper<Dept> { // DAO接口开发完成
}
在生产端需要提供有业务接口的实现子类
package com.yootk.provider.service.impl;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.yootk.common.dto.DeptDTO;
import com.yootk.common.util.DeepBeanUtils;
import com.yootk.provider.dao.IDeptDAO;
import com.yootk.provider.vo.Dept;
import com.yootk.service.IDeptService;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
@Service
public class DeptServiceImpl implements IDeptService {
@Autowired
private IDeptDAO deptDAO;
@Override
public DeptDTO get(long id) {
DeptDTO dto = new DeptDTO(); // 实例化传输对象
// 在本地端通过了VO类实现了数据的加载,随后将此数据拷贝到DTO对象之中
BeanUtils.copyProperties(this.deptDAO.selectById(id), dto); // 属性拷贝
return dto;
}
@Override
public boolean add(DeptDTO dto) {
Dept dept = new Dept(); // 数据层最终需要的是一个VO类型
BeanUtils.copyProperties(dto, dept);
return this.deptDAO.insert(dept) > 0; // 更新行数大于0
}
@Override
public List<DeptDTO> list() {
QueryWrapper<Dept> wrapper = new QueryWrapper<>();
List<DeptDTO> allDepts = DeepBeanUtils.copyListProperties(
this.deptDAO.selectList(wrapper), DeptDTO::new); // 集合数据拷贝
return allDepts;
}
@Override
public Map<String, Object> split(int currentPage, int lineSize, String column, String keyword) {
QueryWrapper<Dept> wrapper = new QueryWrapper<>();
wrapper.like(column, keyword); // 设置模糊查询操作
int count = this.deptDAO.selectCount(wrapper); // 统计个数
// 实现数据的查询处理
IPage<Dept> page = this.deptDAO.selectPage(new Page<>(currentPage, lineSize, count), wrapper);
Map<String, Object> map = new HashMap<>(); // 包装返回结果
map.put("allDepts", DeepBeanUtils.copyListProperties(page.getRecords(), DeptDTO::new));
map.put("allRecorders", page.getTotal());
map.put("allPages", page.getPages());
return map;
}
}
启动类
package com.yootk.provider;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class StartProviderDept8001Application {
public static void main(String[] args) {
SpringApplication.run(StartProviderDept8001Application.class, args);
}
}
测试
package com.yootk.test;
import com.yootk.common.dto.DeptDTO;
import com.yootk.provider.StartProviderDept8001Application;
import com.yootk.service.IDeptService;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit.jupiter.SpringExtension;
import org.springframework.test.context.web.WebAppConfiguration;
import java.util.Map;
@ExtendWith(SpringExtension.class)
@WebAppConfiguration
@SpringBootTest(classes = StartProviderDept8001Application.class)
public class TestDeptService {
@Autowired
private IDeptService deptService; // 注入业务接口对象
@Test
public void testGet() {
System.out.println(this.deptService.get(1));
}
@Test
public void testList() {
System.out.println(this.deptService.list());
}
@Test
public void testAdd() {
DeptDTO dto = new DeptDTO();
dto.setDname("公益部");
dto.setLoc("洛阳");
System.out.println(this.deptService.add(dto));
}
@Test
public void testSplit() {
Map<String, Object> map = this.deptService.split(1, 2, "dname", "");
System.out.println(map);
}
}
controller
package com.yootk.provider.action;
import com.yootk.common.dto.DeptDTO;
import com.yootk.service.IDeptService;
import io.swagger.annotations.ApiImplicitParam;
import io.swagger.annotations.ApiImplicitParams;
import io.swagger.annotations.ApiOperation;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import javax.servlet.http.HttpServletRequest;
import java.util.Enumeration;
@RestController
@RequestMapping("/provider/dept/*") // 微服务提供者父路径
@Slf4j // 使用一个注解
public class DeptAction {
@Autowired
private IDeptService deptService;
@ApiOperation(value="部门查询", notes = "根据部门编号查询部门详细信息")
@GetMapping("get/{id}")
public Object get(@PathVariable("id") long id) {
return this.deptService.get(id);
}
@ApiOperation(value="部门增加", notes = "增加新的部门信息")
@ApiImplicitParams({
@ApiImplicitParam(name = "deptDTO", required = true,
dataType = "DeptDTO", value = "部门传输对象实例")
})
@PostMapping("add")
public Object add(@RequestBody DeptDTO deptDTO) { // 后面会修改参数模式为JSON
return this.deptService.add(deptDTO);
}
@ApiOperation(value="部门列表", notes = "查询部门的完整信息")
@GetMapping("list")
public Object list() {
return this.deptService.list();
}
@ApiOperation(value="部门分页查询", notes = "根据指定的数据库参数实现部门数据的分页加载")
@ApiImplicitParams({
@ApiImplicitParam(name="cp", value = "当前所在页", required = true, dataType = "int"),
@ApiImplicitParam(name="ls", value = "每页显示的数据行数", required = true, dataType = "int"),
@ApiImplicitParam(name="col", value = "模糊查询列", required = true, dataType = "String"),
@ApiImplicitParam(name="kw", value = "模糊查询关键字", required = true, dataType = "String")
})
@GetMapping("split")
public Object split(int cp, int ls, String col, String kw) {
return this.deptService.split(cp, ls, col, kw);
}
}
common-api子模块修改
由于该模块需要被其他子模块所使用,那么这个时候就需要对代码进行编译.
build.gradle:
jar { enabled = true} // 允许打包为jar文件
bootJar { enabled = false } // 不允许打包为Boot执行文件
javadocTask { enabled = false } // 不需要打包为doc文件
Gradle编译:
gradle build
【provider-dept-8001子模块】启动当前的部门微服务测试
consumer-springboot-80 子模块
application.yml
server: # 服务端配置
port: 80 # 这个接口可以随意,反正最终都是由前端提供服务
spring:
application: # 配置应用信息
name: consumer # 是微服务的名称
cloud: # Cloud配置
nacos: # Nacos注册中心配置
discovery: # 发现服务
server-addr: nacos-server:8848 # Nacos服务地址 代理nacos-proxy:8848
namespace: 96c23d77-8d08-4648-b750-1217845607ee # 命名空间ID
group: MICROCLOUD_GROUP # 一般建议大写
cluster-name: MuyanCluster # 配置集群名称
register-enabled: false # 消费端不注册
controller
package com.yootk.consumer.action;
import com.yootk.common.dto.DeptDTO;
import com.yootk.service.IDeptService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
@RequestMapping("/consumer/dept/*") // 两个不同的服务路径
public class DeptConsumerAction { // 消费端Action
@Autowired // 由容器帮助用户自动实例化接口对象
private IDeptService deptService;
@GetMapping("add") // 消费端接口名称
public Object addDept(DeptDTO dto) {
return this.deptService.add(dto);
}
@GetMapping("get")
public Object get(Long deptno) {
return this.deptService.get(deptno);
}
@GetMapping("list")
public Object list() {
return this.deptService.list();
}
@GetMapping("split")
public Object split(int cp, int ls, String col, String kw) {
return this.deptService.split(cp, ls, col, kw);
}
}
启动类
package com.yootk.consumer;
import muyan.yootk.config.ribbon.DeptProviderRibbonConfig;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
import org.springframework.cloud.netflix.ribbon.RibbonClient;
import org.springframework.cloud.openfeign.EnableFeignClients;
@SpringBootApplication
@EnableDiscoveryClient
@EnableFeignClients("com.yootk.service") // Feign扫描包
public class StartConsumerApplication {
public static void main(String[] args) {
SpringApplication.run(StartConsumerApplication.class, args);
}
}