绝对干货,超全的 MyBatis 动态代理原理讲解!
Posted Java知音_
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了绝对干货,超全的 MyBatis 动态代理原理讲解!相关的知识,希望对你有一定的参考价值。
点击关注公众号,实用技术文章及时了解
1.MyBatis简介
MyBatis是一个ORM工具,封装了JDBC的操作,简化业务编程;
Mybatis在web工程中,与Spring集成,提供业务读写数据库的能力。
2.使用步骤
1.引入依赖
采用Maven包依赖管理,mybatis-3.5.5版本;同时需要数据库连接驱动
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.5.5</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.49</version>
</dependency>
2.配置文件
配置文件配置数据库连接源,及映射文件。
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE configuration PUBLIC "-//mybatis.org//DTD Config 3.0//EN" "http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
<environments default="development">
<environment id="development">
<transactionManager type="JDBC" />
<!-- 数据库连接方式 -->
<dataSource type="POOLED">
<property name="driver" value="com.mysql.jdbc.Driver" />
<property name="url" value="jdbc:mysql://localhost/user" />
<property name="username" value="root" />
<property name="password" value="123456" />
</dataSource>
</environment>
</environments>
<!-- 注册表映射文件 -->
<mappers>
<mapper resource="mybatis/User.xml"/>
</mappers>
</configuration>
3.接口定义
定义实体
package com.xiongxin.mybatis.entity;
public class User
private String username;
private String password;
...getter&&setter
接口定义
package com.xiongxin.mybatis.mapper;
import com.xiongxin.mybatis.entity.User;
import java.util.List;
public interface UserMapper
List<User> queryUser();
定义映射文件
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.xiongxin.mybatis.mapper.UserMapper">
<select id="queryUser" resultType="com.xiongxin.mybatis.entity.User">
select * from tbl_user
</select>
</mapper>
4.加载执行
package com.xiongxin.mybatis;
import com.alibaba.fastjson.JSON;
import com.xiongxin.mybatis.entity.User;
import com.xiongxin.mybatis.mapper.UserMapper;
import org.apache.ibatis.io.Resources;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;
import java.io.IOException;
import java.io.Reader;
import java.util.List;
public class TestMain
public static void main(String[] args) throws IOException
String resource = "mybatis-config.xml";
//加载 mybatis 的配置文件(它也加载关联的映射文件)
Reader reader = Resources.getResourceAsReader(resource);
//构建 sqlSession 的工厂
SqlSessionFactory sessionFactory = new SqlSessionFactoryBuilder().build(reader);
//创建能执行映射文件中 sql 的 sqlSession
SqlSession session = sessionFactory.openSession();
UserMapper userMapper = session.getMapper(UserMapper.class);
List<User> users = userMapper.queryUser();
System.out.println(JSON.toJSONString(users));
---------------------------------
..consule print..
["password":"password","username":"xiongxin"]
到这里,这个Mybatis的使用环节结束。
整个实现过程中,我们并未编写Mapper的实现类,框架是如何在无实现类的场景下实现接口方法返回的呢?
这里就不得不说到接口的动态代理方法了。
3.原理解析
SqlSession接口的实现中,获取Mapper的代理实现类
使用了JDK动态代理的功能
代理类执行方法调用
方法调用中执行MethodInvoker
最终执行execue方法。
获取返回结果Result
4.手撕框架
前置知识:
源码:
<dependencies>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.74</version>
</dependency>
<dependency>
<groupId>com.h2database</groupId>
<artifactId>h2</artifactId>
<version>1.4.199</version>
</dependency>
</dependencies>
package com.dbutil.session;
import java.lang.annotation.Retention;
import java.lang.annotation.Target;
import java.lang.reflect.*;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.Statement;
import java.util.ArrayList;
import java.util.List;
import static java.lang.annotation.ElementType.*;
import static java.lang.annotation.RetentionPolicy.RUNTIME;
/**
* @author xiongxin
*/
public class SqlSession
public static Connection getConnH2() throws Exception
String url = "jdbc:h2:mem:db_h2;MODE=MYSQL;INIT=RUNSCRIPT FROM './src/main/resources/schema.sql'";
String user = "root";
String password = "123456";
//1.加载驱动程序
Class.forName("org.h2.Driver");
//2.获得数据库链接
Connection conn = DriverManager.getConnection(url, user, password);
return conn;
/**
* 自定义注解
*/
@Target(TYPE, FIELD, METHOD)
@Retention(RUNTIME)
public @interface QueryList
public String value();
/**
* 动态代理
*
* @param mapperInterface
* @param <T>
* @return
*/
public static <T> T getMapper(Class<T> mapperInterface)
return (T) Proxy.newProxyInstance(mapperInterface.getClassLoader(), new Class[]mapperInterface, new MapperInvocationHandler());
/**
* 代理类方法
*/
public static class MapperInvocationHandler implements InvocationHandler
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable
String sql = method.getAnnotation(QueryList.class).value();
Class<?> returnType = method.getReturnType();
//返回类型为List
if (returnType == List.class)
Type genericReturnType = method.getGenericReturnType();
String typeName = genericReturnType.getTypeName();
String replace = typeName.replace("java.util.List<", "").replace(">", "");
//获取泛型类型
Class<?> forName = Class.forName(replace);
return SqlSession.queryList(sql, forName);
return null;
/**
* 结果集转换
*
* @param <T>
*/
public interface ResultMap<T>
T convert(ResultSet resultSet) throws Exception;
/**
* 创建连接并执行
*
* @param sql
* @param resultMap
* @param <T>
* @return
* @throws Exception
*/
public static <T> List<T> queryList(String sql, ResultMap<T> resultMap) throws Exception
//jdbc数据库
Connection conn = getConnH2();
//3.通过数据库的连接操作数据库,实现增删改查(使用Statement类)
Statement st = conn.createStatement();
ResultSet rs = st.executeQuery(sql);
List<T> list = new ArrayList<>();
//4.处理数据库的返回结果(使用ResultSet类)
while (rs.next())
T convert = resultMap.convert(rs);
list.add(convert);
//关闭资源
rs.close();
st.close();
conn.close();
return list;
/**
* 查询数据集
*
* @param sql
* @param returnType
* @param <T>
* @return
* @throws Exception
*/
public static <T> List<T> queryList(String sql, Class<T> returnType) throws Exception
List<T> list = SqlSession.queryList(sql, rs ->
T obj = returnType.newInstance();
Field[] declaredFields = returnType.getDeclaredFields();
for (Field declaredField : declaredFields)
Class<?> type = declaredField.getType();
//类型为String时的处理方法
if (type == String.class)
String value = rs.getString(declaredField.getName());
String fieldName = declaredField.getName();
Method method = returnType.getDeclaredMethod(
"set".concat(fieldName.substring(0, 1).toUpperCase().concat(fieldName.substring(1))),
String.class);
method.invoke(obj, value);
if (type == Long.class)
Long value = rs.getLong(declaredField.getName());
String fieldName = declaredField.getName();
Method method = returnType.getDeclaredMethod(
"set".concat(fieldName.substring(0, 1).toUpperCase().concat(fieldName.substring(1))),
Long.class);
method.invoke(obj, value);
//其他类型处理方法
return obj;
);
return list;
schema.sql文件
drop table if exists user;
CREATE TABLE user
(
id int(11) NOT NULL AUTO_INCREMENT,
username varchar(255) DEFAULT NULL,
password varchar(255) DEFAULT NULL,
PRIMARY KEY (id)
);
insert into user(id,username,password) values(1,'xiongxina','123456');
insert into user(id,username,password) values(2,'xiongxinb','123456');
insert into user(id,username,password) values(3,'xiongxinc','123456');
mapper定义
package com.dbutil.mapper;
import com.dbutil.entity.UserEntity;
import com.dbutil.session.SqlSession;
import java.util.List;
public interface UserMapper
@SqlSession.QueryList("select * from user")
List<UserEntity> queryUser();
使用:
package com.dbutil;
import com.dbutil.entity.UserEntity;
import com.dbutil.mapper.UserMapper;
import com.dbutil.session.SqlSession;
import java.util.List;
public class UserService
public static void main(String[] args) throws Exception
UserMapper userMapper = SqlSession.getMapper(UserMapper.class);
List<UserEntity> userEntities = userMapper.queryUser();
for (UserEntity userEntity : userEntities)
System.out.println(userEntity);
来源:blog.csdn.net/tsxiong123/article/
details/119057825
推荐
PS:因为公众号平台更改了推送规则,如果不想错过内容,记得读完点一下“在看”,加个“星标”,这样每次新文章推送才会第一时间出现在你的订阅列表里。点“在看”支持我们吧!
以上是关于绝对干货,超全的 MyBatis 动态代理原理讲解!的主要内容,如果未能解决你的问题,请参考以下文章