SpringBoot整合全局异常处理&SpringBoot整合定时任务Task&SpringBoot整合异步任务

============整合全局异常===========

1.整合web访问的全局异常

  如果不做全局异常处理直接访问如果报错,页面会报错500错误,对于界面的显示非常不友好,因此需要做处理。

全局异常处理的类:

package cn.qlq.ExceptionHandler;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.servlet.ModelAndView;

@ControllerAdvice
public class WebExceptionHandler {
    public static final String ERROR_VIEW = "error";

    @ExceptionHandler(value = Exception.class)
    public Object errorHandler(HttpServletRequest reqest, HttpServletResponse response, Exception e) throws Exception {
        e.printStackTrace();
        ModelAndView mav = new ModelAndView();
        mav.addObject("exception", e);
        mav.addObject("url", reqest.getRequestURL());
        mav.setViewName(ERROR_VIEW);
        return mav;
    }
}

 

拦截到异常之后会跳转到error页面error.html:

目录结构:

内容如下:

<!DOCTYPE html>
<html>
<head lang="en">
    <meta charset="UTF-8" />
    <title>捕获全局异常</title>
</head>
<body>
    <h1 style="color: red">发生错误:</h1>
    <div th:text="${url}"></div>
    <div th:text="${exception.message}"></div>
</body>
</html>

 

测试:

2.整合ajax全局异常处理

   ajax异常处理就是捕捉到异常之后返回一个封装的JSON实体,ajax请求根据返回的状态判断是否请求成功。

封装的工具类:

package cn.qlq.utils;

import java.io.Serializable;

public class JSONResultUtil<T> implements Serializable {

    private static final long serialVersionUID = 3637122497350396679L;

    private boolean success;
    private T data;
    private String msg;

    public boolean isSuccess() {
        return success;
    }

    public void setSuccess(boolean success) {
        this.success = success;
    }

    public T getData() {
        return data;
    }

    public void setData(T data) {
        this.data = data;
    }

    public String getMsg() {
        return msg;
    }

    public void setMsg(String msg) {
        this.msg = msg;
    }

    public JSONResultUtil(boolean success) {
        this.success = success;
    }

    public JSONResultUtil(boolean success, String msg) {
        super();
        this.success = success;
        this.msg = msg;
    }

    public JSONResultUtil(boolean success, T data, String msg) {
        super();
        this.success = success;
        this.data = data;
        this.msg = msg;
    }

    /**
     * 返回正确结果不带数据
     * 
     * @return
     */
    public static JSONResultUtil ok() {
        return new JSONResultUtil(true);
    }

    /**
     * 返回错误的结果带错误信息
     * 
     * @param msg
     * @return
     */
    public static JSONResultUtil error(String msg) {
        return new JSONResultUtil(false, msg);
    }

}

 

 

Ajax请求的错误处理器:

package cn.qlq.ExceptionHandler;

import javax.servlet.http.HttpServletRequest;

import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;

import cn.qlq.utils.JSONResultUtil;

@RestControllerAdvice
public class AjaxExceptionHandler {
    @ExceptionHandler(value = Exception.class)
    public JSONResultUtil defaultErrorHandler(HttpServletRequest req, Exception e) throws Exception {

        e.printStackTrace();
        return JSONResultUtil.error(e.getMessage());
    }
}

 

测试:

前台JS:

$.ajax({
        url: "/MySpringboot/err/getAjaxerror",
        type: "POST",
        async: false,
        success: function(data) {
            if(data.success) {
                alert("success");
            } else {
                alert("发生异常:" + data.msg);
            }
        },
        error: function (response, ajaxOptions, thrownError) {
            alert("error");       
        }
    });

后台制造除零异常:

    @RequestMapping("/getAjaxerror")
    @ResponseBody
    public JSONResultUtil getAjaxerror() {
        int a = 1 / 0;
        return JSONResultUtil.ok();
    }

 结果:

3.一个通用的全局异常处理器

  不管是web请求还是ajax请求都可以用它处理。内部根据是否是ajax请求返回对应的数据

package cn.qlq.ExceptionHandler;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;
import org.springframework.web.servlet.ModelAndView;

import cn.qlq.utils.JSONResultUtil;

@RestControllerAdvice
public class MyExceptionHandler {
    public static final String ERROR_VIEW = "error";

    @ExceptionHandler(value = Exception.class)
    public Object errorHandler(HttpServletRequest reqest, HttpServletResponse response, Exception e) throws Exception {

        e.printStackTrace();

        if (isAjax(reqest)) {
            return JSONResultUtil.error(e.getMessage());
        } else {
            ModelAndView mav = new ModelAndView();
            mav.addObject("exception", e);
            mav.addObject("url", reqest.getRequestURL());
            mav.setViewName(ERROR_VIEW);
            return mav;
        }
    }

    /**
     * 根据请求头是否携带X-Requested-With参数判断是否是ajax请求
     * 
     * @param httpRequest
     * @return
     */
    public static boolean isAjax(HttpServletRequest httpRequest) {
        return (httpRequest.getHeader("X-Requested-With") != null
                && "XMLHttpRequest".equals(httpRequest.getHeader("X-Requested-With").toString()));
    }
}

 

 

 

