浅尝Spring注解开发_Servlet 3.0 与 SpringMVC

浅尝Spring注解开发,基于Spring 4.3.12

Servlet3.0新增了注解支持、异步处理,可以省去web.xml文件,异步处理更高效。

浅尝Spring注解开发_自定义注册组件、属性赋值、自动装配

浅尝Spring注解开发_Bean生命周期及执行过程

浅尝Spring注解开发_AOP原理及完整过程分析(源码)

浅尝Spring注解开发_声明式事务及原理

浅尝Spring注解开发_简单理解BeanFactoryPostProcessor、BeanDefinitionRegistryPostProcessor、ApplicationListener

Spring注解开发_Spring容器创建概述

浅尝Spring注解开发_Servlet3.0与SpringMVC

Servlet 测试

创建Web动态工程,Servlet3.0需要Tomcat7.0及以上

注解包括@WebServlet()、@WebListener、@WebFilter

  • 发送请求

    <%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
    <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
    <html>
    <head>
    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
    <title>Insert title here</title>
    </head>
    <body>
    <a href="hello">hello</a>
    </body>
    </html>
  • 处理请求,利用@WebServlet()注解标注

    @WebServlet("/hello")
    public class HelloServlet extends HttpServlet { @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
    // TODO Auto-generated method stub
    //super.doGet(req, resp);
    resp.getWriter().write("hello...");
    }
    }

Servlet容器初始化ServletContainerInitializer

Shared libraries(共享库) / runtimes pluggability(运行时插件能力)

容器在启动应用的时候,会扫描当前应用每一个jar包里面META-INF/services/javax.servlet.ServletContainerInitializer指定的实现类,启动并运行这个实现类的方法,并传入感兴趣的类型

原理

  1. Servlet容器启动会扫描,当前应用里面每一个jar包的ServletContainerInitializer的实现
  2. 提供ServletContainerInitializer的实现类:
    1. 必须绑定在,META-INF/services/javax.servlet.ServletContainerInitializer文件中
    2. 文件的内容就是ServletContainerInitializer实现类的全类名

