随着不断地使用Spring,以及后续的Boot、cloud,不断的体会到这个拯救Java的生态体系的强大,也使我对于这个框架有了极大的好奇心,以至于产生了我为什么不能写一个这样的框架的思考。
通过自学及参考谭勇德(Tom)老师的《Spring 5核心原理于30个类手写实战》这本书,记录此系列博客 。
愿每个想探究Spring原理的人,学习道路一帆风顺

本文是基于注解的IOC初始化,不是XML!!!
代码的含义我都以注释的形式写在代码块中了,请放心查阅

文章目录

  • Annotation 的前世今生
  • 1 定位Bean扫描路径
  • 2 读取Annotation 元数据
    • 2.1 AnnotationConfigApplicationContext通过调用注解Bean定义读取器
    • 2.2 AnnotationScopeMetadataResolver解析作用域元数据
    • 2.3 AnnotationConfigUtils处理注解Bean定义类中的通用注解
    • 2.4 AnnotationConfigUtils根据注解 Bean定义类中配置的作用域为其应用相应的代理策略
    • 2.5 BeanDefinitionReaderUtils向容器注册Bean
  • 3 扫描指定包并解析为BeanDefinition
    • 3.1 ClassPathBeanDefinitionScanner扫描给定的包及其子包
    • 3.2 ClassPathScanningCandidateComponentProvider扫描给定包及其子包的类
  • 4 注册注解BeanDefinition

Annotation 的前世今生

从Spring2.0以后的版本中,Spring也引入了基于注解(Annotation)方式的配置,注解(Annotation)是JDK1.5中引入的—个新特性,用于简化Bean的配置,可以取代XML配置文件。
而开发入员对注解(Annotation)的态度也是萝卜青菜各有所爱,个人认为注解可以大大简化配置,提高开发速度,但也给后期维护增加了难度。目前来说XML方式发展的相对成熟,方便千统—管理。随看SpringBoot的兴起,基于注解的开发甚至实现了零配置。

SpringIOC容器对于类级别的注解类内部的注解分以下两种处理策略:

  1. 类级别的注解:如@Component,@Repository、@Controller、@Service以及JavaEE6的@ManagedBean和@Named注解都是添加在类上面的类级别注解,Spring容器根据注解的过泡规则扫描读取注解Bean定义类,并将其注册到SpringIOC容器中。
  2. 类内部的注解∶如@Autowire、@Value、@Resource以及EJB和WebService相关的注解等,都是添加在类内部的字段或者方法上的类内部注解,SpringlOC容器通过Bean后置注解处理器解析Bean内部的注解。

下面将根据这两种处理策略,分别分析Spring处理注解相关的源码。

1 定位Bean扫描路径

在Spring中管理注解Bean定义的容器有两个: AnnotationConfigApplicationContext和AnnotationConfigWebApplicationContex。

这两个类是专门处理Spring 注解方式配置的容器,直接依赖于注解作为容器配置信息来源的IOC容器。

AnnotationConfigWebApplicationContext 是AnnotationConfigApplicationContext 的 Web版本,两者的用法以及对注解的处理方式几乎没有差别。现在我们以AnnotationConfigApplicationContext为例看看它的源码︰

public class AnnotationConfigApplicationContext extends GenericApplicationContext implements AnnotationConfigRegistry {//保存一个读取注解的Bean定义读取器,并将其设置到容器中private final AnnotatedBeanDefinitionReader reader;//保存一个扫描指定类路径中注解Bean定义的扫描器,并将其设置到容器中private final ClassPathBeanDefinitionScanner scanner;/*** Create a new AnnotationConfigApplicationContext that needs to be populated* through {@link #register} calls and then manually {@linkplain #refresh refreshed}.*///默认构造函数,初始化一个空容器,容器不包含任何 Bean 信息,需要在稍后通过调用其register()//方法注册配置类,并调用refresh()方法刷新容器,触发容器对注解Bean的载入、解析和注册过程public AnnotationConfigApplicationContext() {this.reader = new AnnotatedBeanDefinitionReader(this);this.scanner = new ClassPathBeanDefinitionScanner(this);}/*** Create a new AnnotationConfigApplicationContext with the given DefaultListableBeanFactory.* @param beanFactory the DefaultListableBeanFactory instance to use for this context*/public AnnotationConfigApplicationContext(DefaultListableBeanFactory beanFactory) {super(beanFactory);this.reader = new AnnotatedBeanDefinitionReader(this);this.scanner = new ClassPathBeanDefinitionScanner(this);}/*** Create a new AnnotationConfigApplicationContext, deriving bean definitions* from the given annotated classes and automatically refreshing the context.* @param annotatedClasses one or more annotated classes,* e.g. {@link Configuration @Configuration} classes*///最常用的构造函数,通过将涉及到的配置类传递给该构造函数,以实现将相应配置类中的Bean自动注册到容器中public AnnotationConfigApplicationContext(Class<?>... annotatedClasses) {this();register(annotatedClasses);refresh();}/*** Create a new AnnotationConfigApplicationContext, scanning for bean definitions* in the given packages and automatically refreshing the context.* @param basePackages the packages to check for annotated classes*///该构造函数会自动扫描以给定的包及其子包下的所有类,并自动识别所有的Spring Bean,将其注册到容器中public AnnotationConfigApplicationContext(String... basePackages) {this();//我们自己模拟写过scan(basePackages);refresh();}/*** {@inheritDoc}* <p>Delegates given environment to underlying {@link AnnotatedBeanDefinitionReader}* and {@link ClassPathBeanDefinitionScanner} members.*/@Overridepublic void setEnvironment(ConfigurableEnvironment environment) {super.setEnvironment(environment);this.reader.setEnvironment(environment);this.scanner.setEnvironment(environment);}/*** Provide a custom {@link BeanNameGenerator} for use with {@link AnnotatedBeanDefinitionReader}* and/or {@link ClassPathBeanDefinitionScanner}, if any.* <p>Default is {@link org.springframework.context.annotation.AnnotationBeanNameGenerator}.* <p>Any call to this method must occur prior to calls to {@link #register(Class...)}* and/or {@link #scan(String...)}.* @see AnnotatedBeanDefinitionReader#setBeanNameGenerator* @see ClassPathBeanDefinitionScanner#setBeanNameGenerator*///为容器的注解Bean读取器和注解Bean扫描器设置Bean名称产生器public void setBeanNameGenerator(BeanNameGenerator beanNameGenerator) {this.reader.setBeanNameGenerator(beanNameGenerator);this.scanner.setBeanNameGenerator(beanNameGenerator);getBeanFactory().registerSingleton(AnnotationConfigUtils.CONFIGURATION_BEAN_NAME_GENERATOR, beanNameGenerator);}/*** Set the {@link ScopeMetadataResolver} to use for detected bean classes.* <p>The default is an {@link AnnotationScopeMetadataResolver}.* <p>Any call to this method must occur prior to calls to {@link #register(Class...)}* and/or {@link #scan(String...)}.*///为容器的注解Bean读取器和注解Bean扫描器设置作用范围元信息解析器public void setScopeMetadataResolver(ScopeMetadataResolver scopeMetadataResolver) {this.reader.setScopeMetadataResolver(scopeMetadataResolver);this.scanner.setScopeMetadataResolver(scopeMetadataResolver);}//---------------------------------------------------------------------// Implementation of AnnotationConfigRegistry//---------------------------------------------------------------------/*** Register one or more annotated classes to be processed.* <p>Note that {@link #refresh()} must be called in order for the context* to fully process the new classes.* @param annotatedClasses one or more annotated classes,* e.g. {@link Configuration @Configuration} classes* @see #scan(String...)* @see #refresh()*///为容器注册一个要被处理的注解Bean,新注册的Bean,必须手动调用容器的//refresh()方法刷新容器,触发容器对新注册的Bean的处理public void register(Class<?>... annotatedClasses) {Assert.notEmpty(annotatedClasses, "At least one annotated class must be specified");this.reader.register(annotatedClasses);}/*** Perform a scan within the specified base packages.* <p>Note that {@link #refresh()} must be called in order for the context* to fully process the new classes.* @param basePackages the packages to check for annotated classes* @see #register(Class...)* @see #refresh()*///扫描指定包路径及其子包下的注解类,为了使新添加的类被处理,必须手动调用//refresh()方法刷新容器public void scan(String... basePackages) {Assert.notEmpty(basePackages, "At least one base package must be specified");this.scanner.scan(basePackages);}//---------------------------------------------------------------------// Convenient methods for registering individual beans//---------------------------------------------------------------------/*** Register a bean from the given bean class, deriving its metadata from* class-declared annotations, and optionally providing explicit constructor* arguments for consideration in the autowiring process.* <p>The bean name will be generated according to annotated component rules.* @param annotatedClass the class of the bean* @param constructorArguments argument values to be fed into Spring's* constructor resolution algorithm, resolving either all arguments or just* specific ones, with the rest to be resolved through regular autowiring* (may be {@code null} or empty)* @since 5.0*/public <T> void registerBean(Class<T> annotatedClass, Object... constructorArguments) {registerBean(null, annotatedClass, constructorArguments);}/*** Register a bean from the given bean class, deriving its metadata from* class-declared annotations, and optionally providing explicit constructor* arguments for consideration in the autowiring process.* @param beanName the name of the bean (may be {@code null})* @param annotatedClass the class of the bean* @param constructorArguments argument values to be fed into Spring's* constructor resolution algorithm, resolving either all arguments or just* specific ones, with the rest to be resolved through regular autowiring* (may be {@code null} or empty)* @since 5.0*/public <T> void registerBean(@Nullable String beanName, Class<T> annotatedClass, Object... constructorArguments) {this.reader.doRegisterBean(annotatedClass, null, beanName, null,bd -> {for (Object arg : constructorArguments) {bd.getConstructorArgumentValues().addGenericArgumentValue(arg);}});}@Overridepublic <T> void registerBean(@Nullable String beanName, Class<T> beanClass, @Nullable Supplier<T> supplier,BeanDefinitionCustomizer... customizers) {this.reader.doRegisterBean(beanClass, supplier, beanName, null, customizers);}}

通过上面的源码分析,我们可以看啊到Spring对注解的处理分为两种方式︰

