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 ...
随机推荐
- ubuntu初始化root密码
输入 sudo passwd root 输入两遍密码.
- sql数据库(更新)
安装postgreSQL,遇到的bug:不要选择默认路径,安装到program files,否则连接数据库的时候会出现问题 如图显示数据库连接成功. 1.创建数据库——执行创建数据库的SQL 语句 C ...
- 装numpy 环境:python3.4+ windows7 +64位系统
机器学习实战python 因为图像处理的原因,初步学习机器学习,选用语言python,参考书籍<机器学习实战> 环境:python3.4+ windows7 +64位系统 首先,今天解决的 ...
- coding++:Spring_IOC(控制反转)详解
IoC是什么: 1):Ioc—Inversion of Control,即“控制反转”,不是什么技术,而是一种设计思想. 2):在Java开发中,Ioc意味着将你设计好的对象交给容器控制,而不是传统的 ...
- 第二次实验报告:使用 Packet Tracer 分析应用层协议
个人信息: 姓名:倪晓东 班级:计算1811 学号:201821121020 1 实验目的 熟练使用Packet Tracer工具.分析抓到的应用层协议数据包,深入理解应用层协议,包括语法.语义.时序 ...
- 【springmvc thymeleaf】springmvc整合thymeleaf
概述 Thymeleaf提供了一组Spring集成,使您可以将其用作Spring MVC应用程序中JSP的全功能替代品. 这些集成将使您能够: @Controller像使用JSP一样,将Spring ...
- 常用的python标准库
os : 操作系统接口 sys: 命令行操作 re : 正则模块 math : 数学模块 time,timedate: 日期模块 random: 随机数模块 threading: 线程 ...
- 剑指offer刷题总结
★ 二维数组的查找 在一个二维数组中(每个一维数组的长度相同),每一行都按照从左到右递增的顺序排序,每一列都按照从上到下递增的顺序排序.请完成一个函数,输入这样的一个二维数组和一个整数,判断数组中是否 ...
- SQL实战(六)
一. 题目描述 查找排除当前最大.最小salary之后的员工的平均工资avg_salary.CREATE TABLE `salaries` ( `emp_no` int(11) NOT NULL,`s ...
- C++ 按址操作
一.指针 二.变量与指针 注意区别char 和char *. !!!!!!! 二.函数与指针 #include<iostream> #include<string> using ...