思想来源
其实这个思想是来源于mybatis和spring的整合,为什么通过@MapperScan(basePackages = { “demo.mapper” })就能扫描到自定义的Mapper接口,这一节只讲扫描过程,不讲Mapper接口是如何创建Bean对象的,接下来就开始探讨自定义ClassPathBeanDefinitionScanner资源加载器吧
自定Mapper接口
package com.source.controller.ClassPathBeanDefinitionScannerTest.mapper;
public interface TestClassPathScannerEmpMapper {
}
package com.source.controller.ClassPathBeanDefinitionScannerTest.mapper;
public interface TestClassPathScannerUserMapper {
}
MyMapperScan注解
package com.source.controller.ClassPathBeanDefinitionScannerTest;
import org.springframework.context.annotation.Import;
import java.lang.annotation.*;
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@Documented
//所有第三方框架都通过@Import进行整合到spring的
@Import(MyMapperScannerRegistrar.class)
public @interface MyMapperScan {
//要扫描的包
String[] basePackages() default {};
}
启动类
package com.source;
import com.source.controller.ClassPathBeanDefinitionScannerTest.MyMapperScan;
import com.source.controller.ClassPathBeanDefinitionScannerTest.MyMapperScannerConfigurer;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.ConfigurableApplicationContext;
@SpringBootApplication
/**
* 添加自己的注解,并指定要扫描的包
*/
@MyMapperScan(basePackages = {"com.source.controller.ClassPathBeanDefinitionScannerTest.mapper"})
public class SpringSourceApplication {
public static ConfigurableApplicationContext context;
public static void main(String[] args) {
context = SpringApplication.run(SpringSourceApplication.class, args);
MyMapperScannerConfigurer bean = context.getBean("myMapperScannerConfigurer", MyMapperScannerConfigurer.class);
}
}
@Import导入的类
package com.source.controller.ClassPathBeanDefinitionScannerTest;
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.context.annotation.ImportBeanDefinitionRegistrar;
import org.springframework.core.annotation.AnnotationAttributes;
import org.springframework.core.type.AnnotationMetadata;
import org.springframework.util.StringUtils;
import java.beans.Introspector;
import java.util.Arrays;
import java.util.Map;
/**
* MyMapperScannerRegistrar:这个类会在一开始解析SpringSourceApplication启动类的时候
* 通过解析@MyMapperScan注解中的@Import导入的,那么spring就会回调registerBeanDefinitions()方法
*/
public class MyMapperScannerRegistrar implements ImportBeanDefinitionRegistrar {
/**
* 1:importingClassMetadata:是什么,就是当前正在解析的beanClass类的元信息,这里是:SpringSourceApplication类
* 2:该方法是在ConfigurationClassPostProcessor解析beanDefinition时候回调的
* 3:为什么能回调?因为该类实现了ImportBeanDefinitionRegistrar接口,具体去看源码
* 4:registerBeanDefinitions()能做什么?能注册beanDefinition到容器中
*/
@Override
public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
//importingClassMetadata:这个就是通过哪个类来导入的当前MyMapperScannerRegistrar
//获取@MapperScan注解中的所有属性值,返回map
Map<String, Object> sacnMap = importingClassMetadata.getAnnotationAttributes(MyMapperScan.class.getName());
//转成AnnotationAttributes的目的是不用做map中value的强制类型转换了
AnnotationAttributes attributes = AnnotationAttributes.fromMap(sacnMap);
//执行beanDefinition的注册
registerBeanDefinitions(attributes,registry);
}
void registerBeanDefinitions(AnnotationAttributes annoAttrs, BeanDefinitionRegistry registry){
//构建bd对象
BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition(MyMapperScannerConfigurer.class);
//此处利用内置的javaBean将字符串首字母小写
String beanName=Introspector.decapitalize(MyMapperScannerConfigurer.class.getSimpleName());
String[] basePackages = annoAttrs.getStringArray("basePackages");
//向db中设置属性,StringUtils这个方法就是将集合一 " , " 分割 拼成字符串
builder.addPropertyValue("basePackage", StringUtils.collectionToCommaDelimitedString(Arrays.asList(basePackages)));
//注册bean定义信息,后期会创建MyMapperScannerConfigurer的bean对象
registry.registerBeanDefinition(beanName,builder.getBeanDefinition());
}
}
资源扫描配置类后置处理器
package com.source.controller.ClassPathBeanDefinitionScannerTest;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.beans.factory.support.BeanDefinitionRegistryPostProcessor;
/**
* 该类是bean工厂的后置处理器
* 1:要知道程序先执行了ConfigurationClassPostProcessor后置处理器(因为排序了),解析了所有的资源文件,并且注册了beanDefinition
* 2:当前这个类,也生成了beanDefition对象,但是主要的一点是这个类实现了BeanDefinitionRegistryPostProcessor接口
* 3:实现了BeanDefinitionRegistryPostProcessor接口,那么在刷新12步骤中就会创建所有的bean工厂后置处理器对象,并注册到一级缓存中
* 4:并且会回调postProcessBeanDefinitionRegistry()方法
*/
public class MyMapperScannerConfigurer implements BeanDefinitionRegistryPostProcessor {
private String basePackage;
public String getBasePackage() {
return basePackage;
}
public void setBasePackage(String basePackage) {
this.basePackage = basePackage;
}
/**
*会回调bean工厂的增强方法,并把容器对象传过来
*/
@Override
public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException {
//创建自定义资源解析器对象
MyClassPathMapperScanner mapperScanner = new MyClassPathMapperScanner(registry);
//注册过滤器,目的是检查所有的mapper接口是否是备选组件
mapperScanner.registerFilters();
//对该包下的资源进行扫描
mapperScanner.scan(basePackage);
}
@Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory configurableListableBeanFactory) throws BeansException {
}
}
自定义资源扫描器实现
package com.source.controller.ClassPathBeanDefinitionScannerTest;
import org.springframework.beans.factory.annotation.AnnotatedBeanDefinition;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.config.BeanDefinitionHolder;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.context.annotation.ClassPathBeanDefinitionScanner;
import org.springframework.core.type.AnnotationMetadata;
import java.util.Set;
public class MyClassPathMapperScanner extends ClassPathBeanDefinitionScanner {
public MyClassPathMapperScanner(BeanDefinitionRegistry registry) {
super(registry,false);
}
public void registerFilters() {
boolean acceptAllInterfaces = true;
if (acceptAllInterfaces) {
//直接返回true,让当前包下的所有类都通过,这一步只要是重写TypeFilter中的match()方法
//在super.doScan()中会回调这个函数接口的match()
addIncludeFilter((metadataReader, metadataReaderFactory) -> true);
}
//排除package-info.java
addExcludeFilter((metadataReader, metadataReaderFactory) -> {
String className = metadataReader.getClassMetadata().getClassName();
return className.endsWith("package-info");
});
}
@Override
protected Set<BeanDefinitionHolder> doScan(String... basePackages) {
//这里又去调用父类的doScan()方法,就是让spring 去加载我们的mapper接口
Set<BeanDefinitionHolder> beanDefinitions = super.doScan(basePackages);
processBeanDefinitions(beanDefinitions);
return beanDefinitions;
}
/**
* 在加载beanDefinition的时候,主要是这个判断,用来判断是否是候选组件的
* 只有是候选组件spring才会为当前资源类创建beanDefinition对象
*/
@Override
protected boolean isCandidateComponent(AnnotatedBeanDefinition beanDefinition){
//获取当前bean的大Class中的元信息
AnnotationMetadata metadata = beanDefinition.getMetadata();
//若是一个接口那么就返回true
return metadata.isInterface();
}
private void processBeanDefinitions(Set<BeanDefinitionHolder> beanDefinitions){
BeanDefinitionRegistry registry = super.getRegistry();
for (BeanDefinitionHolder holder : beanDefinitions) {
BeanDefinition beanDefinition = holder.getBeanDefinition();
String beanClassName = beanDefinition.getBeanClassName();
Class<? extends BeanDefinition> mapperClass = beanDefinition.getClass();
//TODO 到这里就完成了mapper接口的加载,后面是为接口生成代理对象,后期会讲到
}
}
}