============整合定时任务Task===========

   在springmvc使用的时候使用到的定时任务一般是quartz,也研究过使用过SpringTask。SpringBoot整合Task非常简单。

(1)@EnableScheduling开启task

package cn.qlq.config;

import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.annotation.EnableScheduling;

@Configuration // 通过该注解来表明该类是一个Spring的配置,相当于一个xml文件
@EnableScheduling
public class SpringTask {

}

 

 (2)通过注解的方式使用task即可。

package cn.qlq.task;

import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;

@Component
public class FirstAnnotationJob {
    private static int count;

    @Scheduled(fixedRate = 10000)
    public void cron() {
        try {
            Thread.sleep(2000);
        } catch (InterruptedException e) {
            System.err.println("InterruptedException " + e);
        }
        System.out.println("spring anno task execute times " + count++);
    }
}

 

结果:

spring anno task execute times 0
spring anno task execute times 1
spring anno task execute times 2

....

关于springTask的使用参考:https://www.cnblogs.com/qlqwjy/p/9960706.html

 

============整合异步任务===========

  开启异步任务的方式比较简单 。异步任务的使用场景是:发布消息、发送短信等一些异步任务上,当然异步可以用线程池实现,发送消息可以用MQ框架实现。

(1)@EnableAsync声明开启异步任务

package cn.qlq.config;

import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.annotation.EnableAsync;
import org.springframework.scheduling.annotation.EnableScheduling;

@Configuration // 通过该注解来表明该类是一个Spring的配置,相当于一个xml文件
//开启Task
@EnableScheduling
//开启异步调用方法
@EnableAsync
public class SpringTask {

}

 

(2)编写异步任务,@Component注入spring,异步的方法加上@Async注解即可

package cn.qlq.task;

import java.util.concurrent.Future;

import org.springframework.scheduling.annotation.Async;
import org.springframework.scheduling.annotation.AsyncResult;
import org.springframework.stereotype.Component;

@Component
public class AsyncTask {

    @Async
    public Future<Boolean> doTask11() throws Exception {
        long start = System.currentTimeMillis();
        Thread.sleep(1000);
        long end = System.currentTimeMillis();
        System.out.println("任务1耗时:" + (end - start) + "毫秒,线程名字:" + Thread.currentThread().getName());
        return new AsyncResult<>(true);
    }

    @Async
    public Future<Boolean> doTask22() throws Exception {
        long start = System.currentTimeMillis();
        Thread.sleep(700);
        long end = System.currentTimeMillis();
        System.out.println("任务2耗时:" + (end - start) + "毫秒,线程名字:" + Thread.currentThread().getName());
        return new AsyncResult<>(true);
    }

    @Async
    public Future<Boolean> doTask33() throws Exception {
        long start = System.currentTimeMillis();
        Thread.sleep(600);
        long end = System.currentTimeMillis();
        System.out.println("任务3耗时:" + (end - start) + "毫秒,线程名字:" + Thread.currentThread().getName());
        return new AsyncResult<>(true);
    }
}

 

(3)Controller层调用异步方法:

package cn.qlq.action;

import java.util.concurrent.Future;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import cn.qlq.task.AsyncTask;


@RestController
@RequestMapping("tasks")
public class AsyncTaskController {

    @Autowired
    private AsyncTask asyncTask;

    @RequestMapping("test1")
    public String test1() throws Exception {

        long start = System.currentTimeMillis();

        Future<Boolean> a = asyncTask.doTask11();
        Future<Boolean> b = asyncTask.doTask22();
        Future<Boolean> c = asyncTask.doTask33();

        while (!a.isDone() || !b.isDone() || !c.isDone()) {
            if (a.isDone() && b.isDone() && c.isDone()) {
                break;
            }
        }

        long end = System.currentTimeMillis();

        String times = "任务全部完成,总耗时:" + (end - start) + "毫秒";
        System.out.println(times);

        return times;
    }
}

  上面执行asyncTask.doTaskXX的时候是异步执行的,相当于三个方法异步执行,下面的while循环直到三个方法都执行完毕。

 

测试:

前台访问:

后台控制台:(发现是通过多线程执行)

 

如果去掉上面三个异步方法的@Async注解查看结果:

前台:

 

后台:(单条线程执行)

 

补充:@Async 也可以加在类上,代表该类的所有方法都是异步。

补充:异步任务执行简单理解

1. 首先理解@Async 也是基于AOP,测试如下: 利用AOP的自调用无效特性来测试

    public void method21() {
        log.info("method21 start");
        try {
            TimeUnit.SECONDS.sleep(sleepTime);
        } catch (InterruptedException ignore) {
            // ignore
        }
        log.info("method21 end");

        this.method22();
    }

    @Async
    public void method22() {
        log.info("method22 start");
        try {
            TimeUnit.SECONDS.sleep(sleepTime);
        } catch (InterruptedException ignore) {
            // ignore
        }
        log.info("method22 end");
    }

结果: 可以看大method22 打了注解也无效

