Mybatis整合Spring思路
需要解决的问题和解决思路
1、如何将接口注入到容器
这个在Mybatis其实已经做好了,是通过动态代理的方式,将接口由代理类注入;
但是要采取一种合适的方式注入,我们常用的方式有@component注解,或是通过@configuration和@bean注解,但是这里显然都不能满足需求。
采取的做法是通过FactoryBean和ImportBeanDefinitionRegistrar的方式
beanFactoryPostProcessor
bean工厂的后置处理器,只能修改beanDeinition,不能往里面新增
ImportBeanDefinitionRegister 可以实现新增beanDefinition
2、如果扫描到mapper注解
通过spring的工具类方式,代码里体现
核心代码
实现FactoryBean
/**
* 通过动态代理生成mapper的实体类
* 实现FactoryBean方法,用于配置到ImportBeanDefinitionRegistrar
*/
public class MapperProxy implements FactoryBean<Object> {
private Class<?> clazz;
/**
* 将需要代理的接口通过参数传递进来
*
* @param clazz
*/
public MapperProxy(Class<?> clazz) {
this.clazz = clazz;
}
@Override
public Object getObject() throws Exception {
//动态代理接口
return Proxy.newProxyInstance(MapperProxy.class.getClassLoader(), new Class[]{clazz}, new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) {
return method.getAnnotation(Select.class).value();
}
});
}
@Override
public Class<?> getObjectType() {
return clazz;
}
}
实现ImportBeanDefinitionRegistrar
/**
* 实现ImportBeanDefinitionRegistrar的registerBeanDefinitions方法
* 可以通过构造beanDefinition来注入bean
* AnnotationMetadata importingClassMetadata这个变量可以获取主类上的所有注解
*/
@Component
public class BeanRegister implements ImportBeanDefinitionRegistrar {
@Override
public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
Map<String, Object> annotationAttributes = importingClassMetadata.getAnnotationAttributes(MapperScan.class.getName());
String basePackage = (String) annotationAttributes.get("value");
Map<String, Class> map = ScanUtils.getMap(basePackage, Mapper.class);
for (Class clazz : map.values()) {
BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition();
AbstractBeanDefinition beanDefinition = builder.getBeanDefinition();
beanDefinition.setBeanClassName(MapperProxy.class.getName());
beanDefinition.getConstructorArgumentValues().addGenericArgumentValue(clazz);
registry.registerBeanDefinition(clazz.getSimpleName(), beanDefinition);
}
}
}
主类
需要使用Import注解
/**
* 实现mybatis整合spring的大致思路
* 主要思路为
* 1、通过jdk动态代理生成接口mapper的代理类
* 2、将代理类注入到容器
* 2.1 采用BeanFactory的方式,工厂化生成代理类
* 2.2 采用ImportBeanDefinitionRegistrar,直接定义BeanDefinition来构造bean
* 3、指定包扫描注解(ScanUtils)
* 测试方法在com.lexiaoyao.springmybatis.SpringMybatisApplicationTests
*/
@SpringBootApplication
@Import(BeanRegister.class)//需要通过Import注解来导入BeanRegister
@MapperScan("com.lexiaoyao.springmybatis")
public class SpringMybatisApplication {
public static void main(String[] args) {
SpringApplication.run(SpringMybatisApplication.class, args);
}
}
扫包工具类
public class ScanUtils {
private static final String RESOURCE_PATTERN = "/**/*.class";
/**
* 可以获取指定路径下的带有某个注解的全部类
*
* @param path 指定路径
* @param annoClass 指定要寻找的注解
* @return
*/
public static Map<String, Class> getMap(String path, Class<? extends Annotation> annoClass) {
Map<String, Class> handlerMap = new HashMap<String, Class>();
//spring工具类,可以获取指定路径下的全部类
ResourcePatternResolver resourcePatternResolver = new PathMatchingResourcePatternResolver();
try {
String pattern = ResourcePatternResolver.CLASSPATH_ALL_URL_PREFIX +
ClassUtils.convertClassNameToResourcePath(path) + RESOURCE_PATTERN;
Resource[] resources = resourcePatternResolver.getResources(pattern);
//MetadataReader 的工厂类
MetadataReaderFactory readerfactory = new CachingMetadataReaderFactory(resourcePatternResolver);
for (Resource resource : resources) {
//用于读取类信息
MetadataReader reader = readerfactory.getMetadataReader(resource);
//扫描到的class
String classname = reader.getClassMetadata().getClassName();
Class<?> clazz = Class.forName(classname);
//判断是否有指定主解
Annotation annotation = clazz.getAnnotation(annoClass);
if (annotation != null) {
//将注解中的类型值作为key,对应的类作为 value
handlerMap.put(classname, clazz);
}
}
} catch (IOException | ClassNotFoundException e) {
}
return handlerMap;
}
}
git
https://github.com/lexiaoyao1995/spring-mybatis