spring boot专题(四)——spring boot源码深入分析

系列文章目录

spring boot专题(一)——spring boot快速上手
spring boot专题(二)——spring boot整合第三方插件
spring boot专题(三)——自定义starter
spring boot专题(四)——spring boot源码深入分析


spring导入bean的几种方式

  • @Configuration+@Bean
  • 包扫描来加载bean, 比如标识@ComponentScan+@Controller @Service @Repository @Compent
  • @Import几种取值来注册bean
    • 实现ImportSelector接口的类
    • 实现ImportBeanDefinitionRegistrar接口來注冊bean
  • 实现factoryBean的方式来导入组件

通过@Import注解来导入ImportSelector组件

1.写一个配置类在配置类上标注一个@Import的注解

package com.yemuxia.config;

import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;

/**
 * @author 史凯强
 * @date 2021/09/29 13:40
 * @desc
 **/
@Configuration
@Import(value = {
    
    TestSelecter.class})
public class AppConfig {
    
    
}

2.在@Import注解的value值 写自己需要导入的组件
在selectImports方法中 就是你需要导入组件的全类名

package com.yemuxia.config;

import org.springframework.context.annotation.ImportSelector;
import org.springframework.core.type.AnnotationMetadata;

/**
 * @author 史凯强
 * @date 2021/09/29 13:42
 * @desc 自定义需要导入的组件
 **/
public class TestSelecter implements ImportSelector {
    
    
    @Override
    public String[] selectImports(AnnotationMetadata importingClassMetadata) {
    
    
        return new String[]{
    
    "com.yemuxia.service.TestServiceImpl"};
    }
}

package com.yemuxia.service;

/**
 * @author 史凯强
 * @date 2021/09/29 13:43
 * @desc
 **/
public class TestServiceImpl {
    
    

    public void testService() {
    
    
        System.out.println("我是通过importSelector导入进来的service");
    }
}

3.controller类

package com.yemuxia.controller;

import com.yemuxia.service.TestServiceImpl;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

/**
 * @author 史凯强
 * @date 2021/09/29 13:45
 * @desc
 **/
@RestController
public class TestController {
    
    

    //自动注入testServiceImpl
    @Autowired
    private TestServiceImpl testService;

    @RequestMapping("test")
    public String testTuling() {
    
    
        testService.testService();
        return "testOk";
    }
}

4.项目运行结果如下
在这里插入图片描述
在这里插入图片描述

通过@Import导入ImportBeanDefinitionRegistrar 从而导入组件

1.修改上面的案例,引入TestImportBeanDefinitionRegistrar

package com.yemuxia.config;

import com.yemuxia.dao.TestDao;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.beans.factory.support.RootBeanDefinition;
import org.springframework.context.annotation.ImportBeanDefinitionRegistrar;
import org.springframework.core.type.AnnotationMetadata;

/**
 * @author 史凯强
 * @date 2021/09/29 14:07
 * @desc
 **/
public class TestImportBeanDefinitionRegistrar implements ImportBeanDefinitionRegistrar {
    
    
    @Override
    public void registerBeanDefinitions(AnnotationMetadata annotationMetadata, BeanDefinitionRegistry beanDefinitionRegistry) {
    
    
        //定义一个BeanDefinition
        RootBeanDefinition rootBeanDefinition = new RootBeanDefinition(TestDao.class);
        //把自定义的bean定义导入到容器中
        beanDefinitionRegistry.registerBeanDefinition("testDao", rootBeanDefinition);
    }
}

2.添加TestDao

package com.yemuxia.dao;

/**
 * @author 史凯强
 * @date 2021/09/29 14:01
 * @desc
 **/
public class TestDao {
    
    
    public void testDao() {
    
    
        System.out.println("我是通过ImportBeanDefinitionRegistrar导入进来testDao组件");
    }
}

3.修改appconfig类

package com.yemuxia.config;

import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;

/**
 * @author 史凯强
 * @date 2021/09/29 13:40
 * @desc
 **/
@Configuration
@Import(value = {
    
    TestSelecter.class,TestImportBeanDefinitionRegistrar.class})
public class AppConfig {
    
    
}

4.service类调整一下逻辑

扫描二维码关注公众号,回复: 13402597 查看本文章
package com.yemuxia.service;

