(也可以看guide哥的全方位的springBoot:https://github.com/Snailclimb/springboot-guide)
@Configuration
public class CorsConfig implements WebMvcConfigurer {
@Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/**")
.allowedOrigins("*")
.allowCredentials(true)
.allowedMethods("GET", "POST", "PUT", "DELETE", "OPTIONS")
.maxAge(3600);
}
}
// 1.first step @EnableAsync :开启异步
@EnableAsync
@SpringBootApplication
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
// 2.second step -> b方法 : @Async 使用异步
@Async
public void syncBatteries(List batteryDTOs) {
batteryDTOs.forEach(s -> scooterService.syncBattery(s));
}
// 3.third step -> 配置async 开启线程池的配置
@Configuration
public class AsyncConfig {
@Bean
public Executor asyncExecutor() {
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
executor.setCorePoolSize(4);
executor.setMaxPoolSize(8);
executor.setQueueCapacity(200);
executor.setThreadNamePrefix("LocustTask-");
executor.initialize();
return executor;
}
}
// 1. first step -> @EnableRetry:开启重试
@EnableRetry
@SpringBootApplication
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
// 2. second step -> 使用重试 抛出的异常会被相应的value 中的RetryException.class捕捉,并开启重试,此例中最多重试三次,每1s后重试一次,不叠加时间 (multiplier = 1)
@Retryable(value = RetryException.class, maxAttempts = 3, backoff = @Backoff(delay = 1000, multiplier = 1))
public String request(Constant.DockingPlat platform, HttpData httpData){
if(处理失败){
throw new RetryException("xxx");
}
}
// 3. second step -> 恢复机制
@Recover
public void recover(RetryException e) {
e.printStackTrace();
log.error("xxx!");
}
1. 方式一 :基于自定义 + 配置添加
//1. first step -> 自定义过滤器:实现filter方法
public class XssFilter implements Filter {
@Override
public void init(FilterConfig config) throws ServletException {
}
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
throws IOException, ServletException {
XssHttpServletRequestWrapper xssRequest = new XssHttpServletRequestWrapper(
(HttpServletRequest) request);
chain.doFilter(xssRequest, response);
}
@Override
public void destroy() {
}
}
//2. second step -> 将自定义的过滤器添加进来:并设置范围
public class FilterConfig {
@Bean
public FilterRegistrationBean xssFilterRegistration() {
FilterRegistrationBean registration = new FilterRegistrationBean();
registration.setDispatcherTypes(DispatcherType.REQUEST);
registration.setFilter(new XssFilter()); //自定义xss过滤器添加进来
registration.addUrlPatterns("/*");
registration.setName("xssFilter");
registration.setOrder(Integer.MAX_VALUE);
return registration;
}
}
2. 方式二 -> 基于 @WebFilter
// 1. first step -> 开启包扫描
@SpringBootApplication
@ServletComponentScan(basePackages = "com.example.demo.filter")
public class DemoApplication {
public static void main(String[] args) {
SpringApplication.run(DemoApplication.class, args);
}
}
// 2. second step -> 自定义过滤器 业务处理
@Slf4j
@WebFilter(filterName = "myFilter1", urlPatterns = "/*")
public class MyFilter1 implements Filter {
@Override
public void init(FilterConfig filterConfig) throws ServletException {
log.info(filterConfig.getFilterName() + " init");
}
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) {
log.info("myFilter1 begin");
try {
log.info("业务方法执行");
chain.doFilter(request, response);
} catch (Exception e) {
log.error("error!", e);
}
log.info("myFilter1 end");
}
@Override
public void destroy() {
}
}
public class LogInterceptor extends HandlerInterceptorAdapter {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
throws Exception {
long startTime = System.currentTimeMillis();
System.out.println("\n-------- LogInterception.preHandle --- ");
System.out.println("Request URL: " + request.getRequestURL());
System.out.println("Start Time: " + System.currentTimeMillis());
request.setAttribute("startTime", startTime);
return true;
}
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, //
Object handler, ModelAndView modelAndView) throws Exception {
System.out.println("\n-------- LogInterception.postHandle --- ");
System.out.println("Request URL: " + request.getRequestURL());
// You can add attributes in the modelAndView
// and use that in the view page
}
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, //
Object handler, Exception ex) throws Exception {
System.out.println("\n-------- LogInterception.afterCompletion --- ");
long startTime = (Long) request.getAttribute("startTime");
long endTime = System.currentTimeMillis();
System.out.println("Request URL: " + request.getRequestURL());
System.out.println("End Time: " + endTime);
System.out.println("Time Taken: " + (endTime - startTime));
}
}
// 1. first step -> 定义全局异常类方法 继承 RuntimeException
public class RRException extends RuntimeException {
private static final long serialVersionUID = 1L;
private String msg;
private int code = 500;
public RRException(String msg) {
super(msg);
this.msg = msg;
}
}
// 2. second step -> @RestControllerAdvice
@RestControllerAdvice
public class RRExceptionHandler {
private Logger logger = LoggerFactory.getLogger(getClass());
/**
* 处理自定义异常
*/
@SysLog("异常记录") //这个是我自己的逻辑不用关心:日志记录的自定义注解
@ExceptionHandler(RRException.class)
public R handleRRException(RRException e){
R r = new R();
r.put("code", e.getCode());
r.put("msg", e.getMessage());
logger.error(e.getMessage(), e); // 这个是我自己的逻辑 不用关心
ThreadLocalUtils.remove(); // 这个是我自己的逻辑 不用关心:异常后清理所有的信息,防止回收问题导致内存溢出
return r;
}
}
// 3.third step -> 抛出异常后会被全局拦截器拦截并 响应给前端 提示
public T getConfigObject(String key, Class clazz) {
String value = getValue(key);
if(StringUtils.isNotBlank(value)){
return new Gson().fromJson(value, clazz);
}
try {
return clazz.newInstance();
} catch (Exception e) {
throw new RRException("获取参数失败"); // 这样前端就会接收到500 ,和msg的错误信息
}
}
1. 如果你a方法调用b 方法 比如: a -> b->c->d .. 方法调用链很长,但是 传参的话很不便利哪里 你可以再a方法中使用如
在d方法中取出
// 1 first step: 设置threadLocal
public R appLogin(String phone, String code) {
SysUserEntity patrolUser = (SysUserEntity) r.get("result");
ThreadLocalUtils.set("loginUser",patrolUser); // 当前登陆用户的信息 -> 方便后面使用
b(); 一系列的方法调用
ThreadLocalUtils.remove("loginUser");// 清理threadLocal-> 回收
return R.ok().put("result", ecLoginResultDTO);
}
// 2 second step -> 使用 threadlocal b方法:
public void b(){
SysUserEntity loginUser = ThreadLocalUtils.get("loginUser");
}
// 3 third step -> 清理threadLocal
1. 在拦截器清理
2. 使用完成后就清理
3. 全局异常处理器清理
// 4 fourth step ->
advice 建议: 当然也可以利用 threadLocal 或者拦截器 ,请求进来的时候根据token,获取用户存入,请求结束后再拦截器清理掉
(注意try catch 和 开启异步 提高响应速度和不影响主业务的使用,建议重要方法加上日志,避免开启代理 还是会耗时)
// 1.first step -> 自定义注解
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface SysLog {
String value() default "";
}
// 2.second step -> @Aspect:开启切面
@Aspect
@Component
@Slf4j
public class SysLogAspect {
@Autowired
private SysLogService sysLogService;
@Pointcut("@annotation(com.immotor.common.annotation.SysLog)") //切点
public void logPointCut() {
}
@Around("logPointCut()")//环绕(处理前后逻辑) 处理
public Object around(ProceedingJoinPoint point) throws Throwable {
long beginTime = System.currentTimeMillis();
//执行方法
Object result = point.proceed();
//执行时长(毫秒)
long time = System.currentTimeMillis() - beginTime;
//保存日志
sysLogService.saveSysLog(point, String.valueOf(time), result);
return result;
}
}
// 3.third step -> 记录日志方法 :开启异步,和异常处理
@Async
@Override
@Transactional(propagation= Propagation.NOT_SUPPORTED)
public void saveSysLog(ProceedingJoinPoint joinPoint, String time, Object result) {
MethodSignature signature = (MethodSignature) joinPoint.getSignature();
Method method = signature.getMethod();
SysLogEntity sysLog = new SysLogEntity();
SysLog syslog = method.getAnnotation(SysLog.class);
if(syslog != null){
//注解上的描述
sysLog.setOperation(syslog.value());
}
//请求的方法名
String className = joinPoint.getTarget().getClass().getName();
String methodName = signature.getName();
sysLog.setMethod(className + "." + methodName + "()");
//请求的参数
try{
Object[] args = joinPoint.getArgs();
String params = new Gson().toJson(args);
if (params.length() < Constant.LogMaxLength){
sysLog.setParams(params);
}else{
sysLog.setParams(params.substring(0,Constant.LogMaxLength-1));
log.warn("日志参数太长无法保存");
}
}catch (Exception e){
log.info("没有获取参数!");
}
// 请求的结果
try {
String resultStr = (String) result;
if (resultStr.length() < Constant.LogMaxLength){
sysLog.setResults(resultStr); //获取响应结果
}else{
sysLog.setResults(resultStr.substring(0,Constant.LogMaxLength-1));
log.warn("日志响应太长无法保存");
}
String username = ((SysUserEntity) SecurityUtils.getSubject().getPrincipal()).getUsername(); // 操作人姓名
sysLog.setUsername(username);
HttpServletRequest request = HttpContextUtils.getHttpServletRequest(); //id
sysLog.setIp(IPUtils.getIpAddr(request));
} catch (Throwable throwable) {
log.info("没有获取到响应结果!");
}
sysLog.setTime(time);
sysLog.setCreateDate(new Date());
this.save(sysLog);
}
4. fourth step : sql ddl
CREATE TABLE `sys_log` (
`ad` int(11) NOT NULL AUTO_INCREMENT COMMENT '自增主键',
`id` varchar(50) NOT NULL,
`username` varchar(50) DEFAULT NULL COMMENT '用户名',
`operation` varchar(50) DEFAULT NULL COMMENT '用户操作',
`type` varchar(255) DEFAULT '方法日志' COMMENT '日志类型: , 异常日志',
`method` varchar(200) DEFAULT NULL COMMENT '请求方法',
`params` varchar(10000) DEFAULT NULL COMMENT '请求参数',
`results` varchar(5000) DEFAULT NULL COMMENT '响应结果',
`status` int(11) DEFAULT '1' COMMENT '执行状态:1成功; 0失败',
`time` varchar(20) DEFAULT NULL COMMENT '执行时长(毫秒)',
`ip` varchar(64) DEFAULT NULL COMMENT 'IP地址',
`create_date` timestamp NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
PRIMARY KEY (`ad`) USING BTREE
) ENGINE=InnoDB AUTO_INCREMENT=32263 DEFAULT CHARSET=utf8mb4 COMMENT='系统日志';
// 1. first step -> 开启swagger的配置
@Configuration
@EnableSwagger2
@EnableSwaggerBootstrapUI
@Profile({"dev","test"}) // 只在开发环境和测试环境中有效
public class SwaggerConfig implements WebMvcConfigurer {
@Bean
public Docket createRestApi() {
return new Docket(DocumentationType.SWAGGER_2)
.apiInfo(apiInfo())
.select()
//加了ApiOperation注解的类,才生成接口文档
.apis(RequestHandlerSelectors.withMethodAnnotation(ApiOperation.class))
//包下的类,才生成接口文档
// .apis(RequestHandlerSelectors.basePackage("com.xxx.xxx"))
.paths(PathSelectors.any())
.build()
.securitySchemes(security());
}
private ApiInfo apiInfo() {
return new ApiInfoBuilder()
.title("xxxtitle")
.description("xxxdesc")
.termsOfServiceUrl("")
.version("v1.0")
.build();
}
private List security() {
return newArrayList(
new ApiKey("token", "token", "header")
);
}
}
2. second step -> 可以开启yml中swagger的安全机制设置 账户名密码:
swagger:
production: false
basic:
enable: true
username: admin
password: admin
3. third step -> 使用
@Api(tags = "统计")
@RestController
@RequestMapping("xxx")
public class xxxController {
@Autowired
BdCountService bdCountService;
@ApiOperation("1.xxxx")
@GetMapping("/general")
public R general(){
return R.ok().put("result",bdCountService.orgGeneralCount());
}
}