add aop 模板
This commit is contained in:
parent
3b41e8c168
commit
ef8dd803cf
90
java/spring/aop/aop模板/接口重复提交处理/重复提交处理.md
Normal file
90
java/spring/aop/aop模板/接口重复提交处理/重复提交处理.md
Normal file
@ -0,0 +1,90 @@
|
||||
# 接口重复提交处理
|
||||
|
||||
定义`AvoidRepeatableCommit`注解,用于对接口重复提交控制的切点
|
||||
|
||||
```java
|
||||
@Target(ElementType.METHOD)
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
public @interface AvoidRepeatableCommit {
|
||||
/**
|
||||
* 指定时间内不可重复提交,单位毫秒,默认1秒
|
||||
*/
|
||||
long timeout() default 1000;
|
||||
}
|
||||
```
|
||||
|
||||
定义**AOP**切面并实现接口重复提交控制逻辑
|
||||
|
||||
```java
|
||||
@Component
|
||||
@Aspect
|
||||
@Slf4j
|
||||
public class AvoidRepeatableCommitAspect {
|
||||
|
||||
@Autowired
|
||||
private StringRedisTemplate stringRedisTemplate;
|
||||
|
||||
//设置切点为AvoidRepeatableCommit注解
|
||||
@Pointcut(value = "@annotation(avoidRepeatableCommit)")
|
||||
public void pointcut(AvoidRepeatableCommit avoidRepeatableCommit) {
|
||||
// pointcut
|
||||
}
|
||||
|
||||
//对切入点进行前置增强
|
||||
@Before(value = "pointcut(avoidRepeatableCommit)",argNames = "point,avoidRepeatableCommit")
|
||||
public void before(JoinPoint point, AvoidRepeatableCommit avoidRepeatableCommit){
|
||||
|
||||
//获取请求对象
|
||||
HttpServletRequest request = ((ServletRequestAttributes) Objects.requireNonNull(RequestContextHolder.getRequestAttributes())).getRequest();
|
||||
|
||||
//获取注解
|
||||
MethodSignature signature = (MethodSignature) point.getSignature();
|
||||
Method method = signature.getMethod();
|
||||
|
||||
//目标类、方法
|
||||
String className = method.getDeclaringClass().getName();
|
||||
String name = method.getName();
|
||||
|
||||
// 得到类名和方法
|
||||
String ipKey = String.format("%s#%s", className, name);
|
||||
int hashCode = ipKey.hashCode();
|
||||
|
||||
//获取IP用于区分用户,这里的IP可以替换为用户ID或者Token
|
||||
String ip = HttpRequestUtil.getIpAddr(request);
|
||||
|
||||
String key = String.format("%s:%s_%d", "AVOID_REPEATABLE_COMMIT", ip, hashCode);
|
||||
|
||||
log.info("ipKey={},hashCode={},key={}", ipKey, hashCode, key);
|
||||
|
||||
long timeout = avoidRepeatableCommit.timeout();
|
||||
|
||||
String value = stringRedisTemplate.opsForValue().get(key);
|
||||
|
||||
if (StringUtils.isNotBlank(value)) {
|
||||
log.info("请勿重复提交表单");
|
||||
//抛出连续请求异常(全局异常处理会处理此异常) --> 请勿连续请求此接口
|
||||
throw new MyException(RestResponse.MSG.AVOID_REPEATABLE_COMMIT).setMsg("请勿连续请求此接口");
|
||||
}
|
||||
|
||||
// 设置过期时间
|
||||
stringRedisTemplate.opsForValue().set(key,ipKey,timeout, TimeUnit.MILLISECONDS);
|
||||
}
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
将`AvoidRepeatableCommit`加到API方法上,可以实现用户对此API短时间内重复请求控制
|
||||
|
||||
```Java
|
||||
@RestController
|
||||
public class UserApi {
|
||||
|
||||
//同一个用户在2000毫秒内重复调用此接口会抛出重复请求异常
|
||||
@AvoidRepeatableCommit(2000)
|
||||
@GetMapping("/getList")
|
||||
public String getList() {
|
||||
return "test";
|
||||
}
|
||||
}
|
||||
```
|
||||
|
171
java/spring/aop/aop模板/接口防爬/设置API在规定时间内可调用次数.md
Normal file
171
java/spring/aop/aop模板/接口防爬/设置API在规定时间内可调用次数.md
Normal file
@ -0,0 +1,171 @@
|
||||
# 设置API在规定时间内可调用次数
|
||||
|
||||
定义配置类,用于读取配置文件中的全局配置
|
||||
|
||||
```java
|
||||
@RefreshScope //以这种方式注释的bean可以在运行时刷新,并且使用的任何组件 他们将在下一个方法调用中获得一个新实例,完全初始化并注入 与所有依赖关系。
|
||||
@ConfigurationProperties(prefix = "request-limit")
|
||||
@Component
|
||||
@Data
|
||||
public class RequestLimitConfig {
|
||||
/**
|
||||
* 是否开启请求限制
|
||||
*/
|
||||
private Boolean start;
|
||||
|
||||
/**
|
||||
* 允许访问的数量
|
||||
*/
|
||||
private int amount;
|
||||
|
||||
/**
|
||||
* 时间段
|
||||
*/
|
||||
private long time;
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
定义RequestLimit注解,用于设置API在规定时间内允许访问的次数
|
||||
|
||||
```java
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Target(ElementType.METHOD)
|
||||
@Documented
|
||||
@Order(Ordered.HIGHEST_PRECEDENCE)
|
||||
public @interface RequestLimit {
|
||||
|
||||
/**
|
||||
* 允许访问的次数,默认值100
|
||||
*/
|
||||
int amount() default 100;
|
||||
|
||||
/**
|
||||
* 时间段,单位为毫秒,默认值一分钟
|
||||
*/
|
||||
long time() default 60000;
|
||||
}
|
||||
```
|
||||
|
||||
切面实现
|
||||
|
||||
```java
|
||||
@Aspect
|
||||
@RefreshScope
|
||||
@Component
|
||||
@Slf4j
|
||||
public class RequestLimitAspect {
|
||||
|
||||
private final String POINT = "execution(* com.xxx.xxx.api..*.*(..))";
|
||||
@Autowired
|
||||
private StringRedisTemplate stringRedisTemplate;
|
||||
|
||||
@Autowired
|
||||
private RequestLimitConfig requestLimitConfig;
|
||||
|
||||
//设置切点为指定包下的方法
|
||||
@Pointcut(POINT)
|
||||
public void pointcut() {
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* 对切点进行前置增强
|
||||
*/
|
||||
@Before(value = "pointcut()",argNames = "point")
|
||||
public void before(JoinPoint point) throws Throwable {
|
||||
|
||||
// 判断是否开启了接口请求限制 (此处可以使用@Conditional优化,使用Conditional控制是否注入此切面)
|
||||
if (requestLimitConfig.getStart()) {
|
||||
ServletRequestAttributes attribute = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
|
||||
|
||||
HttpServletRequest request = attribute.getRequest();
|
||||
|
||||
//获取IP
|
||||
String ip = IpUtils.getIpAddr(request);
|
||||
|
||||
//获取请求路径
|
||||
String url = request.getRequestURL().toString();
|
||||
|
||||
//获取方法名称
|
||||
String methodName = point.getSignature().getName();
|
||||
|
||||
String key = String.format("%s:%s:%s","REQUEST_LIMIT",ip,methodName);
|
||||
Method currentMethod = getMethod(point);
|
||||
|
||||
//查看接口是否有RequestLimit注解,如果没有则按yml的值全局验证
|
||||
if (currentMethod.isAnnotationPresent(RequestLimit.class)) {
|
||||
//获取注解
|
||||
RequestLimit requestLimit = currentMethod.getAnnotation(RequestLimit.class);
|
||||
boolean checkResult = checkWithRedis(requestLimit.amount(), requestLimit.time(), key);
|
||||
if (checkResult) {
|
||||
log.info("requestLimited," + "[用户ip:{}],[访问地址:{}]超过了限定的次数[{}]次", ip, url, requestLimit.amount());
|
||||
//抛出接口请求过于频繁异常(全局异常处理会处理此异常) --> 接口请求过于频繁
|
||||
throw new Exception();
|
||||
}
|
||||
return;
|
||||
}
|
||||
boolean checkResult = checkWithRedis(requestLimitConfig.getAmount(), requestLimitConfig.getTime(), key);
|
||||
if (checkResult) {
|
||||
log.info("requestLimited," + "[用户ip:{}],[访问地址:{}]超过了限定的次数[{}]次", ip, url, requestLimitConfig.getAmount());
|
||||
//抛出接口请求过于频繁异常(全局异常处理会处理此异常) --> 接口请求过于频繁
|
||||
throw new Exception();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 以redis实现请求记录
|
||||
* @param amount 请求次数 规定时间内可请求的次数
|
||||
* @param time 规定时间
|
||||
* @param key redis key
|
||||
* @return true 超过规定时间请求限制阈值 false 未超过规定时间请求限制阈值
|
||||
*/
|
||||
private boolean checkWithRedis(int amount, long time, String key) {
|
||||
//请求次数+1并取得已请求数
|
||||
long count = Optional.ofNullable(stringRedisTemplate.opsForValue().increment(key,1)).orElse(0L);
|
||||
//如果是第一次请求则设置超时时间
|
||||
if (1==count) {
|
||||
stringRedisTemplate.expire(key,time,TimeUnit.MILLISECONDS);
|
||||
}
|
||||
//判断已请求数是否超过规定时间内可请求次数
|
||||
return count > amount;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取当前切点的方法
|
||||
*/
|
||||
private Method getMethod(JoinPoint point) throws NoSuchMethodException {
|
||||
Signature sig = point.getSignature();
|
||||
MethodSignature msig = (MethodSignature) sig;
|
||||
Object target = point.getTarget();
|
||||
return target.getClass().getMethod(msig.getName(), msig.getParameterTypes());
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
配置文件设置全局api请求限制
|
||||
|
||||
```yaml
|
||||
#请求限制参数
|
||||
request-limit:
|
||||
start: false # 是否开启请求限制,默认关闭 //此处不开启单独使用注解不生效
|
||||
amount: 100 # 100次
|
||||
time: 60000 # 1分钟 单位毫秒
|
||||
```
|
||||
|
||||
单独对某个API设置请求限制
|
||||
|
||||
```java
|
||||
@RestController
|
||||
public class UserApi {
|
||||
//设置接口来自于同一个IP的请求在1分钟内不能超过200次
|
||||
//amout 规定次数 time 规定时间 单位毫秒;如果配置文件中没有开启请求限制添加此注解不生效
|
||||
@RequestLimit(amount = 200, time = 60000)
|
||||
@GetMapping("/getList")
|
||||
public String getList() {
|
||||
return "test";
|
||||
}
|
||||
}
|
||||
```
|
||||
|
99
java/spring/aop/aop模板/权限校验/权限校验.md
Normal file
99
java/spring/aop/aop模板/权限校验/权限校验.md
Normal file
@ -0,0 +1,99 @@
|
||||
# AOP权限校验实现
|
||||
|
||||
定义`AuthorityVerify`权限校验注解,用于权限校验的切点
|
||||
|
||||
```java
|
||||
@Target({ElementType.METHOD, ElementType.TYPE})
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
public @interface AuthorityVerify {
|
||||
String value() default "";
|
||||
}
|
||||
```
|
||||
|
||||
定义**AOP**切面并实现针对权限校验的实现
|
||||
|
||||
```java
|
||||
@Aspect
|
||||
@Component
|
||||
@Slf4j
|
||||
public class AuthorityVerifyAspect {
|
||||
|
||||
@Autowired
|
||||
CategoryMenuService categoryMenuService;
|
||||
|
||||
@Autowired
|
||||
RedisUtil redisUtil;
|
||||
|
||||
//设置切点为AuthorityVerify注解
|
||||
@Pointcut(value = "@annotation(authorityVerify)")
|
||||
public void pointcut(AuthorityVerify authorityVerify) {
|
||||
|
||||
}
|
||||
|
||||
//对切点进行环绕增强
|
||||
@Around(value = "pointcut(authorityVerify)")
|
||||
public Object doAround(ProceedingJoinPoint joinPoint, AuthorityVerify authorityVerify) throws Throwable {
|
||||
|
||||
ServletRequestAttributes attribute = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
|
||||
|
||||
HttpServletRequest request = attribute.getRequest();
|
||||
|
||||
//获取请求路径
|
||||
String url = request.getRequestURI();
|
||||
|
||||
// 解析出请求者的ID和用户名
|
||||
String adminUid = request.getAttribute(SysConf.ADMIN_UID).toString();
|
||||
|
||||
String key = String.format("%s:%s","ADMIN_VISIT_MENU",adminUid);
|
||||
|
||||
// 从Redis中获取用户的权限信息
|
||||
String visitUrlStr = redisUtil.get(key);
|
||||
|
||||
LinkedTreeMap<String, String> visitMap = new LinkedTreeMap<>();
|
||||
|
||||
//判断用户权限是否存储再Redis中
|
||||
if (StringUtils.isNotEmpty(visitUrlStr)) {
|
||||
// 存在则讲redis取到的字符串进行序列化
|
||||
visitMap = (LinkedTreeMap<String, String>) JsonUtils.jsonToMap(visitUrlStr, String.class);
|
||||
} else {
|
||||
// Redis中不存在则从数据库中获取并存储到Redis中
|
||||
|
||||
// 查询数据库获取获取用户权限列表
|
||||
List<CategoryMenu> buttonList = categoryMenuService.getMenuByUserId(adminUid);
|
||||
|
||||
//将权限添加到Map中
|
||||
for (CategoryMenu item : buttonList) {
|
||||
if (StringUtils.isNotEmpty(item.getUrl())) {
|
||||
visitMap.put(item.getUrl(), item.getUrl());
|
||||
}
|
||||
}
|
||||
// 将访问URL存储到Redis中
|
||||
redisUtil.setEx(key, JsonUtils.objectToJson(visitMap), 1, TimeUnit.HOURS);
|
||||
}
|
||||
|
||||
// 判断该角色是否能够访问该接口
|
||||
if (visitMap.get(url) != null) {
|
||||
log.info("用户拥有操作权限,访问的路径: {},拥有的权限接口:{}", url, visitMap.get(url));
|
||||
//执行业务
|
||||
return joinPoint.proceed();
|
||||
} else {
|
||||
log.info("用户不具有操作权限,访问的路径: {}", url);
|
||||
return ResultUtil.result(ECode.NO_OPERATION_AUTHORITY, MessageConf.RESTAPI_NO_PRIVILEGE);
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
将`AuthorityVerify`加到Api方法上则可以实现用户对此API的权限校验
|
||||
|
||||
```Java
|
||||
@RestController
|
||||
public class UserApi {
|
||||
@AuthorityVerify
|
||||
@GetMapping("/getList")
|
||||
public String getList() {
|
||||
return "test";
|
||||
}
|
||||
}
|
||||
```
|
||||
|
107
maven/Maven.md
107
maven/Maven.md
@ -1,16 +1,15 @@
|
||||
> 本文由 [简悦 SimpRead](http://ksria.com/simpread/) 转码, 原文地址 [javaguide.cn](https://javaguide.cn/tools/maven/maven-core-concepts.html)
|
||||
|
||||
> Apache Maven 的本质是一个软件项目管理和理解工具。基于项目对象模型 (Project Object Model,POM) 的概念,Maven 可以从一条中心信息管理项目的构建、报告和文档。
|
||||
# Maven
|
||||
|
||||
> 这部分内容主要根据 Maven 官方文档整理,做了对应的删减,主要保留比较重要的部分,不涉及实战,主要是一些重要概念的介绍。
|
||||
## 目录
|
||||
|
||||
# Maven 介绍
|
||||
-----------------------
|
||||
[TOC]
|
||||
|
||||
## Maven 介绍
|
||||
|
||||
[Mavenopen in new window](https://github.com/apache/maven) 官方文档是这样介绍的 Maven 的:
|
||||
|
||||
> Apache Maven is a software project management and comprehension tool. Based on the concept of a project object model (POM), Maven can manage a project's build, reporting and documentation from a central piece of information.
|
||||
>
|
||||
> Apache Maven 的本质是一个软件项目管理和理解工具。基于项目对象模型 (Project Object Model,POM) 的概念,Maven 可以从一条中心信息管理项目的构建、报告和文档。
|
||||
|
||||
**什么是 POM?** 每一个 Maven 工程都有一个 `pom.xml` 文件,位于根目录中,包含项目构建生命周期的详细信息。通过 `pom.xml` 文件,我们可以定义项目的坐标、项目依赖、项目信息、插件信息等等配置。
|
||||
@ -23,7 +22,7 @@
|
||||
|
||||
关于 Maven 的基本使用这里就不介绍了,建议看看官网的 5 分钟上手 Maven 的教程:[Maven in 5 Minutesopen in new window](https://maven.apache.org/guides/getting-started/maven-in-five-minutes.html) 。
|
||||
|
||||
[#](#maven-坐标) Maven 坐标
|
||||
Maven 坐标
|
||||
-----------------------
|
||||
|
||||
项目中依赖的第三方库以及插件可统称为构件。每一个构件都可以使用 Maven 坐标唯一标识,坐标元素包括:
|
||||
@ -38,30 +37,28 @@
|
||||
|
||||
举个例子(引入阿里巴巴开源的 EasyExcel) :
|
||||
|
||||
```
|
||||
```xml
|
||||
<dependency>
|
||||
<groupId>com.alibaba</groupId>
|
||||
<artifactId>easyexcel</artifactId>
|
||||
<version>3.1.1</version>
|
||||
</dependency>
|
||||
|
||||
|
||||
```
|
||||
|
||||
你可以在 https://mvnrepository.com/ 这个网站上找到几乎所有可用的构件,如果你的项目使用的是 Maven 作为构建工具,那这个网站你一定会经常接触。
|
||||
|
||||
![](https://guide-blog-images.oss-cn-shenzhen.aliyuncs.com/github/javaguide/tools/maven/mvnrepository.com.png)
|
||||
![](assets/mvnrepository.com.png)
|
||||
|
||||
[#](#maven-依赖) Maven 依赖
|
||||
Maven 依赖
|
||||
-----------------------
|
||||
|
||||
如果使用 Maven 构建产生的构件(例如 Jar 文件)被其他的项目引用,那么该构件就是其他项目的依赖。
|
||||
|
||||
### [#](#依赖配置) 依赖配置
|
||||
### 依赖配置
|
||||
|
||||
**配置信息示例** :
|
||||
|
||||
```
|
||||
```xml
|
||||
<project>
|
||||
<dependencies>
|
||||
<dependency>
|
||||
@ -80,8 +77,6 @@
|
||||
</dependency>
|
||||
</dependencies>
|
||||
</project>
|
||||
|
||||
|
||||
```
|
||||
|
||||
**配置说明** :
|
||||
@ -94,7 +89,7 @@
|
||||
* optional(可选): 标记依赖是否可选
|
||||
* exclusions(可选):用来排除传递性依赖, 例如 jar 包冲突
|
||||
|
||||
### [#](#依赖范围) 依赖范围
|
||||
### 依赖范围
|
||||
|
||||
**classpath** 用于指定 `.class` 文件存放的位置,类加载器会从该路径中加载所需的 `.class` 文件到内存中。
|
||||
|
||||
@ -112,26 +107,23 @@ Maven 的依赖范围如下:
|
||||
* **runtime**:运行时依赖范围,对于测试和运行有效,但是在编译主代码时无效,典型的就是 JDBC 驱动实现。
|
||||
* **system**:系统依赖范围,使用 system 范围的依赖时必须通过 systemPath 元素显示地指定依赖文件的路径,不依赖 Maven 仓库解析,所以可能会造成建构的不可移植。
|
||||
|
||||
### [#](#传递依赖性) 传递依赖性
|
||||
### 传递依赖性
|
||||
|
||||
### [#](#依赖冲突) 依赖冲突
|
||||
### 依赖冲突
|
||||
|
||||
**1、对于 Maven 而言,同一个 groupId 同一个 artifactId 下,只能使用一个 version。**
|
||||
|
||||
```
|
||||
```xml
|
||||
<dependency>
|
||||
<groupId>in.hocg.boot</groupId>
|
||||
<artifactId>mybatis-plus-spring-boot-starter</artifactId>
|
||||
<version>1.0.48</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>in.hocg.boot</groupId>
|
||||
<artifactId>mybatis-plus-spring-boot-starter</artifactId>
|
||||
<version>1.0.49</version>
|
||||
</dependency>
|
||||
|
||||
|
||||
```
|
||||
|
||||
若相同类型但版本不同的依赖存在于同一个 pom 文件,只会引入后一个声明的依赖。
|
||||
@ -143,8 +135,6 @@ Maven 的依赖范围如下:
|
||||
```
|
||||
依赖链路一:A -> B -> C -> X(1.0)
|
||||
依赖链路二:A -> D -> X(2.0)
|
||||
|
||||
|
||||
```
|
||||
|
||||
这两条依赖路径上有两个版本的 X,为了避免依赖重复,Maven 只会选择其中的一个进行解析。
|
||||
@ -169,8 +159,6 @@ Maven 在遇到这种问题的时候,会遵循 **路径最短优先** 和 **
|
||||
```
|
||||
依赖链路一:A -> B -> X(1.0) // dist = 3
|
||||
依赖链路二:A -> D -> X(2.0) // dist = 2
|
||||
|
||||
|
||||
```
|
||||
|
||||
因此,Maven 又定义了声明顺序优先原则。
|
||||
@ -181,18 +169,16 @@ Maven 在遇到这种问题的时候,会遵循 **路径最短优先** 和 **
|
||||
|
||||
在依赖路径长度相等的前提下,在 `pom.xml` 中依赖声明的顺序决定了谁会被解析使用,顺序最前的那个依赖优胜。该例中,如果 B 的依赖声明在 D 之前,那么 X (1.0) 就会被解析使用。
|
||||
|
||||
```
|
||||
```xml
|
||||
<dependencies>
|
||||
...
|
||||
dependency B
|
||||
...
|
||||
dependency D
|
||||
</dependencies>
|
||||
|
||||
|
||||
```
|
||||
|
||||
### [#](#排除依赖) 排除依赖
|
||||
### 排除依赖
|
||||
|
||||
单纯依赖 Maven 来进行依赖调解,在很多情况下是不适用的,需要我们手动排除依赖。
|
||||
|
||||
@ -201,8 +187,6 @@ Maven 在遇到这种问题的时候,会遵循 **路径最短优先** 和 **
|
||||
```
|
||||
依赖链路一:A -> B -> C -> X(1.5) // dist = 3
|
||||
依赖链路二:A -> D -> X(1.0) // dist = 2
|
||||
|
||||
|
||||
```
|
||||
|
||||
根据路径最短优先原则,X(1.0) 会被解析使用,也就是说实际用的是 1.0 版本的 X。
|
||||
@ -213,7 +197,7 @@ Maven 在遇到这种问题的时候,会遵循 **路径最短优先** 和 **
|
||||
|
||||
**如何解决呢?** 我们可以通过`exclusive`标签手动将 X(1.0) 给排除。
|
||||
|
||||
```
|
||||
```xml
|
||||
<dependencyB>
|
||||
......
|
||||
<exclusions>
|
||||
@ -223,8 +207,6 @@ Maven 在遇到这种问题的时候,会遵循 **路径最短优先** 和 **
|
||||
</exclusion>
|
||||
</exclusions>
|
||||
</dependency>
|
||||
|
||||
|
||||
```
|
||||
|
||||
一般我们在解决依赖冲突的时候,都会优先保留版本较高的。这是因为大部分 jar 在升级的时候都会做到向下兼容。
|
||||
@ -236,13 +218,11 @@ Maven 在遇到这种问题的时候,会遵循 **路径最短优先** 和 **
|
||||
```
|
||||
依赖链路一:A -> B -> C -> X(1.5) // dist = 3
|
||||
依赖链路二:A -> D -> X(1.0) // dist = 2
|
||||
|
||||
|
||||
```
|
||||
|
||||
我们保留了 1.5 版本的 X,但是这个版本的 X 删除了 1.0 版本中的某些类。这个时候,我们可以考虑升级 D 的版本到一个 X 兼容的版本。
|
||||
|
||||
[#](#maven-仓库) Maven 仓库
|
||||
Maven 仓库
|
||||
-----------------------
|
||||
|
||||
在 Maven 世界中,任何一个依赖、插件或者项目构建的输出,都可以称为 **构件** 。
|
||||
@ -266,7 +246,7 @@ Maven 依赖包寻找顺序:
|
||||
2. 本地仓库没有找到的话,会去远程仓库找寻,下载包到本地仓库。
|
||||
3. 远程仓库没有找到的话,会报错。
|
||||
|
||||
[#](#maven-生命周期) Maven 生命周期
|
||||
Maven 生命周期
|
||||
---------------------------
|
||||
|
||||
Maven 的生命周期就是为了对所有的构建过程进行抽象和统一,包含了项目的清理、初始化、编译、测试、打包、集成测试、验证、部署和站点生成等几乎所有构建步骤。
|
||||
@ -281,11 +261,11 @@ Maven 定义了 3 个生命周期`META-INF/plexus/components.xml`:
|
||||
|
||||
执行 Maven 生命周期的命令格式如下:
|
||||
|
||||
### [#](#default-生命周期) default 生命周期
|
||||
### default 生命周期
|
||||
|
||||
`default`生命周期是在没有任何关联插件的情况下定义的,是 Maven 的主要生命周期,用于构建应用程序,共包含 23 个阶段。
|
||||
|
||||
```
|
||||
```xml
|
||||
<phases>
|
||||
|
||||
<phase>validate</phase>
|
||||
@ -334,27 +314,18 @@ Maven 定义了 3 个生命周期`META-INF/plexus/components.xml`:
|
||||
|
||||
<phase>deploy</phase>
|
||||
</phases>
|
||||
|
||||
|
||||
```
|
||||
|
||||
根据前面提到的阶段间依赖关系理论,当我们执行 `mvn test`命令的时候,会执行从 validate 到 test 的所有阶段,这也就解释了为什么执行测试的时候,项目的代码能够自动编译。
|
||||
|
||||
### [#](#clean-生命周期) clean 生命周期
|
||||
### clean 生命周期
|
||||
|
||||
clean 生命周期的目的是清理项目,共包含 3 个阶段:
|
||||
|
||||
1. pre-clean
|
||||
2. clean
|
||||
3. post-clean
|
||||
|
||||
```
|
||||
```xml
|
||||
<phases>
|
||||
|
||||
<phase>pre-clean</phase>
|
||||
|
||||
<phase>clean</phase>
|
||||
|
||||
<phase>post-clean</phase>
|
||||
</phases>
|
||||
<default-phases>
|
||||
@ -362,24 +333,16 @@ clean 生命周期的目的是清理项目,共包含 3 个阶段:
|
||||
org.apache.maven.plugins:maven-clean-plugin:2.5:clean
|
||||
</clean>
|
||||
</default-phases>
|
||||
|
||||
|
||||
```
|
||||
|
||||
根据前面提到的阶段间依赖关系理论,当我们执行 `mvn clean` 的时候,会执行 clean 生命周期中的 pre-clean 和 clean 阶段。
|
||||
|
||||
### [#](#site-生命周期) site 生命周期
|
||||
### site 生命周期
|
||||
|
||||
site 生命周期的目的是建立和发布项目站点,共包含 4 个阶段:
|
||||
|
||||
1. pre-site
|
||||
2. site
|
||||
3. post-site
|
||||
4. site-deploy
|
||||
|
||||
```
|
||||
```xml
|
||||
<phases>
|
||||
|
||||
<phase>pre-site</phase>
|
||||
|
||||
<phase>site</phase>
|
||||
@ -396,24 +359,22 @@ site 生命周期的目的是建立和发布项目站点,共包含 4 个阶段
|
||||
org.apache.maven.plugins:maven-site-plugin:3.3:deploy
|
||||
</site-deploy>
|
||||
</default-phases>
|
||||
|
||||
|
||||
```
|
||||
|
||||
Maven 能够基于 `pom.xml` 所包含的信息,自动生成一个友好的站点,方便团队交流和发布项目信息。
|
||||
|
||||
[#](#maven-插件) Maven 插件
|
||||
Maven 插件
|
||||
-----------------------
|
||||
|
||||
Maven 本质上是一个插件执行框架,所有的执行过程,都是由一个一个插件独立完成的。像咱们日常使用到的 install、clean、deploy 等命令,其实底层都是一个一个的 Maven 插件。关于 Maven 的核心插件可以参考官方的这篇文档:https://maven.apache.org/plugins/index.html 。
|
||||
|
||||
![](https://guide-blog-images.oss-cn-shenzhen.aliyuncs.com/github/javaguide/tools/maven/maven-plugins.png)
|
||||
![](assets/maven-plugins.png)
|
||||
|
||||
除了 Maven 自带的插件之外,还有一些三方提供的插件比如单测覆盖率插件 jacoco-maven-plugin、帮助开发检测代码中不合规范的地方的插件 maven-checkstyle-plugin、分析代码质量的 sonar-maven-plugin。并且,我们还可以自定义插件来满足自己的需求。
|
||||
|
||||
jacoco-maven-plugin 使用示例:
|
||||
|
||||
```
|
||||
```xml
|
||||
<build>
|
||||
<plugins>
|
||||
<plugin>
|
||||
@ -435,10 +396,8 @@ jacoco-maven-plugin 使用示例:
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
|
||||
|
||||
</plugins>
|
||||
</build>
|
||||
```
|
||||
|
||||
你可以将 Maven 插件理解为一组任务的集合,用户可以通过命令行直接运行指定插件的任务,也可以将插件任务挂载到构建生命周期,随着生命周期运行。
|
||||
@ -448,7 +407,7 @@ Maven 插件被分为下面两种类型:
|
||||
* **Build plugins** :在构建时执行。
|
||||
* **Reporting plugins**:在网站生成过程中执行。
|
||||
|
||||
[#](#maven-多模块管理) Maven 多模块管理
|
||||
Maven 多模块管理
|
||||
-----------------------------
|
||||
|
||||
多模块管理简单地来说就是将一个项目分为多个模块,每个模块只负责单一的功能实现。直观的表现就是一个 Maven 项目中不止有一个 `pom.xml` 文件,会在不同的目录中有多个 `pom.xml` 文件,进而实现多模块管理。
|
||||
@ -464,7 +423,7 @@ Maven 插件被分为下面两种类型:
|
||||
|
||||
如下图所示,Dubbo 项目就被分成了多个子模块比如 dubbo-common(公共逻辑模块)、dubbo-remoting(远程通讯模块)、dubbo-rpc(远程调用模块)。
|
||||
|
||||
![](https://guide-blog-images.oss-cn-shenzhen.aliyuncs.com/github/javaguide/tools/maven/dubbo-maven-multi-module.png)
|
||||
![](assets/dubbo-maven-multi-module.png)
|
||||
|
||||
[#](#文章推荐) 文章推荐
|
||||
---------------
|
||||
|
BIN
maven/assets/dubbo-maven-multi-module.png
Normal file
BIN
maven/assets/dubbo-maven-multi-module.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 68 KiB |
BIN
maven/assets/maven-plugins.png
Normal file
BIN
maven/assets/maven-plugins.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 74 KiB |
BIN
maven/assets/mvnrepository.com.png
Normal file
BIN
maven/assets/mvnrepository.com.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 188 KiB |
Loading…
Reference in New Issue
Block a user