import com.yemuxia.dao.TestDao;
import org.springframework.beans.factory.annotation.Autowired;

/**
 * @author 史凯强
 * @date 2021/09/29 13:43
 * @desc
 **/
public class TestServiceImpl {
    
    
    @Autowired
    private TestDao testDao;

    public void testService() {
    
    
        testDao.testDao();
        System.out.println("我是通过importSelector导入进来的service");
    }
}

5.运行项目
在这里插入图片描述
在这里插入图片描述

spring boot自动装配原理

spring boot注解组合图

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

根据上面的@SpringBootApplication注解 我们来着重分析如下二个类
①:AutoConfigurationImportSelector.class
②:AutoConfigurationPackages.Registrar.class

AutoConfigurationImportSelector

@Override
public String[] selectImports(AnnotationMetadata annotationMetadata) {
    
    
	if (!isEnabled(annotationMetadata)) {
    
    
		return NO_IMPORTS;
	}
	AutoConfigurationMetadata autoConfigurationMetadata = AutoConfigurationMetadataLoader
			.loadMetadata(this.beanClassLoader);
	AnnotationAttributes attributes = getAttributes(annotationMetadata);
	//去META-INF/spring.factories文件中 查询 EnableAutoConfiguration等于值
	List<String> configurations = getCandidateConfigurations(annotationMetadata,
			attributes);
	//去除重复的配置类,若我们自己写的starter 可能存在重复的
	configurations = removeDuplicates(configurations);
	Set<String> exclusions = getExclusions(annotationMetadata, attributes);
	checkExcludedClasses(configurations, exclusions);
	configurations.removeAll(exclusions);
	//根据maven 导入的启动器过滤出 需要导入的配置类
	configurations = filter(configurations, autoConfigurationMetadata);
	fireAutoConfigurationImportEvents(configurations, exclusions);
	return StringUtils.toStringArray(configurations);
}
//去spring.factories 中去查询EnableAutoConfirution类
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()) {
    
    
					String factoryClassName = ((String) entry.getKey()).trim();
					for (String factoryName : StringUtils.commaDelimitedListToStringArray((String) entry.getValue())) {
    
    
						result.add(factoryClassName, factoryName.trim());
					}
				}
			}
			cache.put(classLoader, result);
			return result;
		}
		catch (IOException ex) {
    
    
			throw new IllegalArgumentException("Unable to load factories from location [" +
					FACTORIES_RESOURCE_LOCATION + "]", ex);
		}
	}

在这里插入图片描述

分析RedisAutoConfiguration类

@Configuration //标识是一个配置类
@ConditionalOnClass(RedisOperations.class)	//判断是否导入了 redis的jar包
@EnableConfigurationProperties(RedisProperties.class)//开启自动配置属性类加载到容器中
@Import({
    
     LettuceConnectionConfiguration.class, JedisConnectionConfiguration.class })
public class RedisAutoConfiguration {
    
    
	//自动配置了一个 redisTemlate 操作对象的
	@Bean
	@ConditionalOnMissingBean(name = "redisTemplate")
	public RedisTemplate<Object, Object> redisTemplate(
			RedisConnectionFactory redisConnectionFactory) throws UnknownHostException {
    
    
		RedisTemplate<Object, Object> template = new RedisTemplate<>();
		template.setConnectionFactory(redisConnectionFactory);
		return template;
	}
	//操作字符串的
	@Bean
	@ConditionalOnMissingBean
	public StringRedisTemplate stringRedisTemplate(
			RedisConnectionFactory redisConnectionFactory) throws UnknownHostException {
    
    
		StringRedisTemplate template = new StringRedisTemplate();
		template.setConnectionFactory(redisConnectionFactory);
		return template;
	}

}

配置jedis 连接工厂配置类

@Configuration
@ConditionalOnClass({
    
     GenericObjectPool.class, JedisConnection.class, Jedis.class })
class JedisConnectionConfiguration extends RedisConnectionConfiguration {
    
    

	private final RedisProperties properties;

	private final List<JedisClientConfigurationBuilderCustomizer> builderCustomizers;

