? ? ? ?上一篇詳細(xì)講解了spring的掃描器ClassPathBeanDefinitionScanner
惠爽,本篇我們我們將模擬mybatis如何通過spring完成Mapper掃描得哆,講解如何通過spring編寫自定義掃描器怯晕。
? ? ? ?既然ClassPathBeanDefinitionScanner
完成了spring的掃描功能,我們完全可以繼承這個類來達(dá)到創(chuàng)建自定義掃描器的目的箱吕。
public class CustomScanner extends ClassPathBeanDefinitionScanner {
public CustomScanner(BeanDefinitionRegistry registry) {
super(registry);
}
}
? ? ? ?這就是我們的自定義掃描器芥驳,當(dāng)然什么功能也沒有,再次基礎(chǔ)上我們繼續(xù)進(jìn)行擴展茬高,上一篇我們講過ClassPathBeanDefinitionScanner
通過includeFilters
來過濾符合條件的業(yè)務(wù)類兆旬,那我們增加我們的掃描規(guī)則即可。如何includeFilters
在添加我們的掃描規(guī)則怎栽?很簡單丽猬,實現(xiàn)addIncludeFilter
即可
public class CustomScanner extends ClassPathBeanDefinitionScanner {
public CustomScanner(BeanDefinitionRegistry registry) {
super(registry);
}
@Override
public void addIncludeFilter(TypeFilter includeFilter) {
super.addIncludeFilter(includeFilter);
}
}
? ? ? ?也就是說,我們只要編寫我們的規(guī)則類TypeFilter 就行了婚瓜,剩下的交給ClassPathBeanDefinitionScanner來完成宝鼓,有讀者嘲諷:“我還以為你要模擬一個ClassPathBeanDefinitionScanner”,答:“我閑的巴刻?mybatis也是利用了ClassPathBeanDefinitionScanner好嗎愚铡?”。
? ? ? ?TypeFilter只是一個接口胡陪,它的實現(xiàn)類在哪里沥寥?spring啟動時會默認(rèn)生成一個ClassPathBeanDefinitionScanner對象,該對象在初始化時會生成一坨默認(rèn)的過濾器(上篇講過):
protected void registerDefaultFilters() {
this.includeFilters.add(new AnnotationTypeFilter(Component.class));
ClassLoader cl = ClassPathScanningCandidateComponentProvider.class.getClassLoader();
try {
this.includeFilters.add(new AnnotationTypeFilter(ClassUtils.forName("javax.annotation.ManagedBean", cl), false));
this.logger.trace("JSR-250 'javax.annotation.ManagedBean' found and supported for component scanning");
} catch (ClassNotFoundException var4) {
}
try {
this.includeFilters.add(new AnnotationTypeFilter(ClassUtils.forName("javax.inject.Named", cl), false));
this.logger.trace("JSR-330 'javax.inject.Named' annotation found and supported for component scanning");
} catch (ClassNotFoundException var3) {
}
}
? ? ? ?看到?jīng)]柠座,就是AnnotationTypeFilter
啦邑雅!
OK,我們先自定義一個注解:
public @interface MyAnnotation {
}
再生成兩個業(yè)務(wù)類妈经,加上上面的注解:
@MyAnnotation
public class MyClass {
}
@MyAnnotation
public class MyClass2 {
}
測試以下:
public class Test {
public static void main(String[] args) {
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
//注冊配置類
context.register(Config.class);
CustomScanner customScanner = new CustomScanner(context);
customScanner.addIncludeFilter(new AnnotationTypeFilter(MyAnnotation.class));
//將帶有MyAnnotation注解的類掃描到springIOC容器中淮野,并返回掃描的個數(shù)
int num = customScanner.scan("com.scan");
System.out.println("掃描的個數(shù):"+num);
context.refresh();
System.out.println(context.getBean(MyClass.class));
}
}
打印結(jié)果:
掃描的個數(shù):2
com.scan.MyClass@1199fe66
? ? ? ?哈哈捧书,簡單吧!其實骤星,這就是spring擴展经瓷,要擴展就得懂spring源碼,歡迎讀者關(guān)注本專題洞难,我將帶大家深入分析并解開spring源碼神秘面紗∮咚保現(xiàn)在java開發(fā),逃不掉spring全家桶队贱!
? ? ? ?spring只能掃描實體類色冀,抽象類和接口是不能掃描的,看上篇講過的源碼:
if (isCandidateComponent(sbd)) {
if (debugEnabled) {
logger.debug("Identified candidate component class: " + resource);
}
//返回BeanDefinition 注冊到 BeanFactory
candidates.add(sbd);
}
? ? ? ?isCandidateComponent這個方法判斷 如果是實體類 返回true,如果是抽象類柱嫌,但是抽象方法 被 @Lookup 注解注釋返回true锋恬。
protected boolean isCandidateComponent(AnnotatedBeanDefinition beanDefinition) {
AnnotationMetadata metadata = beanDefinition.getMetadata();
return (metadata.isIndependent() && (metadata.isConcrete() ||
(metadata.isAbstract() && metadata.hasAnnotatedMethods(Lookup.class.getName()))));
}
也就是說如果你是接口,isCandidateComponent返回的是false慎式,candidates.add(sbd);就不執(zhí)行了伶氢,自然添加不到IOC容器中,mybatis就是修改了這個方法瘪吏,讓Mapper接口能掃描到IOC容器中的癣防,我們先模擬:
@MyAnnotation
public interface MyInterFace {
@Select("select * from table")
public void select();
}
public class CustomScanner extends ClassPathBeanDefinitionScanner {
public CustomScanner(BeanDefinitionRegistry registry) {
super(registry);
}
@Override
public void addIncludeFilter(TypeFilter includeFilter) {
super.addIncludeFilter(includeFilter);
}
@Override
protected boolean isCandidateComponent(MetadataReader metadataReader) throws IOException {
//如果是接口,則返回true掌眠,會添加到IOC容器中去
return beanDefinition.getMetadata().isInterface();
}
}
public class Test {
public static void main(String[] args) {
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
//注冊配置類
context.register(Config.class);
CustomScanner customScanner = new CustomScanner(context);
customScanner.addIncludeFilter(new AnnotationTypeFilter(MyAnnotation.class));
int num = customScanner.scan("com.scan");
System.out.println("掃描的個數(shù):"+num);
context.refresh();
}
}
打印結(jié)果
掃描的個數(shù):1
然后蕾盯,spring會根據(jù)MyInterFace生成一個動態(tài)代理類,并通過反射拿到你所有的方法蓝丙,拿到方法后再通過反射拿到你的注解信息级遭,還需要我繼續(xù)說嗎?
實際上,mybatis就是這么做的渺尘,不信看mybatis源碼:
ClassPathMapperScanner.java // mybatis的自定義掃描類
protected boolean isCandidateComponent(AnnotatedBeanDefinition beanDefinition) {
return beanDefinition.getMetadata().isInterface() && beanDefinition.getMetadata().isIndependent();
}
? ? ? ?這就是spring擴展開發(fā)挫鸽,插件開發(fā)就是這個原理吧,前提是你要對spring足夠的掌握鸥跟,歡迎大家關(guān)注本專題丢郊,一起精通spring源碼。