Servlet3.0注解开发应用

Servlet3.0注解开发应用

1、简介和测试

  • index.jsp

    <%@ 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>
    
  • HelloServlet

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

2、ServletContainerInitializer

8.2.4 Shared libraries / runtimes pluggability
In addition to supporting fragments and use of annotations one of the requirements
is that not only we be able to plug-in things that are bundled in the WEB-INF/lib
but also plugin shared copies of frameworks - including being able to plug-in to the
web container things like JAX-WS, JAX-RS and JSF that build on top of the web
container. The ServletContainerInitializer allows handling such a use case
as described below.
An instance of the ServletContainerInitializer is looked up via the jar
services API by the container at container / application startup time. The framework
providing an implementation of the ServletContainerInitializer MUST
bundle in the META-INF/services directory of the jar file a file called
javax.servlet.ServletContainerInitializer, as per the jar services API,
that points to the implementation class of the ServletContainerInitializer.
In addition to the ServletContainerInitializer we also have an annotation -
HandlesTypes. The annotation will be applied on the implementation of
ServletContainerInitializer to express interest in classes that are either
annotated with the classes specified in the value or if a class extends / implements
one of those classes anywhere in the classes super types. The container uses the
HandlesTypes annotation to determine when to invoke the initializer’s onStartup
method. When examining the classes of an application to see if they match any of the
criteria specified by the HandlesTypes annotation of a
ServletContainerInitializer, the container may run into class loading
problems if one or more of the application’s optional JAR files are missing. Since the
container is not in a position to decide whether these types of class loading failures
will prevent the application from working correctly, it must ignore them, while at
the same time providing a configuration option that would log them.
If an implementation of ServletContainerInitializer does not have the
@HandlesTypes annotation, or if there are no matches to any of the HandlesType
specified, then it will get invoked once for every application with null as the value
of the Set. This will allow for the initializer to determine based on the resources
available in the application whether it needs to initialize a servet / filter or not.

  • 1、Servlet容器启动会扫描,当前应用里面每一个jar包的
    ServletContainerInitializer的实现
  • 2、提供ServletContainerInitializer的实现类;
    必须绑定在,META-INF/services/javax.servlet.ServletContainerInitializer
    文件的内容就是ServletContainerInitializer实现类的全类名;
  • 总结:容器在启动应用的时候,会扫描当前应用每一个jar包里面
    META-INF/services/javax.servlet.ServletContainerInitializer
    指定的实现类,启动并运行这个实现类的方法;传入感兴趣的类型;
    • ServletContainerInitializer;
      @HandlesTypes;

3、通过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的映射信息
      filter.addMappingForUrlPatterns(EnumSet.of(DispatcherType.REQUEST), true, "/*");
      
   }

}

4、与Springmvc整合

原理:

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
  <modelVersion>4.0.0</modelVersion>
  <groupId>com.atguigu</groupId>
  <artifactId>springmvc-annotation</artifactId>
  <version>0.0.1-SNAPSHOT</version>
  <packaging>war</packaging>
  
  <dependencies>
   <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-webmvc</artifactId>
      <version>4.3.11.RELEASE</version>
   </dependency>
   
   <dependency>
      <groupId>javax.servlet</groupId>
      <artifactId>servlet-api</artifactId>
      <version>3.0-alpha-1</version>
      <scope>provided</scope>
   </dependency>
  </dependencies>

  <build>
   <plugins>
      <plugin>
         <groupId>org.apache.maven.plugins</groupId>
         <artifactId>maven-war-plugin</artifactId>
         <version>2.4</version>
         <configuration>
            <failOnMissingWebXml>false</failOnMissingWebXml>
         </configuration>
      </plugin>
   </plugins>
  </build>
</project>
  • 1、web容器在启动的时候,会扫描每个jar包下的META-INF/services/javax.servlet.ServletContainerInitializer

  • 2、加载这个文件指定的类SpringServletContainerInitializer

  • 3、spring的应用一启动会加载感兴趣的WebApplicationInitializer接口的下的所有组件;

  • 4、并且为WebApplicationInitializer组件创建对象(组件不是接口,不是抽象类)

    • 实现WebApplicationInitializer接口的三个抽象类
      • 1)、AbstractContextLoaderInitializer:创建根容器;createRootApplicationContext();
      • 2)、AbstractDispatcherServletInitializer:
        • 创建一个web的ioc容器;createServletApplicationContext();
        • 创建了DispatcherServlet;createDispatcherServlet();
        • 将创建的DispatcherServlet添加到ServletContext中;
        • getServletMappings();
      • 3)、AbstractAnnotationConfigDispatcherServletInitializer:注解方式配置的DispatcherServlet初始化器
        • 创建根容器:createRootApplicationContext()
          getRootConfigClasses();传入一个配置类
          创建web的ioc容器: createServletApplicationContext();
          获取配置类;getServletConfigClasses();
    @HandlesTypes({WebApplicationInitializer.class})
    public class SpringServletContainerInitializer implements ServletContainerInitializer {
        public SpringServletContainerInitializer() {
        }
    
        public void onStartup(Set<Class<?>> webAppInitializerClasses, ServletContext servletContext) throws ServletException {
            List<WebApplicationInitializer> initializers = new LinkedList();
            Iterator var4;
            if (webAppInitializerClasses != null) {
                var4 = webAppInitializerClasses.iterator();
    
                while(var4.hasNext()) {
                    Class<?> waiClass = (Class)var4.next();
                    if (!waiClass.isInterface() && !Modifier.isAbstract(waiClass.getModifiers()) && WebApplicationInitializer.class.isAssignableFrom(waiClass)) {
                        try {
                            initializers.add((WebApplicationInitializer)waiClass.newInstance());
                        } catch (Throwable var7) {
                            throw new ServletException("Failed to instantiate WebApplicationInitializer class", var7);
                        }
                    }
                }
            }
    
            if (initializers.isEmpty()) {
                servletContext.log("No Spring WebApplicationInitializer types detected on classpath");
            } else {
                servletContext.log(initializers.size() + " Spring WebApplicationInitializers detected on classpath");
                AnnotationAwareOrderComparator.sort(initializers);
                var4 = initializers.iterator();
    
                while(var4.hasNext()) {
                    WebApplicationInitializer initializer = (WebApplicationInitializer)var4.next();
                    initializer.onStartup(servletContext);
                }
    
            }
        }
    }
    

    总结:
    以注解方式来启动SpringMVC;继承AbstractAnnotationConfigDispatcherServletInitializer;
    实现抽象方法指定DispatcherServlet的配置信息;

