SpringBoot原理简单分析

1.1 起步依赖分析

1.1.1 spring-boot-starter-parent分析

  • 该文件是SpringBoot默认控制依赖版本,所有SpringBoot都继承自这个

1.1.1.1 常见依赖管理

按住ctrl ,单击spring-boot-starter-parent后,看到spring-bbot-dependencies

继续点击spring-bbot-dependencies,可发现这个pom文件中在properties标签中有很多项目中常用依赖名称和版本号,这些是当前SpringBoot版本给你选好了的依赖版本。

再继续往下翻,会看到有个dependencyManagement的标签,这个标签管理着各种常见依赖。

1.1.1.1 关于application文件的加载

spring-bbot-dependencies下面可看到


不管是yml,还是yaml或properties都在这里有定义资源路径和名称
所以在项目的resources文件夹下创建application*.yml(yaml,properties)都可以加载

1.1.1 spring-boot-starter-web分析

按住ctrl ,单击spring-boot-starter-web后
看到dependencies下面有spring-boot-starter、spring-boot-tomcat,spring-web,spring-webmvc等等依赖。
点进spring-boot-tomcat后你会发现dependencies标签下有apache之类的一些依赖。

1.2 自动配置原理解析

1.2.1 @SpringBootApplication注解解析

安装ctrl,点击@SpringBootApplication注解,代码如下:

@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@SpringBootConfiguration 
@EnableAutoConfiguration
@ComponentScan(
    excludeFilters = {@Filter(
    type = FilterType.CUSTOM,
    classes = {TypeExcludeFilter.class}
), @Filter(
    type = FilterType.CUSTOM,
    classes = {AutoConfigurationExcludeFilter.class}
)}
)
public @interface SpringBootApplication {
    @AliasFor(
        annotation = EnableAutoConfiguration.class
    )
    Class<?>[] exclude() default {};

    @AliasFor(
        annotation = EnableAutoConfiguration.class
    )
    String[] excludeName() default {};

    @AliasFor(
        annotation = ComponentScan.class,
        attribute = "basePackages"
    )
    String[] scanBasePackages() default {};

    @AliasFor(
        annotation = ComponentScan.class,
        attribute = "basePackageClasses"
    )
    Class<?>[] scanBasePackageClasses() default {};
}

@SpringBootApplication = {@Configuration, @EnableAutoConfiguration,@ComponentScan}

1.2.1.1 @SpringBootConfiguration 注解

单击进入后发现,代码如下:



package org.springframework.boot;

import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import org.springframework.context.annotation.Configuration;

@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Configuration
public @interface SpringBootConfiguration {
}

  • 这个类中主要的就是@Configuration注解,这个声明当前类,是配置类。
  • @SpringBootConfiguration继承自@Configuration,内部并无任何改变,因此@SpringBootConfiguration功能和@Configuration是一样的

1.2.1.2 @EnableAutoConfiguration 注解

  • 点进@EnableAutoConfiguration注解后内容如下
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@AutoConfigurationPackage
@Import({AutoConfigurationImportSelector.class})
public @interface EnableAutoConfiguration {
    String ENABLED_OVERRIDE_PROPERTY = "spring.boot.enableautoconfiguration";

    Class<?>[] exclude() default {};

    String[] excludeName() default {};
}

这里关键是@Import({AutoConfigurationImportSelector.class}),通过AutoConfigurationImportSelector.class,@EnableAutoConfiguration可以帮助SpringBoot应用将所有符合条件的@Configuration配置都加载到当前SpringBoot创建并使用的IOC容器。

接下来我们进入AutoConfigurationImportSelector.class,可以看到内部有一个方法

   public String[] selectImports(AnnotationMetadata annotationMetadata) {
        if (!this.isEnabled(annotationMetadata)) {
            return NO_IMPORTS;
        } else {
            AutoConfigurationMetadata autoConfigurationMetadata = AutoConfigurationMetadataLoader.loadMetadata(this.beanClassLoader);
            AutoConfigurationImportSelector.AutoConfigurationEntry autoConfigurationEntry = this.getAutoConfigurationEntry(autoConfigurationMetadata, annotationMetadata);//这里是重点
            return StringUtils.toStringArray(autoConfigurationEntry.getConfigurations());
        }
    }

我们看到这个函数中调用了getAutoConfigurationEntry(autoConfigurationMetadata, annotationMetadata)方法,进入到getAutoConfigurationEntry()下,在这个方法中有如下一段代码:

 List<String> configurations = this.getCandidateConfigurations(annotationMetadata, attributes);

