SpringBoot使用技巧(统一异常处理,异步执行,随机端口)

 2019-11-18    0 条评论    7980 浏览

SpringBoot 统一异常处理 异步执行 随机端口 使用技巧

1、统一异常处理

对于接口的定义,我们通常会固定格式,例如:

{

"status": true, "code": 200, "message": "成功", "data": "hello:25797-RedAnts" }

但是如果API接口请求时写错就返回404了且返回的格式不一致,因此我们解析时自然就报错了。

Whitelabel Error Page
This application has no explicit mapping for /error, so you are seeing this as a fallback.

Thu Oct 24 09:41:28 CST 2019 There was an unexpected error (type=Not Found, status=404). No message available

需求驱动开发,为了解决这个问题我们需要统一返回的格式。代码如下:

package cn.guangboyuan.controller.advice;

import cn.guangboyuan.model.ResponseData; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.http.HttpStatus; import org.springframework.web.bind.annotation.ControllerAdvice; import org.springframework.web.bind.annotation.ExceptionHandler; import org.springframework.web.bind.annotation.ResponseBody; import org.springframework.web.servlet.NoHandlerFoundException;

import javax.servlet.http.HttpServletRequest;

/**

  • @author RedAnts

  • 统一异常处理 */ @ControllerAdvice public class GlobalExceptionHandler {

    private static final Logger logger = LoggerFactory.getLogger(GlobalExceptionHandler.class);

    @ExceptionHandler(value = Exception.class) @ResponseBody public ResponseData defaultErrorHandler(HttpServletRequest req, Exception e) throws Exception { logger.error("统一处理异常的具体信息:{}", e); ResponseData r = new ResponseData(); r.setMessage(e.getMessage()); if (e instanceof NoHandlerFoundException) { r.setCode(HttpStatus.NOT_FOUND.value()); } else { r.setCode(HttpStatus.INTERNAL_SERVER_ERROR.value()); } r.setData(null); r.setStatus(false); return r; } }

配置文件新增配置:

#出现错误时,直接抛出异常
spring.mvc.throw-exception-if-no-handler-found=true
#不要为我们工程中的资源文件建立映射
spring.resources.add-mappings=false

ResponseData是我们统一返回信息时封装的实体。

package cn.guangboyuan.model;

import lombok.Getter; import lombok.Setter;

/**

  • @author fuchenggang
  • @description
  • @createtime 2019/10/22 */ @Getter@Setter public class ResponseData { private Boolean status = true; private int code = 200; private String message; private Object data;

}

再次执行不存在的的API接口

{
"status": false,
"code": 404,
"message": "No handler found for GET /hello1",
"data": null
}

2、异步执行

异步调用就是不用等待结果的返回就执行后边的逻辑。spring中只需要通过@Async注解即可并在启动类上增加注解@EnableAsync。

注:一定要用外部类调用指定的异步方法。

@Async使用方法代码:

package cn.guangboyuan.service.async.impl;

import cn.guangboyuan.service.async.AsyncService; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.scheduling.annotation.Async; import org.springframework.stereotype.Service;

/**

  • @author fuchenggang

  • @description

  • @createtime 2019/10/22 */ @Service public class AsyncServiceImpl implements AsyncService {

    private static final Logger logger = LoggerFactory.getLogger(AsyncServiceImpl.class);

    @Async @Override public void saveLog() { logger.info("异步执行保存日志任务"); } }

线程池参数配置类:

package cn.guangboyuan.configuration;

import lombok.Getter; import lombok.Setter; import org.springframework.boot.context.properties.ConfigurationProperties; import org.springframework.context.annotation.Configuration;

/**

  • @author fuchenggang

  • @description

  • @createtime 2019/10/22 */ @Getter@Setter @Configuration @ConfigurationProperties(prefix = "spring.task.poot") public class TaskThreadPoolConfig { //核心线程数 private int corePoolSize = 5;

    //最大线程数 private int maxPoolSize = 50;

    //线程池维护线程所允许的空闲时间 private int keepAliveSeconds = 60;

    //队列长度 private int queueCapacity = 10000;

    private String treadNamePrefix = "FSH-AsyncTask-"; }

线程池配置类:

package cn.guangboyuan.configuration;