2021-02-07 21:47:42.668 | cmdb - INFO | http-nio-8088-exec-2 | com.xm.ggn.test.async.AsyncService | line:131 - method21 start
2021-02-07 21:47:52.672 | cmdb - INFO | http-nio-8088-exec-2 | com.xm.ggn.test.async.AsyncService | line:137 - method21 end
2021-02-07 21:47:52.673 | cmdb - INFO | http-nio-8088-exec-2 | com.xm.ggn.test.async.AsyncService | line:144 - method22 start
2021-02-07 21:48:02.677 | cmdb - INFO | http-nio-8088-exec-2 | com.xm.ggn.test.async.AsyncService | line:150 - method22 end
2021-02-07 21:48:02.677 | cmdb - INFO | http-nio-8088-exec-2 | com.xm.ggn.test.async.AsyncTestController | line:42 - test2 end

2.  查看源码如下

(1) EnableAsync注解源码如下:

@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Import({AsyncConfigurationSelector.class})
public @interface EnableAsync {
    Class<? extends Annotation> annotation() default Annotation.class;

    boolean proxyTargetClass() default false;

    AdviceMode mode() default AdviceMode.PROXY;

    int order() default 2147483647;
}

可以看到是引入AsyncConfigurationSelector 类

(2) AsyncConfigurationSelector 源码如下:

public class AsyncConfigurationSelector extends AdviceModeImportSelector<EnableAsync> {
    private static final String ASYNC_EXECUTION_ASPECT_CONFIGURATION_CLASS_NAME = "org.springframework.scheduling.aspectj.AspectJAsyncConfiguration";

    public AsyncConfigurationSelector() {
    }

    @Nullable
    public String[] selectImports(AdviceMode adviceMode) {
        switch(adviceMode) {
        case PROXY:
            return new String[]{ProxyAsyncConfiguration.class.getName()};
        case ASPECTJ:
            return new String[]{"org.springframework.scheduling.aspectj.AspectJAsyncConfiguration"};
        default:
            return null;
        }
    }
}

  可以看到引入ProxyAsyncConfiguration

(3) ProxyAsyncConfiguration源码如下:

//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by Fernflower decompiler)
//

package org.springframework.scheduling.annotation;

import java.lang.annotation.Annotation;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Role;
import org.springframework.core.annotation.AnnotationUtils;
import org.springframework.util.Assert;

@Configuration
@Role(2)
public class ProxyAsyncConfiguration extends AbstractAsyncConfiguration {
    public ProxyAsyncConfiguration() {
    }

    @Bean(
        name = {"org.springframework.context.annotation.internalAsyncAnnotationProcessor"}
    )
    @Role(2)
    public AsyncAnnotationBeanPostProcessor asyncAdvisor() {
        Assert.notNull(this.enableAsync, "@EnableAsync annotation metadata was not injected");
        AsyncAnnotationBeanPostProcessor bpp = new AsyncAnnotationBeanPostProcessor();
        bpp.configure(this.executor, this.exceptionHandler);
        Class<? extends Annotation> customAsyncAnnotation = this.enableAsync.getClass("annotation");
        if (customAsyncAnnotation != AnnotationUtils.getDefaultValue(EnableAsync.class, "annotation")) {
            bpp.setAsyncAnnotationType(customAsyncAnnotation);
        }

        bpp.setProxyTargetClass(this.enableAsync.getBoolean("proxyTargetClass"));
        bpp.setOrder(((Integer)this.enableAsync.getNumber("order")).intValue());
        return bpp;
    }
}

可以看到主要是注入AsyncAnnotationBeanPostProcessor  到Spring中

(4) AsyncAnnotationBeanPostProcessor  主要代码如下:

    public void setBeanFactory(BeanFactory beanFactory) {
        super.setBeanFactory(beanFactory);
        AsyncAnnotationAdvisor advisor = new AsyncAnnotationAdvisor(this.executor, this.exceptionHandler);
        if (this.asyncAnnotationType != null) {
            advisor.setAsyncAnnotationType(this.asyncAnnotationType);
        }

        advisor.setBeanFactory(beanFactory);
        this.advisor = advisor;
    }

(5) AsyncAnnotationAdvisor 代码如下:  是另一种实现AOP的方式。主要代码是AsyncAnnotationAdvisor构造方法中 构建通知和切入点

//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by Fernflower decompiler)
//

package org.springframework.scheduling.annotation;

import java.lang.annotation.Annotation;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedHashSet;
import java.util.Set;
import java.util.concurrent.Executor;
import java.util.function.Supplier;
import org.aopalliance.aop.Advice;
import org.springframework.aop.Pointcut;
import org.springframework.aop.interceptor.AsyncUncaughtExceptionHandler;
import org.springframework.aop.support.AbstractPointcutAdvisor;
import org.springframework.aop.support.ComposablePointcut;
import org.springframework.aop.support.annotation.AnnotationMatchingPointcut;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.BeanFactoryAware;
import org.springframework.lang.Nullable;
import org.springframework.util.Assert;
import org.springframework.util.ClassUtils;
import org.springframework.util.function.SingletonSupplier;

public class AsyncAnnotationAdvisor extends AbstractPointcutAdvisor implements BeanFactoryAware {
    private Advice advice;
    private Pointcut pointcut;

    public AsyncAnnotationAdvisor() {
        this((Supplier)null, (Supplier)null);
    }

