mybatis接口式编程原理

众所周知,mybatis实现了接口式编程,简化了我们Dao层的编写,通过定义一个接口和XMl文件即可实现对数据库sql的执行。那它是怎么现实的呢,其实是利用了java的动态代理。

我们先聊聊动态代理模式:

代理模式:对原有功能,进行增强

一、静态代理:构成--->1.抽象接口2.目标对象3.代理对象

重新定义一个类继承这接口,并定义此接口的引用,用来引用目标对象,调用原方法

public abstract class AbstractObject {
    public abstract void operation();
}

目标对象角色

public class RealObject extends AbstractObject {
    @Override
    public void operation() {
        //一些操作
        System.out.println("一些操作");
    }
}

代理对象角色

public class ProxyObject extends AbstractObject{
    RealObject realObject = new RealObject();
    @Override
    public void operation() {
        //调用目标对象之前可以做相关操作
        System.out.println("before");        
        realObject.operation();        
        //调用目标对象之后可以做相关操作
        System.out.println("after");
    }
}

客户端

public class Client {
    public static void main(String[] args) {
        AbstractObject obj = new ProxyObject();
        obj.operation();
    }

}

二、动态代理:每个类都存在虚拟的class代理类,只存在于内存中

public class ProxyDemo {
	public static void main(String[] args) {
		//目标对象
		final Dao demoDao = new DemoDao();
		//接口Dao的代理类的代理对象
		Dao dao = (Dao)Proxy.newProxyInstance(ProxyDemo.class.getClassLoader(), 
					new Class[]{Dao.class}, //指定代理接口
					new InvocationHandler() {//拦截代理对象的所有方法。

				@Override //proxy代表代理对象本身                method 调用哪个方法,引用哪个方法对象  
				//args调用方法的参数封装成对象装进数组
				public Object invoke(Object proxy, Method method, Object[] args)
				throws Throwable {
					//System.out.println(method.getName()+"    "+Arrays.toString(args));
					//1.增强的功能
					System.out.println("-----有方法进入,拦截到的方法为-----");
					//2.调用目标对象原有的方法
					//method.invoke(demoDao, args);
					return null;
				}
			});
			//System.out.println(dao);  tostring方法被拦截,一直返回null
			//System.out.println(dao.getClass());
			//dao.add(10, 20);
			dao.equals("abc");
	
	}
}

下面说明下mybatis是怎么利用动态代理来现实接口式编程的,首先在mybatis中只定义了接口,如果需要调用接口中的方法必须要有现实类对象,利用动态代理来创建实现类对象:

MapperProxyimplementsInvocationHandler覆写类里invoke()方法-->Proxy.newProxyInstance(类加载器,接口.class数组,MapperProxy对象)-->sqlSession.getMapper(接口.class)==Proxy.newProxyInstance()-->IMessageimessage=Proxy.newProxyInstance()

imessage.queryMessageList==MapperProxy.invoke();

根据上面的流程,Proxy.newProxyInstance()创建了代理对象,可以把它看做接口的实现类对象,MapperProxy拦截了代理对象的所有方法,就是说代理对象执行什么方法都会进入到invoke()方法中,这就有个实现类对象

下面说说代理对象执行接口中的方法,为什么可以执行xml中配置的sql,这个就涉及接口式编程的规则了,也是为什么要设置这些规则的原因。

加载mybatis配置信息configuratioon,也就加载了Mapper的xml信息-->因为接口全名称.方法名=namespace.id-->所以MapperProxy.invoke()中有这样的代码:sqlSession.selectList(namespace.id,parameter)根据返回值类型决定用selectOne还是selectList

这样当我们用代理对象调用接口中方法时,就会在Mapper.invoke()方法中,执行sqlSession.selectList方法,也就是imessage.queryMessageList(parameter)=sqlSession.selectList(namespace.id,parameter),也就执行我们xml中的sql,这也是为什么我们namespace必须要是接口的完全限定名,而sql的id必须和接口中方法名一致的原因。

相关推荐