为什么要使用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所在的位置:
部分内容如下:
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)活跃的社区与论坛,以及丰富的开发文档;
共同学习,写下你的评论
暂无评论
作者其他优质文章