手写spring+springmvc+mybatis框架篇【Mybatis】

本文涉及的产品
RDS MySQL Serverless 基础系列,0.5-2RCU 50GB
云数据库 RDS MySQL,集群系列 2核4GB
推荐场景:
搭建个人博客
云数据库 RDS MySQL,高可用系列 2核4GB
简介: 手写spring+springmvc+mybatis框架篇【Mybatis】

整合Mybatis是本项目中的一个难点。
实现功能:
1 动态绑定用户输入参数
2 Mybatis的resultType动态绑定返回实体类。
3 在spring中的接口注入
4 xml版本的mapper注入。
关于Mybatis的优秀文章给大家推荐两个:
1、手写简化版mybatis
https://my.oschina.net/liughDevelop/blog/1631006
2、Mybatis源码解读-设计模式总结
http://www.crazyant.net/2022.html
手写板大致思路如下:
这里的Myconfiguration和我的JDBCUtils类似。
实现思路:
首先用XmlBuilderMapper类读取mapper.xml(我是在initBean中指定要读取的配置文件,并没有写成在xml中配置动态的读取xml。)文件,获取MapperInfo对象保存信息。
用户用MysqlSession的getMapper方法,返回一个代理对象,用这个代理对象来执行MySqlSession定义的selectOne方法来查询,Mybaits中的SqlSession中定义了大量的方法,我这里简化只有一个selectOne方法。
然后这个方法调用executor执行器来解析sql,传入参数,执行数据库操作。最后返回结果。将返回结果封装成对象。
动态代理一般有两种,一种是jdk一种是cglib动态代理,本项目采用是jdk动态代理实现。
XmlBuilderMapper.class
package spring.mybatis;
import lombok.extern.slf4j.Slf4j;
import org.dom4j.Document;
import org.dom4j.DocumentException;
import org.dom4j.Element;
import org.dom4j.io.SAXReader;
import spring.constants.Constants;
import java.io.File;
import java.util.ArrayList;
import java.util.List;
/**

  • @ClassName XmlBuilder
  • @Description
  • @Data 2018/7/8
  • @Author xiao liang

*/
@Slf4j
public class XmlBuilderMapper {
public List buildMapper(String xmlMapperPath){

   //实例化mapperInfo的链表,每一条sql对应一个MapperInfo对象
   List<MapperInfo> mapperInfoList = new ArrayList<>();
   MapperInfo mapperInfo = new MapperInfo();
   // 创建saxReader对象
   SAXReader reader = new SAXReader();
   // 通过read方法读取一个文件 转换成Document对象
   Document document = null;
   String pathName = Constants.PATH + xmlMapperPath;
   try {
       document = reader.read(new File(pathName));
   } catch (DocumentException e) {
       log.error("文件没有找到,{}", pathName);
   }
   //获取根节点元素
   Element node = document.getRootElement();
   mapperInfo.setInterfaceName(node.attributeValue("namespace"));
   //获取所有的bean
   List<Element> elementsList = node.elements();
   for (Element element :
           elementsList) {
       if ("select".equals(element.getName())){
           mapperInfo.setMethodName(element.attributeValue("id"));
           mapperInfo.setResultClassName(element.attributeValue("resultType"));
           mapperInfo.setSqlContent(element.getText());
       }
       mapperInfoList.add(mapperInfo);
   }
   return mapperInfoList;

}
}
然后介绍一下MapperInfo对象
package spring.mybatis;
import lombok.Data;
/**

  • @ClassName MapperInfo
  • @Description 用来封装读取mapper.xml文件后的信息
  • @Data 2018/7/8
  • @Author xiao liang

*/
@Data
public class MapperInfo {
//namespace命名空间
private String interfaceName;
//sql内容
private String sqlContent;
//对应的方法名
private String methodName;
//返回值的class名
private String resultClassName;
}
JDBCUtils工具类我在开篇就介绍了,也没什么新内容,不贴在这里了
其实关键点就是动态代理和执行器
MySqlSession(动态代理部分)
package spring.mybatis;
import java.lang.reflect.Proxy;
/**

  • @ClassName MySqlSession
  • @Description
  • @Data 2018/7/8
  • @Author xiao liang

*/
public class MySqlSession {
public T selectOne(MapperInfo mapperInfo ,Object[] paremeters){

   MyExecutor myexecutor = new MyExecutor();
   return myexecutor.query(mapperInfo,paremeters);

}

public T getMapper(Class<?> aClass,String mybatisXmlName){

   return (T) Proxy.newProxyInstance(aClass.getClassLoader(),new Class[]{aClass},new MyMapperProxy(this,mybatisXmlName));

}
}
MyMapperProxy(jdk动态代理的实现) 代理之后执行的还是sqlSession中的方法
package spring.mybatis;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.util.List;
/**

  • @ClassName MyMapperProxy
  • @Description
  • @Data 2018/7/8
  • @Author xiao liang

*/
public class MyMapperProxy implements InvocationHandler {
private MySqlSession mySqlSession;
private String mybatisXmlName;
//mybatisXmlName传入的要读取的xml文件名
public MyMapperProxy(MySqlSession mySqlSession , String mybatisXmlName){

  this.mySqlSession = mySqlSession;
  this.mybatisXmlName = mybatisXmlName;

}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {

   XmlBuilderMapper xmlBuilderMapper = new XmlBuilderMapper();
   List<MapperInfo> mapperInfoList = xmlBuilderMapper.buildMapper(mybatisXmlName);
   //如果存在sql,开始执行方法
   if (mapperInfoList != null && mapperInfoList.size() != 0){
       for (MapperInfo mapperInfo :
               mapperInfoList) {
           if (!method.getDeclaringClass().getName().equals(mapperInfo.getInterfaceName())){
               return null;
           }
           if (method.getName().equals(mapperInfo.getMethodName())){
               //其实最后执行的mySqlSession中的方法,args是用户传递的参数数组
               return mySqlSession.selectOne(mapperInfo,args);
           }
       }
   }
   return null;

}
}
返回到MySqlSession后,就轮到执行器MyExcutor出场了。
MyExcutor:也是一个难点,先说一下程序的逻辑。读取到mapperinfo对象,获得定义的sql,返回类的名称等信息。
用正则解析sql,根据#{},判断sql中有几处?,然后将用户传递的参数按照顺序依次写入到sql的?中。
最后读取结果集,用set方法注入到返回的对象中,这样就实现了返回时候的实体类绑定了。
package spring.mybatis;
import lombok.extern.slf4j.Slf4j;
import spring.Utils.GetMethodName;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
/**

  • @ClassName MyExecutor
  • @Description 执行器
  • @Data 2018/7/8
  • @Author xiao liang

*/
@Slf4j
public class MyExecutor {
public T query(MapperInfo mapperInfo, Object[] paremeters) {

   //获取mapper.xml文件中的sql语句
   String preSql = mapperInfo.getSqlContent();
   //正则匹配规则,匹配有几个#{}
   String rgex = "#\\{.*?}";
   String sql = null;
   String resultClassName = mapperInfo.getResultClassName();
   Class<?> aClass = null;
   Field[] fields = null;
   Method[] methods = null;
   Object object = null;
   Pattern pattern = Pattern.compile(rgex);
   Matcher m = pattern.matcher(preSql);
   Connection connection = null;
   ResultSet rs = null;
   PreparedStatement preparedStatement = null;
   //Preparement注入参数的个数
   int orderPre = 0;
   //每匹配一次,加一
   while (m.find()) {
       orderPre++;
   }
   //匹配完成之后,将#{}用?代替,preparement执行sql
   sql = m.replaceAll("?");
   try {
       aClass = Class.forName(resultClassName);
       fields = aClass.getDeclaredFields();
   } catch (ClassNotFoundException e) {
       e.printStackTrace();
   }
   try {
       JDBCUtils jdbcUtils = new JDBCUtils();
        connection = jdbcUtils.getConnection();
       preparedStatement = connection.prepareStatement(sql);
       //将用户传递过来的参数按照顺序依次赋值给prepareStatement的sql
       for (int i = 1; i <= orderPre; i++) {
           preparedStatement.setObject(i, paremeters[i - 1]);
       }
       rs = preparedStatement.executeQuery();
       object = aClass.newInstance();
       while (rs.next()) {
           int i = 1;
           for (Field field :
                   fields) {
               //遍历每个属性,然后将结果集中的数据用set方法注入到返回的对象中
               String setMethodNameByField = GetMethodName.getSetMethodNameByField(field.getName());
               Method method2 = aClass.getMethod(setMethodNameByField, field.getType());
               if (field.getType().getSimpleName().equals("String")) {
                   method2.invoke(object, rs.getString(i));
               } else if (field.getType().getSimpleName().equals("Integer")) {
                   method2.invoke(object, rs.getInt(i));
               }
               i++;
           }
       }
       return (T) object;
   } catch (SQLException e) {
       log.error("sql语句异常,请检查sql{}", sql);
       e.printStackTrace();
   } catch (InstantiationException e) {
       e.printStackTrace();
   } catch (IllegalAccessException e) {
       e.printStackTrace();
   } catch (InvocationTargetException e) {
       e.printStackTrace();
   } catch (NoSuchMethodException e) {
       e.printStackTrace();
   }
   finally {
       JDBCUtils.colseResource(connection,preparedStatement,rs);
   }
   return null;

}
}
我将此项目上传到了github,需要的童鞋可以自行下载。
https://github.com/836219171/MySSM

