JDK常用核心原理

概述

在 Mybatis 中,常用的作用就是讲数据库中的表的字段映射为对象的属性,在进入Mybatis之前,原生的 JDBC 有几个步骤:导入 JDBC 驱动包,通过 DriverManager 注册驱动,创建连接,创建 Statement,增删改查,操作结果集,关闭连接

过程详解

首先进行类的加载,通过 DriverManager 注册驱动

Class.forName("com.mysql.jdbc.Driver");
Connection connection = DriverManager.getConnection("");

为什么在这里可以直接注册进去,com.mysql.jdbc.Driver 被加载到 Driver.class ,在 DriverManager 中,首先有一个静态代码块来进行初始化加载 Driver

static {
loadInitialDrivers();
println("JDBC DriverManager initialized");
}

通过 loadInitialDrivers(),来加载 Driver,拿出 jdbc.drivers,通过 ServiceLoader 读取 Driver.class,读取拿出 driver 和 所有迭代器,一直迭代

private static void loadInitialDrivers() {
String drivers;
// 访问修饰符,在这里把 jdbc.drivers 拿出来
try {
drivers = AccessController.doPrivileged(new PrivilegedAction<String>() {
public String run() {
return System.getProperty("jdbc.drivers");
}
});
} catch (Exception ex) {
drivers = null;
} AccessController.doPrivileged(new PrivilegedAction<Void>() {
public Void run() {
// 读取拿出 driver 和 所有迭代器
ServiceLoader<Driver> loadedDrivers = ServiceLoader.load(Driver.class);
Iterator<Driver> driversIterator = loadedDrivers.iterator(); // 一直进行迭代
try{
while(driversIterator.hasNext()) {
driversIterator.next();
}
} catch(Throwable t) {
// Do nothing
}
return null;
}
}); println("DriverManager.initialize: jdbc.drivers = " + drivers); if (drivers == null || drivers.equals("")) {
return;
}
String[] driversList = drivers.split(":");
println("number of Drivers:" + driversList.length);
for (String aDriver : driversList) {
try {
println("DriverManager.Initialize: loading " + aDriver);
Class.forName(aDriver, true,
ClassLoader.getSystemClassLoader());
} catch (Exception ex) {
println("DriverManager.Initialize: load failed: " + ex);
}
}
}

从 Driver 加载完后,就可以得到一个和数据库的连接 connection ,connection 就可以创建一个 Statement,Statement 就可以进行执行 sql 语句,将结果返回一个结果集,获取出来的结果集遍历放进一个 List 集合中

Statement statement = connection.createStatement();
ResultSet resultSet = statement.executeQuery("select * from mybatis.user");
while (resultSet.next()) {
int id = resultSet.getInt(1);
String username = resultSet.getString(2);
list.add(new User(id,username));
}

在原生的 JDBC 直接操作中,繁杂的步骤在业务代码中不会使用,而 Mybatis 可以在更好的便利度上使用

JDK动态代理

sql语句解析替换

在 JDK 动态代理中,利用了 Proxy 这个类来实现,在 Proxy 中,有着 newProxyInstance() 方法,创建一个动态代理实例

interface UserMapper {
@Select("select * from mybatis.user where id =#{id}")
List<User> selectUserList();
} public static void main(String[] args) {
UserMapper userMapper = (UserMapper) Proxy.newProxyInstance(
JDKMybatis.class.getClassLoader(),
new Class<?>[]{UserMapper.class},
new InvocationHandler() {
/**
* 在 invoke() 方法中就可以进行查找 method,args
* @param proxy 动态代理
* @param method 方法
* @param args 参数
*/
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
// 把注解类获取,可以查出注解的值等多种其他值
Select annotation = method.getAnnotation(Select.class);
if (annotation != null) {
String[] value = annotation.value();
System.out.println(Arrays.toString(value));
}
return null;
}
}); userMapper.selectUserList(1);
}

newProxyInstance() 的创建需要三个参数,查看源码,可以知道需要 ClassLoader 类加载器,interfaces 接口(Mapper 接口),InvocationHandler 处理器,来进行处理

public static Object newProxyInstance(ClassLoader loader,
Class<?>[] interfaces,
InvocationHandler h)



把 sql 语句中的参数取出来放进 args,这时需要一个 Map 来进行传值

问题

当在通过反射获取方法的参数名,method.getParameters() 获取出来的参数都是 arg0,arg1...无意义参数