    public AsyncAnnotationAdvisor(@Nullable Executor executor, @Nullable AsyncUncaughtExceptionHandler exceptionHandler) {
        this((Supplier)SingletonSupplier.ofNullable(executor), (Supplier)SingletonSupplier.ofNullable(exceptionHandler));
    }

    public AsyncAnnotationAdvisor(@Nullable Supplier<Executor> executor, @Nullable Supplier<AsyncUncaughtExceptionHandler> exceptionHandler) {
        Set<Class<? extends Annotation>> asyncAnnotationTypes = new LinkedHashSet(2);
        asyncAnnotationTypes.add(Async.class);

        try {
            asyncAnnotationTypes.add(ClassUtils.forName("javax.ejb.Asynchronous", AsyncAnnotationAdvisor.class.getClassLoader()));
        } catch (ClassNotFoundException var5) {
            ;
        }

        this.advice = this.buildAdvice(executor, exceptionHandler);
        this.pointcut = this.buildPointcut(asyncAnnotationTypes);
    }

    public void setAsyncAnnotationType(Class<? extends Annotation> asyncAnnotationType) {
        Assert.notNull(asyncAnnotationType, "'asyncAnnotationType' must not be null");
        Set<Class<? extends Annotation>> asyncAnnotationTypes = new HashSet();
        asyncAnnotationTypes.add(asyncAnnotationType);
        this.pointcut = this.buildPointcut(asyncAnnotationTypes);
    }

    public void setBeanFactory(BeanFactory beanFactory) {
        if (this.advice instanceof BeanFactoryAware) {
            ((BeanFactoryAware)this.advice).setBeanFactory(beanFactory);
        }

    }

    public Advice getAdvice() {
        return this.advice;
    }

    public Pointcut getPointcut() {
        return this.pointcut;
    }

    protected Advice buildAdvice(@Nullable Supplier<Executor> executor, @Nullable Supplier<AsyncUncaughtExceptionHandler> exceptionHandler) {
        AnnotationAsyncExecutionInterceptor interceptor = new AnnotationAsyncExecutionInterceptor((Executor)null);
        interceptor.configure(executor, exceptionHandler);
        return interceptor;
    }

    protected Pointcut buildPointcut(Set<Class<? extends Annotation>> asyncAnnotationTypes) {
        ComposablePointcut result = null;

        AnnotationMatchingPointcut mpc;
        for(Iterator var3 = asyncAnnotationTypes.iterator(); var3.hasNext(); result = result.union(mpc)) {
            Class<? extends Annotation> asyncAnnotationType = (Class)var3.next();
            Pointcut cpc = new AnnotationMatchingPointcut(asyncAnnotationType, true);
            mpc = new AnnotationMatchingPointcut((Class)null, asyncAnnotationType, true);
            if (result == null) {
                result = new ComposablePointcut(cpc);
            } else {
                result.union(cpc);
            }
        }

        return (Pointcut)(result != null ? result : Pointcut.TRUE);
    }
}

可以看到返回的主要的增强类是:buildAdvice 方法中的AnnotationAsyncExecutionInterceptor,也就是主要的AOP逻辑在其父类org.springframework.aop.interceptor.AsyncExecutionInterceptor中。如下:

    @Override
    @Nullable
    public Object invoke(final MethodInvocation invocation) throws Throwable {
        Class<?> targetClass = (invocation.getThis() != null ? AopUtils.getTargetClass(invocation.getThis()) : null);
        Method specificMethod = ClassUtils.getMostSpecificMethod(invocation.getMethod(), targetClass);
        final Method userDeclaredMethod = BridgeMethodResolver.findBridgedMethod(specificMethod);

        AsyncTaskExecutor executor = determineAsyncExecutor(userDeclaredMethod);
        if (executor == null) {
            throw new IllegalStateException(
                    "No executor specified and no default executor set on AsyncExecutionInterceptor either");
        }

        Callable<Object> task = () -> {
            try {
                Object result = invocation.proceed();
                if (result instanceof Future) {
                    return ((Future<?>) result).get();
                }
            }
            catch (ExecutionException ex) {
                handleError(ex.getCause(), userDeclaredMethod, invocation.getArguments());
            }
            catch (Throwable ex) {
                handleError(ex, userDeclaredMethod, invocation.getArguments());
            }
            return null;
        };

        return doSubmit(task, executor, invocation.getMethod().getReturnType());
    }

主要的pointcut切入点是: 根据注解进行判断

    protected Pointcut buildPointcut(Set<Class<? extends Annotation>> asyncAnnotationTypes) {
        ComposablePointcut result = null;

        AnnotationMatchingPointcut mpc;
        for(Iterator var3 = asyncAnnotationTypes.iterator(); var3.hasNext(); result = result.union(mpc)) {
            Class<? extends Annotation> asyncAnnotationType = (Class)var3.next();
            Pointcut cpc = new AnnotationMatchingPointcut(asyncAnnotationType, true);
            mpc = new AnnotationMatchingPointcut((Class)null, asyncAnnotationType, true);
            if (result == null) {
                result = new ComposablePointcut(cpc);
            } else {
                result.union(cpc);
            }
        }

        return (Pointcut)(result != null ? result : Pointcut.TRUE);
    }

