Files
blog-press/docs/Web-Backend/SpringBoot/SpringBoot-Common.md
2025-12-16 18:07:42 +08:00

874 lines
29 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

---
title: SpringBoot Common
date: 2025-12-15
---
# 一、框架说明
  本框架基于Spring Boot3框架二次开发增加了依赖包管理和启动项配置等功能。
# 二、项目结构
| 项目模块 | 模块含义 | 主要功能 |
| :-----------: | :-------------------: | :---------------------------------: |
| starters | SpringBoot 启动项 | 主要包括web、jdbc和log启动项配置 |
| autoconfigure | staters具体启动配置类 | 主要包括web和jdbc具体的启动项配置类 |
| dependencies | 依赖项 | 主要包括本框架中的依赖包管理 |
| framework | 通用配置 | 主要包括web和data的一些通用工具方法 |
# 三、项目说明
## 3.1 common模块
  该模块主要声明项目结构包括autoconfigure、dependencies、framework、starter-parent和starters等模块。
```xml
<modules>
<!-- modules表示聚合关系即common有以下模块 -->
<module>dependencies</module>
<module>starters</module>
<module>framework</module>
<module>autoconfigure</module>
<module>starter-parent</module>
</modules>
```
## 3.2 dependencies模块
&emsp;&emsp;该模块为其他模块的父模块,声明了一些常用依赖包及版本,通过`<dependencyManagement>`管理,只声明依赖的版本,并不会实际引入依赖。
&emsp;&emsp;后续有新增依赖时需要先在dependencies模块声明版本然后在相应的starter模块中增加依赖。
```xml
<properties>
<java.version>17</java.version>
<maven.compiler.source>${java.version}</maven.compiler.source>
<maven.compiler.target>${java.version}</maven.compiler.target>
<spring.boot.version>3.5.0</spring.boot.version>
<springdoc.version>2.7.0</springdoc.version>
<logback-more-appenders.version>1.8.8</logback-more-appenders.version>
<fluency-fluentd.version>2.7.0</fluency-fluentd.version>
<mybatis-spring.version>3.0.4</mybatis-spring.version>
<mybatis.plus.version>3.5.12</mybatis.plus.version>
<sa.token.version>1.43.0</sa.token.version>
<hutool.version>5.8.26</hutool.version>
<commons.io.version>2.19.0</commons.io.version>
<commons.collections.version>4.4</commons.collections.version>
<commons-lang3.version>3.14.0</commons-lang3.version>
<commons.net.version>3.9.0</commons.net.version>
<guava.version>31.1-jre</guava.version>
<mapstruct.version>1.5.5.Final</mapstruct.version>
<yitter.idgenerator.version>1.0.6</yitter.idgenerator.version>
<lombok.version>1.18.30</lombok.version>
<lombok.mapstruct.version>0.2.0</lombok.mapstruct.version>
<maven-compiler-plugin.version>3.11.0</maven-compiler-plugin.version>
<docker-maven-plugin.version>0.41.0</docker-maven-plugin.version>
<native-maven-plugin.version>0.10.2</native-maven-plugin.version>
</properties>
<!-- dependencyManagement部分只声明依赖的版本并不会实际引入依赖。
需要在具体的模块中显式声明依赖,才能让模块使用这些库 -->
<dependencyManagement>
<dependencies>
<!-- SpringBoot依赖 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-dependencies</artifactId>
<version>${spring.boot.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<!-- SpringDoc OpenAPI + Swagger UI -->
<dependency>
<groupId>org.springdoc</groupId>
<artifactId>springdoc-openapi-starter-webmvc-ui</artifactId>
<version>${springdoc.version}</version>
</dependency>
<!-- 扩展 logback appender -->
<dependency>
<groupId>com.sndyuk</groupId>
<artifactId>logback-more-appenders</artifactId>
<version>${logback-more-appenders.version}</version>
</dependency>
<!-- Fluentd 日志搜集和转发 -->
<dependency>
<groupId>org.komamitsu</groupId>
<artifactId>fluency-fluentd</artifactId>
<version>${fluency-fluentd.version}</version>
</dependency>
<!-- SaToken BOM -->
<dependency>
<groupId>cn.dev33</groupId>
<artifactId>sa-token-bom</artifactId>
<version>${sa.token.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<!-- hutool BOM -->
<dependency>
<groupId>cn.hutool</groupId>
<artifactId>hutool-bom</artifactId>
<version>${hutool.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<!-- MyBatis -->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis-spring</artifactId>
<version>${mybatis-spring.version}</version>
</dependency>
<!-- MyBatis-Plus Maven BOM -->
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-bom</artifactId>
<version>${mybatis.plus.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<!-- IO工具类 -->
<dependency>
<groupId>commons-io</groupId>
<artifactId>commons-io</artifactId>
<version>${commons.io.version}</version>
</dependency>
<!-- 集合工具类 -->
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-collections4</artifactId>
<version>${commons.collections.version}</version>
</dependency>
<!-- 字符串工具类 -->
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
<version>${commons-lang3.version}</version>
</dependency>
<!-- 网络工具类 -->
<dependency>
<groupId>commons-net</groupId>
<artifactId>commons-net</artifactId>
<version>${commons.net.version}</version>
</dependency>
<!-- 工具类集合、缓存、并发、IO、字符串 -->
<dependency>
<groupId>com.google.guava</groupId>
<artifactId>guava-bom</artifactId>
<version>${guava.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<!-- 实体映射工具类 -->
<dependency>
<groupId>org.mapstruct</groupId>
<artifactId>mapstruct</artifactId>
<version>${mapstruct.version}</version>
</dependency>
<!-- 雪花ID生成器 -->
<dependency>
<groupId>com.github.yitter</groupId>
<artifactId>yitter-idgenerator</artifactId>
<version>${yitter.idgenerator.version}</version>
</dependency>
<!-- starter中的依赖 -->
<dependency>
<groupId>com.cxx</groupId>
<artifactId>starter-logging</artifactId>
<version>2.0.0</version>
</dependency>
<dependency>
<groupId>com.cxx</groupId>
<artifactId>starter-web</artifactId>
<version>2.0.0</version>
</dependency>
<dependency>
<groupId>com.cxx</groupId>
<artifactId>starter-jdbc</artifactId>
<version>2.0.0</version>
</dependency>
</dependencies>
</dependencyManagement>
<build>
<pluginManagement>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<version>${spring.boot.version}</version>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>${maven-compiler-plugin.version}</version>
</plugin>
<plugin>
<groupId>io.fabric8</groupId>
<artifactId>docker-maven-plugin</artifactId>
<version>${docker-maven-plugin.version}</version>
</plugin>
<plugin>
<groupId>org.graalvm.buildtools</groupId>
<artifactId>native-maven-plugin</artifactId>
<version>${native-maven-plugin.version}</version>
</plugin>
</plugins>
</pluginManagement>
</build>
```
::: tip
dependencyManagement部分只声明依赖的版本并不会实际引入依赖。
需要在具体的模块中显式声明依赖,才能让模块使用这些库
:::
## 3.3 framework模块
&emsp;&emsp;该模块主要是一些通用的配置。
### 3.3.1 data
&emsp;&emsp;主要提供了2个数据库实体类的基类。
::: code-group
```java [AbstractIdEntity]
@Getter
@Setter
public abstract class AbstractIdEntity {
@TableId(value = "id", type = IdType.ASSIGN_ID)
private Long id;
}
```
```java [AbstractEntity]
@Getter
@Setter
public abstract class AbstractEntity extends AbstractIdEntity {
@TableField(value = "create_time", fill = FieldFill.INSERT)
private LocalDateTime createTime;
@TableField(value = "create_by", fill = FieldFill.INSERT)
private String createBy;
@TableField(value = "update_time", fill = FieldFill.INSERT_UPDATE)
private LocalDateTime updateTime;
@TableField(value = "update_by", fill = FieldFill.INSERT_UPDATE)
private String updateBy;
}
```
:::
### 3.3.2 web
&emsp;&emsp;主要提供了自定义异常处理类。
::: code-group
```java [ErrorResponse]
/**
* 错误响应
*/
public final class ErrorResponse {
/**
* 自定义code
*/
private final String code;
/**
* 自定义消息
*/
private final String message;
public ErrorResponse(String message) {
this.code = "Unspecified";
this.message = message;
}
public ErrorResponse(String code, String message) {
this.code = code;
this.message = message;
}
public String getCode() {
return code;
}
public String getMessage() {
return message;
}
}
```
```java [CustomException]
/**
* 自定义异常类
*/
public class CustomException extends AbstractException {
public CustomException(String errorMessage) {
super(errorMessage);
}
public CustomException(String code, String errorMessage) {
super(code, errorMessage);
}
public CustomException(String errorMessage, Exception innerException) {
super(errorMessage, innerException);
}
public CustomException(String code, String errorMessage, Exception innerException) {
super(code, errorMessage, innerException);
}
}
```
```java [AbstractException]
/**
* 抽象异常类
*/
public abstract class AbstractException extends RuntimeException {
protected String code;
public AbstractException(String errorMessage) {
super(errorMessage);
}
public AbstractException(String code, String errorMessage) {
super(errorMessage);
this.code = code;
}
public AbstractException(String errorMessage, Exception innerException) {
super(errorMessage, innerException);
}
public AbstractException(String code, String errorMessage, Exception innerException) {
super(errorMessage, innerException);
this.code = code;
}
public String getCode() {
return code;
}
public void setCode(String code) {
this.code = code;
}
}
```
:::
### 3.4 starters模块
&emsp;&emsp;该模块为整个项目的核心模块在实际项目中通过引入相应的starter模块并配合autoconfigure模块中的功能即可实现快速自动装配。
&emsp;&emsp;该模块的pom.xml声明的依赖会注入到实际的项目中。
&emsp;&emsp;在springboot3中可以在**main->resources->META-INF->spring->org.springframework.boot.autoconfigure.AutoConfiguration.imports**中声明需要自动执行的类。
#### 3.4.1 stater-jdbc
&emsp;&emsp;该模块主要提供数据库相关的配置功能。
&emsp;&emsp;目前实现的功能有:
1. 配置MybatisPlus拦截器添加乐观锁和分页插件。
2. 自定义MybatisPlus ID生成器雪花ID
3. 配置MybatisPlus自动填充字段create_time、create_by、update_time和update_by
::: code-group
```java [JdbcAutoConfiguration]
/**
* jdbc自动配置
*/
@Configuration(proxyBeanMethods = false)
@ConditionalOnClass(SqlSessionFactory.class)
@Import({YitterGenerator.class, MybatisMetaObjectHandler.class})
public class JdbcAutoConfiguration {
/**
* MybatisPlus拦截器
* @return 拦截器
*/
@Bean
public MybatisPlusInterceptor mybatisPlusInterceptor() {
MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
// 乐观锁插件
interceptor.addInnerInterceptor(new OptimisticLockerInnerInterceptor());
// 分页插件
interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL));
return interceptor;
}
}
```
```java [YitterGenerator]
/**
* 雪花id生成器
*/
public class YitterGenerator implements IdentifierGenerator {
@Override
public Number nextId(Object entity) {
return YitIdHelper.nextId();
}
}
```
```java [MybatisMetaObjectHandler]
/**
* Mybatis Plus自动填充
*/
public class MybatisMetaObjectHandler implements MetaObjectHandler {
@Override
public void insertFill(MetaObject metaObject) {
this.strictInsertFill(metaObject, Constants.CREATE_TIME_FLAG, LocalDateTime.class, LocalDateTime.now());
this.strictInsertFill(metaObject, Constants.UPDATE_TIME_FLAG, LocalDateTime.class, LocalDateTime.now());
if (StpUtil.isLogin()) {
this.strictInsertFill(metaObject, Constants.CREATE_BY_FLAG, String.class, StpUtil.getLoginIdAsString());
this.strictInsertFill(metaObject, Constants.UPDATE_BY_FLAG, String.class, StpUtil.getLoginIdAsString());
}
}
@Override
public void updateFill(MetaObject metaObject) {
this.strictUpdateFill(metaObject, Constants.UPDATE_TIME_FLAG, LocalDateTime.class, LocalDateTime.now());
if (StpUtil.isLogin()) {
this.strictUpdateFill(metaObject, Constants.UPDATE_BY_FLAG, String.class, StpUtil.getLoginIdAsString());
}
}
}
```
:::
&emsp;&emsp;`@ConditionalOnClass(SqlSessionFactory.class)`表示只有存在SqlSessionFactory类时这个配置类才会生效。即如果没有 MyBatis 相关依赖,这个配置类会被 Spring 完全忽略。
&emsp;&emsp;`@Import`用于导入其他配置类或组件到当前配置类中。
#### 3.4.2 starter-web
&emsp;&emsp;该模块主要提供web相关的配置功能。
&emsp;&emsp;目前实现的功能有:
1. 注册审计拦截器
2. 注册Sa-Token拦截器
3. 注册安全拦截器
4. 配置CORS跨越
5. 全局异常处理器
::: code-group
```java [ServerAutoConfiguration]
@Configuration()
@Import({DefaultExceptionAdvice.class, AuditBodyAdvice.class, CustomProperties.class})
public class ServerAutoConfiguration implements WebMvcConfigurer {
@Resource
private CustomProperties customProperties;
/**
* 注册拦截器 需要实现 WebMvcConfigurer 接口
* @param registry 注册器
*/
@Override
public void addInterceptors(InterceptorRegistry registry) {
// 注册安全拦截器
// registry.addInterceptor(new SecurityInterceptor());
// 注册审计拦截器
registry.addInterceptor(new AuditInterceptor(customProperties.getBasePackage()));
// 注册Sa-Token拦截器 登录校验
registry.addInterceptor(new SaInterceptor(handle -> StpUtil.checkLogin()))
.excludePathPatterns("/error", "/swagger-ui/**", "/swagger-resources/**", "/v3/api-docs/**")
.excludePathPatterns("/files/**")
.excludePathPatterns("/doc.html", "/webjars/**");
}
/**
* 配置CORS跨越 需要实现 WebMvcConfigurer 接口
* @return 过滤器
*/
@Bean
@Order(-128)
public CorsFilter corsFilter() {
CorsConfiguration config = new CorsConfiguration();
// 允许所有来源
config.addAllowedOrigin("*");
// 允许所有请求头
config.addAllowedHeader("*");
// 允许所有请求方法
config.addAllowedMethod("*");
UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
source.registerCorsConfiguration("/**", config);
return new CorsFilter(source);
}
}
```
```java [AuditInterceptor]
public class AuditInterceptor implements HandlerInterceptor {
private static final Logger logger = LoggerFactory.getLogger(AuditInterceptor.class);
private final String basePackage;
public AuditInterceptor(String basePackage) {
this.basePackage = basePackage;
}
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
// 如果是Http请求
if (handler instanceof HandlerMethod hd) {
request.setAttribute(Constants.AUDIT_TIME_FLAG, System.currentTimeMillis());
// 判断该请求是否需要审计
if (WebUtils.checkIsAuditPackages(hd.getBeanType().getPackage(), basePackage)) {
handleAuditPackageRequest(request);
}
}
// true表示继续处理请求
return true;
}
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
// 这里我们不需要处理时间计算,所有的计算在 afterCompletion 中完成
}
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
// 获取开始时间
Long startTime = (Long) request.getAttribute(Constants.AUDIT_TIME_FLAG);
if (startTime != null) {
long duration = System.currentTimeMillis() - startTime;
logger.info("<AuditSummary> Request URL {} | Time Taken {} ms", request.getRequestURI(), duration);
}
}
/**
* 处理审计包请求
* @param request 请求
*/
private void handleAuditPackageRequest(HttpServletRequest request) {
if (StringUtils.isEmpty(request.getQueryString())) {
logger.info("<AuditSummary> {} {}", request.getMethod(), request.getRequestURI());
} else {
logger.info("<AuditSummary> {} {}?{}", request.getMethod(), request.getRequestURI(), WebUtils.format2UTF8(request.getQueryString()));
}
}
}
```
```java [DefaultExceptionAdvice]
/**
* 全局异常处理器
*/
@RestControllerAdvice
public class DefaultExceptionAdvice {
private static final Logger logger = LoggerFactory.getLogger(DefaultExceptionAdvice.class);
/**
* 处理SaToken权限错误
* @param e 权限异常
* @return 错误响应
*/
@ExceptionHandler(SaTokenException.class)
@ResponseStatus(HttpStatus.UNAUTHORIZED)
public ErrorResponse handleSaTokenException(SaTokenException e) {
ErrorResponse response = new ErrorResponse(String.valueOf(e.getCode()), e.getMessage());
logger.error("<{}> {}", response.getCode(), e.toString());
return response;
}
/**
* 处理参数校验错误
* @param e 校验异常
* @return 错误响应
*/
@ExceptionHandler(MethodArgumentNotValidException.class)
public ErrorResponse handleValidationException(MethodArgumentNotValidException e) {
ErrorResponse response = new ErrorResponse(WebUtils.formatValidationException(e.getBindingResult().getFieldErrors()));
logger.error("<{}> {}", response.getCode(), response.getMessage());
return response;
}
/**
* 处理自定义异常错误
* @param e 自定义异常
* @return 错误响应
*/
@ExceptionHandler(AbstractException.class)
@ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
public ErrorResponse handleAbstractException(AbstractException e) {
ErrorResponse response;
if (e.getCode() == null || e.getCode().isEmpty()) {
response = new ErrorResponse(e.getMessage());
} else {
response = new ErrorResponse(e.getCode(), e.getMessage());
}
logger.error(String.format("<%s> ", response.getCode()), e);
return response;
}
/**
* 处理默认异常错误
* @param e 默认异常
* @return 错误响应
*/
@ExceptionHandler
@ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
public ErrorResponse handleDefaultException(Exception e) {
ErrorResponse response = new ErrorResponse(e.getMessage());
logger.error(String.format("<%s> ", response.getCode()), e);
return response;
}
}
```
```java [AuditBodyAdvice]
@ControllerAdvice
@Import({CustomProperties.class})
public class AuditBodyAdvice implements RequestBodyAdvice, ResponseBodyAdvice<Object> {
@Resource
private CustomProperties customProperties;
private final ObjectMapper objectMapper = new ObjectMapper();
private static final Logger logger = LoggerFactory.getLogger(AuditBodyAdvice.class);
@Override
public boolean supports(MethodParameter methodParameter, Type targetType, Class<? extends HttpMessageConverter<?>> converterType) {
// 判断是否为需要审计的包
String auditPackages = customProperties.getBasePackage();
return WebUtils.checkIsAuditPackages(methodParameter.getDeclaringClass().getPackage(), auditPackages)
&& AbstractJackson2HttpMessageConverter.class.isAssignableFrom(converterType);
}
@Override
public HttpInputMessage beforeBodyRead(HttpInputMessage inputMessage, MethodParameter parameter, Type targetType, Class<? extends HttpMessageConverter<?>> converterType) throws IOException {
return inputMessage;
}
@Override
public Object afterBodyRead(Object body, HttpInputMessage inputMessage, MethodParameter parameter, Type targetType, Class<? extends HttpMessageConverter<?>> converterType) {
// 入参结束
try {
String jsonBody = objectMapper.writeValueAsString(body);
logger.info("<AuditRequest> {}", jsonBody);
} catch (JsonProcessingException e) {
logger.info("<AuditResponse> {}", body);
}
return body;
}
@Override
public Object handleEmptyBody(Object body, HttpInputMessage inputMessage, MethodParameter parameter, Type targetType, Class<? extends HttpMessageConverter<?>> converterType) {
return body;
}
@Override
public boolean supports(MethodParameter returnType, Class<? extends HttpMessageConverter<?>> converterType) {
// 判断是否为需要审计的包
String auditPackages = customProperties.getBasePackage();
return WebUtils.checkIsAuditPackages(returnType.getDeclaringClass().getPackage(), auditPackages)
&& AbstractJackson2HttpMessageConverter.class.isAssignableFrom(converterType);
}
@Override
public Object beforeBodyWrite(Object body, MethodParameter returnType, MediaType selectedContentType, Class<? extends HttpMessageConverter<?>> selectedConverterType, ServerHttpRequest request, ServerHttpResponse response) {
// 出参结束
if (body != null && !(body instanceof ErrorResponse)) {
try {
String jsonBody = objectMapper.writeValueAsString(body);
logger.info("<AuditResponse> {}", jsonBody);
} catch (JsonProcessingException e) {
logger.info("<AuditResponse> {}", body);
}
}
return body;
}
}
```
```java [CustomProperties]
/**
* 用于将配置文件(如 application.properties 或 application.yml中的属性值绑定到 Java 对象
*/
@ConfigurationProperties(prefix = "web-starter")
public class CustomProperties {
// yml中web-starter下的base-package字段值
private String basePackage = "";
public String getBasePackage() {
return basePackage;
}
public void setBasePackage(String basePackage) {
this.basePackage = basePackage;
}
}
```
:::
&emsp;&emsp;`@ConfigurationProperties`用于将配置文件(如 application.properties 或 application.yml中的属性值绑定到 Java 对象
::: tip
SpringBoot MVC总体执行顺序
1. 进入Tomcat容器
2. 进入Filter过滤器
3. 进入Servlet容器
4. 进入Interceptor拦截器
5. 进入Controller控制器
6. 进入AOP
:::
::: tip
请求和响应体拦截器
RequestBodyAdvice, ResponseBodyAdvice主要发生在Controller执行前后
1. preHandle():请求处理前
2. beforeBodyRead请求体反序列化前
3. @RequestBodyController方法参数绑定
4. Controller请求处理
5. beforeBodyWrite():响应体序列化之前
6. postHandle():请求处理后
7. 视图渲染
8. afterCompletion():请求结束
:::
::: tip
审计拦截器 可以实现请求日志打印等功能
HandlerInterceptor 拦截器执行顺序:
1. preHandle():请求处理前 按注册顺序依次执行。
2. Controller请求处理 请求到达Controller并被处理。
3. postHandle():请求处理后,视图渲染前 按注册顺序逆序执行。
4. afterCompletion():视图渲染后 按注册顺序逆序执行。
:::
# 四、项目发布
## 4.1 发布到Git中
1. 在Gitee/Github中创建工程需要在工程中创建一个文件夹例如repo后续发布的文件要放在该文件夹下。
2. 在maven的setting.xml中添加server和repository信息
```xml
<servers>
<server>
<id>gitee</id>
<username>Cxx0822</username>
<password>token</password>
</server>
</servers>
<profiles>
<profile>
<id>gitee</id>
<repositories>
<repository>
<id>central</id>
<url>https://maven.aliyun.com/repository/central</url>
<releases>
<enabled>true</enabled>
</releases>
<snapshots>
<enabled>true</enabled>
</snapshots>
</repository>
<repository>
<id>gitee</id>
<url>https://gitee.com/Cxx0822/springboot2-common/raw/master/repo</url>
</repository>
</repositories>
<pluginRepositories>
<pluginRepository>
<id>central</id>
<url>https://maven.aliyun.com/repository/central</url>
<releases>
<enabled>true</enabled>
</releases>
<snapshots>
<enabled>true</enabled>
</snapshots>
</pluginRepository>
</pluginRepositories>
</profile>
</profiles>
<activeProfiles>
<activeProfile>gitee</activeProfile>
</activeProfiles>
```
&emsp;&emsp;注这里要去掉mirror的阿里云镜像。
3. 在工程的根目录的pom.xml中添加发布配置
```xml
<distributionManagement>
<repository>
<id>gitee</id>
<name>springboot2-common</name>
<url>file:D:/temp/maven</url>
</repository>
</distributionManagement>
```
&emsp;&emsp;注gitee不支持通过deploy发布jar包可以先发布到本地再将文件复制到项目文件夹下通过git push推送。
4. 将本地产生的发布文件上传至git仓库中。
5. 其他项目引用:
```xml
<dependency>
<groupId>com.cxx</groupId>
<artifactId>starter-web</artifactId>
<version>1.0.0</version>
</dependency>
<dependency>
<groupId>com.cxx</groupId>
<artifactId>starter-jdbc</artifactId>
<version>1.0.0</version>
</dependency>
<dependency>
<groupId>com.cxx</groupId>
<artifactId>starter-logging</artifactId>
<version>1.0.0</version>
</dependency>
```
# 五、项目使用
1. 将父工程改为starter-parent模块
```xml
<parent>
<groupId>com.cxx</groupId>
<artifactId>starter-parent</artifactId>
<version>2.0.0</version>
<relativePath/>
</parent>
```
2. 根据需要引入starter-web、starter-jdbc和starter-logging模块
```xml
<dependencies>
<dependency>
<groupId>com.cxx</groupId>
<artifactId>starter-web</artifactId>
<version>2.0.0</version>
</dependency>
<dependency>
<groupId>com.cxx</groupId>
<artifactId>starter-jdbc</artifactId>
<version>2.0.0</version>
</dependency>
<dependency>
<groupId>com.cxx</groupId>
<artifactId>starter-logging</artifactId>
<version>2.0.0</version>
</dependency>
</dependencies>
```
3. 打包模块时需要添加spring-boot-maven-plugin
```xml
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
```