  1. 直接将注解Bean注册到容器中
    可以在初始化容器时注册;也可以在容器创建之后手动调用注册方法向容器注册,然后通过手动刷新容器,使得容器对注册的注解Bean进行处理。
  2. 通过扫描指定的包及其子包下的所有类
    在初始化注解容器时指定要自动扫描的路径,如果容器创建以后向给定路径动态添加了注解Bean,则需要手动调用容器扫描的方法,然后手动刷新容器,使得容器对所注册的 Bean进行处理。

接下来,将会对两种处理方式详细分析其实现过程。

2 读取Annotation 元数据

当创建注解处理容器时,如果传入的初始参数是具体的注解Bean定义类时,注解容器读取并注册。

2.1 AnnotationConfigApplicationContext通过调用注解Bean定义读取器

AnnotatedBeanDefinitionReader的register()方法向容器注册指定的注解Bean ,注解Bean定义读取器向容器注册注解Bean的源码如下:

 //注册多个注解Bean定义类public void register(Class<?>... annotatedClasses) {for (Class<?> annotatedClass : annotatedClasses) {registerBean(annotatedClass);}}//注册一个注解Bean定义类public void registerBean(Class<?> annotatedClass) {doRegisterBean(annotatedClass, null, null, null);}public <T> void registerBean(Class<T> annotatedClass, @Nullable Supplier<T> instanceSupplier) {doRegisterBean(annotatedClass, instanceSupplier, null, null);}public <T> void registerBean(Class<T> annotatedClass, String name, @Nullable Supplier<T> instanceSupplier) {doRegisterBean(annotatedClass, instanceSupplier, name, null);}//Bean定义读取器注册注解Bean定义的入口方法@SuppressWarnings("unchecked")public void registerBean(Class<?> annotatedClass, Class<? extends Annotation>... qualifiers) {doRegisterBean(annotatedClass, null, null, qualifiers);}//Bean定义读取器向容器注册注解Bean定义类@SuppressWarnings("unchecked")public void registerBean(Class<?> annotatedClass, String name, Class<? extends Annotation>... qualifiers) {doRegisterBean(annotatedClass, null, name, qualifiers);}//Bean定义读取器向容器注册注解Bean定义类<T> void doRegisterBean(Class<T> annotatedClass, @Nullable Supplier<T> instanceSupplier, @Nullable String name,@Nullable Class<? extends Annotation>[] qualifiers, BeanDefinitionCustomizer... definitionCustomizers) {//根据指定的注解Bean定义类,创建Spring容器中对注解Bean的封装的数据结构AnnotatedGenericBeanDefinition abd = new AnnotatedGenericBeanDefinition(annotatedClass);if (this.conditionEvaluator.shouldSkip(abd.getMetadata())) {return;}abd.setInstanceSupplier(instanceSupplier);//========第一步===========================//解析注解Bean定义的作用域,若@Scope("prototype"),则Bean为原型类型;//若@Scope("singleton"),则Bean为单态类型ScopeMetadata scopeMetadata = this.scopeMetadataResolver.resolveScopeMetadata(abd);//为注解Bean定义设置作用域abd.setScope(scopeMetadata.getScopeName());//为注解Bean定义生成Bean名称String beanName = (name != null ? name : this.beanNameGenerator.generateBeanName(abd, this.registry));//========第二步===========================//处理注解Bean定义中的通用注解AnnotationConfigUtils.processCommonDefinitionAnnotations(abd);//如果在向容器注册注解Bean定义时,使用了额外的限定符注解,则解析限定符注解。//主要是配置的关于autowiring自动依赖注入装配的限定条件,即@Qualifier注解//Spring自动依赖注入装配默认是按类型装配,如果使用@Qualifier则按名称if (qualifiers != null) {for (Class<? extends Annotation> qualifier : qualifiers) {//如果配置了@Primary注解,设置该Bean为autowiring自动依赖注入装//配时的首选if (Primary.class == qualifier) {abd.setPrimary(true);}//如果配置了@Lazy注解,则设置该Bean为非延迟初始化,如果没有配置,//则该Bean为预实例化else if (Lazy.class == qualifier) {abd.setLazyInit(true);}//如果使用了除@Primary和@Lazy以外的其他注解,则为该Bean添加一//个autowiring自动依赖注入装配限定符,该Bean在进autowiring//自动依赖注入装配时,根据名称装配限定符指定的Beanelse {abd.addQualifier(new AutowireCandidateQualifier(qualifier));}}}for (BeanDefinitionCustomizer customizer : definitionCustomizers) {customizer.customize(abd);}//创建一个指定Bean名称的Bean定义对象,封装注解Bean定义类数据BeanDefinitionHolder definitionHolder = new BeanDefinitionHolder(abd, beanName);//========第三步===========================//根据注解Bean定义类中配置的作用域,创建相应的代理对象definitionHolder = AnnotationConfigUtils.applyScopedProxyMode(scopeMetadata, definitionHolder, this.registry);//========第四步===========================//向IOC容器注册注解Bean类定义对象BeanDefinitionReaderUtils.registerBeanDefinition(definitionHolder, this.registry);}

从上面的源码我们可以看出,注册注解Bean定义类的基本步骤︰
a、需要使用注解元数据解析器解析注解Bean中关于作用域的配置。

b、使用AnnotationConfigUtils的processCommonDefinitionAnnotations()方法处理注解Bean定义类中通用的注解。

c、使用AnnotationConfigUtils的applyScopedProxyMode()方法创建对于作用域的代理对象。

d、通过BeanDefinitionReaderUtils向容器注册 Bean。
我们对上面这四步的源码进行分析:

2.2 AnnotationScopeMetadataResolver解析作用域元数据

AnnotationScopeMetadataResolver通过resolveScopeMetadata()方法解析注解Bean定义类的作用域元信息,即判断注册的Bean是原生类型(prototype)还是单态(singleton)类型,其源码如下︰

