聊聊、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 里面配置了。
下一篇来聊聊,这些注解和标签被调用了,怎么识别的呢?我怎么知道要去扫描这个包下面的类?哪里有定义吗?
聊聊、Spring ServletContainerInitializer的更多相关文章
- 从架构演进的角度聊聊Spring Cloud都做了些什么?
Spring Cloud作为一套微服务治理的框架,几乎考虑到了微服务治理的方方面面,之前也写过一些关于Spring Cloud文章,主要偏重各组件的使用,本次分享主要解答这两个问题:Spring Cl ...
- 聊聊 Spring Boot 2.x 那些事儿
本文目录: 即将的 Spring 2.0 - Spring 2.0 是什么 - 开发环境和 IDE - 使用 Spring Initializr 快速入门 Starter 组件 - Web:REST ...
- 朱晔和你聊Spring系列S1E1:聊聊Spring家族的几大件
朱晔和你聊Spring系列S1E1:聊聊Spring家族的几大件 [下载本文PDF进行阅读] Spring家族很庞大,从最早先出现的服务于企业级程序开发的Core.安全方面的Security.到后来的 ...
- 从架构演进的角度聊聊Spring Cloud都做了些什么
1.从架构演进的角度聊聊Spring Cloud都做了些什么?2.中小型互联网公司微服务实践-经验和教训3.Spring Cloud在国内中小型公司能用起来吗?
- 聊聊 Spring Boot 2.0 的 WebFlux
聊聊 Spring Boot 2.0 的 WebFlux## 前言 对照下 Spring Web MVC ,Spring Web MVC 是基于 Servlet API 和 Servlet 容器设计的 ...
- 【小家Spring】聊聊Spring中的数据绑定 --- BeanWrapper以及内省Introspector和PropertyDescriptor
#### 每篇一句 > 千古以来要饭的没有要早饭的,知道为什么吗? #### 相关阅读 [[小家Spring]聊聊Spring中的数据转换:Converter.ConversionService ...
- 【小家Spring】聊聊Spring中的数据绑定 --- DataBinder本尊(源码分析)
每篇一句 唯有热爱和坚持,才能让你在程序人生中屹立不倒,切忌跟风什么语言或就学什么去~ 相关阅读 [小家Spring]聊聊Spring中的数据绑定 --- 属性访问器PropertyAccessor和 ...
- 聊聊spring之bean对象的实例化过程
在spring实例化 之前bean对象封装成 beanDefinition 对象 想了解详情的请参考上一篇文章 好了 我们聊聊 Bean 的实例化过程的几个重要角色 BeanDefinitionReg ...
- 【Spring注解驱动开发】聊聊Spring注解驱动开发那些事儿!
写在前面 今天,面了一个工作5年的小伙伴,面试结果不理想啊!也不是我说,工作5年了,问多线程的知识:就只知道继承Thread类和实现Runnable接口!问Java集合,竟然说HashMap是线程安全 ...
随机推荐
- django2.2连接mysql遇到的坑
1.mysql数据库配置 2.首先需要建一个myweb数据库 3.执行数据库迁移命令makemigrations python manage.py makemigrations MySite 报错: ...
- zen-Coding在Notepad++中的使用
zen-Coding是一款快速编写HTML,CSS(或其他格式化语言)代码的编辑器插件,这个插件可以用缩写方式完成大量重复的编码工作,是web前端从业者的利器. zen-Coding插件支持多种编辑器 ...
- js世界这么大,闭包想看看
什么是闭包,为什么要用他?闭包是能够访问其他函数作用域的函数.我们来分析下句子成分(语文大神),闭包是函数,js函数的作用域分为全局作用域,局部作用域,eval作用域,并没有块级作用域形象的讲,每个函 ...
- java后台导出pdf
新页面打开wpf @RequestMapping("/showPdf") public String getpic( HttpServletRequest request, Htt ...
- 设置禁止网络连接后,jdbc如何连接到数据库
设置禁止网络连接,可在my.ini文件中添加如下两行 skip-networking enable-named-pipe 可以通过 SHOW VARIABLES LIKE '%skip_ne%' 来查 ...
- UITabBarController的tabBarItem图标真机不显示
在xib中分别设置了图标选择和未选择的图片 模拟器效果 选中 未选中 当时使用多种机型进行测试时发现部分机型第一次进入应用除第一个tabbaritem图标可以显示,其余均不能显示,点击其余tabbar ...
- 关于 export default 和 export
// 第一组 export default function crc32() { // 输出 // ... } import crc32 from 'crc32'; // 输入 // 第二组 expo ...
- 爬虫学习(十六)——jsonpath
jsonpath介绍 jsonpath是一种信息抽取类库,是从json文档中抽取指定信息的工具,提供多种语言实现的版本 jsonpath对json来说,就相当于xpath对于xml jsonpath和 ...
- java后台poi根据模板导出excel
public class ExcelUtils { private static final String INSPECTIONRECORD_SURFACE_TEMPLET_PATH = " ...
- 统计重复IP并排序
#降序排列 sort ip20180623.log | uniq -c | sort -rn | more #可以输出到文件哦 sort ip20180623.log | uniq -c | sort ...