- 定义一个EnableMapperScan注解
@Import(MapperProxyHandlerRegister.class) 标注将MapperProxyHandlerRegister导入到容器中。
@Target({ElementType.METHOD,ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Import(MapperProxyHandlerRegister.class)
public @interface EnableMapperScan {
String baskPackages();
}
- MapperProxyHandlerRegister
ImportBeanDefinitionRegistrar 是一个特殊组件,实现了这个接口的组件registry会动态给容器中批量注册组件,这里的关键就是获取到baskPackages 指定的路径,然后批量扫描classpath下的所有的接口,然后定义BeanDefinition,注册到容器中。
注意:AnnotationMetadata metadata封装的是标注了@Import注解的那个类。这样就可以拿到最终因哪个类扫描的这个配置类。
/**
* Mapper组件注册器
*/
public class MapperProxyHandlerRegister implements ImportBeanDefinitionRegistrar {
/**
* @param metadata 注意:metadata是Spring解析@Import注解标注的那个类的元信息,而不是导入的配置类的元信息
* @param registry BeanDefinition注册器
* @param generator BeanName生成器
*/
@Override
public void registerBeanDefinitions(AnnotationMetadata metadata,
BeanDefinitionRegistry registry,
BeanNameGenerator generator) {
//这里的这个metadata封装的是标注了@Import注解的那个类,如果标注@Import注解因其他类似@Enablexxx注解进一步解析到的
//那这里的metadata封装的就是解析@Enablexxx那个类的元数据信息,
//总之,metadata封装的就是Spring原始解析类上关联的@Import注解的类
Map<String, Object> annotationAttributes = metadata.getAnnotationAttributes(EnableMapperScan.class.getName());
String baskPackages = annotationAttributes.get("baskPackages").toString();
//获取此包下所有的类
try {
Set<Class<?>> scan = scan(baskPackages);
if (CollUtil.isNotEmpty(scan)){
//遍历接口
for (Class<?> mapperClass : scan) {
//注册到容器
RootBeanDefinition definition = new RootBeanDefinition();
definition.setBeanClass(MapperFactoryBean.class);
//设置构造方法参数值
ConstructorArgumentValues constructorArgumentValues = new ConstructorArgumentValues();
constructorArgumentValues.addIndexedArgumentValue(0,mapperClass);
definition.setConstructorArgumentValues(constructorArgumentValues);
//definition.setFactoryMethodName("getObject");
//注册Bean
registry.registerBeanDefinition(mapperClass.getName(),definition);
}
}
} catch (Exception e) {
e.printStackTrace();
}
}
/*
1. 创建A --> 依赖B --> 创建B(依赖A) -> 为A创建代理对象 ,并从缓存中拿,后注入A的是一个代理对象。
* */
public static Set<Class<?>> scan(String basePackage) throws IOException, ClassNotFoundException {
// 创建一个扫描器
//ClassPathScanningCandidateComponentProvider scanner = new ClassPathScanningCandidateComponentProvider(false);
// 可以添加过滤器来筛选特定类型的类,这里先不添加任何过滤器,获取所有类
// 例如,如果只想获取带有特定注解的类,可以添加如下代码
// scanner.addIncludeFilter(new AnnotationTypeFilter(YourAnnotation.class));
// 如果只想获取特定接口的实现类,可以添加如下代码
// scanner.addIncludeFilter(new AssignableTypeFilter(YourInterface.class));
// 构建要扫描的资源路径模式
ResourcePatternResolver resolver = new PathMatchingResourcePatternResolver();
String packageSearchPath = ResourcePatternResolver.CLASSPATH_ALL_URL_PREFIX +
resolveBasePackage(basePackage) + "/**/*.class";
Resource[] resources = resolver.getResources(packageSearchPath);
MetadataReaderFactory metadataReaderFactory = new SimpleMetadataReaderFactory();
Set<Class<?>> classes = new HashSet<>();
for (Resource resource : resources) {
MetadataReader metadataReader = metadataReaderFactory.getMetadataReader(resource);
String sourceClassName = metadataReader.getClassMetadata().getClassName();
if (metadataReader.getClassMetadata().isInterface()){
//只扫描mapper接口
classes.add(Class.forName(sourceClassName));
}
//System.out.println("sourceClassName = " + sourceClassName);
// 排除接口和抽象类,只获取具体类
//if (!metadataReader.getClassMetadata().isInterface() &&!metadataReader.getClassMetadata().isAbstract()) {
// String className = metadataReader.getClassMetadata().getClassName();
// classes.add(Class.forName(className));
//}
}
return classes;
}
private static String resolveBasePackage(String basePackage) {
return basePackage.replace('.', '/');
}
- MapperProxy
这个类封装了代理对象的核心方法,主要就是获取mapper接口上标注了@Select @Update @Delete注解的方法,解析sql参数,获取sqlsessionfactory,然后执行sql。这里简单模拟输出获取到的sql。
/**
mapper组件动态代理方法拦截实现
*/
public class MapperProxy implements InvocationHandler {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
if (method.getDeclaringClass() == Object.class){
return method.invoke(this,args);
}
String name = method.getDeclaringClass().getName();
String methodName = method.getName();
System.out.println("mapperProxy 执行方法,被代理对象拦截 ===》 " + name + "." +methodName + "()");
Select select = method.getAnnotation(Select.class);
if (select != null){
String[] value = select.value();
//拦截方法
System.out.println("拦截到的SQL:===> " + Arrays.toString(value));
}
Class<?> returnType = method.getReturnType();
if (Number.class.isAssignableFrom(returnType) || returnType.isPrimitive()){
return 1;
}
return null;
}
}
- MapperFactoryBean
这个类实现了FactoryBean 接口,实现了这个接口的类Spring会进一步调用getObject方法创建Bean对象,这个Bean对象的类型是getObjectType返回的数据类型,由此,这个类型就是真正的Mapper接口类型,可以实现基于Mapper接口的自动装配。
public class MapperFactoryBean implements FactoryBean {
private Class mapperInterFaceClazz;
public MapperFactoryBean(Class mapperInterFaceClazz){
this.mapperInterFaceClazz = mapperInterFaceClazz;
}
@Override
public Object getObject() throws Exception {
/**
在创建代理对象时,传入需要实现的接口mapperInterFaceClazz
如果这个接口还继承了其他接口,在基于实现类调方法时,父接口的方法也会被代理对象拦截。
原理很简单,面向对象三大特征之一: 继承,子类继承父类,就把父类所有的内容全都继承
JVM在判断时,如果满足继承结构以及安全检查
*/
return Proxy.newProxyInstance(mapperInterFaceClazz.getClassLoader(),new Class[]{mapperInterFaceClazz},new MapperProxy());
}
@Override
public Class<?> getObjectType() {
return mapperInterFaceClazz;
}
}
- MainApp
@SpringBootApplication
@EnableMapperScan(baskPackages = "com.example.ssm3.mapper")