Mybatis插件机制
创始人
2024-02-21 08:21:41

什么是插件机制

插件插件, 就是能在执行某个方法之前加入一些功能代码, 有啥方法能够实现呢?当然是动态代理了, 为啥要使用动态代理应为他是为了写框架扩展性必备的东西。 只要定义一些接口 或者类 就行使用jdk自带的或者CGLIB之类的动态代理库完成方法的织入。

学习之前需要掌握的知识点

1、 动态代理 2、注解 3、反射 4、责任链的设计模式

反射调用对象

public class Invocation {private final Object target; //目标对象private final Method method; //目标方法private final Object[] args; //目标参数public Object proceed() throws InvocationTargetException, IllegalAccessException {return method.invoke(target, args); //反射调用目标方法}
}

这里需要注意 proceed这个方法, 因为这个方法的设计得很重要。

Interceptor 接口设计

public interface Interceptor {/*** 拦截执行方法* @param invocation* @return* @throws Throwable*/Object intercept(Invocation invocation) throws Throwable;default Object plugin(Object target) {return Plugin.wrap(target, this); //对目标对象进行动态代理,this为拦截器}default void setProperties(Properties properties) {/*properties 属性注射方法*/// NOP}}

责任链模式的设计

public class InterceptorChain {private final List interceptors = new ArrayList<>(); //所有的执行器链public Object pluginAll(Object target) {//target 目标对象for (Interceptor interceptor : interceptors) {target = interceptor.plugin(target); //进行代理}return target;}public void addInterceptor(Interceptor interceptor) {interceptors.add(interceptor);}public List getInterceptors() {return Collections.unmodifiableList(interceptors);}}

注解设计

我们插件机制最细维度是希望能够为某个具体的方法进行功能上的增强。在java语法中, 一个方法的标识 需要有所属的Class对象,已经方法名称 , 已经方法形参类型。 所以我们可以设计一个注解来说明要在那个类对象的那个方法进行插件机制的功能增强。

@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target({})
public @interface Signature {/*** Returns the java type.** @return the java type*/Class type(); //类对象/*** Returns the method name.** @return the method name*/String method();//方法/*** Returns java types for method argument.* @return java types for method argument*/Class[] args(); //参数的类对象数组
}

当然为了支持一个拦截器能够插入多个方法, 我们需要再设计一个注解 也就是支持 @Signature 注解的数组形式。

@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface Intercepts {/*** Returns method signatures to intercept.** @return method signatures*/Signature[] value(); //数组形式
}

解析注解

注解提供了配置说明, 但是具体这个配置说明的功能还需要我们去编写代码去实现。

提取注解 getSignatureMap

private static Map, Set> getSignatureMap(Interceptor interceptor) {Intercepts interceptsAnnotation = interceptor.getClass().getAnnotation(Intercepts.class); //获取注解// issue #251if (interceptsAnnotation == null) { //没有发现注解throw new PluginException("No @Intercepts annotation was found in interceptor " + interceptor.getClass().getName());}Signature[] sigs = interceptsAnnotation.value(); //获取注解的valueMap, Set> signatureMap = new HashMap<>(); //创建存储对象for (Signature sig : sigs) { //遍历数组Set methods = MapUtil.computeIfAbsent(signatureMap, sig.type(), k -> new HashSet<>()); //判断这个类对象的Set是否存在,不存在就新建,存在就用原来的try {Method method = sig.type().getMethod(sig.method(), sig.args()); //获取目标方法methods.add(method); //塞入目标方法} catch (NoSuchMethodException e) {throw new PluginException("Could not find method on " + sig.type() + " named " + sig.method() + ". Cause: " + e, e); //方法找不到异常}}return signatureMap; //注解信息,提取完毕}

这样我们就获得了 该拦截器插件 能对那个类的那些方法进行增强的信息。那么接下来就是需要判断增强的目标对象是否在插件的类和方法声明内。

/*** * @param type 增强的目标方法* @param signatureMap 增强允许的类和方法列表* @return 增强的接口*/private static Class[] getAllInterfaces(Class type, Map, Set> signatureMap) {Set> interfaces = new HashSet<>(); //用来存放实现的接口, 因为采用的是jdk的动态代理while (type != null) {for (Class c : type.getInterfaces()) { //获取当前类的所有接口if (signatureMap.containsKey(c)) { //接口是否在提取出来的注解信息中存在interfaces.add(c);}}type = type.getSuperclass(); //获取父类}return interfaces.toArray(new Class[0]);}

进行代理

public static Object wrap(Object target, Interceptor interceptor) {Map, Set> signatureMap = getSignatureMap(interceptor); //提取Intercepts注解信息完毕Class type = target.getClass(); //获取目标类类对象Class[] interfaces = getAllInterfaces(type, signatureMap); //获取type 在signatrueMap中所有的接口集合if (interfaces.length > 0) {//说明有接口, 进行动态代理return Proxy.newProxyInstance(type.getClassLoader(), //当前类的类加载器interfaces, //接口new Plugin(target, interceptor, signatureMap)); //invocationhandler}return target;}

Invocationhandler 精髓实现

 public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {try {Set methods = signatureMap.get(method.getDeclaringClass()); //获取目标方法if (methods != null && methods.contains(method)) { //当前方法是否是在拦截中return interceptor.intercept(new Invocation(target, method, args)); //调用拦截器}return method.invoke(target, args); //不在拦截中直接调用} catch (Exception e) {throw ExceptionUtil.unwrapThrowable(e);}}

源码插件机制扩展点

从以上代码分析,要实现代理我们只需要调用InterceptorChain.pluginAll 传入目标方法就行。 Mybatis源码中留下这样的扩展点有那些呢?

其实只要看pluginAll在那些源码中用到就行, 在IDEA中只需在鼠标方法在这个方法上然后按住ctrl 接着再按鼠标左键
在这里插入图片描述

可见Mybatis源码中一共留下了4个插件扩展点, 分别是对参数处理器 、 结果集处理器、表达式处理器、执行器处理器的增强代理。

相关内容

热门资讯

【看表情包学Linux】进程地...   🤣 爆笑教程 👉 《看表情包学Linux》👈 猛...
吉字五行及吉凶 吉字五行中代表... 五行解析在文化中,五行是非常重要的概念之一,在这里解析一下五行对于人们生活的影响。首先,金属代表的是...
月老姻缘灵签内容详解大全 月老... 月老灵签 姻缘签44签 求解签君尔目下之人。本是可心满意足之人。焉知后来之人。一个比一个更美好。就此...
六爻排盘蛇 六爻排盘预测绝招 ... 六爻排盘结果怎么看纳甲六爻在线排盘姓名:出生年:1981性别:男占事:起卦方式:手动摇卦公历时间:2...
吉凶由情绪决定 每日吉凶 每月... 情绪的力量情绪是我们生活中一个重要的组成部分。我们每天都会通过不同的方式感受到情绪的存在,而情绪的质...
吉凶悔吝的解释是什么 风水形势... 吉凶悔吝的解释是什么从古至今,人们对于吉凶悔吝都有着不同的看法。所谓吉,是指好的运气,让人们沾沾自喜...
如何看每日生肖运势 每日生肖运... 背景说明每个人都希望自己的运势越来越好,而对于人来说,生肖运势是一个参考价值很高的判断标准。按照传统...
最准观音灵签21签解签 观音灵... 观音灵签21签解签-遵医嘱,健康长寿观音精神签证是中国民间宗教信仰的重要形式,也是一种广泛流传的祈祷...
梦见红裤子被水冲走 梦见河里洗... 梦见红裤子被水冲走红裤子是一种比较鲜艳的颜色,在梦中出现可能代表着某种情绪或状态。而被水冲走则更加具...
十二星座对象配对 12星座最佳... 12星座配偶标准白羊座:温柔善良的人乐观单纯的白羊座在恋爱时喜欢另一半无条件的宠爱自己,另一半对自己...
带水又带土的名字女孩名字有哪些... 含水和土的字有哪些含水的字:淦、澜、浸、泼、滴、没、汪、沸、鸿、沔、浩、渣、溢、潺江、注、漭、淬、澧...
六爻失物卦 在线占卜失物 六爻... 六爻占卜 寻找失物公历起卦时间:2012年12月24日9时44分(按公历时间起卦)农历:仁辰年十一月...
天网今日生肖运势 每日特吉生肖... 天网今日生肖运势天网今日,十二生肖依旧是重要的关键词之一。根据传统文化和民间信仰,每个人都属于一个生...
六爻代表书籍 六爻预测好的书籍... 学六爻的书籍那些比较经典,最好适合初学者的。从古至今六爻类的书流传于世的非常少,六爻类最经典的几本书...
十二星座女生专属花卉 小葩画1... 狮子座的女生喜欢什么样的花1.狮子座的女生喜欢鲜艳、华丽、高贵的花。2.狮子座的女生通常有着自信、热...
客厅风水禁忌及化解 客厅推拉门... 客厅风水禁忌及解决方案客厅是家庭中最重要的空间之一,也是最容易受到风水影响的空间之一。在客厅里,我们...
吉凶参半牛兔在含义 牛兔相冲到... 吉凶参半牛兔在啥意思吉凶参半牛兔在是指属牛的和属兔的结婚以后生活吉凶各一半。丑牛与子鼠六合,因此最宜...
各生肖属相的车牌号码吉凶对照表... 十二生肖与车牌号的佳搭配 十二生肖车牌号吉凶对照表通常每个人的黄道十二宫都会影响车牌号码的运行模式,...
客厅西部尖角的风水 客厅有棱角... 客厅西南角最好的风水是什么?客厅西南角最好的风水是什么?客厅西南角最好的风水是什么?房子的方形风水是...
带昶字的女孩名字 带滢的女孩名... 长字命名的寓意及意义长字命名的寓意和意义是正直、坚强、努力、阳光、前途似景、忠诚。长是一个通用词。长...
六壬怎么算命 六壬掐指神算金口... 什么是六仁?刘仁是中国古代的算命方法,起源于汉代,是中国道教学派的经典之一。刘仁包括六个神:天乙、天...
四月二十九生肖运势 十二生肖鸡... 女1993农历四月二十九早上十点生辰八字是什么如何出生时间:公历 1993年 6月 18日 10点本...
带心字的游戏女孩名字大全集 游... 2020男孩怎么起名有内涵 带心字的男孩名字大全心繁体:心起名五行:金姓名学笔画:4画简体笔画:4画...
八字鬼谷子算命 鬼谷子精髓50... 什么是八字鬼谷子算命?八字鬼谷子算命,又称李静算命,是中国传统的民间算命方式。八字鬼谷子算命起源于六...
十二星座下个月的运势女生 十二... 白羊座下个月的运势女生白羊座女生本身就充满了无限活力和热情,下个月的运势也不会让你失望。职业上可能会...
号令天下手机吉凶预测 号令天下... 手机号怎么算吉凶?用最后四个手机号码除以80,然后减去整数部分(只留小数),再乘以80,就会得到一个...
命理十二生肖今年运势 明天运势... 命理十二生肖今年运势今年每年都有不同的转瞬即逝的岁月。对于不同的黄道十二宫来说,它每年都有自己独特的...
八字长生好吗 八字中帝旺到长生... 八字日坐长生一定富吗丁火曰元生于未月,余气通根,年支丙火也能助身,但于上两透旺食,生财耗身过甚,故命...
带日的名字女孩名字大全 起名带... 日字旁边的女孩名字大全日字旁边的女孩名字推荐1、诗晗、慧曦、Xi、仲晴2、小芸、小娟、会晴、若昕、敏...
号令天下固话号码测吉凶 查电话... 周易81测手机号码吉凶,号令天下手机号码测吉凶提起周易81测手机号码吉凶,大家都知道,有人问天下手机...