SpringBoot自动配置-注解解析

304 阅读3分钟

@SpringBootApplication

这是一个组合注解,也标记了springboot启动类。

@Target(ElementType.TYPE) //注解的作用目标
@Retention(RetentionPolicy.RUNTIME) //注解的保留位置
@Documented //说明该注解将被包含在javadoc中
@Inherited //说明子类可以继承父类中的该注解
@SpringBootConfiguration //同 configuation
@EnableAutoConfiguration
@ComponentScan(excludeFilters = {
		@Filter(type = FilterType.CUSTOM, classes = TypeExcludeFilter.class),
		@Filter(type = FilterType.CUSTOM, classes = AutoConfigurationExcludeFilter.class) })
public @interface SpringBootApplication {
}

@SpringBootConfiguration

首先看第一个注解@SpringBootConfiguration如下: 其实就是对Configuration注解进行了一层包装

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

@Configuration

@Configuration注解是spring中的注解,标记这是配置类。

@EnableAutoConfiguration

@EnableAutoConfiguration 是一个组合注解,包含了@AutoConfigurationPackage @Import两个注解

@AutoConfigurationPackage  //将启动类信息注册到bean注册表中
@Import(AutoConfigurationImportSelector.class)
public @interface EnableAutoConfiguration {
	String ENABLED_OVERRIDE_PROPERTY = "spring.boot.enableautoconfiguration";
	Class<?>[] exclude() default {};//设置不加载类型
	String[] excludeName() default {};//设置不加载名称
}

@AutoConfigurationPackage

@AutoConfigurationPackage注解其中包含了一个@import注解,@import注解的作用是将一个类加载到spring容器中

@Import(AutoConfigurationPackages.Registrar.class)

接下来分析Registrar主要做了什么

static class Registrar implements ImportBeanDefinitionRegistrar, DeterminableImports {
		@Override
                //注册bean定义 
		public void registerBeanDefinitions(AnnotationMetadata metadata,
				BeanDefinitionRegistry registry) {
			register(registry, new PackageImport(metadata).getPackageName());
		}
		@Override
		public Set<Object> determineImports(AnnotationMetadata metadata) {
			return Collections.singleton(new PackageImport(metadata));
		}

	}

看到registerBeanDefinitions方法,有两个入参AnnotationMetadata和BeanDefinitionRegistry。

AnnotationMetadata

是一个记录注解信息的元对象,记录是哪个类以及哪个包

image.png

BeanDefinitionRegistry

是bean定义的注册表 image.png

register

接下来看register方法,有两个入参数BeanDefinitionRegistry,packageNames 有两个入参数BeanDefinitionRegistry是bean注册表,packageNames是包路径

private static final String BEAN = AutoConfigurationPackages.class.getName();


public static void register(BeanDefinitionRegistry registry, String... packageNames) {
    //如果已经注册过AutoConfigurationPackages则进入
    if (registry.containsBeanDefinition(BEAN)) {
	BeanDefinition beanDefinition = registry.getBeanDefinition(BEAN);
	ConstructorArgumentValues constructorArguments = beanDefinition
					.getConstructorArgumentValues();
	constructorArguments.addIndexedArgumentValue(0,
					addBasePackages(constructorArguments, packageNames));
    }
    else {
        //创建GenericBeanDefinition对象
	GenericBeanDefinition beanDefinition = new GenericBeanDefinition();
        //设置为BasePackages
	beanDefinition.setBeanClass(BasePackages.class);
	beanDefinition.getConstructorArgumentValues().addIndexedArgumentValue(0,
					packageNames);
	beanDefinition.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
        //注册到注册表中
	registry.registerBeanDefinition(BEAN, beanDefinition);
		}
	}

由此可见@Import(AutoConfigurationPackages.Registrar.class)就是将包路径以及启动类信息注册到beanDefinition注册表中。

@Import(AutoConfigurationImportSelector.class)

将AutoConfigurationImportSelector装配到springIoc容器中。接下来看此类具体做了什么,具体看selectImports方法.

  1. 判断是否包含enableautoconfiguration注解
public String[] selectImports(AnnotationMetadata annotationMetadata) {
    //是否包含enableautoconfiguration注解
    if (!isEnabled(annotationMetadata)) {
	return NO_IMPORTS;
    }
  1. 使用类加载器加载配置文件 获取加载类的条件
AutoConfigurationMetadata autoConfigurationMetadata = 
AutoConfigurationMetadataLoader.loadMetadata(this.beanClassLoader);

看一下AutoConfigurationMetadataLoader.loadMetadata方法。此处path的值是META-INF/spring-autoconfigure-metadata.properties

AutoConfigurationMetadataLoader:  
protected static final String PATH = "META-INF/"+ "spring-autoconfigure-metadata.properties";
public static AutoConfigurationMetadata loadMetadata(ClassLoader classLoader) {
                return loadMetadata(classLoader, PATH);
        }

找到路径下的文件,可发现是一堆配置以 类.条件=值的形式存储。也就是加载这个类需要的条件,以及这个条件具体的值是什么。

image.png 3. 获取注解配置的exclude以及excludeName配置,也就是需要过滤的类的配置。

    AnnotationAttributes attributes = getAttributes(annotationMetadata);
  1. 获取所有需要加载的配置类
    List<String> configurations = getCandidateConfigurations(annotationMetadata,
				attributes);

进入getCandidateConfigurations方法,主要看SpringFactoriesLoader.loadFactoryNames方法

protected List<String> getCandidateConfigurations(AnnotationMetadata metadata,
			AnnotationAttributes attributes) {
    List<String> configurations = SpringFactoriesLoader.loadFactoryNames(
                    getSpringFactoriesLoaderFactoryClass(), getBeanClassLoader());
    ...
    return configurations;
}

两个入参一个是EnableAutoConfiguration注解类一个是类加载器。

public static List<String> loadFactoryNames(Class<?> factoryClass, @Nullable ClassLoader classLoader) {
    String factoryClassName = factoryClass.getName();
    //先获取所有需要加载的类,getOrDefault通过第一个参数获取到本次需要的类
    return loadSpringFactories(classLoader).getOrDefault(factoryClassName, Collections.emptyList());
}

主要看loadSpringFactories方法

//FACTORIES_RESOURCE_LOCATION的值
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;
    }
    try {
        //判断类加载器是否为空,通过类加载器加载资源文件
        Enumeration<URL> urls = (classLoader != null ?
                        classLoader.getResources(FACTORIES_RESOURCE_LOCATION) :
                        ClassLoader.getSystemResources(FACTORIES_RESOURCE_LOCATION));

找到META-INF/spring.factories文件,就是要加载的各个组件配置 image.png 接下来就是解析url并放入map集合中

        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()) {
                List<String> factoryClassNames = Arrays.asList(
                StringUtils.commaDelimitedListToStringArray((String) entry.getValue()));
                result.addAll((String) entry.getKey(), factoryClassNames);
            }
        }
        cache.put(classLoader, result);
        return result;
    }
        ...
    }
}
  1. 删除重复的需要加载的类
    configurations = removeDuplicates(configurations);
  1. 找到不需要配置的类
    Set<String> exclusions = getExclusions(annotationMetadata, attributes);
  1. 校验排除类,如果exclusions不是自动配置类则会抛出异常
    checkExcludedClasses(configurations, exclusions);
    //移除所有排除类
    configurations.removeAll(exclusions);
    //根据autoConfigurationMetadata条件过滤不需要加载的配置类
    configurations = filter(configurations, autoConfigurationMetadata);
  1. 发送事件通知给spring自动配置监听器
    fireAutoConfigurationImportEvents(configurations, exclusions);
    return StringUtils.toStringArray(configurations);
}

private void fireAutoConfigurationImportEvents(List<String> configurations,Set<String> exclusions) {
    //加载AutoConfigurationImportListener类对应spring.factories的类的数组
    List<AutoConfigurationImportListener> listeners = getAutoConfigurationImportListeners();
    if (!listeners.isEmpty()) {
        //创建事件
        AutoConfigurationImportEvent event = new AutoConfigurationImportEvent(this,
                            configurations, exclusions);
        //遍历监听器,发送事件通知                   
        for (AutoConfigurationImportListener listener : listeners) {
                invokeAwareMethods(listener);
                listener.onAutoConfigurationImportEvent(event);
        }
    }
}

@ComponentScan

对AutoConfigurationPackage注解获得的包及其子报进行注解扫描 作用等于<context:componet-scan base-package="xxx.xxx.xxx" />