2. Spring Boot之Enable模块驱动

739 阅读10分钟

理解@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模块

  1. 首先定义一个被@Configuration 标注的类

    @Configuration
    public class HelloWorldConfiguration {
        @Bean
        public String helloWorld(){ // 创建名为"helloWorld" String 类型的Bean
            return "Hello,World";
        }
    }
    
  2. 定义@EnableHelloWorld注解

    /**
     * 模仿{@link EnableWebMvc} 编程模式 所写的Enable***注解
     * @author ajin
     */
    @Target({ElementType.TYPE})
    @Retention(RetentionPolicy.RUNTIME)
    @Documented
    @Import(HelloWorldConfiguration.class)
    public @interface EnableHelloWorld {
    
    }
    
  3. 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();
        }
    }
    
  4. 启动引导类

    控制台输出如下:

    helloWorld = Hello,World

基于“接口编程”实现@Enable模块

基于接口编程,需要实现ImportSelectorImportBeanDefinitionRegistrar接口

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);

}
自我实现
  1. 新建一个服务器接口Server

    /**
     * 服务器接口
     * @author ajin
     */
    public interface Server {
        /**
         * 启动服务器
         * */
        void start();
        /**
         * 关闭服务器
         * */
        void stop();
    
        /**
         * 服务器类型
         * */
        enum  Type{
            HTTP, // HTTP服务器
            FTP  // FTP 服务器
        }
    }
    
  2. 实现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服务器关闭中。。。");
        }
    }
    
  3. 定义@EnableServer

    /**
     * @author ajin
     */
    @Target({ElementType.TYPE})
    @Retention(RetentionPolicy.RUNTIME)
    @Documented
    @Import(ServerImportSelector.class) // 导入 ServerImportSelector
    public @interface EnableServer {
    
        /**
         * 设置服务器的类型
         * */
        Server.Type type();
    }
    
    
  4. 实现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;
        }
    }
    
  5. 定义引导类,并执行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自我实现部分的代码,作少部分修改。

  1. 修改注解EnableServer

    /**
     * @author ajin
     */
    @Target({ElementType.TYPE})
    @Retention(RetentionPolicy.RUNTIME)
    @Documented
    //@Import(ServerImportSelector.class) // 导入 ServerImportSelector
    @Import(ServerImportBeanDefinitionRegistrar.class)
    public @interface EnableServer {
    
        /**
         * 设置服务器的类型
         * */
        Server.Type type();
    }
    
  2. 新增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)
    
                    );
    
        }
    }
    
  3. 启动引导类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.registerAnnotationConfigProcessorsConfigurationClassPostProcessor注册为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都为ConfigurationClassMap

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