1.写在前面
前面我们已经介绍了Spring
的几种BeanDefinition
,同时介绍了几种常用的BeanDefinition
中的对应的属性。以及几种BeanDefinition的实现类的区别,同时也介绍了RootBeanDefinition
和ChildBeanDefinition
的缺点,以及为什么引入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
中的两个属性:reader
和scanner
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();
}
}
上面的代码表示所有的构造函数,你会发现都会调用对应的无参的构造函数。无参的构造函数有对应reader
和scanner
属性,同时给这个两个属性进行对应的初始化。本节主要讲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)
,这个方法就是注册一个BeanDefinition
到Spring
的容器中去。那么重点来了,Spring
怎么将AppConfig
这个类变成一个AnnotatedGenericBeanDefinition
添加到Spring
的BeanFactory
的beanDefinitionMap
中去。这就是这个方法的职责。具体的代码如下:
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
中属性,例如:lazy
,primary
,dependsOn
,role
,description
等属性。如果没有配置的话,就直接用默认的。最后将这个对象包装成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
对象就创建好了。同时往ClassPathBeanDefinitionScanner
的includeFilters
中的属性中添加一个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
添加到BeanFactory
中BeanDefinitionMap
中去。我们再回到原来的代码的地方
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
类的初始化的过程,同时介绍对应的两个属性reader
和scanner
属性。然后介绍了下Spring的扫描的机制,还简单的模拟了一个自定义注解。