点进getCandidateConfigurations()函数,代码如下:

   protected List<String> getCandidateConfigurations(AnnotationMetadata metadata, AnnotationAttributes attributes) {
        List<String> configurations = SpringFactoriesLoader.loadFactoryNames(this.getSpringFactoriesLoaderFactoryClass(), this.getBeanClassLoader());
        Assert.notEmpty(configurations, "No auto configuration classes found in META-INF/spring.factories. If you are using a custom packaging, make sure that file is correct.");
        return configurations;
    }

这段代码中可以看到这段字符串,“No auto configuration classes found in META-INF/spring.factories. If you are using a custom packaging, make sure that file is correct.”

在方法中借助Spring框架原有的一个工具类SpringFactoriesLoader的支持,@EnableAutoConfiguration可以智能的自动配置功效才得以大功告成!
接下来查看META-INF/spring.factories
查看当前所处包路径,可看到路径:package org.springframework.boot.autoconfigure;

打开箭头所指的地方,找到org.springframework.boot.autoconfigure,打开后可看见里面文件如下:

这里面存在spring.factories,打开spring.factories
spring.factories文件里每一个xxxAutoConfiguration文件一般都会有下面的条件注解:

  • @ConditionalOnClass : classpath中存在该类时起效
  • @ConditionalOnMissingClass : classpath中不存在该类时起效
  • @ConditionalOnBean : DI容器中存在该类型Bean时起效
  • @ConditionalOnMissingBean : DI容器中不存在该类型Bean时起效
  • @ConditionalOnSingleCandidate : DI容器中该类型Bean只有一个或@Primary的只有一个时起效
  • @ConditionalOnExpression : SpEL表达式结果为true时
  • @ConditionalOnProperty : 参数设置或者值一致时起效
  • @ConditionalOnResource : 指定的文件存在时起效
  • @ConditionalOnJndi : 指定的JNDI存在时起效
  • @ConditionalOnJava : 指定的Java版本存在时起效
  • @ConditionalOnWebApplication : Web应用环境下起效
  • @ConditionalOnNotWebApplication : 非Web应用环境下起效
org.springframework.boot.autoconfigure.web.reactive.WebFluxAutoConfiguration,\
org.springframework.boot.autoconfigure.web.reactive.error.ErrorWebFluxAutoConfiguration,\
org.springframework.boot.autoconfigure.web.reactive.function.client.ClientHttpConnectorAutoConfiguration,\
org.springframework.boot.autoconfigure.web.reactive.function.client.WebClientAutoConfiguration,\
org.springframework.boot.autoconfigure.web.servlet.DispatcherServletAutoConfiguration,\
org.springframework.boot.autoconfigure.web.servlet.ServletWebServerFactoryAutoConfiguration,\
org.springframework.boot.autoconfigure.web.servlet.error.ErrorMvcAutoConfiguration,\
org.springframework.boot.autoconfigure.web.servlet.HttpEncodingAutoConfiguration,\
org.springframework.boot.autoconfigure.web.servlet.MultipartAutoConfiguration,\
org.springframework.boot.autoconfigure.web.servlet.WebMvcAutoConfiguration,\
org.springframework.boot.autoconfigure.websocket.reactive.WebSocketReactiveAutoConfiguration,\
org.springframework.boot.autoconfigure.websocket.servlet.WebSocketServletAutoConfiguration,\
org.springframework.boot.autoconfigure.websocket.servlet.WebSocketMessagingAutoConfiguration,\
org.springframework.boot.autoconfigure.webservices.WebServicesAutoConfiguration,\
org.springframework.boot.autoconfigure.webservices.client.WebServiceTemplateAutoConfiguration

  • SpringFactoriesLoader

SpringFactoriesLoader属于Spring框架私有的一种扩展方案(类似于Java的SPI方案java.util.ServiceLoader),其主要功能就是从指定的配置文件MATA-INF/spring-factories加载配置,Spring-factories是一个典型的Java Properties文件,只不过Key和Value都是Java类型的完整类名,

对于@EnableAutoConfiguration来讲,SpringFactoriesLoader的用途稍微不同一些,其本意是为了提供SPI拓展的场景,而在@EnableAutoConfiguration场景中,它提供了一种配置查找的功能支持,即根据@EnableAutoConfiguration的完整类名org.springframework.boot.autoconfig.EnableAutoConfiguration作为查找的KEY,获得对应一组@Configuration类。

SpringFactoriesLoader是一个抽象类,类中定义的静态属性,定义了其加载资源的路径public static final String FACTORIES_RESOURCE_LOCATION = “META-INF/spring.factories”,此外还有三个静态方法:

  • loadFactories:加载指定的factoryClass并进行实例化。
  • loadFactoryNames:加载指定的factoryClass的名称集合。
  • instantiateFactory:对指定的factoryClass进行实例化。
