这里写目录标题
- 前言
- 一、Spring扫描-@ComponentScan注解介绍
- @ComponentScan作用
- @ComponentScan重要参数
- 二、Spring扫描-源码分析
- 声明关键点
- 源代码解读
- Spring扫描流程图
前言
先不废话了,直接干吧。
一、Spring扫描-@ComponentScan注解介绍
@ComponentScan作用
@ComponentScan注解的作用可以简述为:将项目中所有被@Component
注解直接或者间接标记的类---->组装成BeanDefinition
---->然后以key=beanName, value=BeanDefinition
的形式存储,为后续生成bean对象做准备。
提示:被@Component注解标记的类分为两种情况,这两种情况的类都会被Spring成功扫描。
1、该类直接被@Component注解标记
@Component
public class UserService {}
2、该类间接被@Component注解标记(@Configuration组成注解包含@Component注解)
@Configuration
public class ProConfig {}
--
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Component
public @interface Configuration {
@AliasFor(
annotation = Component.class
)
String value() default "";
boolean proxyBeanMethods() default true;
}
上面是说的通过扫描得到BeanDefinition对象,我们还可以通过直接定义BeanDefinition,或解析spring.xml文件的<bean/ >,或者 @Bean注解得到BeanDefinition对象。(后续章节会分析@Bean注解是怎么生成BeanDefinition的)。IOC容器创建BeanDefinition的一些常见方式
@ComponentScan重要参数
@ComponentScan中有多个参数,其中尤为重要的几个参数如下:
- value:用来指定basePackage的路径;
- excludeFilters:可以把被@Component标识的类
排除
扫描; - includeFilters:可以把不被@Component标识的类加入到扫描;
示例:
@ComponentScan(value = "com.zhouyu",
excludeFilters = {@ComponentScan.Filter(type = FilterType.ASSIGNABLE_TYPE,classes = UserService.class)},
includeFilters = {@ComponentScan.Filter(type = FilterType.ASSIGNABLE_TYPE,classes = User.class)})
二、Spring扫描-源码分析
声明关键点
在讲源码之前呢,我们可以先增强几个知识点的概念。这些知识点在阅读Spring扫描源码中起着关键作用。
1、@ComponentScan只会扫描被 @Component标识的类
。 诸如@Bean、spring.xml文件的< bean/>标签、编程式声明BeanDefinition等,都不会被@ComponentScan扫描;
2、@ComponentScan的excludeFilters
和includeFilters
参数影响着类是否成功被扫描。excludeFilters中指定了一个带有@Component注解的类,该类也不会被扫描;includeFilters中指定了一个不带有@Component注解的类,该类也会被扫描;
3、被@ComponentScan扫描注入到IOC容器中的BeanDefinition,其具体实现类是:ScannedGenericBeanDefinition
。
4、@ComponentScan扫描涉及到的相关注解:@Conditional
和@Scope
和@Lookup
和@Lazy
、@Primary
、@DependsOn
、@Role
、@Description
。
- @Conditional的作用是:即使该类被@Component修饰,但是Conditional返回false,该类也不会被成功扫描;
- @Scope的作用是:仅仅作为一个标识,赋值给BeanDefinition的scope属性;
- @Lookup:抽象类本身不能被扫描进容器,但是被@Lookup注解修饰则可以;
- @Lazy、@Primary、@DependsOn、@Role:这几个类是一起被处理的,也只是用来给BeanDefinition赋值,分别对应着:setLazyInit(boolean)、setPrimary(boolean)、setDependsOn(value)、setRole(value)、setDescription(value);
5、须知Resource
类和MetadataReader
类。
- Resource[]数组对象会存储basePackage包路径下所有的class的文件对象(注意:是所有的class的文件对象都会存储,无论有没有被@Component修饰);
- MetadataReader类是Spring用来解析类的信息(比如类名、类中的方法、类上的注解、是否是抽象类…,这些都可以称之为类的元数据)的一个工具类。MetadataReader、ClassMetadata、AnnotationMetadata都可以用来解析类信息;
6、@ComponentScan扫描最终生成两个map:Map<String, BeanDefinition> beanDefinitionMap
与Map<String, String> aliasMap
。
- beanDefinitionMap用于存储BeanDefinition,key是beanName,value是BeanDefinition;
- aliasMap用于存储bean的别名,key是别名,value是beanName。@Component没有设置bean别名功能,@Bean可以设置别名。
@ComponentScan("org.example")
public class AppConfig {
/**
* 声明:
* 如果@Bean没有设置value参数,那么beanName = "createBookService";
* 如果@Bean设置了多个value参数,那么beanName = array[0]
*/
@Bean({"bookService","bookService2"})
public BookService createBookService(){
return new BookService();
}
}
7、其他:
-
ASM技术
:为什么要使用ASM技术,Spring启动的时候需要去扫描,如果指定的包路径比较宽泛,那么扫描的类是非常多的,那如果在Spring启动时就把这些类全部加载进JVM了,这样不太好,所以使用了ASM技术;
源代码解读
源码具体位置:org.springframework.context.annotation.ClassPathBeanDefinitionScanner#doScan
protected Set<BeanDefinitionHolder> doScan(String... basePackages) {
Assert.notEmpty(basePackages, "At least one base package must be specified");
Set<BeanDefinitionHolder> beanDefinitions = new LinkedHashSet<>();
// 遍历basePackages
for (String basePackage : basePackages) {
// 扫描basePackages下所有的文件,进行include、exclude、@Conditional判断后返回BeanDefinition
Set<BeanDefinition> candidates = findCandidateComponents(basePackage);
for (BeanDefinition candidate : candidates) {
ScopeMetadata scopeMetadata = this.scopeMetadataResolver.resolveScopeMetadata(candidate); // scope解析:单例与多例
candidate.setScope(scopeMetadata.getScopeName());
String beanName = this.beanNameGenerator.generateBeanName(candidate, this.registry); // 生成beanName:默认是类名首字母小写,如果@Component中指定了name则使用指定的name
/* ScannedGenericBeanDefinition extends GenericBeanDefinition(AbstractBeanDefinition) implements AnnotatedBeanDefinition */
// 设置BeanDefinition的默认值
if (candidate instanceof AbstractBeanDefinition) {
postProcessBeanDefinition((AbstractBeanDefinition) candidate, beanName);
}
if (candidate instanceof AnnotatedBeanDefinition) {
// 解析@Lazy、@Primary、@DependsOn、@Role、@Description
AnnotationConfigUtils.processCommonDefinitionAnnotations((AnnotatedBeanDefinition) candidate);
}
// 检查Spring容器中是否已经存在该beanName(如果存在则抛出异常)
if (checkCandidate(beanName, candidate)) {
BeanDefinitionHolder definitionHolder = new BeanDefinitionHolder(candidate, beanName);
definitionHolder =
AnnotationConfigUtils.applyScopedProxyMode(scopeMetadata, definitionHolder, this.registry);
beanDefinitions.add(definitionHolder);
// 注册: beanDefinitionMap.put(beanName, beanDefinition)
registerBeanDefinition(definitionHolder, this.registry);
}
}
}
return beanDefinitions;
}
Spring扫描流程图