	JedisConnectionConfiguration(RedisProperties properties,
			ObjectProvider<RedisSentinelConfiguration> sentinelConfiguration,
			ObjectProvider<RedisClusterConfiguration> clusterConfiguration,
			ObjectProvider<List<JedisClientConfigurationBuilderCustomizer>> builderCustomizers) {
    
    
		super(properties, sentinelConfiguration, clusterConfiguration);
		this.properties = properties;
		this.builderCustomizers = builderCustomizers
				.getIfAvailable(Collections::emptyList);
	}

	@Bean
	@ConditionalOnMissingBean(RedisConnectionFactory.class)
	public JedisConnectionFactory redisConnectionFactory() throws UnknownHostException {
    
    
		return createJedisConnectionFactory();
	}

	private JedisConnectionFactory createJedisConnectionFactory() {
    
    
		JedisClientConfiguration clientConfiguration = getJedisClientConfiguration();
		if (getSentinelConfig() != null) {
    
    
			return new JedisConnectionFactory(getSentinelConfig(), clientConfiguration);
		}
		if (getClusterConfiguration() != null) {
    
    
			return new JedisConnectionFactory(getClusterConfiguration(),
					clientConfiguration);
		}
		return new JedisConnectionFactory(getStandaloneConfig(), clientConfiguration);
	}

	private JedisClientConfiguration getJedisClientConfiguration() {
    
    
		JedisClientConfigurationBuilder builder = applyProperties(
				JedisClientConfiguration.builder());
		RedisProperties.Pool pool = this.properties.getJedis().getPool();
		if (pool != null) {
    
    
			applyPooling(pool, builder);
		}
		if (StringUtils.hasText(this.properties.getUrl())) {
    
    
			customizeConfigurationFromUrl(builder);
		}
		customize(builder);
		return builder.build();
	}

	private JedisClientConfigurationBuilder applyProperties(
			JedisClientConfigurationBuilder builder) {
    
    
		if (this.properties.isSsl()) {
    
    
			builder.useSsl();
		}
		if (this.properties.getTimeout() != null) {
    
    
			Duration timeout = this.properties.getTimeout();
			builder.readTimeout(timeout).connectTimeout(timeout);
		}
		return builder;
	}

	private void applyPooling(RedisProperties.Pool pool,
			JedisClientConfiguration.JedisClientConfigurationBuilder builder) {
    
    
		builder.usePooling().poolConfig(jedisPoolConfig(pool));
	}

	private JedisPoolConfig jedisPoolConfig(RedisProperties.Pool pool) {
    
    
		JedisPoolConfig config = new JedisPoolConfig();
		config.setMaxTotal(pool.getMaxActive());
		config.setMaxIdle(pool.getMaxIdle());
		config.setMinIdle(pool.getMinIdle());
		if (pool.getMaxWait() != null) {
    
    
			config.setMaxWaitMillis(pool.getMaxWait().toMillis());
		}
		return config;
	}

	private void customizeConfigurationFromUrl(
			JedisClientConfiguration.JedisClientConfigurationBuilder builder) {
    
    
		ConnectionInfo connectionInfo = parseUrl(this.properties.getUrl());
		if (connectionInfo.isUseSsl()) {
    
    
			builder.useSsl();
		}
	}

	private void customize(
			JedisClientConfiguration.JedisClientConfigurationBuilder builder) {
    
    
		for (JedisClientConfigurationBuilderCustomizer customizer : this.builderCustomizers) {
    
    
			customizer.customize(builder);
		}
	}

}

org.springframework.boot.autoconfigure.data.redis.RedisProperties
我们yml中能配置什么类,在这个类中都会有一个属性一一对应

@ConfigurationProperties(prefix = "spring.redis")
public class RedisProperties {
    
    

	/**
	 * Database index used by the connection factory.
	 */
	private int database = 0;

	/**
	 * Connection URL. Overrides host, port, and password. User is ignored. Example:
	 * redis://user:[email protected]:6379
	 */
	private String url;

	/**
	 * Redis server host.
	 */
	private String host = "localhost";

	/**
	 * Login password of the redis server.
	 */
	private String password;

	/**
	 * Redis server port.
	 */
	private int port = 6379;

	/**
	 * Whether to enable SSL support.
	 */
	private boolean ssl;

	/**
	 * Connection timeout.
	 */
	private Duration timeout;

	private Sentinel sentinel;

	private Cluster cluster;

	private final Jedis jedis = new Jedis();

