|
众所周知,将对象交给spring管理有三种方法
1. @Bean, 即
public class AppConfig {
@Bean
AccDataCountMapper accDataCountMapper(){
//实例化一个 AccDataCountMapper 对象
AccDataCountMapper accDataCountMapper = (AccDataCountMapper) Proxy.newProxyInstance(SelfFactoryBean.class.getClassLoader(), new Class[]{AccDataCountMapper.class}, new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
//先不做实现
return null;
}
});
return accDataCountMapper;
}
}
问题来了,如果只有一个两个mapper,还好,一堆的话,不是要写到吐。不便于扩展。
2.注册
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(AppConfig.class);
context.getBeanFactory().registerSingleton("accMapper", AccDataCountMapper.class);
通过这种方式直接注册了,就失去了交给spring 管理的意义了。
3.FactoryBean
public class SelfFactoryBean implements FactoryBean {
@Override
public Object getObject() throws Exception {
//实例化一个 AccDataCountMapper 对象
AccDataCountMapper accDataCountMapper = (AccDataCountMapper)Proxy.newProxyInstance(SelfFactoryBean.class.getClassLoader(), new Class[]{AccDataCountMapper.class}, new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
//先不做实现
return null;
}
});
return accDataCountMapper;
}
@Override
public Class<?> getObjectType() {
return AccDataCountMapper.class;
}
@Override
public boolean isSingleton() {
return true;
}
}
当然,目前还是静态的,即通过指定类。
修改下
public class SelfFactoryBean implements FactoryBean {
//动态实例化
private Class mapper;
public SelfFactoryBean(Class mapper){
this.mapper = mapper;
}
@Override
public Object getObject() throws Exception {
// Proxy.newProxyInstance jdk 代理对象
Object o = Proxy.newProxyInstance(SelfFactoryBean.class.getClassLoader(), new Class[]{mapper}, new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
//判断是否有这个类
if(proxy.getClass().equals(method.getDeclaringClass())){
return method.invoke(this,args);
}else{
// 执行代理逻辑
return null;
}
}
});
return o;
}
@Override
public Class<?> getObjectType() {
return mapper;
}
@Override
public boolean isSingleton() {
return false;
}
}
这下子是个动态生成了,比较灵活,不用写死,便于扩展。
但是问题又来了。mapper从哪来呢,在哪里传过来呢。动态生成了但是不能用啊。
这时候就需要了解下 ImportBeanDefinitionRegistrar动态注册bean 了。
我有动态bean,你可以动态注册,两者结合下
public class SelfImportBeanDefinitionRegister implements ImportBeanDefinitionRegistrar {
@Override
public void registerBeanDefinitions(AnnotationMetadata annotationMetadata, BeanDefinitionRegistry beanDefinitionRegistry) {
//mapper 集合
List<Class> maps = new ArrayList<>();
maps.add(AccDataCountMapper.class);
maps.add(ProductGoodsMapper.class);
for(Class mapper : maps){
//通过spring的 BeanDefinitionBuilder 生成 SelfFactoryBean,并在SelfFactoryBean
BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition();
AbstractBeanDefinition bd = builder.getBeanDefinition();
//传入要实例化的mapper 类型
bd.getConstructorArgumentValues().addGenericArgumentValue(mapper);
bd.setBeanClass(SelfFactoryBean.class);
beanDefinitionRegistry.registerBeanDefinition("self" + mapper.getSimpleName(),bd);
}
}
}
当然,这里也是个静态的注册,不够灵活
这里的静态注册,结合上面的动态bean工厂 ,实现了 静态注册。
接下来实现动态注册
动态注册的话就需要用到扫描pack了,即配置一个mapper路径,自己扫描路径的mapper
@Retention(RetentionPolicy.RUNTIME)
@Import(SelfImportBeanDefinitionRegister.class)
public @interface SelfScan {
String value() default "";//要扫描的路径
}
创建一个注解,用来配置扫描的pack路径。 我们这个 SelfImportBeanDefinitionRegister 动态注册,要自动扫描的是 SelfScan 注解的路径,所以这里引入下 @Import(SelfImportBeanDefinitionRegister.class) 。
修改下静态注册为动态注册
public class SelfImportBeanDefinitionRegister implements ImportBeanDefinitionRegistrar {
@Override
public void registerBeanDefinitions(AnnotationMetadata annotationMetadata, BeanDefinitionRegistry beanDefinitionRegistry) {
/**
* ImportBeanDefinitionRegistrar 配合 @Import 使用
* spring 通过在 SelfScan 引入的使用:这一行 @Import(SelfImportBeanDefinitionRegister.class) 找到 @SelfScan("com.gifmall.mapper") 配置的value值,即: com.gifmall.mapper
*/
Map<String,Object> annotationAttributes = annotationMetadata.getAnnotationAttributes(SelfScan.class.getName());
/**
* 等同于
* AnnotationAttributes mapAttr = AnnotationAttributes.fromMap(annotationAttributes);
* mapAttr.getStringArray("value");
*/
String o = annotationAttributes.get("value").toString();
System.out.println(o.toString());
System.out.println("开始扫描pack ");
List<String> mapperList = new ArrayList<>();
for(String s : o.split(",")){
System.out.println("开始扫描 : "+ s);
//扫描pack下的bean,并进行添加
mapperList.addAll(basePackageScan(s));
}
System.out.println("扫描结束");
System.out.println("开始打印pack 并进行注册");
for(String map : mapperList){
System.out.println("开始注册 :" + map);
Class cla = null;
try {
//通过类路径和类名转换成类
cla = Class.forName(map);
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
//通过spring的 BeanDefinitionBuilder 生成 SelfFactoryBean,并注册Bean
BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition();
AbstractBeanDefinition bd = builder.getBeanDefinition();
//传入要实例化的mapper 类型
bd.getConstructorArgumentValues().addGenericArgumentValue(cla);
bd.setBeanClass(SelfFactoryBean.class);
beanDefinitionRegistry.registerBeanDefinition("self" + cla.getSimpleName(),bd);
}
System.out.println("打印、注册结束");
}
private List<String> basePackageScan(String packName){
List<String>classNames = new ArrayList<String>();
URL url = this.getClass().getClassLoader().getResource(packName.replaceAll("\\.", "/"));
String fileUrl = url.getFile()+"/";// E:/workspace/com/spring/mvc/
//由于上面读取中文会URLEncode ,所以这里将中文路径生成的class写死
fileUrl = "F:\\项目\\gifmall\\out\\production\\gifmall\\com\\gifmall\\mapper\\";
File file = new File(fileUrl);//把路径转为文件目录
String[] fileStr = file.list();//列出文件目录所有文件和文件夹的名字
for (String path : fileStr) {//遍历所有的文件或文件夹的名字
File pathFile = new File(fileUrl+path);//根据url和名字拼装成完整的url路径E:/workspace/com/spring/mvc
if(pathFile.isDirectory()){
basePackageScan(packName+"."+path);
}else{//class 文件
//mybatis.mapper.UserMapper
classNames.add(packName+"."+pathFile.getName().replace(".class", ""));
}
}
return classNames;
}
}
自此,动态注册,动态代理,注解扫描mapper pack 都已经完成了。注解配置在哪呢
当然是Appconfig 了
@Configuration//加了全配置注解会被cglib增强
@SelfScan("com.gifmall.mapper")
public class AppConfig {
}
告诉他扫描这个pack 下的mapper
测试一下
public class TestMain {
public static void main(String[] args) {
//通过注解方式启动spring。 Appconfig 相当于通过spring 的 xml配置
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(AppConfig.class);
//直接注册方式
// context.getBeanFactory().registerSingleton("accMapper", AccDataCountMapper.class);
//因为 在 registerBeanDefinitions 中注册的时候加入了 self 所以这里取的时候也需要加下
context.getBean("self" + "AccDataCountMapper");
}
}
结果:

至此,spring 整合mybaits 底层实现结束。
当然了,可能不太好理解。很多问号????
首先,spring 已经可以管理很多bean了。而我有一堆mapper ,要怎么交给spring 管理呢。
肯定不能把这一堆mapper直接扔给spring注册的。现在有10个,20个,你可以都写出来,给它。以后来了50个,100个呢。
所以就需要做到自动扫描了。--- SelfImportBeanDefinitionRegister + 注解 SelfScan 实现
然后我有这么多mapper,每个mapper类型是不是不一样,怎么保证我要的是我要的类型呢。这就需要动态代理了 -- SelfFactoryBean
自动扫描+动态代理都完成了,需要告诉下spring,自动扫描谁了,在 AppConfig 配置要扫描的mapper pack。
最后进行测试
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(AppConfig.class);
context.getBean("self" + "AccDataCountMapper");
辅助参考文章:
https://blog.csdn.net/liurenyou/article/details/97025292
ImportBeanDefinitionRegistrar动态注册bean
https://blog.csdn.net/jiachunchun/article/details/94569246
视频
https://www.bilibili.com/video/BV1p5411x7DB?p=1 |