SpringBoot自动配置原理是什么?
面试过程中问得最多的可能是自动装配的原理,而自动装配是在启动过程中完成,只不过在刚开始的时候我们选择性的跳过了,下面详细讲解自动装配的过程。
1、在springboot的启动过程中,有一个步骤是创建上下文,如果不记得可以看下面的代码:
public ConfigurableApplicationContext run(String... args) { StopWatch stopWatch =newStopWatch(); stopWatch.start(); ConfigurableApplicationContext context =null; Collection exceptionReporters =newArrayList<>(); configureHeadlessProperty(); SpringApplicationRunListeners listeners = getRunListeners(args); listeners.starting();try{ ApplicationArguments applicationArguments =newDefaultApplicationArguments(args); ConfigurableEnvironment environment = prepareEnvironment(listeners, applicationArguments); configureIgnoreBeanInfo(environment); Banner printedBanner = printBanner(environment); context = createApplicationContext(); exceptionReporters = getSpringFactoriesInstances(SpringBootExceptionReporter.class,newClass[] { ConfigurableApplicationContext.class},context);//此处完成自动装配的过程 prepareContext(context, environment, listeners, applicationArguments, printedBanner); refreshContext(context); afterRefresh(context, applicationArguments); stopWatch.stop();if(this.logStartupInfo) {newStartupInfoLogger(this.mainApplicationClass).logStarted(getApplicationLog(), stopWatch); } listeners.started(context); callRunners(context, applicationArguments); }catch(Throwable ex) { handleRunFailure(context, ex, exceptionReporters, listeners);thrownewIllegalStateException(ex); }try{ listeners.running(context); }catch(Throwable ex) { handleRunFailure(context, ex, exceptionReporters,null);thrownewIllegalStateException(ex); }returncontext; }
2、在prepareContext方法中查找load方法,一层一层向内点击,找到最终的load方法
//prepareContext方法privatevoid prepareContext(ConfigurableApplicationContext context, ConfigurableEnvironment environment, SpringApplicationRunListeners listeners, ApplicationArguments applicationArguments, Banner printedBanner) { context.setEnvironment(environment); postProcessApplicationContext(context); applyInitializers(context); listeners.contextPrepared(context);if(this.logStartupInfo) { logStartupInfo(context.getParent() ==null); logStartupProfileInfo(context); }// Add boot specific singleton beansConfigurableListableBeanFactory beanFactory = context.getBeanFactory(); beanFactory.registerSingleton("springApplicationArguments", applicationArguments);if(printedBanner !=null) { beanFactory.registerSingleton("springBootBanner", printedBanner); }if(beanFactory instanceof DefaultListableBeanFactory) { ((DefaultListableBeanFactory) beanFactory) .setAllowBeanDefinitionOverriding(this.allowBeanDefinitionOverriding); }if(this.lazyInitialization) { context.addBeanFactoryPostProcessor(new LazyInitializationBeanFactoryPostProcessor()); }// Load the sourcesSet sources = getAllSources(); Assert.notEmpty(sources,"Sources must not be empty");//load方法完成该功能load(context, sources.toArray(new Object[0])); listeners.contextLoaded(context); }/** * Load beans into the application context. *@paramcontext the context to load beans into *@paramsources the sources to load * 加载bean对象到context中 */protectedvoid load(ApplicationContext context, Object[] sources) {if(logger.isDebugEnabled()) { logger.debug("Loading source "+ StringUtils.arrayToCommaDelimitedString(sources)); }//获取bean对象定义的加载器BeanDefinitionLoader loader = createBeanDefinitionLoader(getBeanDefinitionRegistry(context), sources);if(this.beanNameGenerator !=null) { loader.setBeanNameGenerator(this.beanNameGenerator); }if(this.resourceLoader !=null) { loader.setResourceLoader(this.resourceLoader); }if(this.environment !=null) { loader.setEnvironment(this.environment); } loader.load(); }/** * Load the sources into the reader. *@returnthe number of loaded beans */int load() { int count =0;for(Object source :this.sources) { count += load(source); }returncount; }
3、实际执行load的是BeanDefinitionLoader中的load方法,如下:
//实际记载bean的方法privateintload(Object source){ Assert.notNull(source,"Source must not be null");//如果是class类型,启用注解类型if(sourceinstanceofClass) {returnload((Class) source); }//如果是resource类型,启动xml解析if(sourceinstanceofResource) {returnload((Resource) source); }//如果是package类型,启用扫描包,例如@ComponentScanif(sourceinstanceofPackage) {returnload((Package) source); }//如果是字符串类型,直接加载if(sourceinstanceofCharSequence) {returnload((CharSequence) source); }thrownewIllegalArgumentException("Invalid source type "+ source.getClass()); }
4、下面方法将用来判断是否资源的类型,是使用groovy加载还是使用注解的方式
privateint load(Class source) {//判断使用groovy脚本if(isGroovyPresent() && GroovyBeanDefinitionSource.class.isAssignableFrom(source)) {// Any GroovyLoaders added in beans{} DSL can contribute beans hereGroovyBeanDefinitionSource loader = BeanUtils.instantiateClass(source, GroovyBeanDefinitionSource.class);load(loader); }//使用注解加载if(isComponent(source)) {this.annotatedReader.register(source);return1; }return0; }
5、下面方法判断启动类中是否包含@Component注解,但是会神奇的发现我们的启动类中并没有该注解,继续更进发现MergedAnnotations类传入了一个参数
SearchStrategy.TYPE_HIERARCHY,会查找继承关系中是否包含这个注解,@SpringBootApplication-->@SpringBootConfiguration-->@Configuration-->@Component,当找到@Component注解之后,会把该对象注册到AnnotatedBeanDefinitionReader对象中
privatebooleanisComponent(Classtype) {// This has to be a bit of a guess. The only way to be sure that this type is// eligible is to make a bean definition out of it and try to instantiate it.if(MergedAnnotations.from(type, SearchStrategy.TYPE_HIERARCHY).isPresent(Component.class)) {returntrue; }// Nested anonymous classes are not eligible for registration, nor are groovy// closuresreturn!type.getName().matches(".*\\$_.*closure.*") && !type.isAnonymousClass() &&type.getConstructors() !=null&&type.getConstructors().length !=0;}/**
* Register a bean from the given bean class, deriving its metadata from
* class-declared annotations.
* 从给定的bean class中注册一个bean对象,从注解中找到相关的元数据
*/privatevoiddoRegisterBean(Class beanClass,@NullableStringname,@NullableClass[] qualifiers,@NullableSupplier supplier,@NullableBeanDefinitionCustomizer[] customizers) { AnnotatedGenericBeanDefinition abd =newAnnotatedGenericBeanDefinition(beanClass);if(this.conditionEvaluator.shouldSkip(abd.getMetadata())) {return; } abd.setInstanceSupplier(supplier); ScopeMetadata scopeMetadata =this.scopeMetadataResolver.resolveScopeMetadata(abd); abd.setScope(scopeMetadata.getScopeName());StringbeanName = (name !=null? name :this.beanNameGenerator.generateBeanName(abd,this.registry)); AnnotationConfigUtils.processCommonDefinitionAnnotations(abd);if(qualifiers !=null) {for(Class qualifier : qualifiers) {if(Primary.class == qualifier) { abd.setPrimary(true); }elseif(Lazy.class == qualifier) { abd.setLazyInit(true); }else{ abd.addQualifier(newAutowireCandidateQualifier(qualifier)); } } }if(customizers !=null) {for(BeanDefinitionCustomizer customizer : customizers) { customizer.customize(abd); } } BeanDefinitionHolder definitionHolder =newBeanDefinitionHolder(abd, beanName); definitionHolder = AnnotationConfigUtils.applyScopedProxyMode(scopeMetadata, definitionHolder,this.registry); BeanDefinitionReaderUtils.registerBeanDefinition(definitionHolder,this.registry); }/**
* Register the given bean definition with the given bean factory.
* 注册主类,如果有别名可以设置别名
*/publicstaticvoidregisterBeanDefinition( BeanDefinitionHolder definitionHolder, BeanDefinitionRegistry registry) throws BeanDefinitionStoreException {// Register bean definition under primary name.StringbeanName = definitionHolder.getBeanName(); registry.registerBeanDefinition(beanName, definitionHolder.getBeanDefinition());// Register aliases for bean name, if any.String[] aliases = definitionHolder.getAliases();if(aliases !=null) {for(Stringalias : aliases) { registry.registerAlias(beanName, alias); } } }//@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@interfaceSpringBootApplication {}//@SpringBootConfiguration@Target(ElementType.TYPE)@Retention(RetentionPolicy.RUNTIME)@Documented@Configurationpublic@interfaceSpringBootConfiguration {}//@Configuration@Target(ElementType.TYPE)@Retention(RetentionPolicy.RUNTIME)@Documented@Componentpublic@interfaceConfiguration {}
当看完上述代码之后,只是完成了启动对象的注入,自动装配还没有开始,下面开始进入到自动装配。
6、自动装配入口,从刷新容器开始
@Overridepublicvoidrefresh()throwsBeansException, IllegalStateException{synchronized(this.startupShutdownMonitor) {// Prepare this context for refreshing.prepareRefresh();// Tell the subclass to refresh the internal bean factory.ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();// Prepare the bean factory for use in this context.prepareBeanFactory(beanFactory);try{// Allows post-processing of the bean factory in context subclasses.postProcessBeanFactory(beanFactory);// Invoke factory processors registered as beans in the context.// 此处是自动装配的入口invokeBeanFactoryPostProcessors(beanFactory); }
7、在
invokeBeanFactoryPostProcessors方法中完成bean的实例化和执行
/**
* Instantiate and invoke all registered BeanFactoryPostProcessor beans,
* respecting explicit order if given.
*
Must be called before singleton instantiation.
*/protectedvoidinvokeBeanFactoryPostProcessors(ConfigurableListableBeanFactory beanFactory){//开始执行beanFactoryPostProcessor对应实现类,需要知道的是beanFactoryPostProcessor是spring的扩展接口,在刷新容器之前,该接口可以用来修改bean元数据信息PostProcessorRegistrationDelegate.invokeBeanFactoryPostProcessors(beanFactory, getBeanFactoryPostProcessors());// Detect a LoadTimeWeaver and prepare for weaving, if found in the meantime// (e.g. through an @Bean method registered by ConfigurationClassPostProcessor)if(beanFactory.getTempClassLoader() ==null&& beanFactory.containsBean(LOAD_TIME_WEAVER_BEAN_NAME)) { beanFactory.addBeanPostProcessor(newLoadTimeWeaverAwareProcessor(beanFactory)); beanFactory.setTempClassLoader(newContextTypeMatchClassLoader(beanFactory.getBeanClassLoader())); } }
8、查看
invokeBeanFactoryPostProcessors的具体执行方法
publicstaticvoidinvokeBeanFactoryPostProcessors(
ConfigurableListableBeanFactory beanFactory, List
9、开始执行自动配置逻辑(启动类指定的配置,非默认配置),可以通过debug的方式一层层向里进行查找,会发现最终会在ConfigurationClassParser类中,此类是所有配置类的解析类,所有的解析逻辑在parser.parse(candidates)中
publicvoidparse(Set
10、继续跟进
doProcessConfigurationClass方法,此方式是支持注解配置的核心逻辑
/** * Apply processing and build a complete {@linkConfigurationClass} by reading the * annotations, members and methods from the source class. This method can be called * multiple times as relevant sources are discovered. *@paramconfigClass the configuration class being build *@paramsourceClass a source class *@returnthe superclass, or {@codenull} if none found or previously processed */@NullableprotectedfinalSourceClass doProcessConfigurationClass(ConfigurationClass configClass, SourceClass sourceClass) throws IOException {//处理内部类逻辑,由于传来的参数是启动类,并不包含内部类,所以跳过if(configClass.getMetadata().isAnnotated(Component.class.getName())) {// Recursively process any member (nested) classes firstprocessMemberClasses(configClass, sourceClass); }// Process any @PropertySource annotations//针对属性配置的解析for(AnnotationAttributes propertySource : AnnotationConfigUtils.attributesForRepeatable( sourceClass.getMetadata(), PropertySources.class,org.springframework.context.annotation.PropertySource.class)) {if(this.environment instanceof ConfigurableEnvironment) { processPropertySource(propertySource); }else{ logger.info("Ignoring @PropertySource annotation on ["+ sourceClass.getMetadata().getClassName() +"]. Reason: Environment must implement ConfigurableEnvironment"); } }// Process any @ComponentScan annotations// 这里是根据启动类@ComponentScan注解来扫描项目中的beanSet 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//遍历项目中的bean,如果是注解定义的bean,则进一步解析Set scannedBeanDefinitions =this.componentScanParser.parse(componentScan, sourceClass.getMetadata().getClassName());// Check the set of scanned definitions for any further config classes and parse recursively if neededfor(BeanDefinitionHolder holder : scannedBeanDefinitions) { BeanDefinition bdCand = holder.getBeanDefinition().getOriginatingBeanDefinition();if(bdCand ==null) { bdCand = holder.getBeanDefinition(); }if(ConfigurationClassUtils.checkConfigurationClassCandidate(bdCand,this.metadataReaderFactory)) {//递归解析,所有的bean,如果有注解,会进一步解析注解中包含的beanparse(bdCand.getBeanClassName(), holder.getBeanName()); } } } }// Process any @Import annotations//递归解析,获取导入的配置类,很多情况下,导入的配置类中会同样包含导入类注解processImports(configClass, sourceClass, getImports(sourceClass),true);// Process any @ImportResource annotations//解析@ImportResource配置类AnnotationAttributes importResource = AnnotationConfigUtils.attributesFor(sourceClass.getMetadata(), ImportResource.class);if(importResource !=null) { String[] resources = importResource.getStringArray("locations"); Class readerClass = importResource.getClass("reader");for(String resource : resources) { String resolvedResource =this.environment.resolveRequiredPlaceholders(resource); configClass.addImportedResource(resolvedResource, readerClass); } }// Process individual @Bean methods//处理@Bean注解修饰的类Set beanMethods = retrieveBeanMethodMetadata(sourceClass);for(MethodMetadata methodMetadata : beanMethods) { configClass.addBeanMethod(new BeanMethod(methodMetadata, configClass)); }// Process default methods on interfaces// 处理接口中的默认方法processInterfaces(configClass, sourceClass);// Process superclass, if any//如果该类有父类,则继续返回,上层方法判断不为空,则继续递归执行if(sourceClass.getMetadata().hasSuperClass()) { String superclass = sourceClass.getMetadata().getSuperClassName();if(superclass !=null&& !superclass.startsWith("java") && !this.knownSuperclasses.containsKey(superclass)) {this.knownSuperclasses.put(superclass, configClass);// Superclass found, return its annotation metadata and recursereturnsourceClass.getSuperClass(); } }// No superclass -> processing is completereturnnull; }
11、查看获取配置类的逻辑
processImports(configClass, sourceClass, getImports(sourceClass),true);/** * Returns {@code@Import} class, considering all meta-annotations. */privateSet getImports(SourceClass sourceClass) throws IOException { Set imports = new LinkedHashSet<>(); Set visited = new LinkedHashSet<>(); collectImports(sourceClass, imports, visited);returnimports; }------------------/** * Recursively collect all declared {@code@Import} values. Unlike most * meta-annotations it is valid to have several {@code@Import}s declared with * different values; the usual process of returning values from the first * meta-annotation on a class is not sufficient. *
For example, it is common for a {@code@Configuration} class to declare direct * {@code@Import}s in addition to meta-imports originating from an {@code@Enable} * annotation. * 看到所有的bean都以导入的方式被加载进去 */privatevoid collectImports(SourceClass sourceClass, Set imports, Set visited) throws IOException {if(visited.add(sourceClass)) {for(SourceClassannotation: sourceClass.getAnnotations()) { String annName =annotation.getMetadata().getClassName();if(!annName.equals(Import.class.getName())) { collectImports(annotation, imports, visited); } } imports.addAll(sourceClass.getAnnotationAttributes(Import.class.getName(),"value")); } }
12、继续回到ConfigurationClassParser中的parse方法中的最后一行,继续跟进该方法:
this.deferredImportSelectorHandler.process()-------------publicvoid process() { List 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<>(); } }---------------publicvoid processGroupImports() {for(DeferredImportSelectorGrouping grouping :this.groupings.values()) { grouping.getImports().forEach(entry -> { ConfigurationClass configurationClass =this.configurationClasses.get( entry.getMetadata());try{ processImports(configurationClass, asSourceClass(configurationClass), asSourceClasses(entry.getImportClassName()),false); }catch(BeanDefinitionStoreException ex) {throwex; }catch(Throwable ex) {thrownew BeanDefinitionStoreException("Failed to process import candidates for configuration class ["+ configurationClass.getMetadata().getClassName() +"]", ex); } }); } }------------/** * Return the imports defined by the group. *@returneach import with its associated configuration class */publicIterable getImports() {for(DeferredImportSelectorHolder deferredImport :this.deferredImports) {this.group.process(deferredImport.getConfigurationClass().getMetadata(), deferredImport.getImportSelector()); }returnthis.group.selectImports(); } }------------publicDeferredImportSelector getImportSelector() {returnthis.importSelector; }------------@Overridepublicvoid 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); } }
如何理解springboot中的starter?
使用spring+springmvc框架进行开发的时候,如果需要引入mybatis框架,那么需要在xml中定义需要的bean对象,这个过程很明显是很麻烦的,如果需要引入额外的其他组件,那么也需要进行复杂的配置,因此在springboot中引入了starter
starter就是一个jar包,写一个@Configuration的配置类,将这些bean定义在其中,然后再starter包的META-INF/spring.factories中写入配置类,那么springboot程序在启动的时候就会按照约定来加载该配置类
开发人员只需要将相应的starter包依赖进应用中,进行相关的属性配置,就可以进行代码开发,而不需要单独进行bean对象的配置