	private final Lettuce lettuce = new Lettuce();

在这里插入图片描述

小结

AutoConfigurationImportSelector为我们容器中注册了那些组件,然后根据maven依赖导入的jar包,根 据条件装配来指定哪些组件 起作用 哪些组件不起作用。

spring boot的jar包的启动原理

ioc容器带动了内嵌tomcat的启动

spring boot的jar包的启动流程

根据自动装配原理来分析spring boot中jar包的启动流程,分析一下spring boot 怎么来自动装配tomcat 相关的组件

tomcat自动配置类以及定制器介绍

在这里插入图片描述

tomcat 作为内嵌容器来分析

@Configuration
@ConditionalOnWebApplication
@EnableConfigurationProperties(ServerProperties.class)
public class EmbeddedWebServerFactoryCustomizerAutoConfiguration {
    
    
	//配置tomcat的
	/**
	 * Nested configuration if Tomcat is being used.
	 */
	@Configuration
	@ConditionalOnClass({
    
     Tomcat.class, UpgradeProtocol.class })
	public static class TomcatWebServerFactoryCustomizerConfiguration {
    
    

		@Bean
		public TomcatWebServerFactoryCustomizer tomcatWebServerFactoryCustomizer(
				Environment environment, ServerProperties serverProperties) {
    
    
			return new TomcatWebServerFactoryCustomizer(environment, serverProperties);
		}

	}
	//配置jetty
	/**
	 * Nested configuration if Jetty is being used.
	 */
	@Configuration
	@ConditionalOnClass({
    
     Server.class, Loader.class, WebAppContext.class })
	public static class JettyWebServerFactoryCustomizerConfiguration {
    
    

		@Bean
		public JettyWebServerFactoryCustomizer jettyWebServerFactoryCustomizer(
				Environment environment, ServerProperties serverProperties) {
    
    
			return new JettyWebServerFactoryCustomizer(environment, serverProperties);
		}

	}
	//配置undertow的
	/**
	 * Nested configuration if Undertow is being used.
	 */
	@Configuration
	@ConditionalOnClass({
    
     Undertow.class, SslClientAuthMode.class })
	public static class UndertowWebServerFactoryCustomizerConfiguration {
    
    

