我们平时用 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. EF中 实现延迟加载 lazyload

    1.创建数据库 2.利用数据库 生成视图 生成2个实体类 和一个model1类 3.写代码 (1) 创建 上下文对象 (2) (3)查询结果 注释: 延迟加载的原因,因为我们操作数据库不会那么简单, ...

  2. splay版

    指针是个好东西 不过就是得判空 还有别忘传引用(其实应该都传引用) #include<cstdio> #include<algorithm> #include<iostr ...

  3. 第32章 TIM—高级定时器—零死角玩转STM32-F429系列

    第32章     TIM—高级定时器 全套200集视频教程和1000页PDF教程请到秉火论坛下载:www.firebbs.cn 野火视频教程优酷观看网址:http://i.youku.com/fire ...

  4. 公众帐号如何向用户发送emoji表情(php版,附emoji编码表)

    //字节转Emoji表情 function bytes_to_emoji($cp) { if ($cp > 0x10000){ # 4 bytes return chr(0xF0 | (($cp ...

  5. 开发常用宏 - iOS

    以下是一些开发中会经常用到的宏,简单的进行了整理,为了今后可以更加方便的使用,从而提升开发的效率,不为此搭进去更多时间. 也希望有大家可以补充,从而使其更加强加! /** * 开发常用宏相关 */ # ...

  6. 基于asp.net MVC 的服务器和客户端的交互(一)

    架构思想 三层架构 提出了一种基于ASP.NET开发方式的三层架构的Web应用系统构造思想.其基本内容是:将面向对象的UML建模与Web应用系统开发 相结合,将整个系统分成适合ASP.NET开发方式的 ...

  7. Spring框架基础2

    Spring框架基础2 测试Spring的AOP思想和注解的使用 导包(在前面的基础上添加) SpringAOP名词解释 AOP编程思想:横向重复代码,纵向抽取:就是说多个地方重复的代码可以抽取出来公 ...

  8. 搞定 mybatis generator 三步走

    基于idea 编辑器下maven项目使用mybatis generator快速生成持久层 添加插件:插件网址:http://www.mybatis.org/generator/running/runn ...

  9. spring-bean(注解方式-管理及依赖注入)

    Bean管理(注解方式) 1.添加注解的依赖包:Spring-aop.jar 2.配置spring的XML文件的引入(查官方源码) 3.开启注解的扫描 <context:component-sc ...

  10. udp发送广播消息

    import socket if __name__ == '__main__': # 创建udpsocket udp_socket = socket.socket(socket.AF_INET, so ...