系列文章目录
spring boot专题(一)——spring boot快速上手
spring boot专题(二)——spring boot整合第三方插件
spring boot专题(三)——自定义starter
spring boot专题(四)——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类调整一下逻辑
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:password@example.com: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容器启动