Spring阶段性学习:基础、配置解析、回调

1、BeanDefinition

是什么?

我们都知道Spring会将我们的类new出来以后存放到它自己的容器当中去,然后Spring还需要对我们的类进行其他很多功能的处理,那么Spring的流程是先将需要new的类的
信息都保存下来,然后统一的去new然后存放到容器当中.BeanDefinition就是存放类型下的.

概览

BeanDefinition是一个接口,其他有很多的实现类.我们先看看该接口的代码:

public interface BeanDefinition extends AttributeAccessor, BeanMetadataElement {

	/**
	 * 单例的字符串值:singleton
	 */
	String SCOPE_SINGLETON = ConfigurableBeanFactory.SCOPE_SINGLETON;

	/**
	 * 原型的字符串值:prototype
	 */
	String SCOPE_PROTOTYPE = ConfigurableBeanFactory.SCOPE_PROTOTYPE;


	int ROLE_APPLICATION = 0;

	int ROLE_SUPPORT = 1;

	int ROLE_INFRASTRUCTURE = 2;

	void setParentName(@Nullable String parentName);			// 设置父级BeanDefinition的name

	@Nullable
	String getParentName();		

	void setBeanClassName(@Nullable String beanClassName);		// 设置Class

	@Nullable
	String getBeanClassName();

	void setScope(@Nullable String scope);						// 设置Scope,如果存在的话

	@Nullable
	String getScope();

	void setLazyInit(boolean lazyInit);							// 设置是否懒加载

 	boolean isLazyInit();

	void setDependsOn(@Nullable String... dependsOn);			// 设置DependsOn

	@Nullable
	String[] getDependsOn();

	void setAutowireCandidate(boolean autowireCandidate);		// 设置调价注入

	boolean isAutowireCandidate();

	void setPrimary(boolean primary);							// 设置Primary

	boolean isPrimary();

	void setFactoryBeanName(@Nullable String factoryBeanName);

	@Nullable
	String getFactoryBeanName();

	void setFactoryMethodName(@Nullable String factoryMethodName);

	@Nullable
	String getFactoryMethodName();

	ConstructorArgumentValues getConstructorArgumentValues();

	default boolean hasConstructorArgumentValues() {
		return !getConstructorArgumentValues().isEmpty();
	}

	MutablePropertyValues getPropertyValues();

	default boolean hasPropertyValues() {
		return !getPropertyValues().isEmpty();
	}
	
    // 以下是属性
	boolean isSingleton();

	boolean isPrototype();

	boolean isAbstract();

	int getRole();

	@Nullable
	String getDescription();

	@Nullable
	String getResourceDescription();

	@Nullable
	BeanDefinition getOriginatingBeanDefinition();

}

以上是BenaDefiniiton(简称bd)这个接口的代码,这里面大多的方法都是见名知意的,这个接口定义了我们类在Spring当中最基本的信息,例如Primary、懒加载等等。

那么既然是接口,就会衍生出很多实现类,不同的实现类用于不同的场景,例如我们Spring内置的BeanDefinition使用RootBeanDefinition,扫描包得到的类使用ScannedGenericBeanDefinition,基本的使用GenericBeanDefinition。在Spring5.x时,BeanDefinition的实现类与子接口有如下:

1、实现类:AbstractBeanDefinition

2、子接口:AnnotatedBeanDefinition

3、实现类:AnnotatedGenericBeanDefinition

4、实现类:ChildBeanDefinition

5、实现类:ConfigurationClassBeanDefinition

6、实现类:GenericBeanDefinition

7、实现类:RootBeanDefinition

8、实现类:ScannedGenericBeanDefinition

这些都是应用于不同的场景。

例如当我们往Spring的bd容器中注册类的时候,它内部就是将我们注册的类的信息封装到AnnotatedGenericBeanDefinition里面去,详见org.springframework.context.annotation.AnnotatedBeanDefinitionReader#doRegisterBean的代码,其中就是将类的信息封装到bd中的代码是:

AnnotatedGenericBeanDefinition abd = new AnnotatedGenericBeanDefinition(annotatedClass);

PS:在我们学习Spring当中,BeanDefinition是非常重要的,一定要理解知道这个BeanDefinition的作用。

2、SpringApplicationContext创建初始化过程

现有以下示例代码:

AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(AppConfig.class);

话不多说我们先来个图:

Spring阶段性学习:基础、配置解析、回调

一图胜千言。

在我们SpringBoot环境中仍然是使用的此AnnotationConfigApplicationContext。

3、XXXAware回调接口

在我们Spring当中有着各种各样的Aware接口,例如:

1、EnvironmentAware:拿到ConfigurableEnvironment

2、EmbeddedValueResolverAware:拿到StringValueResolver

3、ResourceLoaderAware:拿到ConfigurableApplicationContext

4、ApplicationEventPublisherAware:拿到ConfigurableApplicationContext

5、MessageSourceAware:拿到ConfigurableApplicationContext

6、ApplicationContextAware:拿到ConfigurableApplicationContext

例如我们写一个类,假设我们这个类会被加入到Spring当中,那么我们实现的接口方法中就会获得各种的对象,其中我们最熟悉的就是ApplicationContext。

那么在Spring当中实现回调此功能的类为:ApplicationContextAwareProcessor。

在了解ApplicationContextAwareProcessor之前我们需要先知道PostProcessor,PostProcessor是Spring给我们提供的回调接口,当我们每个类实例化完后,就会调用我们实现此接口的类的方法,并将实例化后的类传入进来,我们来看一下PostProcessor接口的代码:

public interface BeanPostProcessor {

	@Nullable
	default Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
		return bean;
	}

	@Nullable
	default Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
		return bean;
	}

}