		@Bean
		public UndertowWebServerFactoryCustomizer undertowWebServerFactoryCustomizer(
				Environment environment, ServerProperties serverProperties) {
    
    
			return new UndertowWebServerFactoryCustomizer(environment, serverProperties);
		}

	}

}

tomat 工厂定制器TomcatWebServerFactoryCustomizer

用来修改设置容器的内容的
(把serverProperties的属性设置到tomcat的创建工厂中)

public class TomcatWebServerFactoryCustomizer implements
		WebServerFactoryCustomizer<ConfigurableTomcatWebServerFactory>, Ordered {
    
    

	private final Environment environment;

	private final ServerProperties serverProperties;

	public TomcatWebServerFactoryCustomizer(Environment environment,
			ServerProperties serverProperties) {
    
    
		this.environment = environment;
		this.serverProperties = serverProperties;
	}

	@Override
	public int getOrder() {
    
    
		return 0;
	}

	@Override
	public void customize(ConfigurableTomcatWebServerFactory factory) {
    
    
		ServerProperties properties = this.serverProperties;
		ServerProperties.Tomcat tomcatProperties = properties.getTomcat();
		PropertyMapper propertyMapper = PropertyMapper.get();
		propertyMapper.from(tomcatProperties::getBasedir).whenNonNull()
				.to(factory::setBaseDirectory);
		propertyMapper.from(tomcatProperties::getBackgroundProcessorDelay).whenNonNull()
				.as(Duration::getSeconds).as(Long::intValue)
				.to(factory::setBackgroundProcessorDelay);
		customizeRemoteIpValve(factory);
		propertyMapper.from(tomcatProperties::getMaxThreads).when(this::isPositive)
				.to((maxThreads) -> customizeMaxThreads(factory,
						tomcatProperties.getMaxThreads()));
		propertyMapper.from(tomcatProperties::getMinSpareThreads).when(this::isPositive)
				.to((minSpareThreads) -> customizeMinThreads(factory, minSpareThreads));
		propertyMapper.from(() -> determineMaxHttpHeaderSize()).when(this::isPositive)
				.to((maxHttpHeaderSize) -> customizeMaxHttpHeaderSize(factory,
						maxHttpHeaderSize));
		propertyMapper.from(tomcatProperties::getMaxHttpPostSize)
				.when((maxHttpPostSize) -> maxHttpPostSize != 0)
				.to((maxHttpPostSize) -> customizeMaxHttpPostSize(factory,
						maxHttpPostSize));
		propertyMapper.from(tomcatProperties::getAccesslog)
				.when(ServerProperties.Tomcat.Accesslog::isEnabled)
				.to((enabled) -> customizeAccessLog(factory));
		propertyMapper.from(tomcatProperties::getUriEncoding).whenNonNull()
				.to(factory::setUriEncoding);
		propertyMapper.from(properties::getConnectionTimeout).whenNonNull()
				.to((connectionTimeout) -> customizeConnectionTimeout(factory,
						connectionTimeout));
		propertyMapper.from(tomcatProperties::getMaxConnections).when(this::isPositive)
				.to((maxConnections) -> customizeMaxConnections(factory, maxConnections));
		propertyMapper.from(tomcatProperties::getAcceptCount).when(this::isPositive)
				.to((acceptCount) -> customizeAcceptCount(factory, acceptCount));
		customizeStaticResources(factory);
		customizeErrorReportValve(properties.getError(), factory);
	}
}

ServletWebServerFactoryAutoConfiguration Servletweb工厂自动配置类

@Configuration
@AutoConfigureOrder(Ordered.HIGHEST_PRECEDENCE)
@ConditionalOnClass(ServletRequest.class)
@ConditionalOnWebApplication(type = Type.SERVLET)
@EnableConfigurationProperties(ServerProperties.class)
@Import({
    
     ServletWebServerFactoryAutoConfiguration.BeanPostProcessorsRegistrar.class,
		ServletWebServerFactoryConfiguration.EmbeddedTomcat.class,
		ServletWebServerFactoryConfiguration.EmbeddedJetty.class,
		ServletWebServerFactoryConfiguration.EmbeddedUndertow.class })
public class ServletWebServerFactoryAutoConfiguration {
    
    

	@Bean
	public ServletWebServerFactoryCustomizer servletWebServerFactoryCustomizer(
			ServerProperties serverProperties) {
    
    
		return new ServletWebServerFactoryCustomizer(serverProperties);
	}

	@Bean
	@ConditionalOnClass(name = "org.apache.catalina.startup.Tomcat")
	public TomcatServletWebServerFactoryCustomizer tomcatServletWebServerFactoryCustomizer(
			ServerProperties serverProperties) {
    
    
		return new TomcatServletWebServerFactoryCustomizer(serverProperties);
	}

	/**
	 * Registers a {@link WebServerFactoryCustomizerBeanPostProcessor}. Registered via
	 * {@link ImportBeanDefinitionRegistrar} for early registration.
	 */
	public static class BeanPostProcessorsRegistrar
			implements ImportBeanDefinitionRegistrar, BeanFactoryAware {
    
    

		private ConfigurableListableBeanFactory beanFactory;

		@Override
		public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
    
    
			if (beanFactory instanceof ConfigurableListableBeanFactory) {
    
    
				this.beanFactory = (ConfigurableListableBeanFactory) beanFactory;
			}
		}

		@Override
		public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata,
				BeanDefinitionRegistry registry) {
    
    
			if (this.beanFactory == null) {
    
    
				return;
			}
			registerSyntheticBeanIfMissing(registry,
					"webServerFactoryCustomizerBeanPostProcessor",
					WebServerFactoryCustomizerBeanPostProcessor.class);
			registerSyntheticBeanIfMissing(registry,
					"errorPageRegistrarBeanPostProcessor",
					ErrorPageRegistrarBeanPostProcessor.class);
		}

		private void registerSyntheticBeanIfMissing(BeanDefinitionRegistry registry,
				String name, Class<?> beanClass) {
    
    
			if (ObjectUtils.isEmpty(
					this.beanFactory.getBeanNamesForType(beanClass, true, false))) {
    
    
				RootBeanDefinition beanDefinition = new RootBeanDefinition(beanClass);
				beanDefinition.setSynthetic(true);
				registry.registerBeanDefinition(name, beanDefinition);
			}
		}

	}

}