public final class SpringFactoriesLoader {
    public static final String FACTORIES_RESOURCE_LOCATION = "META-INF/spring.factories";
    private static final Log logger = LogFactory.getLog(SpringFactoriesLoader.class);
    private static final Map<ClassLoader, MultiValueMap<String, String>> cache = new ConcurrentReferenceHashMap();

    private SpringFactoriesLoader() {
    }

    public static <T> List<T> loadFactories(Class<T> factoryClass, @Nullable ClassLoader classLoader) {
        Assert.notNull(factoryClass, "'factoryClass' must not be null");
        ClassLoader classLoaderToUse = classLoader;
        if (classLoader == null) {
            classLoaderToUse = SpringFactoriesLoader.class.getClassLoader();
        }

        List<String> factoryNames = loadFactoryNames(factoryClass, classLoaderToUse);
        if (logger.isTraceEnabled()) {
            logger.trace("Loaded [" + factoryClass.getName() + "] names: " + factoryNames);
        }

        List<T> result = new ArrayList(factoryNames.size());
        Iterator var5 = factoryNames.iterator();

        while(var5.hasNext()) {
            String factoryName = (String)var5.next();
            result.add(instantiateFactory(factoryName, factoryClass, classLoaderToUse));
        }

        AnnotationAwareOrderComparator.sort(result);
        return result;
    }

    public static List<String> loadFactoryNames(Class<?> factoryClass, @Nullable ClassLoader classLoader) {
        String factoryClassName = factoryClass.getName();
        return (List)loadSpringFactories(classLoader).getOrDefault(factoryClassName, Collections.emptyList());
    }

    private static Map<String, List<String>> loadSpringFactories(@Nullable ClassLoader classLoader) {
        MultiValueMap<String, String> result = (MultiValueMap)cache.get(classLoader);
        if (result != null) {
            return result;
        } else {
            try {
                Enumeration<URL> urls = classLoader != null ? classLoader.getResources("META-INF/spring.factories") : ClassLoader.getSystemResources("META-INF/spring.factories");
                LinkedMultiValueMap result = new LinkedMultiValueMap();

                while(urls.hasMoreElements()) {
                    URL url = (URL)urls.nextElement();
                    UrlResource resource = new UrlResource(url);
                    Properties properties = PropertiesLoaderUtils.loadProperties(resource);
                    Iterator var6 = properties.entrySet().iterator();

                    while(var6.hasNext()) {
                        Entry<?, ?> entry = (Entry)var6.next();
                        String factoryClassName = ((String)entry.getKey()).trim();
                        String[] var9 = StringUtils.commaDelimitedListToStringArray((String)entry.getValue());
                        int var10 = var9.length;

                        for(int var11 = 0; var11 < var10; ++var11) {
                            String factoryName = var9[var11];
                            result.add(factoryClassName, factoryName.trim());
                        }
                    }
                }

                cache.put(classLoader, result);
                return result;
            } catch (IOException var13) {
                throw new IllegalArgumentException("Unable to load factories from location [META-INF/spring.factories]", var13);
            }
        }
    }

    private static <T> T instantiateFactory(String instanceClassName, Class<T> factoryClass, ClassLoader classLoader) {
        try {
            Class<?> instanceClass = ClassUtils.forName(instanceClassName, classLoader);
            if (!factoryClass.isAssignableFrom(instanceClass)) {
                throw new IllegalArgumentException("Class [" + instanceClassName + "] is not assignable to [" + factoryClass.getName() + "]");
            } else {
                return ReflectionUtils.accessibleConstructor(instanceClass, new Class[0]).newInstance();
            }
        } catch (Throwable var4) {
            throw new IllegalArgumentException("Unable to instantiate factory class: " + factoryClass.getName(), var4);
        }
    }
}

loadFactories方法首先获取类加载器,然后调用loadFactoryNames方法获取所有的指定资源的名称集合、接着调用instantiateFactory方法实例化这些资源类并将其添加到result集合中。最后调用AnnotationAwareOrderComparator.sort方法进行集合的排序。

  • SpringBoot配置文件参数加载

spring.factories文件中:

org.springframework.boot.autoconfigure.websocket.servlet.WebSocketServletAutoConfiguration,\

按住ctrl,点击org.springframework.boot.autoconfigure.websocket.servlet.WebSocketServletAutoConfiguration


