在主配置文件中
连接数据库的信息,有了他们就能创建Connection对象
有了它就有了映射配置信息,获得映射配置文件的位置,读到下面的配置文件。
在映射配置文件中
有了它就有了执行的SQL语句,就可以获取PreparedSatement
此配置中还有封装的实体类全限制类名。(用什么语句执行,封装到哪里去)
这些个配置信息是一上来我们要做的事情,也就是读成一个流,解析配置文件!
此处用的dom4j的技术,解析xml,读取配置信息。
selectList方法(被封装在session.getMapper中)
这些所有的方法都被mybatis封装,此时要想使上述方法执行,我们需要给方法提供两个信息。
由于将来会有多个查询方法,对应会有多个Mapper对象,所以将其存储到map集合中key: namespace + id value:Mapper对象
//根据dao接口的字节码创建dao的代理对象
Public T getMapper(Class daoInterfaceClass){
/*
类加载器:它使用的和被代理对象是相同的类加载器
代理对象要实现的接口:和被代理对象实现相同的接口
如何代理:它就是增强的方法、我们需要自己来提供。
此处是一个InvocationHandler的接口,我们需要写一个该接口的实现类,在实现类中调用selectList方法。
*/
Proxy.newProxyInstance(类加载器、代理对象要实现的接口的字节码数组,如何代理);
}
即上述的selectList方法在动态代理的InvocationHandler的接口中实现。
public class Resources {
/** 使用类加载器读取配置文件的类
* 根据传入的参数,获取一个字节输入流
* @param filePath
* @return
*/
public static InputStream getResourceAsStream(String filePath){
return Resources.class.getClassLoader().getResourceAsStream(filePath);
}
public class SqlSessionFactoryBuilder {
/**用于创建一个SqlSessionFactory对象
* 根据参数的字节输入流来构建一个SqlSessionFactory工厂
* @param config
* @return
*/
//返回值是SqlSessionFactory一个接口
public SqlSessionFactory build(InputStream config){
//XMLConfigBuilder是使用dom4j+xpath解析的主配置文件所提取的封装对象。
Configuration cfg = XMLConfigBuilder.loadConfiguration(config);
//DefaultSqlSessionFactory是SqlSessionFactory接口的实现类
return new DefaultSqlSessionFactory(cfg);
}
创建SqlSessionFactory接口:
public interface SqlSessionFactory {
/**
* 用于打开一个新的SqlSession对象
* @return
*/
SqlSession openSession();
}
创建SqlSessionFactory接口的实现类:
public class DefaultSqlSessionFactory implements SqlSessionFactory{
private Configuration cfg;
public DefaultSqlSessionFactory(Configuration cfg){
this.cfg = cfg;
}
/**SqlSessionFactory接口的实现类
* 用于创建一个新的操作数据库对象
* @return
*/
@Override
public SqlSession openSession() {
return new DefaultSqlSession(cfg);
}
}
4.调用SqlSessionFactory中的方法,得到SqlSession对象,其中有个方法应该是getMapper,参数是dao的接口字节码
创建SqlSession接口:
public interface SqlSession {
/**它里面可以创建dao接口的代理对象
* 根据参数创建一个代理对象
* @param daoInterfaceClass dao的接口字节码
* @param 泛型先声明在使用,声明在使用之前
* @return
*/
T getMapper(Class daoInterfaceClass);
/**
* 释放资源
*/
void close();
}
创建SqlSession接口的实现类:
public class DefaultSqlSession implements SqlSession {
private Configuration cfg;
private Connection connection;
public DefaultSqlSession(Configuration cfg){
this.cfg = cfg;
connection = DataSourceUtil.getConnection(cfg);
}
/**SqlSession接口的实现类
* 用于创建代理对象
* @param daoInterfaceClass dao的接口字节码
* @param
* @return
*/
@Override
public T getMapper(Class daoInterfaceClass) {
return (T) Proxy.newProxyInstance(daoInterfaceClass.getClassLoader(),
new Class[]{daoInterfaceClass},new MapperProxy(cfg.getMappers(),connection));
}
/**
* 用于释放资源
*/
@Override
public void close() {
if(connection != null) {
try {
connection.close();
} catch (Exception e) {
e.printStackTrace();
}
}
}
创建动态代理的代理方法,实现InvocationHandler接口
public class MapperProxy implements InvocationHandler {
//map的key是全限定类名+方法名
private Map mappers;
private Connection conn;
public MapperProxy(Map mappers,Connection conn){
this.mappers = mappers;
this.conn = conn;
}
/**
* 用于对方法进行增强的,我们的增强其实就是调用selectList方法
* @param proxy
* @param method
* @param args
* @return
* @throws Throwable
*/
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
//1.获取方法名
String methodName = method.getName();
//2.获取方法所在类的名称
String className = method.getDeclaringClass().getName();
//3.组合key
String key = className+"."+methodName;
//4.获取mappers中的Mapper对象
Mapper mapper = mappers.get(key);
//5.判断是否有mapper
if(mapper == null){
throw new IllegalArgumentException("传入的参数有误");
}
//6.调用工具类执行查询所有
return new Executor().selectList(mapper,conn);
}
5.调用SqlSession对象中的getMapper方法,创建dao接口的代理对象
分析:
我们需要最后实现查询所有,查询所有的功能在Executor()工具类中,最后就是要调用Executor()工具类。
什么时候调用呢?就是创建dao实现类增强的时候。
那什么时候创建dao的实现类呢?就是创建代理对象实现getMapper方法的时候,在getMapper方法中调用InvocationHandler 的实现类MapperProxy,其中MapperProxy类就干了从某个地找出来要执行的语句和封装的结果。
从哪找要执行的语句和封装的结果呢?就是配置文件中,XMLConfigBuilder负责读取,读取完后存储到Configuration对象中。