在这里插入图片描述

ServletWebServerFactoryCustomizer核心代码

在这里插入图片描述

TomcatServletWebServerFactoryCustomizer核心定制代码

public class TomcatServletWebServerFactoryCustomizer
		implements WebServerFactoryCustomizer<TomcatServletWebServerFactory>, Ordered {
    
    

	private final ServerProperties serverProperties;

	public TomcatServletWebServerFactoryCustomizer(ServerProperties serverProperties) {
    
    
		this.serverProperties = serverProperties;
	}

	@Override
	public int getOrder() {
    
    
		return 0;
	}

	@Override
	public void customize(TomcatServletWebServerFactory factory) {
    
    
		ServerProperties.Tomcat tomcatProperties = this.serverProperties.getTomcat();
		if (!ObjectUtils.isEmpty(tomcatProperties.getAdditionalTldSkipPatterns())) {
    
    
			factory.getTldSkipPatterns()
					.addAll(tomcatProperties.getAdditionalTldSkipPatterns());
		}
		if (tomcatProperties.getRedirectContextRoot() != null) {
    
    
			customizeRedirectContextRoot(factory,
					tomcatProperties.getRedirectContextRoot());
		}
		if (tomcatProperties.getUseRelativeRedirects() != null) {
    
    
			customizeUseRelativeRedirects(factory,
					tomcatProperties.getUseRelativeRedirects());
		}
	}

	private void customizeRedirectContextRoot(ConfigurableTomcatWebServerFactory factory,
			boolean redirectContextRoot) {
    
    
		factory.addContextCustomizers((context) -> context
				.setMapperContextRootRedirectEnabled(redirectContextRoot));
	}

	private void customizeUseRelativeRedirects(ConfigurableTomcatWebServerFactory factory,
			boolean useRelativeRedirects) {
    
    
		factory.addContextCustomizers(
				(context) -> context.setUseRelativeRedirects(useRelativeRedirects));
	}

}

ServletWebServerFactoryConfiguration 容器工厂配置类

@Configuration
class ServletWebServerFactoryConfiguration {
    
    

	@Configuration
	@ConditionalOnClass({
    
     Servlet.class, Tomcat.class, UpgradeProtocol.class })
	@ConditionalOnMissingBean(value = ServletWebServerFactory.class, search = SearchStrategy.CURRENT)
	public static class EmbeddedTomcat {
    
    
		//配置tomcat 容器工厂
		@Bean
		public TomcatServletWebServerFactory tomcatServletWebServerFactory() {
    
    
			return new TomcatServletWebServerFactory();
		}

	}
}

启动流程分析

在这里插入图片描述
在这里插入图片描述
1)传入主配置类,以及命令行参数
2)创建SpringApplication对象
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
3)运行SpringbootApplication的run方法

public ConfigurableApplicationContext run(String... args) {
    
    
		StopWatch stopWatch = new StopWatch();
		stopWatch.start();
		//创建一个 容器对象
		ConfigurableApplicationContext context = null;
		Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList<>();
		configureHeadlessProperty();
		//去meta-info/spring.factories中获取SpringApplicationRunListener 监听器(事件发布监听器)
		SpringApplicationRunListeners listeners = getRunListeners(args);
		//发布容器 starting事件(通过spring的事件多播器)
		listeners.starting();
		try {
    
    
			//封装命令行参数
			ApplicationArguments applicationArguments = new DefaultApplicationArguments(
					args);
			//准备容器环境 1:获取或者创建环境 2:把命令行参数设置到环境中 3:通过监听器发布环境准备事件
			ConfigurableEnvironment environment = prepareEnvironment(listeners,
					applicationArguments);
			configureIgnoreBeanInfo(environment);
			//打印springboot的图标
			Banner printedBanner = printBanner(environment);
			//创建容器 根据webApplicationType 来创建容器 通过反射创建
			context = createApplicationContext();
			//去meta-info类中 获取异常报告
			exceptionReporters = getSpringFactoriesInstances(
					SpringBootExceptionReporter.class,
					new Class[] {
    
     ConfigurableApplicationContext.class }, context);
			/*准备环境 
			1:把环境设置到容器中 
			2: 循环调用AppplicationInitnazlier 进行容器初始化工作 
			3:发布容器上下文准备完成事件 
			4:注册关于springboot特性的相关单例Bean 
			5:发布容器上下文加载完毕事件
			*/
			prepareContext(context, environment, listeners, applicationArguments,
					printedBanner);
			refreshContext(context);
			//运行 ApplicationRunner 和CommandLineRunner
			afterRefresh(context, applicationArguments);
			stopWatch.stop();
			if (this.logStartupInfo) {
    
    
				new StartupInfoLogger(this.mainApplicationClass)
						.logStarted(getApplicationLog(), stopWatch);
			}
			//发布容器启动事件
			listeners.started(context);
			//运行 ApplicationRunner 和CommandLineRunner
			callRunners(context, applicationArguments);
		}
		catch (Throwable ex) {
    
    
			//运行 ApplicationRunner 和CommandLineRunner
			handleRunFailure(context, ex, exceptionReporters, listeners);
			throw new IllegalStateException(ex);
		}

		try {
    
    
			//发布容器运行事件
			listeners.running(context);
		}
		catch (Throwable ex) {
    
    
			handleRunFailure(context, ex, exceptionReporters, null);
			throw new IllegalStateException(ex);
		}
		return context;
	}

4)org.springframework.boot.SpringApplication#refreshContext 5)org.springframework.context.support.AbstractApplicationContext#refresh 6)org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext#onRefresh 7)org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext#createWebServer

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
WebServerFactoryCustomizerBeanPostProcessor是何时注册到容器中的
在这里插入图片描述
8)org.springframework.boot.web.embedded.tomcat.TomcatServletWebServerFactory#getWebServer创建 tomcat 并且容器启动
在这里插入图片描述
在这里插入图片描述

private void initialize() throws WebServerException {
    
    
		TomcatWebServer.logger
				.info("Tomcat initialized with port(s): " + getPortsDescription(false));
		synchronized (this.monitor) {
    
    
			try {
    
    
				addInstanceIdToEngineName();

				Context context = findContext();
				context.addLifecycleListener((event) -> {
    
    
					if (context.equals(event.getSource())
							&& Lifecycle.START_EVENT.equals(event.getType())) {
    
    
						// Remove service connectors so that protocol binding doesn't
						// happen when the service is started.
						removeServiceConnectors();
					}
				});

				// Start the server to trigger initialization listeners
				this.tomcat.start();

				// We can re-throw failure exception directly in the main thread
				rethrowDeferredStartupExceptions();

				try {
    
    
					ContextBindings.bindClassLoader(context, context.getNamingToken(),
							getClass().getClassLoader());
				}
				catch (NamingException ex) {
    
    
					// Naming is not enabled. Continue
				}

				// Unlike Jetty, all Tomcat threads are daemon threads. We create a
				// blocking non-daemon to stop immediate shutdown
				startDaemonAwaitThread();
			}
			catch (Exception ex) {
    
    
				stopSilently();
				throw new WebServerException("Unable to start embedded Tomcat", ex);
			}
		}
	}

9)在IOC 容器中的 org.springframework.context.support.AbstractApplicationContext#refresh 的 onReFresh()带动tomcat启动
在这里插入图片描述

spring boot中内嵌tomcat启动流程图

在这里插入图片描述

AutoConfigurationImportSelector#selectImports 的方法是怎么触发的

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

