1:Mybatis的原理
mybatis的底层使用的是反射机制来实现的,需要了解mybatis的原理,首先需要弄清楚两个问题
1:动态代理,什么是动态代理,以及反射机制,怎么使用反射,以及自定义注解。
2:mybatis的接口为什么可以调用,接口为什么可以生成对象。
第一个问题:什么是动态代理:动态代理就是对原对象的一个加强,在不改变原对象的同时,增加原对象的功能和属性。要实现动态代理必须满足一个条件(代理对象必须实现一个接口),动态代理其实就是和目标对象实现相同的接口,然后将目标对象传入,实现目标对象的同时,动态生成代理对象。目前比较流行的两个代理工具,jdk的动态代理,以及cglib动态代理。
KindWomen kw = (KindWomen) Proxy.newProxyInstance(p.getClass().getClassLoader(), p.getClass().getInterfaces(), new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
method.invoke(p, null);
return 1;
}
});
上述中的kw就是代理对象,代理的目标对象就是p,则增强了目标对象的功能,invoke方法就是执行目标对象,return返回的是目标对象方法执行的结果。反射就不过多介绍了,比较基础。
第二个问题自定义注解:什么是自定义注解,自定义注解就是接口,且这个接口实现了java.lang.annotation接口,但是这种写法编译器不允许,所以自定义注解需要固定的写法:
public @interface MyTest {
int age() default -1 ;
String name() default "xiaozhilei";
int value();
}
@interface 这种格式,就会使Mytest自动的去继承java.lang.annotation接口,接口中定义如下
Documented:某一类型的注释被javadoc或者某种类似的工具进行文档化
Inherited:指示注释类型被自动继承
Retention:指示注释类型将会保留多久;其默认值为RetentionPolicy.CLASS(编译器将把注释记录在类文件中,但在运行时VM不需要保留注释。);它还可以取值为RetentionPolicy.SOURCE(编译器要丢弃的注释。)和RetentionPolicy.RUNTIME(编译器将把注释记录在类文件中,在运行时 VM 将保留注释,因此可以反射性地读取。)。其中@Override和@SuppressWarnings是RetentionPolicy.SOURCE级别的;@Deprecated是RetentionPolicy.RUNTIME级别的
Target:指示注释类型适用的程序元素的种类;其ElementType的取值可以是ANNOTATION_TYPE(注释类型声明),CONSTRUCTOR(构造器声明),FIELD(属性声明),TYPE等;例如:
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME):
注意自定义的注解可以用在,方法,属性,类上面,因为这些pojo都实现了java.lang.annotation接口,可以使用自定义注解,使用他的方法。
自定义注解需要会的几个方法:
public class MyUserHandler implements InvocationHandler {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
//判断该方法上面是否有注解
boolean flag = method.isAnnotationPresent(ExtSelsecct.class);
//如果该方法上面有注解则获取方法里面的参数
ExtSelsecct select = method.getDeclaredAnnotation(ExtSelsecct.class);
//获取注解中的值
//String userType = select.value();
//System.out.println("注解中的值:" + userType);
if(select != null){
String value = select.value();
System.out.println(value);
System.out.println(method.getName()+ "方法返回值"+ method.getReturnType());
}
Parameter[] params = method.getParameters();
for (Parameter parameter : params) {
System.out.println(parameter.getType().getName() +"vlaue" + parameter.getType());
}
//遍历参数
for (int i =0;i<args.length;i++) {
System.out.println("args" + args[i]);
}
//在反射的方法前执行
System.out.println("开始执行查询操作 select * from rd_user where user_id = 1");
return "uername:xiaozhilei; age:20;height 175cm";
}
1:method.isAnnotationPresent(ExtSelsecct.class) 判断该方法上是否有注解。
2:ExtSelsecct select = method.getDeclaredAnnotation(ExtSelsecct.class); 得到该方法上面的注解对象。
通过这些操作就可以对注解进行操作了。
2:mybatis通过接口生成对象的过程
这个其实上面已经介绍过了,mybatis接口生成对象,其实不是真正的接口对象,而是通过代理类来实现生成的代理对象。类似这种设计的还有匿名对象也是可以做到的,废话不多说看代码。
1:生成接口对象代码:
public void selectUser(){
UserMapper mapper= (UserMapper) Proxy.newProxyInstance(UserMapper.class.getClassLoader(),new Class[]{UserMapper.class}, new MyUserHandler());
mapper.selectUserByUserId("xiaozhilei");
}
通过proxy动态代理生成userMapper的代理对象,转成userMapper进行赋值,myuserHander是具体实现,代码如下2
2:怎么实现查询,那就更简单了,直接实现InvocationHandler,通过invoke方法就可以操作接口方法,实现查询,其实就是曾强他的功能,只是目标对象是接口,所有功能交给代理对象来处理。
public class MyUserHandler implements InvocationHandler {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
//判断该方法上面是否有注解
boolean flag = method.isAnnotationPresent(ExtSelsecct.class);
//如果该方法上面有注解则获取方法里面的参数
ExtSelsecct select = method.getDeclaredAnnotation(ExtSelsecct.class);
//获取注解中的值
//String userType = select.value();
//System.out.println("注解中的值:" + userType);
if(select != null){
String value = select.value();
System.out.println(value);
System.out.println(method.getName()+ "方法返回值"+ method.getReturnType());
}
Parameter[] params = method.getParameters();
for (Parameter parameter : params) {
System.out.println(parameter.getType().getName() +"vlaue" + parameter.getType());
}
//遍历参数
for (int i =0;i<args.length;i++) {
System.out.println("args" + args[i]);
}
//在反射的方法前执行
System.out.println("开始执行查询操作 select * from rd_user where user_id = 1");
return "uername:xiaozhilei; age:20;height 175cm";
}
这样整个mybatis的整个执行过程就结束了。
最后:我这个只是mybatis源码实现的思路,具体的源码的实现比这个更要复杂更要完善,希望看了这个对源码有更好的理解,让我们一起去探索吧。