实现:

//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[]{"/"};
   }

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

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

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

}

5、定制SpringMVC

  • 1)、@EnableWebMvc:开启SpringMVC定制配置功能;
    相当于xml中配置mvc:annotation-driven/;
  • 2)、配置组件(视图解析器、视图映射、静态资源映射、拦截器。。。)
    只要将配置类extends WebMvcConfigurerAdapter
  • 详细内容参考springmvc官方文档
//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...");
   }

}

6、servlet3.0异步请求

在Servlet 3.0之前,Servlet采用Thread-Per-Request的方式处理请求。

即每一次Http请求都由某一个线程从头到尾负责处理。

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

@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("主线程开始。。。"+Thread.currentThread()+"==>"+System.currentTimeMillis());
      AsyncContext startAsync = req.startAsync();
      
      //3、业务逻辑进行异步处理;开始异步处理
      startAsync.start(new Runnable() {
         @Override
         public void run() {
            try {
               System.out.println("副线程开始。。。"+Thread.currentThread()+"==>"+System.currentTimeMillis());
               sayHello();
               startAsync.complete();
               //获取到异步上下文
               AsyncContext asyncContext = req.getAsyncContext();
               //4、获取响应
               ServletResponse response = asyncContext.getResponse();
               response.getWriter().write("hello async...");
               System.out.println("副线程结束。。。"+Thread.currentThread()+"==>"+System.currentTimeMillis());
            } catch (Exception e) {
            }
         }
      });       
      System.out.println("主线程结束。。。"+Thread.currentThread()+"==>"+System.currentTimeMillis());
   }

   public void sayHello() throws Exception{
      System.out.println(Thread.currentThread()+" processing...");
      Thread.sleep(3000);
   }
}

7、SpringMVC异步处理

1、通过返回Callable实现异步处理

@Controller
public class AsyncController {
    
   /**
    * 1、控制器返回Callable
    * 2、Spring异步处理,将Callable 提交到 TaskExecutor 使用一个隔离的线程进行执行
    * 3、DispatcherServlet和所有的Filter退出web容器的线程,但是response 保持打开状态;
    * 4、Callable返回结果,SpringMVC将请求重新派发给容器,恢复之前的处理;
    * 5、根据Callable返回的结果。SpringMVC继续进行视图渲染流程等(从收请求-视图渲染)。
    * 
    * preHandle.../springmvc-annotation/async01
      主线程开始...Thread[http-bio-8081-exec-3,5,main]==>1513932494700
      主线程结束...Thread[http-bio-8081-exec-3,5,main]==>1513932494700
      =========DispatcherServlet及所有的Filter退出线程============================
      
      ================等待Callable执行==========
      副线程开始...Thread[MvcAsync1,5,main]==>1513932494707
      副线程开始...Thread[MvcAsync1,5,main]==>1513932496708
      ================Callable执行完成==========
      
      ================再次收到之前重发过来的请求========
      preHandle.../springmvc-annotation/async01
      postHandle...(Callable的之前的返回值就是目标方法的返回值)
      afterCompletion...
      
      异步的拦截器:
         1)、原生API的AsyncListener
         2)、SpringMVC:实现AsyncHandlerInterceptor;
    * @return
    */
   @ResponseBody
   @RequestMapping("/async01")
   public Callable<String> async01(){
      System.out.println("主线程开始..."+Thread.currentThread()+"==>"+System.currentTimeMillis());
      
      Callable<String> callable = new Callable<String>() {
         @Override
         public String call() throws Exception {
            System.out.println("副线程开始..."+Thread.currentThread()+"==>"+System.currentTimeMillis());
            Thread.sleep(2000);
            System.out.println("副线程开始..."+Thread.currentThread()+"==>"+System.currentTimeMillis());
            return "Callable<String> async01()";
         }
      };
      
      System.out.println("主线程结束..."+Thread.currentThread()+"==>"+System.currentTimeMillis());
      return callable;
   }

}

2、结合消息中间件,可以使用DeferredResult实现异步处理

@Controller
public class AsyncController {
   
   
   @ResponseBody
   @RequestMapping("/createOrder")
   public DeferredResult<Object> createOrder(){
      DeferredResult<Object> deferredResult = new DeferredResult<>((long)3000, "create fail...");
         
      DeferredResultQueue.save(deferredResult);
      
      return deferredResult;
   }
   
   
   @ResponseBody
   @RequestMapping("/create")
   public String create(){
      //创建订单
      String order = UUID.randomUUID().toString();
      DeferredResult<Object> deferredResult = DeferredResultQueue.get();
      deferredResult.setResult(order);
      return "success===>"+order;
   }
}
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();
   }

}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值