在Java8之前,代码编译为class文件后,方法参数的类型是固定的,但参数名称却丢失了,在编译的时候,需要有编译的选项,javac -parameters 默认是关闭的,需要在 idea 中设置开启,开启完成后,重新编译源文件



这种方式只能临时解决当前环境设置,在其他人运行代码时还是要重新设置

另一种解决方式,在pom文件中添加编译参数:

<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<compilerArgument>-parameters</compilerArgument>
<source>1.8</source>
<target>1.8</target>
</configuration>
</plugin>
</plugins>

编译完成后,重新执行,再次通过method.getParameters()获取参数:



解析原来的 sql ,就要把 #{} 给替换掉,这时候可以使用 StringBuffer 类来实现替换

private static String parseSql(String sql, Map<String, Object> argsNameMap) {
// 定义为常量数组
char[] str = {'#', '{'};
StringBuilder stringBuilder = new StringBuilder();
for (int i = 0; i < sql.length(); i++) {
char aloneParseSql = sql.charAt(i); if (str[0] == aloneParseSql) {
int nextIndex = i + 1;
char nextChar = sql.charAt(nextIndex); // # 后应该是 { ,不匹配直接抛出异常
if (str[1] != nextChar) {
throw new RuntimeException(String.format(
"此处应该是:#{\n sql:%s\n index:%d",
stringBuilder.toString(), nextIndex));
}
/*
1 已经解析完的下标
2 解析完的 #{} 内的参数名
3 把对应的 argsName 的值 argsValue 取出来
4 追加到原来的 stringBuilder 中的 sql 语句后面
*/
StringBuilder partStringBuilder = new StringBuilder();
i = partParseSql(partStringBuilder, sql, nextIndex);
String argsName = partStringBuilder.toString();
Object argsValue = argsNameMap.get(argsName);
stringBuilder.append(argsValue.toString());
} // 如果没有条件,直接追加
stringBuilder.append(aloneParseSql);
}
return stringBuilder.toString();
}

在其中需要把需要替换的值,再用 StringBuffer 类来实现

private static int partParseSql(StringBuilder partStringBuilder, String sql, int nextIndex) {
// 由于 nextIndex 当前指针指向的是 { 所以要加一位,把后面内容解析
nextIndex++;
char[] str = {'}'};
for (; nextIndex < sql.length(); nextIndex++) {
char indexSql = sql.charAt(nextIndex);
if (str[0] != indexSql) {
partStringBuilder.append(indexSql);
}
if (str[0] == indexSql) {
return nextIndex;
}
}
throw new RuntimeException(String.format(
"缺少:}\n index:%d",
nextIndex));
}

再重新在 invoke 方法中进行调用,完成 sql 语句的动态拼装

@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
// 把注解类获取,可以查出注解的值等多种其他值
Select annotation = method.getAnnotation(Select.class);
Map<String, Object> argsNameMap = MapBuildArgsName(method, args);
if (annotation != null) {
String[] value = annotation.value();
String sql = value[0];
sql = parseSql(sql, argsNameMap);
System.out.println(sql);
}
return null;
}