应用

  1. 创建一个实现ServletContainerInitializer的类

    public class MyServletContainerInitializer implements ServletContainerInitializer {
    
    	@Override
    public void onStartup(Set<Class<?>> arg0, ServletContext sc) throws ServletException {
    // TODO Auto-generated method stub } }
  2. 在src目录(或idea在resources目录下)创建META-INF/services/目录和javax.servlet.ServletContainerInitializer文件,内容为实现类全类名

    com.atguigu.servlet.MyServletContainerInitializer
  3. 实现接口方法,两个参数,一个注解

    1. @HandlesTypes:指定感兴趣的类
    2. ServletContext:当前web的ServletContext
    3. Set<Class<?>>:感兴趣的类的子类集合
    //容器启动的时候会将@HandlesTypes指定的这个类型下面的子类(实现类,子接口等)传递过来;
    //传入感兴趣的类型;
    @HandlesTypes(value={HelloService.class})
    public class MyServletContainerInitializer implements ServletContainerInitializer { /**
    * 应用启动的时候,会运行onStartup方法;
    *
    * Set<Class<?>> arg0:感兴趣的类型的所有子类型;
    * ServletContext arg1:代表当前Web应用的ServletContext;一个Web应用一个ServletContext;
    *
    */
    @Override
    public void onStartup(Set<Class<?>> arg0, ServletContext sc) throws ServletException {
    // TODO Auto-generated method stub
    System.out.println("感兴趣的类型:");
    for (Class<?> claz : arg0) {
    System.out.println(claz);
    } } }
  4. @HandlesTypes的值是感兴趣的类,参数Set<Class<?>> arg0的值是感兴趣的类的子类、子接口、实现类等

    • 创建接口HelloService,并创建多个继承或实现此接口的子类
  5. 应用启动就会获得指定的类型

ServletContext注册Web三大组件

编码方式注册Servlet、Filter、Listener

  1. 使用ServletContext注册Web组件(Servlet、Filter、Listener)
  2. 使用编码的方式,在项目启动的时候给ServletContext里面添加组件
  3. 必须在项目启动的时候来添加
    1. ServletContainerInitializer得到的ServletContext
    2. ServletContextListener监听时得到的ServletContext

应用

  1. 自定义Servlet,继承HttpServlet

    public class UserServlet extends HttpServlet {
    
    	@Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
    // TODO Auto-generated method stub
    resp.getWriter().write("tomcat...");
    } }
  2. 自定义Filter,实现Filter

    public class UserFilter implements Filter {
    
    	@Override
    public void destroy() {
    // TODO Auto-generated method stub } @Override
    public void doFilter(ServletRequest arg0, ServletResponse arg1, FilterChain arg2)
    throws IOException, ServletException {
    // 过滤请求
    System.out.println("UserFilter...doFilter...");
    //放行
    arg2.doFilter(arg0, arg1); } @Override
    public void init(FilterConfig arg0) throws ServletException {
    // TODO Auto-generated method stub } }
  3. 自定义Listener,实现ServletContextListener

    /**
    * 监听项目的启动和停止
    */
    public class UserListener implements ServletContextListener { //监听ServletContext销毁
    @Override
    public void contextDestroyed(ServletContextEvent arg0) {
    // TODO Auto-generated method stub
    System.out.println("UserListener...contextDestroyed...");
    } //监听ServletContext启动初始化
    @Override
    public void contextInitialized(ServletContextEvent arg0) {
    // TODO Auto-generated method stub //在这里项目启动前也可以得到的ServletContext
    ServletContext servletContext = arg0.getServletContext();
    System.out.println("UserListener...contextInitialized...");
    } }
  4. 注册

    • ServletContainerInitializer的一个参数是ServletContext
    • 另外在上面Listener监听时也能得到ServletContext
    //容器启动的时候会将@HandlesTypes指定的这个类型下面的子类(实现类,子接口等)传递过来;
    //传入感兴趣的类型;
    @HandlesTypes(value={HelloService.class})
    public class MyServletContainerInitializer implements ServletContainerInitializer { /**
    * 应用启动的时候,会运行onStartup方法;
    *
    * Set<Class<?>> arg0:感兴趣的类型的所有子类型;
    * ServletContext arg1:代表当前Web应用的ServletContext;一个Web应用一个ServletContext;
    *
    * 1)、使用ServletContext注册Web组件(Servlet、Filter、Listener)
    * 2)、使用编码的方式,在项目启动的时候给ServletContext里面添加组件;
    * 必须在项目启动的时候来添加;
    * 1)、ServletContainerInitializer得到的ServletContext;
    * 2)、ServletContextListener得到的ServletContext;
    */
    @Override
    public void onStartup(Set<Class<?>> arg0, ServletContext sc) throws ServletException {
    // TODO Auto-generated method stub
    System.out.println("感兴趣的类型:");
    for (Class<?> claz : arg0) {
    System.out.println(claz);
    } //注册组件 ServletRegistration
    ServletRegistration.Dynamic servlet = sc.addServlet("userServlet", new UserServlet());
    //配置servlet的映射信息
    servlet.addMapping("/user"); //注册Listener
    sc.addListener(UserListener.class); //注册Filter FilterRegistration
    FilterRegistration.Dynamic filter = sc.addFilter("userFilter", UserFilter.class);
    //配置Filter的映射信息,另一个方法参数是直接使用Servlet拦截
    filter.addMappingForUrlPatterns(EnumSet.of(DispatcherType.REQUEST), true, "/*"); } }

Servlet与SpringMVC整合

原理分析

web容器启动,扫描指定位置类名,发现感兴趣的类,然后从这个类的子类中选择创建组件对象

  1. 导入jar包

            <dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-webmvc</artifactId>
    <version>4.3.11.RELEASE</version>
    </dependency>
    <dependency>
    <groupId>javax.servlet</groupId>
    <artifactId>javax.servlet-api</artifactId>
    <version>3.0.1</version>
    <scope>provided</scope>
    </dependency>
  2. web容器在启动的时候,会扫描每个jar包下的META-INF/services/javax.servlet.ServletContainerInitializer,打开spring-web.jar目录下的文件

    org.springframework.web.SpringServletContainerInitializer
  3. 加载这个文件指定的类SpringServletContainerInitializer,发现@HandlesTypes注解中感兴趣的类是WebApplicationInitializer

  4. Spring的应用一启动会加载感兴趣的WebApplicationInitializer接口的下的所有组件,并且为WebApplicationInitializer组件创建对象(组件不是接口,不是抽象类)

    @HandlesTypes(WebApplicationInitializer.class)
    public class SpringServletContainerInitializer implements ServletContainerInitializer {
    @Override
    public void onStartup(Set<Class<?>> webAppInitializerClasses, ServletContext servletContext)
    throws ServletException {
    //...
    //防御:一些 servlet 容器为我们提供了无效的类,不管@HandlesTypes 说什么......
    if (!waiClass.isInterface() && !Modifier.isAbstract(waiClass.getModifiers()) &&
    WebApplicationInitializer.class.isAssignableFrom(waiClass)) {
    initializers.add((WebApplicationInitializer) waiClass.newInstance());
    }
    //...
    }
  5. 关于感兴趣的类WebApplicationInitializer的子类(子接口、抽象)三个抽象类:

    1. AbstractContextLoaderInitializer:创建根容器;createRootApplicationContext()
    2. AbstractDispatcherServletInitializer:
      1. 创建一个web的ioc容器:createServletApplicationContext()
      2. 创建了DispatcherServlet:createDispatcherServlet()
      3. 将创建的DispatcherServlet添加到ServletContext中:servletContext.addServlet(servletName, dispatcherServlet)
      4. 保留一个抽象方法待实现,返回将注册 DispatcherServlet 的名称:getServletMappings()
    3. AbstractAnnotationConfigDispatcherServletInitializer:注解方式配置的DispatcherServlet初始化器
      1. 创建根容器:createRootApplicationContext()
      2. 保留一个抽象方法待实现,传入一个配置类:getRootConfigClasses()
      3. 创建web的ioc容器: createServletApplicationContext()
      4. 保留一个抽象方法待实现,获取配置类:getServletConfigClasses()
  6. 总结:以注解方式来启动SpringMVC,可以选择其中一个抽象类,例如继承AbstractAnnotationConfigDispatcherServletInitializer,实现抽象方法指定DispatcherServlet的配置信息

整合

SpringMVC官方文档Web on Servlet Stack (spring.io)

Spring关于DispatcherServlet的解释,将DispatcherServlet分为具有上下继承关系的两部分,其中可在多个实例之间共享的Root WebApplicationContext包含中间服务层Service、数据源Repositories,和可在特定于Servlet的子项被覆盖的控制器Controllers、视图解析器ViewResolver、处理器映射器HandlerMapping其他与web相关的bean)

  讲过上节分析,选择感兴趣的类WebApplicationInitializer的其中一个子接口AbstractAnnotationConfigDispatcherServletInitializer来实现

  1. 实现AbstractAnnotationConfigDispatcherServletInitializer接口并配置

    //web容器启动的时候创建对象;调用方法来初始化容器以前前端控制器
    public class MyWebAppInitializer extends AbstractAnnotationConfigDispatcherServletInitializer { //获取根容器的配置类;(Spring的配置文件) 父容器;
    @Override
    protected Class<?>[] getRootConfigClasses() {
    // TODO Auto-generated method stub
    return new Class<?>[]{RootConfig.class};
    } //获取web容器的配置类(SpringMVC配置文件) 子容器;
    @Override
    protected Class<?>[] getServletConfigClasses() {
    // TODO Auto-generated method stub
    return new Class<?>[]{AppConfig.class};
    } //获取DispatcherServlet的映射信息
    // /:拦截所有请求(包括静态资源(xx.js,xx.png)),但是不包括*.jsp;
    // /*:拦截所有请求;连*.jsp页面都拦截;jsp页面是tomcat的jsp引擎解析的;
    @Override
    protected String[] getServletMappings() {
    // TODO Auto-generated method stub
    return new String[]{"/"};
    } }
  2. 创建两个配置

    1. Spring的配置文件,根容器

      //Spring的容器不扫描controller;父容器
      @ComponentScan(value="com.atguigu",excludeFilters={
      @Filter(type=FilterType.ANNOTATION,classes={Controller.class})
      })
      public class RootConfig { }
    2. SpringMVC配置文件,web容器

      //SpringMVC只扫描Controller;子容器
      //useDefaultFilters=false 禁用默认的过滤规则;
      @ComponentScan(value="com.atguigu",includeFilters={
      @Filter(type=FilterType.ANNOTATION,classes={Controller.class})
      },useDefaultFilters=false)
      public class AppConfig extends WebMvcConfigurerAdapter { }
  3. 创建业务

    1. Service

      @Service
      public class HelloService implements ApplicationContextAware { public String sayHello(String name){ return "Hello "+name;
      } }
    2. Controller

      @Controller
      public class HelloController { @Autowired
      HelloService helloService; @ResponseBody
      @RequestMapping("/hello")
      public String hello(){
      String hello = helloService.sayHello("tomcat..");
      return hello;
      }
      }

定制SpringMVC

与之前使用xml文件相似,可以配置视图解析器、静态资源访问、拦截器

可参考Spring文档Web on Servlet Stack (spring.io)

  1. @EnableWebMvc:开启SpringMVC定制配置功能,与xml中的<mvc:annotation-driven>相同

  2. 配置组件,实现WebMvcConfigurer(Spring4中用WebMvcConfigurerAdapter)

    //SpringMVC只扫描Controller;子容器
    //useDefaultFilters=false 禁用默认的过滤规则;
    @ComponentScan(value="com.atguigu",includeFilters={
    @Filter(type=FilterType.ANNOTATION,classes={Controller.class})
    },useDefaultFilters=false)
    @EnableWebMvc
    public class AppConfig extends WebMvcConfigurerAdapter { //定制 //视图解析器
    @Override
    public void configureViewResolvers(ViewResolverRegistry registry) {
    // TODO Auto-generated method stub
    //默认所有的页面都从 /WEB-INF/ xxx .jsp
    //registry.jsp();
    registry.jsp("/WEB-INF/views/", ".jsp");
    } //静态资源访问
    @Override
    public void configureDefaultServletHandling(DefaultServletHandlerConfigurer configurer) {
    // TODO Auto-generated method stub
    configurer.enable();
    } //拦截器
    @Override
    public void addInterceptors(InterceptorRegistry registry) {
    // TODO Auto-generated method stub
    //super.addInterceptors(registry);
    registry.addInterceptor(new MyFirstInterceptor()).addPathPatterns("/**");
    } }

    拦截器

    public class MyFirstInterceptor implements HandlerInterceptor {
    
    	//目标方法运行之前执行
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
    throws Exception {
    // TODO Auto-generated method stub
    System.out.println("preHandle..."+request.getRequestURI());
    return true;
    } //目标方法执行正确以后执行
    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler,
    ModelAndView modelAndView) throws Exception {
    // TODO Auto-generated method stub
    System.out.println("postHandle..."); } //页面响应以后执行
    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex)
    throws Exception {
    // TODO Auto-generated method stub
    System.out.println("afterCompletion...");
    } }

Servlet3.0 异步请求

  在Servlet3.0之前,Servlet才采用Thread—Per—Request的方式处理请求。即每一次http请求都由一个线程从头到尾负责处理。

  如果一个请求需要进行IO操作,比如访问数据库,调用第三方服务接口等,那么其所对应的线程将同步地等待IO操作完成,而IO操作是非常慢的,所以此时的线程并不能及时的释放回线程池以供后续使用,在并发量越来越大的情况下,这将带来严重的新能问题。即便像Spring、struts这样的高层框架也脱离不了这样的问题,因为他们都是建立在Servlet之上的。为了解决这样的问题,Servlet3.0引入了一步处理,然后在Servlet3.1中又引入了非阻塞IO来进行增加一步处理的性能。

同步请求

每次请求时从线程池获取一个线程,然后处理请求,直到处理完成,仍由这个线程响应。如果一个线程等待时间过长,其他请求就会占满线程池

验证

  1. 发送一个请求,获取处理请求前后的线程

    @WebServlet("/hello")
    public class HelloServlet extends HttpServlet { @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
    // TODO Auto-generated method stub
    //super.doGet(req, resp);
    System.out.println(Thread.currentThread()+" start...");
    try {
    sayHello();
    } catch (Exception e) {
    e.printStackTrace();
    }
    resp.getWriter().write("hello...");
    System.out.println(Thread.currentThread()+" end...");
    } public void sayHello() throws Exception{
    System.out.println(Thread.currentThread()+" processing...");
    Thread.sleep(3000);
    } }
  2. 输出

    • 整个请求都是由一个线程处理
    Thread[http-nio-8888-exec-10,5,main] start...
    Thread[http-nio-8888-exec-10,5,main] processing...
    Thread[http-nio-8888-exec-10,5,main] end...

异步请求

当请求过来时,由主线程池中一条线程处理,当开始异步处理时,主线程就结束返回,等待其他请求,异步处理线程的一条线程同时开始处理请求。

验证

  1. 异步对象AsyncContext可以开始和完成异步处理,同时开启注解@WebServlet(value="/xxx",asyncSupported=true)

    @WebServlet(value="/async",asyncSupported=true)
    public class HelloAsyncServlet extends HttpServlet { @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
    //1、支持异步处理asyncSupported=true
    //2、开启异步模式
    //主线程开始
    System.out.println("main thread start..."+Thread.currentThread()+"==>"+System.currentTimeMillis());
    AsyncContext startAsync = req.startAsync(); //3、业务逻辑进行异步处理;开始异步处理
    startAsync.start(new Runnable() {
    @Override
    public void run() {
    try {
    //副线程开始
    System.out.println("vice thread start..."+Thread.currentThread()+"==>"+System.currentTimeMillis());
    sayHello();
    startAsync.complete();
    //获取到异步上下文
    //AsyncContext asyncContext = req.getAsyncContext();
    //4、获取响应
    //使用asyncContext.getResponse()副线程不能结束,仍使用startAsync.getResponse()获取响应
    //ServletResponse response = asyncContext.getResponse();
    ServletResponse response = startAsync.getResponse();
    response.getWriter().write("hello async...");
    //副线程结束
    System.out.println("vice thread end..."+Thread.currentThread()+"==>"+System.currentTimeMillis());
    } catch (Exception e) {
    }
    }
    });
    //主线程结束
    System.out.println("main thread end..."+Thread.currentThread()+"==>"+System.currentTimeMillis());
    } //处理过程
    public void sayHello() throws Exception{
    System.out.println(Thread.currentThread()+" processing...");
    Thread.sleep(3000);
    }
    }
  2. 输出

    • 主线程开启后遇到异步处理立刻结束
    • 主线程结束同时副线程开始处理异步请求
    //主线程
    main thread start...Thread[http-nio-8888-exec-5,5,main]==>1651997065639
    main thread end...Thread[http-nio-8888-exec-5,5,main]==>1651997065648
    //副线程
    vice thread start...Thread[http-nio-8888-exec-7,5,main]==>1651997065648
    Thread[http-nio-8888-exec-7,5,main] processing...
    vice thread end...Thread[http-nio-8888-exec-7,5,main]==>1651997068656