 //解析注解Bean定义类中的作用域元信息@Overridepublic ScopeMetadata resolveScopeMetadata(BeanDefinition definition) {ScopeMetadata metadata = new ScopeMetadata();if (definition instanceof AnnotatedBeanDefinition) {AnnotatedBeanDefinition annDef = (AnnotatedBeanDefinition) definition;//从注解Bean定义类的属性中查找属性为”Scope”的值,即@Scope注解的值//AnnotationConfigUtils.attributesFor()方法将Bean//中所有的注解和注解的值存放在一个map集合中AnnotationAttributes attributes = AnnotationConfigUtils.attributesFor(annDef.getMetadata(), this.scopeAnnotationType);//将获取到的@Scope注解的值设置到要返回的对象中if (attributes != null) {metadata.setScopeName(attributes.getString("value"));//获取@Scope注解中的proxyMode属性值,在创建代理对象时会用到ScopedProxyMode proxyMode = attributes.getEnum("proxyMode");//如果@Scope的proxyMode属性为DEFAULT或者NOif (proxyMode == ScopedProxyMode.DEFAULT) {//设置proxyMode为NOproxyMode = this.defaultProxyMode;}//为返回的元数据设置proxyModemetadata.setScopedProxyMode(proxyMode);}}//返回解析的作用域元信息对象return metadata;}

上述代码中的annDef.getMetadata().getAnnotationAttributes()方法就是获取对象中指定类型的注解的值。

2.3 AnnotationConfigUtils处理注解Bean定义类中的通用注解

AnnotationConfigUtils类的processCommonDefinitionAnnotations()在向容器注册Bean之前,首先对注解Bean定义类中的通用Spring注解进行处理,源码如下:

 //处理Bean定义中通用注解static void processCommonDefinitionAnnotations(AnnotatedBeanDefinition abd, AnnotatedTypeMetadata metadata) {AnnotationAttributes lazy = attributesFor(metadata, Lazy.class);//如果Bean定义中有@Lazy注解,则将该Bean预实例化属性设置为@lazy注解的值if (lazy != null) {abd.setLazyInit(lazy.getBoolean("value"));}else if (abd.getMetadata() != metadata) {lazy = attributesFor(abd.getMetadata(), Lazy.class);if (lazy != null) {abd.setLazyInit(lazy.getBoolean("value"));}}//如果Bean定义中有@Primary注解,则为该Bean设置为autowiring自动依赖注入装配的首选对象if (metadata.isAnnotated(Primary.class.getName())) {abd.setPrimary(true);}//如果Bean定义中有@ DependsOn注解,则为该Bean设置所依赖的Bean名称,//容器将确保在实例化该Bean之前首先实例化所依赖的BeanAnnotationAttributes dependsOn = attributesFor(metadata, DependsOn.class);if (dependsOn != null) {abd.setDependsOn(dependsOn.getStringArray("value"));}if (abd instanceof AbstractBeanDefinition) {AbstractBeanDefinition absBd = (AbstractBeanDefinition) abd;AnnotationAttributes role = attributesFor(metadata, Role.class);if (role != null) {absBd.setRole(role.getNumber("value").intValue());}AnnotationAttributes description = attributesFor(metadata, Description.class);if (description != null) {absBd.setDescription(description.getString("value"));}}}

2.4 AnnotationConfigUtils根据注解 Bean定义类中配置的作用域为其应用相应的代理策略

AnnotationConfigUtils类的applyScopedProxyMode()方法根据注解Bean定义类中配置的作用域@Scope注解的值,为Bean定义应用相应的代理模式,主要是在Spring面向切面编程(AOP)中使用。
源码如下:

 //根据作用域为Bean应用引用的代码模式static BeanDefinitionHolder applyScopedProxyMode(ScopeMetadata metadata, BeanDefinitionHolder definition, BeanDefinitionRegistry registry) {//获取注解Bean定义类中@Scope注解的proxyMode属性值ScopedProxyMode scopedProxyMode = metadata.getScopedProxyMode();//如果配置的@Scope注解的proxyMode属性值为NO,则不应用代理模式if (scopedProxyMode.equals(ScopedProxyMode.NO)) {return definition;}//获取配置的@Scope注解的proxyMode属性值,如果为TARGET_CLASS//则返回true,如果为INTERFACES,则返回falseboolean proxyTargetClass = scopedProxyMode.equals(ScopedProxyMode.TARGET_CLASS);//为注册的Bean创建相应模式的代理对象return ScopedProxyCreator.createScopedProxy(definition, registry, proxyTargetClass);}

这段为Bean引用创建相应模式的代理,这里不做深入的分析。

2.5 BeanDefinitionReaderUtils向容器注册Bean

BeanDefinitionReaderUtils主要是校验BeanDefinition 信息,然后将Bean添加到容器中一个管理BeanDefinition的 HashMap 中。

3 扫描指定包并解析为BeanDefinition

当创建注解处理容器时,如果传入的初始参数是注解Bean定义类所在的包时,注解容器将扫描给定的包及其子包,将扫描到的注解Bean定义载入并注册。

3.1 ClassPathBeanDefinitionScanner扫描给定的包及其子包

AnnotationConfigApplicationContext通过调用类路径Bean定义扫描器ClassPathBeanDefinitionScanner扫描给定包及其子包下的所有类,主要源码如下∶

public class ClassPathBeanDefinitionScanner extends ClassPathScanningCandidateComponentProvider {//创建一个类路径Bean定义扫描器public ClassPathBeanDefinitionScanner(BeanDefinitionRegistry registry) {this(registry, true);}//为容器创建一个类路径Bean定义扫描器,并指定是否使用默认的扫描过滤规则。//即Spring默认扫描配置:@Component、@Repository、@Service、@Controller//注解的Bean,同时也支持JavaEE6的@ManagedBean和JSR-330的@Named注解public ClassPathBeanDefinitionScanner(BeanDefinitionRegistry registry, boolean useDefaultFilters) {this(registry, useDefaultFilters, getOrCreateEnvironment(registry));}public ClassPathBeanDefinitionScanner(BeanDefinitionRegistry registry, boolean useDefaultFilters,Environment environment) {this(registry, useDefaultFilters, environment,(registry instanceof ResourceLoader ? (ResourceLoader) registry : null));}public ClassPathBeanDefinitionScanner(BeanDefinitionRegistry registry, boolean useDefaultFilters,Environment environment, @Nullable ResourceLoader resourceLoader) {Assert.notNull(registry, "BeanDefinitionRegistry must not be null");//为容器设置加载Bean定义的注册器this.registry = registry;if (useDefaultFilters) {registerDefaultFilters();}setEnvironment(environment);//为容器设置资源加载器setResourceLoader(resourceLoader);}//调用类路径Bean定义扫描器入口方法public int scan(String... basePackages) {//获取容器中已经注册的Bean个数int beanCountAtScanStart = this.registry.getBeanDefinitionCount();//启动扫描器扫描给定包doScan(basePackages);// Register annotation config processors, if necessary.//注册注解配置(Annotation config)处理器if (this.includeAnnotationConfig) {AnnotationConfigUtils.registerAnnotationConfigProcessors(this.registry);}//返回注册的Bean个数return (this.registry.getBeanDefinitionCount() - beanCountAtScanStart);}//类路径Bean定义扫描器扫描给定包及其子包protected Set<BeanDefinitionHolder> doScan(String... basePackages) {Assert.notEmpty(basePackages, "At least one base package must be specified");//创建一个集合,存放扫描到Bean定义的封装类Set<BeanDefinitionHolder> beanDefinitions = new LinkedHashSet<>();//遍历扫描所有给定的包for (String basePackage : basePackages) {//调用父类ClassPathScanningCandidateComponentProvider的方法//扫描给定类路径,获取符合条件的Bean定义Set<BeanDefinition> candidates = findCandidateComponents(basePackage);//遍历扫描到的Beanfor (BeanDefinition candidate : candidates) {//获取Bean定义类中@Scope注解的值,即获取Bean的作用域ScopeMetadata scopeMetadata = this.scopeMetadataResolver.resolveScopeMetadata(candidate);//为Bean设置注解配置的作用域candidate.setScope(scopeMetadata.getScopeName());//为Bean生成名称String beanName = this.beanNameGenerator.generateBeanName(candidate, this.registry);//如果扫描到的Bean不是Spring的注解Bean,则为Bean设置默认值,//设置Bean的自动依赖注入装配属性等if (candidate instanceof AbstractBeanDefinition) {postProcessBeanDefinition((AbstractBeanDefinition) candidate, beanName);}//如果扫描到的Bean是Spring的注解Bean,则处理其通用的Spring注解if (candidate instanceof AnnotatedBeanDefinition) {//处理注解Bean中通用的注解,在分析注解Bean定义类读取器时已经分析过AnnotationConfigUtils.processCommonDefinitionAnnotations((AnnotatedBeanDefinition) candidate);}//根据Bean名称检查指定的Bean是否需要在容器中注册,或者在容器中冲突if (checkCandidate(beanName, candidate)) {BeanDefinitionHolder definitionHolder = new BeanDefinitionHolder(candidate, beanName);//根据注解中配置的作用域,为Bean应用相应的代理模式definitionHolder =AnnotationConfigUtils.applyScopedProxyMode(scopeMetadata, definitionHolder, this.registry);beanDefinitions.add(definitionHolder);//向容器注册扫描到的BeanregisterBeanDefinition(definitionHolder, this.registry);}}}return beanDefinitions;}
}

类路径Bean定义扫描器ClassPathBeanDefinitionScanner主要通过findCandidateComponents()方法调用其父类ClassPathScanningCandidateComponentProvider类来扫描获取给定包及其子包下的类。

3.2 ClassPathScanningCandidateComponentProvider扫描给定包及其子包的类

ClassPathScanningCandidateComponentProvider类的findCandidateComponents()方法具体实现扫描给定类路径包的功能,主要源码如下︰

public class ClassPathScanningCandidateComponentProvider implements EnvironmentCapable, ResourceLoaderAware {//保存过滤规则要包含的注解,即Spring默认的@Component、@Repository、@Service、//@Controller注解的Bean,以及JavaEE6的@ManagedBean和JSR-330的@Named注解private final List<TypeFilter> includeFilters = new LinkedList<>();//保存过滤规则要排除的注解private final List<TypeFilter> excludeFilters = new LinkedList<>();//构造方法,该方法在子类ClassPathBeanDefinitionScanner的构造方法中被调用public ClassPathScanningCandidateComponentProvider(boolean useDefaultFilters) {this(useDefaultFilters, new StandardEnvironment());}public ClassPathScanningCandidateComponentProvider(boolean useDefaultFilters, Environment environment) {//如果使用Spring默认的过滤规则,则向容器注册过滤规则if (useDefaultFilters) {registerDefaultFilters();}setEnvironment(environment);setResourceLoader(null);}//向容器注册过滤规则@SuppressWarnings("unchecked")protected void registerDefaultFilters() {//向要包含的过滤规则中添加@Component注解类,注意Spring中@Repository//@Service和@Controller都是Component,因为这些注解都添加了@Component注解this.includeFilters.add(new AnnotationTypeFilter(Component.class));//获取当前类的类加载器ClassLoader cl = ClassPathScanningCandidateComponentProvider.class.getClassLoader();try {//向要包含的过滤规则添加JavaEE6的@ManagedBean注解this.includeFilters.add(new AnnotationTypeFilter(((Class<? extends Annotation>) ClassUtils.forName("javax.annotation.ManagedBean", cl)), false));logger.debug("JSR-250 'javax.annotation.ManagedBean' found and supported for component scanning");}catch (ClassNotFoundException ex) {// JSR-250 1.1 API (as included in Java EE 6) not available - simply skip.}try {//向要包含的过滤规则添加@Named注解this.includeFilters.add(new AnnotationTypeFilter(((Class<? extends Annotation>) ClassUtils.forName("javax.inject.Named", cl)), false));logger.debug("JSR-330 'javax.inject.Named' annotation found and supported for component scanning");}catch (ClassNotFoundException ex) {// JSR-330 API not available - simply skip.}}//扫描给定类路径的包public Set<BeanDefinition> findCandidateComponents(String basePackage) {if (this.componentsIndex != null && indexSupportsIncludeFilters()) {return addCandidateComponentsFromIndex(this.componentsIndex, basePackage);}else {return scanCandidateComponents(basePackage);}}private Set<BeanDefinition> addCandidateComponentsFromIndex(CandidateComponentsIndex index, String basePackage) {//创建存储扫描到的类的集合Set<BeanDefinition> candidates = new LinkedHashSet<>();try {Set<String> types = new HashSet<>();for (TypeFilter filter : this.includeFilters) {String stereotype = extractStereotype(filter);if (stereotype == null) {throw new IllegalArgumentException("Failed to extract stereotype from "+ filter);}types.addAll(index.getCandidateTypes(basePackage, stereotype));}boolean traceEnabled = logger.isTraceEnabled();boolean debugEnabled = logger.isDebugEnabled();for (String type : types) {//为指定资源获取元数据读取器,元信息读取器通过汇编(ASM)读//取资源元信息MetadataReader metadataReader = getMetadataReaderFactory().getMetadataReader(type);//如果扫描到的类符合容器配置的过滤规则if (isCandidateComponent(metadataReader)) {//通过汇编(ASM)读取资源字节码中的Bean定义元信息AnnotatedGenericBeanDefinition sbd = new AnnotatedGenericBeanDefinition(metadataReader.getAnnotationMetadata());if (isCandidateComponent(sbd)) {if (debugEnabled) {logger.debug("Using candidate component class from index: " + type);}candidates.add(sbd);}else {if (debugEnabled) {logger.debug("Ignored because not a concrete top-level class: " + type);}}}else {if (traceEnabled) {logger.trace("Ignored because matching an exclude filter: " + type);}}}}catch (IOException ex) {throw new BeanDefinitionStoreException("I/O failure during classpath scanning", ex);}return candidates;}//判断元信息读取器读取的类是否符合容器定义的注解过滤规则protected boolean isCandidateComponent(MetadataReader metadataReader) throws IOException {//如果读取的类的注解在排除注解过滤规则中,返回falsefor (TypeFilter tf : this.excludeFilters) {if (tf.match(metadataReader, getMetadataReaderFactory())) {return false;}}//如果读取的类的注解在包含的注解的过滤规则中,则返回turefor (TypeFilter tf : this.includeFilters) {if (tf.match(metadataReader, getMetadataReaderFactory())) {return isConditionMatch(metadataReader);}}//如果读取的类的注解既不在排除规则,也不在包含规则中,则返回falsereturn false;}
}

4 注册注解BeanDefinition

AnnotationConfigWebApplicationContext是AnnotationConfigApplicationContext的 Web版,它们对于注解Bean的注册和扫描是基本相同的,但是AnnotationConfigWebApplicationContext对注解Bean定义的载入稍有不同,AnnotationConfigWebApplicationContext注入注解Bean定义:

 //载入注解Bean定义资源@Overrideprotected void loadBeanDefinitions(DefaultListableBeanFactory beanFactory) {//为容器设置注解Bean定义读取器AnnotatedBeanDefinitionReader reader = getAnnotatedBeanDefinitionReader(beanFactory);//为容器设置类路径Bean定义扫描器ClassPathBeanDefinitionScanner scanner = getClassPathBeanDefinitionScanner(beanFactory);//获取容器的Bean名称生成器BeanNameGenerator beanNameGenerator = getBeanNameGenerator();//为注解Bean定义读取器和类路径扫描器设置Bean名称生成器if (beanNameGenerator != null) {reader.setBeanNameGenerator(beanNameGenerator);scanner.setBeanNameGenerator(beanNameGenerator);beanFactory.registerSingleton(AnnotationConfigUtils.CONFIGURATION_BEAN_NAME_GENERATOR, beanNameGenerator);}//获取容器的作用域元信息解析器ScopeMetadataResolver scopeMetadataResolver = getScopeMetadataResolver();//为注解Bean定义读取器和类路径扫描器设置作用域元信息解析器if (scopeMetadataResolver != null) {reader.setScopeMetadataResolver(scopeMetadataResolver);scanner.setScopeMetadataResolver(scopeMetadataResolver);}if (!this.annotatedClasses.isEmpty()) {if (logger.isInfoEnabled()) {logger.info("Registering annotated classes: [" +StringUtils.collectionToCommaDelimitedString(this.annotatedClasses) + "]");}reader.register(this.annotatedClasses.toArray(new Class<?>[this.annotatedClasses.size()]));}if (!this.basePackages.isEmpty()) {if (logger.isInfoEnabled()) {logger.info("Scanning base packages: [" +StringUtils.collectionToCommaDelimitedString(this.basePackages) + "]");}scanner.scan(this.basePackages.toArray(new String[this.basePackages.size()]));}//获取容器定义的Bean定义资源路径String[] configLocations = getConfigLocations();//如果定位的Bean定义资源路径不为空if (configLocations != null) {for (String configLocation : configLocations) {try {//使用当前容器的类加载器加载定位路径的字节码类文件Class<?> clazz = ClassUtils.forName(configLocation, getClassLoader());if (logger.isInfoEnabled()) {logger.info("Successfully resolved class for [" + configLocation + "]");}reader.register(clazz);}catch (ClassNotFoundException ex) {if (logger.isDebugEnabled()) {logger.debug("Could not load class for config location [" + configLocation +"] - trying package scan. " + ex);}//如果容器类加载器加载定义路径的Bean定义资源失败//则启用容器类路径扫描器扫描给定路径包及其子包中的类int count = scanner.scan(configLocation);if (logger.isInfoEnabled()) {if (count == 0) {logger.info("No annotated classes found for specified class/package [" + configLocation + "]");}else {logger.info("Found " + count + " annotated classes in package [" + configLocation + "]");}}}}}}

以上就是解析和注入注解配置资源的全过程分析。
至此,结束

从Spring源码探究IOC初始化流程相关推荐