Mybatis(一)Porxy动态代理和sql解析替换的更多相关文章

  1. jdk动态代理和cglib动态代理底层实现原理详细解析(cglib动态代理篇)

    代理模式是一种很常见的模式,本文主要分析cglib动态代理的过程 1. 举例 使用cglib代理需要引入两个包,maven的话包引入如下 <!-- https://mvnrepository.c ...

  2. 基于JDK动态代理和CGLIB动态代理的实现Spring注解管理事务(@Trasactional)到底有什么区别。

    基于JDK动态代理和CGLIB动态代理的实现Spring注解管理事务(@Trasactional)到底有什么区别. 我还是喜欢基于Schema风格的Spring事务管理,但也有很多人在用基于@Tras ...

  3. Spring -- <tx:annotation-driven>注解基于JDK动态代理和CGLIB动态代理的实现Spring注解管理事务(@Trasactional)的区别。

    借鉴:http://jinnianshilongnian.iteye.com/blog/1508018 基于JDK动态代理和CGLIB动态代理的实现Spring注解管理事务(@Trasactional ...

  4. Spring <tx:annotation-driven>注解 JDK动态代理和CGLIB动态代理 区别。

    基于JDK动态代理和CGLIB动态代理的实现Spring注解管理事务(@Trasactional)到底有什么区别. 我还是喜欢基于Schema风格的Spring事务管理,但也有很多人在用基于@Tras ...

  5. 通过一个工具类更深入理解动态代理和Threadlocal

    动态代理和Threadlocal 一个代理类返回指定的接口,将方法调用指定的调用处理程序的代理类的实例.返回的是一个代理类,由指定的类装载器的定义和实现指定接口指定代理实例调用处理程序最近用到一个工具 ...

  6. JDK动态代理和CGLIB的区别

    Aspect默认情况下不用实现接口,但对于目标对象,在默认情况下必须实现接口 如果没有实现接口必须引入CGLIB库 我们可以通过Advice中添加一个JoinPoint参数,这个值会由spring自动 ...

  7. JDK动态代理和CGLib动态代理简单演示

    JDK1.3之后,Java提供了动态代理的技术,允许开发者在运行期间创建接口的代理实例. 一.首先我们进行JDK动态代理的演示. 现在我们有一个简单的业务接口Saying,如下: package te ...

  8. SpringAOP-JDK 动态代理和 CGLIB 代理

    在 Spring 中 AOP 代理使用 JDK 动态代理和 CGLIB 代理来实现,默认如果目标对象是接口,则使用 JDK 动态代理,否则使用 CGLIB 来生成代理类. 1.JDK 动态代理 那么接 ...

  9. JDK动态代理和CGLIB代理的区别

    一.原理区别: java动态代理是利用反射机制生成一个实现代理接口的匿名类,在调用具体方法前调用InvokeHandler来处理. 而cglib动态代理是利用asm开源包,对代理对象类的class文件 ...

随机推荐

  1. Jmeter的脚本参数化

    一.变量 Jmeter中的变量用法:${变量名称} 变量定义:两种 1.用户定义变量 User Defined Variables 2.用户参数  User Parameters 1.1用户定义变量 ...

  2. 《PYTHON机器学习及实践-从零开始通往KAGGLE竞赛之路》 分享下载

    转: <PYTHON机器学习及实践-从零开始通往KAGGLE竞赛之路> 分享下载 书籍信息 书名: PYTHON机器学习及实践-从零开始通往KAGGLE竞赛之路 标签: PYTHON机器学 ...

  3. 为 APK 文件增加右键菜单组实现快捷安装

    0.结果 1.需求 迫于每次都要打开 Powershell 手动敲 adb install xxx.apk 太麻烦,就想通过注册表搞一个右键菜单,实现快捷安装 apk 的功能. 最后决定先实现三个功能 ...

  4. Salesforce LWC学习(三十一) Quick Action适配

    本篇参考:https://www.lightningdesignsystem.com/components/modals/ 随着salesforce lwc的优化,越来越多的项目从aura转到了lwc ...

  5. js 浮点运算bug

    js几个浮点运算的bug,比如6.9-1.1,7*0.8,2.1/0.3,2.2+2.1 实现思路 通过将浮点数放大倍数到整型(最后再除以相应倍数),再进行运算操作,这样就能得到正确的结果了 比如:1 ...

  6. Mybatis系列全解(二):Mybatis简介与环境搭建

    封面:洛小汐 作者:潘潘 Mybatis 是一套持久层框架,灵活易用,特别流行. 前言 Mybatis系列全解,我们预计准备10+篇文章,让我们了解到 Mybatis 的基本全貌,真正从入门到上手,从 ...

  7. Java实现解压缩文件和文件夹

    一 前言 项目开发中,总会遇到解压缩文件的时候.比如,用户下载多个文件时,服务端可以将多个文件压缩成一个文件(例如xx.zip或xx.rar).用户上传资料时,允许上传压缩文件,服务端进行解压读取每一 ...

  8. asp.net core 实现支持自定义 Content-Type

    asp.net core 实现支持自定义 Content-Type Intro 我们最近有一个原本是内网的服务要上公网,在公网上有一层 Cloudflare 作为网站的公网流量提供者,CloudFla ...

  9. Hibernate学习实例

    一 Hibernate简介 Hibernate是一种Java语言下的对象关系映射(ORM)解决方案.为面向对象的领域模型到传统的关系型数据库的映射提供了一个使用方便的框架. 二 Hibernate设计 ...

  10. 关于github的使用学习心得

    先写先介绍一下如何用github上创建一个项目吧. 用户登录后的界面如上所示.右下角是我们已经建好的库.点击其中任何一个就可以查看相应的库了.如果要新建一个项目的话,就点击Start a projec ...