53SpringMVC异步处理

SpringMVC 的控制器是单例的,这意味着对同一个类中的处理方法发送的所有请求,都会共享同一个实例,因此控制器方法不适合处理耗时的方法。
当控制器确实需要处理耗时方法时,建议采用异步方法来处理。即让控制器方法返回一个Callable对象,SpirngMVC 会启动新的线程来执行该Callable对象,而Callable对象的call方法的返回值才会被当成真正的处理方法返回值返回给DispatcherServlet,进而进行视图解析。
栗子:

用户请求url:

/query?name=lyx&price=15.2"

控制器:

@Controller
public class UserController
{
    @Resource(name="userService")
    private UserService userService;
    @RequestMapping("/query")
    public Callable<String> queryMethod(Model model)
    {
        Callable<String> result = () ->{
            System.out.println("---------------------"+new Date()+"---------------------");
            //userService.getBooks()是个耗时方法
            model.addAttribute("books",userService.getBooks());
            return "result";
        };
        System.out.println("---------------------"+new Date()+"---------------------");
        return result;
    }
}

耗时方法类:

public class UserServiceImpl implements UserService
{
    @Override
    public List<String> getBooks() {
        try {
            Thread.sleep(6000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        return List.of("疯狂java讲义","轻量级javaweb应用实战","http协议");
    }
}

单单这样还是不过,还要开启异步处理功能,所以要进行一些配置:
开启异步处理支持只需要在web.xml的servlet元素中添加如下配置即可。

<async-supported>true</async-supported>

当然,如果没次执行该控制器方法就会创建一条全新的线程是不好的,所以最好要配置线程池来管理线程,其中SpirngMVC 内置了一个线程池ThreadPoolTaskExecutor

<mvc:annotation-driven>
<!--开启异步支持,并且指定线程池以及超市时间-->
  <mvc:async-support task-executor="taskExecutor"
                       default-timeout="5000">
        <!--配置超时拦截器-->
        <mvc:callable-interceptors>
            <bean class="com.lyx.app.controller.MyCallableInterceptor"/>
        </mvc:callable-interceptors></mvc:async-support>
</mvc:annotation-driven>
<!--配置线程池-->
<bean id="taskExecutor"
      class="org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor"
      p:maxPoolSize="100"
      p:corePoolSize="10"
      p:queueCapacity="1020"
      p:keepAliveSeconds="30"/>

我们看到,上面的async-support不仅指定了线程池还制定了超时拦截器,该拦截器会在连接超时自动触发,写法与普通的控制器一样,不过该拦截器得实现CallableProcessingInterceptor接口,重写handleTimeout方法。

public class MyCallableInterceptor implements CallableProcessingInterceptor
{
    @Override
    public  <T> Object handleTimeout(NativeWebRequest request, Callable<T> task) throws Exception {
    //timeout为逻辑是图名,其解析结果为一个超时提醒的jsp页面
        return "timeout";
    }
}

除了用Callable之外,SpringMVC 自己提供了异步处理策略,使用DeferredResult支持的异步处理
直接看改写的控制器吧:

@Controller
public class UserController
{
    @Resource(name="userService")
    private UserService userService;
    @RequestMapping("/query")
    public DeferredResult<String> queryMethod(Model model)
    {
    //创建DeferredResult,50000(ms)为超时长,timeout为超时默认返回值
        var deferredResult = new DeferredResult<String>(5000L,"timeout");
        new Thread(()->{
            System.out.println("---------------------"+new Date()+"---------------------");
            model.addAttribute("books",userService.getBooks());
            //下面的result才是真正的返回的逻辑视图名
            deferredResult.setResult("result");
        }).start();
        System.out.println("---------------------"+new Date()+"---------------------");
        return deferredResult;
    }
}

其实使用DeferredResult并不一定需要开启新线程,只要在超时之前执行了deferredResult.setResult(“xxx”);即可正常返回正常的逻辑视图名。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值