Spring源码系列(四)Spring扫描机制(一)

1.写在前面

前面我们已经介绍了Spring的几种BeanDefinition,同时介绍了几种常用的BeanDefinition中的对应的属性。以及几种BeanDefinition的实现类的区别,同时也介绍了RootBeanDefinitionChildBeanDefinition的缺点,以及为什么引入GenericBeanDefinition,以及GenericBeanDefinition的优点。今天我们会介绍AnnotationConfigApplicationContext中构造函数中两个属性的意思(reader,scanner)。以及会介绍一下自定义的注解,怎么让我们定义的注解,能给Spring扫描到,有点类似@MapperScan

2.AnnotationConfigApplicationContext

为了将清楚这个类对应的构造函数,我们写出了如下的测试的代码:

package com.ys.annotationConfigApplicationContextTest;

import org.springframework.stereotype.Component;

@Component
public class A {
}

package com.ys.annotationConfigApplicationContextTest;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;

@Configuration
@ComponentScan("com.ys.annotationConfigApplicationContextTest")
public class AppConfig {
}

package com.ys.annotationConfigApplicationContextTest;

import org.springframework.context.annotation.AnnotationConfigApplicationContext;

public class Main {
    public static void main(String[] args) {
        AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(AppConfig.class);
    }
}

我们在扫描的包下面写了一个对应的A类,并且这个类加了@Component注解。这样就能确保Spring能扫描到对应的A类,至于这个原理是什么?我们会在本篇博客中讲到,但是要讲清楚这个问题,我们需要知道,这个扫描的路径怎么来的?而这个扫描的路径是加在AppConfig类上面,所以要把这个类注册到Spring容器中去,那么是怎么注册到Spring容器中去的呢?这就要知道了解我们对应的AnnotationConfigApplicationContext中的两个属性:readerscanner

2.1.reader属性

要搞清楚这个属性,我们需要打开对应的AnnotationConfigApplicationContext的无参构造函数,因为这个类所有的构造函数都会调用这个无参的函数,具体的代码如下,由于篇幅原因,我们只贴关键的代码:

package org.springframework.context.annotation;

import java.util.function.Supplier;

import org.springframework.beans.factory.config.BeanDefinitionCustomizer;
import org.springframework.beans.factory.support.BeanNameGenerator;
import org.springframework.beans.factory.support.DefaultListableBeanFactory;
import org.springframework.context.support.GenericApplicationContext;
import org.springframework.core.env.ConfigurableEnvironment;
import org.springframework.lang.Nullable;
import org.springframework.util.Assert;

public class AnnotationConfigApplicationContext extends GenericApplicationContext implements AnnotationConfigRegistry {

	private final AnnotatedBeanDefinitionReader reader;

	private final ClassPathBeanDefinitionScanner scanner;

	public AnnotationConfigApplicationContext() {
		this.reader = new AnnotatedBeanDefinitionReader(this);
		this.scanner = new ClassPathBeanDefinitionScanner(this);
	}

	public AnnotationConfigApplicationContext(DefaultListableBeanFactory beanFactory) {
		super(beanFactory);
		this.reader = new AnnotatedBeanDefinitionReader(this);
		this.scanner = new ClassPathBeanDefinitionScanner(this);
	}

	public AnnotationConfigApplicationContext(Class<?>... componentClasses) {
		this();
		register(componentClasses);
		refresh();
	}

	public AnnotationConfigApplicationContext(String... basePackages) {
		this();
		scan(basePackages);
		refresh();
	}

}

上面的代码表示所有的构造函数,你会发现都会调用对应的无参的构造函数。无参的构造函数有对应readerscanner属性,同时给这个两个属性进行对应的初始化。本节主要讲reader这个属性。我们可以看到这个属性的类型是AnnotatedBeanDefinitionReader。从字面上的意思可以理解,这个对象主要是读AnnotatedBeanDefinition这个BeanDefinition的,那么这个AnnotatedBeanDefinition对应的是什么呢?上篇博客中有介绍到,这个AnnotatedBeanDefinition是一个接口,这儿实现应该是AnnotatedGenericBeanDefinition主要对应的是我们的配置类,也就是我们这儿的AppConfig这个类。我们点进去看下这个类的构造函数干了啥。具体的代码如下:

public static Set<BeanDefinitionHolder> registerAnnotationConfigProcessors(
			BeanDefinitionRegistry registry, @Nullable Object source) {

    	//获取对应的BeanFactory
		DefaultListableBeanFactory beanFactory = unwrapDefaultListableBeanFactory(registry);
		if (beanFactory != null) {
             //bean排序的处理器
			if (!(beanFactory.getDependencyComparator() instanceof AnnotationAwareOrderComparator)) {
				beanFactory.setDependencyComparator(AnnotationAwareOrderComparator.INSTANCE);
			}
           	 //自动装配的规则
			if (!(beanFactory.getAutowireCandidateResolver() instanceof ContextAnnotationAutowireCandidateResolver)) {
				beanFactory.setAutowireCandidateResolver(new ContextAnnotationAutowireCandidateResolver());
			}
		}

		Set<BeanDefinitionHolder> beanDefs = new LinkedHashSet<>(8);

		if (!registry.containsBeanDefinition(CONFIGURATION_ANNOTATION_PROCESSOR_BEAN_NAME)) {
             //注册ConfigurationClassPostProcessor一个比较核心的BeanFactoryPostProcessor
			RootBeanDefinition def = new RootBeanDefinition(ConfigurationClassPostProcessor.class);
			def.setSource(source);
			beanDefs.add(registerPostProcessor(registry, def, CONFIGURATION_ANNOTATION_PROCESSOR_BEAN_NAME));
		}

		if (!registry.containsBeanDefinition(AUTOWIRED_ANNOTATION_PROCESSOR_BEAN_NAME)) {
             //注册AutowiredAnnotationBeanPostProcessor一个BeanPostProcessor
			RootBeanDefinition def = new RootBeanDefinition(AutowiredAnnotationBeanPostProcessor.class);
			def.setSource(source);
			beanDefs.add(registerPostProcessor(registry, def, AUTOWIRED_ANNOTATION_PROCESSOR_BEAN_NAME));
		}

		if (jsr250Present && !registry.containsBeanDefinition(COMMON_ANNOTATION_PROCESSOR_BEAN_NAME)) {
             //注册CommonAnnotationBeanPostProcessor一个BeanPostProcessor,这个是为了支持JSR-250的注解
			RootBeanDefinition def = new RootBeanDefinition(CommonAnnotationBeanPostProcessor.class);
			def.setSource(source);
			beanDefs.add(registerPostProcessor(registry, def, COMMON_ANNOTATION_PROCESSOR_BEAN_NAME));
		}

		// 如果要支持JPA,注册一个PersistenceAnnotationBeanPostProcessor. 默认是不会添加的
		if (jpaPresent && !registry.containsBeanDefinition(PERSISTENCE_ANNOTATION_PROCESSOR_BEAN_NAME)) {
			RootBeanDefinition def = new RootBeanDefinition();
			try {
				def.setBeanClass(ClassUtils.forName(PERSISTENCE_ANNOTATION_PROCESSOR_CLASS_NAME,
						AnnotationConfigUtils.class.getClassLoader()));
			}
			catch (ClassNotFoundException ex) {
				throw new IllegalStateException(
						"Cannot load optional framework class: " + PERSISTENCE_ANNOTATION_PROCESSOR_CLASS_NAME, ex);
			}
			def.setSource(source);
			beanDefs.add(registerPostProcessor(registry, def, PERSISTENCE_ANNOTATION_PROCESSOR_BEAN_NAME));
		}
		
         //注册一个EventListenerMethodProcessor同样是BeanFactoryPostProcessor
		if (!registry.containsBeanDefinition(EVENT_LISTENER_PROCESSOR_BEAN_NAME)) {
			RootBeanDefinition def = new RootBeanDefinition(EventListenerMethodProcessor.class);
			def.setSource(source);
			beanDefs.add(registerPostProcessor(registry, def, EVENT_LISTENER_PROCESSOR_BEAN_NAME));
		}
         //注册一个DefaultEventListenerFactory
		if (!registry.containsBeanDefinition(EVENT_LISTENER_FACTORY_BEAN_NAME)) {
			RootBeanDefinition def = new RootBeanDefinition(DefaultEventListenerFactory.class);
			def.setSource(source);
			beanDefs.add(registerPostProcessor(registry, def, EVENT_LISTENER_FACTORY_BEAN_NAME));
		}

		return beanDefs;
	}

