zoukankan      html  css  js  c++  java
  • 聊聊、Spring ServletContainerInitializer

      我们平时用 Java 注解很多,例如 @Configuration、@Component、@Service,我们习惯于通过 XML 方式来实现 Web,而用 Java 注解方式来实现 Web 却很少。今天来说下,Spring 是怎么通过 Java 注解方式来实现 Web 和 Mvc。

    一、实现 WebApplicationInitializer 接口  


    public class WebInitializer implements WebApplicationInitializer{
    @Override
    public void onStartup(ServletContext servletContext) throws ServletException {}
    }

    二、继承 AbstractAnnotationConfigDispatcherServletInitializer 


     public class WebMvcInitializer  extends AbstractAnnotationConfigDispatcherServletInitializer {}

    三、容器启动 


      这里必须说下容器,容器就是运行程序的器件。像我们最熟悉的 Tomcat,Jetty 等等,要让容器知道怎么运行你的程序,那么你的程序必须符合容器的规范,或者说遵循规矩。

      Tomcat 是怎么做到的呢?我们不用 web.xml 来配置 Servlet,我们就用 Java 代码,继承 AbstractAnnotationConfigDispatcherServletInitializer 就行了。怎么做到的?

      这里就不去分析 catalina 启动脚本,后面单独分析。

      我们只说下 Spring 代码层面的逻辑,首先 javax.servlet.ServletContainerInitializer 这个接口类。这个类的存在就是为了实现 Java 代码配置。而且是 Servlet 3.0 版本才提供,低于 3.0 都没法用

      Java 代码实现配置。看看源代码 

    package javax.servlet;
    public interface ServletContainerInitializer {
    public void onStartup(Set<Class<?>> c, ServletContext ctx)
            throws ServletException; 
    }
        其中有这样一段注解:
    * Interface which allows a library /runtime to be notified of a web
    * application's startup phase and perform any required programmatic
    * registration of servlets, filters, and listeners in response to it.
    *
    * <p>Implementations of this interface may be annotated with
    * {@link javax.servlet.annotation.HandlesTypes HandlesTypes}, in order to
    * receive (at their {@link #onStartup} method) the Set of application
    * classes that implement, extend, or have been annotated with the class
    * types specified by the annotation. 
    *
    * <p>If an implementation of this interface does not use this annotation,
    * or none of the application classes match the ones specified
    * by the annotation, the container must pass a <tt>null</tt> Set of classes
    * to {@link #onStartup}.
    *
    * <p>Implementations of this interface must be declared by a JAR file
    * resource located inside the <tt>META-INF/services</tt> directory and
    * named for the fully qualified class name of this interface, and will be 
    * discovered using the runtime's service provider lookup mechanism
    * or a container specific mechanism that is semantically equivalent to
    * it. In either case, ServletContainerInitializer services from web
    * fragment JAR files excluded from an absolute ordering must be ignored,
    * and the order in which these services are discovered must follow the
    * application's classloading delegation model. 

      意思是,这个接口是为了程序启动时,实现 Servlet,Filter,Listener 注册的,但是实现这个接口的类必须在一个 Jar包 的 META-INF/services 目录下面的 javax.servlet.ServletContainerInitializer 文件中有定义。而且实现类必须有 @HandlesTypes 注解。什么意思? 

    @HandlesTypes
    @Target({ElementType.TYPE})
    @Retention(RetentionPolicy.RUNTIME)
    public @interface HandlesTypes {
        Class[] value();
    }   

      我们来看看,Spring-web-xxx.jar 包下面的 META-INF/services 文件夹,就有 javax.servlet.ServletContainerInitializer 这么一个文件,里面内容是 org.springframework.web.SpringServletContainerInitializer。这里可能需要了解JDK SPI机制,SPI全称为(Service Provider Interface) ,是JDK内置的一种服务提供发现机制。后面文章单独分析。也就是说,启动容器之后,会调用org.springframework.web.SpringServletContainerInitializer中的onStartup方法。

      那么 org.springframework.web.SpringServletContainerInitializer 这个类又是什么样?来看看

    @HandlesTypes(WebApplicationInitializer.class)
    public class SpringServletContainerInitializer implements ServletContainerInitializer {
    @Override
     public void onStartup(Set<Class<?>> webAppInitializerClasses, ServletContext servletContext)
       throws ServletException {
        List<WebApplicationInitializer> initializers = new LinkedList<WebApplicationInitializer>();
      if (webAppInitializerClasses != null) {
       for (Class<?> waiClass : webAppInitializerClasses) {
        if (!waiClass.isInterface() && !Modifier.isAbstract(waiClass.getModifiers()) &&
          WebApplicationInitializer.class.isAssignableFrom(waiClass)) {
         try {
          initializers.add((WebApplicationInitializer) waiClass.newInstance());
         }
         catch (Throwable ex) {
          throw new ServletException("Failed to instantiate WebApplicationInitializer class", ex);
         }
        }
       }
      }
      if (initializers.isEmpty()) {
       servletContext.log("No Spring WebApplicationInitializer types detected on classpath");
       return;
      }
      servletContext.log(initializers.size() + " Spring WebApplicationInitializers detected on classpath");
      AnnotationAwareOrderComparator.sort(initializers);
      for (WebApplicationInitializer initializer : initializers) {
       initializer.onStartup(servletContext);
      }
       }
    } 

      有这么一段注解:

     * Delegate the {@code ServletContext} to any {@link WebApplicationInitializer}
     * implementations present on the application classpath.
     * <p>Because this class declares @{@code HandlesTypes(WebApplicationInitializer.class)},
     * Servlet 3.0+ containers will automatically scan the classpath for implementations
     * of Spring's {@code WebApplicationInitializer} interface and provide the set of all
     * such types to the {@code webAppInitializerClasses} parameter of this method.
     * <p>If no {@code WebApplicationInitializer} implementations are found on the classpath,
     * this method is effectively a no-op. An INFO-level log message will be issued notifying
     * the user that the {@code ServletContainerInitializer} has indeed been invoked but that
     * no {@code WebApplicationInitializer} implementations were found.
     * <p>Assuming that one or more {@code WebApplicationInitializer} types are detected,
     * they will be instantiated (and <em>sorted</em> if the @{@link
     * org.springframework.core.annotation.Order @Order} annotation is present or
     * the {@link org.springframework.core.Ordered Ordered} interface has been
     * implemented). Then the {@link WebApplicationInitializer#onStartup(ServletContext)}
     * method will be invoked on each instance, delegating the {@code ServletContext} such
     * that each instance may register and configure servlets such as Spring's
     * {@code DispatcherServlet}, listeners such as Spring's {@code ContextLoaderListener},
     * or any other Servlet API componentry such as filters.
     * @param webAppInitializerClasses all implementations of
     * {@link WebApplicationInitializer} found on the application classpath
     * @param servletContext the servlet context to be initialized
     * @see WebApplicationInitializer#onStartup(ServletContext)
     * @see AnnotationAwareOrderComparator

       原来如此,Servlet 3.0+  容器会自动去扫描 classpath 下所有实现了 WebApplicationInitializer 接口的类,如果没有找到相关类,那么 An INFO-level log message will be issued notifying the user,会有一条INFO级别 的日志。

      如果找到了多个实现类,那么都会被实例化,如果实现了 org.springframework.core.Ordered 接口或者添加了 @Order注解,那么就按照顺序来。

    (1)org.springframework.core.Ordered
    public interface Ordered {
     /**
      * Useful constant for the highest precedence value.
      * @see java.lang.Integer#MIN_VALUE
      */
     int HIGHEST_PRECEDENCE = Integer.MIN_VALUE;
     /**
      * Useful constant for the lowest precedence value.
      * @see java.lang.Integer#MAX_VALUE
      */
     int LOWEST_PRECEDENCE = Integer.MAX_VALUE;
     int getOrder();
    }
    (2)@Order
    @Retention(RetentionPolicy.RUNTIME)
    @Target({ElementType.TYPE, ElementType.METHOD, ElementType.FIELD})
    @Documented
    public @interface Order {
     /**
      * The order value.
      * <p>Default is {@link Ordered#LOWEST_PRECEDENCE}.
      * @see Ordered#getOrder()
      */
     int value() default Ordered.LOWEST_PRECEDENCE;
    }

      为什么 @MapperScan 和 @ComponentScan 会被调用?到这里就很清晰了,<mybatis:scan> 和 <context:component-scan> 标签的调用就很简单了,因为我们在 xml 里面配置了。

      下一篇来聊聊,这些注解和标签被调用了,怎么识别的呢?我怎么知道要去扫描这个包下面的类?哪里有定义吗?

  • 相关阅读:
    return
    debug_backtrace final catch
    Exponential Backoff
    2014 MapReduce
    Extract, Transform, Load
    Factorization of a 768-bit RSA modulus
    奇偶校验 汉明码 如果一条信息中包含更多用于纠错的位,且通过妥善安排这些纠错位使得不同的出错位产生不同的错误结果,那么我们就可以找出出错位了。 crc CRC 循环冗余校验
    An Explanation of the Deflate Algorithm
    通过极限手续定义正数的无理指数方幂
    算术根的存在唯一性
  • 原文地址:https://www.cnblogs.com/xums/p/10339328.html
Copyright © 2011-2022 走看看