  1. 从Spring源码探究DI属性注入流程

    随着不断地使用Spring,以及后续的Boot.cloud,不断的体会到这个拯救Java的生态体系的强大,也使我对于这个框架有了极大的好奇心,以至于产生了我为什么不能写一个这样的框架的思考. 通过自学 ...

  2. 从Spring源码探究AOP代码织入的过程

    随着不断地使用Spring,以及后续的Boot.cloud,不断的体会到这个拯救Java的生态体系的强大,也使我对于这个框架有了极大的好奇心,以至于产生了我为什么不能写一个这样的框架的思考. 通过自学 ...

  3. 手撸spring源码分析IOC实现原理

    手撸spring源码分析IOC实现原理 文章出处:https://github.com/fuzhengwei/small-spring 根据小付哥的手撸spring核心源码一步步学习出来的结果收货总结 ...

  4. Spring源码——声明式事务流程

    前言 最近回顾了一下Spring源码,准备用思维导图的方式简单的将整个源码内容的流程展示出来,思维导图.图片等文件更新在https://github.com/MrSorrow/spring-frame ...

  5. Spring源码(十)-IOC终结篇

    前言 不知不觉,Spring源码已经写了9篇,这一章就画上一个句号了,IOC这块就暂时结束.这一篇就之前的内容做些总结,主要写一些大致的流程. [详细参考]bean的初始化 1.bean的生命周期 1 ...

