为了账号安全,请及时绑定邮箱和手机立即绑定

为什么要使用SpringBoot?使用SpringBoot的最大好处是什么?

使用SpringBoot的最大好处就是简化配置,它实现了自动化配置。         

这里以SpringBoot 2.1.4.RELEASE版本和Spring 5.1.6.RELEASE版本为例。     

API文档:https://docs.spring.io/spring-boot/docs/current/api/     

自动化配置的原理如下:     

一个SpringBoot构建的项目都会有一个入口启动类,其中有个最重要的注解就是@SpringBootApplication,其源码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
@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类上有一个重要的注解@EnableAutoConfiguration,它就是实现自动化配置的核心。当SpringBoot项目启动的时候,就会调用@EnableAutoConfiguration来进一步加载系统所需的一些配置信息,完成自动化配置。    

@EnableAutoConfiguration的源码如下:    

1
2
3
4
5
6
7
8
9
10
11
@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 {};
}

在EnableAutoConfiguration类中,它使用@Import注解来导入配置类AutoConfigurationImportSelector。     

使用@Import注解可以导入三种类型的配置类,如下:     

(1)直接导入配置类:@Import({xxxConfiguration.class})      


(2)依据条件选择配置类:@Import({xxxSelector.class})      


(3)动态注册 Bean:@Import({xxxRegistrar.class})      

  

我们进入查看AutoConfigurationImportSelector类的源码,(由于源码太多,在此不再展示),其中用到了一个重要的类SpringFactoriesLoader,该类位于org.springframework.core.io.support包下,它才是真正加载项目所需要的jar包的类,它主要用于加载 classpath下所有 JAR 文件的 META-INF/spring.factories 文件,并分析出其中定义的工厂类。这些工厂类进而被启动逻辑使用,应用于进一步初始化工作。       

SpringFactoriesLoader类是spring框架自己使用的内部工具类,本身被声明为 final,表示不可以被其他类继承。    

SpringFactoriesLoader类的源码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by Fernflower decompiler)
//
package org.springframework.core.io.support;
import java.io.IOException;
import java.net.URL;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Enumeration;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.Map.Entry;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.core.annotation.AnnotationAwareOrderComparator;
import org.springframework.core.io.UrlResource;
import org.springframework.lang.Nullable;
import org.springframework.util.Assert;
import org.springframework.util.ClassUtils;
import org.springframework.util.ConcurrentReferenceHashMap;
import org.springframework.util.LinkedMultiValueMap;
import org.springframework.util.MultiValueMap;
import org.springframework.util.ReflectionUtils;
import org.springframework.util.StringUtils;
/**
 * SpringFactoriesLoader#loadFactories设计用于加载和实例化指定类型的工厂,这些工厂类型的定义
 * 来自classpath中多个JAR包内常量FACTORIES_RESOURCE_LOCATION所指定的那些spring.factories文件。
 * spring.factories文件的格式必须是属性文件格式,每条属性的key必须是接口或者抽象类的全限定名,
 * 而属性值value是一个逗号分割的实现类的名称。
*/
public final class SpringFactoriesLoader {
    /*
    * 要加载的资源路径,该常量定义了该工具类要从每个jar包中提取的工厂类定义属性文件的相对路径
    * 在classpath中的多个JAR中,要扫描的工厂配置文件的在本JAR包中的路径。
    * 实际上,Springboot的每个 autoconfigure包都包含spring.factories这个配置文件。
    */
    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() {
    }
    /**
    * @param factoryClass 工厂所属接口/抽象类全限定名称
    * @param classLoader 所要使用的类加载器
    *                    
    * 该方法会读取classpath上所有的jar包中的所有  META-INF/spring.factories 属性文件,找出其中定义的匹配类型 factoryClass 的工厂类,
    * 然后创建每个工厂类的对象/实例,并返回这些工厂类对象/实例的列表
    */
    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();
        }
        //加载类型为factoryClass的工厂的名称,其实是一个个的全限定类名,使用指定的classloader:classLoaderToUse
        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;
    }
    /**
    
    * @param factoryClass  工厂所属接口/抽象类全限定名称
    * @param classLoader 类加载器
    * @return 
    
    * 该方法会读取classpath上所有的jar包中的所有  META-INF/spring.factories 属性文件,找出其中定义的匹配类型 factoryClass 的工厂类,
    * 然后并返回这些工厂类的名字列表,注意是包含包名的全限定名。
    */
    public static List<String> loadFactoryNames(Class<?> factoryClass, @Nullable ClassLoader classLoader) {
        String factoryClassName = factoryClass.getName();
        // 1. 使用指定的classloader扫描classpath上所有的JAR包中的文件META-INF/spring.factories,加载其中的多值工厂属性定义,使用多值Map的形式返回,
        // 2. 返回多值Map中key为factoryClassName的工厂名称列表,如果没有相应的entry,返回空列表而不是返回null
        return (List)loadSpringFactories(classLoader).getOrDefault(factoryClassName, Collections.emptyList());
    }
    /**
    * @param classLoader 类加载器
    *
    * 使用指定的classloader扫描classpath上所有的JAR包中的文件META-INF/spring.factories,加载其中的多值
    * 工厂属性定义,使用多值Map的形式返回
    **/
    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 {
                // 扫描classpath上所有JAR中的文件META-INF/spring.factories
                Enumeration<URL> urls = classLoader != null ? classLoader.getResources("META-INF/spring.factories") : ClassLoader.getSystemResources("META-INF/spring.factories");
                LinkedMultiValueMap result = new LinkedMultiValueMap();
                while(urls.hasMoreElements()) {
                    // 找到的每个META-INF/spring.factories文件都是一个Properties文件,将其内容
                    // 加载到一个 Properties 对象然后处理其中的每个属性
                    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);
            }
        }
    }
    /**
    * @param instanceClassName 工厂实现类全限定名称
    * @param factoryClass 工厂所属接口/抽象类全限定名称
    * @param classLoader 所要使用的类加载器
    **/
    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);
        }
    }
}

