上节我们了解了springboot的应用的启动原理,其所有的核心都在run方法里,来看一段简单的代码:
@SpringBootApplication
public class AppApplication {
public static void main(String[] args) {
SpringApplication.run(AppApplication.class, args);
}
还是原来的味道,我们上节了解了run,接下来看看注解springBootApplication的原理过程
SpringBootApplication
我们可以发现该注解位于org.springframework.boot.autoconfigure包下,基本上我们的springboot应用都会用到该注解,都说该注解是自动装配作用,到底是如何运作的呢?其实我们不难发现该注解是一个组合注解,代码如下:
//springBootApplication.java
@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 {
/**
* Exclude specific auto-configuration classes such that they will never be applied.
* @return the classes to exclude
*/
@AliasFor(annotation = EnableAutoConfiguration.class)
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
*/
@AliasFor(annotation = EnableAutoConfiguration.class)
String[] excludeName() default {};
/**
* Base packages to scan for annotated components. Use {@link #scanBasePackageClasses}
* for a type-safe alternative to String-based package names.
* @return base packages to scan
* @since 1.3.0
*/
@AliasFor(annotation = ComponentScan.class, attribute = "basePackages")
String[] scanBasePackages() default {};
/**
* Type-safe alternative to {@link #scanBasePackages} for specifying the packages to
* scan for annotated components. The package of each class specified will be scanned.
* <p>
* Consider creating a special no-op marker class or interface in each package that
* serves no purpose other than being referenced by this attribute.
* @return base packages to scan
* @since 1.3.0
*/
@AliasFor(annotation = ComponentScan.class, attribute = "basePackageClasses")
Class<?>[] scanBasePackageClasses() default {};
}
我们接下来看一下这些注解的作用:
Inherited
该注解是JDK自带的注解,该注解一般作用于类上时,表明子类是可以继承它的,对方法或者属性时无效的.关于该注解的详解这里给大家推荐一篇写的不错的文章关于java 注解中元注解Inherited的使用详解,博主写的很详细这里就不多说了,我们接着看
SpringBootConfiguration
该注解位于org.springframework.boot.@SpringBootConfiguration包下,其主要的作用是用来标记是一个springboot的配置类,我们来看代码:
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Configuration
public @interface SpringBootConfiguration {
}
可以看到的是,我们该注解的上面还是继承spring的Configuration的注解,就是表明是一个配置类.
ComponentScan
该注解位于org.springframework.context.annotation包下,其主要的作用是用来扫描指定路径下的组件(@Componment、@Configuration、@Service等标注的注解)
EnableAutoConfiguration
该注解位于org.springframework.boot.autoconfigure包下,其主要的作用是用来开启自动装配的功能,也是springboot项目中最核心的注解,我们来看代码:
@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 {};
}
从代码中看到,AutoConfigurationPackage注解引起了我们的关注,该注解位于org.springframework.boot.autoconfigure包下,其主要的作用是自动配置包,可以获取主程序所在的包路径,同时将组件注册到spring的容器中,代码如下:
/**
* Indicates that the package containing the annotated class should be registered with
* {@link AutoConfigurationPackages}.
*
* @author Phillip Webb
* @since 1.3.0
* @see AutoConfigurationPackages
*/
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@Import(AutoConfigurationPackages.Registrar.class)
public @interface AutoConfigurationPackage {
}
从代码中可以看到,我们需要的注重点放在注解Import上,该注解位于org.springframework.context.annotation包下,其主要的作用是引入别的资源文件,关于该注解的使用可以看这篇文章@Import注解——导入资源,其中最重要的AutoConfigurationImportSelector才是我们需要重点分析的对象,接下来我们来看.
AutoConfigurationImportSelector
该类位于org.springframework.boot.autoconfigure包下,分别实现了DeferredImportSelector、BeanClassLoaderAware、ResourceLoaderAware、BeanFactoryAware、EnvironmentAware、Ordered 接口,来处理EnableAutoConfiguration注解的资源引入,来看代码:
getCandidateConfigurations
该getCandidateConfigurations(AnnotationMetadata metadata, AnnotationAttributes attributes) 方法主要是用来获取符合条件的配置类数组:
//AutoConfigurationImportSelector.java
protected List<String> getCandidateConfigurations(AnnotationMetadata metadata, AnnotationAttributes attributes) {
//1.从META-INF/spring.factories文件下加载指定类型的类的数组
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;
}
- 在1. 处,通过调用#getSpringFactoriesLoaderFactoryClass()在meta-inf/spring.factories下加载我们的配置类EnableAutoConfiguration,来看代码:
/**
* Return the class used by {@link SpringFactoriesLoader} to load configuration
* candidates.
* @return the factory class
*/
protected Class<?> getSpringFactoriesLoaderFactoryClass() {
return EnableAutoConfiguration.class;
}
微信截图_20190906221139.png在来接着看,在上述代码中通过SpringFactoriesLoader#loadFactoryNames(...)方法从meta-inf/spring.factories中加载指定类型EnableAutoConfiguration的类.来看一张图:
微信截图_20190906230558.png我们来看通过#getCandidateConfigurations(AnnotationMetadata metadata, AnnotationAttributes attributes)方法是如何调用的过程,来看张调用过程的图:
在上图的执行过程中我们总结了大概有3步的过程,分别来看下:
- 在第1步处,通过调用#refresh来准备spring容器的初始化过程,关于该部分的详解我们在上篇[spring容器之从bean的实例中获取对2(https://www.jianshu.com/p/b587b501bec6)说的很清楚了,想了解的可以去看看 .
- 在第2步处,通过#getCandidateConfigurations(...)从meta-inf/spring.factories文件下加载所有的EnableAutoConfiguration配置类型数组
- 第3步,才是我们接下来需要了解的东西,首先我们来看一段代码:
private final DeferredImportSelector.Group group;
public Iterable<Group.Entry> getImports() {
for (DeferredImportSelectorHolder deferredImport : this.deferredImports) {
this.group.process(deferredImport.getConfigurationClass().getMetadata(),
deferredImport.getImportSelector());
}
return this.group.selectImports();
}
}
该段代码来源于org.springframework.context.annotation.ConfigurationClassParser下内部类DeferredImportSelectorGroupingHandler#Iterable(...)方法,该方法的主要作用配置由注解import导入的每一个关联的配置类.
- 在上述代码中我们可以看到的是通过调用DeferredImportSelector.group#process(AnnotationMetadata metadata, DeferredImportSelector selector)方法来处理标有注解import的注解.后面来说.
- 在方法的最后通过调用DeferredImportSelector.group.selectImports()来选择需要导入的配置类.后续来说.
从上图中我们可以看到在此处该方法导入了22个配置类.在我们这段代码中一直有一个东东我们需要搞明白,那就是Group实例是如何来的问题,在我们的AutoConfiguationImportSelector类中有一个方法getImportGroup来获取对应的Group实例的接下来我们来看代码:
getImportGroup
public Class<? extends Group> getImportGroup() {
return AutoConfigurationGroup.class;
}
该方法位于AutoConfiguationImportSelector,其次是该类实现了DeferredImportSelector接口,接着看:
AutoConfigurationGroup
首先是AutoConfigurationGroup位于AutoConfiguationImportSelector类的内部类,同样的该类实现了DeferredImportSelector.Group .BeanClassLoaderAware以及BeanFactoryAware和ResourceLoaderAware接口.
/**AnnotationMetadata类型的映射*/
private final Map<String, AnnotationMetadata> entries = new LinkedHashMap<>();
/**保存AutoConfigurationEntry的集合*/
private final List<AutoConfigurationEntry> autoConfigurationEntries = new ArrayList<>();
private ClassLoader beanClassLoader;
private BeanFactory beanFactory;
private ResourceLoader resourceLoader;
private AutoConfigurationMetadata autoConfigurationMetadata;
微信截图_20190907191127.png上面这些代码是AutoConfigurationGroup类的属性,在该类中有一个AutoConfigurationGroup#process(...)方法,在该方法中是进行的赋值操作,包括我们的entries属性,其中key为配置类的全限定名称,来看张图:
微信截图_20190907174151.png看完了上图中entries属性的赋值过程,发现也没有我们想象的那么难,接着我们来看autoConfigurationEntries属性的赋值过程,同样是在#process(...)方法中进行赋值的操作,来看:
我们也看到了,其中AutoConfigurationEntry为AutoConfigurationImportSelector的内部类,来看代码:
protected static class AutoConfigurationEntry {
/** 配置类的全类名的数组*/
private final List<String> configurations;
/***
* 排除的配置类的全类名的数组
*/
private final Set<String> exclusions;
private AutoConfigurationEntry() {
this.configurations = Collections.emptyList();
this.exclusions = Collections.emptySet();
}
接着看,其中AutoConfigurationMetadata属性主要的作用是用来保存自动配置的的元数据.通过方法#getAutoConfigurationMetadata()来初始化的,来看代码:
private AutoConfigurationMetadata getAutoConfigurationMetadata() {
//如果不存在的话,进行加载
if (this.autoConfigurationMetadata == null) {
this.autoConfigurationMetadata = AutoConfigurationMetadataLoader.loadMetadata(this.beanClassLoader);
}
//存在的话直接返回
return this.autoConfigurationMetadata;
}
微信截图_20190907204621.png
简单的来说一下,要想让InfluxDbAutoConfiguration配置类生效,对应的InfluxDbAutoConfiguration类上@ConditionalOnClass({ InfluxDB.class }) 注解上就应该有对应的类型,因此,所以,autoConfigurationMetadata 属性,用途就是制定配置类(Configuration)的生效条件(Condition)
process方法
该方法位于#AutoConfigurationImportSelector#AutoConfigurationGroup内部类的# process(AnnotationMetadata annotationMetadata, DeferredImportSelector deferredImportSelector)方法:
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()));
//1.获取AutoConfigurationEntry实例
AutoConfigurationEntry autoConfigurationEntry = ((AutoConfigurationImportSelector) deferredImportSelector)
.getAutoConfigurationEntry(getAutoConfigurationMetadata(), annotationMetadata);
//2.保存
this.autoConfigurationEntries.add(autoConfigurationEntry);
//3.遍历添加到entries中
for (String importClassName : autoConfigurationEntry.getConfigurations()) {
this.entries.putIfAbsent(importClassName, annotationMetadata);
}
}
该方法就做了上述代码的三件事我们来看一下,其中我们的参数AnnotationMetadata一般是用来标注注解SpringBootApplication的元数据,因为我们的注解SpringBootApplication组合了注解EnableAutoConfiguration.参数deferredImportSelector一般是用来@EnableAutoConfiguration定义的import注解标注的类,也就是AutoConfigurationImportSelector对象.
- 在1处,通过调用#AutoConfigurationImportSelector#getAutoConfigurationEntry(AutoConfigurationMetadata autoConfigurationMetadata, AnnotationMetadata annotationMetadata) 方法来获取AutoConfigurationEntry对象,我们来看该方法:
protected AutoConfigurationEntry getAutoConfigurationEntry(AutoConfigurationMetadata autoConfigurationMetadata,
AnnotationMetadata annotationMetadata) {
//1.判断是否开启,没有的话,直接返回一个空数组
if (!isEnabled(annotationMetadata)) {
return EMPTY_ENTRY;
}
//2.获取原注解的属性
AnnotationAttributes attributes = getAttributes(annotationMetadata);
//3.获取符合条件的配置类数组
List<String> configurations = getCandidateConfigurations(annotationMetadata, attributes);
//4.去除重复的配置类
configurations = removeDuplicates(configurations);
//5.获取需要排除的配置类
Set<String> exclusions = getExclusions(annotationMetadata, attributes);
//5.1.校验排除的配置类
checkExcludedClasses(configurations, exclusions);
//5.2.从配置类数组中移除掉排除的配置类
configurations.removeAll(exclusions);
//6.过滤不符合的配置类从configurations中
configurations = filter(configurations, autoConfigurationMetadata);
//7.触发自动配置类引入完成的事件
fireAutoConfigurationImportEvents(configurations, exclusions);
//8.创建AutoConfigurationEntry对象
return new AutoConfigurationEntry(configurations, exclusions);
}
简单的来看一下上述方法都做了些什么过程:
- 在1.处,通过调用#isEnabled(annotationMetadata)来判断是否开启,未开启的话,直接返回一个空数组,代码如下:
//AutoConfigurationImportSelector.java
protected boolean isEnabled(AnnotationMetadata metadata) {
//判断'spring.boot.EnableAutoConfiguration'是否配置,是否开启自动配置
//默认情况下(未配置)
if (getClass() == AutoConfigurationImportSelector.class) {
return getEnvironment().getProperty(EnableAutoConfiguration.ENABLED_OVERRIDE_PROPERTY, Boolean.class, true);
}
return true;
}
- 在2处,通过调用方法# getAttributes(annotationMetadata)来获取原注解的属性,代码如下:
protected AnnotationAttributes getAttributes(AnnotationMetadata metadata) {
//
String name = getAnnotationClass().getName();
//获取属性
AnnotationAttributes attributes = AnnotationAttributes.fromMap(metadata.getAnnotationAttributes(name, true));
Assert.notNull(attributes, () -> "No auto-configuration attributes found. Is " + metadata.getClassName()
+ " annotated with " + ClassUtils.getShortName(name) + "?");
return attributes;
}
上述代码中首先是通过方法#getAnnotationClass().getName()获取到的是@EnableAutoConfiguration,所以这里返回的注解属性只能是exclude和excludeName这两个.
- 在3处通过方法#getCandidateConfigurations(annotationMetadata, attributes)获取符合条件的配置类的数组.
- 在4处,通过方法#removeDuplicates(configurations)将获取到的配置类进行过滤处理,这里选择需要的,代码如下:
protected final <T> List<T> removeDuplicates(List<T> list) {
return new ArrayList<>(new LinkedHashSet<>(list));
}
- 在5处,通过调用#getExclusions(annotationMetadata, attributes)方法来获取需要排除的配置类,代码如下:
//AutoConfigurationImportSelector.java
protected Set<String> getExclusions(AnnotationMetadata metadata, AnnotationAttributes attributes) {
Set<String> excluded = new LinkedHashSet<>();
//注解属性上的exclude
excluded.addAll(asList(attributes, "exclude"));
//添加注解上的excludeName属性
excluded.addAll(Arrays.asList(attributes.getStringArray("excludeName")));
//添加配置文件上的属性
excluded.addAll(getExcludeAutoConfigurationsProperty());
return excluded;
}
从上面的代码来看获取排除类的方式总共有三种,分别来看:
- 通过注解上的exclude属性来排除的.
//AutoConfigurationImportSelector.java
protected final List<String> asList(AnnotationAttributes attributes, String name) {
String[] value = attributes.getStringArray(name);
return Arrays.asList((value != null) ? value : new String[0]);
}
- 第二种是通过注解上的excludeName属性来排除的
- 方式三是通过配置文件上的属性来排除的,这里一般是spring.autoconfigure.exclude配置
private static final String PROPERTY_NAME_AUTOCONFIGURE_EXCLUDE = "spring.autoconfigure.exclude";
private List<String> getExcludeAutoConfigurationsProperty() {
if (getEnvironment() instanceof ConfigurableEnvironment) {
Binder binder = Binder.get(getEnvironment());
return binder.bind(PROPERTY_NAME_AUTOCONFIGURE_EXCLUDE, String[].class).map(Arrays::asList)
.orElse(Collections.emptyList());
}
String[] excludes = getEnvironment().getProperty(PROPERTY_NAME_AUTOCONFIGURE_EXCLUDE, String[].class);
return (excludes != null) ? Arrays.asList(excludes) : Collections.emptyList();
}
- 在5.1.处,通过方法#checkExcludedClasses(configurations, exclusions)对我们要排除的配置类进行合法性的检验,代码如下:
private void checkExcludedClasses(List<String> configurations, Set<String> exclusions) {
//用来保存需要检验的配置类
List<String> invalidExcludes = new ArrayList<>(exclusions.size());
//从集合exclusions中遍历处理
//如果该配置类在exclusions中(不在configurations中),进行添加操作
for (String exclusion : exclusions) {
//classPath中存在该配置类
if (ClassUtils.isPresent(exclusion, getClass().getClassLoader()) && !configurations.contains(exclusion)) {
invalidExcludes.add(exclusion);
}
}
//invalidExcludes不为null,抛IllegalStateException异常
if (!invalidExcludes.isEmpty()) {
handleInvalidExcludes(invalidExcludes);
}
}
/**
* Handle any invalid excludes that have been specified.
* @param invalidExcludes the list of invalid excludes (will always have at least one
* element)
*/
protected void handleInvalidExcludes(List<String> invalidExcludes) {
StringBuilder message = new StringBuilder();
for (String exclude : invalidExcludes) {
message.append("\t- ").append(exclude).append(String.format("%n"));
}
throw new IllegalStateException(String.format(
"The following classes could not be excluded because they are" + " not auto-configuration classes:%n%s",
message));
}
从上面的代码中可以看处,排除的过程是这样的,因为我们的配置类是存在于classPath下的,而不存在与configurations中,所以这样就可以直接排除了,接着看:
- 在5.2.处,从我们的配置类数组中configurations移除掉要排除的配置类
- 在6处,通过方法#filter(List<String> configurations, AutoConfigurationMetadata autoConfigurationMetadata)从configurations过滤掉不符合的配置类,后续来说该方法
- 在7处,通过方法# fireAutoConfigurationImportEvents(List<String> configurations, Set<String> exclusions)来触发自动配置类完成的一些列事件,代码如下:
//AutoConfigurationImportSelector.java
private void fireAutoConfigurationImportEvents(List<String> configurations, Set<String> exclusions) {
//1.在meta-INF目录下的spring.factories中加载指定类型为AutoConfigurationImportListener的所有类的数组
List<AutoConfigurationImportListener> listeners = getAutoConfigurationImportListeners();
//不为null
if (!listeners.isEmpty()) {
//2.通过我们的配置类数组和exclusions来构建AutoConfigurationImportEvent对象
AutoConfigurationImportEvent event = new AutoConfigurationImportEvent(this, configurations, exclusions);
//3.遍历 AutoConfigurationImportListener监听器们逐个通知
for (AutoConfigurationImportListener listener : listeners) {
//3.1设置AutoConfigurationImportListener属性
invokeAwareMethods(listener);
//3.2.进行通知操作
listener.onAutoConfigurationImportEvent(event);
}
}
}
上述代码中,不能理解,大致做了三件事,我们分别来看一下:
- 首先是通过#getAutoConfigurationImportListeners()方法在meta-INF目录下的spring.factories文件中去加载指定类型(AutoConfigurationImportListener)的所有类的集合,我们通过dbug会发现如下图:
在我们的集合中有一个类,接着看:
- 在上述条件成立的情况下,也就是当我们的listeners不为null时:
- 在2处创建一个AutoConfigurationImportEvent事件对象
- 在3处遍历 AutoConfigurationImportListener监听器们逐个通知
- 在3.1.处,通过调用#invokeAwareMethods(Object instance)方法来设置AutoConfigurationImportListener的相关属性,代码如下:
private void invokeAwareMethods(Object instance) {
//是Aware类型的
if (instance instanceof Aware) {
if (instance instanceof BeanClassLoaderAware) {
((BeanClassLoaderAware) instance).setBeanClassLoader(this.beanClassLoader);
}
if (instance instanceof BeanFactoryAware) {
((BeanFactoryAware) instance).setBeanFactory(this.beanFactory);
}
if (instance instanceof EnvironmentAware) {
((EnvironmentAware) instance).setEnvironment(this.environment);
}
if (instance instanceof ResourceLoaderAware) {
((ResourceLoaderAware) instance).setResourceLoader(this.resourceLoader);
}
}
}
代码简单,这里就不说啥了,我们接着看:
- 在3.2.处,对我们的事件进行通知处理操作,代码如下:
//ConditionEvaluationReportAutoConfigurationImportListener.java
public void onAutoConfigurationImportEvent(AutoConfigurationImportEvent event) {
if (this.beanFactory != null) {
ConditionEvaluationReport report = ConditionEvaluationReport.get(this.beanFactory);
report.recordEvaluationCandidates(event.getCandidateConfigurations());
report.recordExclusions(event.getExclusions());
}
}
代码简单,就不啰嗦了,继续回到我们的getAutoConfigurationEntry(...)方法
- 在8处,创建AutoConfigurationEntry对象
关于process方法我们基本上完了,我们来看一下注解AutoConfigurationPackage的作用
AutoConfigurationPackage
该注解位于org.springframework.boot.autoconfigure包下,官方是这样解释的:
Class for storing auto-configuration packages for reference later (e.g. by JPA entity scanner)
大致的意思是这样的,如果使用该注解@AutoConfigurationPackage所在的包,会被注册到spring IOC容器中的一个bean,我们通过获取该bean就能获取该bean所在的包,如:获取JPA等.
接下来,我们AutoConfigurationPackage类下的一些重要的方法
Registrar
Registrar是AutoConfigurationPackage类的一个内部类,可以看到的是它实现了ImportBeanDefinitionRegistrar和DeterminableImports接口,代码如下:
/**
* {@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));
}
}
在上述代码中,我们需要关注几个点如:
- 在方法#registerBeanDefinitions(...)中,需要我们new PackageImport对象作为参数的传递,那么PackageImport是什么,我们来看代码:
//AutoConfigurationPackages.java
/**
* Wrapper for a package import.
*/
private static final class PackageImport {
//包名
private final String packageName;
PackageImport(AnnotationMetadata metadata) {
this.packageName = ClassUtils.getPackageName(metadata.getClassName());
}
public String getPackageName() {
return this.packageName;
}
@Override
public boolean equals(Object obj) {
if (obj == null || getClass() != obj.getClass()) {
return false;
}
return this.packageName.equals(((PackageImport) obj).packageName);
}
@Override
public int hashCode() {
return this.packageName.hashCode();
}
@Override
public String toString() {
return "Package Import " + this.packageName;
}
}
我们发现PackageImport类为AutoConfigurationPackages的内部类,其主要的作用是用来获取包名.接着看
register方法
该方法主要的作用是注册包名的bean到spring容器中,以备后续通过该bean直接获取对应的包名,代码如下:
private static final String BEAN = AutoConfigurationPackages.class.getName();
public static void register(BeanDefinitionRegistry registry, String... packageNames) {
//如果存在该bean
if (registry.containsBeanDefinition(BEAN)) {
//获取该bean的定义
BeanDefinition beanDefinition = registry.getBeanDefinition(BEAN);
//获取构造参数以及构造函数等
ConstructorArgumentValues constructorArguments = beanDefinition.getConstructorArgumentValues();
//修改其包名属性
constructorArguments.addIndexedArgumentValue(0, addBasePackages(constructorArguments, packageNames));
}
//不存在该bean,创建一个GenericBeanDefinition兵注册
else {
GenericBeanDefinition beanDefinition = new GenericBeanDefinition();
beanDefinition.setBeanClass(BasePackages.class);
beanDefinition.getConstructorArgumentValues().addIndexedArgumentValue(0, packageNames);
beanDefinition.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
registry.registerBeanDefinition(BEAN, beanDefinition);
}
}
在上述注册的BEAN的类型为BasePackages类型的,其中BasePackages为AutoConfigurationPackages的内部类,来看代码:
/**
* Holder for the base package (name may be null to indicate no scanning).
*/
static final class BasePackages {
private final List<String> packages;
private boolean loggedBasePackageInfo;
BasePackages(String... names) {
List<String> packages = new ArrayList<>();
for (String name : names) {
if (StringUtils.hasText(name)) {
packages.add(name);
}
}
this.packages = packages;
}
public List<String> get() {
if (!this.loggedBasePackageInfo) {
if (this.packages.isEmpty()) {
if (logger.isWarnEnabled()) {
logger.warn("@EnableAutoConfiguration was declared on a class "
+ "in the default package. Automatic @Repository and "
+ "@Entity scanning is not enabled.");
}
}
else {
if (logger.isDebugEnabled()) {
String packageNames = StringUtils.collectionToCommaDelimitedString(this.packages);
logger.debug("@EnableAutoConfiguration was declared on a class " + "in the package '"
+ packageNames + "'. Automatic @Repository and @Entity scanning is " + "enabled.");
}
}
this.loggedBasePackageInfo = true;
}
return this.packages;
}
}
这段代码实质就是对packages的封装过程,无需多言,接着我们来看看注册的过程
- 在注册的过程中,如果存在BEAN仅仅是对包名属性进行修改,其中是通过方法addBasePackages(...)来操作,我们来看该方法:
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);
}
在我们的注册方法中,如果不存在该 BEAN ,则创建一个 Bean ,并进行注册.
- 首先是通过#has(BeanFactory beanFactory)来判断是否在spring容器中存在该bean,代码如下:
/**
* Determine if the auto-configuration base packages for the given bean factory are
* available.
* @param beanFactory the source bean factory
* @return true if there are auto-config packages available
*/
public static boolean has(BeanFactory beanFactory) {
return beanFactory.containsBean(BEAN) && !get(beanFactory).isEmpty();
}
- 接着是通过get方法来从容器中获取我们的bean,代码如下:
/**
* Return the auto-configuration base packages for the given bean factory.
* @param beanFactory the source bean factory
* @return a list of auto-configuration packages
* @throws IllegalStateException if auto-configuration is not enabled
*/
public static List<String> get(BeanFactory beanFactory) {
try {
return beanFactory.getBean(BEAN, BasePackages.class).get();
}
catch (NoSuchBeanDefinitionException ex) {
throw new IllegalStateException("Unable to retrieve @EnableAutoConfiguration base packages");
}
}
到这里关于springboot的自动装配的过程分析的差不多了....
网友评论