SpringMVC异步处理

参考SpringMVC官方文档Web on Servlet Stack (spring.io)

返回Callable

Controller返回Callable类型的返回值,异步处理在Callable类中进行

过程

Callable处理过程

  1. 控制器返回Callable
  2. SpringMVC异步处理,将Callable 提交到 TaskExecutor 使用一个隔离的线程进行执行
  3. DispatcherServlet和所有的Filter退出web容器的线程,但是response 保持打开状态
  4. Callable返回结果,SpringMVC将请求重新派发给容器,恢复之前的处理
  5. 根据Callable返回的结果。SpringMVC继续进行视图渲染流程等(从收请求-视图渲染)
  6. 对于异步处理,普通拦截器不能拦截异步中的代码,使用异步拦截器:
    1. 原生API的AsyncListener
    2. SpringMVC:实现AsyncHandlerInterceptor

应用

  1. 异步处理

    @Controller
    public class AsyncController { @ResponseBody
    @RequestMapping("/async01")
    public Callable<String> async01(){
    //主线程开始
    System.out.println("main thread start..."+Thread.currentThread()+"==>"+System.currentTimeMillis()); Callable<String> callable = new Callable<String>() {
    @Override
    public String call() throws Exception {
    //副线程开始
    System.out.println("vice thread start..."+Thread.currentThread()+"==>"+System.currentTimeMillis()); //处理业务
    Thread.sleep(2000);
    //副线程开始
    System.out.println("vice thread end..."+Thread.currentThread()+"==>"+System.currentTimeMillis()); return "Callable<String> async01()";
    }
    }; //主线程结束
    System.out.println("main thread end..."+Thread.currentThread()+"==>"+System.currentTimeMillis()); return callable;
    }
    }
  2. 输出

    • 主线程和副线程不是同一线程
    • 拦截器拦截了两次同一个请求
    • 上面两处都是Callable的原因,Callable会另起一个线程,并且结束前端控制器,等异步处理完在恢复前端控制器继续执行,所以有两次拦截器,并且要使用异步拦截器
    //拦截器
    preHandle.../springmvc_annotation_war/async01 //主线程,执行完DispatcherServlet及所有的Filter退出线程
    main thread start...Thread[http-nio-8888-exec-10,5,main]==>1652003199856
    main thread end...Thread[http-nio-8888-exec-10,5,main]==>1652003199860 //副线程,等待Callable执行
    vice thread start...Thread[MvcAsync1,5,main]==>1652003199873
    vice thread end...Thread[MvcAsync1,5,main]==>1652003201875 //拦截器,再次收到之前重发过来的请求
    preHandle.../springmvc_annotation_war/async01
    postHandle...
    afterCompletion...

