• SpringBoot(二)核心原理


     SpringBoot

    SpringApplicationRunListener:接口的作用主要就是在Spring Boot 启动初始化的过程中可以通过SpringApplicationRunListener接口回调来让用户在启动的各个流程中可以加入自己的逻辑

     

     

    @SpringBootApplication annotation can be used to enable those three features, that is:

    • @EnableAutoConfiguration: enable Spring Boot’s auto-configuration mechanism(扫描“spring.factories”文件,实现自动注册)
    • @ComponentScan: enable @Component scan on the package where the application is located (see the best practices)等同于(<compan-scan/>扫描加上注解的类)
    • @Configuration: allow to register extra beans in the context or import additional configuration classes等同于(spring中在spring.xml文件中 写一个<bean/>。bean注册及bean管理)

    内置Tomcat

    The @SpringBootApplication annotation is equivalent to using @Configuration@EnableAutoConfiguration, and @ComponentScan with their default attributes, as shown in the following example:

    SpringBoot自动配置模块

    该配置模块的主要使用到了SpringFactoriesLoader,即Spring工厂加载器,该对象提供了loadFactoryNames方法,入参为factoryClass和classLoader,即需要传入工厂类名称和对应的类加载器,方法会根据指定的classLoader,加载该类加器搜索路径下的指定文件,即spring.factories文件,传入的工厂类为接口,而文件中对应的类则是接口的实现类,或最终作为实现类,所以文件中一般为如下图这种一对多的类名集合,获取到这些实现类的类名后,loadFactoryNames方法返回类名集合,方法调用方得到这些集合后,再通过反射获取这些类的类对象、构造方法,最终生成实例。

     

    SpringBoot自动化配置关键组件关系图

    mybatis-spring-boot-starterspring-boot-starter-web等组件的META-INF文件下均含有spring.factories文件,自动配置模块中,SpringFactoriesLoader收集到文件中的类全名并返回一个类全名的数组,返回的类全名通过反射被实例化,就形成了具体的工厂实例,工厂实例来生成组件具体需要的bean。

    之前我们提到了EnableAutoConfiguration注解,其类图如下:

     

    可以发现其最终实现了ImportSelector(选择器)和BeanClassLoaderAware(bean类加载器中间件),重点关注一下AutoConfigurationImportSelectorselectImports方法。

     

     

    MybatisAutoConfiguration

     在上面的代码可以看到自动配置器会根据传入的factoryClass.getName()到项目系统路径下所有的spring.factories文件中找到相应的key,从而加载里面的类。我们就选取这个mybatis-spring-boot-autoconfigure下的spring.factories文件

    发现Spring的@Configuration,俨然是一个通过注解标注的springBean,继续向下看,

    • @ConditionalOnClass({ SqlSessionFactory.class, SqlSessionFactoryBean.class}):当存在SqlSessionFactory.classSqlSessionFactoryBean.class这两个类时才解析MybatisAutoConfiguration配置类,否则不解析这一个配置类,make sence,我们需要mybatis为我们返回会话对象,就必须有会话工厂相关类。

    • @CondtionalOnBean(DataSource.class):只有处理已经被声明为bean的dataSource。

    • @ConditionalOnMissingBean(MapperFactoryBean.class)这个注解的意思是如果容器中不存在name指定的bean则创建bean注入,否则不执行(该类源码较长,篇幅限制不全粘贴)

    以上配置可以保证sqlSessionFactory、sqlSessionTemplate、dataSource等mybatis所需的组件均可被自动配置,@Configuration注解已经提供了Spring的上下文环境,所以以上组件的配置方式与Spring启动时通过mybatis.xml文件进行配置起到一个效果。

    通过分析我们可以发现,只要一个基于SpringBoot项目的类路径下存在SqlSessionFactory.classSqlSessionFactoryBean.class,并且容器中已经注册了dataSourceBean,就可以触发自动化配置,意思说我们只要在maven的项目中加入了mybatis所需要的若干依赖,就可以触发自动配置,但引入mybatis原生依赖的话,每集成一个功能都要去修改其自动化配置类,那就得不到开箱即用的效果了。

     这里是截取的mybatis-spring-boot-starter的源码中pom.xml文件中所有依赖:

     

    因为maven依赖的传递性,我们只要依赖starter就可以依赖到所有需要自动配置的类,实现开箱即用的功能。也体现出Springboot简化了Spring框架带来的大量XML配置以及复杂的依赖管理,让开发人员可以更加关注业务逻辑的开发。

    SpringBoot 内置tomcat  

    @SpringBootApplication->,@EnableAutoConfiguration->@ComponentScan,@EnableAutoConfiguration->AutoConfigurationImportSelector:selectImports()

    -getAutoConfigurationEntry()->getCandidateConfigurations():

    org.springframework.boot.autoconfigure.EnableAutoConfiguration=org.springframework.boot.autoconfigure.web.servlet.ServletWebServerFactoryAutoConfiguration,

    autoconfig.jar 下面 spring-boot-autoconfigure-2.1.1.RELEASE.jar->spring.factories

    ServletWebServerFactoryAutoConfiguration(生产出一个servlet容器)):
    EmbeddedTomcat->TomcatServletWebServerFactory->getWebServer
    public WebServer getWebServer(ServletContextInitializer... initializers) {
    //等于创建了server 里面在添加Connector,host 等 Tomcat tomcat = new Tomcat(); File baseDir = this.baseDirectory != null ? this.baseDirectory : this.createTempDir("tomcat"); tomcat.setBaseDir(baseDir.getAbsolutePath()); Connector connector = new Connector(this.protocol); tomcat.getService().addConnector(connector); this.customizeConnector(connector); tomcat.setConnector(connector); tomcat.getHost().setAutoDeploy(false); this.configureEngine(tomcat.getEngine()); Iterator var5 = this.additionalTomcatConnectors.iterator(); while(var5.hasNext()) { Connector additionalConnector = (Connector)var5.next(); tomcat.getService().addConnector(additionalConnector); } this.prepareContext(tomcat.getHost(), initializers); return this.getTomcatWebServer(tomcat); }

    Tomcat 在什么时候被创建的?

    SpringApplication.run(SpringbootProviderApplication.class, args)->refreshContext()->onRefresh->
    ServletWebServerApplicationContext->createWebServer()->
     protected void onRefresh() {
            super.onRefresh();
    
            try {
                this.createWebServer();
            } catch (Throwable var2) {
                throw new ApplicationContextException("Unable to start web server", var2);
            }
        }
      private void createWebServer() {
            WebServer webServer = this.webServer;
            ServletContext servletContext = this.getServletContext();
            if (webServer == null && servletContext == null) {
    //在加载的时候getWebServerFactory 已经加载了 ServletWebServerFactory factory = this.getWebServerFactory();
    //创建webServer ServletWebServerFactory 有不同的实现类 如 tomcat ,jetty的 this.webServer = factory.getWebServer(new ServletContextInitializer[]{this.getSelfInitializer()}); } else if (servletContext != null) { try { this.getSelfInitializer().onStartup(servletContext); } catch (ServletException var4) { throw new ApplicationContextException("Cannot initialize servlet context", var4); } } this.initPropertySources(); }

    SpringApplication.run()

    Spring Context   IOC 容器 (spring所有初始化的操作)

    Listener

    Filter

    1.创建SpringApplication应用

     public SpringApplication(ResourceLoader resourceLoader, Class... primarySources) {
            this.sources = new LinkedHashSet();
            this.bannerMode = Mode.CONSOLE;
            this.logStartupInfo = true;
            this.addCommandLineProperties = true;
            this.addConversionService = true;
            this.headless = true;
            this.registerShutdownHook = true;
            this.additionalProfiles = new HashSet();
            this.isCustomEnvironment = false;
            this.resourceLoader = resourceLoader;
            Assert.notNull(primarySources, "PrimarySources must not be null");
            this.primarySources = new LinkedHashSet(Arrays.asList(primarySources));
    //判断web应用类型
    this.webApplicationType = WebApplicationType.deduceFromClasspath();
    //初始化
    this.setInitializers(this.getSpringFactoriesInstances(ApplicationContextInitializer.class));
    //设计监听器 SpringFactories-> spring.factories文件
    this.setListeners(this.getSpringFactoriesInstances(ApplicationListener.class)); this.mainApplicationClass = this.deduceMainApplicationClass(); }
    # Application Listeners
    org.springframework.context.ApplicationListener=
    org.springframework.boot.autoconfigure.BackgroundPreinitializer
    # Auto Configuration Import Listeners
    org.springframework.boot.autoconfigure.AutoConfigurationImportListener=
    org.springframework.boot.autoconfigure.condition.ConditionEvaluationReportAutoConfigurationImportListener

    2.run (以下源码删除了部分代码)

     public ConfigurableApplicationContext run(String... args) {
           
            ConfigurableApplicationContext context = null;
            Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList();
            this.configureHeadlessProperty();  
            Collection exceptionReporters;
            try {
                ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
                ConfigurableEnvironment environment = this.prepareEnvironment(listeners, applicationArguments);
                this.configureIgnoreBeanInfo(environment);
    //打印banner Banner printedBanner
    = this.printBanner(environment);
    // spring上下文 context
    = this.createApplicationContext(); exceptionReporters = this.getSpringFactoriesInstances(SpringBootExceptionReporter.class, new Class[]{ConfigurableApplicationContext.class}, context); this.prepareContext(context, environment, listeners, applicationArguments, printedBanner);
    // 在该方法中加载tomcat :AbstractApplicationContext->refresh——>onRefresh(创建web容器)
    this.refreshContext(context); this.afterRefresh(context, applicationArguments); if (this.logStartupInfo) { (new StartupInfoLogger(this.mainApplicationClass)).logStarted(this.getApplicationLog(), stopWatch); } listeners.started(context); this.callRunners(context, applicationArguments); } catch (Throwable var10) { this.handleRunFailure(context, var10, exceptionReporters, listeners); throw new IllegalStateException(var10); } try { listeners.running(context); return context; } catch (Throwable var9) { this.handleRunFailure(context, var9, exceptionReporters, (SpringApplicationRunListeners)null); throw new IllegalStateException(var9); } }

    创建web容器即Tomcat

     protected void onRefresh() {
            super.onRefresh();
    
            try {
                this.createWebServer();
            } catch (Throwable var2) {
                throw new ApplicationContextException("Unable to start web server", var2);
            }
        }

    spring-boot-starter?

     自己动手写个spring-boot-start  

  • 相关阅读:
    DOM 文本节点 、节点列表
    haslayout综合【转】
    css兼容性详解
    重温textjustify:interideograph
    掌握三点即可轻松打造出良好的交互设计效果
    ASP.NET 中的正则表达式
    Net中的反射使用入门
    ASP.NET2.0页面状态持续[转]
    使用XmlTextWriter对象创建XML文件[转]
    判断SQLSERVER数据库表字段为空的问题
  • 原文地址:https://www.cnblogs.com/fanBlog/p/11126267.html
Copyright © 2020-2023  润新知