Tomcat
StandardContext初始化过程
//org.apache.catalina.core.StandardContext#startInternal // 子容器启动 for (Container child : findChildren()) { if (!child.getState().isAvailable()) { child.start(); } } //初始化WebappLoader if (getLoader() == null) { WebappLoader webappLoader = new WebappLoader(getParentClassLoader()); webappLoader.setDelegate(getDelegate()); setLoader(webappLoader); } //实例化contextManager ,用于管理session Manager contextManager = null; try { contextManager = getCluster().createManager(getName()); } catch (Exception ex) { log.error("standardContext.clusterFail", ex); ok = false; } setManager(contextManager); //1. //初始化所有Listener实例, if (ok) { if (!listenerStart()) { log.error( "Error listenerStart"); } } //2. try { // 启动StandardManager,该类管理Session信息 if ((manager != null) && (manager instanceof Lifecycle)) { ((Lifecycle) getManager()).start(); } //启动后台线程, 检查seesion超时 super.threadStart(); } //3. 初始化 application filters if (ok) { if (!filterStart()) { log.error("Error filterStart"); ok = false; } } //4. //加载和初始化标记"load on startup"大于1的Servelet if (ok) { loadOnStartup(findChildren()); }
Tomcat初始化监听对象:
监听器分两种LifeCycleListener, EventListener ,如图所示,
参考资料:
https://blog.csdn.net/yangsnow_rain_wind/article/details/80118321
serveltContext
serveltContext标示Tomcat存储当前Web程序的上下文信息,
- 每个Engine有多个war包, 每个war包就是一个web应用程序, 每个web应用程序只有一个Context上下文,这就是servletContext.
- 每个servletcontext下可以有多个servlet, 每个servlet需要有配置url映射. 每个servlet需要被包装为StandardWrapper类, 存储到servletcontext容器中.
- ServletContext 对象是一个为整个 web 应用提供共享的内存,多个servlet实例见可以通过context进行通信.
- ServletContext 在tomcat中对应的类是ApplicationContext
参照笔记: https://blog.csdn.net/yangsnow_rain_wind/article/details/80118321
1、spi简单说明
spi,即service privider interface,是jdk为厂商和插件提供的一种解耦机制。
spi的具体规范为:当服务的提供者,提供了服务接口的一种实现之后,在jar包的META-INF/services/目录里同时创建一个以服务接口命名的文件。该文件里就是实现该服务接口的具体实现类。而当外部程序装配这个模块的时候,就能通过该jar包META-INF/services/里的配置文件找到具体的实现类名,并通过反射机制实例化,完成模块的注入。 基于这样一个约定就能很好的找到服务接口的实现类,而不需要再代码里制定。jdk提供服务实现查找的一个工具类:java.util.ServiceLoader
2、spring-web中的具体应用
从servlet3.0开始,web容器启动时为提供给第三方组件机会做一些初始化的工作,例如注册servlet或者filtes等,servlet规范中通过ServletContainerInitializer.onStartup实现此功能。
每个框架要使用ServletContainerInitializer就必须在对应的jar包的META-INF/services 目录创建一个名为javax.servlet.ServletContainerInitializer的文件,文件内容指定具体的ServletContainerInitializer实现类,那么,当web容器启动时就会运行这个初始化器做一些组件内的初始化工作。
一般伴随着ServletContainerInitializer一起使用的还有HandlesTypes注解,通过HandlesTypes可以将感兴趣的一些类注入到ServletContainerInitializerde的onStartup方法作为参数传入。
spring-web的jar定义了一个具体的实现类,SpringServletContainerInitializer,并且在META-INF/services/目录下定义了如下文件org.springframework.web.SpringServletContainerInitializer。
3、SpringServletContainerInitializer
SpringServletContainerInitializer是接口ServletContainerInitializer的实现类,tomcat启动时调用processServletContainerInitializers,通过SPI机制,找到classpath下所有实现ServletContainerInitializers接口的类,然后初始化这个类,并调用此类的接口onStartup.
SpringServletContainerInitializer类上注解为@HandlesTypes(WebApplicationInitializer.class)~,它的作用是将感兴趣的一些类注入到ServletContainerInitializer,并作为参数传给onStartup方法,并后续完成类初始化。
最终ServletContainerInitializers.onStartup方法变成了实现WebApplicationInitializer.onStartup(ServletContext)
4. WebApplicationInitializer
通过实现接口WebApplicationInitializer,在其中可以添加servlet,listener等,在加载Web项目的时候会加载这个接口实现类,从而起到web.xml相同的作用。
其重要的一个实现类是SpringBootServletInitializer
5. SpringBootServletInitializer
事件监听
ServletContextListener:在servlet启动时,获得监听(contextInitialized,contextDestroyed)
ContextLoaderListener: 用来在部署相关Web应用时初始化其上下文环境(initWebApplicationContext, 并放置在servletContext.attr(".root")属性中),在停止退出Web应用时清理其资源
ServletContextListener:
包括两个接口方法:contextInitialized,contextDestroyed,在Web 应用的生命周期中,ServletContext 对象最早被创建,最晚被销毁,常被用于在web容器启动war之前对servlet进行提前配置,
如设置attribute参数到servletContext中。SpringMVC框架中被用来初始化IOC容器等,(ContextLoaderListener类中在contextInitialized方法中初始化initWebApplicationContext(被放在servletContext.attr(".root")属性中))
Filter
Filter(过滤器)可以改变HTTP请求的内容、响应、及header信息,过滤器通常不产生响应或像 servlet 那样对请求作出响应,而是修改或调整到资源的请求,修改或调整来自资源的响应。
当容器接收到传入的请求时,它将获取列表中所有符合该请求的过虑器组成过虑器链(FilterChain),然后通过FilterChain嵌套调用各过虑器的Filter.doFilter()方法(一个过滤器可以使得滤器链调用下一个过滤器),传入ServletRequest 和ServletResponse进行处理。
当滤器链调用完各Filter.doFilter()方法后,会调用相关Servlet.service()方法,从而进行Servlet的处理。注意,Filter.doFilter()可以阻止过滤器链接下来的调用,返回时过滤器负责填充Reponse对象。
过滤器实例移除之前,容器必须先调用过滤器的Filter.destroy()方法。
web.xml中组件加载顺序为:
context-param -> listener -> filter -> servlet(同类则按编写顺序执行)
ApplicationContextInitializer:
ApplicationContextInitializer是Spring框架原有的东西,这个类的主要作用就是在ConfigurableApplicationContext类型(或者子类型)的ApplicationContext做refresh之前,允许我们对ConfiurableApplicationContext的实例做进一步的设置和处理。
ApplicationContextInitializer接口是在spring容器刷新Refresh之前执行的一个回调函数
实际应用:
在一个Springboot应用中,classpath上会包含很多jar包,有些jar包需要在ConfigurableApplicationContext#refresh()调用之前对应用上下文做一些初始化动作,因此它们会提供自己的ApplicationContextInitializer实现类,然后放在自己的META-INF/spring.factories属性文件中,这样相应的ApplicationContextInitializer实现类就会被SpringApplication#initialize发现。
添加方法有三种
1. 启动类里手动增加initializer
@SpringBootApplication @EnableConfigServer @EnableDiscoveryClient public class ConfigServer { public static void main(String[] args) { SpringApplication springApplication = new SpringApplication(ConfigServer.class); // 方法一:添加自定义的 ApplicationContextInitializer 实现类的实例(注册ApplicationContextInitializer) springApplication.addInitializers(new ApplicationContextInitializer1()); ConfigurableApplicationContext context = springApplication.run(args); context.close(); } }
2.application.properties添加配置方式
context.initializer.classes = com.springboot.xx.xx.ApplicationPropertiesApplicationContextInitializer
3. 使用spring.factories方式
1. 先定义先定义ApplicationContextInitializer
2、然后在项目下的resources下新建META-INF文件夹,文件夹下新建spring.factories文件
org.springframework.context.ApplicationContextInitializer=com.x.ApplicationContextInitializer1
总结:
ApplicationContextInitializer是Spring留出来允许我们在上下文刷新之前做自定义操作的钩子,若我们有需求想要深度整合Spring上下文,借助它不乏是一个非常好的实现。
网关作用:
在微服务实施之后,各个服务的拆分粒度很小,对于客户端来说,做一个操作可能会涉及到 后端的多个服务组件的调用,那意味着它需要频繁的发起多次访问才能进行数据聚合实现用 户的功能。 如果我们在所有的微服务之前增加一个网关,对于客户端来说它需要做什么功能操作直接调 用网关并且告诉网关所要做的事情即可,网关根据请求的功能对后端的多个服务的数据进行 聚合聚合哦从而减少客户端的调用频次。 并且,由于有了网关的聚合,我们还可以在网关层对请求进行统一鉴权和认证; 包括还可 以实现限流、请求日志统一记录、 灰度发布等等。
Enable 注解不仅仅可以像前面演示的案例一样很简单的实 现多个 Configuration 的整合,还可以实现一些复杂的场 景,比如可以根据上下文来激活不同类型的 bean, @Import 注解可以配置三种不同的 class
1. 第一种就是前面演示过的,基于普通 bean 或者带有 @Configuration 的 bean 进行诸如
2. 实现 ImportSelector 接口进行动态注入
3. 实现 ImportBeanDefinitionRegistrar 接口进行动态注入
@EnableAutoConfiguration 注解的实现原理
EnableConfigurationProperties