(6) 主要的AOP代码如下:

    public Object invoke(final MethodInvocation invocation) throws Throwable {
        Class<?> targetClass = (invocation.getThis() != null ? AopUtils.getTargetClass(invocation.getThis()) : null);
        Method specificMethod = ClassUtils.getMostSpecificMethod(invocation.getMethod(), targetClass);
        final Method userDeclaredMethod = BridgeMethodResolver.findBridgedMethod(specificMethod);

        AsyncTaskExecutor executor = determineAsyncExecutor(userDeclaredMethod);
        if (executor == null) {
            throw new IllegalStateException(
                    "No executor specified and no default executor set on AsyncExecutionInterceptor either");
        }

        Callable<Object> task = () -> {
            try {
                Object result = invocation.proceed();
                if (result instanceof Future) {
                    return ((Future<?>) result).get();
                }
            }
            catch (ExecutionException ex) {
                handleError(ex.getCause(), userDeclaredMethod, invocation.getArguments());
            }
            catch (Throwable ex) {
                handleError(ex, userDeclaredMethod, invocation.getArguments());
            }
            return null;
        };

        return doSubmit(task, executor, invocation.getMethod().getReturnType());
    }

(7) 可以看到主要的Executor配置在TaskExecutionAutoConfiguration,主要的配置在TaskExecutionProperties中

/*
 * Copyright 2012-2019 the original author or authors.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      https://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package org.springframework.boot.autoconfigure.task;

import java.util.concurrent.Executor;

import org.springframework.beans.factory.ObjectProvider;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.autoconfigure.task.TaskExecutionProperties.Shutdown;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.boot.task.TaskExecutorBuilder;
import org.springframework.boot.task.TaskExecutorCustomizer;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Lazy;
import org.springframework.core.task.TaskDecorator;
import org.springframework.core.task.TaskExecutor;
import org.springframework.scheduling.annotation.AsyncAnnotationBeanPostProcessor;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;

/**
 * {@link EnableAutoConfiguration Auto-configuration} for {@link TaskExecutor}.
 *
 * @author Stephane Nicoll
 * @author Camille Vienot
 * @since 2.1.0
 */
@ConditionalOnClass(ThreadPoolTaskExecutor.class)
@Configuration(proxyBeanMethods = false)
@EnableConfigurationProperties(TaskExecutionProperties.class)
public class TaskExecutionAutoConfiguration {

    /**
     * Bean name of the application {@link TaskExecutor}.
     */
    public static final String APPLICATION_TASK_EXECUTOR_BEAN_NAME = "applicationTaskExecutor";

    @Bean
    @ConditionalOnMissingBean
    public TaskExecutorBuilder taskExecutorBuilder(TaskExecutionProperties properties,
            ObjectProvider<TaskExecutorCustomizer> taskExecutorCustomizers,
            ObjectProvider<TaskDecorator> taskDecorator) {
        TaskExecutionProperties.Pool pool = properties.getPool();
        TaskExecutorBuilder builder = new TaskExecutorBuilder();
        builder = builder.queueCapacity(pool.getQueueCapacity());
        builder = builder.corePoolSize(pool.getCoreSize());
        builder = builder.maxPoolSize(pool.getMaxSize());
        builder = builder.allowCoreThreadTimeOut(pool.isAllowCoreThreadTimeout());
        builder = builder.keepAlive(pool.getKeepAlive());
        Shutdown shutdown = properties.getShutdown();
        builder = builder.awaitTermination(shutdown.isAwaitTermination());
        builder = builder.awaitTerminationPeriod(shutdown.getAwaitTerminationPeriod());
        builder = builder.threadNamePrefix(properties.getThreadNamePrefix());
        builder = builder.customizers(taskExecutorCustomizers.orderedStream()::iterator);
        builder = builder.taskDecorator(taskDecorator.getIfUnique());
        return builder;
    }

    @Lazy
    @Bean(name = { APPLICATION_TASK_EXECUTOR_BEAN_NAME,
            AsyncAnnotationBeanPostProcessor.DEFAULT_TASK_EXECUTOR_BEAN_NAME })
    @ConditionalOnMissingBean(Executor.class)
    public ThreadPoolTaskExecutor applicationTaskExecutor(TaskExecutorBuilder builder) {
        return builder.build();
    }

}

3. 重新定义线程前缀以及其他信息

(1) 修改 properties文件

spring.task.execution.thread-name-prefix=myTask-
spring.task.execution.pool.core-size=4
spring.task.execution.pool.max-size=4

(2)测试结果:

