0
点赞
收藏
分享

微信扫一扫

SpringBoot启动原理

pipu 2022-01-08 阅读 74

springboot是如何通过一个@EnableXXX实现自动配置 的,就是因为依赖里面集成了各种spring-boot-starter-xxx,只要满足各种依赖里的XXXAutoConfiguration自动配置类上面的@ConditionalOnClass(xx.class)或者其他xxxProperties条件,就启用自动配置类了。

1.为什么SpringBoot的jar可以直接运行

<build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>

pom.xml中配置打包插件,这个插件会在jar包下的META-INF生成MANIFEST.MF文件(文件里有一个start-class指定启动类),也会打包所依赖的jar到fat jar包中,但是这样它就能做到启动main方法吗?其实不是,而是Main-Class中的自定义类加载器-启动类JarLauncher做到的,通过加载BOOT-INF/classes目录及BOOT-INF/lib目录下jar文件,找到start-class的main方法实现了fat jar的启动

 如何验证或者调试?可以创建一个jar application debug,引入这个启动类的依赖,就能搜索到JarLauncher类了,在main方法中打断点并启动debug即可。

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-loader</artifactId>
        </dependency>

 

2.SpringBoot是如何启动Spring容器

以前的spring是怎么启动的,用ApplicationContext构造器传入配置类或配置文件实例化一个spring容器,里面会运行一个最重要的refresh方法,他在方法中会调invokeBeanFactoryPostProcessors(beanFactory)读取各种注解Component等等 加载那些配置类读取到beanDefinitionMap而SpringBoot是在SpringApplication.run中实例化一个spring容器,除了这个还会读取全局配置文件。

public static void main(String[] args) {
        //spring
		ApplicationContext applicationContext = new ClassPathXMLApplicationContext("xxx.pr");
		ApplicationContext applicationContext = new AnnotationConfigApplicationContext(AppConfig.class);
        //springboot Application是当前启动类
        SpringApplication.run(Application.class, args);

	}
// run的时候初始化
public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) {
		this.resourceLoader = resourceLoader;
		Assert.notNull(primarySources, "PrimarySources must not be null");
		this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources));
		this.webApplicationType = WebApplicationType.deduceFromClasspath();
		setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class));
		setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
		this.mainApplicationClass = deduceMainApplicationClass();
	}
1.获取启动类:根据启动类加载ioc容器
2.获取web应用类型(是webflux还是servlet)
3.spring.factories读取了对外扩展的ApplicationContextInitializer   ,ApplicationListener 。  对外扩展, 对内解耦(比如全局配置文件、热部署插件)
4.根据main推算出所在的类

// run方法内部
public ConfigurableApplicationContext run(String... args) {
		// 省略部分代码 
//这里根据前面判断出的web应用类型,创建了servlet spring context
//AnnotationConfigServletWebServerApplicationContext
			context = createApplicationContext();
			exceptionReporters = getSpringFactoriesInstances(SpringBootExceptionReporter.class,
					new Class[] { ConfigurableApplicationContext.class }, context);
			prepareContext(context, environment, listeners, applicationArguments, printedBanner);
          // 调用spring中的refresh
			refreshContext(context);
			afterRefresh(context, applicationArguments);
			

			// 省略部分代码  
		return context;
	}

3.SpringBoot如何启动内置Tomcat

在spring的refresh中的onrefresh方法里createWebServer中的getWebServer启动了内嵌tomcat(创建servlet容器注册DispatcherServlet),如果是外置Tomcat走的是else分支因为外部是有ServletContext的。getSelfInitializer里拿到了 DispatcherServlet(自动配置类DispatcherServletAutoConfiguration配置的,内部是ServletRegistrationBean 注册的servlet)和XXFilter.、

@Bean
    public ServletRegistrationBean myServlet(){
        // 声明一个servlet注册器Bean
        ServletRegistrationBean servletRegistrationBean = new ServletRegistrationBean();
        // 设置相应的servlet
        servletRegistrationBean.setServlet(new BeanServlet());
        // 设置名字
        servletRegistrationBean.setName("BeanServlet");
        // 添加映射规则
        servletRegistrationBean.addUrlMappings("/BeanServlet");
        return servletRegistrationBean;

    }
 

这里的factory是TomcatServletWebServerFactory,因为自动配置类ServletWebServerFactoryAutoConfiguration里只有静态内部类里的Tomcat生效了

所以是TomcatServletWebServerFactory.getWebServer,这样tomcat就启动了

 

4.外置Tomcat如何启动SpringBoot

设置当前maven项目的打包方式,让tomcat相关的依赖不参与打包部署 ,因为外置tomcat服务器已经有这些jar包

 <!--打包方式  默认是jar-->
    <packaging>war</packaging>
<!--让它不参与打包部署-->
        <dependency>
            <artifactId>spring-boot-starter-tomcat</artifactId>
            <groupId>org.springframework.boot</groupId>
            <scope>provided</scope>
        </dependency>
// 当tomcat启动时就会调用configure方法, 从而在springboot启动类的基础启动springboot
// 什么时候调用?
public class TomcatStartSpringBoot extends SpringBootServletInitializer {

    @Override
    protected SpringApplicationBuilder configure(SpringApplicationBuilder builder) {
        return builder.sources(Application.class);
    }
}

5.什么是SPI机制

 SPI ,全称为 Service Provider Interface(服务提供者接口),是一种服务发现机制。它通过在ClassPath路径下的META-INF/services文件夹查找文件,自动加载文件里所定义的类。

大概: 当servlet容器启动时候 就会去META-INF/services 文件夹中找到javax.servlet.ServletContainerInitializer, 这个文件里面肯定绑定一个ServletContainerInitializer实现类. 当servlet容器启动时候就会去该文件中找到ServletContainerInitializer的实现类,从而创建它的实例调用onstartUp,这里面又依次调用WebApplicationInitializer 实现类的onstartUp

在spring-boot-web的META-INF/services的SPI文件中实现类是org.springframework.web.SpringServletContainerInitializer

 其实这AbstractDispatcherServletInitializer,AbstractContextLoaderInitializer2个实现类就是帮我们创建了ContextLoaderListener 和DispatcherServlet

 

@HandlesTypes(WebApplicationInitializer.class),@HandlesTypes传入的类为ServletContainerInitializer感兴趣的,容器会自动在classpath中找到 WebApplicationInitializer 会传入到onStartup方法的webAppInitializerClasses中。Set<Class<?>> webAppInitializerClasses 这里面也包括之前定义的TomcatStartSpringBoot(因为之前继承的SpringBootServletInitializer 实现了WebApplicationInitializer )

@HandlesTypes(WebApplicationInitializer.class)
public class SpringServletContainerInitializer implements ServletContainerInitializer {
	@Override
	public void onStartup(@Nullable Set<Class<?>> webAppInitializerClasses, ServletContext servletContext)
			throws ServletException {

		List<WebApplicationInitializer> initializers = new LinkedList<>();

		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 {
						initializers.add((WebApplicationInitializer)
								ReflectionUtils.accessibleConstructor(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);
		}
	}

}
举报

相关推荐

0 条评论