SpringBoot原理浅析

starter-* POM依赖

使用SpringBoot开发时,在pom.xml文件中引入的依赖一般都是形如spring-boot-starter-*。starter依赖是居于某个场景或者功能的,我们引入一个starter依赖之后,它会间接引入实现这个场景或功能所需的其他依赖。我们可以把这些starters称为场景启动器,只需要在项目里面引入这些starter相关场景的所有依赖都会导入进来。要用什么功能就导入什么场景的启动器。这里以spring-boot-starter-web为例分析。


    org.springframework.boot
    spring-boot-starter-web

spring-boot-starter-web的间接依赖

我们来看下spring-boot-starter-web的pom文件,它定义了一个父类spring-boot-starters

    
        org.springframework.boot
        spring-boot-starters
        1.5.4.RELEASE
    
    spring-boot-starter-web
    Spring Boot Web Starter
    http://projects.spring.io/spring-boot/
    
        Pivotal Software, Inc.
        http://www.spring.io
    
    
        ${basedir}/../..
    

spring-boot-starters的打包类型为pom,它定义好了SpringBoot中所有的starter,同时它的父类为spring-boot-parent

    
        org.springframework.boot
        spring-boot-parent
        1.5.4.RELEASE
        ../spring-boot-parent
    
    spring-boot-starters
    pom
    Spring Boot Starters
    Spring Boot Starters
    http://projects.spring.io/spring-boot/
    
        Pivotal Software, Inc.
        http://www.spring.io
    
    
        ${basedir}/..
    
    
        spring-boot-starter
        spring-boot-starter-activemq
        spring-boot-starter-amqp
        spring-boot-starter-aop
        spring-boot-starter-artemis
        spring-boot-starter-batch
        spring-boot-starter-cache
        spring-boot-starter-cloud-connectors
        spring-boot-starter-data-cassandra
        spring-boot-starter-data-couchbase
        spring-boot-starter-data-elasticsearch
        spring-boot-starter-data-gemfire
        spring-boot-starter-data-jpa
        spring-boot-starter-data-ldap
        spring-boot-starter-data-mongodb
        spring-boot-starter-data-neo4j
        spring-boot-starter-data-redis
        spring-boot-starter-data-rest
        spring-boot-starter-data-solr
        spring-boot-starter-freemarker
        spring-boot-starter-groovy-templates
        spring-boot-starter-hateoas
        spring-boot-starter-integration
        spring-boot-starter-jdbc
        spring-boot-starter-jersey
        spring-boot-starter-jetty
        spring-boot-starter-jooq
        spring-boot-starter-jta-atomikos
        spring-boot-starter-jta-bitronix
        spring-boot-starter-jta-narayana
        spring-boot-starter-logging
        spring-boot-starter-log4j2
        spring-boot-starter-mail
        spring-boot-starter-mobile
        spring-boot-starter-mustache
        spring-boot-starter-actuator
        spring-boot-starter-parent
        spring-boot-starter-security
        spring-boot-starter-social-facebook
        spring-boot-starter-social-twitter
        spring-boot-starter-social-linkedin
        spring-boot-starter-remote-shell
        spring-boot-starter-test
        spring-boot-starter-thymeleaf
        spring-boot-starter-tomcat
        spring-boot-starter-undertow
        spring-boot-starter-validation
        spring-boot-starter-web
        spring-boot-starter-websocket
        spring-boot-starter-web-services
    

