springBoot的重试,异步,过滤器,拦截器,全局异常处理,日志记录

 

以下 均是实例,没有进行原理说明,建议单独百度看原理一起食用

(也可以看guide哥的全方位的springBoot:https://github.com/Snailclimb/springboot-guide)

1. springBoot 解决跨域
 

@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);
    }
}

2. springBoot开启异步:

           @EnableAsync

(注意比如A调用B方法, 那么B 要异步:是public 方法且和A不在同类下,否则不能走代理;)

// 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;
    }
}

 

3.spring的重试机制:

         @EnableRetry

 (使用场景:比如我们接入三方接口,有的需要重试逻辑 (注意事项和2的一样, 非同类方法且public))

// 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!");
}

 

 

4. springBoot过滤器:filter

   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() {
    }
}

4. springBoot拦截器:intercepter

   

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));
    }

}

5. springBoot全局异常处理

// 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的错误信息
		}
	}

6. springBoot 中的threadLocal使用: 

    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,获取用户存入,请求结束后再拦截器清理掉

7. springBoot中自定义注解进行,异常记录

(注意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='系统日志';

8 .springBoot 设置bootstrap-ui的swagger2

// 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());
    }
}

 

你可能感兴趣的:(javeEE)