上面的AnnotatedBeanDefinitionReader的核心代码就是上面的,准确的说:就是往BeanFactory中的beanDefinitionMap中添加了ConfigurationClassPostProcessor(BeanFactoryPostProcessor)、AutowiredAnnotationBeanPostProcessor(BeanPostProcessor)、CommonAnnotationBeanPostProcessor(BeanPostProcessor)、EventListenerMethodProcessor(BeanFactoryPostProcessor)、DefaultEventListenerFactory默认的情况是这五个,具体的如下图:

在这里插入图片描述

那么什么时候将PersistenceAnnotationBeanPostProcessor,JPA的支持,添加进去呢?可以看到对应的代码是只要jpaPresent的值为true就可以了,那么什么时候为true呢?具体的如下图:

在这里插入图片描述

可以发现,我们只需要添加这个对应的这个两个类即可。默认的情况下JSR250默认是支持的。那么读者可能会问这个JSR250又是啥?我们可以直接看官网的解释:Using @PostConstruct and @PreDestroy具体的如下图:

在这里插入图片描述

所以大概的知道这个对象在初始化的时候做了一些什么事,注册了一些重要的BeanDefinition,还有Bean排序的处理器,还有就是自动装配的候选对象的处理器。同时这个类还有一个很重要的方法就是register(Class<?>... componentClasses),这个方法就是注册一个BeanDefinitionSpring的容器中去。那么重点来了,Spring怎么将AppConfig这个类变成一个AnnotatedGenericBeanDefinition添加到SpringBeanFactorybeanDefinitionMap中去。这就是这个方法的职责。具体的代码如下:

public class AnnotationConfigApplicationContext extends GenericApplicationContext implements AnnotationConfigRegistry {
	public AnnotationConfigApplicationContext(Class<?>... componentClasses) {
		this();
		register(componentClasses);
		refresh();
	}
    
    @Override
	public void register(Class<?>... componentClasses) {
		Assert.notEmpty(componentClasses, "At least one component class must be specified");
		this.reader.register(componentClasses);
	}
}

我们发现最终调用的AnnotatedBeanDefinitionReader类中register的方法,我们直接看重点的方法,具体的代码如下:

//beanClas=AppConfig.class,其他的属性都为null,因为调用链前面传过来就是null
private <T> void doRegisterBean(Class<T> beanClass, @Nullable String name,
                                @Nullable Class<? extends Annotation>[] qualifiers, @Nullable Supplier<T> supplier,
                                @Nullable BeanDefinitionCustomizer[] customizers) {

    //创建对应的BeanDefinition
    AnnotatedGenericBeanDefinition abd = new AnnotatedGenericBeanDefinition(beanClass);
    //判断是否跳过
    if (this.conditionEvaluator.shouldSkip(abd.getMetadata())) {
        return;
    }

    //null
    abd.setInstanceSupplier(supplier);
    ScopeMetadata scopeMetadata = this.scopeMetadataResolver.resolveScopeMetadata(abd);
    //singlate
    abd.setScope(scopeMetadata.getScopeName());
    //生成对应的beanName,指定了beanName的话,就用指定的beanName,如果没有指定的话就直接用短类名,首字母换成小写,如果前两个字母都是大写的话,就直接返回
    String beanName = (name != null ? name : this.beanNameGenerator.generateBeanName(abd, this.registry));
	
    //设置lazy,primary,dependsOn,role,description等属性
    AnnotationConfigUtils.processCommonDefinitionAnnotations(abd);
    //null
    if (qualifiers != null) {
        for (Class<? extends Annotation> qualifier : qualifiers) {
            if (Primary.class == qualifier) {
                abd.setPrimary(true);
            }
            else if (Lazy.class == qualifier) {
                abd.setLazyInit(true);
            }
            else {
                abd.addQualifier(new AutowireCandidateQualifier(qualifier));
            }
        }
    }
    //null
    if (customizers != null) {
        for (BeanDefinitionCustomizer customizer : customizers) {
            customizer.customize(abd);
        }
    }

    //包装好BeanDefinition
    BeanDefinitionHolder definitionHolder = new BeanDefinitionHolder(abd, beanName);
    //设置对应的作用域
    definitionHolder = AnnotationConfigUtils.applyScopedProxyMode(scopeMetadata, definitionHolder, this.registry);
    //注册BeanDefinition
    BeanDefinitionReaderUtils.registerBeanDefinition(definitionHolder, this.registry);
}

我们终于看到了对应的注册AppConfig的方法,先是将AppConfig转成AnnotatedGenericBeanDefinition,然后给他生成对应beanName,生成的规则:**如果指定了beanName的话,就用指定的beanName,如果没有指定的话就直接用短类名,首字母换成小写,如果短类名的前两个字母都是大写的话,就直接返回。**然后解析这个AppConfig类中的一些BeanDefinition中属性,例如:lazyprimarydependsOnroledescription等属性。如果没有配置的话,就直接用默认的。最后将这个对象包装成BeanDefinitionHolder,再设置好对应的作用域,最后添加到BeanFactory中的BeanDefinitionMap中去,具体的如下图:

在这里插入图片描述

至此整个AppConfig类就变成AnnotatedGenericBeanDefinition添加到BeanFactory中去,就讲完了,接下来就要讲扫描的原理。要搞清扫描的原理。我们需要理解scanner这个属性对应创建ClassPathBeanDefinitionScanner对象中干了什么事。

2.2.scanner属性

这个属性主要是ClassPathBeanDefinitionScanner对象,而这个对象就是用来做扫描的。那么创建这个对象的时候,做了些什么操作,笔者直接去看对应代码,由于篇幅的问题,笔者这儿只展示核心的代码。具体的代码如下:

public ClassPathBeanDefinitionScanner(BeanDefinitionRegistry registry, boolean useDefaultFilters,
			Environment environment, @Nullable ResourceLoader resourceLoader) {
    Assert.notNull(registry, "BeanDefinitionRegistry must not be null");
    this.registry = registry;

    //useDefaultFilters的值默认为true
    if (useDefaultFilters) {
        registerDefaultFilters();
    }
    //设置对应的环境
    setEnvironment(environment);
    //设置对应的资源的加载器,主要是用来加载指定包名下的所有的class
    setResourceLoader(resourceLoader);
}

上面我们要看的方法就是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.
    }
}

上面的includeFilters属性中会添加要扫描的注解,还有一个是excludeFilters属性中是添加要忽略的注解。由于后面的类都是没有的,所以通过ClassUtils.forName方法都是加载不出来的。所以这儿不会添加到includeFilters的集合中去的。这儿只添加了一个Component的注解。至此这个ClassPathBeanDefinitionScanner对象就创建好了。同时往ClassPathBeanDefinitionScannerincludeFilters中的属性中添加一个Component注解,至此整个AnnotationConfigApplicationContext这个对象就创建完成了。但是还是没有讲到我们扫描的原理还是没有讲到。下个小节会直接讲到。

3.扫描的机制

