点一下关注吧!!!非常感谢!!持续更新!!!
大数据篇正在更新!https://blog.****.net/w776341482/category_12713819.html
目前已经更新到了:
- MyBatis(正在更新)
框架实现
上节已经实现了部分内容 下面我们继续
框架优化
上述我们编写了自定义的框架,我们解决了 JDBC 带来的一系列的问题,但是目前也出现了一些问题:
- DAO 的实现类存在重复的代码,整过操作模板重复,创建 SqlSession等等
- DAO 的实现类中有硬编码,调用 SqlSession 的方法时,参数 Statement的 ID 硬编码。
SqlSession
解决:使用代理模式来创建接口的代理对象,我们在 SqlSession 中加入新的方法:getMapper,我们修改 SqlSession
<T> T getMappper(Class<?> mapperClass);
修改完成后,对应的截图如下:
DefaultSqlSession
我们在实现类中进行实现刚才的 getMapper 的方法:
@Override
public <T> T getMapper(Class<?> mapperClass) {
Object proxyInstance = Proxy.newProxyInstance(DefaultSqlSession.class.getClassLoader(), new Class[]{mapperClass}, new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
String methodName = method.getName();
if (method.getDeclaringClass() == Object.class) {
return method.invoke(this, args);
}
String className = method.getDeclaringClass().getName();
String statementId = className + "." + methodName;
Type genericReturnType = method.getGenericReturnType();
if(genericReturnType instanceof ParameterizedType){
List<Object> objects = selectList(statementId, args);
return objects;
}
return selectOne(statementId,args);
}
});
return (T) proxyInstance;
}
对应的截图如下所示:
- Override:表示该方法是对父类或接口方法的重写。
- T:定义了一个泛型方法,T 是返回值的类型,占位符在调用时会被具体类型替换。
- getMapper(Class<?> mapperClass):方法接收一个 Class 类型参数,表示需要生成的 Mapper 接口的类型。
Object proxyInstance = Proxy.newProxyInstance(
DefaultSqlSession.class.getClassLoader(),
new Class[]{mapperClass},
new InvocationHandler() { ... }
);
- Proxy.newProxyInstance:创建动态代理对象。
- DefaultSqlSession.class.getClassLoader():指定类加载器,用于加载动态生成的代理类。
- new Class[]{mapperClass}:指定代理类需要实现的接口数组,这里仅实现 mapperClass。
- new InvocationHandler():传入一个 InvocationHandler,用于处理方法调用逻辑。
动态代理逻辑
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { ... }
invoke 方法:当代理对象调用任何方法时,都会触发此方法。
参数:
- Object proxy:代理对象本身。
- Method method:当前被调用的方法对象。
- Object[] args:调用方法时传递的参数。
方法调用逻辑
String methodName = method.getName();
if (method.getDeclaringClass() == Object.class) {
return method.invoke(this, args);
}
- 获取方法名:通过 method.getName() 获取被调用的方法名。
- if 判断:检查方法是否是 Object 类的方法(如 toString()、equals() 等)。如果是,直接调用原始实现,通过 method.invoke(this, args) 执行。
SQL 语句标识符
String className = method.getDeclaringClass().getName();
String statementId = className + "." + methodName;
- className:获取方法所在类的全限定名。
- statementId:拼接方法所在类名和方法名,生成唯一的 SQL 语句标识符,用于在 Mapper 中定位具体的 SQL 语句。
方法返回类型判断
Type genericReturnType = method.getGenericReturnType();
if (genericReturnType instanceof ParameterizedType) {
List<Object> objects = selectList(statementId, args);
return objects;
}
- genericReturnType:获取方法的返回值类型。
- instanceof 检查:如果返回类型是参数化类型(例如 List),则调用 selectList 方法执行查询。
- selectList(statementId, args):模拟批量查询方法,传入 statementId 和参数 args。
这段代码实现了一个动态代理机制,用于生成符合特定 Mapper 接口(mapperClass)的动态代理对象。
代理对象的方法调用逻辑包括以下几部分:
- 检查方法是否属于 Object,如果是则直接调用原始实现。
- 根据方法名和类名生成唯一的 statementId,用于定位 SQL 语句。
- 根据返回值类型决定调用批量查询(selectList)还是单结果查询(selectOne)。
- 返回查询结果。
测试方法
我们编写一个方法,来通过 SqlSession的 openSession、getMapper 等方法来进行查询测试:
package icu.wzk.test;
import icu.wzk.bean.Resources;
import icu.wzk.bean.SqlSession;
import icu.wzk.bean.SqlSessionFactory;
import icu.wzk.bean.SqlSessionFactoryBuilder;
import icu.wzk.dao.UserInfoMapper;
import icu.wzk.model.UserInfo;
import java.io.InputStream;
public class Test02 {
public static void main(String[] args) throws Exception {
InputStream inputStream = Resources.getResourceAsStream("sqlMapConfig.xml");
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
SqlSession sqlSession = sqlSessionFactory.openSession();
UserInfo userInfo = new UserInfo();
userInfo.setUsername("wzk");
UserInfoMapper userInfoMapper = sqlSession.getMapper(UserInfoMapper.class);
System.out.println("userInfoMapper: " + userInfoMapper);
System.out.println(userInfoMapper.selectOne(userInfo));
}
}
对应的截图如下所示:
运行结果
执行之后,控制台输出的结果如下所示:
log4j:WARN No appenders could be found for logger (com.mchange.v2.log.MLog).
log4j:WARN Please initialize the log4j system properly.
log4j:WARN See http://logging.apache.org/log4j/1.2/faq.html#noconfig for more info.
userInfoMapper: icu.wzk.bean.DefaultSqlSession$1@61dc03ce
SimpleExecutor getBoundSql: SELECT * FROM user_info WHERE username=?
UserInfo(id=1, username=wzk, password=icu, age=18)
对应的截图如下所示: