SpringBoot自动配置?这篇文章就哇~金色普通

springboot利用了spring的注解:包括@Bean、@Conditional、@Configuration等,将原有对spring繁杂的xml抛弃掉,使用以AutoConfiguration为后缀的来@Bean对象,以Properties为后缀的类充当属性类,代替了xml文件的配置。

SpringBoot自动配置

先从主程序入口来看,我刚创建好springboot项目看见这俩货脑袋顶好几个问号

//为什么要加这个注解
@SpringBootApplication
public class AlllApplication {

    public static void main(String[] args) {
    //这个run方法是怎么回事?
        SpringApplication.run(AlllApplication.class, args);
    }

}

不管了,挑一个就下手,先分析注解@SpringBootApplication,鼠标放上ctrl+左键,走你~

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

东西真多啊,还好咱spring注解也多少会点,直接看跟springboot相关的

  1. @SpringBootConfiguration
    关于这个类源码描述为
    Indicates that a class provides Spring Boot application,碰巧咱工地英语也不会,直接百度:表示类提供了Spring引导应用程序
    ???
    咦,引导程序,莫非…
    继续看看这个注解的其他信息
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Configuration
public @interface SpringBootConfiguration {
@AliasFor(annotation = Configuration.class)
	boolean proxyBeanMethods() default true;

}

都是熟人,都是熟悉的元注解:修饰的范围、生命周期、声明此类的注释加进javadoc中、作为一个配置类
看到这明白了上面的那句,这个注解@SpringBootConfiguration表明是一个springboot的配置类的存在

问题1 : 既然是配置类不是表明他修饰的@SpringBootApplication 是一个配置类么?那么这个加在主启动类上的注解到底配置了哪些好玩的东西呢?
带着疑问看这个注解类的内部


@AliasFor(annotation = Configuration.class)
	boolean proxyBeanMethods() default true;

一个默认为返回为true的proxyBeanMethods()的函数,至于为什么我叫他函数而不是方法,是个人会把只有一个方法的接口叫函数,大家随意。
@AliasFor(annotation = Configuration.class)这个注解也是spring为我们提供的,表明这个函数的是跟Configuration.class注解对应的,作用是起别名,确保不会跟@Configuration发生冲突
------现在,这个注解总体上我们看出来,表示“我@SpringBootConfiguration ”在谁头上谁就是springBoot的配置类
到这里,我刚才的疑问又出来了,你到底要配置什么?


回到@SpringBootApplication中,继续看这货脑袋上的元注解
我这次把目光放在了 @EnableAutoConfiguration 上,来,ctrl+左键进去,哎~舒服…咳咳,我的意思是看到这类中的内容就激动了


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

先从注解下手,也都熟悉:修饰的范围、生命周期、将注释加进javadoc、可继承该注解,下一个不认识的注解@AutoConfigurationPackage,话不多说点进去看看

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@Import(AutoConfigurationPackages.Registrar.class)
public @interface AutoConfigurationPackage {

}

使用了spring注解的@Import方式来注册这个抽象类AutoConfigurationPackages中的静态内部类Registrar

问题2: Registrar 是干什么的,为什么要注册它?

static class Registrar implements ImportBeanDefinitionRegistrar, DeterminableImports {
		/*
		重写了ImportBeanDefinitionRegistrar接口中的方法,在这里是先调用外部方法register(),这个方法都干什么了呢?
		1. 先以LinkedHashMap来保存一条数据[K-V][0-new PackageImport(metadata).getPackageName()]:[ConstructorArgumentValues]
		2. 最后以[k-v]形式注册的是:regist.registerBeanDefinition(AutoConfigurationPackages.class.getName(),GenericBeanDefinition)
		(注册这需要了解spring以@Import形式注册的三种方式)
		*/
		@Override
		public void registerBeanDefinitions(AnnotationMetadata metadata, BeanDefinitionRegistry registry) {
			register(registry, new PackageImport(metadata).getPackageName());
		}
		/*
		将注解的元信息包装成PackageImport类并返回
		*/
		@Override
		public Set<Object> determineImports(AnnotationMetadata metadata) {
			return Collections.singleton(new PackageImport(metadata));
		}
	}

@AutoConfigurationPackage总结:向容器中注册了Registrar这个静态内部类,对这个类的描述是:
{@link ImportBeanDefinitionRegistrar} to store the base package from the importing configuration.
主要作用还是注册了GenericBeanDefinition这个类
这我又来疑问了

问题3: 这个GenericBeanDefinition类究竟是干嘛的呢?