spring-boot-parent的父pom为spring-boot-dependencies,该pom文件中定义了我们所需要具体jar包的version。


        
        5.14.5
        2.7.7
        1.9.53
        1.5.5
        1.8.10
        2.6.0
        3.9.3
        2.1.4
        2.3.5
        3.1.4
        1.3.3
        1.9.3
        3.2.2
        1.10
        1.4
        2.1.1
        2.1
        1.6
        2.4.2
        2.3.7
        2.1.0
        1.3.2
        10.13.1.1
        1.6.1
        3.1.4
        2.10.4
        3.2.2
        1.50.5
        3.2.1
        2.3.26-incubating
        2.4.5
        8.2.4
        3.0.0
        2.9
        2.4.11
        2.8.0
        1.4.195
        1.3
        3.7.7
        3.7.1
        1.1.3
        5.0.12.Final
        5.3.5.Final
        2.5.1
        2.3.13
        2.4.11
        2.3.5
        2.21
        4.1.3
        4.5.3
        4.4.6
        8.2.6.Final
        2.8.8
        2.7.8
        3.21.0-GA 
        1.0.0
        1.5.6
        1.2
        1.1.0.Final
        1.1.6
        2.2.13
        3.3.1.Final
        7.6.0.Final
        2.0.6
        2.9.0
        2.25.1
        2.0.4
        9.4.5.v20170502
        2.2.0.v201112011158
        8.0.33
        1.1-rev-1
        1.13
        4.2.2
        2.9.9
        1.3.6
        3.9.2
        20140107
        1.4.0
        2.2.0
        1.2
        1.3.1
        4.12
        3.5.3
        2.7
        1.1.11
        1.16.16
        1.5.9
        6.1.0.jre7
        1.10.19
        3.4.2
        5.1.42
        5.5.24.Final
        1.9.22
        2.1.3
        9.4.1212.jre7
        4.1.4
        2.0.8.RELEASE
        2.0.7.RELEASE
        2.53.1
        2.21
        2.2.2
        3.1.0
        1.1.1
        1.7.25
        1.17
        5.5.4
        1.0-groovy-2.4
        4.3.9.RELEASE
        1.7.3.RELEASE
        1.2.4.RELEASE
        3.0.7.RELEASE
        Ingalls-SR4
        0.23.0.RELEASE
        4.3.10.RELEASE
        1.2.2.RELEASE
        1.1.6.RELEASE
        2.3.1.RELEASE
        1.2.7.RELEASE
        1.1.5.RELEASE
        1.2.0.RELEASE
        1.1.3.RELEASE
        1.2.0.RELEASE
        4.2.3.RELEASE
        1.0.8.RELEASE
        2.0.14.RELEASE
        1.3.1.RELEASE
        1.1.4.RELEASE
        2.0.3.RELEASE
        1.0.2.RELEASE
        1.1.2.RELEASE
        2.4.0.RELEASE
        3.15.1
        3.1.0
        ${javax-mail.version}
        2.1.5.RELEASE
        2.1.3.RELEASE
        2.1.2.RELEASE
        1.4.0
        1.3
        2.1.0.RELEASE
        8.5.15
        1.4.15.Final
        3.2.1
        9f96c74
        0.32-1
        1.6.3
        1.4.01
        
        1.10
        1.5.0
        2.2.2
        1.8
        2.6
        2.6.1
        3.1
        2.10
        2.8.2
        2.10
        1.4
        2.18.1
        2.5.2
        1.10
        2.2
        2.6
        2.10.4
        2.7
        2.4.3
        3.5.1
        2.4
        2.18.1
        2.6
        2.2
    

这就解释了springboot为我们定义好了依赖包的版本,在开发过程中jar包冲突是个常见且让人头痛的问题,而使用springboot的话由于其帮我们选择好了依赖包的版本,所以很好的解决了包冲突等繁琐问题。如果spring-boot-parent没有定义的jar包,而此时项目中需要使用的话,需要我们自己定义好版本。

程序入口

@SpringBootApplication
public class StartSpringBootMain {

    public static void main(String[] args) throws Exception {
        SpringApplication.run(StartSpringBootMain.class, args);
    }
}

我们在主程序类中加入@SpringBootApplication注解就能启用SpringBoot,下面就来看看这个注解类

@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 {
  ......
}

其中@ComponentScan是我们熟悉的组件扫描,其作用是使@Controller、@Service、@Componet等组件生效。这里它排除了两个Filter类TypeExcludeFilterAutoConfigurationExcludeFilter

@SpringBootConfiguration注解相当于@Configuration,表示应用该注解的类为配置类,相当于applicationContext.xml文件。

@Configuration
public @interface SpringBootConfiguration {
}

@EnableAutoConfiguration注解表示开启自动配置的功能,之前需要开发人员手动进行的配置,SpringBoot帮我们自动配置。它的源码如下