DeferredResult

上面的Callable使用与简单场景,可以处理当前简单业务,对于多应用复杂业务可以使用DeferredResult类型,在当前应用处理请求,然后继续向下执行,当其他应用完成请求之后,通知DeferredResult就可以响应结果结束请求。对应下图:

应用1是当前应用,负责接受请求,中间隔着其他组件,应用2负责异步处理请求,然后将结果返回给当前应用1。

官方文档

  在 Servlet 容器中启用异步请求处理功能后,控制器方法可以使用 DeferredResult 包装任何支持的控制器方法返回值,如以下示例所示:

@GetMapping("/quotes")
@ResponseBody
public DeferredResult<String> quotes() {
DeferredResult<String> deferredResult = new DeferredResult<String>();
// Save the deferredResult somewhere..
// 将deferredResult保存到某个地方。
return deferredResult;
} // From some other thread...
// 从其他线程...
deferredResult.setResult(result);

  控制器可以从不同的线程异步生成返回值 — 例如,响应外部事件(JMS 消息)、计划任务或其他事件。

应用

  1. 请求

    • 在当前应用接收请求,然后将DeferredResult保存起来
    • 然后返回
    @Controller
    public class AsyncController { @ResponseBody
    @RequestMapping("/createOrder")
    public DeferredResult<Object> createOrder(){
    //使用超时值和要使用的默认结果创建DeferredResult
    DeferredResult<Object> deferredResult = new DeferredResult<>((long)3000, "create fail..."); DeferredResultQueue.save(deferredResult); return deferredResult;
    }
    }
  2. 保存DeferredResult队列

    • 有存取功能
    public class DeferredResultQueue {
    
    	private static Queue<DeferredResult<Object>> queue = new ConcurrentLinkedQueue<DeferredResult<Object>>();
    
    	public static void save(DeferredResult<Object> deferredResult){
    queue.add(deferredResult);
    } public static DeferredResult<Object> get( ){
    return queue.poll();
    } }
  3. 模拟另一个应用,异步处理业务

    • 真实请情况可以是另一个应用或线程
    	@ResponseBody
    @RequestMapping("/create")
    public String create(){
    //创建订单
    String order = UUID.randomUUID().toString();
    //取出DeferredResult
    DeferredResult<Object> deferredResult = DeferredResultQueue.get();
    //放入结果,同时应用1完成异步处理
    deferredResult.setResult(order);
    return "success===>"+order;
    }
  4. 结果

    成功
    52917142-3a12-4df5-9c89-258cbf0d0c3a
    success===>52917142-3a12-4df5-9c89-258cbf0d0c3a
    失败
    create fail...
  5. DeferredResult部分文档

    DeferredResult 提供了使用 Callable 进行异步请求处理的替代方法。 虽然代表应用程序同时执行 Callable,但使用 DeferredResult 应用程序可以从其选择的线程中生成结果。
    子类可以扩展此类以轻松地将附加数据或行为与 DeferredResult 关联。 例如,可能希望通过扩展类并为用户添加一个附加属性来关联用于创建 DeferredResult 的用户。 这样,以后可以很容易地访问用户,而无需使用数据结构来进行映射。 将附加行为与此类关联的示例可以通过扩展类以实现附加接口来实现。 例如,您可能希望实现 Comparable 以便在将 DeferredResult 添加到 PriorityQueue 时以正确的顺序对其进行处理。 onCompletion
    public void onCompletion(Runnable callback)
    注册代码在异步请求完成时调用。
    当异步请求因任何原因(包括超时和网络错误)完成时,从容器线程调用此方法。
    这对于检测延迟实例不再可用很有用。 public boolean setResult(T result)
    设置DeferredResult的值并处理它。

