|
|
# Spring scan
|
|
|
|
|
|
- Author: [HuiFer](https://github.com/huifer)
|
|
|
- 源码阅读仓库: [SourceHot-Spring](https://github.com/SourceHot/spring-framework-read)
|
|
|
|
|
|
## 解析
|
|
|
|
|
|
- Spring 注解形式使用有下面两种方式
|
|
|
1. 通过`AnnotationConfigApplicationContext`参数:扫描包
|
|
|
2. 通过 xml 配置`context:component-scan`属性`base-package`
|
|
|
|
|
|
```java
|
|
|
AnnotationConfigApplicationContext aac =
|
|
|
new AnnotationConfigApplicationContext("com.huifer.source.spring.ann");
|
|
|
```
|
|
|
|
|
|
```xml
|
|
|
<context:component-scan base-package="com.huifer.source.spring.ann">
|
|
|
</context:component-scan>
|
|
|
```
|
|
|
|
|
|
- 目标明确开始找入口方法
|
|
|
- `AnnotationConfigApplicationContext`直接点进去看就找到了
|
|
|
|
|
|
```java
|
|
|
public AnnotationConfigApplicationContext(String... basePackages) {
|
|
|
this();
|
|
|
// 扫描包
|
|
|
scan(basePackages);
|
|
|
refresh();
|
|
|
}
|
|
|
```
|
|
|
|
|
|
- `context:component-scan`寻找方式:冒号`:`钱+NamespaceHandler 或者全文搜索`component-scan`,最终找到`org.springframework.context.config.ContextNamespaceHandler`
|
|
|
|
|
|
```java
|
|
|
public class ContextNamespaceHandler extends NamespaceHandlerSupport {
|
|
|
|
|
|
@Override
|
|
|
public void init() {
|
|
|
registerBeanDefinitionParser("property-placeholder", new PropertyPlaceholderBeanDefinitionParser());
|
|
|
registerBeanDefinitionParser("property-override", new PropertyOverrideBeanDefinitionParser());
|
|
|
registerBeanDefinitionParser("annotation-config", new AnnotationConfigBeanDefinitionParser());
|
|
|
registerBeanDefinitionParser("component-scan", new ComponentScanBeanDefinitionParser());
|
|
|
registerBeanDefinitionParser("load-time-weaver", new LoadTimeWeaverBeanDefinitionParser());
|
|
|
registerBeanDefinitionParser("spring-configured", new SpringConfiguredBeanDefinitionParser());
|
|
|
registerBeanDefinitionParser("mbean-export", new MBeanExportBeanDefinitionParser());
|
|
|
registerBeanDefinitionParser("mbean-server", new MBeanServerBeanDefinitionParser());
|
|
|
}
|
|
|
|
|
|
}
|
|
|
```
|
|
|
|
|
|
### org.springframework.context.annotation.ComponentScanBeanDefinitionParser
|
|
|
|
|
|
![image-20200115093602651](../../../images/spring/image-20200115093602651.png)
|
|
|
|
|
|
- 实现`BeanDefinitionParser`直接看`parse`方法
|
|
|
|
|
|
```java
|
|
|
@Override
|
|
|
@Nullable
|
|
|
public BeanDefinition parse(Element element, ParserContext parserContext) {
|
|
|
// 获取 base-package 属性值
|
|
|
String basePackage = element.getAttribute(BASE_PACKAGE_ATTRIBUTE);
|
|
|
// 处理 ${}
|
|
|
basePackage = parserContext.getReaderContext().getEnvironment().resolvePlaceholders(basePackage);
|
|
|
// 分隔符`,;\t\n`切分
|
|
|
String[] basePackages = StringUtils.tokenizeToStringArray(basePackage,
|
|
|
ConfigurableApplicationContext.CONFIG_LOCATION_DELIMITERS);
|
|
|
|
|
|
// Actually scan for bean definitions and register them.
|
|
|
// 扫描对象创建
|
|
|
ClassPathBeanDefinitionScanner scanner = configureScanner(parserContext, element);
|
|
|
// 执行扫描方法
|
|
|
Set<BeanDefinitionHolder> beanDefinitions = scanner.doScan(basePackages);
|
|
|
// 注册组件,触发监听
|
|
|
registerComponents(parserContext.getReaderContext(), beanDefinitions, element);
|
|
|
|
|
|
return null;
|
|
|
}
|
|
|
|
|
|
```
|
|
|
|
|
|
- 回过头看`AnnotationConfigApplicationContext`
|
|
|
|
|
|
### org.springframework.context.annotation.AnnotationConfigApplicationContext
|
|
|
|
|
|
```java
|
|
|
public AnnotationConfigApplicationContext(String... basePackages) {
|
|
|
this();
|
|
|
// 扫描包
|
|
|
scan(basePackages);
|
|
|
refresh();
|
|
|
}
|
|
|
```
|
|
|
|
|
|
```java
|
|
|
private final ClassPathBeanDefinitionScanner scanner;
|
|
|
|
|
|
@Override
|
|
|
public void scan(String... basePackages) {
|
|
|
Assert.notEmpty(basePackages, "At least one base package must be specified");
|
|
|
this.scanner.scan(basePackages);
|
|
|
}
|
|
|
|
|
|
```
|
|
|
|
|
|
- `org.springframework.context.annotation.ClassPathBeanDefinitionScanner.scan`
|
|
|
|
|
|
```java
|
|
|
public int scan(String... basePackages) {
|
|
|
|
|
|
// 获取bean数量
|
|
|
int beanCountAtScanStart = this.registry.getBeanDefinitionCount();
|
|
|
// 执行扫描
|
|
|
doScan(basePackages);
|
|
|
|
|
|
// Register annotation config processors, if necessary.
|
|
|
if (this.includeAnnotationConfig) {
|
|
|
AnnotationConfigUtils.registerAnnotationConfigProcessors(this.registry);
|
|
|
}
|
|
|
|
|
|
return (this.registry.getBeanDefinitionCount() - beanCountAtScanStart);
|
|
|
}
|
|
|
```
|
|
|
|
|
|
- 这个地方`doScan`似曾相识,他就是`org.springframework.context.annotation.ComponentScanBeanDefinitionParser.parse`中的`doScan`,下一步解析 doScan
|
|
|
|
|
|
### org.springframework.context.annotation.ClassPathBeanDefinitionScanner.doScan
|
|
|
|
|
|
```java
|
|
|
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) {
|
|
|
// 寻找组件
|
|
|
Set<BeanDefinition> candidates = findCandidateComponents(basePackage);
|
|
|
for (BeanDefinition candidate : candidates) {
|
|
|
// bean 作用域设置
|
|
|
ScopeMetadata scopeMetadata = this.scopeMetadataResolver.resolveScopeMetadata(candidate);
|
|
|
// 设置生命周期
|
|
|
candidate.setScope(scopeMetadata.getScopeName());
|
|
|
// 创建beanName
|
|
|
String beanName = this.beanNameGenerator.generateBeanName(candidate, this.registry);
|
|
|
if (candidate instanceof AbstractBeanDefinition) {
|
|
|
// 设置默认属性 具体方法:org.springframework.beans.factory.support.AbstractBeanDefinition.applyDefaults
|
|
|
postProcessBeanDefinition((AbstractBeanDefinition) candidate, beanName);
|
|
|
}
|
|
|
if (candidate instanceof AnnotatedBeanDefinition) {
|
|
|
// 读取Lazy,Primary 等注解
|
|
|
AnnotationConfigUtils.processCommonDefinitionAnnotations((AnnotatedBeanDefinition) candidate);
|
|
|
}
|
|
|
// bean的重复检查
|
|
|
if (checkCandidate(beanName, candidate)) {
|
|
|
// 创建 BeanDefinitionHolder
|
|
|
BeanDefinitionHolder definitionHolder = new BeanDefinitionHolder(candidate, beanName);
|
|
|
// 代理对象的处理
|
|
|
definitionHolder =
|
|
|
AnnotationConfigUtils.applyScopedProxyMode(scopeMetadata, definitionHolder, this.registry);
|
|
|
// 放入list中,最后返回用
|
|
|
beanDefinitions.add(definitionHolder);
|
|
|
// 注册bean
|
|
|
registerBeanDefinition(definitionHolder, this.registry);
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
return beanDefinitions;
|
|
|
}
|
|
|
|
|
|
```
|
|
|
|
|
|
#### org.springframework.context.annotation.ClassPathScanningCandidateComponentProvider#findCandidateComponents
|
|
|
|
|
|
```java
|
|
|
public Set<BeanDefinition> findCandidateComponents(String basePackage) {
|
|
|
// 扫描
|
|
|
if (this.componentsIndex != null && indexSupportsIncludeFilters()) {
|
|
|
return addCandidateComponentsFromIndex(this.componentsIndex, basePackage);
|
|
|
}
|
|
|
else {
|
|
|
return scanCandidateComponents(basePackage);
|
|
|
}
|
|
|
}
|
|
|
|
|
|
```
|
|
|
|
|
|
```java
|
|
|
/**
|
|
|
* 扫描当前包路径下的资源
|
|
|
* @param basePackage
|
|
|
* @return
|
|
|
*/
|
|
|
private Set<BeanDefinition> scanCandidateComponents(String basePackage) {
|
|
|
Set<BeanDefinition> candidates = new LinkedHashSet<>();
|
|
|
try {
|
|
|
// 字符串拼接出一个编译后的路径 classpath://
|
|
|
// 这里替换了通配符
|
|
|
String packageSearchPath = ResourcePatternResolver.CLASSPATH_ALL_URL_PREFIX +
|
|
|
resolveBasePackage(basePackage) + '/' + this.resourcePattern;
|
|
|
// 获取资源
|
|
|
Resource[] resources = getResourcePatternResolver().getResources(packageSearchPath);
|
|
|
// 日志级别
|
|
|
boolean traceEnabled = logger.isTraceEnabled();
|
|
|
boolean debugEnabled = logger.isDebugEnabled();
|
|
|
for (Resource resource : resources) {
|
|
|
if (traceEnabled) {
|
|
|
logger.trace("Scanning " + resource);
|
|
|
}
|
|
|
if (resource.isReadable()) {
|
|
|
try {
|
|
|
// 获取 MetadataReader
|
|
|
MetadataReader metadataReader = getMetadataReaderFactory().getMetadataReader(resource);
|
|
|
// 判断是否是 Component
|
|
|
if (isCandidateComponent(metadataReader)) {
|
|
|
ScannedGenericBeanDefinition sbd = new ScannedGenericBeanDefinition(metadataReader);
|
|
|
sbd.setResource(resource);
|
|
|
sbd.setSource(resource);
|
|
|
if (isCandidateComponent(sbd)) {
|
|
|
if (debugEnabled) {
|
|
|
logger.debug("Identified candidate component class: " + resource);
|
|
|
}
|
|
|
candidates.add(sbd);
|
|
|
}
|
|
|
else {
|
|
|
if (debugEnabled) {
|
|
|
logger.debug("Ignored because not a concrete top-level class: " + resource);
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
else {
|
|
|
if (traceEnabled) {
|
|
|
logger.trace("Ignored because not matching any filter: " + resource);
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
catch (Throwable ex) {
|
|
|
throw new BeanDefinitionStoreException(
|
|
|
"Failed to read candidate component class: " + resource, ex);
|
|
|
}
|
|
|
}
|
|
|
else {
|
|
|
if (traceEnabled) {
|
|
|
logger.trace("Ignored because not readable: " + resource);
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
catch (IOException ex) {
|
|
|
throw new BeanDefinitionStoreException("I/O failure during classpath scanning", ex);
|
|
|
}
|
|
|
return candidates;
|
|
|
}
|
|
|
|
|
|
```
|
|
|
|
|
|
#### org.springframework.context.annotation.ScopeMetadataResolver#resolveScopeMetadata
|
|
|
|
|
|
```java
|
|
|
/**
|
|
|
* 生命周期设置
|
|
|
*
|
|
|
* @param definition the target bean definition
|
|
|
* @return
|
|
|
*/
|
|
|
@Override
|
|
|
public ScopeMetadata resolveScopeMetadata(BeanDefinition definition) {
|
|
|
ScopeMetadata metadata = new ScopeMetadata();
|
|
|
// 判断是否属于 AnnotatedBeanDefinition
|
|
|
if (definition instanceof AnnotatedBeanDefinition) {
|
|
|
AnnotatedBeanDefinition annDef = (AnnotatedBeanDefinition) definition;
|
|
|
AnnotationAttributes attributes = AnnotationConfigUtils.attributesFor(
|
|
|
annDef.getMetadata(), this.scopeAnnotationType);
|
|
|
if (attributes != null) {
|
|
|
// 获取 value 属性值并且设置
|
|
|
metadata.setScopeName(attributes.getString("value"));
|
|
|
// 获取 proxyMode 属性值并且设置
|
|
|
ScopedProxyMode proxyMode = attributes.getEnum("proxyMode");
|
|
|
if (proxyMode == ScopedProxyMode.DEFAULT) {
|
|
|
proxyMode = this.defaultProxyMode;
|
|
|
}
|
|
|
metadata.setScopedProxyMode(proxyMode);
|
|
|
}
|
|
|
}
|
|
|
return metadata;
|
|
|
}
|
|
|
|
|
|
|
|
|
```
|
|
|
|
|
|
- `org.springframework.context.annotation.AnnotationScopeMetadataResolverTests#resolveScopeMetadataShouldReadScopedProxyModeFromAnnotation`测试用例
|
|
|
|
|
|
```java
|
|
|
@Test
|
|
|
public void resolveScopeMetadataShouldReadScopedProxyModeFromAnnotation() {
|
|
|
AnnotatedBeanDefinition bd = new AnnotatedGenericBeanDefinition(AnnotatedWithScopedProxy.class);
|
|
|
ScopeMetadata scopeMetadata = this.scopeMetadataResolver.resolveScopeMetadata(bd);
|
|
|
assertNotNull("resolveScopeMetadata(..) must *never* return null.", scopeMetadata);
|
|
|
assertEquals("request", scopeMetadata.getScopeName());
|
|
|
assertEquals(TARGET_CLASS, scopeMetadata.getScopedProxyMode());
|
|
|
}
|
|
|
|
|
|
```
|
|
|
|
|
|
![image-20200115141708702](../../../images/spring/image-20200115141708702.png)
|
|
|
|
|
|
#### org.springframework.beans.factory.support.BeanNameGenerator#generateBeanName
|
|
|
|
|
|
- 创建 beanName `org.springframework.context.annotation.AnnotationBeanNameGenerator#generateBeanName`
|
|
|
|
|
|
```java
|
|
|
@Override
|
|
|
public String generateBeanName(BeanDefinition definition, BeanDefinitionRegistry registry) {
|
|
|
if (definition instanceof AnnotatedBeanDefinition) {
|
|
|
// 如果存在bean(value="") value存在
|
|
|
String beanName = determineBeanNameFromAnnotation((AnnotatedBeanDefinition) definition);
|
|
|
if (StringUtils.hasText(beanName)) {
|
|
|
// Explicit bean name found.
|
|
|
return beanName;
|
|
|
}
|
|
|
}
|
|
|
// Fallback: generate a unique default bean name.
|
|
|
// 创建beanName
|
|
|
return buildDefaultBeanName(definition, registry);
|
|
|
}
|
|
|
|
|
|
```
|
|
|
|
|
|
```java
|
|
|
@Nullable
|
|
|
protected String determineBeanNameFromAnnotation(AnnotatedBeanDefinition annotatedDef) {
|
|
|
AnnotationMetadata amd = annotatedDef.getMetadata();
|
|
|
Set<String> types = amd.getAnnotationTypes();
|
|
|
String beanName = null;
|
|
|
for (String type : types) {
|
|
|
AnnotationAttributes attributes = AnnotationConfigUtils.attributesFor(amd, type);
|
|
|
if (attributes != null && isStereotypeWithNameValue(type, amd.getMetaAnnotationTypes(type), attributes)) {
|
|
|
// 获取注解的value 属性值
|
|
|
Object value = attributes.get("value");
|
|
|
if (value instanceof String) {
|
|
|
String strVal = (String) value;
|
|
|
// 判断是否存在值
|
|
|
if (StringUtils.hasLength(strVal)) {
|
|
|
if (beanName != null && !strVal.equals(beanName)) {
|
|
|
throw new IllegalStateException("Stereotype annotations suggest inconsistent " +
|
|
|
"component names: '" + beanName + "' versus '" + strVal + "'");
|
|
|
}
|
|
|
// beanName = value属性值
|
|
|
beanName = strVal;
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
return beanName;
|
|
|
}
|
|
|
|
|
|
```
|
|
|
|
|
|
```java
|
|
|
@Service(value = "dhc")
|
|
|
public class DemoService {
|
|
|
|
|
|
}
|
|
|
```
|
|
|
|
|
|
![image-20200115143315633](../../../images/spring/image-20200115143315633.png)
|
|
|
|
|
|
- `org.springframework.context.annotation.AnnotationBeanNameGenerator#buildDefaultBeanName(org.springframework.beans.factory.config.BeanDefinition, org.springframework.beans.factory.support.BeanDefinitionRegistry)`
|
|
|
- `org.springframework.context.annotation.AnnotationBeanNameGenerator#buildDefaultBeanName(org.springframework.beans.factory.config.BeanDefinition)`
|
|
|
|
|
|
```JAVA
|
|
|
protected String buildDefaultBeanName(BeanDefinition definition) {
|
|
|
// 获取bean class name
|
|
|
String beanClassName = definition.getBeanClassName();
|
|
|
Assert.state(beanClassName != null, "No bean class name set");
|
|
|
// 获取短类名,
|
|
|
String shortClassName = ClassUtils.getShortName(beanClassName);
|
|
|
// 第一个字母小写
|
|
|
return Introspector.decapitalize(shortClassName);
|
|
|
}
|
|
|
|
|
|
```
|
|
|
|
|
|
```JAVA
|
|
|
@Configuration
|
|
|
public class BeanConfig {
|
|
|
@Scope(value =ConfigurableBeanFactory.SCOPE_PROTOTYPE)
|
|
|
@Bean(value = "hc")
|
|
|
public Ubean f() {
|
|
|
return new Ubean();
|
|
|
}
|
|
|
}
|
|
|
|
|
|
```
|
|
|
|
|
|
![image-20200115143456554](../../../images/spring/image-20200115143456554.png)
|
|
|
|
|
|
#### org.springframework.context.annotation.ClassPathBeanDefinitionScanner#postProcessBeanDefinition
|
|
|
|
|
|
- 这个方法没什么难点,直接是 set 方法
|
|
|
|
|
|
```java
|
|
|
protected void postProcessBeanDefinition(AbstractBeanDefinition beanDefinition, String beanName) {
|
|
|
beanDefinition.applyDefaults(this.beanDefinitionDefaults);
|
|
|
if (this.autowireCandidatePatterns != null) {
|
|
|
beanDefinition.setAutowireCandidate(PatternMatchUtils.simpleMatch(this.autowireCandidatePatterns, beanName));
|
|
|
}
|
|
|
}
|
|
|
|
|
|
```
|
|
|
|
|
|
```java
|
|
|
public void applyDefaults(BeanDefinitionDefaults defaults) {
|
|
|
setLazyInit(defaults.isLazyInit());
|
|
|
setAutowireMode(defaults.getAutowireMode());
|
|
|
setDependencyCheck(defaults.getDependencyCheck());
|
|
|
setInitMethodName(defaults.getInitMethodName());
|
|
|
setEnforceInitMethod(false);
|
|
|
setDestroyMethodName(defaults.getDestroyMethodName());
|
|
|
setEnforceDestroyMethod(false);
|
|
|
}
|
|
|
|
|
|
```
|
|
|
|
|
|
#### org.springframework.context.annotation.AnnotationConfigUtils#processCommonDefinitionAnnotations(org.springframework.beans.factory.annotation.AnnotatedBeanDefinition)
|
|
|
|
|
|
```java
|
|
|
public static void processCommonDefinitionAnnotations(AnnotatedBeanDefinition abd) {
|
|
|
processCommonDefinitionAnnotations(abd, abd.getMetadata());
|
|
|
}
|
|
|
|
|
|
```
|
|
|
|
|
|
```java
|
|
|
static void processCommonDefinitionAnnotations(AnnotatedBeanDefinition abd, AnnotatedTypeMetadata metadata) {
|
|
|
// 获取 lazy 注解
|
|
|
AnnotationAttributes lazy = attributesFor(metadata, Lazy.class);
|
|
|
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"));
|
|
|
}
|
|
|
}
|
|
|
|
|
|
if (metadata.isAnnotated(Primary.class.getName())) {
|
|
|
abd.setPrimary(true);
|
|
|
}
|
|
|
AnnotationAttributes dependsOn = attributesFor(metadata, DependsOn.class);
|
|
|
if (dependsOn != null) {
|
|
|
abd.setDependsOn(dependsOn.getStringArray("value"));
|
|
|
}
|
|
|
|
|
|
AnnotationAttributes role = attributesFor(metadata, Role.class);
|
|
|
if (role != null) {
|
|
|
abd.setRole(role.getNumber("value").intValue());
|
|
|
}
|
|
|
AnnotationAttributes description = attributesFor(metadata, Description.class);
|
|
|
if (description != null) {
|
|
|
abd.setDescription(description.getString("value"));
|
|
|
}
|
|
|
}
|
|
|
```
|
|
|
|
|
|
- 方法思路:
|
|
|
1. 获取注解的属性值
|
|
|
2. 设置注解属性
|
|
|
|
|
|
#### org.springframework.context.annotation.ClassPathBeanDefinitionScanner#checkCandidate
|
|
|
|
|
|
- 重复检查
|
|
|
|
|
|
```java
|
|
|
protected boolean checkCandidate(String beanName, BeanDefinition beanDefinition) throws IllegalStateException {
|
|
|
// 判断当前 beanName 是否在注册表中
|
|
|
if (!this.registry.containsBeanDefinition(beanName)) {
|
|
|
return true;
|
|
|
}
|
|
|
// 从注册表中获取
|
|
|
BeanDefinition existingDef = this.registry.getBeanDefinition(beanName);
|
|
|
// 当前的bean
|
|
|
BeanDefinition originatingDef = existingDef.getOriginatingBeanDefinition();
|
|
|
if (originatingDef != null) {
|
|
|
existingDef = originatingDef;
|
|
|
}
|
|
|
if (isCompatible(beanDefinition, existingDef)) {
|
|
|
return false;
|
|
|
}
|
|
|
throw new ConflictingBeanDefinitionException("Annotation-specified bean name '" + beanName +
|
|
|
"' for bean class [" + beanDefinition.getBeanClassName() + "] conflicts with existing, " +
|
|
|
"non-compatible bean definition of same name and class [" + existingDef.getBeanClassName() + "]");
|
|
|
}
|
|
|
|
|
|
```
|
|
|
|
|
|
#### org.springframework.context.annotation.AnnotationConfigUtils#applyScopedProxyMode
|
|
|
|
|
|
```JAVA
|
|
|
static BeanDefinitionHolder applyScopedProxyMode(
|
|
|
ScopeMetadata metadata, BeanDefinitionHolder definition, BeanDefinitionRegistry registry) {
|
|
|
|
|
|
ScopedProxyMode scopedProxyMode = metadata.getScopedProxyMode();
|
|
|
if (scopedProxyMode.equals(ScopedProxyMode.NO)) {
|
|
|
return definition;
|
|
|
}
|
|
|
boolean proxyTargetClass = scopedProxyMode.equals(ScopedProxyMode.TARGET_CLASS);
|
|
|
// 创建代理对象
|
|
|
return ScopedProxyCreator.createScopedProxy(definition, registry, proxyTargetClass);
|
|
|
}
|
|
|
|
|
|
```
|