前面的一个小节,主要是为了这个小节铺垫的,如果学一门技术,不清楚来龙去脉,就会学的云里雾里的。扫描的原理就是在doScan方法中的。当我们给这个方法打上断点的时候,调试的结果如下:

在这里插入图片描述

你会发现这个调用栈很长,同时是从refresh方法中调用过来,这样调试会很容易云里雾里。那我们有什么方法呢?很显然是有的,Spring提供一个方法叫做scan方法,我们只需要调用这个方法即可。同时还需要我们将原来的那个ComponentScan这个注解给去除掉即可。然后再调试,对应的方法栈如下:

在这里插入图片描述

这个时候我们看下doSacnd 的方法,具体的代码如下:

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) {
        //扫描对应包下的所有的class文件
        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);
                registerBeanDefinition(definitionHolder, this.registry);
            }
        }
    }
    return beanDefinitions;
}

上面的findCandidateComponents(basePackage);方法,我们需要看下它的实现规则是怎么样的?具体的代码如下:

private Set<BeanDefinition> scanCandidateComponents(String basePackage) {
    Set<BeanDefinition> candidates = new LinkedHashSet<>();
    try {
        //拼接对应的路径
        String packageSearchPath = ResourcePatternResolver.CLASSPATH_ALL_URL_PREFIX +
            resolveBasePackage(basePackage) + '/' + this.resourcePattern;
        //取出对应包下的所有的class文件
        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 = getMetadataReaderFactory().getMetadataReader(resource);
                    //这个判断是第一层过滤
                    if (isCandidateComponent(metadataReader)) {
                        ScannedGenericBeanDefinition sbd = new ScannedGenericBeanDefinition(metadataReader);
                        sbd.setSource(resource);
                        //这个判断是第二层过滤
                        if (isCandidateComponent(sbd)) {
                            if (debugEnabled) {
                                logger.debug("Identified candidate component class: " + resource);
                            }
                            //双层过滤后才会添加candidates中去,最后进行返回。
                            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;
}

笔者这儿录了一个GIF,方便读者观看。

在这里插入图片描述

这儿可以看到Spring将为我们所有的类的路径对应的封装成了Resource对象,然后这儿传入对应的路径,就可以获取其下面的所有的class,然后再次取对对应的元数据。然后调用isCandidateComponent(metadataReader)方法,这个方法是第一次过滤,我们可以跟进去看看,具体的代码如下:

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;
}

在这里插入图片描述

可以看到只要满足我们对应的includeFilters中的注解,就直接返回true,然后会返回到原来的方法,继续执行后续的操作。这个时候会执行第二层过滤,主要的方法是isCandidateComponent(sbd),具体的代码如下:

protected boolean isCandidateComponent(AnnotatedBeanDefinition beanDefinition) {
    AnnotationMetadata metadata = beanDefinition.getMetadata();
    return (metadata.isIndependent() && (metadata.isConcrete() ||
                                         (metadata.isAbstract() && metadata.hasAnnotatedMethods(Lookup.class.getName()))));
}

在这里插入图片描述

可以发现直接取出对应的元数据,然后判断这个类是不是前面一个判断是不是基础的类,不是接口也不是抽象类,会直接返回true,还有一种情况就是加了LookUp注解的类同时这个类是抽象的,这两种只要满足一个就返回true。然后我们看原来的代码。最后将扫描出来的类封装 ScannedGenericBeanDefinition添加到BeanFactoryBeanDefinitionMap中去。我们再回到原来的代码的地方

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) {
        //扫描对应包下的所有的class文件
        Set<BeanDefinition> candidates = findCandidateComponents(basePackage);
        //进行过滤后的BeanDefinition都会扫描出来
        for (BeanDefinition candidate : candidates) {
            //获取对应Scope
            ScopeMetadata scopeMetadata = this.scopeMetadataResolver.resolveScopeMetadata(candidate);
            //设置对应的Scope
            candidate.setScope(scopeMetadata.getScopeName());
            //生成对应的beanName,这个beanName的生成规则和前面一样
            String beanName = this.beanNameGenerator.generateBeanName(candidate, this.registry);
            //这个判断肯定会进,所有的BeanDefinition的父类是AbstractBeanDefinition
            if (candidate instanceof AbstractBeanDefinition) {
                //设置一些默认值
                postProcessBeanDefinition((AbstractBeanDefinition) candidate, beanName);
            }
            //这个判断也会进,扫描出来BeanDefinition都是实现了这个AnnotatedBeanDefinition接口
            if (candidate instanceof AnnotatedBeanDefinition) {
                //这儿设置lazy,primary,dependsOn,role,description属性,没有给值就设置默认值
                AnnotationConfigUtils.processCommonDefinitionAnnotations((AnnotatedBeanDefinition) candidate);
            }
            //判断这个BeanDefinition是否在容器中了,如果在直接不注册
            if (checkCandidate(beanName, candidate)) {
                //将对应BeanDefinition封装成对应BeanDefinitionHolder对象
                BeanDefinitionHolder definitionHolder = new BeanDefinitionHolder(candidate, beanName);
                //设置作用域代理模式
                definitionHolder =
                    AnnotationConfigUtils.applyScopedProxyMode(scopeMetadata, definitionHolder, this.registry);
                //添加到beanDefinitions集合中去,最后进行返回。
                beanDefinitions.add(definitionHolder);
                //将这个BeanDefinitionHolder添加到BeanFactory的BeanDefinitionMap中去
                registerBeanDefinition(definitionHolder, this.registry);
            }
        }
    }
    return beanDefinitions;
}