更详细的文章推荐:

DeferredResult的使用场景及用法 - 胡桃同学的个人空间 - OSCHINA - 中文开源技术交流社区

浅尝Spring注解开发_Servlet3.0与SpringMVC的更多相关文章

  1. 浅尝Spring注解开发_AOP原理及完整过程分析(源码)

    浅尝Spring注解开发_AOP原理及完整过程分析(源码) 浅尝Spring注解开发,基于Spring 4.3.12 分析AOP执行过程及源码,包含AOP注解使用.AOP原理.分析Annotation ...

  2. 浅尝Spring注解开发_自定义注册组件、属性赋值、自动装配

    Spring注解开发 浅尝Spring注解开发,基于Spring 4.3.12 包含自定义扫描组件.自定义导入组件.手动注册组件.自动注入方法和参数.使用Spring容器底层组件等 配置 @Confi ...

  3. 浅尝Spring注解开发_Bean生命周期及执行过程

    Spring注解开发 浅尝Spring注解开发,基于Spring 4.3.12 包含Bean生命周期.自定义初始化方法.Debug BeanPostProcessor执行过程及在Spring底层中的应 ...

  4. Spring注解开发_Spring容器创建概述

    浅尝Spring注解开发_Spring容器创建概述 浅尝Spring注解开发,基于Spring 4.3.12 概述Spring容器创建的过程,包括12个方法的执行 浅尝Spring注解开发_自定义注册 ...

  5. JAVAEE——SSH项目实战06:统计信息管理、Spring注解开发和EasyUI

    作者: kent鹏 转载请注明出处: http://www.cnblogs.com/xieyupeng/p/7190925.html 一.统计信息管理   二.Spring注解开发 1.service ...

  6. spring注解开发:容器中注册组件方式

    1.包扫描+组件标注注解 使用到的注解如下,主要针对自己写的类 @Controller @Service @Repository @Component @ComponentScan 参考 spring ...

  7. Spring注解开发系列专栏

    这个系列主要是讲Spring注解的使用,可以为后面SpringBoot的学习带来一定的帮助.我觉得从Spring直接过度到SpringBoot还是有点快,还是得需要一个演变的过程.从Spring开发, ...

  8. Spring注解开发系列Ⅵ --- AOP&事务

    注解开发 --- AOP AOP称为面向切面编程,在程序开发中主要用来解决一些系统层面上的问题,比如日志,事务,权限等待,Struts2的拦截器设计就是基于AOP的思想,横向重复,纵向抽取.详细的AO ...

  9. Annotation(三)——Spring注解开发

    Spring框架的核心功能IoC(Inversion of Control),也就是通过Spring容器进行对象的管理,以及对象之间组合关系的映射.通常情况下我们会在xml配置文件中进行action, ...