这个接口里面的bean参数就是实例化后的对象。

看完这个后我们再回来看ApplicationContextAwareProcessor这个类,它实现于PostProcessor接口,在其postProcessBeforeInitialization方法中做了具体的功能实现,这里我截取一下此类的核心代码:

private void invokeAwareInterfaces(Object bean) {
		if (bean instanceof Aware) {
			if (bean instanceof EnvironmentAware) {
				((EnvironmentAware) bean).setEnvironment(this.applicationContext.getEnvironment());
			}
			if (bean instanceof EmbeddedValueResolverAware) {
				((EmbeddedValueResolverAware) bean).setEmbeddedValueResolver(this.embeddedValueResolver);
			}
			if (bean instanceof ResourceLoaderAware) {
				((ResourceLoaderAware) bean).setResourceLoader(this.applicationContext);
			}
			if (bean instanceof ApplicationEventPublisherAware) {
				((ApplicationEventPublisherAware) bean).setApplicationEventPublisher(this.applicationContext);
			}
			if (bean instanceof MessageSourceAware) {
				((MessageSourceAware) bean).setMessageSource(this.applicationContext);
			}
			if (bean instanceof ApplicationContextAware) {
				((ApplicationContextAware) bean).setApplicationContext(this.applicationContext);
			}
		}
	}

这个代码应该都能看懂吧。

4、BeanPostProcessor

BeanPostProcessor是Spring提供对外的回调接口,我们在第标题3中已经大概说了这个接口,我们这里再次描述:

BeanPostProcessor是Spring提供的可扩展接口,我们实现此方法以后可以在拿到Spring容器当中实例化的每一个对象,使用此扩展接口的方式很简单,写一个类实现此接口,并使其能加入到Spring当中,加入@Component注解。@Import都可以。

这个接口可以实现很多功能,我们更换其实例化的对象,或者更改其对象内容,或者做代理。例如:日志、事物等。我们这里给一个例子,模拟一个事务注解的功能。

首先来一个代表开启事物的注解:

@Target({ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface MyTransactional {
}

再来一个类使用此注解:

@Component
public class Entity1 {

	@MyTransactional
	public void updateBatch(){
		System.out.println("coding 1.....");
		if (1==1) {
			throw new RuntimeException("xxxxx");
		}
		System.out.println("coding 2.....");
	}

	public void updateBatch2(){
		System.out.println("updateBatch2........");
	}
}

再来一个BeanPostProcessor:

@Component
public class MyTransactionalBeanPostProcessor implements BeanPostProcessor {
	@Override
	public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
		Method[] methods = bean.getClass().getMethods();
		boolean isNeedProxy = false;
		if (methods != null && methods.length > 0){
			for (Method method : methods) {
				MyTransactional annotation = method.getAnnotation(MyTransactional.class);
				if (annotation != null){
					isNeedProxy = true;
					break;
				}
			}
		}
		if (!isNeedProxy){
			return bean;
		}
		Enhancer enhancer = new Enhancer();
		enhancer.setSuperclass(bean.getClass());
		enhancer.setCallback(new MethodInterceptor() {
			@Override
			public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
				MyTransactional annotation = method.getAnnotation(MyTransactional.class);
				if (annotation == null){
					return methodProxy.invokeSuper(o,objects);
				}
				try {
					/* 打开事务 */
					System.out.println("事务开始了!!!!!");
					return methodProxy.invokeSuper(o,objects);
				}catch (Throwable e){
					/* 回滚事务 */
					System.out.println("回滚事务!!!!!");
					throw e.getCause();
				} finally {
					/* 提交事务 */
					System.out.println("提交事务!!!!!");
				}
			}
		});
		return enhancer.create();
	}
}

然后我们从ApplicationContext中去获取此Entity1,执行其updateBatch,结果如下:

事务开始了!!!!!
coding 1.....
回滚事务!!!!!
提交事务!!!!!
Exception in thread "main" java.lang.RuntimeException: xxxxx
	at com.dh.testEntity.Entity1.updateBatch(Entity1.java:21)
	at com.dh.testEntity.Entity1$$EnhancerByCGLIB$$75f7b0e9.CGLIB$updateBatch$0(<generated>)
	at com.dh.testEntity.Entity1$$EnhancerByCGLIB$$75f7b0e9$$FastClassByCGLIB$$52ecdc45.invoke(<generated>)
	at org.springframework.cglib.proxy.MethodProxy.invokeSuper(MethodProxy.java:228)
	at com.dh.process.MyTransactionalBeanPostProcessor$1.intercept(MyTransactionalBeanPostProcessor.java:64)
	at com.dh.testEntity.Entity1$$EnhancerByCGLIB$$75f7b0e9.updateBatch(<generated>)
	at com.dh.main.Main1.test1(Main1.java:42)
	at com.dh.main.Main1.main(Main1.java:46)

相信到这来了以后大概就知道了整体的流程,我们可以使用cglib在BeanPostProcessor当中拿到带有我们自定义注解的Object,然后进行代理拦截方法操作。

注意:

1、我们的BeanPostProcessor是在bean实例化之后,放入Spring的bean容器之前被调用执行

2、该扩展接口只能对现有bean进行增强,但并不能增加bean

5、BeanFactoryPostProcessor

在我们刚才的BeanPostProcessor中,只能对现有bean进行更改,但如果有这种需求,我们需要动态的往Spring当中去注入Bean或者修改未实例化Bean的信息,那么就可以使用这个接口BeanFactoryPostProcessor。先来看看这个接口的样子:

public interface BeanFactoryPostProcessor {
	void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException;
}

这里面给了我们BeanFactory,给了beanFactory那么就好办了,可以直接加入我们生成的,或者扫描到的类进去。

在这里我们可以拿到所有未实例化的BeanDefinition,并且可以修改其属性内容。使用方法如下示例:

@Component
public class TestMyBeanFactoryPostProcessor implements BeanFactoryPostProcessor {
	@Override
	public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
		BeanDefinition pojo1 = beanFactory.getBeanDefinition("pojo1");
		pojo1.setPrimary(true);
        beanFactory.registerSingleton("xxssxx",XXX.class);
	}
}

注意:这个回调接口有两种方式可以给Spring,第一个就是加入@Component注解并让Spring扫描到,第二个就是ApplicationContext.addBeanFactoryPostProcessor(xxxx)。但是他们执行的位置都是一样的,并不会存在先后顺序。

调用的位置:

Spring阶段性学习:基础、配置解析、回调

在PostProcessorRegistrationDelegate.invokeBeanFactoryPostProcessors方法当中,我们手动给的BeanFactoryPostProcessor会被存入到regularPostProcessors集合当中,然后会等待解析完配置、扫描包以后,将扫描出来的BeanFactoryPostProcessor存入到registryProcessors当中,然后这这两行代码统一去调用。

invokeBeanFactoryPostProcessors(registryProcessors, beanFactory);
invokeBeanFactoryPostProcessors(regularPostProcessors, beanFactory);

6、BeanDefinitionRegistryPostProcessor

首先我们要知道:

1、我们的BeanDefinitionRegistryPostProcessor是BeanFactoryPostProcessor的子接口

2、此接口里面可以拿到Bean的注册器,可以直接注册BeanDefinition

先来看看接口:

@Component
public class TestMyBeanFactoryPostProcessor implements BeanDefinitionRegistryPostProcessor {
   @Override
   public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException {
      BeanDefinitionBuilder beanDefinitionBuilder = BeanDefinitionBuilder.genericBeanDefinition(XXXX.class);
	  registry.registerBeanDefinition("xxxxxx",beanDefinitionBuilder.getBeanDefinition());
   }
}

注意在这里我们直接拿到了注册器,可以自己直接注册BD进去。

我们加入BeanDefinitionRegistryPostProcessor的方式有两种,第一种是@Component,第二种是ApplicationContext.addBeanFactoryPostProcessor(xxxx)。

但他们执行的位置不一样。

如果是@Component的话,那么就是在Spring解析操作完以后去调用此接口,此时我们能拿到所有的BeanDefinition。

如果手动调用方法加入到Spring的,那么则会在Spring进行解析操作以前去调用此接口,此时我们只能拿到Spring内置的BeanDefinition

源码查看:

@Component的操作时调用的地方:

Spring阶段性学习:基础、配置解析、回调

Spring阶段性学习:基础、配置解析、回调

它是在Spring解析配置完成以后再从容器当中拿到类型为此接口的类,并进行相应的调用执行。

如果是手动加入到容器里面去的话:

Spring阶段性学习:基础、配置解析、回调

这里已进入此方法就拿到参数的集合,参数的集合就是我们调用方法存入的集合,这里还没有进行配置类解析,那么自然也就没有我们其他的BeanDefinition,所以用这种方法加入回调接口的话,你除了Spring内置的以外,用户自定义的都是拿不到的。

这个回调接口在Spring当中应用非常重要,例如我们解析配置类的代码就是实现了此接口,然后在此进行解析操作的,此类叫做:ConfigurationClassPostProcessor。

7、解析Configuration配置类

在Spring当中,解析配置的类叫做ConfigurationClassPostProcessor,该类实现于BeanDefinitionRegistryPostProcessor接口,对于配置的解析全部都是存在于其postProcessBeanDefinitionRegistry方法当中的。

如何调用解析类?

我们首先先知道其如何去调用这个ConfigurationClassPostProcessor的postProcessBeanDefinitionRegistry方法:

首先进入到ApplicationContext的refresh方法的invokeBeanFactoryPostProcessors(beanFactory);中:

Spring阶段性学习:基础、配置解析、回调

进入红线部分的方法中:

Spring阶段性学习:基础、配置解析、回调

注意看标红的代码处,此时Spring还未解析配置,那么当前beanFactory当中的BeanDefinition肯定是没有的,就算是有,那也只是Spring内置的。

这里的beanFactory.getBeanNamesForType(BeanDefinitionRegistryPostProcessor.class, true, false);方法的意思就是拿到bean工厂中类型为BeanDefinitionRegistryPostProcessor的Bean的beanName。

断点后发现,此处postProcessorNames数组的长度为1,里面的这个值就是我们的ConfigurationClassPostProcessor。

Spring阶段性学习:基础、配置解析、回调

注意,我们的ConfigurationClassPostProcessor在Spring当中的beanName就是这个值,验证如下:

Spring阶段性学习:基础、配置解析、回调

,然后继续看:

Spring阶段性学习:基础、配置解析、回调

注意这里的registryProcessors.addAll(currentRegistryProcessors),registryProcessors也可能存在我们手动加入Spring的回调,所以需要合并

invokeBeanDefinitionRegistryPostProcessors(currentRegistryProcessors, registry);这行代码就是真正的去调用,进入这行代码后:

Spring阶段性学习:基础、配置解析、回调

代码里面如何实现功能的?

概览

public void processConfigBeanDefinitions(BeanDefinitionRegistry registry) {
    List<BeanDefinitionHolder> configCandidates = new ArrayList<>();
    // 获取当前容器中所有BeanDefinition的名称,然后进行遍历,因为要看这里面有否有配置类,如果有配置类,那么就会解析这些配置类,如果没有,那么就直接跳过了。
    String[] candidateNames = registry.getBeanDefinitionNames();
    // 遍历这些类进行解析
    for (String beanName : candidateNames) {
        // 拿到BeanDefinition
        BeanDefinition beanDef = registry.getBeanDefinition(beanName);
        // 明面上的意思:判断该类是否已经加载过了,如果加载过了,那么则无需再加载
        // 实际上:判断这个了是否存在lite或者full的配置类标识,如果存在这个标识中任意一个,说明此类已经被解析过,并且是配置类,那么就不用再去解析了
        if (ConfigurationClassUtils.isFullConfigurationClass(beanDef) || ConfigurationClassUtils.isLiteConfigurationClass(beanDef)) {	// 判断该BeanDefinition是否存在full或lite的标识
            // 打个log,说这个beandefinition已经加载过了。
            if (logger.isDebugEnabled()) {
                logger.debug("Bean definition has already been processed as a configuration class: " + beanDef);
            }
        }
        /* 
        	如果没加载过,那么判断此类是否是配置类,如果是则加入到configCandidates中后面会对这些配置类进行解析,
        	此方法里面代码逻辑:
        		1、判断这个类的类型,因为注解类和其他类获取类信息的方式是不一样的
        		2、判断配置类的类型
        			1):该类如果包含@Configuration注解,那么设置标志位为full
        			2):否则判断是否为接口,如果是,则返回false,否则判断其是否包含@Component、@ComponentScan、@Import、@ImportResource、@Bean,如果包含此中任意一个,则认为其是一个半配置类,设置标志位为lite,如果都不包含,则返回false
        			3):如果既不是全配置类,也不是半个配置类,那么则返回false
        			4):如果类上存在@Order注解,那么则设置其order标志位的值
        			5):返回true
         */
        else if (ConfigurationClassUtils.checkConfigurationClassCandidate(beanDef, this.metadataReaderFactory)) {	/*  */
            configCandidates.add(new BeanDefinitionHolder(beanDef, beanName));
        }
    }

    // 前面的遍历中,如果一个配置类都没有,那就没必要继续下去了,直接return了
    if (configCandidates.isEmpty()) {
        return;
    }

    // 记得在ConfigurationClassUtils.checkConfigurationClassCandidate这个if里面注释说了如果存在@Order就会设置其标志位的值吗?
   	// 这里就是把@Order设置标志位的值拿出来,遍历一次
    configCandidates.sort((bd1, bd2) -> {
        int i1 = ConfigurationClassUtils.getOrder(bd1.getBeanDefinition());
        int i2 = ConfigurationClassUtils.getOrder(bd2.getBeanDefinition());
        return Integer.compare(i1, i2);
    });

    // 这里不要太过于关心,就是拿到BeanName生成器,如果我们给了就拿我们的,没有则回去拿Spring自己内置的生成器
    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();
    }

    // ConfigurationClassParser这个类我们见名知意,就是做【配置类解析】的
    ConfigurationClassParser parser = new ConfigurationClassParser(
        this.metadataReaderFactory, this.problemReporter, this.environment,
        this.resourceLoader, this.componentScanBeanNameGenerator, registry);
	// 这个集合里面就是我们当前现有的所有配置类
    Set<BeanDefinitionHolder> candidates = new LinkedHashSet<>(configCandidates);
    // 记录已经解析了的配置类
    Set<ConfigurationClass> alreadyParsed = new HashSet<>(configCandidates.size());
    do {
        /* 解析这些类 */
        parser.parse(candidates);
        // 校验
        parser.validate();
		// 解析拿到的类
        Set<ConfigurationClass> configClasses = new LinkedHashSet<>(parser.getConfigurationClasses());
        configClasses.removeAll(alreadyParsed);

        // Read the model and create bean definitions based on its content
        if (this.reader == null) {
            this.reader = new ConfigurationClassBeanDefinitionReader(
                registry, this.sourceExtractor, this.resourceLoader, this.environment,
                this.importBeanNameGenerator, parser.getImportRegistry());
        }
        /* 注意看这个方法,是将我们解析的来在此处进行解析 */
        this.reader.loadBeanDefinitions(configClasses);
        alreadyParsed.addAll(configClasses);

        candidates.clear();
       ...........
    }
    while (!candidates.isEmpty());

   ...........
}

这一个方法里面概涵了解析配置类的操作。代码中干的事情我都以注释的形式在上面写上,一些无用的代码此处未贴上来。

parse解析

Spring阶段性学习:基础、配置解析、回调

进入这个方法后,实际上到了此处代码:

protected void processConfigurationClass(ConfigurationClass configClass) throws IOException {
    if (this.conditionEvaluator.shouldSkip(configClass.getMetadata(), ConfigurationPhase.PARSE_CONFIGURATION)) {
        return;
    }
    /*处理imported的情况,就是当前类被其他类Import的情况*/
    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);
        }
    }
    /* 仅仅是转换为SourceClass */
    // 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中解析当前类,
*  1、首先解析PropertySources,
*  2、然后解析ComponentScan,解析完后放置到存放BD的集合中,然后遍历ComponentScan中的类如果是配置类,那么去调用其parse方法进行解析这个配置类.
*     ASM将class文件转换为类,中间也有可能有一些我们配置的过滤的包路径
*     还包括了一些@Lazy的设置啊等等
*  3、然后解析Import中的类
*     1、判断当前类中Import的类如果为空,则停止Import的解析
*     2、遍历Import中引入的类,
*        2.1、如果实现了ImportSelector,那么就拿到其返回值,并将这些类解析为SourceClass,然后再调用当前的processImports方法解析这些SourceClass,因为有可能这些类里面也包含了@Import
*        2.2、如果实现了ImportBeanDefinitionRegistrar,那么则拿到其返回的实例对象,加入到importBeanDefinitionRegistrars集合在中去,然后在org.springframework.context.annotation.ConfigurationClassBeanDefinitionReader#loadBeanDefinitions(java.util.Set)中实例化进行调用方法
*        2.3、如果以上的不成立,那么说明是一个普通类,但是注意如果是普通类的话,那么说明它可能也是一个配置类,那么就需要重新给这个类解析一圈,就重新调用org.springframework.context.annotation.ConfigurationClassParser#processConfigurationClass(org.springframework.context.annotation.ConfigurationClass)
*           方法进行解析,注意此时的参数,就是我们的普通类
*  #、最后将当前类存放至configurationClasses集合中去,这里第一次进来的时候是AppConfig,然后在处理Import的时候,这个方法还会被调用一遍,被调用时此时的ConfigurationClass为Pojo2

此doProcessConfigurationClass方法中具体做了哪些事情?先贴代码:

@Nullable
	protected final SourceClass doProcessConfigurationClass(ConfigurationClass configClass, SourceClass sourceClass)
			throws IOException {

		/* 处理内部类的情况 */
		processMemberClasses(configClass, sourceClass);

		/* 处理@PropertySource的情况,并解析 */
		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");
			}
		}

		/* 处理@ComponentScan和s的情况 */
		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) {
				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());
					}
				}
			}
		}

		// 处理@Import的情况
		processImports(configClass, sourceClass, getImports(sourceClass), true);

		/* 处理@ImportResource和s的情况 */
		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);
			}
		}

		// 处理@Bean方法的情况
		Set<MethodMetadata> beanMethods = retrieveBeanMethodMetadata(sourceClass);
		for (MethodMetadata methodMetadata : beanMethods) {
			configClass.addBeanMethod(new BeanMethod(methodMetadata, configClass));
		}

		..............

		// No superclass -> processing is complete
		return null;
	}

每部分代码具体干的事情我在代码里注释的方式进行表达,不重要的....忽略了。

我们这里挑几个重要的来说:

@ComponentScan

// Process any @ComponentScan annotations
/* 处理ComponentScan和s的情况 */
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
        /* 解析我们配置类上的ComponentScan和s注解信息,然后拿到扫描到的类信息,
				 * 注意,此时这些BeanDefinition已经被放入到存放BeanDefinition的容器当中,此处再次for循环的原因是为了判断这些类是否包含配置类,如果是的话,那么还得递归的去调用再次解析
				 * */
        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());
            }
        }
    }
}

这段代码就是解析扫描包这个注解的代码,首先拿到这个类上所有的@ComponentScan和@ComponentScans。

然后进行遍历解析,然后调用this.componentScanParser.parse方法解析我们填入的扫描包的字符串路径,返回结果为扫描到的所有BeanDefinitionHolder,这个BeanDefinitionHolder其实就是BeanName+BeanDefinition的合并版,为的是方便传参。这个parse方法我们先暂停一下,看下面一点。

看下面遍历scannedBeanDefinitions集合的for循环,重点第二个if,先是校验是否为配置类(这个东西在上面我们说过并详细解释过),如果不是那么则不操作,如果是则再次对这个类进行解析,递归调用当前我们这个方法,当前方法执行完成后,则会将此类put到configurationClasses当中,不仅仅是当前递归的时候会put,我们首次调用解析的时候仍然会put。

我们现在回头来看上面的parse方法:Set scannedBeanDefinitions = this.componentScanParser.parse(componentScan, sourceClass.getMetadata().getClassName());

public Set<BeanDefinitionHolder> parse(AnnotationAttributes componentScan, final String declaringClass) {
    /*  创建一个扫描器,
    	注意在我们AnnotationConfigApplicationContext的无参构造器中创建了一个这个Scanner,但是这里扫描包仍然自己创建了一个Scanner
    	这也验证了我们第2部分中对于AnnotationConfigApplicationContext的无参构造器中创建了的这个Scanner是没有使用的验证。
    */
    ClassPathBeanDefinitionScanner scanner = new ClassPathBeanDefinitionScanner(this.registry,
                                                                                componentScan.getBoolean("useDefaultFilters"), this.environment, this.resourceLoader);
    /* Bean的名称生成器 */
    Class<? extends BeanNameGenerator> generatorClass = componentScan.getClass("nameGenerator");
    boolean useInheritedGenerator = (BeanNameGenerator.class == generatorClass);
    scanner.setBeanNameGenerator(useInheritedGenerator ? this.beanNameGenerator :
                                 BeanUtils.instantiateClass(generatorClass));
    /* Web当中的,暂不知 */
    ScopedProxyMode scopedProxyMode = componentScan.getEnum("scopedProxy");
    if (scopedProxyMode != ScopedProxyMode.DEFAULT) {
        scanner.setScopedProxyMode(scopedProxyMode);
    }
    else {
        Class<? extends ScopeMetadataResolver> resolverClass = componentScan.getClass("scopeResolver");
        scanner.setScopeMetadataResolver(BeanUtils.instantiateClass(resolverClass));
    }

    scanner.setResourcePattern(componentScan.getString("resourcePattern"));
    /* 过滤器,在此处会加入进去,后序解析的时候回进行相应的过滤操作 */
    for (AnnotationAttributes filter : componentScan.getAnnotationArray("includeFilters")) {
        for (TypeFilter typeFilter : typeFiltersFor(filter)) {
            scanner.addIncludeFilter(typeFilter);
        }
    }
    /* 剔除的过滤 */
    for (AnnotationAttributes filter : componentScan.getAnnotationArray("excludeFilters")) {
        for (TypeFilter typeFilter : typeFiltersFor(filter)) {
            scanner.addExcludeFilter(typeFilter);
        }
    }
    /*
		* 判断是否需要懒加载,如果需要懒加载,那么则设置scanner中默认懒加载为true
		* 注意这里获取是否lazy是我们的配置类的注解,如果配置类上@Lazy加上了的话,那么这个配置类配置中扫描的其他类如果没设置@Lazy的话那么则会依据当前这个配置类的Lazy来设置其Lazy值,
		* 具体详见:
		* 	org.springframework.context.annotation.ClassPathBeanDefinitionScanner.doScan中
		* 			postProcessBeanDefinition((AbstractBeanDefinition) candidate, beanName);
		* 			AnnotationConfigUtils.processCommonDefinitionAnnotations((AnnotatedBeanDefinition) candidate);
		* 			这两行代码
		* */
    boolean lazyInit = componentScan.getBoolean("lazyInit");
    if (lazyInit) {
        scanner.getBeanDefinitionDefaults().setLazyInit(true);
    }

    Set<String> basePackages = new LinkedHashSet<>();
    /* 拿到所有扫描包的路径 */
    String[] basePackagesArray = componentScan.getStringArray("basePackages");
    for (String pkg : basePackagesArray) {
        String[] tokenized = StringUtils.tokenizeToStringArray(this.environment.resolvePlaceholders(pkg),
                                                               ConfigurableApplicationContext.CONFIG_LOCATION_DELIMITERS);
        Collections.addAll(basePackages, tokenized);
    }
    /* 拿到扫描类 */
    for (Class<?> clazz : componentScan.getClassArray("basePackageClasses")) {
        basePackages.add(ClassUtils.getPackageName(clazz));
    }
    if (basePackages.isEmpty()) {
        basePackages.add(ClassUtils.getPackageName(declaringClass));
    }
    /* 过滤排除操作,不用细究,只需要知道这里将不需要的路径给记下来了 */
    scanner.addExcludeFilter(new AbstractTypeHierarchyTraversingFilter(false, false) {
        @Override
        protected boolean matchClassName(String className) {
            return declaringClass.equals(className);
        }
    });
    /* 这里才是正儿八经的去扫描包路径下面的类的方法 */
    return scanner.doScan(StringUtils.toStringArray(basePackages));
}

解释都写在注释上面了,现在继续进入doScan方法:

protected Set<BeanDefinitionHolder> doScan(String... basePackages) {
    Assert.notEmpty(basePackages, "At least one base package must be specified");
    Set<BeanDefinitionHolder> beanDefinitions = new LinkedHashSet<>();
    /* 将多个包路径循环扫描 */
    for (String basePackage : basePackages) {
        /* 查找当前包下所有的类,并转换为BeanDefinition,这里扫描类文件并将其转换为class的操作使用的ASM一个开源的文件转class的项目,这里不细究 */
        Set<BeanDefinition> candidates = findCandidateComponents(basePackage);
        /* 将扫描到的类循环遍历 */
        for (BeanDefinition candidate : candidates) {
            /* 拿到Scope作用域 */
            ScopeMetadata scopeMetadata = this.scopeMetadataResolver.resolveScopeMetadata(candidate);
            candidate.setScope(scopeMetadata.getScopeName());
            /* 生成BeanName */
            String beanName = this.beanNameGenerator.generateBeanName(candidate, this.registry);
            /* 是否为AbstractBeanDefinition,这里扫描到的Bean类型都为ScannedGenericBeanDefinition,此类间接继承于AbstractBeanDefinition,所以此处一定是true */
            if (candidate instanceof AbstractBeanDefinition) {
                postProcessBeanDefinition((AbstractBeanDefinition) candidate, beanName);
            }
            /* ScannedGenericBeanDefinition同样实现了AnnotatedBeanDefinition,所以这里也肯定是true */
            if (candidate instanceof AnnotatedBeanDefinition) {
                AnnotationConfigUtils.processCommonDefinitionAnnotations((AnnotatedBeanDefinition) candidate);
            }
            /* 检查一下这个Bean是否允许被放入Spring中来,如果是,则直接放进去 */
            if (checkCandidate(beanName, candidate)) {
                BeanDefinitionHolder definitionHolder = new BeanDefinitionHolder(candidate, beanName);
                definitionHolder =
                    AnnotationConfigUtils.applyScopedProxyMode(scopeMetadata, definitionHolder, this.registry);
                beanDefinitions.add(definitionHolder);
                registerBeanDefinition(definitionHolder, this.registry);
            }
        }
    }
    return beanDefinitions;
}

这里代码的解释都写在了注释里面,我们需要重点来说一下:

if (candidate instanceof AbstractBeanDefinition) {
    postProcessBeanDefinition((AbstractBeanDefinition) candidate, beanName);
}

if (candidate instanceof AnnotatedBeanDefinition) {
    AnnotationConfigUtils.processCommonDefinitionAnnotations((AnnotatedBeanDefinition) candidate);
}

注意我们当选的BeanDefinition的类型为:ScannedGenericBeanDefinition,这个可以在findCandidateComponents的scanCandidateComponents方法中找到:

Spring阶段性学习:基础、配置解析、回调

那么既然当前的BeanDefinition的类型为ScannedGenericBeanDefinition,那么这两个if都是会进入的。

这两个if里面的实际调用的方法干的事情就是设置一些BeanDefinition的默认属性,例如是否为azy,如果没有设置则设置为其最*的配置类的lazy设置,此处在前面的代码处也有体现,例如AppConfig这个类配置了@CompentScan和@Lazy,那么根据这个配置扫描出来的类如果没设置@Lazy的话,那么则会按照AppConfig的配置进行设置。

那么扫描包说完了,我们再来说一下@Import

@Import

在我们的doProcessConfigurationClass中的processImports(configClass, sourceClass, getImports(sourceClass), true);这一行代码中:

processImports(configClass, sourceClass, getImports(sourceClass), true);

当我们的配置类上拥有@Import注解,那么会进入这个类进行解析。

首先在看源码之前我们要知道Import进来的类分为三种:

? 1、普通的类

? 2、实现了ImportSelector接口的类

? 3、实现了ImportBeanDefinitionRegistrar接口的类

如果是普通的类,那么就放入Spring了,如果是ImportSelector或者ImportBeanDefinitionRegistrar的话,那么则会去调用其回调方法。

我们看看ImportSelector和ImportBeanDefinitionRegistrar的接口代码:

public interface ImportSelector {
    // 这个返回的String[]的值示例:return new String[]{"com.dh.App1","com.dh.App2"}。那么Spring就会把你数组中的ckassName实例化到Spring当中去
    String[] selectImports(AnnotationMetadata importingClassMetadata);
}
public interface ImportBeanDefinitionRegistrar {
    void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry);
}

ImportSelector和ImportBeanDefinitionRegistrar,给的参数为AnnotationMetadata,从这个参数里面可以拿到该@Import注解的类上其他的注解信息以及类信息。

除此之外,ImportBeanDefinitionRegistrar中还给了一个BeanDefinitionRegistry注册器,可以直接用这个对象把BeanDefinition注册到Spring当中,可以查看一下@MapperScan注解里面,这个是Mybatis的扫描包注解,我们进入此注解的代码中可以看到:

Spring阶段性学习:基础、配置解析、回调

它Import了一个MapperScannerRegistrar,进入这个类:

Spring阶段性学习:基础、配置解析、回调

其实现了这两个接口,那么你看mybatis源码就知道它如何集成的了,而且入口在哪,这个类会扫描注解上的包路径,然后进行处理后将创建mapper接口的实现类后装入BeanDefinition,并使用注册器放入spring中,那么我们在使用的时候就直接在Mapper变量上@Autowired,然后根据类型就可以自动注入进来。

下面在代码中对Import解析进行详细的描述

private void processImports(ConfigurationClass configClass, SourceClass currentSourceClass,
                            Collection<SourceClass> importCandidates, boolean checkForCircularImports) {
    if (importCandidates.isEmpty()) {
        return;
    }
    if (checkForCircularImports && isChainedImportOnStack(configClass)) {
        this.problemReporter.error(new CircularImportProblem(configClass, this.importStack));
    }
    else {
        this.importStack.push(configClass);
        try {
            /* @Import注解中加入的类,对这些类进行遍历 */
            for (SourceClass candidate : importCandidates) {
                /* 首先第一个判断:
                   判断当前Import的类是否是ImportSelector的实现类,如果是则去调用其processImports方法拿到其返回的className数组进行操作
                */
                if (candidate.isAssignable(ImportSelector.class)) {
                    // Candidate class is an ImportSelector -> delegate to it to determine imports
                    // 拿到当前类
                    Class<?> candidateClass = candidate.loadClass();
                    /* 实例化我们的配置的类, */
                    ImportSelector selector = BeanUtils.instantiateClass(candidateClass, ImportSelector.class);
                    ParserStrategyUtils.invokeAwareMethods(
                        selector, this.environment, this.resourceLoader, this.registry);
                    if (this.deferredImportSelectors != null && selector instanceof DeferredImportSelector) {
                        this.deferredImportSelectors.add(
                            new DeferredImportSelectorHolder(configClass, (DeferredImportSelector) selector));
                    }
                    else {
                        /* 调用我们实现ImportSelector的selectImports方法,拿到我们需要实例化的类名数组,然后进行实例化 */
                        String[] importClassNames = selector.selectImports(currentSourceClass.getMetadata());
                        Collection<SourceClass> importSourceClasses = asSourceClasses(importClassNames);
                        /* 将ImportSelector返回的值进行处理,递归调用本身 */
                        processImports(configClass, currentSourceClass, importSourceClasses, false);
                    }
                }
                else if (candidate.isAssignable(ImportBeanDefinitionRegistrar.class)) {
                    // Candidate class is an ImportBeanDefinitionRegistrar ->
                    // delegate to it to register additional bean definitions
                    Class<?> candidateClass = candidate.loadClass();
                    ImportBeanDefinitionRegistrar registrar =
                        BeanUtils.instantiateClass(candidateClass, ImportBeanDefinitionRegistrar.class);
                    ParserStrategyUtils.invokeAwareMethods(
                        registrar, this.environment, this.resourceLoader, this.registry);
                    /* 这个和Selector不同,它并不是直接实例化后调用方法拿到返回值进行处理了,而是先存入importBeanDefinitionRegistrars变量中后面统一处理 */
                    configClass.addImportBeanDefinitionRegistrar(registrar, currentSourceClass.getMetadata());
                }
                else {
                    // Candidate class not an ImportSelector or ImportBeanDefinitionRegistrar ->
                    // process it as an @Configuration class
                    this.importStack.registerImport(
                        currentSourceClass.getMetadata(), candidate.getMetadata().getClassName());
                    /* 到这里的时候,说明当前的这个类是一个普通的类,那么就会将当前的类存入configurationClasses变量中,注意此时并没有存入Spring的bd容器里面去 */
                    processConfigurationClass(candidate.asConfigClass(configClass));
                }
            }
        }
        catch (BeanDefinitionStoreException ex) {
            throw ex;
        }
        catch (Throwable ex) {
            throw new BeanDefinitionStoreException(
                "Failed to process import candidates for configuration class [" +
                configClass.getMetadata().getClassName() + "]", ex);
        }
        finally {
            this.importStack.pop();
        }
    }
}

上述中循环里面解析分为三种情况:

? 第一种:解析实现接口ImportSelector的

? 第二种:解析实现接口ImportBeanDefinitionRegistrar

? 第三种:以上不成立,就当作一个普通类来解析的

第一种解析实现ImportSelector接口的:

// 拿到当前类
Class<?> candidateClass = candidate.loadClass();
/* 实例化我们的配置的类, */
ImportSelector selector = BeanUtils.instantiateClass(candidateClass, ImportSelector.class);
ParserStrategyUtils.invokeAwareMethods(
    selector, this.environment, this.resourceLoader, this.registry);
if (this.deferredImportSelectors != null && selector instanceof DeferredImportSelector) {
    this.deferredImportSelectors.add(
        new DeferredImportSelectorHolder(configClass, (DeferredImportSelector) selector));
}
else {
    /* 调用我们实现ImportSelector的selectImports方法,拿到我们需要实例化的类名数组,然后进行实例化 */
    String[] importClassNames = selector.selectImports(currentSourceClass.getMetadata());
    Collection<SourceClass> importSourceClasses = asSourceClasses(importClassNames);
    /* 将ImportSelector返回的值进行处理,递归调用本身 */
    processImports(configClass, currentSourceClass, importSourceClasses, false);
}

首先实例化这个类,然后调用方法获取到ClassName数组,然后调用asSourceClasses方法将这些className转换为SourceClass集合,然后再将这些作为参数调用当前的这个方法,因为Import的这些类也可能是配置类。

第二种:解析实现接口ImportBeanDefinitionRegistrar的:

else if (candidate.isAssignable(ImportBeanDefinitionRegistrar.class)) {
    // Candidate class is an ImportBeanDefinitionRegistrar ->
    // delegate to it to register additional bean definitions
    Class<?> candidateClass = candidate.loadClass();
    ImportBeanDefinitionRegistrar registrar =
        BeanUtils.instantiateClass(candidateClass, ImportBeanDefinitionRegistrar.class);
    ParserStrategyUtils.invokeAwareMethods(
        registrar, this.environment, this.resourceLoader, this.registry);
    /* 这个和Selector不同,它并不是直接实例化后调用方法拿到返回值进行处理了,而是先存入importBeanDefinitionRegistrars变量中后面统一处理 */
    configClass.addImportBeanDefinitionRegistrar(registrar, currentSourceClass.getMetadata());
}

如果类型为ImportBeanDefinitionRegistrar,那么则会将其存入configClass里面,等待后面进行操作。

第三种:解析普通类

else {
    // Candidate class not an ImportSelector or ImportBeanDefinitionRegistrar ->
    // process it as an @Configuration class
    this.importStack.registerImport(
        currentSourceClass.getMetadata(), candidate.getMetadata().getClassName());
    /* 到这里的时候,说明当前的这个类是一个普通的类,那么就会将当前的类存入configurationClasses变量中,注意此时并没有存入Spring的bd容器里面去 */
    processConfigurationClass(candidate.asConfigClass(configClass));
}

此处因为不知道其普通类中是否包含配置信息,所以使其重新回到processConfigurationClass方法中重新走一遍上述的所有流程,而这些流程走完以后,就会将当前这个类put到configurationClasses当中去

额外解释:

? 我们的ImportSelector和ImportBeanDefinitionRegistrar都可以往Spring中放入Bean,为什么有两个呢?

? 1、首先ImportSelector返回的className最后生成的BeanDefinition的类型都为:StandardAnnotationMetadata,并且除了获取ClassName以外,创建到放入Spring的过程都由Spring处理,我们无法控制。

? 2、而我们的ImportBeanDefinitionRegistrar它由于回调中存在Registry,所以我们所有操作包括创建和放入Spring,都由我们来操作,所以BeanDefinition的类型这些的都是自己去控制的。

这两个回调接口在使用场景上各有不同,具体什么时候使用哪个的话,仁者见仁智者见智吧。

这里我们不再去探究@Import下面的@ImportResource等其他的具体实现。

parse方法以后的解析

注意在我们的parse方法中,存在两个问题,processConfigurationClass中的类未放入Spring,ImportBeanDefinitionRegistrar的回调未当时进行回调调用。

那么这就是是在parse后面的this.reader.loadBeanDefinitions(configClasses);代码中进行操作:

Spring阶段性学习:基础、配置解析、回调

这里面的代码:

Spring阶段性学习:基础、配置解析、回调

再次进入loadBeanDefinitionsForConfigurationClass方法:

Spring阶段性学习:基础、配置解析、回调

这里的代码很静很清楚了,我们都用红框标注除了拍,分别是处理@Import进来的普通类,需要放入Spring的、解析@bean方法的、还有对ImportBeanDefinitionRegistrar进行处理回调的,这里面的方法代码都比较少了,我们这里就不再单独的进去查看代码了。

注意这里有一个点没有说,那就是我们的XML解析,因为现在大部分都没有使用XML配置了,所以此处就忽略掉。

对配置类的Cglib代理

具体到另外一篇博客中查看:https://www.cnblogs.com/daihang2366/p/15125874.html

内容太多了,记住起来太累了。

交流QQ:2366567504

如果文中有误的地方,请提出,我会及时改正。

Spring阶段性学习:基础、配置解析、回调

上一篇:理解Java虚拟机类加载器


下一篇:SpringCloud - 微服务开发之 四大问题 + 四大技术点