2021-02-07 22:42:13.698 | cmdb - INFO | http-nio-8088-exec-1 | com.xm.ggn.test.async.AsyncTestController | line:24 - test start
2021-02-07 22:42:13.732 | cmdb - INFO | http-nio-8088-exec-1 | com.xm.ggn.test.async.AsyncTestController | line:35 - test end
2021-02-07 22:42:13.765 | cmdb - INFO | myTask-3 | com.xm.ggn.test.async.AsyncService | line:22 - method1 start
2021-02-07 22:42:13.765 | cmdb - INFO | myTask-5 | com.xm.ggn.test.async.AsyncService | line:44 - method3 start
2021-02-07 22:42:13.766 | cmdb - INFO | myTask-6 | com.xm.ggn.test.async.AsyncService | line:55 - method4 start
2021-02-07 22:42:13.766 | cmdb - INFO | myTask-4 | com.xm.ggn.test.async.AsyncService | line:33 - method2 start
2021-02-07 22:42:13.924 | cmdb - INFO | http-nio-8088-exec-1 | com.xm.ggn.config.RestResponseAdvice | line:38 - body: null
2021-02-07 22:42:23.766 | cmdb - INFO | myTask-5 | com.xm.ggn.test.async.AsyncService | line:50 - method3 end
2021-02-07 22:42:23.766 | cmdb - INFO | myTask-3 | com.xm.ggn.test.async.AsyncService | line:28 - method1 end
2021-02-07 22:42:23.767 | cmdb - INFO | myTask-5 | com.xm.ggn.test.async.AsyncService | line:66 - method5 start
2021-02-07 22:42:23.767 | cmdb - INFO | myTask-3 | com.xm.ggn.test.async.AsyncService | line:77 - method6 start
2021-02-07 22:42:23.769 | cmdb - INFO | myTask-6 | com.xm.ggn.test.async.AsyncService | line:61 - method4 end
2021-02-07 22:42:23.770 | cmdb - INFO | myTask-6 | com.xm.ggn.test.async.AsyncService | line:88 - method7 start
2021-02-07 22:42:23.771 | cmdb - INFO | myTask-4 | com.xm.ggn.test.async.AsyncService | line:39 - method2 end
2021-02-07 22:42:23.771 | cmdb - INFO | myTask-4 | com.xm.ggn.test.async.AsyncService | line:99 - method8 start
2021-02-07 22:42:33.768 | cmdb - INFO | myTask-3 | com.xm.ggn.test.async.AsyncService | line:83 - method6 end
2021-02-07 22:42:33.768 | cmdb - INFO | myTask-5 | com.xm.ggn.test.async.AsyncService | line:72 - method5 end
2021-02-07 22:42:33.769 | cmdb - INFO | myTask-3 | com.xm.ggn.test.async.AsyncService | line:110 - method9 start
2021-02-07 22:42:33.769 | cmdb - INFO | myTask-5 | com.xm.ggn.test.async.AsyncService | line:121 - method10 start
2021-02-07 22:42:33.771 | cmdb - INFO | myTask-6 | com.xm.ggn.test.async.AsyncService | line:94 - method7 end
2021-02-07 22:42:33.773 | cmdb - INFO | myTask-4 | com.xm.ggn.test.async.AsyncService | line:105 - method8 end

4. 通过代码修改线程池相关信息

 (1) 配置类信息

package com.xm.ggn.test.async.config;

import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;

import java.time.Duration;

/**
 * @Author: qlq
 * @Description
 * @Date: 22:54 2021/2/7
 */
@ConfigurationProperties("my.async")
@Data
// 也可以直接用@Component 注入Spring,这样就不用@EnableConfigurationProperties(AsyncProperties.class) 注入方式
public class AsyncProperties {

    private String threadNamePrefix = "myTest---";

    private Pool pool = new Pool();

    @Data
    public static class Pool {

        private int queueCapacity = Integer.MAX_VALUE;

        private int coreSize = 8;

        private int maxSize = Integer.MAX_VALUE;

        private boolean allowCoreThreadTimeout = true;

        private Duration keepAlive = Duration.ofSeconds(60);
    }
}

 

(2) 修改配置信息

package com.xm.ggn.test.async.config;

import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.annotation.AsyncAnnotationBeanPostProcessor;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;

import java.util.concurrent.ThreadFactory;
import java.util.concurrent.atomic.AtomicInteger;

/**
 * @Author: qlq
 * @Description
 * @Date: 22:57 2021/2/7
 */
@Configuration
// EnableConfigurationProperties注解使使用 @ConfigurationProperties 注解的类生效。实际就是AsyncProperties注入Spring中,或者直接在AsyncProperties类上加@Component也可以
@EnableConfigurationProperties(AsyncProperties.class)
public class AsyncConfiguration {

    public static final String APPLICATION_TASK_EXECUTOR_BEAN_NAME = "applicationTaskExecutor";

    @Bean(name = { APPLICATION_TASK_EXECUTOR_BEAN_NAME,
            AsyncAnnotationBeanPostProcessor.DEFAULT_TASK_EXECUTOR_BEAN_NAME })
    public ThreadPoolTaskExecutor applicationTaskExecutor(AsyncProperties asyncProperties) {
        AsyncProperties.Pool pool = asyncProperties.getPool();
        ThreadPoolTaskExecutor threadPoolTaskExecutor = new ThreadPoolTaskExecutor();
        threadPoolTaskExecutor.setCorePoolSize(pool.getCoreSize());
        threadPoolTaskExecutor.setMaxPoolSize(pool.getMaxSize());
        threadPoolTaskExecutor.setQueueCapacity(pool.getQueueCapacity());
        // 重置线程工厂
        threadPoolTaskExecutor.setThreadFactory(new ThreadFactory() {

            private AtomicInteger count = new AtomicInteger(0);

            @Override
            public Thread newThread(Runnable r) {
                Thread thread = new Thread(r);
                thread.setName(asyncProperties.getThreadNamePrefix() + count.addAndGet(1));
                return thread;
            }
        });
        return threadPoolTaskExecutor;
    }
}

 

