写一下mybati是如何整合spring的关键代码
这是在b站看一个大佬分析源码后相当于对看后的一个总结,以前总搞不懂读spring源码有什么,以前就以为是单纯的学习他的设计模式,设计思想当然这些也很重要,但是对于现在的我来说。看了总是忘记,但是今天看了哪个大佬讲以后发现 看了spring源码以后我们必须得从设计者的角度取思考当初为什么这么设计,以及当前spring的源码对于我有什么用,哪些其他框架用了这些spring的留下的特性。像mybatis整合spring 如果mybatis开发人员没有对于spring源码非常熟悉,我觉得应该是不可能写出那样优美的代码的。
所以希望从今天开始,在未来能够对很多框架,甚至操作系统的底层非常了解,能够知其然并且知其所以然。废话不多说 开始分析。
我们都知道Mybatis只需要写一个接口就可以帮我们写成代理对象帮我们完成操作。
先简单的写一个实现
public interface userDao {
@Select("select * from test")
public void test();
}
public class MyInvocationHandler implements InvocationHandler{
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("连接数据库");
System.out.println("query db:"+method.getAnnotation(Select.class).value()[0]);
//Object invoke = method.invoke(proxy, args);
System.out.println("断开数据库");
return null;
}
}
@Component
public class MyService {
@Autowired
userDao userDao;
public void test(){
userDao.test();
}
}
到了这里动态代理基本完成 剩下的就是生成代理对象即可,我们通过自动注入userDao然后调用代理对象增强后的方法,但是我们要自动注入必须要注入容器吧。那么我们如何将一个对象注入容器了?
加@ComPonent?这里是要生成代理对象啊 ,对象需要我们自己new 出来 于是我们想到用facotryBean
于是有了这样的代码 ,实现FactoryBean 接口 我们就可以将我们想要的对象 放入spring容器
@Component
public class MyFactoryBean implements FactoryBean {
public MyFactoryBean() {
}
public Object getObject() throws Exception {
Class [] clazz=new Class[]{userDao.class};
userDao o = (userDao) Proxy.newProxyInstance(Main.class.getClassLoader(), clazz, new MyInvocationHandler());
return o;
}
public Class<?> getObjectType() {
return userDao.class;
}
}
然后我们在Main方法来调用一下这时候发现spring容器里面有了代理对象
public class Main {
public static void main(String[] args) {
AnnotationConfigApplicationContext ac=new AnnotationConfigApplicationContext(AppConfig.class);
MyService bean = ac.getBean(MyService.class);
bean.test();
}
}
但是这样有一个问题就是我们生成代理对象都是固定的 于是我们把class当作传说传入写成动态的
但是这样又产生一个问题我们不能直接用@Component注解了因为我们要传参数,如果用了@Component 所有创建过程交给spring spring怎么知道我们要传入哪个class
public class MyFactoryBean implements FactoryBean {
Class MapperInterface;
public MyFactoryBean() {
}
public MyFactoryBean(Class mapperInterface) {
MapperInterface = mapperInterface;
}
public Object getObject() throws Exception {
Class [] clazz=new Class[]{MapperInterface};
Object o = Proxy.newProxyInstance(MyFactoryBean.class.getClassLoader(), clazz, new MyInvocationHandler());
return o;
}
public Class<?> getObjectType() {
return userDao.class;
}
}
于是我们采用
@Bean
MyFactoryBean getMyFactoryBean(){
return new MyFactoryBean(userDao.class);
}
自己配置一个bean 这里下面官网给出的xml配置是异曲同工把,
大家第一次用spring整合mybatis的时候应该都写过这样的代码吧
我们只需要有几个Mapper配置几个Mapper就行了
但是这样也很麻烦我们有100个Mapper岂不是要写100个配置bean或者一个xml配置bean
我们想一想mybatis怎么来实现的 我们用mybatis的时候是不是用了一个MapperScan的注解 于是我们自己定义一个注解
@Retention(RetentionPolicy.RUNTIME)
@Import(MyRegistery.class)
public @interface MyMapperScanner {
}
而spring为我们提供了一个外部接口ImportBeanDefinitionRegistrar 实现这个接口我们可以自己往容器自己注入一个对象。
不知道大家对BeanDefinition有没有了解 看名字就知道bean定义,存储了一些bean的信息,例如bean是不是单例,是不是懒加载等等,初始化bean的时候需要用的这些信息,而所有的BeanDefinition放到一个Map中叫做
BeanDefinitionMap
实现了ImportBeanDefinitionRegistrar接口我们就可以通过我们定义的MyMapperScan扫描到对应路径下所有的类得到全限定类名 然后通过循环一个一个传入一个一个的注册到容器中
```java
public class MyRegistery implements ImportBeanDefinitionRegistrar{
public void registerBeanDefinitions(AnnotationMetadata annotationMetadata, BeanDefinitionRegistry beanDefinitionRegistry) {
//for 循环 遍历 mapeerscan下的所有类
String className="cn.cdut.dao.userDao";
BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition(MyFactoryBean.class);
GenericBeanDefinition beanDefinition = (GenericBeanDefinition) builder.getBeanDefinition();
beanDefinition.getConstructorArgumentValues().addGenericArgumentValue(className);
//修改注入模型 为3(也可以不修改)
beanDefinition.setAutowireMode(3);
beanDefinitionRegistry.registerBeanDefinition("dove", beanDefinition);
//}
}
}
最后为了确认我们的代码没问题展示下运行结果
到此基本的mybatis整合spring的流程就是这样,这是我第一次体会到体会到学源码的作用,
希望自己有能力,有机会参与到一个框架的开发。