自定义注解
使用场景还是很多的,比如说是 在一定时间内限制接口请求次数 下面就来举个栗子
业务需求
限制一个接口或者多个接口在规定的时间内只能请求规定好的次数以内
首先定义一个自定义注解
import java.lang.annotation.*;
@Documented // @Documented注解只是用来做标识,没什么实际作用,了解就好。
@Target(ElementType.METHOD) // 说明该注解只能放在方法上面
@Retention(RetentionPolicy.RUNTIME)
public @interface AccessLimit {
/**
* 秒
* 可以加默认值,根据自己的业务需求考虑是否添加
* @return
*/
int seconds();
// int seconds() default 1;
/**
* 最大请求数量
* 可以加默认值,根据自己的业务需求考虑是否添加
* @return
*/
int maxCount();
}
注解解释:
@Documented
该注解只是用来做标识,没什么实际作用,了解就好。
@Target
规定可以修饰的对象范围
ElementType 可以规定的范围有:
1.CONSTRUCTOR:用于描述构造器
2.FIELD:用于描述域
3.LOCAL_VARIABLE:用于描述局部变量
4.METHOD:用于描述方法
5.PACKAGE:用于描述包
6.PARAMETER:用于描述参数
7.TYPE:用于描述类、接口(包括注解类型) 或enum声明
@Retention
规定声明周期
RetentionPolicy.SOURCE:注解只保留在源文件,当Java文件编译成class文件的时候,注解被遗弃;
RetentionPolicy.CLASS:注解被保留到class文件,但jvm加载class文件时候被遗弃,这是默认的生命周期;
RetentionPolicy.RUNTIME:注解不仅被保存到class文件中,jvm加载class文件之后,仍然存在;
到此为止注解就自定义完毕了,那么就可以考虑自己是使用spring的AOP还是写一个大的拦截器
AOP
使用AOP进行拦截
import lombok.extern.slf4j.Slf4j;
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.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestAttributes;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import javax.servlet.http.HttpServletRequest;
@Slf4j
@Aspect // 把当前类标识为一个切面供容器读取
@Component
public class LimitRequestAspect {
// 定义切点
// 让所有有@accessLimit注解的方法都执行切面方法
@Pointcut("@annotation(accessLimit)")
public void excudeService(AccessLimit accessLimit) {
}
@Around("excudeService(accessLimit)")
public Object doAround(ProceedingJoinPoint pjp, AccessLimit accessLimit) throws Throwable {
// 获得request对象 这个对象根据自己的需求考虑是否获取
RequestAttributes ra = RequestContextHolder.getRequestAttributes();
ServletRequestAttributes sra = (ServletRequestAttributes) ra;
HttpServletRequest request = sra.getRequest();
String requestURI = request.getRequestURI();
log.info("请求地址:{}", requestURI);
log.info("最大请求数量:{}", accessLimit.maxCount());
log.info("限制时间:{}", accessLimit.seconds());
// 现在拿到了注解上规定的参数,那么就可以搭配redis进行操作
// 流程例如:先去redis里面获取已访问的次数,如果获取不到或者获取到的次数没有超过注解上规定的最大访问次数的话,那么就将次数+1放回redis中并且过期时间设置为注解上设置的过期时间
// 如果超出访问限制,那么直接 return; 具体return什么信息就看你的业务需求了,比如直接返回 "不ok",或者放回定好的统一返回类也行
return pjp.proceed(); // 这个是放行的返回的结果,如果不放行,就返回别的信息
}
}
定义一个统一的拦截器
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.HandlerInterceptor;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
@Slf4j
public class LoginInterceptor implements HandlerInterceptor {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
// 判断请求是否属于方法的请求
if (handler instanceof HandlerMethod) {
HandlerMethod handlerMethod = (HandlerMethod) handler;
// 获取方法中的注解,看是否有该注解
AccessLimit accessLimit = handlerMethod.getMethodAnnotation(AccessLimit.class);
if (accessLimit == null) {
return true;
}
// 单位时间
int seconds = accessLimit.seconds();
// 访问次数
int maxCount = accessLimit.maxCount();
// 是否需要登陆
boolean needLogin = accessLimit.needLogin();
String key = request.getRequestURI();
log.info("访问地址: {}", key);
log.info("单位时间:{}", seconds);
log.info("访问次数:{}", maxCount);
log.info("是否需要登陆:{}", needLogin);
}
return true;
}
}
定义完了后,还有在注入到拦截器中
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
@Configuration
public class WebMvcConfiguration implements WebMvcConfigurer {
// 1:把LoginInterceptor放入到ioc容器中
@Bean
public LoginInterceptor getLoginInterceptor(){
return new LoginInterceptor();
}
//:2:注册拦截器的方法
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(getLoginInterceptor())
// 根据自己的实际业务进行配置,下面是我举例
.addPathPatterns("/api/**","/","/index")
// 代表不拦截路由
.excludePathPatterns("/login/**")
.excludePathPatterns("/api/kaptcha");
}
}
最后
无论是使用AOP还是自定义拦截器的方式都是需要将自定义好的直接放在你需要拦截的方法上
例如:
这样当访问这个方法的时候,就触发上面的注解
我建议使用AOP方式,因为比较灵活,只会拦截处理加了注解的方法,而且也不用注册拦截器,比较简单明了,省事
如果是用拦截器的方法的,就要注册拦截器,比较麻烦
以上是我个人意见,具体使用哪个自己根据自己的业务需求来决定

267

被折叠的 条评论
为什么被折叠?



