spring boot中servlet启动原理详解编程语言
2 createApplicationContext():创建IOC容器,如果是web应用则创建AnnotationConfigEmbeddedWebApplacation的IOC容器,如果不是,则创建AnnotationConfigApplication的IOC容器
public static final String DEFAULT_CONTEXT_CLASS = "org.springframework.context." + "annotation.AnnotationConfigApplicationContext"; /** * The class name of application context that will be used by default for web * environments. public static final String DEFAULT_WEB_CONTEXT_CLASS = "org.springframework." + "boot.context.embedded.AnnotationConfigEmbeddedWebApplicationContext";
protected ConfigurableApplicationContext createApplicationContext() { Class ? contextClass = this.applicationContextClass; if (contextClass == null) { try {
//根据应用环境,创建不同的IOC容器 contextClass = Class.forName(this.webEnvironment ? DEFAULT_WEB_CONTEXT_CLASS : DEFAULT_CONTEXT_CLASS); catch (ClassNotFoundException ex) { throw new IllegalStateException( "Unable create a default ApplicationContext, " + "please specify an ApplicationContextClass", ex); return (ConfigurableApplicationContext) BeanUtils.instantiate(contextClass); }
3 refreshContext(context) spring boot刷新IOC容器(创建容器对象,并初始化容器,创建容器每一个组件)
private void refreshContext(ConfigurableApplicationContext context) { refresh(context); if (this.registerShutdownHook) { try { context.registerShutdownHook(); catch (AccessControlException ex) { // Not allowed in some environments. }
4 refresh(context);刷新刚才创建的IOC容器
protected void refresh(ApplicationContext applicationContext) { Assert.isInstanceOf(AbstractApplicationContext.class, applicationContext); ((AbstractApplicationContext) applicationContext).refresh(); }
5 调用父类的refresh()的方法
public void refresh() throws BeansException, IllegalStateException { Object var1 = this.startupShutdownMonitor; synchronized(this.startupShutdownMonitor) { this.prepareRefresh(); ConfigurableListableBeanFactory beanFactory = this.obtainFreshBeanFactory(); this.prepareBeanFactory(beanFactory); try { this.postProcessBeanFactory(beanFactory); this.invokeBeanFactoryPostProcessors(beanFactory); this.registerBeanPostProcessors(beanFactory); this.initMessageSource(); this.initApplicationEventMulticaster(); this.onRefresh(); this.registerListeners(); this.finishBeanFactoryInitialization(beanFactory); this.finishRefresh(); } catch (BeansException var9) { if (this.logger.isWarnEnabled()) { this.logger.warn("Exception encountered during context initialization - cancelling refresh attempt: " + var9); this.destroyBeans(); this.cancelRefresh(var9); throw var9; } finally { this.resetCommonCaches(); }
6 抽象父类AbstractApplicationContext类的子类EmbeddedWebApplicationContext的onRefresh方法
@Override protected void onRefresh() { super.onRefresh(); try { createEmbeddedServletContainer(); catch (Throwable ex) { throw new ApplicationContextException("Unable to start embedded container", ex); }
7 在createEmbeddedServletContainer放啊发中会获取嵌入式Servlet容器工厂,由容器工厂创建Servlet
private void createEmbeddedServletContainer() { EmbeddedServletContainer localContainer = this.embeddedServletContainer; ServletContext localServletContext = getServletContext(); if (localContainer == null localServletContext == null) {
//获取嵌入式Servlet容器工厂 EmbeddedServletContainerFactory containerFactory = getEmbeddedServletContainerFactory();
//根据容器工厂获取对应嵌入式Servlet容器 this.embeddedServletContainer = containerFactory .getEmbeddedServletContainer(getSelfInitializer()); else if (localServletContext != null) { try { getSelfInitializer().onStartup(localServletContext); catch (ServletException ex) { throw new ApplicationContextException("Cannot initialize servlet context", ex); initPropertySources(); }
8 从IOC容器中获取Servlet容器工厂
//EmbeddedWebApplicationContext#getEmbeddedServletContainerFactory protected EmbeddedServletContainerFactory getEmbeddedServletContainerFactory() { // Use bean names so that we dont consider the hierarchy String[] beanNames = getBeanFactory() .getBeanNamesForType(EmbeddedServletContainerFactory.class); if (beanNames.length == 0) { throw new ApplicationContextException( "Unable to start EmbeddedWebApplicationContext due to missing " + "EmbeddedServletContainerFactory bean."); if (beanNames.length 1) { throw new ApplicationContextException( "Unable to start EmbeddedWebApplicationContext due to multiple " + "EmbeddedServletContainerFactory beans : " + StringUtils.arrayToCommaDelimitedString(beanNames)); return getBeanFactory().getBean(beanNames[0], EmbeddedServletContainerFactory.class); }
9 使用Servlet容器工厂获取嵌入式Servlet容器,具体使用哪一个容器工厂看配置环境依赖
this.embeddedServletContainer = containerFactory .getEmbeddedServletContainer(getSelfInitializer());
10 上述创建过程 首先启动IOC容器,接着启动嵌入式Servlet容器,接着将IOC容器中剩下没有创建的对象获取出来,比如自己创建的controller
// Instantiate all remaining (non-lazy-init) singletons. finishBeanFactoryInitialization(beanFactory);
protected void finishBeanFactoryInitialization(ConfigurableListableBeanFactory beanFactory) { // Initialize conversion service for this context. if (beanFactory.containsBean(CONVERSION_SERVICE_BEAN_NAME) beanFactory.isTypeMatch(CONVERSION_SERVICE_BEAN_NAME, ConversionService.class)) { beanFactory.setConversionService( beanFactory.getBean(CONVERSION_SERVICE_BEAN_NAME, ConversionService.class)); // Register a default embedded value resolver if no bean post-processor // (such as a PropertyPlaceholderConfigurer bean) registered any before: // at this point, primarily for resolution in annotation attribute values. if (!beanFactory.hasEmbeddedValueResolver()) { beanFactory.addEmbeddedValueResolver(new StringValueResolver() { @Override public String resolveStringValue(String strVal) { return getEnvironment().resolvePlaceholders(strVal); }); // Initialize LoadTimeWeaverAware beans early to allow for registering their transformers early. String[] weaverAwareNames = beanFactory.getBeanNamesForType(LoadTimeWeaverAware.class, false, false); for (String weaverAwareName : weaverAwareNames) { getBean(weaverAwareName); // Stop using the temporary ClassLoader for type matching. beanFactory.setTempClassLoader(null); // Allow for caching all bean definition metadata, not expecting further changes. beanFactory.freezeConfiguration(); // Instantiate all remaining (non-lazy-init) singletons. beanFactory.preInstantiateSingletons(); }
看看 preInstantiateSingletons方法
public void preInstantiateSingletons() throws BeansException { if (this.logger.isDebugEnabled()) { this.logger.debug("Pre-instantiating singletons in " + this); List String beanNames = new ArrayList(this.beanDefinitionNames); Iterator var2 = beanNames.iterator(); while(true) { while(true) { String beanName; RootBeanDefinition bd; do { do { do { if (!var2.hasNext()) { var2 = beanNames.iterator(); while(var2.hasNext()) { beanName = (String)var2.next(); Object singletonInstance = this.getSingleton(beanName); if (singletonInstance instanceof SmartInitializingSingleton) { final SmartInitializingSingleton smartSingleton = (SmartInitializingSingleton)singletonInstance; if (System.getSecurityManager() != null) { AccessController.doPrivileged(new PrivilegedAction Object () { public Object run() { smartSingleton.afterSingletonsInstantiated(); return null; }, this.getAccessControlContext()); } else { smartSingleton.afterSingletonsInstantiated(); return; beanName = (String)var2.next(); bd = this.getMergedLocalBeanDefinition(beanName); } while(bd.isAbstract()); } while(!bd.isSingleton()); } while(bd.isLazyInit()); if (this.isFactoryBean(beanName)) { final FactoryBean ? factory = (FactoryBean)this.getBean(" " + beanName); boolean isEagerInit; if (System.getSecurityManager() != null factory instanceof SmartFactoryBean) { isEagerInit = ((Boolean)AccessController.doPrivileged(new PrivilegedAction Boolean () { public Boolean run() { return ((SmartFactoryBean)factory).isEagerInit(); }, this.getAccessControlContext())).booleanValue(); } else { isEagerInit = factory instanceof SmartFactoryBean ((SmartFactoryBean)factory).isEagerInit(); if (isEagerInit) { this.getBean(beanName); } else {
//注册bean this.getBean(beanName); }
是使用getBean方法来通过反射将所有未创建的实例创建出来
使用嵌入式Servlet容器:
优点: 简单,便携
缺点: 默认不支持jsp,优化定制比较复杂
使用外置Servlet容器的步骤:
1 必须创建war项目,需要剑豪web项目的目录结构
2 嵌入式Tomcat依赖scope指定provided
3 编写SpringBootServletInitializer类子类,并重写configure方法
public class ServletInitializer extends SpringBootServletInitializer { @Override protected SpringApplicationBuilder configure(SpringApplicationBuilder application) { return application.sources(SpringBoot04WebJspApplication.class); }
4 启动服务器
jar包和war包启动区别
jar包:执行SpringBootApplication的run方法,启动IOC容器,然后创建嵌入式Servlet容器
war包: 先是启动Servlet服务器,服务器启动Springboot应用(springBootServletInitizer),然后启动IOC容器
Servlet 3.0+规则
1 服务器启动(web应用启动),会创建当前web应用里面所有jar包里面的ServletContainerlnitializer实例
2 ServletContainerInitializer的实现放在jar包的META-INF/services文件夹下
3 [email protected],在应用启动的时候加载指定的类。
外部Tomcat流程以及原理
① 启动Tomcat
② 根据上述描述的Servlet3.0+规则,可以在Spring的web模块里面找到有个文件名为javax.servlet.ServletContainerInitializer的文件,而文件的内容为org.springframework.web.SpringServletContainerInitializer,用于加载SpringServletContainerInitializer类
③看看SpringServletContainerInitializer定义
@HandlesTypes(WebApplicationInitializer.class) public class SpringServletContainerInitializer implements ServletContainerInitializer { /** * Delegate the [email protected] ServletContext} to any [email protected] WebApplicationInitializer} * implementations present on the application classpath. * p Because this class declares @[email protected] HandlesTypes(WebApplicationInitializer.class)}, * Servlet 3.0+ containers will automatically scan the classpath for implementations * of Springs [email protected] WebApplicationInitializer} interface and provide the set of all * such types to the [email protected] webAppInitializerClasses} parameter of this method. * p If no [email protected] 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 [email protected] ServletContainerInitializer} has indeed been invoked but that * no [email protected] WebApplicationInitializer} implementations were found. * p Assuming that one or more [email protected] WebApplicationInitializer} types are detected, * they will be instantiated (and em sorted /em if the @[email protected] * org.springframework.core.annotation.Order @Order} annotation is present or * the [email protected] org.springframework.core.Ordered Ordered} interface has been * implemented). Then the [email protected] WebApplicationInitializer#onStartup(ServletContext)} * method will be invoked on each instance, delegating the [email protected] ServletContext} such * that each instance may register and configure servlets such as Springs * [email protected] DispatcherServlet}, listeners such as Springs [email protected] ContextLoaderListener}, * or any other Servlet API componentry such as filters. * @param webAppInitializerClasses all implementations of * [email protected] WebApplicationInitializer} found on the application classpath * @param servletContext the servlet context to be initialized * @see WebApplicationInitializer#onStartup(ServletContext) * @see AnnotationAwareOrderComparator @Override public void onStartup(Set Class ? webAppInitializerClasses, ServletContext servletContext) throws ServletException { List WebApplicationInitializer initializers = new LinkedList WebApplicationInitializer (); if (webAppInitializerClasses != null) { for (Class ? waiClass : webAppInitializerClasses) { // Be defensive: Some servlet containers provide us with invalid classes, // no matter what @HandlesTypes says... if (!waiClass.isInterface() !Modifier.isAbstract(waiClass.getModifiers()) WebApplicationInitializer.class.isAssignableFrom(waiClass)) { try {
//为所有的WebApplicationInitializer类型创建实例,并加入集合中 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);
//调用每一个WebApplicationInitializer实例的onstartup方法 for (WebApplicationInitializer initializer : initializers) { initializer.onStartup(servletContext); }
在上面一段长长的注释中可以看到,[email protected](WebApplicationInitializer.class)标注的所有WebApplicationInitializer这个类型的类都传入到onStartup方法的Set参数中,并通过反射为这些WebApplicationInitializer类型的类创建实例;
④ 方法最后,每一个WebApplicationInitilizer实现调用自己onstartup方法
⑤ 而WebApplicationInitializer有个抽象实现类SpringBootServletInitializer(记住我们继承了该抽象类),则会调用每一个WebApplicationInitializer实例(包括SpringBootServletInitializer)的onStartup方法:
public abstract class SpringBootServletInitializer implements WebApplicationInitializer { //other code... @Override public void onStartup(ServletContext servletContext) throws ServletException { // Logger initialization is deferred in case a ordered // LogServletContextInitializer is being used this.logger = LogFactory.getLog(getClass()); //创建IOC容器 WebApplicationContext rootAppContext = createRootApplicationContext( servletContext); if (rootAppContext != null) { servletContext.addListener(new ContextLoaderListener(rootAppContext) { @Override public void contextInitialized(ServletContextEvent event) { // no-op because the application context is already initialized }); else { this.logger.debug("No ContextLoaderListener registered, as " + "createRootApplicationContext() did not " + "return an application context"); protected WebApplicationContext createRootApplicationContext( ServletContext servletContext) { //创建Spring应用构建器,并进行相关属性设置 SpringApplicationBuilder builder = createSpringApplicationBuilder(); StandardServletEnvironment environment = new StandardServletEnvironment(); environment.initPropertySources(servletContext, null); builder.environment(environment); builder.main(getClass()); ApplicationContext parent = getExistingRootWebApplicationContext(servletContext); if (parent != null) { this.logger.info("Root context already created (using as parent)."); servletContext.setAttribute( WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, null); builder.initializers(new ParentContextApplicationContextInitializer(parent)); builder.initializers( new ServletContextApplicationContextInitializer(servletContext)); builder.contextClass(AnnotationConfigEmbeddedWebApplicationContext.class); //调用configure方法,创建war类型的web项目后,由于编写SpringBootServletInitializer的子类重写configure方法,所以此处调用的是我们定义的子类重写的configure方法 builder = configure(builder); //通过构建器构建了一个Spring应用 SpringApplication application = builder.build(); if (application.getSources().isEmpty() AnnotationUtils .findAnnotation(getClass(), Configuration.class) != null) { application.getSources().add(getClass()); Assert.state(!application.getSources().isEmpty(), "No SpringApplication sources have been defined. Either override the " + "configure method or add an @Configuration annotation"); // Ensure error pages are registered if (this.registerErrorPageFilter) { application.getSources().add(ErrorPageFilterConfiguration.class); //启动Spring应用 return run(application); //Spring应用启动,创建并返回IOC容器 protected WebApplicationContext run(SpringApplication application) { return (WebApplicationContext) application.run(); }
SpringBootServletInitializer实例执行onStartup方法的时候会通过createRootApplicationContext方法来执行run方法,接下来的过程就同以jar包形式启动的应用的run过程一样了,在内部会创建IOC容器并返回,只是以war包形式的应用在创建IOC容器过程中,不再创建Servlet容器了。
原文地址:http://www.cnblogs.com/developerxiaofeng/p/9081689.html
原创文章,作者:ItWorker,如若转载,请注明出处:https://blog.ytso.com/16226.html
cjava相关文章
- 玩转 Spring Boot 原理篇(自动装配源码剖析)(十二)
- spring boot自动配置原理面试题_Spring boot面试
- Spring | 注解开发详解
- Spring Boot 实现万能文件在线预览
- Spring Cloud:第五章:Zuul服务网关
- Java项目分享-适合 Spring Boot 初学者的 OA 开源项目
- Spring Boot 整合定时任务,可以动态编辑的定时任务
- Just:Spring Boot 应用的新命令行界面
- 扼住高并发、高性能的咽喉---Spring Boot并发进阶
- Spring MVC常用注解--“姐妹花”@RequestBody和@ResponseBody
- 将Bean交给Spring容器管理有几种方式?
- Spring Boot应用部署到Tomcat中无法启动问题 javax.servlet.ServletContext.getVirtualServerName()
- Spring基础(五):Bean的生命周期
- Spring 6.0已发布,AOT和Native Image成为新亮点
- Spring Boot 3.0横空出世,快来看看是不是该升级了
- Spring Boot + flowable 快速实现工作流
- Spring Boot 2.6之后,动态权限控制终于可以用起来了!
- (一)spring cloud架构整合-springcloud简介详解架构师
- java获取Spring中的bean方法总结详解编程语言
- 获取Spring容器Bean对象工具类详解编程语言
- Spring Boot 2 (六):使用 Docker 部署 Spring Boot 开源软件云收藏详解编程语言
- Spring ElasticsearchTemplate 经纬度按距离排序详解编程语言
- spring boot详解编程语言
- 基于properties文件的Spring Boot多环境切换详解编程语言
- spring boot 请求地址带有.json 兼容处理详解编程语言