下文源碼分析版本Spring 5.2.5 releasehtml
@Component
做爲一種由Spring容器託管的通用組件,任何被@Component
標註的組件均爲組件掃描的對象。 相似的組件好比@Repository
,@Service
或者使用@Component
註解做爲自定義註釋。java
//@see org.springframework.context.annotation.ClassPathBeanDefinitionScanner
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Indexed
public @interface Component {
/**
* The value may indicate a suggestion for a logical component name,
* to be turned into a Spring bean in case of an autodetected component.
* @return the suggested component name, if any (or empty String otherwise)
*/
String value() default "";
}
複製代碼
@Repository
自定義一個註解@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
//@Component
@Repository
public @interface StringRepository {
String value() default "";
}
複製代碼
NameRepository
,加上該註解@StringRepository("chineseNameRepository")
public class NameRepository {
public List<String> findAll() {
return Arrays.asList("張三", "李四", "王二麻子");
}
}
複製代碼
NameRepository
類是否能被Spring容器加載建立一個測試類ComponentBootStrap
,代碼以下spring
@Configuration
@ComponentScan
public class ComponentBootStrap {
public static void main(String[] args) {
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(ComponentBootStrap.class);
// context.register(ComponentBootStrap.class);
// context.refresh();
NameRepository nameRepository = context.getBean(NameRepository.class);
System.out.println("nameRepository.findAll() = " + nameRepository.findAll());
}
}
複製代碼
小提示:類NameRepository
與引導類ComponentBootStrap
需放到一個包下,才能被@ComponentScan
掃描加載。 輸出結果爲:nameRepository.findAll() = [張三, 李四, 王二麻子]
,這說明了自定義註解類@StringRepository
也有相似於@Component
的功能。 結論:只要你註解中包含了@Component
,就能被Spring容器託管。由於@Component
是元註解,也能夠聯合元註解去創造組合註解,好比@RestController
就是由@Controller
與@ResponseBody
組成的。 官網beans-meta-annotations編程
上面例子來源於小馬哥的《Spring Boot編程思想》,NameRepository
註解不是繼承@ComponentScan
,可是效果倒是繼承,借用小馬哥對這種模式註解的定義爲@ComponentScan
的派生性。那下文就探索一下@ComponentScan
派生性的原理。bash
只要註解裏面含有@Component
,就會被Spring掃描,並註冊。爲何會這麼呢?是否是跟掃描方式有關係呢? Spring掃描bean的方式有兩種,一種是自定義標籤<context:component-scan base-package="com.xxx"/>這裏base-package值填寫NameRepository
的包名,一種是註解@ComponentScan
,若是不填寫value值,就默認掃描註解類的包下全部類。app
第一種自定義標籤的方式這裏暫時不討論,之後會專門寫一篇來解析。 咱們這裏重點講解註解@ComponentScan
方式。ide
@ComponentScan
源碼@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@Documented
@Repeatable(ComponentScans.class)
public @interface ComponentScan {
@AliasFor("basePackages")
String[] value() default {};
@AliasFor("value")
String[] basePackages() default {};
Class<?>[] basePackageClasses() default {};
Class<? extends BeanNameGenerator> nameGenerator() default BeanNameGenerator.class;
Class<? extends ScopeMetadataResolver> scopeResolver() default AnnotationScopeMetadataResolver.class;
ScopedProxyMode scopedProxy() default ScopedProxyMode.DEFAULT;
String resourcePattern() default ClassPathScanningCandidateComponentProvider.DEFAULT_RESOURCE_PATTERN;
// 這裏默認爲true,說明使用默認的過濾器,下文會詳細說這裏
boolean useDefaultFilters() default true;
Filter[] includeFilters() default {};
Filter[] excludeFilters() default {};
boolean lazyInit() default false;
@Retention(RetentionPolicy.RUNTIME)
@Target({})
@interface Filter {
FilterType type() default FilterType.ANNOTATION;
@AliasFor("classes")
Class<?>[] value() default {};
@AliasFor("value")
Class<?>[] classes() default {};
String[] pattern() default {};
}
}
複製代碼
該註解裏面有不少屬性,我這裏就不介紹,有興趣的自行查閱資料。裏面有個註解@AliasFor("value")
常常出現,要是有讀者有興趣,我下次就單獨寫篇文章來介紹這個。說偏題了,來繼續探索,Spring是在哪裏進行掃描的呢?看到這裏不少人都會比較困惑,個人方法通常是先利用idea的find usages,快捷鍵也就是CTRL+G,來搜索那個地方調用了該註解,以下源碼分析
org.springframework.context.annotation.ConfigurationClassParser#doProcessConfigurationClass
該方法裏面找到了解析
@ComponentScan
的地方。可是猛的一看這個類,好傢伙,裏面不只僅只解析咱們談論的
@ComponentScan
,還解析
@Import
,
@Bean
,
@PropertySources
,
@ComponentScans
,
@ImportResource
等,下次將一次性來說解這個類。
來就重點來看核心代碼ConfigurationClassParser#doProcessConfigurationClass
:post
protected final SourceClass doProcessConfigurationClass(
ConfigurationClass configClass, SourceClass sourceClass, Predicate<String> filter){
···
// Process any @ComponentScan annotations
// 獲取ComponentScans,ComponentScan註解裏面全部的屬性
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
// 核心代碼parse:下面重點解析
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());
}
}
}
}
···
複製代碼
ComponentScanAnnotationParser#parse(AnnotationAttributes,String)測試
public Set<BeanDefinitionHolder> parse(AnnotationAttributes componentScan, final String declaringClass) {
// 這裏new一個scanner對象,由於@ComponentScan註解裏面的useDefaultFilters默認爲true,因此這裏傳入的值爲true
// 也就是在構造器中使用了默認的過濾器,下文會介紹
ClassPathBeanDefinitionScanner scanner = new ClassPathBeanDefinitionScanner(this.registry,
componentScan.getBoolean("useDefaultFilters"), this.environment, this.resourceLoader);
Class<? extends BeanNameGenerator> generatorClass = componentScan.getClass("nameGenerator");
boolean useInheritedGenerator = (BeanNameGenerator.class == generatorClass);
scanner.setBeanNameGenerator(useInheritedGenerator ? this.beanNameGenerator :
BeanUtils.instantiateClass(generatorClass));
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);
}
}
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);
}
});
// 上面都是將註解裏面的屬性賦值給scanner,而後解析方法委託給ClassPathBeanDefinitionScanner#doScan
return scanner.doScan(StringUtils.toStringArray(basePackages));
}
複製代碼
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就是以前的@ScanComponent所在的包名
for (String basePackage : basePackages) {
// 找到候選組件
Set<BeanDefinition> candidates = findCandidateComponents(basePackage);
for (BeanDefinition candidate : candidates) {
ScopeMetadata scopeMetadata = this.scopeMetadataResolver.resolveScopeMetadata(candidate);
candidate.setScope(scopeMetadata.getScopeName());
String beanName = this.beanNameGenerator.generateBeanName(candidate, this.registry);
if (candidate instanceof AbstractBeanDefinition) {
postProcessBeanDefinition((AbstractBeanDefinition) candidate, beanName);
}
if (candidate instanceof AnnotatedBeanDefinition) {
AnnotationConfigUtils.processCommonDefinitionAnnotations((AnnotatedBeanDefinition) candidate);
}
if (checkCandidate(beanName, candidate)) {
BeanDefinitionHolder definitionHolder = new BeanDefinitionHolder(candidate, beanName);
definitionHolder =
AnnotationConfigUtils.applyScopedProxyMode(scopeMetadata, definitionHolder, this.registry);
beanDefinitions.add(definitionHolder);
// 將候選組件註冊爲BeanDefinition
registerBeanDefinition(definitionHolder, this.registry);
}
}
}
return beanDefinitions;
}
複製代碼
掃描候選組件的類路徑 ClassPathScanningCandidateComponentProvider#findCandidateComponents(String)
public Set<BeanDefinition> findCandidateComponents(String basePackage) {
if (this.componentsIndex != null && indexSupportsIncludeFilters()) {
return addCandidateComponentsFromIndex(this.componentsIndex, basePackage);
}
else {
return scanCandidateComponents(basePackage);
}
}
private Set<BeanDefinition> scanCandidateComponents(String basePackage) {
Set<BeanDefinition> candidates = new LinkedHashSet<>();
try {
// String CLASSPATH_ALL_URL_PREFIX = "classpath*:";
// 假如這裏的basePackage是com.example.learnspring,resolveBasePackage(basePackage)就把包名變成com/example/learnspring
// resolveBasePackage(basePackage)的目的是將包名裏面的"."換成"/",
String packageSearchPath = ResourcePatternResolver.CLASSPATH_ALL_URL_PREFIX +
resolveBasePackage(basePackage) + '/' + "**/*.class";
// 根據傳入的路徑解析成Resource
Resource[] resources = getResourcePatternResolver().getResources(packageSearchPath);
···省去日誌
if (resource.isReadable()) {
try {
// 產生一個訪問元數據的門面類MetadataReader
MetadataReader metadataReader = getMetadataReaderFactory().getMetadataReader(resource);
// 判斷是否爲候選組件
if (isCandidateComponent(metadataReader)) {
ScannedGenericBeanDefinition sbd = new ScannedGenericBeanDefinition(metadataReader);
sbd.setResource(resource);
sbd.setSource(resource);
if (isCandidateComponent(sbd)) {
candidates.add(sbd);
}
else {
if (debugEnabled) {
logger.debug("Ignored because not a concrete top-level class: " + resource);
}
}
}
}
}
catch (Throwable ex) {
···
}
return candidates;
}
複製代碼
這裏先介紹一下MetadataReader
,這是一個經過ASM訪問類元數據的門面類,在這裏的實現類是SimpleMetadataReader
,元數據就是SimpleAnnotationMetadataReadingVisitor
經過ASM產生的。
final class SimpleMetadataReader implements MetadataReader {
private static final int PARSING_OPTIONS = ClassReader.SKIP_DEBUG
| ClassReader.SKIP_CODE | ClassReader.SKIP_FRAMES;
private final Resource resource;
// 數據元信息,包括className,superClassName等等,
// 若是仍是不清楚,看下面類StringRepository數據元信息圖
private final AnnotationMetadata annotationMetadata;
SimpleMetadataReader(Resource resource, @Nullable ClassLoader classLoader) throws IOException {
// 經過SimpleAnnotationMetadataReadingVisitor來建立AnnotationMetadata,原理是ASM,對這裏有興趣的同窗能夠在這裏打個斷點,盡情調試
SimpleAnnotationMetadataReadingVisitor visitor = new SimpleAnnotationMetadataReadingVisitor(classLoader);
getClassReader(resource).accept(visitor, PARSING_OPTIONS);
this.resource = resource;
this.annotationMetadata = visitor.getMetadata();
}
複製代碼
protected boolean isCandidateComponent(MetadataReader metadataReader) throws IOException {
for (TypeFilter tf : this.excludeFilters) {
if (tf.match(metadataReader, getMetadataReaderFactory())) {
return false;
}
}
for (TypeFilter tf : this.includeFilters) {
if (tf.match(metadataReader, getMetadataReaderFactory())) {
return isConditionMatch(metadataReader);
}
}
return false;
}
複製代碼
咱們的類StringRepository
以前已經成功被Spring加載了,全部在這裏確定是返回true的,所以在這裏打個斷點進行調試。 excludeFilters
: 排除含有註解的元信息 includeFilters
:匹配含有註解的元信息 這裏發現includeFilters
裏面包含了兩個AnnotationTypeFilter
,分別包含Component
與ManagedBean
,這是由於在registerDefaultFilters
方法裏面註冊了默認的過濾器。
protected void registerDefaultFilters() {
this.includeFilters.add(new AnnotationTypeFilter(Component.class));
ClassLoader cl = ClassPathScanningCandidateComponentProvider.class.getClassLoader();
try {
this.includeFilters.add(new AnnotationTypeFilter(
((Class<? extends Annotation>) ClassUtils.forName("javax.annotation.ManagedBean", cl)), false));
logger.trace("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 {
this.includeFilters.add(new AnnotationTypeFilter(
((Class<? extends Annotation>) ClassUtils.forName("javax.inject.Named", cl)), false));
logger.trace("JSR-330 'javax.inject.Named' annotation found and supported for component scanning");
}
catch (ClassNotFoundException ex) {
// JSR-330 API not available - simply skip.
}
}
複製代碼
搜索一下哪裏調用了registerDefaultFilters
,發現ClassPathBeanDefinitionScanner#ClassPathBeanDefinitionScanner()
public ClassPathBeanDefinitionScanner(BeanDefinitionRegistry registry, boolean useDefaultFilters,
Environment environment, @Nullable ResourceLoader resourceLoader) {
Assert.notNull(registry, "BeanDefinitionRegistry must not be null");
this.registry = registry;
if (useDefaultFilters) {
// 設置默認的過濾器
registerDefaultFilters();
}
setEnvironment(environment);
setResourceLoader(resourceLoader);
}
複製代碼
最後發如今ClassPathBeanDefinitionScanner
構造器中設置了默認的過濾器,也就是在ComponentScanAnnotationParser#parse
方法的第一行,建立ClassPathBeanDefinitionScanner
對象,與上文造成呼應。
public Set<BeanDefinitionHolder> parse(AnnotationAttributes componentScan, final String declaringClass) {
ClassPathBeanDefinitionScanner scanner = new ClassPathBeanDefinitionScanner(this.registry,
componentScan.getBoolean("useDefaultFilters"), this.environment, this.resourceLoader);
複製代碼
繞來繞去,你可能有些暈了,讓咱們在整理一下,剛纔介紹到判斷是候選組件,也就是下面的match()方法返回true。從方法名猜想只要類中包含註解@Component
或者@ManageBean
就會匹配成功。下面繼續深刻源碼分析。
protected boolean isCandidateComponent(MetadataReader metadataReader) throws IOException {
for (TypeFilter tf : this.excludeFilters) {
if (tf.match(metadataReader, getMetadataReaderFactory())) {
return false;
}
}
for (TypeFilter tf : this.includeFilters) {
// 核心判斷邏輯
if (tf.match(metadataReader, getMetadataReaderFactory())) {
// 判斷元信息是否包含@Conditional,跟條件相關的,這裏省略
return isConditionMatch(metadataReader);
}
}
return false;
}
複製代碼
AbstractTypeHierarchyTraversingFilter#match(MetadataReader,MetadataReaderFactory)
public boolean match(MetadataReader metadataReader, MetadataReaderFactory metadataReaderFactory)
throws IOException {
// 主要講解這個方法,下面的邏輯有興趣的同窗能夠本身研究
if (matchSelf(metadataReader)) {
return true;
}
······
return false;
}
protected boolean matchSelf(MetadataReader metadataReader) {
// 獲取元信息,這裏是自定義註解@StringRepository信息
AnnotationMetadata metadata = metadataReader.getAnnotationMetadata();
// 下面的this.annotationType.getName()是傳入的org.springframework.stereotype.Component
return metadata.hasAnnotation(this.annotationType.getName()) ||
(this.considerMetaAnnotations && metadata.hasMetaAnnotation(this.annotationType.getName()));
}
複製代碼
AnnotationMetadata#hasMetaAnnotation
// 判斷底層註解是否包含metaAnnotationName
default boolean hasMetaAnnotation(String metaAnnotationName) {
return getAnnotations().get(metaAnnotationName,
MergedAnnotation::isMetaPresent).isPresent();
}
複製代碼
到了這裏, 就已經很清晰了,只有兩個方法,一個是metadata.hasAnnotation(this.annotationType.getName()),這是判斷類上是否含有給定的註解, 還有個方法就是 metadata.hasMetaAnnotation(this.annotationType.getName()),這是判斷底層class是否含有給定的註解,而@StringRepository的底層確實包含了@Component,因此這裏方法返回true。
若是有地方有疑惑或者寫的有很差,能夠評論或者經過郵箱聯繫我creazycoder@sina.com