至此整个Spring中的扫描机制就讲清楚了,主要要考虑的就是两个过滤的方法,那么我们怎么去写自己的注解呢?类似MapperScan这个注解

4.模拟自定义注解

我们只需要修改原来对应的两个过滤函数的返回值即可完成接口扫描。具体的测试的代码如下:

package com.ys.mapperScan;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface MyScan {
}
package com.ys.mapperScan;

import org.springframework.context.annotation.Configuration;

@Configuration
@MyScan()
public class AppConfig {
}

package com.ys.mapperScan;

import org.springframework.beans.factory.annotation.AnnotatedBeanDefinition;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.context.annotation.ClassPathBeanDefinitionScanner;
import org.springframework.core.type.classreading.MetadataReader;

import java.io.IOException;

public class MyMapperScan extends ClassPathBeanDefinitionScanner {

    public MyMapperScan(BeanDefinitionRegistry registry) {
        super(registry);
    }

    //第一个过滤,扫描所有的类
    @Override
    protected boolean isCandidateComponent(MetadataReader metadataReader) throws IOException {
        return true;
    }

    //第二个过滤,返回的是接口
    @Override
    protected boolean isCandidateComponent(AnnotatedBeanDefinition beanDefinition) {
        return beanDefinition.getMetadata().isInterface() && beanDefinition.getMetadata().isIndependent();
    }
}

package com.ys.mapperScan.inte;

public interface Test {
}

package com.ys.mapperScan;

import org.springframework.context.annotation.AnnotationConfigApplicationContext;

public class Main {
    public static void main(String[] args) {
        AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
        context.register(AppConfig.class);
        context.refresh();
        MyMapperScan myMapperScan = new MyMapperScan(context);
        int scan = myMapperScan.scan("com.ys.mapperScan.inte");
        System.out.println(scan);
    }
}

运行的结果如下:

在这里插入图片描述

扫描的接口真好是一个,由于篇幅有限,就简单的写下了。笔者会在后面模拟一个MapperScan注解

5.写在最后

本篇博客主要介绍AnnotationConfigApplicationContext类的初始化的过程,同时介绍对应的两个属性readerscanner属性。然后介绍了下Spring的扫描的机制,还简单的模拟了一个自定义注解。

  • 2
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值