文章目录
- 历史
- 前言
- 框架版本
- 实现方式
- 自定义注解编写
- 编写切面切点
- 逻辑编写
- 编写测试类
- 问题扩充
- 完整代码
历史
之前写了个更加复杂的接口版本控制,发现后期不利于阅读,尝试采取AOP思想写了个简单的。
old 接口版本控制
前言
本篇博客需要实现的内容:
使用AOP思想,编写一个接口版本控制。
要求:
低于限定版本的接口不允许访问!
框架版本
- springboot 2.x
实现方式
自定义注解编写
编写一个自定义注解,放置于指定controller的方法上
。
@Target({ElementType.METHOD}) //用于方法
@Retention(RetentionPolicy.RUNTIME) //运行时有效
@Documented //这个工具被java doc 收录
public @interface ApiVersion {
/**
* 携带参数,默认为1
* @return
*/
int value() default 1;
}
编写切面切点
/**
* 定义切点
*/
@Pointcut("@annotation(cn.linkpower.dtuAuthPlatform.config.apiVersion.ApiVersionConfig.ApiVersion)")
public void resources(){ }
此处增强方式采取@Around 环绕增强
。
@Around("resources()")
public Object check(ProceedingJoinPoint pjp) throws Throwable {
return null;
}
逻辑编写
1、当该注解修饰指定方法时,触发
@Around
中的逻辑。
2、获取对应的HttpServletRequest
对象。
3、根据HttpServletRequest
对象。拿到该请求接口
的实际地址
信息。
4、正则比对是否是指定的接口。
5、如果是符合要求的接口,则获取该接口上指定的 @ApiVersion
注释对象。
6、根据注释对象
,获取其中设置的初始值。
7、比较初始值,如果初始值符合要求,则继续执行;否则不继续执行并返回提示信息。
上面的描述还是有点多,这就是下面代码的实现思想。具体看代码:
@Around("resources()")
public Object check(ProceedingJoinPoint pjp) throws Throwable {
// 1、如果方法上标识了该注解
// 拿到请求
ServletRequestAttributes requestAttributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
HttpServletRequest request = requestAttributes.getRequest();
// 获取请求的uri
String requestURI = request.getRequestURI();
log.info("请求的url:{}",request.getRequestURL());
log.info("请求的uri:{}",requestURI);
// 2、第二重校验,判断是否符合要求
Matcher matcher = VERSION_PREFIX_PATTERN.matcher(requestURI);
if(matcher.find()){
// 3、符合正则表达式,则获取其中的值
log.info("matcher-->{}",matcher);
Integer version = Integer.valueOf(matcher.group(1));
log.info("version--->{}",String.valueOf(version));
// 获取注解中设定的值
MethodSignature ms = (MethodSignature) pjp.getSignature();
// 获取对应方法对象
Method method = ms.getMethod();
// 通过方法对象,根据反射获取方法上的注解
ApiVersion annotation = method.getAnnotation(ApiVersion.class);
Integer oldVersion = annotation.value();
log.info("oldVersion-->{}",oldVersion);
// 4、判断是否符合要求
if((version - oldVersion) >= 0){
// 继续执行
return pjp.proceed();
}
}
// 这里应该返回一个json,或者抛出一个自定义异常
return "请求无效。。。";
}
编写测试类
@RestController
public class Controller {
@ApiVersionConfig.ApiVersion(2)
@RequestMapping("{version}/test1")
public String test1(@RequestParam(value = "id",required = false) String id) throws MyException {
if("1".equals(id)){
throw new MyException("异常测试。。。。");
}
return "666";
}
}
请求测试,按照规定,必须>= 2
才能走上述接口,否则失败。
-
>= 2
:
http://localhost:10025/v2/test1
?id=66
-
< 2
:
http://localhost:10025/v1/test1
?id=66
问题扩充
- 为什么使用
Around
?这里使用Before
不更好么?
这里需要判断,如果不符合要求的,则需要禁止继续执行。
查看源码,发现
ProceedingJoinPoint
类中具有proceed()
方法,调用则可以继续执行后续逻辑。
并且,ProceedingJoinPoint
只能用于Around
。
完整代码
这次就不放置于github了,下面代码为核心代码:
package cn.linkpower.dtuAuthPlatform.config.apiVersion;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import javax.servlet.http.HttpServletRequest;
import java.lang.annotation.*;
import java.lang.reflect.Method;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
@Aspect
@Component
public class ApiVersionConfig {
private Logger log = LoggerFactory.getLogger(ApiVersionConfig.class);
// 正则
private final static Pattern VERSION_PREFIX_PATTERN = Pattern.compile("v(\\d+)/");
/**
* 自定义注解,实现接口版本管理
*/
@Target({ElementType.METHOD}) //用于方法
@Retention(RetentionPolicy.RUNTIME) //运行时有效
@Documented //这个工具被java doc 收录
public @interface ApiVersion {
/**
* 携带参数,默认为1
* @return
*/
int value() default 1;
}
/**
* 定义切点
*/
@Pointcut("@annotation(cn.linkpower.dtuAuthPlatform.config.apiVersion.ApiVersionConfig.ApiVersion)")
public void resources(){ }
@Around("resources()")
public Object check(ProceedingJoinPoint pjp) throws Throwable {
// 1、如果方法上标识了该注解
// 拿到请求
ServletRequestAttributes requestAttributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
HttpServletRequest request = requestAttributes.getRequest();
// 获取请求的uri
String requestURI = request.getRequestURI();
log.info("请求的url:{}",request.getRequestURL());
log.info("请求的uri:{}",requestURI);
// 2、第二重校验,判断是否符合要求
Matcher matcher = VERSION_PREFIX_PATTERN.matcher(requestURI);
if(matcher.find()){
// 3、符合正则表达式,则获取其中的值
log.info("matcher-->{}",matcher);
Integer version = Integer.valueOf(matcher.group(1));
log.info("version--->{}",String.valueOf(version));
// 获取注解中设定的值
MethodSignature ms = (MethodSignature) pjp.getSignature();
// 获取对应方法对象
Method method = ms.getMethod();
// 通过方法对象,根据反射获取方法上的注解
ApiVersion annotation = method.getAnnotation(ApiVersion.class);
Integer oldVersion = annotation.value();
log.info("oldVersion-->{}",oldVersion);
// 4、判断是否符合要求
if((version - oldVersion) >= 0){
// 继续执行
return pjp.proceed();
}
}
// 这里应该返回一个json,或者抛出一个自定义异常
return "请求无效。。。";
}
}