本篇通过模仿Mybatis实现过程,探索底层源码,更好的熟悉Mybatis。本文大部分摘抄案例上手 Spring 全家桶,版权属于他们 ,等会文末会提供地址链接。
1、创建一个类,实现 InvocationHandler 接口,该类就具备了创建动态代理对象的功能,定义两个核心方法。
自定义 getInstance 方法:入参为目标对象,通过 Proxy.newProxyInstance 方法创建代理对象,并返回。
/**
* 入参为目标对象
* @param clazz
* @return
*/
public Object getInstance(Class clazz){
Object newProxyInstance = Proxy.newProxyInstance(clazz.getClassLoader(), new Class[]{clazz}, this);
return newProxyInstance;
}
实现接口的 invoke 方法,通过反射机制完成业务逻辑代码。
@Override
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable {
// TODO Auto-generated method stub
return null;
}
invoke 方法是核心代码,在该方法中实现具体的业务需求,接下来我们来看如何实现。
既然是对数据库进行操作,则一定需要数据库连接对象,数据库相关信息配置在 config.xml 中。因此 invoke 方法第一步,就是要解析 config.xml,创建数据库连接对象,使用 C3P0 数据库连接池。
/**
* 解析数据源
* @return
* @throws DocumentException
*/
public static Map<String,String> getC3P0Properties() throws DocumentException {
Map<String, String> map = new HashMap<>();
SAXReader saxReader = new SAXReader();
Document document = saxReader.read("/Users/guozh/Workspace/Java/mybaitis-demo/src/main/resources/config.xml");
//根节点
Element rootElement = document.getRootElement();
Iterator it01 = rootElement.elementIterator();
while(it01.hasNext()){
Element ele01 = (Element) it01.next();
if(ele01.getName().equals("environments")){
Iterator it02 = ele01.elementIterator();
while(it02.hasNext()){
Element el02 = (Element) it02.next();
if(el02.getName().equals("environment")){
Iterator it03 = el02.elementIterator();
while(it03.hasNext()){
Element el03 = (Element) it03.next();
if(el03.getName().equals("dataSource")){
Iterator it04 = el03.elementIterator();
while(it04.hasNext()){
Element el04 = (Element) it04.next();
map.put(el04.attributeValue("name"),el04.attributeValue("value"));
}
}
}
}
}
}
}
return map;
}
/**
* 获取 C3P0 信息,创建数据源对象
* @return
*/
public Connection createDataSources(){
try {
Map<String,String> map = getC3P0Properties();
ComboPooledDataSource datasource = new ComboPooledDataSource();
datasource.setDriverClass(map.get("driver"));
datasource.setJdbcUrl(map.get("url"));
datasource.setUser(map.get("username"));
datasource.setPassword(map.get("password"));
datasource.setInitialPoolSize(20);
datasource.setMaxPoolSize(40);
datasource.setMinPoolSize(2);
datasource.setAcquireIncrement(5);
Connection conn = datasource.getConnection();
return conn;
} catch (DocumentException e) {
e.printStackTrace();
} catch (SQLException e) {
e.printStackTrace();
} catch (PropertyVetoException e) {
e.printStackTrace();
}
return null;
}
有了数据库连接,接下来就需要获取待执行的 SQL 语句,SQL 的定义全部写在 StudentDAO.xml 中。
继续解析 XML,执行 SQL 语句,SQL 执行完毕,查询结果会保存在 ResultSet 中,还需要将 ResultSet 对象中的数据进行解析,封装到 POJO 中返回,这一功能同样需要两步操作。
- 反射机制创建 Student 对象。
- 通过反射动态执行类中所有属性的 setter 方法,完成赋值。
这样就将 ResultSet 中的数据封装到 POJO 中了。
//获取 sql 语句
String sql = element.getText();
//获取参数类型
String parameterType = element.attributeValue("parameterType");
//创建 pstmt
PreparedStatement pstmt = createPstmt(sql,parameterType,conn,args);
ResultSet rs = pstmt.executeQuery();
if(rs.next()){
//读取返回数据类型
String resultType = element.attributeValue("resultType");
//反射创建对象
Class clazz = Class.forName(resultType);
obj = clazz.newInstance();
//获取 ResultSet 数据
ResultSetMetaData rsmd = rs.getMetaData();
//遍历实体类属性集合,依次将结果集中的值赋给属性
Field[] fields = clazz.getDeclaredFields();
for(int i = 0; i < fields.length; i++){
Object value = setFieldValueByResultSet(fields[i],rsmd,rs);
//通过属性名找到对应的 setter 方法
String name = fields[i].getName();
name = name.substring(0, 1).toUpperCase() + name.substring(1);
String MethodName = "set"+name;
Method methodObj = clazz.getMethod(MethodName,fields[i].getType());
//调用 setter 方法完成赋值
methodObj.invoke(obj, value);
}
}
以上是核心思路,甚至代码不全,但不妨碍学习。
以上代码笔记内容来自付费专栏:案例上手 Spring 全家桶
PS:并没有透露关键内容,纯粹是零碎笔记。如果侵犯版权,请联系我。
本文由老郭种树原创,转载请注明:https://guozh.net/spring-mybatis/