SpringBoot(二)核心原理
SpringBoot
1.自动装配原理:
在BeanFactoryPostProcessor实现类里面ConfigurationClassPostProcessor,实现自动装配
核心代码 :
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
@Override public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) { int factoryId = System.identityHashCode(beanFactory); if (this.factoriesPostProcessed.contains(factoryId)) { throw new IllegalStateException( "postProcessBeanFactory already called on this post-processor against " + beanFactory); } this.factoriesPostProcessed.add(factoryId); if (!this.registriesPostProcessed.contains(factoryId)) { // BeanDefinitionRegistryPostProcessor hook apparently not supported... // Simply call processConfigurationClasses lazily at this point then. processConfigBeanDefinitions((BeanDefinitionRegistry) beanFactory); } //扫描相关注解类 enhanceConfigurationClasses(beanFactory); beanFactory.addBeanPostProcessor(new ImportAwareBeanPostProcessor(beanFactory)); } //ConfigurationClassParser doProcessConfigurationClass(configClass, sourceClass);
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
@Nullable protected final SourceClass doProcessConfigurationClass(ConfigurationClass configClass, SourceClass sourceClass) throws IOException { // Recursively process any member (nested) classes first processMemberClasses(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.warn("Ignoring @PropertySource annotation on [" + sourceClass.getMetadata().getClassName() + "]. Reason: Environment must implement ConfigurableEnvironment"); } } // Process any @ComponentScan annotations //解析加了 @ComponentScan 只是将扫描的到类 存放到集合里面 这里不做具体的实例化工作 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 //解析 @scan到的bean 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) { if (ConfigurationClassUtils.checkConfigurationClassCandidate( holder.getBeanDefinition(), this.metadataReaderFactory)) { parse(holder.getBeanDefinition().getBeanClassName(), holder.getBeanName()); } } } } // Process any @Import annotations //configClass 是指启动类 就是 /** * @SpringBootApplication * public class SpringbootSourceApplication * 收集 @import 中指定的类,并且创建 import指定类对象 * 如:@Import({ InstA.class, RegisterBean.class,SelectImportBeanDemo.class,ImportClassBeanDemo.class}) * @Import(AutoConfigurationImportSelector.class) * public @interface EnableAutoConfiguration */ processImports(configClass, sourceClass, getImports(sourceClass), true); // Process any @ImportResource annotations AnnotationAttributes importResource = AnnotationConfigUtils.attributesFor(sourceClass.getMetadata(), ImportResource.class); if (importResource != null) { String[] resources = importResource.getStringArray("locations"); Class<? extends BeanDefinitionReader> readerClass = importResource.getClass("reader"); for (String resource : resources) { String resolvedResource = this.environment.resolveRequiredPlaceholders(resource); configClass.addImportedResource(resolvedResource, readerClass); } } // Process individual @Bean methods Set<MethodMetadata> 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 recurse return sourceClass.getSuperClass(); } } // No superclass -> processing is complete return null; } // private void processImports(ConfigurationClass configClass, SourceClass currentSourceClass, Collection<SourceClass> importCandidates, boolean checkForCircularImports) throws IOException { if (importCandidates.isEmpty()) { return; } if (checkForCircularImports && isChainedImportOnStack(configClass)) { this.problemReporter.error(new CircularImportProblem(configClass, this.importStack)); } else { this.importStack.push(configClass); try { for (SourceClass candidate : importCandidates) { if (candidate.isAssignable(ImportSelector.class)) { // Candidate class is an ImportSelector -> delegate to it to determine imports Class<?> candidateClass = candidate.loadClass(); //@import 中导入的类创建对象 ImportSelector selector = BeanUtils.instantiateClass(candidateClass, ImportSelector.class); ParserStrategyUtils.invokeAwareMethods( selector, this.environment, this.resourceLoader, this.registry); if (this.deferredImportSelectors != null && selector instanceof DeferredImportSelector) { this.deferredImportSelectors.add( new DeferredImportSelectorHolder(configClass, (DeferredImportSelector) selector)); } else { // AutoConfigurationImportSelector String[] importClassNames = selector.selectImports(currentSourceClass.getMetadata()); Collection<SourceClass> importSourceClasses = asSourceClasses(importClassNames); processImports(configClass, currentSourceClass, importSourceClasses, false); } } else if (candidate.isAssignable(ImportBeanDefinitionRegistrar.class)) { // Candidate class is an ImportBeanDefinitionRegistrar -> // delegate to it to register additional bean definitions Class<?> candidateClass = candidate.loadClass(); ImportBeanDefinitionRegistrar registrar = BeanUtils.instantiateClass(candidateClass, ImportBeanDefinitionRegistrar.class); ParserStrategyUtils.invokeAwareMethods( registrar, this.environment, this.resourceLoader, this.registry); configClass.addImportBeanDefinitionRegistrar(registrar, currentSourceClass.getMetadata()); } else { // Candidate class not an ImportSelector or ImportBeanDefinitionRegistrar -> // process it as an @Configuration class // 处理 @Configuration this.importStack.registerImport( currentSourceClass.getMetadata(), candidate.getMetadata().getClassName()); processConfigurationClass(candidate.asConfigClass(configClass)); } } } catch (BeanDefinitionStoreException ex) { throw ex; } catch (Throwable ex) { throw new BeanDefinitionStoreException( "Failed to process import candidates for configuration class [" + configClass.getMetadata().getClassName() + "]", ex); } finally { this.importStack.pop(); } } }
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
@Override public String[] selectImports(AnnotationMetadata annotationMetadata) { if (!isEnabled(annotationMetadata)) { return NO_IMPORTS; } AutoConfigurationEntry autoConfigurationEntry = getAutoConfigurationEntry(annotationMetadata); return StringUtils.toStringArray(autoConfigurationEntry.getConfigurations()); } protected AutoConfigurationEntry getAutoConfigurationEntry(AnnotationMetadata annotationMetadata) { if (!isEnabled(annotationMetadata)) { return EMPTY_ENTRY; } AnnotationAttributes attributes = getAttributes(annotationMetadata); //获取Spring.factories 下面 EnableAutoConfiguration 的value 存放如list里面 //扫描所有的spring.factories 文件 将 EnableAutoConfiguration 的value 都获取到并且存放到缓存里面 List<String> configurations = getCandidateConfigurations(annotationMetadata, attributes); //移除重复的 configurations = removeDuplicates(configurations); //获取排除的 Set<String> exclusions = getExclusions(annotationMetadata, attributes); checkExcludedClasses(configurations, exclusions); //从 获取到的自动装配类里面 移除 指定的排除类 configurations.removeAll(exclusions); configurations = getConfigurationClassFilter().filter(configurations); fireAutoConfigurationImportEvents(configurations, exclusions); return new AutoConfigurationEntry(configurations, exclusions); }
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
@Override public Set<Object> determineImports(AnnotationMetadata metadata) { List<String> candidateConfigurations = getCandidateConfigurations(metadata, null); Set<String> result = new LinkedHashSet<>(candidateConfigurations); result.removeAll(getExclusions(metadata, null)); return Collections.unmodifiableSet(result); } public static final String FACTORIES_RESOURCE_LOCATION = "META-INF/spring.factories"; private static Map<String, List<String>> loadSpringFactories(@Nullable ClassLoader classLoader) { MultiValueMap<String, String> result = cache.get(classLoader); if (result != null) return result; try { Enumeration<URL> urls = (classLoader != null ? classLoader.getResources(FACTORIES_RESOURCE_LOCATION) : ClassLoader.getSystemResources(FACTORIES_RESOURCE_LOCATION)); result = new LinkedMultiValueMap<>(); while (urls.hasMoreElements()) { URL url = urls.nextElement(); UrlResource resource = new UrlResource(url); Properties properties = PropertiesLoaderUtils.loadProperties(resource); for (Map.Entry<?, ?> entry : properties.entrySet()) { List<String> factoryClassNames = Arrays.asList( StringUtils.commaDelimitedListToStringArray((String) entry.getValue())); result.addAll((String) entry.getKey(), factoryClassNames); } } cache.put(classLoader, result); return result; } catch (IOException ex) { throw new IllegalArgumentException("Unable to load factories from location [" + FACTORIES_RESOURCE_LOCATION + "]", ex); } }
2.内置tomcat
onfresh()里面创建ServletWebServerApplicationContext.onRefresh创建Tomcat
ServletContainerInitializer
SpringServletContainerInitializer .onStartup
SpringApplicationRunListener:接口的作用主要就是在Spring Boot 启动初始化的过程中可以通过SpringApplicationRunListener接口回调来让用户在启动的各个流程中可以加入自己的逻辑
@SpringBootApplication
annotation can be used to enable those three features, that is:
@EnableAutoConfiguration
: enable Spring Boot’s auto-configuration mechanism(扫描“spring.factories”文件,实现自动注册)@ComponentScan
: enable@Component
scan on the package where the application is located (see the best practices)等同于(<compan-scan/>扫描加上注解的类)@Configuration
: allow to register extra beans in the context or import additional configuration classes等同于(spring中在spring.xml文件中 写一个<bean/>。bean注册及bean管理)
内置Tomcat
application
annotation is equivalent to using @Configuration
, @EnableAutoConfiguration
, and @ComponentScan
with their default attributes, as shown in the following example:
SpringBoot自动配置模块
该配置模块的主要使用到了SpringFactoriesLoader
,即Spring工厂加载器,该对象提供了loadFactoryNames
方法,入参为factoryClass和classLoader,即需要传入工厂类名称和对应的类加载器,方法会根据指定的classLoader,加载该类加器搜索路径下的指定文件,即spring.factories
文件,传入的工厂类为接口,而文件中对应的类则是接口的实现类,或最终作为实现类,所以文件中一般为如下图这种一对多的类名集合,获取到这些实现类的类名后,loadFactoryNames
方法返回类名集合,方法调用方得到这些集合后,再通过反射获取这些类的类对象、构造方法,最终生成实例。
SpringBoot自动化配置关键组件关系图
mybatis-spring-boot-starter
、spring-boot-starter-web
等组件的META-INF文件下均含有spring.factories
文件,自动配置模块中,SpringFactoriesLoader
收集到文件中的类全名并返回一个类全名的数组,返回的类全名通过反射被实例化,就形成了具体的工厂实例,工厂实例来生成组件具体需要的bean。
之前我们提到了EnableAutoConfiguration
注解,其类图如下:
可以发现其最终实现了ImportSelector
(选择器)和BeanClassLoaderAware
(bean类加载器中间件),重点关注一下AutoConfigurationImportSelector
的selectImports
方法。
MybatisAutoConfiguration
在上面的代码可以看到自动配置器会根据传入的factoryClass.getName()
到项目系统路径下所有的spring.factories
文件中找到相应的key,从而加载里面的类。我们就选取这个mybatis-spring-boot-autoconfigure
下的spring.factories
文件
发现Spring的@Configuration
,俨然是一个通过注解标注的springBean,继续向下看,
-
@ConditionalOnClass({ SqlSessionFactory.class, SqlSessionFactoryBean.class})
:当存在SqlSessionFactory.class
,SqlSessionFactoryBean.class
这两个类时才解析MybatisAutoConfiguration
配置类,否则不解析这一个配置类,make sence,我们需要mybatis为我们返回会话对象,就必须有会话工厂相关类。 -
@CondtionalOnBean(DataSource.class)
:只有处理已经被声明为bean的dataSource。 -
@ConditionalOnMissingBean(MapperFactoryBean.class)
这个注解的意思是如果容器中不存在name指定的bean则创建bean注入,否则不执行(该类源码较长,篇幅限制不全粘贴)
以上配置可以保证sqlSessionFactory、sqlSessionTemplate、dataSource
等mybatis所需的组件均可被自动配置,@Configuration
注解已经提供了Spring的上下文环境,所以以上组件的配置方式与Spring启动时通过mybatis.xml文件进行配置起到一个效果。
通过分析我们可以发现,只要一个基于SpringBoot项目的类路径下存在SqlSessionFactory.class
, SqlSessionFactoryBean.class
,并且容器中已经注册了dataSourceBean,就可以触发自动化配置,意思说我们只要在maven的项目中加入了mybatis所需要的若干依赖,就可以触发自动配置,但引入mybatis原生依赖的话,每集成一个功能都要去修改其自动化配置类,那就得不到开箱即用的效果了。
这里是截取的mybatis-spring-boot-starter
的源码中pom.xml文件中所有依赖:
因为maven依赖的传递性,我们只要依赖starter就可以依赖到所有需要自动配置的类,实现开箱即用的功能。也体现出Springboot简化了Spring框架带来的大量XML配置以及复杂的依赖管理,让开发人员可以更加关注业务逻辑的开发。
SpringBoot 内置tomcat
@SpringBootApplication->,@EnableAutoConfiguration->@ComponentScan,@EnableAutoConfiguration->AutoConfigurationImportSelector:selectImports()
-getAutoConfigurationEntry()->getCandidateConfigurations():
org.springframework.boot.autoconfigure.EnableAutoConfiguration=org.springframework.boot.autoconfigure.web.servlet.ServletWebServerFactoryAutoConfiguration,\
autoconfig.jar 下面 spring-boot-autoconfigure-2.1.1.RELEASE.jar->spring.factories
ServletWebServerFactoryAutoConfiguration(生产出一个servlet容器)):
EmbeddedTomcat->TomcatServletWebServerFactory->getWebServer
public WebServer getWebServer(ServletContextInitializer... initializers) {
//等于创建了server 里面在添加Connector,host 等 Tomcat tomcat = new Tomcat(); File baseDir = this.baseDirectory != null ? this.baseDirectory : this.createTempDir("tomcat"); tomcat.setBaseDir(baseDir.getAbsolutePath()); Connector connector = new Connector(this.protocol); tomcat.getService().addConnector(connector); this.customizeConnector(connector); tomcat.setConnector(connector); tomcat.getHost().setAutoDeploy(false); this.configureEngine(tomcat.getEngine()); Iterator var5 = this.additionalTomcatConnectors.iterator(); while(var5.hasNext()) { Connector additionalConnector = (Connector)var5.next(); tomcat.getService().addConnector(additionalConnector); } this.prepareContext(tomcat.getHost(), initializers); return this.getTomcatWebServer(tomcat); }
Tomcat 在什么时候被创建的?
SpringApplication.run(SpringbootProviderApplication.class, args)->refreshContext()->onRefresh->
ServletWebServerApplicationContext->createWebServer()->
protected void onRefresh() { super.onRefresh(); try { this.createWebServer(); } catch (Throwable var2) { throw new ApplicationContextException("Unable to start web server", var2); } }
private void createWebServer() { WebServer webServer = this.webServer; ServletContext servletContext = this.getServletContext(); if (webServer == null && servletContext == null) {
//在加载的时候getWebServerFactory 已经加载了 ServletWebServerFactory factory = this.getWebServerFactory();
//创建webServer ServletWebServerFactory 有不同的实现类 如 tomcat ,jetty的 this.webServer = factory.getWebServer(new ServletContextInitializer[]{this.getSelfInitializer()}); } else if (servletContext != null) { try { this.getSelfInitializer().onStartup(servletContext); } catch (ServletException var4) { throw new ApplicationContextException("Cannot initialize servlet context", var4); } } this.initPropertySources(); }
SpringApplication.run()
Spring Context IOC 容器 (spring所有初始化的操作)
Listener
Filter
1.创建SpringApplication应用
public SpringApplication(ResourceLoader resourceLoader, Class... primarySources) { this.sources = new LinkedHashSet(); this.bannerMode = Mode.CONSOLE; this.logStartupInfo = true; this.addCommandLineProperties = true; this.addConversionService = true; this.headless = true; this.registerShutdownHook = true; this.additionalProfiles = new HashSet(); this.isCustomEnvironment = false; this.resourceLoader = resourceLoader; Assert.notNull(primarySources, "PrimarySources must not be null"); this.primarySources = new LinkedHashSet(Arrays.asList(primarySources));
//判断web应用类型 this.webApplicationType = WebApplicationType.deduceFromClasspath();
//初始化 this.setInitializers(this.getSpringFactoriesInstances(ApplicationContextInitializer.class));
//设计监听器 SpringFactories-> spring.factories文件 this.setListeners(this.getSpringFactoriesInstances(ApplicationListener.class)); this.mainApplicationClass = this.deduceMainApplicationClass(); }
# Application Listeners
org.springframework.context.ApplicationListener=\
org.springframework.boot.autoconfigure.BackgroundPreinitializer
# Auto Configuration Import Listeners
org.springframework.boot.autoconfigure.AutoConfigurationImportListener=\
org.springframework.boot.autoconfigure.condition.ConditionEvaluationReportAutoConfigurationImportListener
2.run (以下源码删除了部分代码)
public ConfigurableApplicationContext run(String... args) { ConfigurableApplicationContext context = null; Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList(); this.configureHeadlessProperty(); Collection exceptionReporters; try { ApplicationArguments applicationArguments = new DefaultApplicationArguments(args); ConfigurableEnvironment environment = this.prepareEnvironment(listeners, applicationArguments); this.configureIgnoreBeanInfo(environment);
//打印banner Banner printedBanner = this.printBanner(environment);
// spring上下文 context = this.createApplicationContext(); exceptionReporters = this.getSpringFactoriesInstances(SpringBootExceptionReporter.class, new Class[]{ConfigurableApplicationContext.class}, context); this.prepareContext(context, environment, listeners, applicationArguments, printedBanner);
// 在该方法中加载tomcat :AbstractApplicationContext->refresh——>onRefresh(创建web容器) this.refreshContext(context); this.afterRefresh(context, applicationArguments); if (this.logStartupInfo) { (new StartupInfoLogger(this.mainApplicationClass)).logStarted(this.getApplicationLog(), stopWatch); } listeners.started(context); this.callRunners(context, applicationArguments); } catch (Throwable var10) { this.handleRunFailure(context, var10, exceptionReporters, listeners); throw new IllegalStateException(var10); } try { listeners.running(context); return context; } catch (Throwable var9) { this.handleRunFailure(context, var9, exceptionReporters, (SpringApplicationRunListeners)null); throw new IllegalStateException(var9); } }
创建web容器即Tomcat
protected void onRefresh() { super.onRefresh(); try { this.createWebServer(); } catch (Throwable var2) { throw new ApplicationContextException("Unable to start web server", var2); } }