文章目录
前言
Spring 中通过不同的bean 负责不同的功能,那么spring 在加载的时候,怎么知道哪些类需要去生成bean?
一、ComponentScan 扫描路径定义:
在spring 的启动类中可以添加 @ComponentScan 并且 定义扫描的bean路径,从而在spring 启动的时候,可以知晓从哪些地方的类需要去生成bean 。
@ComponentScan(basePackages = "com.xxxx")
main
public static void main(String[] args) {
ConfigurableApplicationContext context = new AnnotationConfigApplicationContext(MyConfig.class);
context.getBean("xxxx");
}
二、包路径扫描:
2.1 AnnotationConfigApplicationContext:
public AnnotationConfigApplicationContext(Class<?>... componentClasses) {
this(); // reader 和 scan 初始化
this.register(componentClasses);
this.refresh();// bean 的生成
}
2.1.1 reader 和 scan 初始化:
public AnnotationConfigApplicationContext() {
StartupStep createAnnotatedBeanDefReader = this.getApplicationStartup().start("spring.context.annotated-bean-reader.create");
// 定义BeanDefinitionReader
this.reader = new AnnotatedBeanDefinitionReader(this);
createAnnotatedBeanDefReader.end();
// 定义BeanDefinitionScanner 这里会把 Component.class 注解类默认放入到 includeFilters的 TypeFilter中
this.scanner = new ClassPathBeanDefinitionScanner(this);
}
2.2 包扫描:
通过 ClassPathBeanDefinitionScanner scan 方法,为每个要生成bean 的类 生成BeanDefinition,后面在创建bean 的时候可以从中获取类的注解,属性 ,方法等。
2.2.1 scan 扫描:
public int scan(String... basePackages) {
int beanCountAtScanStart = this.registry.getBeanDefinitionCount();
this.doScan(basePackages);// 做扫描工作
if (this.includeAnnotationConfig) {
AnnotationConfigUtils.registerAnnotationConfigProcessors(this.registry);
}
// 返回本次扫描了多少个BeanDefinition
return this.registry.getBeanDefinitionCount() - beanCountAtScanStart;
}
2.2.2 doScan 扫描:
protected Set<BeanDefinitionHolder> doScan(String... basePackages) {
Assert.notEmpty(basePackages, "At least one base package must be specified");
Set<BeanDefinitionHolder> beanDefinitions = new LinkedHashSet();
String[] var3 = basePackages;
int var4 = basePackages.length;
// 遍历每个包路径
for(int var5 = 0; var5 < var4; ++var5) {
String basePackage = var3[var5];
// 遍历获取每个路径下的 BeanDefinition
Set<BeanDefinition> candidates = this.findCandidateComponents(basePackage);
Iterator var8 = candidates.iterator();
while(var8.hasNext()) {
BeanDefinition candidate = (BeanDefinition)var8.next();
// 获取 这个类的 scope
ScopeMetadata scopeMetadata = this.scopeMetadataResolver.resolveScopeMetadata(candidate);
candidate.setScope(scopeMetadata.getScopeName());
// 获取 这个类的 bean 名称 如果 @Component 中定义了value 值则获取改制 否则按照默认的规则生成bean name
// 即 类的第一个字母小写.但是如果 类的名字第一个和
// 第二个字母都是大写,则直接使用,生成的bean 的首字母和第二个字母都是大写
String beanName = this.beanNameGenerator.generateBeanName(candidate, this.registry);
if (candidate instanceof AbstractBeanDefinition) {
// BeanDefinition 赋值默认值(是否懒加载等)
this.postProcessBeanDefinition((AbstractBeanDefinition)candidate, beanName);
}
if (candidate instanceof AnnotatedBeanDefinition) {
// 处理 @Lazy @Primary,@DependsOn @Role @Description等注解
AnnotationConfigUtils.processCommonDefinitionAnnotations((AnnotatedBeanDefinition)candidate);
}
// 如果改bean 的 BeanDefinition 已经存在大部分会抛异常出来
if (this.checkCandidate(beanName, candidate)) {
// 检查spring 中是否已经有改beanName 如果没有存在则进行注册
BeanDefinitionHolder definitionHolder = new BeanDefinitionHolder(candidate, beanName);
definitionHolder = AnnotationConfigUtils.applyScopedProxyMode(scopeMetadata, definitionHolder, this.registry);
beanDefinitions.add(definitionHolder);
// 注册最终放入到 DefaultListableBeanFactory 的 Map<String, BeanDefinition> beanDefinitionMap; 中
this.registerBeanDefinition(definitionHolder, this.registry);
}
}
}
return beanDefinitions;
}
2.2.3 findCandidateComponents 获取BeanDefinition :
public Set<BeanDefinition> findCandidateComponents(String basePackage) {
return this.componentsIndex != null && this.indexSupportsIncludeFilters() ? this.addCandidateComponentsFromIndex(this.componentsIndex, basePackage) : this.scanCandidateComponents(basePackage);
}
scanCandidateComponents: 扫描 @Component
private Set<BeanDefinition> scanCandidateComponents(String basePackage) {
// BeanDefinition set 集合将扫描到有 @Component 注解标注并且符合条件的包装成BeanDefinition
Set<BeanDefinition> candidates = new LinkedHashSet();
try {
// 定义class 的资源路径
String packageSearchPath = "classpath*:" + this.resolveBasePackage(basePackage) + '/' + this.resourcePattern;
// 获取包路径下的 file 资源
Resource[] resources = this.getResourcePatternResolver().getResources(packageSearchPath);
boolean traceEnabled = this.logger.isTraceEnabled();
boolean debugEnabled = this.logger.isDebugEnabled();
Resource[] var7 = resources;
int var8 = resources.length;
for(int var9 = 0; var9 < var8; ++var9) {
// 获取到每个file 资源
Resource resource = var7[var9];
if (traceEnabled) {
this.logger.trace("Scanning " + resource);
}
try {
// 获取改资源的 类元数据信息 (注解,属性,方法等)
MetadataReader metadataReader = this.getMetadataReaderFactory().getMetadataReader(resource);
if (this.isCandidateComponent(metadataReader)) {
// 符合生成bean 的条件,构造BeanDefinition
ScannedGenericBeanDefinition sbd = new ScannedGenericBeanDefinition(metadataReader);
sbd.setSource(resource);
if (this.isCandidateComponent((AnnotatedBeanDefinition)sbd)) {
if (debugEnabled) {
this.logger.debug("Identified candidate component class: " + resource);
}
// 加入 bean定义的set 集合
candidates.add(sbd);
} else if (debugEnabled) {
this.logger.debug("Ignored because not a concrete top-level class: " + resource);
}
} else if (traceEnabled) {
this.logger.trace("Ignored because not matching any filter: " + resource);
}
} catch (FileNotFoundException var13) {
if (traceEnabled) {
this.logger.trace("Ignored non-readable " + resource + ": " + var13.getMessage());
}
} catch (Throwable var14) {
throw new BeanDefinitionStoreException("Failed to read candidate component class: " + resource, var14);
}
}
return candidates;
} catch (IOException var15) {
throw new BeanDefinitionStoreException("I/O failure during classpath scanning", var15);
}
}
isCandidateComponent 是否符合bean 的条件: 这个类不是内部类,也不是接口 ,或者改类是抽象类但是没有定义 Lookup 则可以满足生成bean 的条件
protected boolean isCandidateComponent(MetadataReader metadataReader) throws IOException {
// 如果 类中定义 了排除 ,则进行匹配,如果该类在排除的范围之内则直接返回false
Iterator var2 = this.excludeFilters.iterator();
TypeFilter tf;
do {
if (!var2.hasNext()) {
// 如果没有被排除则,查看是否在 包含 filter 之内,如果都不包含也返回false
// 在容器准备阶段已经定义了一个 默认的 的includeFilter ,它就是 @Component
var2 = this.includeFilters.iterator();
do {
if (!var2.hasNext()) {
return false;
}
tf = (TypeFilter)var2.next();
} while(!tf.match(metadataReader, this.getMetadataReaderFactory()));
// 类中有@Component 注解,在进行条件匹配
return this.isConditionMatch(metadataReader);
}
tf = (TypeFilter)var2.next();
} while(!tf.match(metadataReader, this.getMetadataReaderFactory()));
return false;
}
是否条件匹配 isConditionMatch:得到true 则意味这个类需要生成baen, 得到false 则不需要生成bean
this.isConditionMatch(metadataReader);
private boolean isConditionMatch(MetadataReader metadataReader) {
if (this.conditionEvaluator == null) {
this.conditionEvaluator = new ConditionEvaluator(this.getRegistry(), this.environment, this.resourcePatternResolver);
}
return !this.conditionEvaluator.shouldSkip(metadataReader.getAnnotationMetadata());
}
public boolean shouldSkip(@Nullable AnnotatedTypeMetadata metadata, @Nullable ConfigurationCondition.ConfigurationPhase phase) {
if (metadata != null && metadata.isAnnotated(Conditional.class.getName())) {
if (phase == null) {
return metadata instanceof AnnotationMetadata && ConfigurationClassUtils.isConfigurationCandidate((AnnotationMetadata)metadata) ? this.shouldSkip(metadata, ConfigurationPhase.PARSE_CONFIGURATION) : this.shouldSkip(metadata, ConfigurationPhase.REGISTER_BEAN);
} else {
// 调用业务本身,改类 实现了Condition 接口 的matches 方法,如果得到true 则意味这个类需要生成baen
// 得到false 则不需要生成bean
List<Condition> conditions = new ArrayList();
Iterator var4 = this.getConditionClasses(metadata).iterator();
while(var4.hasNext()) {
String[] conditionClasses = (String[])var4.next();
String[] var6 = conditionClasses;
int var7 = conditionClasses.length;
for(int var8 = 0; var8 < var7; ++var8) {
String conditionClass = var6[var8];
Condition condition = this.getCondition(conditionClass, this.context.getClassLoader());
conditions.add(condition);
}
}
AnnotationAwareOrderComparator.sort(conditions);
var4 = conditions.iterator();
Condition condition;
ConfigurationCondition.ConfigurationPhase requiredPhase;
do {
do {
if (!var4.hasNext()) {
return false;
}
condition = (Condition)var4.next();
requiredPhase = null;
if (condition instanceof ConfigurationCondition) {
requiredPhase = ((ConfigurationCondition)condition).getConfigurationPhase();
}
} while(requiredPhase != null && requiredPhase != phase);
} while(condition.matches(this.context, metadata));
return true;
}
} else {
// 类上没有条件注解则直接 返回false ,意味着 这个类需要生成bean
return false;
}
}
三、扩展:
3.1 条件Condition :
Spring中的Condition接口是用于在runtime时决定一个bean或者某配置是否需要被创建或加载的。你可以使用@Conditional注解,并实现Condition接口,然后通过Condition#matches()方法来定义条件。
例如:
public class OnSystemPropertyCondition implements Condition {
@Override
public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
return.getProperty("my.property") != null;
}
}
在此例中,如果系统属性"my.property"存在,那么条件就满足,否则条件不满足。
然后你可以使用@Conditional注解来利用这个Condition:
@Configuration
@Conditional(OnSystemPropertyCondition.class)
public class ConditionConfig {
// some beans definitions...
}
在上面的例子中,只有当"my.property"系统属性存在时,ConditionConfig的定义和它下面的Bean才会被创建和注册到Spring的ApplicationContext中。
Condition提供了很好的方式来控制Bean的创建和配置的加载,使其可以适应不同的环境和条件。
3.2 Lookup 注解 :
Spring的@Lookup注解是用来覆盖在Spring IoC容器中单例Bean的某些方法,使它们每次被调用时都返回一个新的原型Bean(prototype bean)实例。
在某些情况下,可能需要在一个单例Bean中维护一些原型Bean的引用。但是,因为单例Bean在器启动时只被创建一次,它所持有的原型Bean也只会在那时创建一次。这可能会导致问题,因为通常我们期望每次请求原型Bean时,容器都能提供一个新的实例。
这时,我们就可以使用@Lookup注解来解决这个问题。@Lookup注解让我们可以定义一个返回特定类型的方法,并在需要时覆盖这个方法,使得每次调用这个方法时容器都会返回一个新的Bean实例。
@Service
public abstract class SingletonService {
// ...
@Lookup
public abstract PrototypeBean createPrototypeBean();
public void doSomething() {
PrototypeBean prototypeBean = createPrototypeBean();
// 处理prototypeBean
}
}
在上面的例子中,SingletonService是一个单例Bean,其中定义了一个用@Lookup注解标注的方法createPrototypeBean。每次调用这个方法,Spring都会为我们返回一个新的PrototypeBean实例。
总结
spring 通过sanner 对其传入的包路径进行扫描,遍历每个路径,获取其路径下的所以file 资源,筛选出有@Component 标注 并且非接接口 非抽象类,没有条件注解(或者条件注解返回true)的类,为其设置name ,填充socpe,lazy,Primary,DependsOn,Description 等属性包装生成BeanDefinition 最终放入到beanDefinitionMap ,供后面生成bean使用。