简单演示springAOP在Mybatis中的使用

MybatisgetMapper底层就是JDK的动态代理,并且运用了springAOP

外部调用

基于sqlSession使用getMapper(接口 . Class)获得动态代理对象

@Test 
public void test2_User(){ 
SysUserMapper 
sysUserMapper 
sq1Session . getMapper(SysUserMapper . class)

 

第一层内置类 DefaultSqlSession.java

此时getMapper会将Class对象和this对象(代指上图的sqlSession)一同传到另一个getMapper中

public <T> T type) { 
return this . configuration . getmapper(type, 
sqlSession: this);

 

第二层内置类 Configuration.java

744 
745 
746 
public <T> 
retu rn 
T getMapper(C1ass<T> type, Sq1Session sq1Session) { 
this . mapperRegistry. getMapper(type, sq1Session) ;

 

第三层内置类 MapperRegistry.java

进一步进入getMapper 就能来到最终执行的地方

28 
29 
38 
31 
32 
33 
34 
35 
36 
37 
38 
39 
public <T> T getMapper(C1ass<T> type, Sq1Session sq1Session) { 
MapperProxyFactory<T> mapperProxyFactory = (MapperProxyFactory)this . knownMappers . get(type) ; 
if (mapperProxyFactory 
null) { 
throw new BindingException( "Type 
is not known to the MapperRegistry. " ) ; 
} else { 
return mapperProxyFactory. newlnstance(sq1Session) 
} catch (Exception var5) { 
throw new BindingException(" Error getting mapper instance. 
Cause: 
+ var5, 
var5) ;

 

BindingException 用来抛出异常 我们常见的也就是这个异常

传入的SysUserMapper要注册到MapperRegistry上,如果没有注册成功就会抛出这个异常

 

public T getMapper type, SqlSession sqlSession) { 
tr:je: interface conj. ia vasn;. ,'nrbat 
(MapperProxyFactory) thi s. known.Mappers. get (type) 
MapperProxyFactory<T> mapperProxyFactory = 
if (mapperProxyFactory null) { 
throw new BindingException 
+ "Type interface com-javasm.mybatis.mapper.SysU 
e + type + " is not known to the MapperRegistry. " ) , 
apper is not known to the MapperRegistry."

 

比如mybatis核心配置文件中没有成功配置SysUserMapper的接口对应的映射文件,就会出这个异常

图中假如没有配置包扫描,使用的是手动配置,然后漏掉了映射文件,就会报这个异常

resource=.com/javasm/myba xml "Z) 
resource= com javasm mybatjs/mapper7SysR01eMapper. xml "D 
(package javasm. mybatis. 
mappers

 

假如注册成功就会是如下情况

28 
29 
30 
31 
32 
33 
type, Sq1Session sqlsession) { 
interface com. iavasili iliaoner_ 
(MapperProxyFactory) his. knownMapperg, get (type) , 
mapperPro.xyFactory = 
thi s. Ma p pe rs 
:ory -= null) { 
ingException ("Type " + type + 
is not known to th 
1 
•interface com.javasm.mybatis.mapper.SysRoleMapper• 
{HashMapSNode@Igog) 
(HashMapSNode@1909) •interface comjavasm.mybatis.mapperSysuserMapper•

 

当注册成功后,进一步进入newInstance这个方法

public <T> T getMapper(C1ass<T> type, Sq1Session sq1Session) { 
MapperProxyFactory<T> mapperProxyFactory = (MapperProxyFactory)this . knownMappers . get(type) ; 
if (mapperProxyFactory 
null) { 
throw new BindingException( "Type 
is not known to the MapperRegistry. " ) ; 
} else { 
return mapperProxyFactory. newlnstlance(sq1Session) ; 
} catch (Exception var5) { 
throw new BindingException(" Error getting mapper instance. 
Cause: 
+ var5, var5);

 

第四层内置类 MapperProxyFactory.java

在这一个类中,会跳转到newInstance方法 这就是JDK的动态代理

在方法的参数表中可以看到 目标对象的类加载器 ,一个新创建的Class数组,一个MapperProxy对象

①目标对象的类加载器

这个东西不是很重要,可以传入任意对象的类加载器,通常传入目标对象,也可以不传,它有默认的ClassLoader()

 

Class数组

对应着手写的方法中的getInterfaces() ,这是这里是直接传入了一个指向目标对象的接口的Class对象到数组中,形成一个Class对象数组

之所以是一个数组是因为JAVA存在单继承多接口的情况

 

 

MapperProxy对象

对应着手写的方法中的InvocationHandler(),必须是一个实现了InvocationHandler接口的对象,或者就干脆是一个“new InvocationHandler()”的匿名函数

 

public class MapperProxyFactory<T> { 
private final Class<T> mapperlnterface; 
new ConcurrentHashMap() ; 
private final Map<Method, Mappermethodlnvoker> methodCache 
public MapperProxyFactory(C1ass<T> mapperlnterface) { this . mapperlnterface 
mapperlnterface ; 
public Class<T> getMapperInterface() { return this . mapperlnterface; 
public Map<Method, MapperMethodInvoker> getMethodCache() { return this . methodCache; } 
protected T newlnstance(MapperProxy<T> mapperproxy) { 
return Proxy. newproxylnstance(this . mapperlnterface . getC1assLoader(), 
new Class . mapperlnterface}, mapperP 
xy) ; 
public T newlnstance(Sq1Session sq1Session) { 
MapperProxy<T> mapperProxy = new MapperProxy(sq1Session, 
return this . newlnstance(mapperproxy) ; 
this . mapperlnterface , 
this . methodCache) ;

 

第五层内置类 MapperProxy.java

通过MapperProxy对象进入这个类,可以看到这个类实现了InvocationHandler接口

public class MapperProxy<T> implements InvocationHand1er, Serializable {

 

手写动态代理方法时,InvocationHandler方法是即时new 即时用 用完就没了 是一个匿名函数

在这里则是由一个有固定名字的类实现了接口,不再以匿名函数的方式使用,并且这个类中肯定有invoke方法,用于执行目标对象中的方法

仔细查看,可以找到这个invoke方法

79 
88 
81 
82 
83 
84 
85 
86 
87 
88 
89 
98 
'@Ovepride 
public Object i+oke(Object proxy, Method method, ObjectC] args) throws Throwable { 
if (Object . class . equals (method . getDec1aringC1ass 
return method . invoke( obj: this, args); 
} else { 
return cachedlnvoker(method) . invoke (proxy, method, args, sq1Session) • 
} catch (Throwable t) { 
throw ExceptionUti1. unwrapThrowabLe(t);

 

这个invoke方法会判断method是否来自于object

如果是就不进行增强,比如toStringgetClass

try 
if (Object. class. equals (method. getDec1aringC1ass { 
return method. invoke (k a this, args) ; 
el se 
DebugüF tostring()

 

执行完毕后就会输出对象

SysUserMapper sysUserMapper = sq1Session. getMapper (SysUserMapper. class) , 
System. out. printin (sysUserMapper) ;

 

如果不是,则执行增强

De bug 模 式 下 执 行 一 个 非 obje ( t 的 方 法 , 此 时 就 会 进 行 增 强 可 以 看 到 执 行 的 方 法 名 以 及 生 成 的 代 理 对 象 名

 

 

源码中代理类实现的接口是一个list集合 因为目标对象可能实现了多个接口

interfaces 
userSerrice. getC1ass () . get Interfaces ( ) 
System. out. 
—iaterraces)

 

 

posted @ 2021-08-31 21:21  夏·舍  阅读(268)  评论(0编辑  收藏  举报