我们平时用 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 里面配置了。

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

聊聊、Spring ServletContainerInitializer的更多相关文章

  1. 从架构演进的角度聊聊Spring Cloud都做了些什么?

    Spring Cloud作为一套微服务治理的框架,几乎考虑到了微服务治理的方方面面,之前也写过一些关于Spring Cloud文章,主要偏重各组件的使用,本次分享主要解答这两个问题:Spring Cl ...

  2. 聊聊 Spring Boot 2.x 那些事儿

    本文目录: 即将的 Spring 2.0 - Spring 2.0 是什么 - 开发环境和 IDE - 使用 Spring Initializr 快速入门 Starter 组件 - Web:REST ...

  3. 朱晔和你聊Spring系列S1E1:聊聊Spring家族的几大件

    朱晔和你聊Spring系列S1E1:聊聊Spring家族的几大件 [下载本文PDF进行阅读] Spring家族很庞大,从最早先出现的服务于企业级程序开发的Core.安全方面的Security.到后来的 ...

  4. 从架构演进的角度聊聊Spring Cloud都做了些什么

    1.从架构演进的角度聊聊Spring Cloud都做了些什么?2.中小型互联网公司微服务实践-经验和教训3.Spring Cloud在国内中小型公司能用起来吗?

  5. 聊聊 Spring Boot 2.0 的 WebFlux

    聊聊 Spring Boot 2.0 的 WebFlux## 前言 对照下 Spring Web MVC ,Spring Web MVC 是基于 Servlet API 和 Servlet 容器设计的 ...

  6. 【小家Spring】聊聊Spring中的数据绑定 --- BeanWrapper以及内省Introspector和PropertyDescriptor

    #### 每篇一句 > 千古以来要饭的没有要早饭的,知道为什么吗? #### 相关阅读 [[小家Spring]聊聊Spring中的数据转换:Converter.ConversionService ...

  7. 【小家Spring】聊聊Spring中的数据绑定 --- DataBinder本尊(源码分析)

    每篇一句 唯有热爱和坚持,才能让你在程序人生中屹立不倒,切忌跟风什么语言或就学什么去~ 相关阅读 [小家Spring]聊聊Spring中的数据绑定 --- 属性访问器PropertyAccessor和 ...

  8. 聊聊spring之bean对象的实例化过程

    在spring实例化 之前bean对象封装成 beanDefinition 对象 想了解详情的请参考上一篇文章 好了 我们聊聊 Bean 的实例化过程的几个重要角色 BeanDefinitionReg ...

  9. 【Spring注解驱动开发】聊聊Spring注解驱动开发那些事儿!

    写在前面 今天,面了一个工作5年的小伙伴,面试结果不理想啊!也不是我说,工作5年了,问多线程的知识:就只知道继承Thread类和实现Runnable接口!问Java集合,竟然说HashMap是线程安全 ...

随机推荐

  1. CentOS 6下PXE+Kickstart无人值守安装操作系统

    一.简介1.1 什么是PXEPXE(Pre-boot Execution Environment,预启动执行环境)是由Intel公司开发的最新技术,工作于Client/Server的网络模式,支持工作 ...

  2. C语言文件操作类型速查

    文件使用方式 含义 "r"(只读) 为输入打开一个文本文件,不存在则失败 "w"(只写) 为输出打开一个文本文件,不存在则新建,存在则删除后再新建 " ...

  3. 前端jQuery之事件流

    1.事件流概念 描述的是从页面中接收事件的顺序 包含事件捕获阶段,处于目标阶段,事件冒泡阶段 2.绑定事件 语法 bind(type,data,fn) 示例:每个标签被点击的时候,弹出其文本 $(&q ...

  4. BIO与NIO

    BIO与NIO 1.传统BIO (1)特点 面向数据流 阻塞式传输 一个客户端对应一个线程 在客户机增多的情况下,线程资源随之增多,会造成cpu资源枯竭 (2)需求 ​ 客户机向服务器输出字符串,逐一 ...

  5. 图解HTTP-1.web和网络基础

    目录 1. 3 项 WWW 构建技术 2. TCP/IP 是互联网相关的各类协议族的总称 协议(protocol) TCP/IP分层管理 TCP/IP通信传输流 封装(encapsulate) 3. ...

  6. OceanBase安装

    背景: OceanBase是阿里巴巴.蚂蚁金服自主研发的可扩展的分布式关系数据库,实现了数千亿条记录.数百 TB 数据上的跨行跨表事务,主要支持支付宝核心的交易.支付.会员和账务系统等 OLTP 和  ...

  7. Linux - bashrc之alias

    1. cd ~ 2. touch .bashrc // 若该文件不存在的话 3. vim .bashrc ----------------复制粘贴如下文本--------------- # alias ...

  8. 高封装的property方法

    class Person(): def __init__(self): self.__age = 0 def set_age(self, age): if age < 0 or age > ...

  9. oauth2.0协议接口-第一篇-api逻辑

    开放平台是支持OAuth2.0和RESTful协议的资源分享平台,经过授权的合作伙伴可以读取和写入资讯.用户.文件.数据库等资源. 1.创建数据库表结构 CMSSyncClient(数据同步客户端) ...

  10. HAN模型理解2

    Hierarchical Attention Networks for Document Classification 论文的理解 在论文的摘要中,它提出了论文的两个特点.第一个就是对应文章所有具有的 ...