面试题:说一下MyBatis动态代理原理?

提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档

文章目录

  • 1.MyBatis简介
  • 2.使用步骤
    • 2.1、引入依赖
    • 2.2、配置文件
    • 2.3、接口定义
    • 2.4、加载执行
  • 3.原理解析


1.MyBatis简介

MyBatis是一个ORM工具,封装了JDBC的操作,简化业务编程;

Mybatis在web工程中,与Spring集成,提供业务读写数据库的能力。

2.使用步骤

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.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>

2.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 mapperPUBLIC "-//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>

2.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 的 sqlSessionSqlSession 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 {@Overridepublic Object invoke(Object proxy, Method method, Object[] args) throws Throwable {String sql = method.getAnnotation(QueryList.class).value();Class<?> returnType = method.getReturnType();//返回类型为Listif (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);}}
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/179571.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Redis 主从架构,Redis 分区,Redis哈希槽的概念,为什么要做Redis分区

文章目录 Redis 主从架构redis replication 的核心机制redis 主从复制的核心原理过程原理Redis集群的主从复制模型是怎样的&#xff1f;生产环境中的 redis 是怎么部署的&#xff1f;机器是什么配置&#xff1f;你往内存里写的是什么数据&#xff1f;说说Redis哈希槽的概念&…

前端入门(四)Ajax、Promise异步、Axios通信、vue-router路由

文章目录 AjaxAjax特点 Promise 异步编程&#xff08;缺&#xff09;Promise基本使用状态 - PromiseState结果 - PromiseResult Axios基本使用 Vue路由 - vue-router单页面Web应用&#xff08;single page web application&#xff0c;SPA&#xff09;vue-router基本使用路由使…

Rabbitmq发送邮件并消费邮件

&#x1f4d1;前言 本文主要是【Rabbitmq】——Rabbitmq发送邮件并消费邮件的文章&#xff0c;如果有什么需要改进的地方还请大佬指出⛺️ &#x1f3ac;作者简介&#xff1a;大家好&#xff0c;我是听风与他&#x1f947; ☁️博客首页&#xff1a;CSDN主页听风与他 &#x1…

【数学】旋转矩阵

参考链接 OpenGL from OpenGL.GL import * from OpenGL.GLUT import * from math import * import numpy as np def draw_axes():glClear(GL_COLOR_BUFFER_BIT)# 绘制坐标轴glColor3f(1.0, 1.0, 1.0) # 设置坐标轴颜色为白色glBegin(GL_LINES)glVertex2f(-1.0, 0.0) # x 轴g…

Python中使用matplotlib库绘图中如何给图形的图例设置中文字体显示

问题&#xff1a;当使用matplotlib绘图时遇到绘图&#xff0c;图例显示不出来中文字体 解决方式&#xff1a; 1&#xff09;加载字体管理库 from matplotlib.font_manager import FontProperties 2&#xff09;设置系统上字体的路径 font FontProperties(fname"C:\\W…

直线(蓝桥杯)

直线 题目描述 本题为填空题&#xff0c;只需要算出结果后&#xff0c;在代码中使用输出语句将所填结果输出即可。 在平面直角坐标系中&#xff0c;两点可以确定一条直线。如果有多点在一条直线上&#xff0c; 那么这些点中任意两点确定的直线是同一条。 给定平面上 2 3 个…

VMD扩展molUP安装与高斯接口使用

molUP是一个VMD扩展&#xff0c;提供了一个简单的方式来加载和保存高斯文件&#xff0c;并分析相关的结果。 molUP为VMD提供了一个图形界面&#xff0c;用户可以加载和保存高斯文件格式的化学结构。这个扩展包括一组工具来设置高斯支持的任何计算&#xff0c;包括ONIOM通过互动…

音视频学习(十九)——rtsp收流(tcp方式)

前言 本文主要介绍以tcp方式实现rtsp拉流。 流程图 流程说明: 客户端发起tcp请求&#xff0c;如向真实相机设备请求&#xff0c;端口一般默认554&#xff1b;tcp连接成功&#xff0c;客户端与服务端开始rtsp信令交互&#xff1b;客户端收到play命令响应后&#xff0c;开启线…

PVE中CT容器安装openwrt X86的极简方法

下载推荐&#xff1a;https://openwrt.ai/ 使用环境PVE8.0&#xff0c;openwrt是以上网址的最新版&#xff0c;内涵及其丰富组件。 问题来源&#xff1a; 在PVE虚拟机可以很方便的使用img文件&#xff0c;转换qm 成一个硬盘文件&#xff0c;加入到虚拟机也就完成了&#xff0c…

五、双向NAT

学习防火墙之前&#xff0c;对路由交换应要有一定的认识 双向NAT1.1.基本原理1.2.NAT Inbound NAT Server1.3.域内NATNAT Server —————————————————————————————————————————————————— 双向NAT 经过前面介绍&#xff0c;…

fiddler设置手机端抓包看这篇文章就足够了,轻松解决!

fiddler设置手机端抓包 安卓手机抓包 第一步&#xff1a;配置电脑和安卓的相关设置 1、手机和fiddler位于同一个局域网内&#xff1b;首先从fiddler处获取到ip地址和端口号&#xff1a; 添加图片注释&#xff0c;不超过 140 字&#xff08;可选&#xff09; &#xff0c;点…

Java基础之常用类

Java基础之常用类 一、包装类1.1、Java基本数据类型及其对应的包装类1.2、包装类的自动装箱、自动拆箱机制1.3、包装类的优点 二、String类三、StringBuffer类和StringBuilder类3.1、主要区别&#xff1a;3.2、StringBuffer/StringBuilder用法(两者用法一致) 四、日期类4.1、Da…

[DASCTF 2023 0X401七月暑期挑战赛] web刷题记录

文章目录 EzFlask方法一 python原型链污染方法二 flask框架静态文件方法三 pin码计算 MyPicDisk方法一 字符串拼接执行命令方法二 phar反序列化 ez_cms EzFlask 考点&#xff1a;python原型链污染、flask框架理解、pin码计算 源码如下 import uuidfrom flask import Flask, re…

近五年—中国十大科技进展(2018年—2022年)

近五年—中国十大科技进展&#xff08;2018-2022&#xff09; 2022年中国十大科技进展1. 中国天眼FAST取得系列重要进展2. 中国空间站完成在轨建造并取得一系列重大进展3. 我国科学家发现玉米和水稻增产关键基因4. 科学家首次发现并证实玻色子奇异金属5. 我国科学家将二氧化碳人…

uni-app 微信小程序 pdf预览

<div click"getPDF">查看体检报告</div>getPDF() {uni.downloadFile({url: ${this.$baseURL}/file/download?id${this.pdfFileId},//编写在线的pdf地址success: function(res) {const filePath res.tempFilePath;uni.openDocument({filePath: filePath…

Roll-A-Ball 游戏

Roll-A-Ball 游戏 1&#xff09;学习资料 b站视频教程&#xff1a;https://www.bilibili.com/video/BV18W411671S/文档&#xff1a; * Roll-A-Ball 教程&#xff08;一)&#xff0c; * Roll-A-Ball 教程&#xff08;二)线上体验roll-a-ball成品 * http://www-personal.umich.e…

Spring面向切面编程(AOP);Spring控制反转(IOC);解释一下Spring AOP里面的几个名词;Spring 的 IoC支持哪些功能

文章目录 Spring面向切面编程(AOP)什么是AOPSpring AOP and AspectJ AOP 的区别&#xff1f;Spring AOP中的动态代理如何理解 Spring 中的代理&#xff1f;解释一下Spring AOP里面的几个名词Spring在运行时通知对象Spring切面可以应用5种类型的通知&#xff1a;什么是切面 Aspe…

第八节HarmonyOS @Component自定义组件的生命周期

在开始之前&#xff0c;我们先明确自定义组件和页面的关系&#xff1a; 1、自定义组件&#xff1a;Component装饰的UI单元&#xff0c;可以组合多个系统组件实现UI的复用。 2、页面&#xff1a;即应用的UI页面。可以由一个或者多个自定义组件组成&#xff0c;Entry装饰的自定…

ArkUI开发进阶—@Builder函数@BuilderParam装饰器的妙用与场景应用【鸿蒙专栏-05】

ArkUI开发进阶—@Builder函数@BuilderParam装饰器的妙用与场景应用 HarmonyOS,作为一款全场景分布式操作系统,为了推动更广泛的应用开发,采用了一种先进而灵活的编程语言——ArkTS。ArkTS是在TypeScript(TS)的基础上发展而来,为HarmonyOS提供了丰富的应用开发工具,使开…

创建一个带有背景图层和前景图层的渲染窗口

开发环境&#xff1a; Windows 11 家庭中文版Microsoft Visual Studio Community 2019VTK-9.3.0.rc0vtk-example demo解决问题&#xff1a; 创建一个带有背景图层和前景图层的渲染窗口&#xff0c;知识点&#xff1a;1. 画布转image&#xff1b;2. 渲染图层设置&#xff1b;3.…