  6. Spring源码——动态AOP实现流程

    前言 最近回顾了一下Spring源码,准备用思维导图的方式简单的将整个源码内容的流程展示出来,思维导图.图片等文件更新在https://github.com/MrSorrow/spring-frame ...

  7. 简述控制反转ioc_阅读Spring源码:IOC控制反转前的处理

    温馨提示:要怀着 这个世界很美好 的心态去看~ 技术经验交流:点击入群 ClassPathXmlApplicationContext的注册方式 源码分析基于Spring4.3 从ClassPathXm ...

  8. Spring源码分析——IOC容器

    1.IOC容器的概念 理解IOC容器的概念之前首先需要了解依赖翻转(又称依赖倒置)的概念 许多复杂的应用都是通过多个类之间的彼此合作实现业务逻辑的,这使得每个对象都需要管理自己与其合作对象的依赖,而如 ...

  9. Spring源码 (事务篇) - 整体流程

    1.解析配置文件 1.1 .解析配置文件的阶段 处理配置文件是在 refresh => invokeBeanFactoryPostProcessors ,即在调用 BeanFactory后置处理 ...

最新文章

  1. 人工智能市场兴起,哪款聊天机器人能提供最好的投资回报?
  2. 循环训练_力量循环训练
  3. webshpere MQ linux 上安装
  4. java file源码_java File源码理解,探索File路径
  5. commons-lang3工具类学习(一)
  6. 提高Java开发效率,Idea必装的几款插件
  7. RabbitMQ学习总结(1)——基础概念详细介绍
  8. Kubernetes-基于EFK进行统一的日志管理原理(kibana查询语法)
  9. margin-left:10px; 不同浏览器距离为什么不一样?
  10. UNIX 操作系统体系结构调整
  11. ad怎么导入cad的外形尺寸_CAD文件导入AD09
  12. java笔试题大全_java笔试题大全带答案(经典11题)
  13. 一名Android程序员的自我修养
  14. 别说 Python 会生成二维码,Java也会。
  15. 吊打面试官系列之:UI自动化面试题汇总,对标P7,从此再也不怕面试官了。
  16. 7 轮面试后,还是挂了 | Google 中国面经分享
  17. 保护FTP和SFTP服务器的10个基本技巧
  18. Oracle同一字段有数字和字母时进行排序
  19. 阿里技术人才能力模型(深度解读系列之一)
  20. 转载:香港实习生微软实习经验分享

热门文章

  1. 《菩萨蛮·隔花才歇帘纤雨》
  2. 如何开启全新旅途,实现旅游市场活力复苏
  3. tableau用数据分组_对数据进行分组
  4. 低客单价的商品怎样做运营、怎么样做优化、以及怎样做推广
  5. 计算机网络共享名称乱码,电脑wifi名称显示乱码怎么回事
  6. 十沣科技TF-Dyna不亚于国际主流商业软件 应用领域广泛
  7. 保弘实业|理财要有合理的计划
  8. Kettle之Carte最佳实践
  9. 看《大话西游》,你哭了么
  10. 【Linux】用进程控制知识做一个简易版shell