jdk动态代理:由浅入深理解mybatis底层
什么是代理
代理模式,目的就是为其他对象提供一个代理以控制对某个对象的访问,代理类为被代理者处理过滤消息,说白了就是对被代理者的方法进行增强。
看到这里,有没有感觉很熟悉?AOP,我们熟知的面向切面编程,不也是对方法增强,对切点进行处理过滤么。
其实AOP这种设计思想,他的精髓便是,在预编译和运行阶段使用动态代理实现的。
初体验
下面是我自己写的小例子。
//代理的接口
/**
* @created with IDEA
* @author: yonyong
* @version: 1.0.0
* @date: 2020/4/21
* @time: 21:13
**/
public interface person {
void doSomething();
void fun1();
void fun2();
}
//被代理者/委托人
/**
* @created with IDEA
* @author: yonyong
* @version: 1.0.0
* @date: 2020/4/21
* @time: 21:13
**/
public class Student implements person{
@Override
public void doSomething() {
System.out.println("dosomeThing");
}
@Override
public void fun1() {
System.out.println("fun1");
}
@Override
public void fun2() {
System.out.println("fun2");
}
}
//实现InvocationHandler接口,加入切面逻辑
/**
* @created with IDEA
* @author: yonyong
* @version: 1.0.0
* @date: 2020/4/21
* @time: 21:15
**/
public class StudentProxyHandler implements InvocationHandler {
Student target;
public StudentProxyHandler(Student target) {
this.target = target;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("pre");
Object obj = method.invoke(target,args);
System.out.println("aft");
return obj;
}
}
/**
* @created with IDEA
* @author: yonyong
* @version: 1.0.0
* @date: 2020/4/21
* @time: 21:19
**/
public class Main {
public static void main(String[] args) {
Student p=new Student();
person person = (person) Proxy.newProxyInstance(p.getClass().getClassLoader(), p.getClass().getInterfaces(), new StudentProxyHandler(p));
person.doSomething();
System.out.println("______________________");
person.fun1();
System.out.println("______________________");
person.fun2();
}
}
运行代码,我们可以得到:
pre
dosomeThing
after
______________________
pre
dosomeThing
after
______________________
pre
dosomeThing
after
我们来通过打印的结果来实实在在的体会代理模式的优点及aop的特性:
如果有种业务场景,需要有多个方法有重复的代码块,或者相同的实现规则,如果不用aop我们可能要每个方法写一份规则,或者自定义个方法,每个方法调用来实现。首先这就已经使代码变得侵入性,也违反了java的封装重构原则。
而使用java动态代理,无论这个委托人有多少方法,他都会执行切面逻辑里的规则,这样很便于后期的代码维护,即便是后续加入新的方法,也无须考虑其他的,因为在切面里都已经做好了,这样代码的侵入性便降了很多。其实这和AOP的原理是一样的。
动态代理与mybatis
看到这里,我便觉得mybatis和j动态代理必定有着很紧密的联系。
但我们通过上面的例子,我们知道,想要用动态代理,必须要有个接口的实现类,否则代理接口便没什么意义。而mybatis只是接口+xml的形式,他是怎么被代理的呢?
还有一个疑问,我们知道Spring的注解如果放在service的接口层,而不是放在实现类,他会找不到这个bean,那为什么mapper层可以加注解呢?
首先我们通过查询资料知道,mybatis确实是有动态代理实现的。那我们带着这个疑问,去看mybatis的源码。
为了便于查看源码,我使用sqlsession的方式获取mapper。
LRoleMapper lRoleMapper = sqlSessionFactory.openSession().getMapper(LRoleMapper.class);
lRoleMapper.selectAll();
点进getMapper的实现类
SqlSessionManager
发现这里有sql的各个方法
Connection getConnection(){}
void commit() {}
rollback(){}
int insert(String statement){}
update(String statement){}
...
那我们在往下寻找,我看到了这个方法
private class SqlSessionInterceptor implements InvocationHandler {
public SqlSessionInterceptor() {
}
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
SqlSession sqlSession = (SqlSession)SqlSessionManager.this.localSqlSession.get();
if (sqlSession != null) {
try {
return method.invoke(sqlSession, args);
} catch (Throwable var12) {
throw ExceptionUtil.unwrapThrowable(var12);
}
} else {
SqlSession autoSqlSession = SqlSessionManager.this.openSession();
Object var7;
try {
Object result = method.invoke(autoSqlSession, args);
autoSqlSession.commit();
var7 = result;
} catch (Throwable var13) {
autoSqlSession.rollback();
throw ExceptionUtil.unwrapThrowable(var13);
} finally {
autoSqlSession.close();
}
return var7;
}
}
}
看到这里,是不是眼前一亮,那我们再看这个类的构造方法
private SqlSessionManager(SqlSessionFactory sqlSessionFactory) {
this.sqlSessionFactory = sqlSessionFactory;
this.sqlSessionProxy = (SqlSession)Proxy.newProxyInstance(SqlSessionFactory.class.getClassLoader(), new Class[]{SqlSession.class}, new SqlSessionManager.SqlSessionInterceptor());
}
那看到这里我们明白了,这个类代理的是SqlSessionFactory这个类,而它的切面逻辑,就是执行方法前,如果当前sqlsession存在sqlsession,就正常执行这个方法,如果不存在,就创建一个session,创建失败再回滚数据。
那这里的opensession,我直接贴源码了,无非就是从配置文件读取jdbc,连接验证后,获取会话之类,大家感兴趣可以一层一层往下扒。
private SqlSession openSessionFromDataSource(ExecutorType execType, TransactionIsolationLevel level, boolean autoCommit) {
Transaction tx = null;
DefaultSqlSession var8;
try {
Environment environment = this.configuration.getEnvironment();
TransactionFactory transactionFactory = this.getTransactionFactoryFromEnvironment(environment);
tx = transactionFactory.newTransaction(environment.getDataSource(), level, autoCommit);
Executor executor = this.configuration.newExecutor(tx, execType);
var8 = new DefaultSqlSession(this.configuration, executor, autoCommit);
} catch (Exception var12) {
this.closeTransaction(tx);
throw ExceptionFactory.wrapException("Error opening session. Cause: " + var12, var12);
} finally {
ErrorContext.instance().reset();
}
return var8;
}
我们继续回到getMapper方法,我们一层一层往下扒,最后我们可以看到这个类
public <T> T getMapper(Class<T> type, SqlSession sqlSession) {
MapperProxyFactory<T> mapperProxyFactory = (MapperProxyFactory)this.knownMappers.get(type);
if (mapperProxyFactory == null) {
throw new BindingException("Type " + type + " is not known to the MapperRegistry.");
} else {
try {
return mapperProxyFactory.newInstance(sqlSession);
} catch (Exception var5) {
throw new BindingException("Error getting mapper instance. Cause: " + var5, var5);
}
}
}
再继续扒newInstance方法
//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by Fernflower decompiler)
//
package org.apache.ibatis.binding;
import java.io.Serializable;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.util.Map;
import org.apache.ibatis.reflection.ExceptionUtil;
import org.apache.ibatis.session.SqlSession;
public class MapperProxy<T> implements InvocationHandler, Serializable {
private static final long serialVersionUID = -6424540398559729838L;
private final SqlSession sqlSession;
private final Class<T> mapperInterface;
private final Map<Method, MapperMethod> methodCache;
public MapperProxy(SqlSession sqlSession, Class<T> mapperInterface, Map<Method, MapperMethod> methodCache) {
this.sqlSession = sqlSession;
this.mapperInterface = mapperInterface;
this.methodCache = methodCache;
}
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
if (Object.class.equals(method.getDeclaringClass())) {
try {
return method.invoke(this, args);
} catch (Throwable var5) {
throw ExceptionUtil.unwrapThrowable(var5);
}
} else {
MapperMethod mapperMethod = this.cachedMapperMethod(method);
return mapperMethod.execute(this.sqlSession, args);
}
}
private MapperMethod cachedMapperMethod(Method method) {
MapperMethod mapperMethod = (MapperMethod)this.methodCache.get(method);
if (mapperMethod == null) {
mapperMethod = new MapperMethod(this.mapperInterface, method, this.sqlSession.getConfiguration());
this.methodCache.put(method, mapperMethod);
}
return mapperMethod;
}
}
那到这里我们可以得出,mybatis中使用了大量的java动态代理。
这个getMapper呢,到这里也就结束了,可以看到,SqlSession这个对象,代理的接口就是我们的mapper层dao,而这里的切面逻辑,如果当前声明的类是Object,直接执行方法,如果不是,执行另外的excute。这里我没有细看,初步理解是区分注解sql与mapper.xml 的sql。因为获取mapper的sql是通过反射得到的Class类。有兴趣的同学可以继续扒,我精力有限就先到这儿了。
到此为止,第一个问题迎刃而解,总结来说,其实我们注入的mapper,是动态代理产生的对象
。
那么为什么Mapper层加注解,spring也能获取到呢。
其实这个问题已经和java动态代理没什么关系了,在这里大概解释一下。
mybatis并不是spring的产品,而作为第三方的插件,我们都知道spring被称作胶水框架,而第三方就需要将自己的产品让spring管理。
换句话说,他们和spring自身的bean生命周期并不是同步的。
spring--------------------
class->扫描->新建实例->交给容器
mybatis ---------------------spring---
class -> 扫描-> 新建对象 -> 交给spring
同理,mybatis那就需要自己创建对象,把他交给spring。而我们平时的那些注解Mapperscan之类的,其实只是mybatis在标志这些接口,使用反射,获取这些类,实现一个ImportBeanDefinitionRegistrar接口,把自己产生的对象交给Spring。
jdk动态代理:由浅入深理解mybatis底层的更多相关文章
- JDK动态代理深入理解分析并手写简易JDK动态代理(下)
原文同步发表至个人博客[夜月归途] 原文链接:http://www.guitu18.com/se/java/2019-01-05/27.html 作者:夜月归途 出处:http://www.guitu ...
- JDK动态代理深入理解分析并手写简易JDK动态代理(上)
原文同步发表至个人博客[夜月归途] 原文链接:http://www.guitu18.com/se/java/2019-01-03/27.html 作者:夜月归途 出处:http://www.guitu ...
- JDK动态代理[4]----ProxyGenerator生成代理类的字节码文件解析
通过前面几篇的分析,我们知道代理类是通过Proxy类的ProxyClassFactory工厂生成的,这个工厂类会去调用ProxyGenerator类的generateProxyClass()方法来生成 ...
- JDK动态代理浅析
原文同步发表至个人博客[夜月归途] 原文链接:http://www.guitu18.com/se/java/2018-06-29/17.html 作者:夜月归途 出处:http://www.guitu ...
- 从Mybatis源码理解jdk动态代理默认调用invoke方法
一.背景最近在工作之余,把开mybatis的源码看了下,决定自己手写个简单版的.实现核心的功能即可.写完之后,执行了一下,正巧在mybatis对Mapper接口的动态代理这个核心代码这边发现一个问题. ...
- jdk动态代理底层实现
一.代理设计模式 代理设计模式是Java常用的设计模式之一. 特点: 01.委托类和代理类有共同的接口或者父类: 02.代理类负责为委托类处理消息,并将消息转发给委托类: 03.委托类和代理类对象通常 ...
- jdk动态代理和cglib动态代理底层实现原理超详细解析(jdk动态代理篇)
代理模式是一种很常见的模式,本文主要分析jdk动态代理的过程 1.举例 public class ProxyFactory implements InvocationHandler { private ...
- MyBatis Mapper 接口如何通过JDK动态代理来包装SqlSession 源码分析
我们以往使用ibatis或者mybatis 都是以这种方式调用XML当中定义的CRUD标签来执行SQL 比如这样 <?xml version="1.0" encoding=& ...
- AOP的底层实现:JDK动态代理与Cglib动态代理
转载自 https://www.cnblogs.com/ltfxy/p/9872870.html SpringAOP底层的实现原理: JDK动态代理:只能对实现了接口的类产生代理.(实现接口默认JDK ...
随机推荐
- leetcode 签到 面试题40. 最小的k个数
题目 输入整数数组 arr ,找出其中最小的 k 个数.例如,输入4.5.1.6.2.7.3.8这8个数字,则最小的4个数字是1.2.3.4. 示例 1: 输入:arr = [3,2,1], k = ...
- 用python的BeautifulSoup分析html
序言 之前用python爬取网页的时候,一直用的是regex或者自带的库sgmllib里的SGMLParser.但是遇到复杂一点的情况时,SGMLParser往往就不那么给力了!(哈,难道说我 too ...
- 关于虚拟机NAT连接外网!!!
点开Virtual Network Editor(开始->VMware),选择vmnet8, NAT setting就可以知道了. 1. 虚拟机NAT和主机的通讯方式,是通过虚拟网关(即NAT ...
- Python第七章-面向对象
面向对象编程基础 一.面向对象概念 1.1 什么是面向过程 就是分析出解决问题所需要的步骤,然后用函数把这些步骤一步一步实现,使用的时候一个一个依次调用就可以了. 生活中的的例子举例. 1.2 ...
- 面试必备:详解Java I/O流,掌握这些就可以说精通了?
@TOC Java IO概述 IO就是输入/输出.Java IO类库基于抽象基础类InputStream和OutputStream构建了一套I/O体系,主要解决从数据源读入数据和将数据写入到目的地问题 ...
- 艾编程coding老师课堂笔记:SpringBoot源码深度解析
思想:有道无术,术尚可求,有术无道,止于术! Spring 开源框架,解决企业级开发的复杂性的问题,简化开发 AOP, IOC Spring 配置越来多,配置不方便管理! Javaweb---Serv ...
- MySQL 解析 json 数组(mysql在5.7开始支持json解析)
1.函数 JSON_EXTRACT 表数据格式: 查询结果: sql 语句: -- ---------------------------- -- Table structure for json ...
- postfix基础邮件服务
postfix基础邮件服务 案例1:postfix基础邮件服务 1.1 问题 本例 ...
- Shell:Day10
shell脚本:明白一点:shell脚本本身是一个工具 在写shell脚本之前,就要明白:这个功能能到底如何实现? curl 访问文件源代码,查看网站状态: 才能通过shell(bash)所提供的逻辑 ...
- Scratch 第3课神奇画笔
素材及视频下载 链接:https://pan.baidu.com/s/1qX0T2B_zczcLaCCpiRrsnA提取码:xfp8