理解@Enable 模块驱动
自定义@Enable模块驱动
@EnableWebMvc
基本思考
我们都知道@EnableWebMvc
这个注解
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@Documented
@Import(DelegatingWebMvcConfiguration.class)
public @interface EnableWebMvc {
}
该注解我们关注与@Import
元标注的内容,它会将DelegatingWebMvcConfiguration
注册称为Spring中的Bean。(DelegatingWebMvcConfiguration
必须添加@Configuration
注解)
@Configuration(proxyBeanMethods = false)
public class DelegatingWebMvcConfiguration extends WebMvcConfigurationSupport {
private final WebMvcConfigurerComposite configurers = new WebMvcConfigurerComposite();
@Autowired(required = false)
public void setConfigurers(List<WebMvcConfigurer> configurers) {
if (!CollectionUtils.isEmpty(configurers)) {
this.configurers.addWebMvcConfigurers(configurers);
}
}
基于“注解驱动”实现@Enable模块
-
首先定义一个被
@Configuration
标注的类@Configuration public class HelloWorldConfiguration { @Bean public String helloWorld(){ // 创建名为"helloWorld" String 类型的Bean return "Hello,World"; } }
-
定义
@EnableHelloWorld
注解/** * 模仿{@link EnableWebMvc} 编程模式 所写的Enable***注解 * @author ajin */ @Target({ElementType.TYPE}) @Retention(RetentionPolicy.RUNTIME) @Documented @Import(HelloWorldConfiguration.class) public @interface EnableHelloWorld { }
-
Spring Boot引导类上添加
@EnableHelloWorld
注解@EnableHelloWorld @Configuration public class AnnotationdesignApplication { public static void main(String[] args) { // 构建 Annotation 配置驱动 Spring上下文 AnnotationConfigApplicationContext context=new AnnotationConfigApplicationContext(); // 注册当前引导类(被@Configuration标注)到Spring上下文 context.register(AnnotationdesignApplication.class); // 启动上下文 context.refresh(); // 获取名称为 "helloWorld " 的Bean对象 String helloWorld = context.getBean("helloWorld",String.class); // 输出用户名称 "HelloWorld" System.out.printf("helloWorld = %s \n" ,helloWorld); // 关闭上下文 context.close(); } }
-
启动引导类
控制台输出如下:
helloWorld = Hello,World
基于“接口编程”实现@Enable
模块
基于接口编程,需要实现
ImportSelector
或ImportBeanDefinitionRegistrar
接口
ImportSelector
编程模式
@EnableCaching
理解
首先我们看看@EnableCaching
的实现。
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Import(CachingConfigurationSelector.class)
public @interface EnableCaching {
...
}
CachingConfigurationSelector
public class CachingConfigurationSelector extends AdviceModeImportSelector<EnableCaching> {
...
@Override
public String[] selectImports(AdviceMode adviceMode) {
switch (adviceMode) {
case PROXY:
return getProxyImports();
case ASPECTJ:
return getAspectJImports();
default:
return null;
}
}
}
@EnableCaching
的注解编程模式是实现ImportSelector
接口,并重写其selectImports(AnnotationMetadata…)
方法,而CachingConfigurationSelector
没有直接实现ImportSelector
接口,而是通过AdviceModeImportSelector
实现。
A泛型表示的是一个Java注解,因为任何Java注解都继承了
Annotation
接口
public abstract class AdviceModeImportSelector<A extends Annotation> implements ImportSelector{
...
@Override
public final String[] selectImports(AnnotationMetadata importingClassMetadata) {
Class<?> annType = GenericTypeResolver.resolveTypeArgument(getClass(), AdviceModeImportSelector.class);
Assert.state(annType != null, "Unresolvable type argument for AdviceModeImportSelector");
AnnotationAttributes attributes = AnnotationConfigUtils.attributesFor(importingClassMetadata, annType);
if (attributes == null) {
throw new IllegalArgumentException(String.format(
"@%s is not present on importing class '%s' as expected",
annType.getSimpleName(), importingClassMetadata.getClassName()));
}
AdviceMode adviceMode = attributes.getEnum(getAdviceModeAttributeName());
String[] imports = selectImports(adviceMode);
if (imports == null) {
throw new IllegalArgumentException("Unknown AdviceMode: " + adviceMode);
}
return imports;
}
@Nullable
protected abstract String[] selectImports(AdviceMode adviceMode);
}
自我实现
-
新建一个服务器接口
Server
/** * 服务器接口 * @author ajin */ public interface Server { /** * 启动服务器 * */ void start(); /** * 关闭服务器 * */ void stop(); /** * 服务器类型 * */ enum Type{ HTTP, // HTTP服务器 FTP // FTP 服务器 } }
-
实现Server接口
/** * @author ajin */ @Component // 根据ImportSelector的契约,确保实现为Spring Bean public class HttpServer implements Server { @Override public void start() { System.out.println("HTTP服务器启动中。。。"); } @Override public void stop() { System.out.println("HTTP服务器关闭中。。。"); } }
** * @author ajin */ @Component public class FtpServer implements Server { @Override public void start() { System.out.println("FTP服务器启动中。。。"); } @Override public void stop() { System.out.println("FTP服务器关闭中。。。"); } }
-
定义
@EnableServer
/** * @author ajin */ @Target({ElementType.TYPE}) @Retention(RetentionPolicy.RUNTIME) @Documented @Import(ServerImportSelector.class) // 导入 ServerImportSelector public @interface EnableServer { /** * 设置服务器的类型 * */ Server.Type type(); }
-
实现
ServerImportSelector
类/** * @author ajin * @see EnableServer */ public class ServerImportSelector implements ImportSelector { @Override public String[] selectImports(AnnotationMetadata importingClassMetadata) { // 读取EnableServer中的所有属性方法,本实例中只有type()属性方法 // key : 属性方法的名称 value : 属性方法的返回对象 Map<String, Object> annotationAttributes = importingClassMetadata.getAnnotationAttributes(EnableServer.class.getName()); // 获取名为type 的属性方法 ,并且强制转化为 Server.Type类型 Server.Type type = (Server.Type) annotationAttributes.get("type"); // 导入的类名称数组 String[] importClassNames = new String[0]; switch (type) { case FTP: importClassNames = new String[]{FtpServer.class.getName()}; break; case HTTP: importClassNames = new String[]{HttpServer.class.getName()}; break; } return importClassNames; } }
-
定义引导类,并执行main方法
@Configuration @EnableServer(type = Server.Type.HTTP) // 设置Http服务器 public class EnableServerBootstrap { public static void main(String[] args) { // 构建Annotation 配置驱动的Spring上下文 AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(); //注册当前引导类 context.register(EnableServerBootstrap.class); // 启动上下文 context.refresh(); // 获取Server Bean 对象 ,此处应为HttpServer Server server= context.getBean(Server.class); // 启动服务器 server.start(); // 停止服务器 server.stop(); context.close(); } }
控制台输出结果为:
HTTP服务器启动中。。。 HTTP服务器关闭中。。。
我们实质上通过给@EnableServer
注解添加type属性,然后ServerImportSelector
类获取我们type属性对应的值,并根据这个值选择注册哪个Bean(HttpServer
/FtpServer
)到Spring中。
ImportBeanDefinitionRegistrar
编程模式
自我实现
我们基于ImportSelector
自我实现部分的代码,作少部分修改。
-
修改注解
EnableServer
/** * @author ajin */ @Target({ElementType.TYPE}) @Retention(RetentionPolicy.RUNTIME) @Documented //@Import(ServerImportSelector.class) // 导入 ServerImportSelector @Import(ServerImportBeanDefinitionRegistrar.class) public @interface EnableServer { /** * 设置服务器的类型 * */ Server.Type type(); }
-
新增
ServerImportBeanDefinitionRegistrar
类public class ServerImportBeanDefinitionRegistrar implements ImportBeanDefinitionRegistrar { @Override public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) { // 复用{@link ServerImportSelector} 实现 ,减少重复编码 ImportSelector importSelector = new ServerImportSelector(); // 筛选Class名称集合 String[] selectClassNames = importSelector.selectImports(importingClassMetadata); // 创建Bean定义 Stream.of(selectClassNames) // 转化为 BeanDefinitionBuilder对象 .map(BeanDefinitionBuilder::genericBeanDefinition) // 转化为 BeanDefinition .map(BeanDefinitionBuilder::getBeanDefinition) .forEach(beanDefinition -> // 注册BeanDefinition 到 BeanDefinitionRegistry BeanDefinitionReaderUtils.registerWithGeneratedName(beanDefinition,registry) ); } }
-
启动引导类
EnableServerBootstrap
控制台输出如下:
HTTP服务器启动中。。。 HTTP服务器关闭中。。。
ImportBeanDefinitionRegistrar
不仅 需要开发人员选择注册哪些Bean ,还要将Bean手动注册到Spring上下文中。
@Enable
模块驱动原理
理解@Configuration
类的装载
我们需要详细了解下
@Configuration
注解的Java类是怎么作为Bean被注册到Spring上下文中的。
ComponentScanBeanDefinitionParser
其实我通过之前对Spring Boot注解的抽象原理理解可知,@Configuration
有一个@Component
元标注,那么我们通过@ComponetScan
或者在xml文件中配置<context:component-scan>
这两种方式可以让@Configuration
类作为Bean注册到Spring中。
我们先理解<context:component-scan>
的解析类ComponentScanBeanDefinitionParser
。在理解之前,你不问一下自己为什么是这个类来处理吗?
我们打开IDEA , 寻找我这个Spring Boot甚至是Spring应用必须依赖的jar包 -> spring-context-xxx.jar,找到META-INF\spring.handles文件,如下所示:
http\://www.springframework.org/schema/context=org.springframework.context.config.ContextNamespaceHandler
http\://www.springframework.org/schema/jee=org.springframework.ejb.config.JeeNamespaceHandler
http\://www.springframework.org/schema/lang=org.springframework.scripting.config.LangNamespaceHandler
http\://www.springframework.org/schema/task=org.springframework.scheduling.config.TaskNamespaceHandler
http\://www.springframework.org/schema/cache=org.springframework.cache.config.CacheNamespaceHandler
这里定义了 标签的处理类
ContextNamespaceHandler
public class ContextNamespaceHandler extends NamespaceHandlerSupport {
@Override
public void init() {
registerBeanDefinitionParser("property-placeholder", new PropertyPlaceholderBeanDefinitionParser());
registerBeanDefinitionParser("property-override", new PropertyOverrideBeanDefinitionParser());
registerBeanDefinitionParser("annotation-config", new AnnotationConfigBeanDefinitionParser());
// 看这里 ComponentScanBeanDefinitionParser
registerBeanDefinitionParser("component-scan", new ComponentScanBeanDefinitionParser());
registerBeanDefinitionParser("load-time-weaver", new LoadTimeWeaverBeanDefinitionParser());
registerBeanDefinitionParser("spring-configured", new SpringConfiguredBeanDefinitionParser());
registerBeanDefinitionParser("mbean-export", new MBeanExportBeanDefinitionParser());
registerBeanDefinitionParser("mbean-server", new MBeanServerBeanDefinitionParser());
}
}
ComponentScanBeanDefinitionParser#parse()
这个方法就是对
<context:component-scan>
标签的解析如果我是Spring框架的设计者(当然我是一个菜鸡),我首先得把标签里面的信息获取,然后再根据这些获取到的信息处理一些事情。
而我们这个标签,就是声明了一些包,告诉Spring框架帮忙扫一下这些包,将其中的
Component
类的Bean注册到Spring 上下文中。
我们验证一下刚刚拙劣的分析哈哈
@Override
@Nullable
public BeanDefinition parse(Element element, ParserContext parserContext) {
// 获取要扫描的包
String basePackage = element.getAttribute(BASE_PACKAGE_ATTRIBUTE);
basePackage = parserContext.getReaderContext().getEnvironment().resolvePlaceholders(basePackage);
String[] basePackages = StringUtils.tokenizeToStringArray(basePackage,
ConfigurableApplicationContext.CONFIG_LOCATION_DELIMITERS);
// Actually scan for bean definitions and register them.
ClassPathBeanDefinitionScanner scanner = configureScanner(parserContext, element);
// 获取这些包中的`Component`类,并生成BeanDefinitionHolder对象
Set<BeanDefinitionHolder> beanDefinitions = scanner.doScan(basePackages);
// 将 Bean注册进Spring上下文
registerComponents(parserContext.getReaderContext(), beanDefinitions, element);
return null;
}
registerComponents
重点看一下
protected void registerComponents(
XmlReaderContext readerContext, Set<BeanDefinitionHolder> beanDefinitions, Element element) {
Object source = readerContext.extractSource(element);
// CompositeComponentDefinition封装了很多的ComponentDefinition,
// 而ComponentDefinition又是由BeanDefinitionHolder封装而来
CompositeComponentDefinition compositeDef = new CompositeComponentDefinition(element.getTagName(), source);
// 这里就是我们ComponentScan相关的Bean
for (BeanDefinitionHolder封装而来 beanDefHolder : beanDefinitions) {
compositeDef.addNestedComponent(new BeanComponentDefinition(beanDefHolder));
}
// Register annotation config processors, if necessary.
boolean annotationConfig = true;
if (element.hasAttribute(ANNOTATION_CONFIG_ATTRIBUTE)) {
annotationConfig = Boolean.parseBoolean(element.getAttribute(ANNOTATION_CONFIG_ATTRIBUTE));
}
if (annotationConfig) {
// 特别注意
Set<BeanDefinitionHolder> processorDefinitions = AnnotationConfigUtils.registerAnnotationConfigProcessors(readerContext.getRegistry(), source);
for (BeanDefinitionHolder processorDefinition : processorDefinitions) {
compositeDef.addNestedComponent(new BeanComponentDefinition(processorDefinition));
}
}
readerContext.fireComponentRegistered(compositeDef);
}
AnnotationConfigUtils#registerAnnotationConfigProcessors
这是一个比较重要的方法,生成一系列的BeanDefinitionHolder
public static Set<BeanDefinitionHolder> registerAnnotationConfigProcessors(
BeanDefinitionRegistry registry, @Nullable Object source) {
DefaultListableBeanFactory beanFactory = unwrapDefaultListableBeanFactory(registry);
if (beanFactory != null) {
if (!(beanFactory.getDependencyComparator() instanceof AnnotationAwareOrderComparator)) {
beanFactory.setDependencyComparator(AnnotationAwareOrderComparator.INSTANCE);
}
if (!(beanFactory.getAutowireCandidateResolver() instanceof ContextAnnotationAutowireCandidateResolver)) {
beanFactory.setAutowireCandidateResolver(new ContextAnnotationAutowireCandidateResolver());
}
}
Set<BeanDefinitionHolder> beanDefs = new LinkedHashSet<>(8);
if (!registry.containsBeanDefinition(CONFIGURATION_ANNOTATION_PROCESSOR_BEAN_NAME)) {
RootBeanDefinition def = new RootBeanDefinition(ConfigurationClassPostProcessor.class);
def.setSource(source);
beanDefs.add(registerPostProcessor(registry, def, CONFIGURATION_ANNOTATION_PROCESSOR_BEAN_NAME));
}
if (!registry.containsBeanDefinition(AUTOWIRED_ANNOTATION_PROCESSOR_BEAN_NAME)) {
RootBeanDefinition def = new RootBeanDefinition(AutowiredAnnotationBeanPostProcessor.class);
def.setSource(source);
beanDefs.add(registerPostProcessor(registry, def, AUTOWIRED_ANNOTATION_PROCESSOR_BEAN_NAME));
}
// Check for JSR-250 support, and if present add the CommonAnnotationBeanPostProcessor.
if (jsr250Present && !registry.containsBeanDefinition(COMMON_ANNOTATION_PROCESSOR_BEAN_NAME)) {
RootBeanDefinition def = new RootBeanDefinition(CommonAnnotationBeanPostProcessor.class);
def.setSource(source);
beanDefs.add(registerPostProcessor(registry, def, COMMON_ANNOTATION_PROCESSOR_BEAN_NAME));
}
// Check for JPA support, and if present add the PersistenceAnnotationBeanPostProcessor.
if (jpaPresent && !registry.containsBeanDefinition(PERSISTENCE_ANNOTATION_PROCESSOR_BEAN_NAME)) {
RootBeanDefinition def = new RootBeanDefinition();
try {
def.setBeanClass(ClassUtils.forName(PERSISTENCE_ANNOTATION_PROCESSOR_CLASS_NAME,
AnnotationConfigUtils.class.getClassLoader()));
}
catch (ClassNotFoundException ex) {
throw new IllegalStateException(
"Cannot load optional framework class: " + PERSISTENCE_ANNOTATION_PROCESSOR_CLASS_NAME, ex);
}
def.setSource(source);
beanDefs.add(registerPostProcessor(registry, def, PERSISTENCE_ANNOTATION_PROCESSOR_BEAN_NAME));
}
if (!registry.containsBeanDefinition(EVENT_LISTENER_PROCESSOR_BEAN_NAME)) {
RootBeanDefinition def = new RootBeanDefinition(EventListenerMethodProcessor.class);
def.setSource(source);
beanDefs.add(registerPostProcessor(registry, def, EVENT_LISTENER_PROCESSOR_BEAN_NAME));
}
if (!registry.containsBeanDefinition(EVENT_LISTENER_FACTORY_BEAN_NAME)) {
RootBeanDefinition def = new RootBeanDefinition(DefaultEventListenerFactory.class);
def.setSource(source);
beanDefs.add(registerPostProcessor(registry, def, EVENT_LISTENER_FACTORY_BEAN_NAME));
}
return beanDefs;
}
AnnotationConfigBeanDefinitionParser
这个类是用来解析<context:annotation-config/>
标签的,这个标签是用来注册一些 BeanPostProcessor
,方便我们基于注解的编程。
ConfigurationClassPostProcessor
AutowiredAnnotationBeanPostProcessor
CommonAnnotationBeanPostProcessor
其parse()方法中最核心的还是AnnotationConfigUtils.registerAnnotationConfigProcessors
public class AnnotationConfigBeanDefinitionParser implements BeanDefinitionParser {
@Override
@Nullable
public BeanDefinition parse(Element element, ParserContext parserContext) {
Object source = parserContext.extractSource(element);
// Obtain bean definitions for all relevant BeanPostProcessors.
Set<BeanDefinitionHolder> processorDefinitions =
AnnotationConfigUtils.registerAnnotationConfigProcessors(parserContext.getRegistry(), source);
// Register component for the surrounding <context:annotation-config> element.
CompositeComponentDefinition compDefinition = new CompositeComponentDefinition(element.getTagName(), source);
parserContext.pushContainingComponent(compDefinition);
// Nest the concrete beans in the surrounding component.
for (BeanDefinitionHolder processorDefinition : processorDefinitions) {
parserContext.registerComponent(new BeanComponentDefinition(processorDefinition));
}
// Finally register the composite component.
parserContext.popAndRegisterContainingComponent();
return null;
}
}
我们看下AnnotationConfigUtils.registerAnnotationConfigProcessors
其核心的部分源码
public static Set<BeanDefinitionHolder> registerAnnotationConfigProcessors(
...
BeanDefinitionRegistry registry, @Nullable Object source){
Set<BeanDefinitionHolder> beanDefs = new LinkedHashSet<>(8);
if (!registry.containsBeanDefinition(CONFIGURATION_ANNOTATION_PROCESSOR_BEAN_NAME)) {
RootBeanDefinition def = new RootBeanDefinition(ConfigurationClassPostProcessor.class);
def.setSource(source);
beanDefs.add(registerPostProcessor(registry, def, CONFIGURATION_ANNOTATION_PROCESSOR_BEAN_NAME));
}
...
}
这里生成了一个ConfigurationClassPostProcessor
类型的RootBeanDefinition
,后面会被注册成为Spring中的Bean。
而刚刚
<context:component-scan/>
也是注册了ConfigurationClassPostProcessor
Bean,因为其解析的时候也是调用了AnnotationConfigUtils#registerAnnotationConfigProcessors()
方法。
其实聊到现在 就是xml配置的上下文场景(ClassPathXmlApplicationContext
),那么对于注解驱动的上下文呢?
AnnotationConfigApplicationContext
是如何注册ConfigurationClassPostProcessor
public class AnnotationConfigApplicationContext extends GenericApplicationContext implements AnnotationConfigRegistry {
private final AnnotatedBeanDefinitionReader reader;
...
}
AnnotatedBeanDefinitionReader
的构造函数如下:
public AnnotatedBeanDefinitionReader(BeanDefinitionRegistry registry, Environment environment) {
Assert.notNull(registry, "BeanDefinitionRegistry must not be null");
Assert.notNull(environment, "Environment must not be null");
this.registry = registry;
this.conditionEvaluator = new ConditionEvaluator(registry, environment, null);
// 这里 注册了ConfigurationClassPostProcessor
AnnotationConfigUtils.registerAnnotationConfigProcessors(this.registry);
}
简单总结
无论是xml配置驱动的上下文还是基于注解驱动的上下文,都会通过AnnotationConfigUtils.registerAnnotationConfigProcessors
将ConfigurationClassPostProcessor
注册为Spring Bean。
ConfigurationClassPostProcessor
我们理解下这个Bean作为BeanFactoryPostProcessor
什么时候回调以及回调的时候做了什么。
回调时机
AbtractApplicationContext#refresh()
方法中调用了invokeBeanFactoryPostProcessors
方法,该方法会回调BeanFactoryPostProcessor#postProcessBeanFactory
方法,具体如下:
protected void invokeBeanFactoryPostProcessors(ConfigurableListableBeanFactory beanFactory) {
// 这里核心方法
PostProcessorRegistrationDelegate.invokeBeanFactoryPostProcessors(beanFactory, getBeanFactoryPostProcessors());
// Detect a LoadTimeWeaver and prepare for weaving, if found in the meantime
// (e.g. through an @Bean method registered by ConfigurationClassPostProcessor)
if (beanFactory.getTempClassLoader() == null && beanFactory.containsBean(LOAD_TIME_WEAVER_BEAN_NAME)) {
beanFactory.addBeanPostProcessor(new LoadTimeWeaverAwareProcessor(beanFactory));
beanFactory.setTempClassLoader(new ContextTypeMatchClassLoader(beanFactory.getBeanClassLoader()));
}
}
-
PostProcessorRegistrationDelegate.invokeBeanFactoryPostProcessors(beanFactory, getBeanFactoryPostProcessors());
-
PostProcessorRegistrationDelegate#invokeBeanFactoryPostProcessors
private static void invokeBeanFactoryPostProcessors( Collection<? extends BeanFactoryPostProcessor> postProcessors, ConfigurableListableBeanFactory beanFactory) { for (BeanFactoryPostProcessor postProcessor : postProcessors) { postProcessor.postProcessBeanFactory(beanFactory); } }
-
回调逻辑
上面的分析,我们大概知道了回调时机,现在我们看下回调方法的逻辑
// ConfigurationClassPostProcessor
// 通过将配置类替换为CGLIB增强的子类,准备用于在运行时为Bean请求提供服务的Configuration类。
@Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) {
int factoryId = System.identityHashCode(beanFactory);
if (this.factoriesPostProcessed.contains(factoryId)) {
throw new IllegalStateException(
"postProcessBeanFactory already called on this post-processor against " + beanFactory);
}
this.factoriesPostProcessed.add(factoryId);
if (!this.registriesPostProcessed.contains(factoryId)) {
// BeanDefinitionRegistryPostProcessor hook apparently not supported...
// Simply call processConfigurationClasses lazily at this point then.
// 核心方法
processConfigBeanDefinitions((BeanDefinitionRegistry) beanFactory);
}
// 增强功能
enhanceConfigurationClasses(beanFactory);
beanFactory.addBeanPostProcessor(new ImportAwareBeanPostProcessor(beanFactory));
}
ConfigurationClassPostProcessor#processConfigBeanDefinitions
该方法的主要逻辑是解析
@Configuration
类中的@Bean
,保存一个key value都为ConfigurationClass
的Map
public void processConfigBeanDefinitions(BeanDefinitionRegistry registry) {
List<BeanDefinitionHolder> configCandidates = new ArrayList<>();
String[] candidateNames = registry.getBeanDefinitionNames();
for (String beanName : candidateNames) {
BeanDefinition beanDef = registry.getBeanDefinition(beanName);
if (beanDef.getAttribute(ConfigurationClassUtils.CONFIGURATION_CLASS_ATTRIBUTE) != null) {
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);
});(Bean)
// 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(
AnnotationConfigUtils.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 {
// 解析BeanDefinitionHolder(Bean)
parser.parse(candidates);
parser.validate();
// 获取解析@Configuration类产生的ConfigurationClass
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());
}
// 根据ConfigurationClass将Bean注册到Spring上下文中
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();
}
}
ConfigurationClassParser#parse()
方法。
public void parse(Set<BeanDefinitionHolder> configCandidates) {
for (BeanDefinitionHolder holder : configCandidates) {
BeanDefinition bd = holder.getBeanDefinition();
try {
if (bd instanceof AnnotatedBeanDefinition) {
parse(((AnnotatedBeanDefinition) bd).getMetadata(), holder.getBeanName());
}
else if (bd instanceof AbstractBeanDefinition && ((AbstractBeanDefinition) bd).hasBeanClass()) {
parse(((AbstractBeanDefinition) bd).getBeanClass(), holder.getBeanName());
}
else {
parse(bd.getBeanClassName(), holder.getBeanName());
}
}
catch (BeanDefinitionStoreException ex) {
throw ex;
}
catch (Throwable ex) {
throw new BeanDefinitionStoreException(
"Failed to parse configuration class [" + bd.getBeanClassName() + "]", ex);
}
}
this.deferredImportSelectorHandler.process();
}
parse()
会根据不同的情况调用两个重载的parse方法,一个是基于CGLib实现的AnnotationMetadataReadingVisitor
和 Java反射实现的StandardAnnotationMetadata
-
parse(((AnnotatedBeanDefinition) bd).getMetadata(), holder.getBeanName());
-
processConfigurationClass
protected void processConfigurationClass(ConfigurationClass configClass) throws IOException { if (this.conditionEvaluator.shouldSkip(configClass.getMetadata(), ConfigurationPhase.PARSE_CONFIGURATION)) { return; } ConfigurationClass existingClass = this.configurationClasses.get(configClass); if (existingClass != null) { if (configClass.isImported()) { if (existingClass.isImported()) { existingClass.mergeImportedBy(configClass); } // Otherwise ignore new imported config class; existing non-imported class overrides it. return; } else { // Explicit bean definition found, probably replacing an import. // Let's remove the old one and go with the new one. this.configurationClasses.remove(configClass); this.knownSuperclasses.values().removeIf(configClass::equals); } } // Recursively process the configuration class and its superclass hierarchy. SourceClass sourceClass = asSourceClass(configClass); do { sourceClass = doProcessConfigurationClass(configClass, sourceClass); } while (sourceClass != null); this.configurationClasses.put(configClass, configClass); }
-
doProcessConfigurationClass
@Nullable protected final SourceClass doProcessConfigurationClass(ConfigurationClass configClass, SourceClass sourceClass) throws IOException { if (configClass.getMetadata().isAnnotated(Component.class.getName())) { // 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.info("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()); } } } } // 1. Process any @Import annotations processImports(configClass, sourceClass, getImports(sourceClass), true); // 2. 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); } } // 3. 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; }
-
-
ConfigurationClassBeanDefinitionReader#loadBeanDefinitionsForConfigurationClass
/**
* Read a particular {@link ConfigurationClass}, registering bean definitions
* for the class itself and all of its {@link Bean} methods.
*/
private void loadBeanDefinitionsForConfigurationClass(
ConfigurationClass configClass, TrackedConditionEvaluator trackedConditionEvaluator) {
if (trackedConditionEvaluator.shouldSkip(configClass)) {
String beanName = configClass.getBeanName();
if (StringUtils.hasLength(beanName) && this.registry.containsBeanDefinition(beanName)) {
this.registry.removeBeanDefinition(beanName);
}
this.importRegistry.removeImportingClass(configClass.getMetadata().getClassName());
return;
}
if (configClass.isImported()) {
registerBeanDefinitionForImportedConfigurationClass(configClass);
}
for (BeanMethod beanMethod : configClass.getBeanMethods()) {
loadBeanDefinitionsForBeanMethod(beanMethod);
}
loadBeanDefinitionsFromImportedResources(configClass.getImportedResources());
loadBeanDefinitionsFromRegistrars(configClass.getImportBeanDefinitionRegistrars());
}
关于ConfigurationClassPostProcessor
的讨论还不够成熟,更详细内容请阅读《Spring Boot编程思想》的第8章--Spring注解驱动设计模式 相关部分
总结
@Enable
模块驱动就像是交给Spring框架的一个开关,我们首先很了解ConfigurationClassPostProcessor
的装配,即AnnotationConfigUtils#registerAnnotationConfigProcessors
所做的工作。
我们@Enable
的模式,一般是创建一个@Enable
注解,然后通过@Import
元标注将其中的类交给Spring去确定该去选择性的加载哪个Bean,本身上也是条件装配的一种特例;而@EnableWebmvc
则是特例,就是将Bean加载进Spring容器。
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@Documented
@Import(DelegatingWebMvcConfiguration.class)
public @interface EnableWebMvc {
}
@Configuration(proxyBeanMethods = false)
public class DelegatingWebMvcConfiguration extends WebMvcConfigurationSupport {
private final WebMvcConfigurerComposite configurers = new WebMvcConfigurerComposite();
@Autowired(required = false)
public void setConfigurers(List<WebMvcConfigurer> configurers) {
if (!CollectionUtils.isEmpty(configurers)) {
this.configurers.addWebMvcConfigurers(configurers);
}
}
...
}
我们的类通常是同时被@Configuration
和@Enable
标注,我们要想使得@Import
中的类被Spring处理,就需要借助ConfigurationClassPostProcessor
来处理,它主要做如下几件事
-
解析
BeanDefinitionHolder
(Spring上下文中保存的Bean),生成ConfigurationClass
-
Process any @Import annotations
-
Process individual @Bean methods
这个就是正常的
@Configuration
&&@Bean
模型
-
-
获取解析
@Configuration
类产生的ConfigurationClass
-
通过
ConfigurationClass
提供的信息,注册Bean