https://www.jianshu.com/p/faa1bfe0d11a
之前在做简易工具的时候发现一个问题。为什么把config里面的配置给配好,就可以完成注入呢?我们只是添加了rocketmq的依赖,此外也只是满足其RocketMQAutoConfiguration的依赖注入,随后什么都没做,为什么可以实现呢。究竟RocketMQAutoConfiguration是怎么被注入的
此时其实毫无思绪。
从启动类来看,并没有找到任何相关的处理
@SpringBootApplication
public class Config {
public static void main(String args[]) {
SpringApplication.run(Config.class, args);
}
}
下面先看看SpringBootApplication注解的内部,看看有哪些注解
@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 {
。。。代码就补贴了
}
从代码上来看总共分为3个注解,分别为@SpringBootConfiguration,@EnableAutoConfiguration,@ComponentScan,从结构上来看,就是三个功能,@SpringBootConfiguration表明该类为springboot的配置类(其内部其实就是我们常用的@Configuration),@EnableAutoConfiguration则是开启自动装配,而@ComponentScan是进行扫描注入。
那么我们只需要关注@EnableAutoConfiguration这个注解是如何工作的了。
先看看其内部
/**
* Enable auto-configuration of the Spring Application Context, attempting to guess and
* configure beans that you are likely to need. Auto-configuration classes are usually
* applied based on your classpath and what beans you have defined. For example, if you
* have {@code tomcat-embedded.jar} on your classpath you are likely to want a
* {@link TomcatServletWebServerFactory} (unless you have defined your own
* {@link ServletWebServerFactory} bean).
* <p>
* When using {@link SpringBootApplication}, the auto-configuration of the context is
* automatically enabled and adding this annotation has therefore no additional effect.
* <p>
* Auto-configuration tries to be as intelligent as possible and will back-away as you
* define more of your own configuration. You can always manually {@link #exclude()} any
* configuration that you never want to apply (use {@link #excludeName()} if you don't
* have access to them). You can also exclude them via the
* {@code spring.autoconfigure.exclude} property. Auto-configuration is always applied
* after user-defined beans have been registered.
* <p>
* The package of the class that is annotated with {@code @EnableAutoConfiguration},
* usually via {@code @SpringBootApplication}, has specific significance and is often used
* as a 'default'. For example, it will be used when scanning for {@code @Entity} classes.
* It is generally recommended that you place {@code @EnableAutoConfiguration} (if you're
* not using {@code @SpringBootApplication}) in a root package so that all sub-packages
* and classes can be searched.
* <p>
* Auto-configuration classes are regular Spring {@link Configuration} beans. They are
* located using the {@link SpringFactoriesLoader} mechanism (keyed against this class).
* Generally auto-configuration beans are {@link Conditional @Conditional} beans (most
* often using {@link ConditionalOnClass @ConditionalOnClass} and
* {@link ConditionalOnMissingBean @ConditionalOnMissingBean} annotations).
*
* @author Phillip Webb
* @author Stephane Nicoll
* @see ConditionalOnBean
* @see ConditionalOnMissingBean
* @see ConditionalOnClass
* @see AutoConfigureAfter
* @see SpringBootApplication
*/
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@AutoConfigurationPackage
@Import(AutoConfigurationImportSelector.class)
public @interface EnableAutoConfiguration {
String ENABLED_OVERRIDE_PROPERTY = "spring.boot.enableautoconfiguration";
/**
* Exclude specific auto-configuration classes such that they will never be applied.
* @return the classes to exclude
*/
Class<?>[] exclude() default {};
/**
* Exclude specific auto-configuration class names such that they will never be
* applied.
* @return the class names to exclude
* @since 1.3.0
*/
String[] excludeName() default {};
}
由于本人英文比较渣,看了一下注释,其实大概发现了点东西。
大概就是说这个注解只是作为自动装配用的,并不会有其他额外的影响。
另外就是自动装配还可以排除一些你不想要的配置类,使用exclude。
SpringFactoriesLoader用于定位自动装配的类。
被自动装配的配置类通常都是在用户定义的bean被注册之后才进行注册的。那么这里我想,应该是在configutationClassPostProcessor(因为扫描注入类,以及导入类的功能都是这里实现的)里面去处理,后面验证一下。
头部的@AutoConfigurationPackage注解暂时不知道是做什么用的,待会再看,先观察一下导入的AutoConfigurationImportSelector这个类具备什么作用。
先观察一下AutoConfigurationImportSelector这个类的结构。
public class AutoConfigurationImportSelector
implements DeferredImportSelector, BeanClassLoaderAware, ResourceLoaderAware,
BeanFactoryAware, EnvironmentAware, Ordered {
private static final String[] NO_IMPORTS = {};
private static final Log logger = LogFactory
.getLog(AutoConfigurationImportSelector.class);
private static final String PROPERTY_NAME_AUTOCONFIGURE_EXCLUDE = "spring.autoconfigure.exclude";
private ConfigurableListableBeanFactory beanFactory;
private Environment environment;
private ClassLoader beanClassLoader;
private ResourceLoader resourceLoader;
}
通过上述代码,大概可以知道,这个类总共实现了多个接口,DeferredImportSelector,BeanClassLoaderAware,ResourceLoaderAware,BeanFactoryAware,EnvironmentAware,Ordered。Ordered呢,主要用于排序,而xxAware接口,其实是用于设置各种spring的相关变量的,如bean工厂,资源加载器,bean的类加载器,环境。所以这里我们要关注的是DeferredImportSelector接口。
下面看看DeferredImportSelector接口的内部结构。
public interface DeferredImportSelector extends ImportSelector {
/**
* Return a specific import group or {@code null} if no grouping is required.
* @return the import group class or {@code null}
*/
@Nullable
default Class<? extends Group> getImportGroup() {
return null;
}
/**
* Interface used to group results from different import selectors.
*/
interface Group {
/**
* Process the {@link AnnotationMetadata} of the importing @{@link Configuration}
* class using the specified {@link DeferredImportSelector}.
*/
void process(AnnotationMetadata metadata, DeferredImportSelector selector);
/**
* Return the {@link Entry entries} of which class(es) should be imported for this
* group.
*/
Iterable<Entry> selectImports();
/**
* An entry that holds the {@link AnnotationMetadata} of the importing
* {@link Configuration} class and the class name to import.
*/
class Entry {
private final AnnotationMetadata metadata;
private final String importClassName;
public Entry(AnnotationMetadata metadata, String importClassName) {
this.metadata = metadata;
this.importClassName = importClassName;
}
/**
* Return the {@link AnnotationMetadata} of the importing
* {@link Configuration} class.
*/
public AnnotationMetadata getMetadata() {
return this.metadata;
}
/**
* Return the fully qualified name of the class to import.
*/
public String getImportClassName() {
return this.importClassName;
}
@Override
public boolean equals(Object o) {
if (this == o) {
return true;
}
if (o == null || getClass() != o.getClass()) {
return false;
}
Entry entry = (Entry) o;
return Objects.equals(this.metadata, entry.metadata) &&
Objects.equals(this.importClassName, entry.importClassName);
}
@Override
public int hashCode() {
return Objects.hash(this.metadata, this.importClassName);
}
}
}
}
public interface ImportSelector {
/**
* Select and return the names of which class(es) should be imported based on
* the {@link AnnotationMetadata} of the importing @{@link Configuration} class.
*/
String[] selectImports(AnnotationMetadata importingClassMetadata);
}
看了一下,没看出个所以然。
用简单粗暴的方式debug一下吧。
我们将断点定位在AutoConfigurationImportSelector的selectImports方法。看一下其执行的流程
截屏2020-07-09下午9.00.22.png
从上述图中,我们可以看到RocketMQAutoConfiguration的影子了。从上述代码执行的流程分析。这一步操作是在容器刷新的过程中执行的,刷新的时候会先执行beanDefinitionRegistoryProcessor的postProcessBeanDefinitionRegistry用于处理一些注册bean的逻辑。
ConfigurationClassPostProcessor的postProcessBeanDefinitionRegistry方法比较主要。
1.找出所有的配置类
2.循环配置类
2.1通过配置类的@componentScan去扫描并且注册bean(通过@compoent注解)
2.2找出导入的bean,通过@import注解
2.3processDeferredImportSelectors---处理导入延时选择器
我们来看看configurationClassPostProcessor是怎么处理的
class ConfigurationClassParser {
public void parse(Set<BeanDefinitionHolder> configCandidates) {
this.deferredImportSelectors = new LinkedList<>();
for (BeanDefinitionHolder holder : configCandidates) {
BeanDefinition bd = holder.getBeanDefinition();
try {
//这里肯定都是注解类型的bean,所以走第一个分支
if (bd instanceof AnnotatedBeanDefinition) {
//这里面就涉及到如何解析类,就是上面的2.1,2.2
parse(((AnnotatedBeanDefinition) bd).getMetadata(), holder.getBeanName());
}
else if (bd instanceof AbstractBeanDefinition && ((AbstractBeanDefinition) bd).hasBeanClass()) {
parse(((AbstractBeanDefinition) bd).getBeanClass(), holder.getBeanName());
}
else {
parse(bd.getBeanClassName(), holder.getBeanName());
}
}
catch (BeanDefinitionStoreException ex) {
throw ex;
}
catch (Throwable ex) {
throw new BeanDefinitionStoreException(
"Failed to parse configuration class [" + bd.getBeanClassName() + "]", ex);
}
}
//延迟导入选择器的执行。。。为什么叫延迟?或许执行到这一步的时候,用户定义的bean已经被注册了,剩下的就是一些自动装配相关需要被注册的类。
processDeferredImportSelectors();
}
}
那么下面来看看这个方法究竟是何方神圣
private void processDeferredImportSelectors() {
List<DeferredImportSelectorHolder> deferredImports = this.deferredImportSelectors;
this.deferredImportSelectors = null;
if (deferredImports == null) {
return;
}
deferredImports.sort(DEFERRED_IMPORT_COMPARATOR);
Map<Object, DeferredImportSelectorGrouping> groupings = new LinkedHashMap<>();
Map<AnnotationMetadata, ConfigurationClass> configurationClasses = new HashMap<>();
for (DeferredImportSelectorHolder deferredImport : deferredImports) {
Class<? extends Group> group = deferredImport.getImportSelector().getImportGroup();
DeferredImportSelectorGrouping grouping = groupings.computeIfAbsent(
(group != null ? group : deferredImport),
key -> new DeferredImportSelectorGrouping(createGroup(group)));
grouping.add(deferredImport);
configurationClasses.put(deferredImport.getConfigurationClass().getMetadata(),
deferredImport.getConfigurationClass());
}
for (DeferredImportSelectorGrouping grouping : groupings.values()) {
grouping.getImports().forEach(entry -> {
ConfigurationClass configurationClass = configurationClasses.get(entry.getMetadata());
try {
processImports(configurationClass, asSourceClass(configurationClass),
asSourceClasses(entry.getImportClassName()), false);
}
catch (BeanDefinitionStoreException ex) {
throw ex;
}
catch (Throwable ex) {
throw new BeanDefinitionStoreException(
"Failed to process import candidates for configuration class [" +
configurationClass.getMetadata().getClassName() + "]", ex);
}
});
}
}
原谅我口嗨了,这个方法我看不下去 = = 还是先解析一下对应的结构。
DeferredImportSelectorHolder
DeferredImportSelectorGrouping
AnnotationMetadata
DeferredImportSelector
先看看DeferredImportSelectorHolder的结构,就是一个内部类,封装了配置类以及延迟导入选择器
class ConfigurationClassParser {
private static class DeferredImportSelectorHolder {
private final ConfigurationClass configurationClass;
private final DeferredImportSelector importSelector;
public DeferredImportSelectorHolder(ConfigurationClass configClass, DeferredImportSelector selector) {
this.configurationClass = configClass;
this.importSelector = selector;
}
public ConfigurationClass getConfigurationClass() {
return this.configurationClass;
}
public DeferredImportSelector getImportSelector() {
return this.importSelector;
}
}
}
再看看DeferredImportSelectorGrouping的结构,发现该变量封装了一个接口类型的变量group,以及DeferredImportSelectorHolder(一个封装ConfigurationClass以及DeferredImportSelector)的变量
class ConfigurationClassParser {
private static class DeferredImportSelectorGrouping {
private final DeferredImportSelector.Group group;
private final List<DeferredImportSelectorHolder> deferredImports = new ArrayList<>();
DeferredImportSelectorGrouping(Group group) {
this.group = group;
}
public void add(DeferredImportSelectorHolder deferredImport) {
this.deferredImports.add(deferredImport);
}
/**
* Return the imports defined by the group.
* @return each import with its associated configuration class
*/
public Iterable<Group.Entry> getImports() {
for (DeferredImportSelectorHolder deferredImport : this.deferredImports) {
this.group.process(deferredImport.getConfigurationClass().getMetadata(),
deferredImport.getImportSelector());
}
return this.group.selectImports();
}
}
}
另外顺便看看DeferredImportSelector.Group的结构,代码如下,看起来这个类相对简单,有一个process(注解元信息,延迟导入选择器)方法,另外还有一个内部的类Entry,封装了元数据以及导入的类名称。
class ConfigurationClassParser {
/**
* Interface used to group results from different import selectors.
*/
interface Group {
/**
* Process the {@link AnnotationMetadata} of the importing @{@link Configuration}
* class using the specified {@link DeferredImportSelector}.
*/
void process(AnnotationMetadata metadata, DeferredImportSelector selector);
/**
* Return the {@link Entry entries} of which class(es) should be imported for this
* group.
*/
Iterable<Entry> selectImports();
/**
* An entry that holds the {@link AnnotationMetadata} of the importing
* {@link Configuration} class and the class name to import.
*/
class Entry {
private final AnnotationMetadata metadata;
private final String importClassName;
public Entry(AnnotationMetadata metadata, String importClassName) {
this.metadata = metadata;
this.importClassName = importClassName;
}
/**
* Return the {@link AnnotationMetadata} of the importing
* {@link Configuration} class.
*/
public AnnotationMetadata getMetadata() {
return this.metadata;
}
/**
* Return the fully qualified name of the class to import.
*/
public String getImportClassName() {
return this.importClassName;
}
@Override
public boolean equals(Object o) {
if (this == o) {
return true;
}
if (o == null || getClass() != o.getClass()) {
return false;
}
Entry entry = (Entry) o;
return Objects.equals(this.metadata, entry.metadata) &&
Objects.equals(this.importClassName, entry.importClassName);
}
@Override
public int hashCode() {
return Objects.hash(this.metadata, this.importClassName);
}
}
}
}
看完上面的类的结构我们回到configurationClassPostProcessor
private void processDeferredImportSelectors() {
//先从成员变量中获取延迟导入选择器的包装信息
List<DeferredImportSelectorHolder> deferredImports = this.deferredImportSelectors;
//切断引用,这里不知道为何要这么做
this.deferredImportSelectors = null;
//若没有任何的延迟导入选择器,则不进行任何处理
if (deferredImports == null) {
return;
}
//先根据比较器进行排序,这里如何比较就不是重点了,先跳过
deferredImports.sort(DEFERRED_IMPORT_COMPARATOR);
//用于保存DeferredImportSelectorGrouping分组的信息,key可能是class对象也可以DeferredImportSelector
Map<Object, DeferredImportSelectorGrouping> groupings = new LinkedHashMap<>();
Map<AnnotationMetadata, ConfigurationClass> configurationClasses = new HashMap<>();
for (DeferredImportSelectorHolder deferredImport : deferredImports) {
//获取延迟导入选择器对应的组的信息,通过debug发现,spring只有AutoConfigurationImportSelector这个类实现了getImportGroup方法返回AutoConfigurationGroup.class,若没有被实现则返回空,一开始deferredImports只有AutoConfigurationImportSelector,所以的话group是不会为空的
Class<? extends Group> group = deferredImport.getImportSelector().getImportGroup();
//groupings-> 这里会生成AutoConfigurationGroup对应的延迟导入选择器的group
DeferredImportSelectorGrouping grouping = groupings.computeIfAbsent(
(group != null ? group : deferredImport),
key -> new DeferredImportSelectorGrouping(createGroup(group)));
//同时将导入选择器加入对应的组中,组里面有一个List<DeferredImportSelectorHolder>,用于保存该组对应的导入选择器列表
grouping.add(deferredImport);
configurationClasses.put(deferredImport.getConfigurationClass().getMetadata(),
deferredImport.getConfigurationClass());
}
//对延迟选择器组进行遍历
for (DeferredImportSelectorGrouping grouping : groupings.values()) {
//取出该组中对应对的延迟导入选择器holder,然后进行处理,那么如何获取到需要被自动装配的配置类呢?那么只能是在grouping.getImports()方法中去实现。所以重点还是grouping.getImports()方法。那么下面重点看看这个方法的实现。
grouping.getImports().forEach(entry -> {
ConfigurationClass configurationClass = configurationClasses.get(entry.getMetadata());
try {
//对每个自动装配的configuration进行处理,这个方法内部最终走到的还是,ConfigurationClassParser的processConfigurationClass(ConfigurationClass configClass)方法,就是如何去处理配置类的相关逻辑,这里其实我们不用去关心,无法就是去处理改配置类相关的一些注入,如@import,@componentScan
processImports(configurationClass, asSourceClass(configurationClass),
asSourceClasses(entry.getImportClassName()), false);
}
catch (BeanDefinitionStoreException ex) {
throw ex;
}
catch (Throwable ex) {
throw new BeanDefinitionStoreException(
"Failed to process import candidates for configuration class [" +
configurationClass.getMetadata().getClassName() + "]", ex);
}
});
}
}
getImports方法的实现如下
class ConfigurationClassParser {
private static class DeferredImportSelectorGrouping {
private final DeferredImportSelector.Group group;
private final List<DeferredImportSelectorHolder> deferredImports = new ArrayList<>();
DeferredImportSelectorGrouping(Group group) {
this.group = group;
}
public void add(DeferredImportSelectorHolder deferredImport) {
this.deferredImports.add(deferredImport);
}
/**
* Return the imports defined by the group.
* @return each import with its associated configuration class
*/
//从这个方法中我们可以得出,最后还是交给Group来处理的,而我们需要关注的group只有AutoConfigurationGroup,所以看看AutoConfigurationGroup的process方法
public Iterable<Group.Entry> getImports() {
for (DeferredImportSelectorHolder deferredImport : this.deferredImports) {
this.group.process(deferredImport.getConfigurationClass().getMetadata(),
deferredImport.getImportSelector());
}
return this.group.selectImports();
}
}
}
AutoConfigurationGroup的process方法,代码如下
public class AutoConfigurationImportSelector
implements DeferredImportSelector, BeanClassLoaderAware, ResourceLoaderAware,
BeanFactoryAware, EnvironmentAware, Ordered {
private static class AutoConfigurationGroup implements DeferredImportSelector.Group,
BeanClassLoaderAware, BeanFactoryAware, ResourceLoaderAware {
private ClassLoader beanClassLoader;
private BeanFactory beanFactory;
private ResourceLoader resourceLoader;
private final Map<String, AnnotationMetadata> entries = new LinkedHashMap<>();
@Override
public void setBeanClassLoader(ClassLoader classLoader) {
this.beanClassLoader = classLoader;
}
@Override
public void setBeanFactory(BeanFactory beanFactory) {
this.beanFactory = beanFactory;
}
@Override
public void setResourceLoader(ResourceLoader resourceLoader) {
this.resourceLoader = resourceLoader;
}
@Override
public void process(AnnotationMetadata annotationMetadata,
DeferredImportSelector deferredImportSelector) {
//通过延迟导入选择器,找出所有需要被导入的类,从例子中我们得中,其实传入的选择器就是AutoConfigurationImportSelector,所以关注这个类的selectImports方法即可。
String[] imports = deferredImportSelector.selectImports(annotationMetadata);
for (String importClassName : imports) {
this.entries.put(importClassName, annotationMetadata);
}
}
@Override
public Iterable<Entry> selectImports() {
return sortAutoConfigurations().stream()
.map((importClassName) -> new Entry(this.entries.get(importClassName),
importClassName))
.collect(Collectors.toList());
}
@Override
public String[] selectImports(AnnotationMetadata annotationMetadata) {
if (!isEnabled(annotationMetadata)) {
return NO_IMPORTS;
}
AutoConfigurationMetadata autoConfigurationMetadata = AutoConfigurationMetadataLoader
.loadMetadata(this.beanClassLoader);
AnnotationAttributes attributes = getAttributes(annotationMetadata);
//到了这里才是最后返回configurations集合的关键,往里面走
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 StringUtils.toStringArray(configurations);
}
/**
* Return the auto-configuration class names that should be considered. By default
* this method will load candidates using {@link SpringFactoriesLoader} with
* {@link #getSpringFactoriesLoaderFactoryClass()}.
* @param metadata the source metadata
* @param attributes the {@link #getAttributes(AnnotationMetadata) annotation
* attributes}
* @return a list of candidate configurations
*/
protected List<String> getCandidateConfigurations(AnnotationMetadata metadata,
AnnotationAttributes attributes) {
//再往里面走。。。
List<String> configurations = SpringFactoriesLoader.loadFactoryNames(
getSpringFactoriesLoaderFactoryClass(), getBeanClassLoader());
Assert.notEmpty(configurations,
"No auto configuration classes found in META-INF/spring.factories. If you "
+ "are using a custom packaging, make sure that file is correct.");
return configurations;
}
}
继续往里面走,最后定位到SpringFactoriesLoader,那么看看其内部执行
private static Map<String, List<String>> loadSpringFactories(@Nullable ClassLoader classLoader) {
MultiValueMap<String, String> result = cache.get(classLoader);
if (result != null) {
return result;
}
try {
//到最后就是通过类加载器,将所有的META-INF/spring.factories资源给找出来
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);
//加载spring.factories资源
Properties properties = PropertiesLoaderUtils.loadProperties(resource);
for (Map.Entry<?, ?> entry : properties.entrySet()) {
List<String> factoryClassNames = Arrays.asList(
StringUtils.commaDelimitedListToStringArray((String) entry.getValue()));
//key=org.springframework.boot.autoconfigure.EnableAutoConfiguration
//value的话 就是对应的若干个configuration
result.addAll((String) entry.getKey(), factoryClassNames);
}
}
//随后放入缓存,其实后续通过类加载器,从缓存中取就好了,避免没有必要的加载
cache.put(classLoader, result);
return result;
}
catch (IOException ex) {
throw new IllegalArgumentException("Unable to load factories from location [" +
FACTORIES_RESOURCE_LOCATION + "]", ex);
}
}
贴一下debug的图,这说明,spring是通过类加载器,去找到对应的META-INF/spring.factories,读取里面的内容,并将其,进行注册。自动装配就这么完成了。
截屏2020-07-10下午3.05.31.png 截屏2020-07-10下午3.01.18.png
网友评论