GenericBeanDefinition 类的描述是:
GenericBeanDefinition is a one-stop shop for standard bean definition purposes.
GenericBeanDefinition是用于标准bean定义的一站式服务。
熟悉spring应该知道这个类表示一个bean标签,它的父类AbstractBeanDefinition中的所有属性就是bean标签中包含的内容,包括beanClass、scope、lazyInit懒加载、initMethodName生命周期等,一个id对应一个GenericBeanDefinition


缓口气,看了半天,我是谁、我在哪、我在干什么,就这种感觉,不过接下来就是解决自动配置的主要注解,提起裤子(精神),继续搞起


********************** 重点吸收************************

回到@EnableAutoConfiguration中接着看@AutoConfigurationPackage下面的注解 @Import(AutoConfigurationImportSelector.class)
又导入了一个类AutoConfigurationImportSelector,并且是实现了ImportSelector接口注册的类,这样我们就明确了目标,直接杀进重写方法的selectImports(AnnotationMetadata),看一下到底注册了什么

@Override
	public String[] selectImports(AnnotationMetadata annotationMetadata) {
		if (!isEnabled(annotationMetadata)) {
			return NO_IMPORTS;
		}
		AutoConfigurationMetadata autoConfigurationMetadata = AutoConfigurationMetadataLoader
				.loadMetadata(this.beanClassLoader);
		AutoConfigurationEntry autoConfigurationEntry = getAutoConfigurationEntry(autoConfigurationMetadata,
				annotationMetadata);
		return StringUtils.toStringArray(autoConfigurationEntry.getConfigurations());
	}
1. 先来看看这行代码loadMetadata(this.beanClassLoader)
AutoConfigurationMetadata autoConfigurationMetadata = AutoConfigurationMetadataLoader
				.loadMetadata(this.beanClassLoader);

对AutoConfigurationMetadataLoader类的描述是:
Internal utility used to load {@link AutoConfigurationMetadata}.
用于加载{@link AutoConfigurationMetadata}的内部实用程序。
深入看看类的内部,哇哦~(在这里只V了主要代码,对这个类感兴趣可以动手自己看看,提高自己)
进入这个类,问题就铺面而来啊,

final class AutoConfigurationMetadataLoader {
	//定义一个路径,嗯?定义一个什么路径,定义这个路径干什么,还有个properties文件,这是个springboot配置?这货在哪找?(我疑问当时是真多[捂脸])
	protected static final String PATH = "META-INF/spring-autoconfigure-metadata.properties";
	//这个我熟,调用一个返回值为AutoConfigurationMetadata的loadMetadata方法
	static AutoConfigurationMetadata loadMetadata(ClassLoader classLoader) {
		return loadMetadata(classLoader, PATH);
	}
static AutoConfigurationMetadata loadMetadata(ClassLoader classLoader, String path) {
		try {
			/*无论进来的classLoader是否为null,都可以使用一个classLoader类加载器来加载这个path,将这个parh对应的properties文件中的内容以一个Enumeration的这么个容器进行返回
			我就好奇了,你这文件保存的是URL,难道是www.csdn.com?还是说www.github.com让我自己clone,看一看究竟是配置了什么。
			*/
			Enumeration<URL> urls = (classLoader != null) ? classLoader.getResources(path)
					: ClassLoader.getSystemResources(path);
			Properties properties = new Properties();
			
			while (urls.hasMoreElements()) {
				properties.putAll(PropertiesLoaderUtils.loadProperties(new UrlResource(urls.nextElement())));
			}
			/*
			将urls集合中的内容保存到Properties 中,下面这个方法返回的是一个静态内部类PropertiesAutoConfigurationMetadata,在Properties 之上进一步包装
			*/
			return loadMetadata(properties);//返回PropertiesAutoConfigurationMetadata
		}
		catch (IOException ex) {
			throw new IllegalArgumentException("Unable to load @ConditionalOnClass location [" + path + "]", ex);
		}
	}
}
(重点 | 将spring.properties内容引入)2 . 再看这段代码getAutoConfigurationEntry()
AutoConfigurationEntry autoConfigurationEntry = getAutoConfigurationEntry(autoConfigurationMetadata,
				annotationMetadata);

深入进去,ctrl+左键走你~

/*
先从参数分析,可以爱着打一个断点来查看
autoConfigurationMetadata: 这个参数是包装了Properties的PropertiesAutoConfigurationMetadata类
annotationMetadata:  StandardAnnotationMetadata对象,表示这个@Import最终是哪个类的头上,得到那个类的元数据,就是注解的信息,在这里是使用反射得到主启动类的元数据
(元数据:注解的属性等,这里我们主启动类上只有这个@SpringBootApplication,如果有其他的注解,可以自行打断点看看,如增加个事务管理器、dubbo等)
*/
protected AutoConfigurationEntry getAutoConfigurationEntry(AutoConfigurationMetadata autoConfigurationMetadata,
			AnnotationMetadata annotationMetadata) {
		if (!isEnabled(annotationMetadata)) {
			return EMPTY_ENTRY;
		}
		//返回一个的层是LinkedHashMap结构的元数据集合,就是注释属性的键值对
		AnnotationAttributes attributes = getAttributes(annotationMetadata);
		//当我debugF8到这后显示这个list元素有124个,那么这些内容是怎么来的呢?
		List<String> configurations = getCandidateConfigurations(annotationMetadata, attributes);
		configurations = removeDuplicates(configurations);
		Set<String> exclusions = getExclusions(annotationMetadata, attributes);
		checkExcludedClasses(configurations, exclusions);
		configurations.removeAll(exclusions);
		//过滤出不需要的元素,保存需要的实例化的元素
		configurations = filter(configurations, autoConfigurationMetadata);
		fireAutoConfigurationImportEvents(configurations, exclusions);
		return new AutoConfigurationEntry(configurations, exclusions);
	}

疑问1: 这个Enumeration是什么?
java.util中的一个接口,类似于迭代器,这里Enumeration实际返回的是它的实现类CompoundEnumeration(在Enumeration基础上加了一个游标)

疑问2:path到底在哪?
spring-boot-autoconfig2.2.5RELEASE包下的META-INFO就可以找打properties文件

疑问3:configurations 集合显示有124个,那么这些元素是从哪里来的呢?
一直debug到SpringFactoriesLoader 类中

public final class SpringFactoriesLoader {
//资源的路径,同疑问2中的路径相同
public static final String FACTORIES_RESOURCE_LOCATION = "META-INF/spring.factories";

private static Map<String, List<String>> loadSpringFactories(@Nullable ClassLoader classLoader) {
		MultiValueMap<String, String> result = cache.get(classLoader);
		if (result != null) {
			return result;
		}
		/*
		这下面的代码就第二次见了,将指定properties中的内容封装到Enumeration<URL>,最终以LinkedMultiValueMap结构进行返回,
		目的就是得到"META-INF/spring.factories"内容,将内容进行封装,保存到cache中,返回result,可以看出springboot增加了很多的map结构:LinkedHashMap、juc中的map等
		*/
		try {
			Enumeration<URL> urls = (classLoader != null ?
					classLoader.getResources(FACTORIES_RESOURCE_LOCATION) :
					ClassLoader.getSystemResources(FACTORIES_RESOURCE_LOCATION));
			result = new LinkedMultiValueMap<>();
			while (urls.hasMoreElements()) {
				URL url = urls.nextElement();
				UrlResource resource = new UrlResource(url);
				Properties properties = PropertiesLoaderUtils.loadProperties(resource);
				for (Map.Entry<?, ?> entry : properties.entrySet()) {
					String factoryTypeName = ((String) entry.getKey()).trim();
					for (String factoryImplementationName : StringUtils.commaDelimitedListToStringArray((String) entry.getValue())) {
						result.add(factoryTypeName, factoryImplementationName.trim());
					}
				}
			}
			//这个cache的结构是ConcurrentReferenceHashMap,这个结构实现了ConcurrentHashMap接口,K是classLoader-V是list
			cache.put(classLoader, result);
			return result;
		}
		catch (IOException ex) {
			throw new IllegalArgumentException("Unable to load factories from location [" +
					FACTORIES_RESOURCE_LOCATION + "]", ex);
		}
	}
}

疑问4:得到的URL是什么,有什么作用?
得到是以"org.springframework.boot.autoconfigure.EnableAutoConfiguration="为K,以其中一个元素"org.springframework.boot.autoconfigure.web.servlet.WebMvcAutoConfiguration,"来看这是一个webMvc的自动配置类,如果我们的项目是一个web项目那么springboot中的容器就会得到这个了类(为什么会得到这个自动配置类?每个自动配置类都有@Configuration,跟我们自定义config同理),省掉了我们原本需要在xml配置的步骤。
这些自动配置类发部分已经设置好了默认值,可以根据项目进行变更。
对于属性的值来讲,有些自动配置类是以···@EnableConfigurationProperties(HttpProperties.class)来引入一个对应的properties文件,这里以HttpEncodingAutoConfiguration举例
···直接在自动配置类中设置属性以WebMvcAutoConfiguration举例,它就没有对应的properties
自动配置类的作用是向容器注入我们需要的对象,感兴趣可以看看在使用web+JDBC等项目springboot都向我们@Bean了哪些类
当然这么多的url是需要过滤的,根据spring的@Conditional进行过滤。


写一篇比看一遍源码要慢的多啊,最后的自动配置这有补充的我再加(暂时先这么多,springboot真省时省力,爱了)

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值