
1.写一个配置类在配置类上标注一个@import的注解
package com.yemuxia.config;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.import;
@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;
public class TestSelecter implements importSelector {
@Override
public String[] selectimports(Annotationmetadata importingClassmetadata) {
return new String[]{"com.yemuxia.service.TestServiceImpl"};
}
}
package com.yemuxia.service;
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;
@RestController
public class TestController {
//自动注入testServiceImpl
@Autowired
private TestServiceImpl testService;
@RequestMapping("test")
public String testTuling() {
testService.testService();
return "testOk";
}
}
4.项目运行结果如下
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;
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;
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;
@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;
public class TestServiceImpl {
@Autowired
private TestDao testDao;
public void testService() {
testDao.testDao();
System.out.println("我是通过importSelector导入进来的service");
}
}
5.运行项目
根据上面的@SpringBootApplication注解 我们来着重分析如下二个类
①:AutoConfigurationimportSelector.class
②:AutoConfigurationPackages.Registrar.class
@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 configurations = getCandidateConfigurations(annotationmetadata,
attributes);
//去除重复的配置类,若我们自己写的starter 可能存在重复的
configurations = removeDuplicates(configurations);
Set 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分析RedisAutoConfiguration类> loadSpringFactories(@Nullable ClassLoader classLoader) { MultiValueMap result = cache.get(classLoader); if (result != null) { return result; } try { Enumeration 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); } }
@Configuration //标识是一个配置类
@ConditionalOnClass(RedisOperations.class) //判断是否导入了 redis的jar包
@EnableConfigurationProperties(RedisProperties.class)//开启自动配置属性类加载到容器中
@import({ LettuceConnectionConfiguration.class, JedisConnectionConfiguration.class })
public class RedisAutoConfiguration {
//自动配置了一个 redisTemlate 操作对象的
@Bean
@ConditionalOnMissingBean(name = "redisTemplate")
public RedisTemplate
配置jedis 连接工厂配置类
@Configuration
@ConditionalOnClass({ GenericObjectPool.class, JedisConnection.class, Jedis.class })
class JedisConnectionConfiguration extends RedisConnectionConfiguration {
private final RedisProperties properties;
private final List builderCustomizers;
JedisConnectionConfiguration(RedisProperties properties,
ObjectProvider sentinelConfiguration,
ObjectProvider clusterConfiguration,
ObjectProvider> 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 {
private int database = 0;
private String url;
private String host = "localhost";
private String password;
private int port = 6379;
private boolean ssl;
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包的启动流程根据自动装配原理来分析spring boot中jar包的启动流程,分析一下spring boot 怎么来自动装配tomcat 相关的组件
tomcat自动配置类以及定制器介绍 tomcat 作为内嵌容器来分析@Configuration
@ConditionalOnWebApplication
@EnableConfigurationProperties(ServerProperties.class)
public class EmbeddedWebServerFactoryCustomizerAutoConfiguration {
//配置tomcat的
@Configuration
@ConditionalOnClass({ Tomcat.class, UpgradeProtocol.class })
public static class TomcatWebServerFactoryCustomizerConfiguration {
@Bean
public TomcatWebServerFactoryCustomizer tomcatWebServerFactoryCustomizer(
Environment environment, ServerProperties serverProperties) {
return new TomcatWebServerFactoryCustomizer(environment, serverProperties);
}
}
//配置jetty
@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的
@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 WebServerFactoryCustomizerServletWebServerFactoryAutoConfiguration Servletweb工厂自动配置类, 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); } }
@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);
}
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 WebServerFactoryCustomizerServletWebServerFactoryConfiguration 容器工厂配置类, 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)); } }
@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 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);
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启动
public void processConfigBeanDefinitions(BeanDefinitionRegistry registry) {
List 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 candidates = new linkedHashSet<>(configCandidates);
Set alreadyParsed = new HashSet<>(configCandidates.size());
do {
parser.parse(candidates);
parser.validate();
Set 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 oldCandidateNames = new HashSet<>(Arrays.asList(candidateNames));
Set 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 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 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 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;
}