import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.aop.interceptor.AsyncUncaughtExceptionHandler; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Configuration; import org.springframework.lang.Nullable; import org.springframework.scheduling.annotation.AsyncConfigurer; import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;

import java.lang.reflect.Method; import java.util.concurrent.Executor; import java.util.concurrent.ThreadPoolExecutor;

/**

  • @author fuchenggang

  • @description

  • @createtime 2019/10/22 */ @Configuration public class AsyncTaskExcutePool implements AsyncConfigurer { private Logger logger = LoggerFactory.getLogger(AsyncTaskExcutePool.class);

    @Autowired private TaskThreadPoolConfig config;

    @Nullable @Override public Executor getAsyncExecutor() { ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor(); executor.setCorePoolSize(config.getCorePoolSize()); executor.setMaxPoolSize(config.getMaxPoolSize()); executor.setQueueCapacity(config.getQueueCapacity()); executor.setKeepAliveSeconds(config.getKeepAliveSeconds()); executor.setThreadNamePrefix(config.getTreadNamePrefix()); executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy()); executor.initialize(); return executor; }

    @Nullable @Override public AsyncUncaughtExceptionHandler getAsyncUncaughtExceptionHandler() { return new AsyncUncaughtExceptionHandler() { @Override public void handleUncaughtException(Throwable throwable, Method method, Object... objects) { logger.error("======="+throwable.getMessage()+"=======",throwable); logger.error("exception method:"+method.getName()); } }; } }

启动类:

package cn.guangboyuan;

import cn.guangboyuan.command.StartCommand; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.scheduling.annotation.EnableAsync;

@SpringBootApplication @EnableAsync public class SpringboottestApplication {

public static void main(String[] args) {
    new StartCommand(args);
    SpringApplication.run(SpringboottestApplication.class, args);
}

}

执行效果:

 

3、随机端口

一个服务想要启动多个实例是,就需要随机获取端口。随机端口这对微服务处理能力的扩容是最好不过的方式。

启动设置参数类:

package cn.guangboyuan.command;

import cn.guangboyuan.util.ServerPortUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.util.StringUtils;

/**

  • @author fuchenggang

  • @description

  • @createtime 2019/10/23 */ public class StartCommand { private Logger logger = LoggerFactory.getLogger(StartCommand.class);

    public StartCommand(String[] args) { Boolean isServerPort = false; String serverPort = ""; if (args != null) { for (String arg : args) { if (StringUtils.hasText(arg) && arg.startsWith("--server.port")) { isServerPort = true; serverPort = arg; break; } } }

     //没有指定端口,则随机生成一个可用的端口
     String port;
     if (!isServerPort) {
         port = String.valueOf(ServerPortUtils.getAvailablePort());
     } else {
         port = serverPort.split("=")[1];
     }
     logger.info("current server.port={}", port);
     System.setProperty("server.port", String.valueOf(port));
    

    } }

响应的工具类:

package cn.guangboyuan.util;

import java.util.Random;

/**

  • @author fuchenggang */ public class ServerPortUtils {

    public static int getAvailablePort() { int max = 65535; int min = 2000; Random random = new Random(); int port = random.nextInt(max) % (max - min + 1) + min; boolean using = NetUtils.isLoclePortUsing(port); if (using) { return getAvailablePort(); } else { return port; } } }

package cn.guangboyuan.util;

import java.io.IOException; import java.net.InetAddress; import java.net.Socket; import java.net.UnknownHostException;

/**

  • @author fuchenggang */ public class NetUtils {

    public static boolean isLoclePortUsing(int port) { boolean flag = true; try { flag = isPortUsing("127.0.0.1", port); } catch (Exception e) { } return flag; }

    public static boolean isPortUsing(String host, int port) throws UnknownHostException { boolean flag = false; InetAddress theAddress = InetAddress.getByName(host); try { new Socket(theAddress, port); flag = true; } catch (IOException e) {} return flag; }

}

启动类代码:

package cn.guangboyuan;

import cn.guangboyuan.command.StartCommand; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.scheduling.annotation.EnableAsync;

@SpringBootApplication @EnableAsync public class SpringboottestApplication {

public static void main(String[] args) {
    new StartCommand(args);
    SpringApplication.run(SpringboottestApplication.class, args);
}

}

启动效果: