这一篇我们来梳理下@SpringBootApplication
这个注解它注入了那些内容。
一、结构分析
1、main方法使用
我们知道@SpringBootApplication
是用在Main方法上面的,例如:
@SpringBootApplication
public class SpringBootSimpleDemoApplication {
public static void main(String[] args) {
SpringApplication.run(SpringBootSimpleDemoApplication.class, args);
}
}
通过怎样,其实就能通过入参SpringBootSimpleDemoApplication.class
,来获取到@SpringBootApplication
。
2、@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 {
@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 {};
@AliasFor(annotation = Configuration.class)
boolean proxyBeanMethods() default true;
}
我们进入这个方法里面,可以看到这个方法里面又引入了几个其他的注解。我们先看@ComponentScan
,我们知道这个是用来自动扫描包路径的组件的,同时这里又引入了两个排除FilterType
:TypeExcludeFilter
、AutoConfigurationExcludeFilter
。
再之后是SpringBootConfiguration
,这个注解其实没有什么特殊的内容,主要是用来标记这个是SpringBoot
配置,就是引入了@Configuration
注解。
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Configuration
public @interface SpringBootConfiguration {
@AliasFor(annotation = Configuration.class)
boolean proxyBeanMethods() default true;
}
然后是@EnableAutoConfiguration
,这种就是@Enablexxx
,用来自动注入的,我们之后的源码也 就是主要分析这类的逻辑。我们之后也可以自己实现一个自定义的@Enablexxx
。
@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 {};
}
二、关于阅读下面流程来拓展Spring的扫描组件注解
这一项是我们来拓展Spring的源码
1、拓展一个与@Component同样的注入组件@BeanClass
我们一般主动注入一个Bean可以是下面的样式,这里以主动注入:
@Configuration
public class MyConfiguration {
@Bean
public Book book()
{
return new Book();
}
}
当然我们也可以直接使用@Service
(也就是@Component
)注解,下面我们就来独立定义一个主键注解,当然不是不是想@Service
本身主要是为了引入@Component
:
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Component
public @interface Service {
@AliasFor(annotation = Component.class)
String value() default "";
}
我们是与@Component
同级别的。
1)、demo1
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Inherited
public @interface BeanClass {
}
这个注解我们将其命名为@BeanClass
,然后也是使用在类上面的。
再定义一个TypeFilter
,如果一个类上面有这个注解,我们就返回true
。
public class BeanClassTypeFilter implements TypeFilter {
@Override
public boolean match(MetadataReader metadataReader, MetadataReaderFactory metadataReaderFactory) throws IOException {
if (metadataReader.getAnnotationMetadata().isAnnotated(BeanClass.class.getName()))
{
return true;
}
return false;
}
}
然后再使用@ComponentScan
引入BeanClassTypeFilter
,并指定扫描的包名。
@Configuration
@ComponentScan(basePackages = "com.fev.springboot.simple.demo",includeFilters = {@ComponentScan.Filter(type = FilterType.CUSTOM,classes = BeanClassTypeFilter.class)})
public class MyConfiguration {
}
最后启动SpringBoot
项目测试,就能获取到了,感觉这个demo其实可以放在上一篇文章。
这里我们启动测试类,就能注入Book
了。
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.context.ApplicationContext;
import org.springframework.test.context.junit4.SpringRunner;
@SpringBootTest
@RunWith(SpringRunner.class)
public class SpringBootSimpleDemoApplicationTests {
@Autowired
private ApplicationContext applicationContext;
@Test
public void test1()
{
Book contextBean = applicationContext.getBean(Book.class);
System.out.println("............" + contextBean);
}
}
............com.fev.springboot.simple.demo.Book@33eb6758
当然这个目前可能不知道为什么,但看到后面源码部分我们就知道了。
三、@ComponentScan在@SpringBootApplication中的源码分析
我们在上一篇是有分析过@ComponentScan
的整体逻辑的,其的解析是在ConfigurationClassPostProcessor
类中的。这里我们直接去doProcessConfigurationClass
方法。
1、doProcessConfigurationClass (ConfigurationClassPostProcessor)
protected final SourceClass doProcessConfigurationClass(ConfigurationClass configClass, SourceClass sourceClass)
throws IOException {
...........
// Process any @ComponentScan annotations
Set<AnnotationAttributes> componentScans = AnnotationConfigUtils.attributesForRepeatable(
sourceClass.getMetadata(), ComponentScans.class, ComponentScan.class);
if (!componentScans.isEmpty() &&
!this.conditionEvaluator.shouldSkip(sourceClass.getMetadata(), ConfigurationPhase.REGISTER_BEAN)) {
for (AnnotationAttributes componentScan : componentScans) {
// The config class is annotated with @ComponentScan -> perform the scan immediately
Set<BeanDefinitionHolder> scannedBeanDefinitions =
this.componentScanParser.parse(componentScan, sourceClass.getMetadata().getClassName());
// Check the set of scanned definitions for any further config classes and parse recursively if needed
for (BeanDefinitionHolder holder : scannedBeanDefinitions) {
BeanDefinition bdCand = holder.getBeanDefinition().getOriginatingBeanDefinition();
if (bdCand == null) {
bdCand = holder.getBeanDefinition();
}
if (ConfigurationClassUtils.checkConfigurationClassCandidate(bdCand, this.metadataReaderFactory)) {
parse(bdCand.getBeanClassName(), holder.getBeanName());
}
}
}
}
..........
// No superclass -> processing is complete
return null;
}
首先这里是我们会扫描到这个注解的信息,包括两个排除FilterexcludeFilters
。
然后便是this.conditionEvaluator.shouldSkip
,这里目前是会false
,这个ConditionEvaluator
我们本篇之后会具体讲。再就是this.componentScanParser.parse
解析获取这个@ComponentScan
要扫描的包下面的组件了。当前componentScanParser
是ComponentScanAnnotationParser
2、this.componentScanParser.parse (ComponentScanAnnotationParser)
public Set<BeanDefinitionHolder> parse(AnnotationAttributes componentScan, final String declaringClass) {
ClassPathBeanDefinitionScanner scanner = new ClassPathBeanDefinitionScanner(this.registry,
componentScan.getBoolean("useDefaultFilters"), this.environment, this.resourceLoader);
.......
for (AnnotationAttributes filter : componentScan.getAnnotationArray("includeFilters")) {
for (TypeFilter typeFilter : typeFiltersFor(filter)) {
scanner.addIncludeFilter(typeFilter);
}
}
for (AnnotationAttributes filter : componentScan.getAnnotationArray("excludeFilters")) {
for (TypeFilter typeFilter : typeFiltersFor(filter)) {
scanner.addExcludeFilter(typeFilter);
}
}
.........
Set<String> basePackages = new LinkedHashSet<>();
String[] basePackagesArray = componentScan.getStringArray("basePackages");
for (String pkg : basePackagesArray) {
String[] tokenized = StringUtils.tokenizeToStringArray(this.environment.resolvePlaceholders(pkg),
ConfigurableApplicationContext.CONFIG_LOCATION_DELIMITERS);
Collections.addAll(basePackages, tokenized);
}
for (Class<?> clazz : componentScan.getClassArray("basePackageClasses")) {
basePackages.add(ClassUtils.getPackageName(clazz));
}
if (basePackages.isEmpty()) {
basePackages.add(ClassUtils.getPackageName(declaringClass));
}
scanner.addExcludeFilter(new AbstractTypeHierarchyTraversingFilter(false, false) {
@Override
protected boolean matchClassName(String className) {
return declaringClass.equals(className);
}
});
return scanner.doScan(StringUtils.toStringArray(basePackages));
}
上面这个就是构建ClassPathBeanDefinitionScanner scanner
,并且讲@ComponentSans
的注入信息添加到scanner
中,例如IncludeFilter
、ExcludeFilter
这些。然后就是一个扫描包的路径问题,如果你有指定要扫描的包名basePackages
,其就会是指定的包名,如果没有指定basePackages.isEmpty()
,其就会是declaringClass
,也就是被@ComponentScan
注解的类的包名。之后就是具体的scanner.doScan(StringUtils.toStringArray(basePackages))
了。
同时这里我们可以注意到这里还另外加了一个ExcludeFilter
即scanner.addExcludeFilter(new AbstractTypeHierarchyTraversingFilter(false, false)
。
3、scanner.doScan (ClassPathBeanDefinitionScanner)
protected Set<BeanDefinitionHolder> doScan(String... basePackages) {
Assert.notEmpty(basePackages, "At least one base package must be specified");
Set<BeanDefinitionHolder> beanDefinitions = new LinkedHashSet<>();
for (String basePackage : basePackages) {
Set<BeanDefinition> candidates = findCandidateComponents(basePackage);
for (BeanDefinition candidate : candidates) {
ScopeMetadata scopeMetadata = this.scopeMetadataResolver.resolveScopeMetadata(candidate);
candidate.setScope(scopeMetadata.getScopeName());
String beanName = this.beanNameGenerator.generateBeanName(candidate, this.registry);
if (candidate instanceof AbstractBeanDefinition) {
postProcessBeanDefinition((AbstractBeanDefinition) candidate, beanName);
}
if (candidate instanceof AnnotatedBeanDefinition) {
AnnotationConfigUtils.processCommonDefinitionAnnotations((AnnotatedBeanDefinition) candidate);
}
if (checkCandidate(beanName, candidate)) {
BeanDefinitionHolder definitionHolder = new BeanDefinitionHolder(candidate, beanName);
definitionHolder =
AnnotationConfigUtils.applyScopedProxyMode(scopeMetadata, definitionHolder, this.registry);
beanDefinitions.add(definitionHolder);
registerBeanDefinition(definitionHolder, this.registry);
}
}
}
return beanDefinitions;
}
这里首先是通过findCandidateComponents
获取到这个包下面所有的.class
的Resource
文件。
1)、findCandidateComponents (重要)
private Set<BeanDefinition> scanCandidateComponents(String basePackage) {
Set<BeanDefinition> candidates = new LinkedHashSet<>();
try {
String packageSearchPath = ResourcePatternResolver.CLASSPATH_ALL_URL_PREFIX +
resolveBasePackage(basePackage) + '/' + this.resourcePattern;
Resource[] resources = getResourcePatternResolver().getResources(packageSearchPath);
......
for (Resource resource : resources) {
if (resource.isReadable()) {
try {
MetadataReader metadataReader = getMetadataReaderFactory().getMetadataReader(resource);
if (isCandidateComponent(metadataReader)) {
ScannedGenericBeanDefinition sbd = new ScannedGenericBeanDefinition(metadataReader);
sbd.setResource(resource);
sbd.setSource(resource);
if (isCandidateComponent(sbd)) {
candidates.add(sbd);
}
.........
}
....
}
catch (Throwable ex) {
throw new BeanDefinitionStoreException(
"Failed to read candidate component class: " + resource, ex);
}
}
..........
}
}
catch (IOException ex) {
throw new BeanDefinitionStoreException("I/O failure during classpath scanning", ex);
}
return candidates;
}
@RestController
public class MyController {
@RequestMapping(value = "hello",method = RequestMethod.GET)
public String hello(@RequestParam String name, @RequestParam Integer age)
{
return "Hello " + name + " - " + age;
}
}
public class Book {
private Integer age;
private String name;
........
}
再通过isCandidateComponent
判断这个资源是不是需要注入的组件,如果是的话就添加到candidates
来返回。
protected boolean isCandidateComponent(MetadataReader metadataReader) throws IOException {
for (TypeFilter tf : this.excludeFilters) {
if (tf.match(metadataReader, getMetadataReaderFactory())) {
return false;
}
}
for (TypeFilter tf : this.includeFilters) {
if (tf.match(metadataReader, getMetadataReaderFactory())) {
return isConditionMatch(metadataReader);
}
}
return false;
}
这里就是前面注入的excludeFilters
、includeFilters
,排除与添加判断了。
这里的excludeFilters
我们在前面是有注入3个:然后我们来看下逻辑:
第一个是判断是否是定义的declaringClass
,也就是被@ComponentScan
注解的类,如果是就排除:
scanner.addExcludeFilter(new AbstractTypeHierarchyTraversingFilter(false, false) {
@Override
protected boolean matchClassName(String className) {
return declaringClass.equals(className);
}
});
第二个是AutoConfigurationExcludeFilter
,这个是用来判断是不是自动注入的配置类。
public class AutoConfigurationExcludeFilter implements TypeFilter, BeanClassLoaderAware {
private ClassLoader beanClassLoader;
private volatile List<String> autoConfigurations;
@Override
public void setBeanClassLoader(ClassLoader beanClassLoader) {
this.beanClassLoader = beanClassLoader;
}
@Override
public boolean match(MetadataReader metadataReader, MetadataReaderFactory metadataReaderFactory)
throws IOException {
return isConfiguration(metadataReader) && isAutoConfiguration(metadataReader);
}
private boolean isConfiguration(MetadataReader metadataReader) {
return metadataReader.getAnnotationMetadata().isAnnotated(Configuration.class.getName());
}
private boolean isAutoConfiguration(MetadataReader metadataReader) {
return getAutoConfigurations().contains(metadataReader.getClassMetadata().getClassName());
}
protected List<String> getAutoConfigurations() {
if (this.autoConfigurations == null) {
this.autoConfigurations = SpringFactoriesLoader.loadFactoryNames(EnableAutoConfiguration.class,
this.beanClassLoader);
}
return this.autoConfigurations;
}
}
这里我们看到其如果有扫描到@Configuration
、且是EnableAutoConfiguration
的配置,也是排除匹配,就不注入这个资源类。
这里的SpringFactoriesLoader.loadFactoryNames(EnableAutoConfiguration.class,this.beanClassLoader)
我们知道其实加载spring.factories
配置的值。例如这个EnableAutoConfiguration
就会加载很多这个xxxAutoConfiguration
:
# Auto Configure
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
.........
org.springframework.boot.autoconfigure.data.rest.RepositoryRestMvcAutoConfiguration,\
org.springframework.boot.autoconfigure.data.web.SpringDataWebAutoConfiguration,\
org.springframework.boot.autoconfigure.elasticsearch.jest.JestAutoConfiguration,\
org.springframework.boot.autoconfigure.flyway.FlywayAutoConfiguration,\
org.springframework.boot.autoconfigure.freemarker.FreeMarkerAutoConfiguration,\
org.springframework.boot.autoconfigure.gson.GsonAutoConfiguration,\
org.springframework.boot.autoconfigure.h2.H2ConsoleAutoConfiguration,\
.......
这里如果是@Component
扫描到的,其就会先忽略,因为@Configuration
我们知道是有专门处理的,然后这个EnableAutoConfiguration
配置也是有其他地方专门处理。这个我们本篇后面也会梳理到。
所有这里再扫描中,对于目前我们的两个使用的Book
、MyController
都是不会排除的return false;
。下面我们就来看下加载的TypeFilter
了。
这里是两个都是AnnotationTypeFilter
主要判断是:
@Override
protected boolean matchSelf(MetadataReader metadataReader) {
AnnotationMetadata metadata = metadataReader.getAnnotationMetadata();
return metadata.hasAnnotation(this.annotationType.getName()) ||
(this.considerMetaAnnotations && metadata.hasMetaAnnotation(this.annotationType.getName()));
}
也就是判断扫描到的类有没有注解为annotationType
的,这里也就是两个注解@Component
、ManagedBean
,即一般的组件,扫描的就返回true
,加到candidates
中返回。
public class AnnotationTypeFilter extends AbstractTypeHierarchyTraversingFilter {
private final Class<? extends Annotation> annotationType;
private final boolean considerMetaAnnotations;
所以这里会扫描到MyController
,因为RestController
的上层就是@Component
注解。
2)、遍历处理candidates
上面处理扫描到组件后就是遍历,根据不同情况进行处理。
for (BeanDefinition candidate : candidates) {
ScopeMetadata scopeMetadata = this.scopeMetadataResolver.resolveScopeMetadata(candidate);
candidate.setScope(scopeMetadata.getScopeName());
String beanName = this.beanNameGenerator.generateBeanName(candidate, this.registry);
if (candidate instanceof AbstractBeanDefinition) {
postProcessBeanDefinition((AbstractBeanDefinition) candidate, beanName);
}
if (candidate instanceof AnnotatedBeanDefinition) {
AnnotationConfigUtils.processCommonDefinitionAnnotations((AnnotatedBeanDefinition) candidate);
}
if (checkCandidate(beanName, candidate)) {
BeanDefinitionHolder definitionHolder = new BeanDefinitionHolder(candidate, beanName);
definitionHolder =
AnnotationConfigUtils.applyScopedProxyMode(scopeMetadata, definitionHolder, this.registry);
beanDefinitions.add(definitionHolder);
registerBeanDefinition(definitionHolder, this.registry);
}
}
前面两个if
就是填充一些信息,就不展开了。第三个if
:checkCandidate(beanName, candidate)
主要是判断当前beanName
有没有注册,没有就返回true
进入这个分支进行registerBeanDefinition(definitionHolder, this.registry)
即BeanDefintion
的注册。
所以这个@Component
注解,在@SpringBootApplication
中的作用,就是加载当前被这个@SpringBootApplication
注解类的包下面的所有组件。
我们根据这个应该也能自定义拓展一个TypeFilter
类型的类,就是我们上面的demo1
。
四、@EnableAutoConfiguration注解
1、结构
@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 {};
}
这里我们可以看到主要是两个注解@AutoConfigurationPackage
、@Import(AutoConfigurationImportSelector.class)
。
2、@AutoConfigurationPackage
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@Import(AutoConfigurationPackages.Registrar.class)
public @interface AutoConfigurationPackage {
}
这个主要是引入AutoConfigurationPackages.Registrar
public abstract class AutoConfigurationPackages {
private static final String BEAN = AutoConfigurationPackages.class.getName();
public static boolean has(BeanFactory beanFactory) {
return beanFactory.containsBean(BEAN) && !get(beanFactory).isEmpty();
}
.........
public static void register(BeanDefinitionRegistry registry, String... packageNames) {
if (registry.containsBeanDefinition(BEAN)) {
.........
}
else {
..........
}
}
private static String[] addBasePackages(ConstructorArgumentValues constructorArguments, String[] packageNames) {
String[] existing = (String[]) constructorArguments.getIndexedArgumentValue(0, String[].class).getValue();
Set<String> merged = new LinkedHashSet<>();
merged.addAll(Arrays.asList(existing));
merged.addAll(Arrays.asList(packageNames));
return StringUtils.toStringArray(merged);
}
/**
* {@link ImportBeanDefinitionRegistrar} to store the base package from the importing
* configuration.
*/
static class Registrar implements ImportBeanDefinitionRegistrar, DeterminableImports {
@Override
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));
}
}
/**
* Wrapper for a package import.
*/
private static final class PackageImport {
private final String packageName;
PackageImport(AnnotationMetadata metadata) {
this.packageName = ClassUtils.getPackageName(metadata.getClassName());
}
...................
然后这个Registrar
是实现的ImportBeanDefinitionRegistrar
,这个我们上一篇有梳理,主要是通过registerBeanDefinitions
来注入BeanDefintion
。,然后这里就是调的:
public static void register(BeanDefinitionRegistry registry, String... packageNames) {
if (registry.containsBeanDefinition(BEAN)) {
BeanDefinition beanDefinition = registry.getBeanDefinition(BEAN);
ConstructorArgumentValues constructorArguments = beanDefinition.getConstructorArgumentValues();
constructorArguments.addIndexedArgumentValue(0, addBasePackages(constructorArguments, packageNames));
}
else {
GenericBeanDefinition beanDefinition = new GenericBeanDefinition();
beanDefinition.setBeanClass(BasePackages.class);
beanDefinition.getConstructorArgumentValues().addIndexedArgumentValue(0, packageNames);
beanDefinition.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
registry.registerBeanDefinition(BEAN, beanDefinition);
}
}
就注入BasePackages.class
,同时这个BeanDefintion
的角色定义是BeanDefinition.ROLE_INFRASTRUCTURE
,即用于Spring
结构体系内部的BeanDefintion
定义,不过这里我并没有看到这个PackageImport
Bean的使用,然后这个PackageImport
主要是用来记录扫描的包名称。我们可以看到其会使用constructorArguments.addIndexedArgumentValue(0, addBasePackages(constructorArguments, packageNames));
方法来累计记载。
下面我们就来看一下@SpringBootApplcation
引入的重要类AutoConfigurationImportSelector
。
五、AutoConfigurationImportSelector
1、结构
public class AutoConfigurationImportSelector implements DeferredImportSelector, BeanClassLoaderAware,
ResourceLoaderAware, BeanFactoryAware, EnvironmentAware, Ordered {
这个它有实现各种Aware
接口,我们上篇文章有梳理,就是在正式调用DeferredImportSelector
(ImportSelector
的子类)前,其会先去调用Aware
接口的方法。
不过这个类的主要逻辑是对DeferredImportSelector
接口的实现,这个接口是一种延迟筛选执行接口。
2、DeferredImportSelector
1)、类实现
public interface DeferredImportSelector extends ImportSelector {
@Nullable
default Class<? extends Group> getImportGroup() {
return null;
}
interface Group {
void process(AnnotationMetadata metadata, DeferredImportSelector selector);
Iterable<Entry> selectImports();
class Entry {
private final AnnotationMetadata metadata;
private final String importClassName;
public Entry(AnnotationMetadata metadata, String importClassName) {
this.metadata = metadata;
this.importClassName = importClassName;
}
public AnnotationMetadata getMetadata() {
return this.metadata;
}
public String getImportClassName() {
return this.importClassName;
}
.........
}
}
}
可以看到这个接口接口主要是一个getImportGroup()
方法,同时这个方法是返回一个Class
,这个Class
是继承的下面的Group
接口,然后其他的就是ImportSelector
接口的方法实现,这个Group
是用来分组的,就是说,用哪个Group
来处理哪一类的DeferredImportSelector
。我们通过上一篇文章知道,ImportSelector
是用来返回需要注册的具体的类的全名,然后再包装为SourceClass
,来进行加载为ConfigurationClass
,最后再通过ConfigurationClass
来解析构建为对应的BeanDefinition
。
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);
}
if (candidate.isAssignable(ImportSelector.class)) {
// Candidate class is an ImportSelector -> delegate to it to determine imports
Class<?> candidateClass = candidate.loadClass();
ImportSelector selector = ParserStrategyUtils.instantiateClass(candidateClass, ImportSelector.class,
this.environment, this.resourceLoader, this.registry);
if (selector instanceof DeferredImportSelector) {
this.deferredImportSelectorHandler.handle(configClass, (DeferredImportSelector) selector);
}
else {
String[] importClassNames = selector.selectImports(currentSourceClass.getMetadata());
Collection<SourceClass> importSourceClasses = asSourceClasses(importClassNames);
processImports(configClass, currentSourceClass, importSourceClasses, false);
}
}
那这个ImportSelector
延迟处理是怎样实现的呢?我们可以看到如果不是selector instanceof DeferredImportSelector
,其是直接的掉用了``ImportSelector的
selector.selectImports(currentSourceClass.getMetadata())方法。然后是
DeferredImportSelector`。
2)、AutoConfigurationImportSelector对DeferredImportSelector的实现
@Override
public Class<? extends Group> getImportGroup() {
return AutoConfigurationGroup.class;
}
我们可以看到这里AutoConfigurationImportSelector
使用的处理DeferredImportSelector
是AutoConfigurationGroup
3)、DeferredImportSelectorHandler
private class DeferredImportSelectorHandler {
@Nullable
private List<DeferredImportSelectorHolder> deferredImportSelectors = new ArrayList<>();
public void handle(ConfigurationClass configClass, DeferredImportSelector importSelector) {
DeferredImportSelectorHolder holder = new DeferredImportSelectorHolder(
configClass, importSelector);
if (this.deferredImportSelectors == null) {
DeferredImportSelectorGroupingHandler handler = new DeferredImportSelectorGroupingHandler();
handler.register(holder);
handler.processGroupImports();
}
else {
this.deferredImportSelectors.add(holder);
}
}
public void process() {
List<DeferredImportSelectorHolder> deferredImports = this.deferredImportSelectors;
this.deferredImportSelectors = null;
try {
if (deferredImports != null) {
DeferredImportSelectorGroupingHandler handler = new DeferredImportSelectorGroupingHandler();
deferredImports.sort(DEFERRED_IMPORT_COMPARATOR);
deferredImports.forEach(handler::register);
handler.processGroupImports();
}
}
finally {
this.deferredImportSelectors = new ArrayList<>();
}
}
}
private static class DeferredImportSelectorHolder {
private final ConfigurationClass configurationClass;
private final DeferredImportSelector importSelector;
..........
}
这里是先通过handler
方法将入参构建为DeferredImportSelectorHolder
,再将其this.deferredImportSelectors.add(holder);
。同时这里还有this.deferredImportSelectors == null
,这个我们看到没人是不会为null
的,但在process()
方法执行过程中就会this.deferredImportSelectors = null
。不过这两个分支的核心执行都是:
DeferredImportSelectorGroupingHandler handler = new DeferredImportSelectorGroupingHandler();
handler.register(holder);
handler.processGroupImports();
所以这里的延迟就是先将这些内容存储起来,先不掉用其的selectImports
方法,然后我们看第一个处理handler.processGroupImports();
的process
方法:
public void parse(Set<BeanDefinitionHolder> configCandidates) {
for (BeanDefinitionHolder holder : configCandidates) {
BeanDefinition bd = holder.getBeanDefinition();
try {
if (bd instanceof AnnotatedBeanDefinition) {
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);
}
}
this.deferredImportSelectorHandler.process();
}
即这个DeferredImportSelector
的正式处理是在parse()
方法最后面,所有要处理的@Configuration
、@Component
、@ComponentScan
这些都已经处理完之后,即所有该扫描加载的BeanDefinition
都已经差不多加载完了,再执行的。
下面我们就通过DeferredImportSelectorGroupingHandler
来具体看下processGroupImports()
方法的执行:
handler.register(holder);
handler.processGroupImports();
这里关键是上面这两个方法的内容,会在这两个方法注入自动配置的内容,我本来是写了一些,当感觉不容易说明白,就去掉了,我们就不具体展开了,这里的关键就是前面的AutoConfigurationGroup
类,主要是它处理加载一些内容(类)。然后这两个方法有会掉回AutoConfigurationImportSelector
。
3、process(AutoConfigurationGroup)
@Override
public void process(AnnotationMetadata annotationMetadata, DeferredImportSelector deferredImportSelector) {
Assert.state(deferredImportSelector instanceof AutoConfigurationImportSelector,
() -> String.format("Only %s implementations are supported, got %s",
AutoConfigurationImportSelector.class.getSimpleName(),
deferredImportSelector.getClass().getName()));
AutoConfigurationEntry autoConfigurationEntry = ((AutoConfigurationImportSelector) deferredImportSelector)
.getAutoConfigurationEntry(getAutoConfigurationMetadata(), annotationMetadata);
this.autoConfigurationEntries.add(autoConfigurationEntry);
for (String importClassName : autoConfigurationEntry.getConfigurations()) {
this.entries.putIfAbsent(importClassName, annotationMetadata);
}
}
这里关键是getAutoConfigurationMetadata()
获取自动配置的元素,然后再去调用AutoConfigurationImportSelector
的getAutoConfigurationEntry
方法。
private AutoConfigurationMetadata getAutoConfigurationMetadata() {
if (this.autoConfigurationMetadata == null) {
this.autoConfigurationMetadata = AutoConfigurationMetadataLoader.loadMetadata(this.beanClassLoader);
}
return this.autoConfigurationMetadata;
}
protected static final String PATH = "META-INF/spring-autoconfigure-metadata.properties";
private AutoConfigurationMetadataLoader() {
}
static AutoConfigurationMetadata loadMetadata(ClassLoader classLoader) {
return loadMetadata(classLoader, PATH);
}
private static class PropertiesAutoConfigurationMetadata implements AutoConfigurationMetadata {
private final Properties properties;
这里主要是加载获取META-INF/spring-autoconfigure-metadata.properties
的内容:
org.springframework.boot.autoconfigure.data.jpa.JpaRepositoriesAutoConfiguration=
org.springframework.boot.autoconfigure.web.client.RestTemplateAutoConfiguration.AutoConfigureAfter=org.springframework.boot.autoconfigure.http.HttpMessageConvertersAutoConfiguration
org.springframework.boot.autoconfigure.data.cassandra.CassandraReactiveDataAutoConfiguration.ConditionalOnClass=com.datastax.driver.core.Cluster,reactor.core.publisher.Flux,org.springframework.data.cassandra.core.ReactiveCassandraTemplate
org.springframework.boot.autoconfigure.web.servlet.error.ErrorMvcAutoConfiguration=
org.springframework.boot.autoconfigure.jersey.JerseyAutoConfiguration.AutoConfigureBefore=org.springframework.boot.autoconfigure.web.servlet.DispatcherServletAutoConfiguration
org.springframework.boot.autoconfigure.jdbc.JdbcTemplateAutoConfiguration.ConditionalOnClass=javax.sql.DataSource,org.springframework.jdbc.core.JdbcTemplate
org.springframework.boot.autoconfigure.jersey.JerseyAutoConfiguration.AutoConfigureAfter=org.springframework.boot.autoconfigure.jackson.JacksonAutoConfiguration
org.springframework.boot.autoconfigure.web.servlet.ServletWebServerFactoryAutoConfiguration.ConditionalOnWebApplication=SERVLET
.................
我们看到这里的配置是有400多定义的key,下面我们就来看下是怎样使用的
4、getAutoConfigurationEntry
protected AutoConfigurationEntry getAutoConfigurationEntry(AutoConfigurationMetadata autoConfigurationMetadata,
AnnotationMetadata annotationMetadata) {
if (!isEnabled(annotationMetadata)) {
return EMPTY_ENTRY;
}
AnnotationAttributes attributes = getAttributes(annotationMetadata);
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);
}
protected static class AutoConfigurationEntry {
private final List<String> configurations;
private final Set<String> exclusions;
然后这里会返回AutoConfigurationEntry
,要注入的configurations
(待注入的类名称)、exclusions
(应剔除的类名称)。
这里整个流程是通过getCandidateConfigurations(annotationMetadata, attributes)
方法获取到spring.factories
文件中以EnableAutoConfiguration
为key
的values
。
然后获取到EnableAutoConfiguration
待剔除的类名exclusions
,再通过checkExcludedClasses
检查configurations
与exclusions
,如果configurations
中没有exclusions
即要剔除的类,就会抛出IllegalStateException
异常。
再之后是拦截看最终的configurations
,已经通过fireAutoConfigurationImportEvents
来发布本次自动配置导入事件。
1)、getCandidateConfigurations(@EnableAutoConfiguration)
protected List<String> getCandidateConfigurations(AnnotationMetadata metadata, AnnotationAttributes attributes) {
List<String> configurations = SpringFactoriesLoader.loadFactoryNames(getSpringFactoriesLoaderFactoryClass(),
getBeanClassLoader());
return configurations;
}
protected Class<?> getSpringFactoriesLoaderFactoryClass() {
return EnableAutoConfiguration.class;
}
这里就是获取META-INF/spring.factories
中以EnableAutoConfiguration
为key
的值。
# Auto Configure
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
org.springframework.boot.autoconfigure.admin.SpringApplicationAdminJmxAutoConfiguration,\
org.springframework.boot.autoconfigure.aop.AopAutoConfiguration,\
org.springframework.boot.autoconfigure.amqp.RabbitAutoConfiguration,\
org.springframework.boot.autoconfigure.batch.BatchAutoConfiguration,\
org.springframework.boot.autoconfigure.cache.CacheAutoConfiguration,\
...........
我们看到通过EnableAutoConfiguration
也 就是自动配置的内容是有124
个,这些都是xxxAutoConfiguration
,也就是自动去注入一些Bean
,注入的原理就是通过这些@Conditionxxx
来前置判断,看最终需不需要注入这些Bean
。这个具体的内容我们下一篇梳理。
@Configuration(proxyBeanMethods = false)
@ConditionalOnProperty(prefix = "spring.aop", name = "auto", havingValue = "true", matchIfMissing = true)
public class AopAutoConfiguration {
@Configuration(proxyBeanMethods = false)
@ConditionalOnClass(Advice.class)
static class AspectJAutoProxyingConfiguration {
@Configuration(proxyBeanMethods = false)
@EnableAspectJAutoProxy(proxyTargetClass = false)
@ConditionalOnProperty(prefix = "spring.aop", name = "proxy-target-class", havingValue = "false",
matchIfMissing = false)
static class JdkDynamicAutoProxyConfiguration {
}
@Configuration(proxyBeanMethods = false)
@EnableAspectJAutoProxy(proxyTargetClass = true)
@ConditionalOnProperty(prefix = "spring.aop", name = "proxy-target-class", havingValue = "true",
matchIfMissing = true)
static class CglibAutoProxyConfiguration {
}
}
@Configuration(proxyBeanMethods = false)
@ConditionalOnMissingClass("org.aspectj.weaver.Advice")
@ConditionalOnProperty(prefix = "spring.aop", name = "proxy-target-class", havingValue = "true",
matchIfMissing = true)
static class ClassProxyingConfiguration {
ClassProxyingConfiguration(BeanFactory beanFactory) {
if (beanFactory instanceof BeanDefinitionRegistry) {
BeanDefinitionRegistry registry = (BeanDefinitionRegistry) beanFactory;
AopConfigUtils.registerAutoProxyCreatorIfNecessary(registry);
AopConfigUtils.forceAutoProxyCreatorToUseClassProxying(registry);
}
}
}
}
2)、filter
private List<String> filter(List<String> configurations, AutoConfigurationMetadata autoConfigurationMetadata) {
long startTime = System.nanoTime();
String[] candidates = StringUtils.toStringArray(configurations);
boolean[] skip = new boolean[candidates.length];
boolean skipped = false;
for (AutoConfigurationImportFilter filter : getAutoConfigurationImportFilters()) {
invokeAwareMethods(filter);
boolean[] match = filter.match(candidates, autoConfigurationMetadata);
for (int i = 0; i < match.length; i++) {
if (!match[i]) {
skip[i] = true;
candidates[i] = null;
skipped = true;
}
}
}
if (!skipped) {
return configurations;
}
List<String> result = new ArrayList<>(candidates.length);
for (int i = 0; i < candidates.length; i++) {
if (!skip[i]) {
result.add(candidates[i]);
}
}
return new ArrayList<>(result);
}
这里主要是通过AutoConfigurationImportFilter
来判断最终candidates
有哪些是匹配的,最终将匹配的添加到result
来最终返回。然后这个AutoConfigurationImportFilter
也同样会先调用invokeAwareMethods(filter)
。
protected List<AutoConfigurationImportFilter> getAutoConfigurationImportFilters() {
return SpringFactoriesLoader.loadFactories(AutoConfigurationImportFilter.class, this.beanClassLoader);
}
这个getAutoConfigurationImportFilters()
就是获取以AutoConfigurationImportFilter
为key的值。
# 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
当前是这3个Condition
。可以看到这些也是属于Condition
这个处理类型的。
通过以上分析我们就知道了@SpringBootApplication
是怎样扫描组件,已经怎样自动注入一些默认的配置类的。
这里其实就是有些自定义一个SpringBoot
的starter
启动的过程了,可以看下我前面写的一个demo,即自定义一个SpringBootStarter
。关键就是对spring.factories
中EnableAutoConfiguration
的value来注入。
然后这里缺的整个Condition
的处理过程,我们下一篇再梳理,这里还与另一个我们前面提到的ConditionEvaluator
处理有一定相似。