自定义spring-boot-starter
简单实现一个解决重复请求的starter
文章目录
导入依赖
<dependencies>
<!-- 为了使用web的拦截器导入 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>2.0.32</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-configuration-processor</artifactId>
<!--表示两个项目之间依赖不传递;不设置optional或者optional是false,表示传递依赖-->
<!--例如:project1依赖a.jar(optional=true),project2依赖project1,则project2不 依赖a.jar-->
<optional>true</optional>
</dependency>
</dependencies>
编写业务逻辑
定义注解
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface RepeatRequest {
boolean support() default true;
String msg() default "重复提交";
}
定义拦截器
package rr;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.HandlerInterceptor;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.logging.Filter;
/**
* @author pzz
* @Date 2023-05-31 15:28
* @description
*/
public abstract class AbstractRepeatRequestHandler implements HandlerInterceptor {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
if(handler instanceof HandlerMethod){
return this.doRepeatCheck(((HandlerMethod) handler),response,request);
}
return HandlerInterceptor.super.preHandle(request, response, handler);
}
/**
* 是否重复请求检查
* @return
*/
public abstract boolean doRepeatCheck(HandlerMethod handlerMethod,HttpServletResponse response, HttpServletRequest request);
public String respToJson(HttpServletResponse response, String string){
String utf8 = "utf-8";
String application = "application/json";
try{
response.setContentType(application);
response.setCharacterEncoding(utf8);
response.getWriter().print(string);
}
catch (IOException e){
e.printStackTrace();
}
return null;
}
}
定义默认实现
package rr;
import com.alibaba.fastjson.JSONObject;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Component;
import org.springframework.web.method.HandlerMethod;
import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.TimeUnit;
/**
* @author pzz
* @Date 2023-05-31 15:41
* @description
*/
public class DefaultRepeatRequestHandler extends AbstractRepeatRequestHandler {
@Resource
private RedisTemplate redisTemplate;
@Override
public boolean doRepeatCheck(HandlerMethod handlerMethod, HttpServletResponse response, HttpServletRequest request) {
RepeatRequest repeatRequest = handlerMethod.getMethodAnnotation(RepeatRequest.class);
if (repeatRequest == null || !repeatRequest.support()) {
return true;
}
String uri = request.getRequestURI();
String token = request.getHeader("authorization");
String key = token + "-" + uri;
Long o = redisTemplate.getExpire(key);
if (o == -2L) {
redisTemplate.opsForValue().set(key, "flag", 1, TimeUnit.MINUTES);
return true;
}
Map<String,Object> map = new HashMap<>();
map.put("code", "500");
map.put("msg", repeatRequest.msg());
map.put("data", null);
respToJson(response, JSONObject.toJSONString(map));
return false;
}
}
定义bean配置类
package rr;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.stereotype.Component;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
import javax.annotation.Resource;
/**
* @author pzz
* @Date 2023-06-01 15:30
* @description
*/
@Configuration
public class Config {
@Bean
// 保证引入此模块的项目 存在实现时 使用项目中的实现 而不是默认实现
@ConditionalOnMissingBean(AbstractRepeatRequestHandler.class)
public AbstractRepeatRequestHandler abstractRepeatRequestHandler(){
return new DefaultRepeatRequestHandler();
}
}
注册拦截器
package rr;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
import javax.annotation.Resource;
/**
* @author pzz
* @Date 2023-06-01 16:34
* @description
*/
@Configuration
public class WebMvcConfig implements WebMvcConfigurer {
@Resource
public AbstractRepeatRequestHandler abstractRepeatRequestHandler;
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(abstractRepeatRequestHandler)
.addPathPatterns("/**");
}
}
自动装配
在 resource目录下建立 META-INF 文件夹 ,在此文件夹下创建spring.factories文件并指定需要自动装配的配置类
org.springframework.boot.autoconfigure.EnableAutoConfiguration=rr.Config,rr.WebMvcConfig
将starter安装到本地仓库
在控制台执行下面的命令,至此大功告成 开始使用
mvn clean install
引入自定义starter并使用
pom引入
<dependency>
<groupId>com.pzz</groupId>
<artifactId>spring-boot-starter-rr</artifactId>
<version>1.0</version>
</dependency>
使用
@GetMapping("/test")
@RepeatRequest
public String test(){
// do anything here
return "success";
}
在项目中使用自定义实现的拦截
@Bean
public AbstractRepeatRequestHandler abstractRepeatRequestHandler(){
return new AbstractRepeatRequestHandler() {
@Override
public boolean doRepeatCheck(HandlerMethod handlerMethod, HttpServletResponse response, HttpServletRequest request) {
System.out.println("自定义...");
return true;
}
};
}
结语
欢迎各位大佬提出更好的想法