public void processConfigBeanDefinitions(BeanDefinitionRegistry registry) {
    
    
		List<BeanDefinitionHolder> configCandidates = new ArrayList<>();
		String[] candidateNames = registry.getBeanDefinitionNames();

		for (String beanName : candidateNames) {
    
    
			BeanDefinition beanDef = registry.getBeanDefinition(beanName);
			if (ConfigurationClassUtils.isFullConfigurationClass(beanDef) ||
					ConfigurationClassUtils.isLiteConfigurationClass(beanDef)) {
    
    
				if (logger.isDebugEnabled()) {
    
    
					logger.debug("Bean definition has already been processed as a configuration class: " + beanDef);
				}
			}
			else if (ConfigurationClassUtils.checkConfigurationClassCandidate(beanDef, this.metadataReaderFactory)) {
    
    
				configCandidates.add(new BeanDefinitionHolder(beanDef, beanName));
			}
		}

		// Return immediately if no @Configuration classes were found
		if (configCandidates.isEmpty()) {
    
    
			return;
		}

		// Sort by previously determined @Order value, if applicable
		configCandidates.sort((bd1, bd2) -> {
    
    
			int i1 = ConfigurationClassUtils.getOrder(bd1.getBeanDefinition());
			int i2 = ConfigurationClassUtils.getOrder(bd2.getBeanDefinition());
			return Integer.compare(i1, i2);
		});

		// Detect any custom bean name generation strategy supplied through the enclosing application context
		SingletonBeanRegistry sbr = null;
		if (registry instanceof SingletonBeanRegistry) {
    
    
			sbr = (SingletonBeanRegistry) registry;
			if (!this.localBeanNameGeneratorSet) {
    
    
				BeanNameGenerator generator = (BeanNameGenerator) sbr.getSingleton(CONFIGURATION_BEAN_NAME_GENERATOR);
				if (generator != null) {
    
    
					this.componentScanBeanNameGenerator = generator;
					this.importBeanNameGenerator = generator;
				}
			}
		}

		if (this.environment == null) {
    
    
			this.environment = new StandardEnvironment();
		}

		// Parse each @Configuration class
		ConfigurationClassParser parser = new ConfigurationClassParser(
				this.metadataReaderFactory, this.problemReporter, this.environment,
				this.resourceLoader, this.componentScanBeanNameGenerator, registry);

		Set<BeanDefinitionHolder> candidates = new LinkedHashSet<>(configCandidates);
		Set<ConfigurationClass> alreadyParsed = new HashSet<>(configCandidates.size());
		do {
    
    
			parser.parse(candidates);
			parser.validate();

			Set<ConfigurationClass> configClasses = new LinkedHashSet<>(parser.getConfigurationClasses());
			configClasses.removeAll(alreadyParsed);

			// Read the model and create bean definitions based on its content
			if (this.reader == null) {
    
    
				this.reader = new ConfigurationClassBeanDefinitionReader(
						registry, this.sourceExtractor, this.resourceLoader, this.environment,
						this.importBeanNameGenerator, parser.getImportRegistry());
			}
			this.reader.loadBeanDefinitions(configClasses);
			alreadyParsed.addAll(configClasses);

			candidates.clear();
			if (registry.getBeanDefinitionCount() > candidateNames.length) {
    
    
				String[] newCandidateNames = registry.getBeanDefinitionNames();
				Set<String> oldCandidateNames = new HashSet<>(Arrays.asList(candidateNames));
				Set<String> alreadyParsedClasses = new HashSet<>();
				for (ConfigurationClass configurationClass : alreadyParsed) {
    
    
					alreadyParsedClasses.add(configurationClass.getMetadata().getClassName());
				}
				for (String candidateName : newCandidateNames) {
    
    
					if (!oldCandidateNames.contains(candidateName)) {
    
    
						BeanDefinition bd = registry.getBeanDefinition(candidateName);
						if (ConfigurationClassUtils.checkConfigurationClassCandidate(bd, this.metadataReaderFactory) &&
								!alreadyParsedClasses.contains(bd.getBeanClassName())) {
    
    
							candidates.add(new BeanDefinitionHolder(bd, candidateName));
						}
					}
				}
				candidateNames = newCandidateNames;
			}
		}
		while (!candidates.isEmpty());

		// Register the ImportRegistry as a bean in order to support ImportAware @Configuration classes
		if (sbr != null && !sbr.containsSingleton(IMPORT_REGISTRY_BEAN_NAME)) {
    
    
			sbr.registerSingleton(IMPORT_REGISTRY_BEAN_NAME, parser.getImportRegistry());
		}

		if (this.metadataReaderFactory instanceof CachingMetadataReaderFactory) {
    
    
			// Clear cache in externally provided MetadataReaderFactory; this is a no-op
			// for a shared cache since it'll be cleared by the ApplicationContext.
			((CachingMetadataReaderFactory) this.metadataReaderFactory).clearCache();
		}
	}

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

@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
	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());
				}
			}
		}
	}

	// Process any @Import annotations
	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;
}

在这里插入图片描述
在这里插入图片描述

spring boot启动war包的原理

外置的tomcat带动了我们的ioc容器启动

猜你喜欢

转载自blog.csdn.net/yemuxiaweiliang/article/details/120531810