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);
话不多说我们先来个图:
一图胜千言。
在我们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)。但是他们执行的位置都是一样的,并不会存在先后顺序。
调用的位置:
在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解析配置完成以后再从容器当中拿到类型为此接口的类,并进行相应的调用执行。
如果是手动加入到容器里面去的话:
这里已进入此方法就拿到参数的集合,参数的集合就是我们调用方法存入的集合,这里还没有进行配置类解析,那么自然也就没有我们其他的BeanDefinition,所以用这种方法加入回调接口的话,你除了Spring内置的以外,用户自定义的都是拿不到的。
这个回调接口在Spring当中应用非常重要,例如我们解析配置类的代码就是实现了此接口,然后在此进行解析操作的,此类叫做:ConfigurationClassPostProcessor。
7、解析Configuration配置类
在Spring当中,解析配置的类叫做ConfigurationClassPostProcessor,该类实现于BeanDefinitionRegistryPostProcessor接口,对于配置的解析全部都是存在于其postProcessBeanDefinitionRegistry方法当中的。
如何调用解析类?
我们首先先知道其如何去调用这个ConfigurationClassPostProcessor的postProcessBeanDefinitionRegistry方法:
首先进入到ApplicationContext的refresh方法的invokeBeanFactoryPostProcessors(beanFactory);中:
进入红线部分的方法中:
注意看标红的代码处,此时Spring还未解析配置,那么当前beanFactory当中的BeanDefinition肯定是没有的,就算是有,那也只是Spring内置的。
这里的beanFactory.getBeanNamesForType(BeanDefinitionRegistryPostProcessor.class, true, false);方法的意思就是拿到bean工厂中类型为BeanDefinitionRegistryPostProcessor的Bean的beanName。
断点后发现,此处postProcessorNames数组的长度为1,里面的这个值就是我们的ConfigurationClassPostProcessor。
注意,我们的ConfigurationClassPostProcessor在Spring当中的beanName就是这个值,验证如下:
,然后继续看:
注意这里的registryProcessors.addAll(currentRegistryProcessors),registryProcessors也可能存在我们手动加入Spring的回调,所以需要合并
invokeBeanDefinitionRegistryPostProcessors(currentRegistryProcessors, registry);这行代码就是真正的去调用,进入这行代码后:
代码里面如何实现功能的?
概览
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解析
进入这个方法后,实际上到了此处代码:
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
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方法中找到:
那么既然当前的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的扫描包注解,我们进入此注解的代码中可以看到:
它Import了一个MapperScannerRegistrar,进入这个类:
其实现了这两个接口,那么你看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);代码中进行操作:
这里面的代码:
再次进入loadBeanDefinitionsForConfigurationClass方法:
这里的代码很静很清楚了,我们都用红框标注除了拍,分别是处理@Import进来的普通类,需要放入Spring的、解析@bean方法的、还有对ImportBeanDefinitionRegistrar进行处理回调的,这里面的方法代码都比较少了,我们这里就不再单独的进去查看代码了。
注意这里有一个点没有说,那就是我们的XML解析,因为现在大部分都没有使用XML配置了,所以此处就忽略掉。
对配置类的Cglib代理
具体到另外一篇博客中查看:https://www.cnblogs.com/daihang2366/p/15125874.html
内容太多了,记住起来太累了。
交流QQ:2366567504
如果文中有误的地方,请提出,我会及时改正。