通过下面的分析可以得知所谓的自定义 Mybatis,其实就是三件事,首先将连接信息和映射信息封装,然后获取动态代理对象,最后调用查询所有方法
文章目录
一、分析
- 初始时我们有了
SqlMapConfig.xml
和UserDao.xml
文件
- 使用 dom4j 解析xml 技术
selectList 方法:
(1)根据配置文件的信息创建 Connection 对象
注册驱动,获取连接
(2)获取预处理对象PrepareStatement
此时需要SQL语句:conn.prepareStatement(sql);
(3)执行查询:ResultSet resultSet = preparedStatement.executeQuery();
(4)遍历结果用于封装
(5)返回list
3. 为了使解析技术里的selectList 方法正常执行,需要为其提供两个信息
(1)连接信息:以前的连接池技术可以解决
(2)映射信息
* 执行的SQL语句
* 封装结果的实体类全限定类名
将这两个信息组成一个Map对象
4. 根据 dao 接口的字节码创建 dao 的动态代理对象
类似:UserDao userDao = session.getMapper(UserDao.class);
二、自定义 Mybatis
1. 读取配置文件
InputStream in= Resources.getResourceAsStream("SqlMapConfig.xml");
所以建立 Resources.java 作用:使用类加载器读取配置文件
/*
使用类加载器读取配置文件
*/
public class Resources {
/**
* 根据传入的参数获取一个字节输入流
* @param filePath
* @return
*/
public static InputStream getResourceAsStream(String filePath){
return Resources.class.getClassLoader().getResourceAsStream(filePath);
}
}
2. 创建SqlSessionFactory工厂
所谓的 工厂 其实就是在接口和具体的子类之间加的一个过渡端,这样如果以后增减子类操作,就可以直接修改工厂即可。
SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder();
SqlSessionFactory factory = builder.build(in);
所以工厂类 SqlSessionFactoryBuilder.java
来连接接口和子类,这里的接口是 SqlSessionFactory.java
public class SqlSessionFactoryBuilder {
/**
* 根据参数的字节输入流来构建一个 SqlSessionFactory 工厂
* @param config
* @return
*/
public SqlSessionFactory build(InputStream config){
//cfg 里存放的是连接信息和映射信息,也就是我们最主要的两个信息
Configuration cfg = XMLConfigBuilder.loadConfiguration(config);
//DefaultSqlSessionFactory 是接口SqlSessionFactory 的实现类,覆写了openSession 方法
return new DefaultSqlSessionFactory(cfg);
}
}
3. 使用工厂生产SqlSession对象
SqlSession session = factory.openSession();
这里的factory
是一个DefaultSqlSessionFactory
的对象,DefaultSqlSessionFactory:
public class DefaultSqlSessionFactory implements SqlSessionFactory{
private Configuration cfg;
public DefaultSqlSessionFactory(Configuration cfg){
this.cfg = cfg;
}
/**
* 用于创建一个新的操作数据库对象
* @return
*/
@Override
public SqlSession openSession() {
return new DefaultSqlSession(cfg);
}
}
这里的 DefaultSqlSession 实现的主要就是创建 动态代理类,及SqlSession 接口相关的一些实现类
4. 使用SqlSession创建Dao接口的代理对象
UserDao userDao = session.getMapper(UserDao.class);
session
是DefaultSqlsession
对象,里面重写了SqlSession
的 getMapper
方法:
/**
* SqlSession接口的实现类
*/
public class DefaultSqlSession implements SqlSession {
private Configuration cfg;
private Connection connection;
public DefaultSqlSession(Configuration cfg){
this.cfg = cfg;
//具体的数据库连接操作
connection = DataSourceUtil.getConnection(cfg);
}
/**
* 用于创建代理对象
* @param daoInterfaceClass dao的接口字节码
* @param <T>
* @return
*/
@Override
public <T> T getMapper(Class<T> 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();
}
}
}
}
这里创建代理对象,传递的 MapperProxy 中对invoke
的重写也具体实现了对SQL语句的查询操作
5. 使用代理对象执行方法
List<User> users = userDao.findAll();
for (User user :
users) {
System.out.println(user);
}
// 6.释放资源
session.close();
in.close();
对于这里的 userDao.findAll()
,MapperProxy 对其封装,然后通过 invoke 转发到工具类执行查询操作。
public class MapperProxy implements InvocationHandler {
//map的key是全限定类名+方法名
private Map<String, Mapper> mappers;
private Connection conn;
public MapperProxy(Map<String, Mapper> 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();
System.out.println("methodName:" + methodName);
//2.获取方法所在类的名称
String className = method.getDeclaringClass().getName();
System.out.println("className:" + className);
//3.组合key
String key = className + "." + methodName;
System.out.println("key:" + key);
//4.获取mappers中的Mapper对象
Mapper mapper = mappers.get(key);
//5.判断是否有mapper
if (mapper == null) {
throw new IllegalArgumentException("传入的参数有误");
}
//6.调用工具类执行查询所有
return new Executor().selectList(mapper, conn);
}
}