@Configuration
@ConditionalOnClass({Servlet.class, ServerContainer.class})
@ConditionalOnWebApplication(
    type = Type.SERVLET
)
@AutoConfigureBefore({ServletWebServerFactoryAutoConfiguration.class})
public class WebSocketServletAutoConfiguration {
    public WebSocketServletAutoConfiguration() {
    }

    @Configuration
    @ConditionalOnClass({Bootstrap.class})
    static class UndertowWebSocketConfiguration {
        UndertowWebSocketConfiguration() {
        }

        @Bean
        @ConditionalOnMissingBean(
            name = {"websocketServletWebServerCustomizer"}
        )
        public UndertowWebSocketServletWebServerCustomizer websocketServletWebServerCustomizer() {
            return new UndertowWebSocketServletWebServerCustomizer();
        }
    }

    @Configuration
    @ConditionalOnClass({WebSocketServerContainerInitializer.class})
    static class JettyWebSocketConfiguration {
        JettyWebSocketConfiguration() {
        }

        @Bean
        @ConditionalOnMissingBean(
            name = {"websocketServletWebServerCustomizer"}
        )
        public JettyWebSocketServletWebServerCustomizer websocketServletWebServerCustomizer() {
            return new JettyWebSocketServletWebServerCustomizer();
        }
    }

    @Configuration
    @ConditionalOnClass({Tomcat.class, WsSci.class})
    static class TomcatWebSocketConfiguration {
        TomcatWebSocketConfiguration() {
        }

        @Bean
        @ConditionalOnMissingBean(
            name = {"websocketServletWebServerCustomizer"}
        )
        public TomcatWebSocketServletWebServerCustomizer websocketServletWebServerCustomizer() {
            return new TomcatWebSocketServletWebServerCustomizer();
        }
    }
}

根据上述代码和代码之前的注解说明,可看出这些代码在什么时候生效。

接下来继续看当前类上:@AutoConfigureBefore({ServletWebServerFactoryAutoConfiguration.class})
,点进ServletWebServerFactoryAutoConfiguration.class


@Configuration
@AutoConfigureOrder(-2147483648)
@ConditionalOnClass({ServletRequest.class})
@ConditionalOnWebApplication(
    type = Type.SERVLET
)
@EnableConfigurationProperties({ServerProperties.class})
@Import({ServletWebServerFactoryAutoConfiguration.BeanPostProcessorsRegistrar.class, EmbeddedTomcat.class, EmbeddedJetty.class, EmbeddedUndertow.class})
public class ServletWebServerFactoryAutoConfiguration {
    public ServletWebServerFactoryAutoConfiguration() {
    }

    @Bean
    public ServletWebServerFactoryCustomizer servletWebServerFactoryCustomizer(ServerProperties serverProperties) {
        return new ServletWebServerFactoryCustomizer(serverProperties);
    }
	......
}

自动装配过程中,通过@EnableConfigurationProperties({ServerProperties.class}),配置服务器相关属性。
点进ServerProperties.class


@ConfigurationProperties(
    prefix = "server",
    ignoreUnknownFields = true
)
public class ServerProperties {
    private Integer port;
    private InetAddress address;
    @NestedConfigurationProperty
    private final ErrorProperties error = new ErrorProperties();
    private Boolean useForwardHeaders;
    private String serverHeader;
    private DataSize maxHttpHeaderSize = DataSize.ofKilobytes(8L);
    private Duration connectionTimeout;
    @NestedConfigurationProperty
    private Ssl ssl;
    @NestedConfigurationProperty
    private final Compression compression = new Compression();
    @NestedConfigurationProperty
    private final Http2 http2 = new Http2();
    private final ServerProperties.Servlet servlet = new ServerProperties.Servlet();
    private final ServerProperties.Tomcat tomcat = new ServerProperties.Tomcat();
    private final ServerProperties.Jetty jetty = new ServerProperties.Jetty();
    private final ServerProperties.Undertow undertow = new ServerProperties.Undertow();
	......
}

可以看见port等等字段,这些自动在程序启动是通过@ConfigurationProperties(
prefix = “server”,
ignoreUnknownFields = true
)注解加载,如果不配置,它会有默认值,从additional-spring-configuration-metadata.json中加载。

可以看到server.port默认值是8080,如果我们不对它进行覆盖的话,Springboot启动后,默认端口就是8080
对于这些属性的覆盖可以通过application.yml(yaml,properties)进行覆盖,如

这样,启动项目后端口就会是8888。

原文地址: https://www.itxsl.cn/content.html?id=58

猜你喜欢

转载自blog.csdn.net/xslde_com/article/details/94726663