相关实践学习
如何在云端创建MySQL数据库
开始实验后,系统会自动创建一台自建MySQL的 源数据库 ECS 实例和一台 目标数据库 RDS。
全面了解阿里云能为你做什么
阿里云在全球各地部署高效节能的绿色数据中心,利用清洁计算为万物互联的新世界提供源源不断的能源动力,目前开服的区域包括中国(华北、华东、华南、香港)、新加坡、美国(美东、美西)、欧洲、中东、澳大利亚、日本。目前阿里云的产品涵盖弹性计算、数据库、存储与CDN、分析与搜索、云通信、网络、管理与监控、应用服务、互联网中间件、移动服务、视频服务等。通过本课程,来了解阿里云能够为你的业务带来哪些帮助 &nbsp; &nbsp; 相关的阿里云产品:云服务器ECS 云服务器 ECS(Elastic Compute Service)是一种弹性可伸缩的计算服务,助您降低 IT 成本,提升运维效率,使您更专注于核心业务创新。产品详情: https://www.aliyun.com/product/ecs
目录
相关文章
|
7天前
|
缓存 Java 开发工具
Spring是如何解决循环依赖的?从底层源码入手,详细解读Spring框架的三级缓存
三级缓存是Spring框架里,一个经典的技术点,它很好地解决了循环依赖的问题,也是很多面试中会被问到的问题,本文从源码入手,详细剖析Spring三级缓存的来龙去脉。
Spring是如何解决循环依赖的?从底层源码入手,详细解读Spring框架的三级缓存
|
7天前
|
缓存 安全 Java
Spring框架中Bean是如何加载的?从底层源码入手,详细解读Bean的创建流程
从底层源码入手,通过代码示例,追踪AnnotationConfigApplicationContext加载配置类、启动Spring容器的整个流程,并对IOC、BeanDefinition、PostProcesser等相关概念进行解释
Spring框架中Bean是如何加载的?从底层源码入手,详细解读Bean的创建流程
|
7天前
|
缓存 前端开发 Java
【Java面试题汇总】Spring,SpringBoot,SpringMVC,Mybatis,JavaWeb篇(2023版)
Soring Boot的起步依赖、启动流程、自动装配、常用的注解、Spring MVC的执行流程、对MVC的理解、RestFull风格、为什么service层要写接口、MyBatis的缓存机制、$和#有什么区别、resultType和resultMap区别、cookie和session的区别是什么?session的工作原理
【Java面试题汇总】Spring,SpringBoot,SpringMVC,Mybatis,JavaWeb篇(2023版)
|
2天前
|
Java 应用服务中间件 开发者
深入探索并实践Spring Boot框架
深入探索并实践Spring Boot框架
13 2
|
2天前
|
机器学习/深度学习 数据采集 JavaScript
ADR智能监测系统源码,系统采用Java开发,基于SpringBoot框架,前端使用Vue,可自动预警药品不良反应
ADR药品不良反应监测系统是一款智能化工具,用于监测和分析药品不良反应。该系统通过收集和分析病历、处方及实验室数据,快速识别潜在不良反应,提升用药安全性。系统采用Java开发,基于SpringBoot框架,前端使用Vue,具备数据采集、清洗、分析等功能模块,并能生成监测报告辅助医务人员决策。通过集成多种数据源并运用机器学习算法,系统可自动预警药品不良反应,有效减少药害事故,保障公众健康。
ADR智能监测系统源码,系统采用Java开发,基于SpringBoot框架,前端使用Vue,可自动预警药品不良反应
|
8天前
|
Java 数据库连接 数据格式
【Java笔记+踩坑】Spring基础2——IOC,DI注解开发、整合Mybatis,Junit
IOC/DI配置管理DruidDataSource和properties、核心容器的创建、获取bean的方式、spring注解开发、注解开发管理第三方bean、Spring整合Mybatis和Junit
【Java笔记+踩坑】Spring基础2——IOC,DI注解开发、整合Mybatis,Junit
|
11天前
|
运维 NoSQL Java
SpringBoot接入轻量级分布式日志框架GrayLog技术分享
在当今的软件开发环境中,日志管理扮演着至关重要的角色,尤其是在微服务架构下,分布式日志的统一收集、分析和展示成为了开发者和运维人员必须面对的问题。GrayLog作为一个轻量级的分布式日志框架,以其简洁、高效和易部署的特性,逐渐受到广大开发者的青睐。本文将详细介绍如何在SpringBoot项目中接入GrayLog,以实现日志的集中管理和分析。
50 1
|
12天前
|
前端开发 JavaScript Java
技术分享:使用Spring Boot3.3与MyBatis-Plus联合实现多层次树结构的异步加载策略
在现代Web开发中,处理多层次树形结构数据是一项常见且重要的任务。这些结构广泛应用于分类管理、组织结构、权限管理等场景。为了提升用户体验和系统性能,采用异步加载策略来动态加载树形结构的各个层级变得尤为重要。本文将详细介绍如何使用Spring Boot3.3与MyBatis-Plus联合实现这一功能。
47 2
|
4月前
|
设计模式 前端开发 Java
了解 Spring MVC 架构、Dispatcher Servlet 和 JSP 文件的关键作用
Spring MVC 是 Spring 框架的一部分,是一个 Web 应用程序框架。它旨在使用 Model-View-Controller(MVC) 设计模式轻松构建Web应用程序。
89 0
|
3月前
|
安全 前端开发 Java
挑战5分钟内基于Springboot+SpringMVC+Mybatis-plus快速构建web后端三层架构
挑战5分钟内基于Springboot+SpringMVC+Mybatis-plus快速构建web后端三层架构
43 1