(3) 测试:

liqiang@root MINGW64 /e/xiangmu/bs-media (master)
$ curl http://localhost:8088/async/test
  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                 Dload  Upload   Total   Spent    Left  Speed
100    59    0    59    0     0     34      0 --:--:--  0:00:01 --:--:--    35{"success":true,"data":null,"msg":"成功","errorCode":"0"}

 

日志如下:

2021-02-07 23:29:21.067 | cmdb - INFO | http-nio-8088-exec-1 | com.xm.ggn.test.async.AsyncTestController | line:24 - test start
2021-02-07 23:29:21.103 | cmdb - INFO | http-nio-8088-exec-1 | com.xm.ggn.test.async.AsyncTestController | line:35 - test end
2021-02-07 23:29:21.126 | cmdb - INFO | myTest---6 | com.xm.ggn.test.async.AsyncService | line:55 - method4 start
2021-02-07 23:29:21.127 | cmdb - INFO | myTest---7 | com.xm.ggn.test.async.AsyncService | line:66 - method5 start
2021-02-07 23:29:21.127 | cmdb - INFO | myTest---2 | com.xm.ggn.test.async.AsyncService | line:99 - method8 start
2021-02-07 23:29:21.127 | cmdb - INFO | myTest---4 | com.xm.ggn.test.async.AsyncService | line:33 - method2 start
2021-02-07 23:29:21.128 | cmdb - INFO | myTest---5 | com.xm.ggn.test.async.AsyncService | line:44 - method3 start
2021-02-07 23:29:21.127 | cmdb - INFO | myTest---1 | com.xm.ggn.test.async.AsyncService | line:88 - method7 start
2021-02-07 23:29:21.128 | cmdb - INFO | myTest---3 | com.xm.ggn.test.async.AsyncService | line:22 - method1 start
2021-02-07 23:29:21.127 | cmdb - INFO | myTest---8 | com.xm.ggn.test.async.AsyncService | line:77 - method6 start
2021-02-07 23:29:21.202 | cmdb - INFO | http-nio-8088-exec-1 | com.xm.ggn.config.RestResponseAdvice | line:38 - body: null
2021-02-07 23:29:31.146 | cmdb - INFO | myTest---6 | com.xm.ggn.test.async.AsyncService | line:61 - method4 end
2021-02-07 23:29:31.171 | cmdb - INFO | myTest---2 | com.xm.ggn.test.async.AsyncService | line:105 - method8 end
2021-02-07 23:29:31.171 | cmdb - INFO | myTest---7 | com.xm.ggn.test.async.AsyncService | line:72 - method5 end
2021-02-07 23:29:31.174 | cmdb - INFO | myTest---7 | com.xm.ggn.test.async.AsyncService | line:121 - method10 start
2021-02-07 23:29:31.174 | cmdb - INFO | myTest---2 | com.xm.ggn.test.async.AsyncService | line:110 - method9 start
2021-02-07 23:29:31.173 | cmdb - INFO | myTest---4 | com.xm.ggn.test.async.AsyncService | line:39 - method2 end
2021-02-07 23:29:31.177 | cmdb - INFO | myTest---1 | com.xm.ggn.test.async.AsyncService | line:94 - method7 end
2021-02-07 23:29:31.177 | cmdb - INFO | myTest---3 | com.xm.ggn.test.async.AsyncService | line:28 - method1 end
2021-02-07 23:29:31.177 | cmdb - INFO | myTest---5 | com.xm.ggn.test.async.AsyncService | line:50 - method3 end
2021-02-07 23:29:31.178 | cmdb - INFO | myTest---8 | com.xm.ggn.test.async.AsyncService | line:83 - method6 end
2021-02-07 23:29:41.177 | cmdb - INFO | myTest---7 | com.xm.ggn.test.async.AsyncService | line:127 - method10 end
2021-02-07 23:29:41.177 | cmdb - INFO | myTest---2 | com.xm.ggn.test.async.AsyncService | line:116 - method9 end

补充:异步任务中关于异常的处理 

默认的异常处理器是org.springframework.aop.interceptor.SimpleAsyncUncaughtExceptionHandler

//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by FernFlower decompiler)
//

package org.springframework.aop.interceptor;

import java.lang.reflect.Method;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

public class SimpleAsyncUncaughtExceptionHandler implements AsyncUncaughtExceptionHandler {
    private static final Log logger = LogFactory.getLog(SimpleAsyncUncaughtExceptionHandler.class);

    public SimpleAsyncUncaughtExceptionHandler() {
    }

    public void handleUncaughtException(Throwable ex, Method method, Object... params) {
        if (logger.isErrorEnabled()) {
            logger.error("Unexpected exception occurred invoking async method: " + method, ex);
        }

    }
}

(1) 代码抛出异常查看日志

    @Async
    public void method2() {
        log.info("method2 start");
        int i = 1 / 0;

        try {
            TimeUnit.SECONDS.sleep(sleepTime);
        } catch (InterruptedException ignore) {
            // ignore
        }
        log.info("method2 end");
    }

日志如下:

2021-02-08 10:17:29.176 | cmdb - ERROR | myTest---4 | org.springframework.aop.interceptor.SimpleAsyncUncaughtExceptionHandler | line:39 - Unexpected exception occurred invoking async method: public void com.xm.ggn.test.async.AsyncService.method2()
java.lang.ArithmeticException: / by zero
    at com.xm.ggn.test.async.AsyncService.method2(AsyncService.java:34)
    at com.xm.ggn.test.async.AsyncService$$FastClassBySpringCGLIB$$7557d9fb.invoke(<generated>)
    at org.springframework.cglib.proxy.MethodProxy.invoke(MethodProxy.java:218)
    at org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.invokeJoinpoint(CglibAopProxy.java:771)
    at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:163)
    at org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.proceed(CglibAopProxy.java:749)
    at org.springframework.aop.interceptor.AsyncExecutionInterceptor.lambda$invoke$0(AsyncExecutionInterceptor.java:115)
    at java.util.concurrent.FutureTask.run$$$capture(FutureTask.java:266)
    at java.util.concurrent.FutureTask.run(FutureTask.java)
    at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149)
    at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624)
    at java.lang.Thread.run(Thread.java:748)

(2) 定义自己的异常处理器,使用第二种方式注入Executor和异常处理器

package com.xm.ggn.test.async.config;

import lombok.extern.slf4j.Slf4j;
import org.springframework.aop.interceptor.AsyncUncaughtExceptionHandler;

import java.lang.reflect.Method;

/**
 * @author: 乔利强
 * @date: 2021/2/8 10:52
 * @description:
 */
@Slf4j
public class MyExceptionHandler implements AsyncUncaughtExceptionHandler {
    @Override
    public void handleUncaughtException(Throwable throwable, Method method, Object... params) {
        log.error("Async method exception, method:{}, params:{}", method.toString(), params);
        log.error("Unexpected exception occurred invoking async method: " + method, throwable);
    }
}

配置方式二:

package com.xm.ggn.test.async.config;

import lombok.extern.slf4j.Slf4j;
import org.springframework.aop.interceptor.AsyncUncaughtExceptionHandler;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.annotation.AsyncConfigurer;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;

import java.util.concurrent.Executor;
import java.util.concurrent.ThreadFactory;
import java.util.concurrent.atomic.AtomicInteger;

/**
 * @author: 乔利强
 * @date: 2021/2/8 10:19
 * @description:
 */
@Slf4j
@Configuration
@EnableConfigurationProperties(AsyncProperties.class)
public class AsyncConfiguration2 implements AsyncConfigurer {

    @Override
    @Bean
    public AsyncUncaughtExceptionHandler getAsyncUncaughtExceptionHandler() {
        return new MyExceptionHandler();
    }

    @Autowired
    private AsyncProperties asyncProperties;

    @Override
    @Bean
    public Executor getAsyncExecutor() {
        AsyncProperties.Pool pool = asyncProperties.getPool();
        ThreadPoolTaskExecutor threadPoolTaskExecutor = new ThreadPoolTaskExecutor();
        threadPoolTaskExecutor.setCorePoolSize(pool.getCoreSize());
        threadPoolTaskExecutor.setMaxPoolSize(pool.getMaxSize());
        threadPoolTaskExecutor.setQueueCapacity(pool.getQueueCapacity());
        // 重置线程工厂
        threadPoolTaskExecutor.setThreadFactory(new ThreadFactory() {
            private AtomicInteger count = new AtomicInteger(0);

            @Override
            public Thread newThread(Runnable r) {
                Thread thread = new Thread(r);
                thread.setName(asyncProperties.getThreadNamePrefix() + "===" + count.addAndGet(1));
                return thread;
            }
        });
        return threadPoolTaskExecutor;
    }
}

测试结果:

2021-02-08 11:15:13.872 | cmdb - ERROR | myTest---===2 | com.xm.ggn.test.async.config.MyExceptionHandler | line:17 - Async method exception, method:public void com.xm.ggn.test.async.AsyncService.method2(), params:[]
2021-02-08 11:15:13.875 | cmdb - ERROR | myTest---===2 | com.xm.ggn.test.async.config.MyExceptionHandler | line:18 - Unexpected exception occurred invoking async method: public void com.xm.ggn.test.async.AsyncService.method2()
java.lang.ArithmeticException: / by zero
    at com.xm.ggn.test.async.AsyncService.method2(AsyncService.java:34)
    at com.xm.ggn.test.async.AsyncService$$FastClassBySpringCGLIB$$7557d9fb.invoke(<generated>)
    at org.springframework.cglib.proxy.MethodProxy.invoke(MethodProxy.java:218)
    at org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.invokeJoinpoint(CglibAopProxy.java:771)
    at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:163)
    at org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.proceed(CglibAopProxy.java:749)
    at org.springframework.aop.interceptor.AsyncExecutionInterceptor.lambda$invoke$0(AsyncExecutionInterceptor.java:115)
    at java.util.concurrent.FutureTask.run$$$capture(FutureTask.java:266)
    at java.util.concurrent.FutureTask.run(FutureTask.java)
    at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149)
    at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624)
    at java.lang.Thread.run(Thread.java:748)

 

posted @ 2019-02-19 22:36  QiaoZhi  阅读(2591)  评论(0编辑  收藏  举报