@AutoConfigurationPackage
@Import(EnableAutoConfigurationImportSelector.class)
public @interface EnableAutoConfiguration {
  ......
}

@AutoConfigurationPackage自动配置包

@Import(AutoConfigurationPackages.Registrar.class)
public @interface AutoConfigurationPackage {

}

AutoConfigurationPackages.Registrar的源码如下

static class Registrar implements ImportBeanDefinitionRegistrar, DeterminableImports {

        @Override
        public void registerBeanDefinitions(AnnotationMetadata metadata,
                BeanDefinitionRegistry registry) {
            register(registry, new PackageImport(metadata).getPackageName());
        }

new PackageImport(metadata).getPackageName()会获取主配置类(@SpringBootApplication标注的类)所在的包名,然后会将该包及下面所有子包里面的所有组件扫描到Spring容器中。
@Import注解的含义为给Spring容器中注入一个对象,@Import(EnableAutoConfigurationImportSelector.class)表示给Spring容器中注入EnableAutoConfigurationImportSelector对象,该对象的字面意思为“开启自动配置导入选择器”。同时它还继承自AutoConfigurationImportSelector

@Deprecated
public class EnableAutoConfigurationImportSelector
        extends AutoConfigurationImportSelector {

AutoConfigurationImportSelector类的作用是将所有需要导入的组件以全类名的方式添加到容器中;其通过selectImports方法筛选出选哟导入的组件的全类名。

    public String[] selectImports(AnnotationMetadata annotationMetadata) {
        try {
            AutoConfigurationMetadata autoConfigurationMetadata = AutoConfigurationMetadataLoader
                    .loadMetadata(this.beanClassLoader);
            AnnotationAttributes attributes = getAttributes(annotationMetadata);
  //这句获取自动配置类
            List configurations = getCandidateConfigurations(annotationMetadata,
                    attributes);
            configurations = removeDuplicates(configurations);
            configurations = sort(configurations, autoConfigurationMetadata);
            Set exclusions = getExclusions(annotationMetadata, attributes);
            checkExcludedClasses(configurations, exclusions);
            configurations.removeAll(exclusions);
            configurations = filter(configurations, autoConfigurationMetadata);
            fireAutoConfigurationImportEvents(configurations, exclusions);
            return configurations.toArray(new String[configurations.size()]);
        }
        catch (IOException ex) {
            throw new IllegalStateException(ex);
        }
    }

getCandidateConfigurations方法用来获取自动配置类,该方法内部的实现最终会委托给SpringFactoriesLoader.loadFactoryNames(EnableAutoConfiguration.class,classLoader);方法,loadFactoryNames方法的源码如下:

public static List loadFactoryNames(Class factoryClass, ClassLoader classLoader) {
    String factoryClassName = factoryClass.getName();
    try {
        Enumeration urls = classLoader != null ? classLoader.getResources("META-INF/spring.factories") : ClassLoader.getSystemResources("META-INF/spring.factories");
        ArrayList result = new ArrayList();
        while(urls.hasMoreElements()) {
            URL url = (URL)urls.nextElement();
            Properties properties = PropertiesLoaderUtils.loadProperties(new UrlResource(url));
            String factoryClassNames = properties.getProperty(factoryClassName);
            result.addAll(Arrays.asList(StringUtils.commaDelimitedListToStringArray(factoryClassNames)));
        }
        return result;
    } catch (IOException var8) {
        throw new IllegalArgumentException("Unable to load [" + factoryClass.getName() + "] factories from location [" + "META-INF/spring.factories" + "]", var8);
    }
}

可以看到其在Spring Boot启动的时候从类路径下的META-INF/spring.factories中获取EnableAutoConfiguration指定的值,将这些值作为自动配置类导入到容器中,自动配置类就生效,帮我们进行自动配置工作;
我们调式源码的时候可以看到加载的自动配置类96个之多



而这些类是定义在spring.factories文件中有定义的。


今天先分析到这里,还存在没有理清楚的地方,后续有时间的话再更新吧。

你可能感兴趣的:(SpringBoot原理浅析)