随机推荐

  1. springboot-数据访问之jdbc

    官网的starthttps://docs.spring.io/spring-boot/docs/current/reference/htmlsingle/#using-boot-starter 111 ...

  2. JavaScript 变动事件

    变动事件,当用户修改了DOM结构(添加或删除元素节点)后发生. 任何时候当元素被添加到DOM中或从DOM中移除时,DOM的结构就发生了变化,而这种变化就会触动变动事件. 1 <html> ...

  3. Eclipse 从SVN检出项目之《文件夹 “” 已不存在 》

    1.eclipse 从svn检出项目 报文件夹不存在, 参考博客 https://blog.csdn.net/wenbsu/article/details/80965680 2.You need to ...

  4. Linux 安装jdk1.8

    Linux安装jdk1.8 总结一句话就是:下载jdk1.8 ==> 解压 ==> 配置环境变量. 一.  jdk的下载,这里有两种方法: 1.去Oracle官网下载. 2.jdk1.8的 ...

  5. C++ pair的基本用法总结

    1,pair的应用 pair是将2个数据组合成一组数据,当需要这样的需求时就可以使用pair,如stl中的map就是将key和value放在一起来保存.另一个应用是,当一个函数需要返回2个数据的时候, ...

  6. CAN总线系列讲座第五讲——CAN总线硬件电路设计

    CAN总线系列讲座第五讲--CAN总线硬件电路设计一  实战学习背景 CAN总线节点的硬件构成方案有两种,其中的方案:(1).MCU控制器+独立CAN控制器+CAN收发器.独立CAN控制器如SJA10 ...

  7. 全面系统讲解CSS工作应用+面试一步搞定

    [TOC] 一.课程介绍 二.HTML基础强化 html常见元素和理解 html常见元素分类 head区元素:(不会在页面上留下元素) * meta * title * style * link * ...

  8. 前端面试题整理——VUE双向绑定原理

    VUE2.0和3.0数据双向绑定的原理,并说出其区别. 代码: <!DOCTYPE html> <html lang="en"> <head> ...

  9. Spark项目应用-电子商务大数据分析总结

    一. 数据采集(要求至少爬取三千条记录,时间跨度超过一星期)数据采集到本地文件内容   爬取详见:python爬取京东评论   爬取了将近20000条数据,156个商品种类,用时2个多小时,期间中断数 ...

  10. python-for循环跳过第一行

    代码: for i in data[1:]: 即可跳过第一行