一般情况下,springboot提供的一些JAR包里面会带有文件META-INF/spring.factories,然后在Springboot启动的时候,根据启动阶段不同的需求,框架就会多次调用SpringFactoriesLoader加载相应的工厂配置信息。      

使用了注解@EnableAutoConfiguration时,就会触发对SpringFactoriesLoader.loadFactoryNames()的调用。         

看一下spring.factories所在的位置:   

https://img1.sycdn.imooc.com//5cf65bbe0001186507170224.jpg

部分内容如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
# Initializers
org.springframework.context.ApplicationContextInitializer=\
org.springframework.boot.autoconfigure.SharedMetadataReaderFactoryContextInitializer,\
org.springframework.boot.autoconfigure.logging.ConditionEvaluationReportLoggingListener
# 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
# Auto Configuration Import Filters
org.springframework.boot.autoconfigure.AutoConfigurationImportFilter=\
org.springframework.boot.autoconfigure.condition.OnBeanCondition,\
org.springframework.boot.autoconfigure.condition.OnClassCondition,\
org.springframework.boot.autoconfigure.condition.OnWebApplicationCondition

以上就是SpringBoot实现自动化配置的原理,或许你看到的源码会与我的不相同,那有可能是jar版本的不一致。    


总结一下使用SpringBoot的好处:    

(1)简化配置,不需要编写太多的xml配置文件;     

(2)基于Spring构建,使开发者快速入门,门槛很低;     

(3)SpringBoot可以创建独立运行的应用而不需要依赖于容器;     

(4)内置tomcat服务器,不需要打包成war包,可以直接放到tomcat中运行;     

(5)提供maven极简配置,以及可视化的相关监控功能,比如性能监控,应用的健康程度等;      

(6)为微服务SpringCloud奠定了基础,使得微服务的构建变得简单;      

(7)Spring可以整合很多各式各样的框架,并能很好的集成;     

(8)活跃的社区与论坛,以及丰富的开发文档;     




点击查看更多内容
4人点赞

若觉得本文不错,就分享一下吧!

0 评论

作者其他优质文章

正在加载中
JAVA开发工程师
手记
163
粉丝
1.5万
获赞与收藏
8507

关注作者,订阅最新文章

阅读免费教程

感谢您的支持,我会继续努力的~
扫码打赏,你说多少就多少
赞赏金额会直接到老师账户
支付方式
打开微信扫一扫,即可进行扫码打赏哦
今天注册有机会得

100积分直接送

付费专栏免费学

大额优惠券免费领

立即参与 放弃机会
微信客服

购课补贴
联系客服咨询优惠详情

帮助反馈 APP下载

慕课网APP
您的移动学习伙伴

公众号

扫描二维码
关注慕课网微信公众号

举报

0/150
提交
取消