【Mybtais】Mybatis 插件 Plugin开发(一)动态代理步步解析

需求:

  对原有系统中的方法进行‘拦截’,在方法执行的前后添加新的处理逻辑。

分析:

  不是办法的办法就是,对原有的每个方法进行修改,添加上新的逻辑;如果需要拦截的方法比较少,选择此方法到是会节省成本。但是面对成百上千的方法怎么办?此时需要用到动态代理来实现。

场景:

  例如:对原有的系统添加日志记录、添加性能分析等等。。。

举例:

  如下,需要对Sleep对象的sleep方法进行“拦截”,并在此方法的执行前后添加新的逻辑。想知道‘睡觉前干了什么?睡觉后干了什么?’

interface Sleep {
    public void sleep();
}
public class SleepImpl implements Sleep{
    public void sleep() {
        System.out.println("我于"+new SimpleDateFormat("yyyy-MM-dd HH:mm").format(new Date())+"开始睡觉");
    }
}

  创建动态代理类,实现InvocationHandler接口即可。下面的wrap方法:传入要被代理的对象target。返回包装后的代理对象。$Proxy 打断点会看到这样的对象。针对下面的sleepProxy对象,sleepProxy.sleep()调用需要拦截的方法。实际上调用的是Plugin中的invoke方法。invoke方法中的method.invoke(target,args)是真是的调用被代理对象的sleep方法。所以直接在此语句的前后添加相应的逻辑即可满足需要。

public class Plugin implements InvocationHandler {

    private Object target;
    Plugin(Object target){
        this.target = target;
    }
    public Object invoke(Object proxy, Method method, Object[] args)
            throws Throwable {
        //睡觉前做的事
        Object result = method.invoke(target, args);
        //睡觉后做的事
        return result;
    }
    public static Object wrap(Object target){
        return Proxy.newProxyInstance(target.getClass().getClassLoader(),
                target.getClass().getInterfaces(),
                new Plugin(target));
    }

}
public class Main {

    public static void main(String[] args) {
        //要被代理的对象
        Sleep sleep = new SleepImpl();
        //代理对象
        Sleep sleepProxy = (Sleep)Plugin.wrap(sleep);
        sleepProxy.sleep();
    }
}

到此,你以为就结束了?不 ,这个仅仅是 说了在睡觉 前后做了什么事,加入还想知道,你在睡觉前后吃了什么东西?当然睡觉后吃东西有点说不通。但 意会就可以了。还有其他巴拉巴拉的需求。你该怎么做?是不是要把所有的 新的逻辑都方法 Plugin中invoke方法中去?这样不合适吧!乱 乱 乱 这样。那咱们能不能抽象出来一个拦截接口,接口中有拦截后要做什么的方法。各种需求只需要实现这个拦截接口即可!

interface Interceptor {

    public void interceptBefore()throws Exception;

    public void interceptAfter()throws Exception;
}
public class SleepBeforeAndAfter implements Interceptor {

    public void interceptBefore() throws Exception {
        System.out.println("之前。。。");
    }

    public void interceptAfter() throws Exception {
        System.out.println("之后。。。");

    }

}

然后动态代理类Plugin需要修改

/**
 * 动态代理
 *
 * @author 魏正迪
 * 2018年10月13日
 */
public class Plugin implements InvocationHandler {

    private Object target;
    private List<Interceptor> iList = new ArrayList<Interceptor>();

    Plugin(Object target , List<Interceptor> iList){
        this.target = target;
        this.iList = iList;
    }

    public Object invoke(Object proxy, Method method, Object[] args)
            throws Throwable {
        for(Interceptor i :iList){
            i.interceptBefore();
        }
        Object result = method.invoke(target, args);
        for(Interceptor i :iList){
            i.interceptAfter();
        }
        return result;
    }

    public static Object wrap(Object target,List<Interceptor> iList){
        return Proxy.newProxyInstance(target.getClass().getClassLoader(),
                target.getClass().getInterfaces(),
                new Plugin(target,iList)
                );
    }

}
public class Main {
    public static void main(String[] args) {
        Sleep sleep = new SleepImpl();
        List<Interceptor> iList = new ArrayList<Interceptor>();
        iList.add(new SleepBeforeAndAfter());
        Sleep sleepProxy = (Sleep)Plugin.wrap(sleep,iList);
        sleepProxy.sleep();
    }
}

现在想对每个对象的方法进行拦截,直接实现Interceptor接口即可!实现其中的两个方法。此时我们新加的逻辑和原有的逻辑并没有什么交集。假如我们想在interceptor中的两个方法中使用被代理对象的各种属性,此时该怎么做?首先想到是将interceptor接口的两个方法添加参数。

public class SleepBeforeAndAfter implements Interceptor {

    public void interceptBefore(Object target, Method method, Object[] args)
            throws Exception {
        System.out.println("之前。。。interceptBefore(Object target, Method method, Object[] args)");

    }

    public void interceptAfter(Object target, Method method, Object[] args)
            throws Exception {
        System.out.println("之后。。。interceptAfter(Object target, Method method, Object[] args)");
    }

}

到此,个人感觉没啥问题了【大牛如发现明显不符的请指出】。但但但但是我们奔着简单明了、面向对象的思想(其实就是mybatis源码插件设计)。我们做出进一步的精简。于是Invocation对象产生了。看到Method对象传进来了。我们是不是可以想到,我们不再 在Plugin中的invoke方法中调用method.invoke(target,args);了,而是在Intercetpor中处理完前后逻辑后进行调用。这样分工明确了。

/**
 * 拦截对象的包装
 * @author 魏正迪
 * 2018年10月13日
 */
public class Invocation {

    private Object target;

    private Object []args;

    private Method method;

    Invocation(Object target,Method method,Object[] args){
        this.target = target;
        this.args = args;
        this.method = method;

    }
    /**
     * 执行拦截对象的对应的方法
     * @return
     * @throws Exception
     */
    public Object process() throws Exception{
        return method.invoke(target, args);
    }

}

此时拦截器Interceptor应该是这样的

interface Interceptor {
    public Object intercept(Invocation invocation)throws Exception;
}
public class SleepBeforeAndAfter implements Interceptor {

    public Object intercept(Invocation invocation) throws Exception{
        System.out.println("拦截sleep方法要执行的方法之前");
        Object result = invocation.process();
        System.out.println("拦截sleep方法要执行的方法之后");
        return result;
    }

}

此时Plugin应该是这样的

public class Plugin implements InvocationHandler {

    private Object target;
    private Interceptor interceptor;

    Plugin(Object target,Interceptor interceptor){
        this.target = target;
        this.interceptor = interceptor;
    }

    public Object invoke(Object proxy, Method method, Object[] args)
            throws Throwable {
        Invocation invocation = new Invocation(target,method,args);
        return interceptor.intercept(invocation);
    }

    public static Object wrap(Object target,Interceptor interceptor){
        return Proxy.newProxyInstance(target.getClass().getClassLoader(),
                target.getClass().getInterfaces(),
                new Plugin(target,interceptor)
                );
    }

}
public class Main {

    public static void main(String[] args) {
        Sleep sleep = new SleepImpl();
        SleepBeforeAndAfter s = new SleepBeforeAndAfter();
        Sleep sleepProxy1 = (Sleep)Plugin.wrap(sleep,s);
        sleepProxy1.sleep();
        Sleep sleepProxy2 = (Sleep)Plugin.wrap(sleepProxy1, s);
        sleepProxy2.sleep();
    }

}

到此,mybatis插件开发的引言完毕!其实是使用了动态代理和责任链结合的方式。

  

原文地址:https://www.cnblogs.com/oldwei/p/9784708.html

时间: 2024-10-18 22:12:41

【Mybtais】Mybatis 插件 Plugin开发(一)动态代理步步解析的相关文章

Android插件化开发-hook动态代理

首先,我们阐述为什么android需要插件化: 1:由于业务的增长,app的方法数逐渐达到65535(有人说用于检索方法数的列表大小使用short存储的,其实我看了源码之后并没有发现相关信息,并对此说法产生了怀疑,不过最后找到的结果就是,65535这个限制可能是由于dalvik的bytecode大小限制的,具体的可以查看官方文档). 2:一个模块的变化都要整体编译一次app,维护成本太大了,用插件开发app会好很多 对于以上问题解决方案不少,著名的有h5,hybird,不过这些都没有native

mybatis(一) mapper动态代理

因为dao开发,会每次创建实体类对象,会传入id等固定查询值,存在硬编码问题,所以采用mapper动态代理(不用创建实体类对象,只需要接口,由mapper自动生成) 与之前mybatis(一)步骤一样,但是需要将mapper.xml文件作出修改: namespace:必须是接口类的全路径 (<mapper namespace="">) id:必须是接口的方法名(<select id=""/>) parameterType:必须是接口方法里面的

Mybatis入门---dao开发和mapper代理开发

在说mabatis之前,先说说单独使用jdbc编程的缺陷. jdbc编程的大概流程大家都很清楚,基本分为以下几步: 加载数据驱动 创建并获取数据库连接 创建jdbc statement对象 设置sql语句,并设置sql语句中的参数 通过statement执行sql并获取结果 对执行的结果进行解析处理 释放资源 1 public static void main(String[] args) { 2 Connection connection = null; 3 PreparedStatement

mybatis源码学习: 动态代理的应用(慢慢改)

动态代理概述 在学spring的时候知道使用动态代理实现aop,入门的列子:需要计算所有方法的调用时间.可以每个方法开始和结束都获取当前时间咋办呢.类似这样: long current=system.currenttimemillis(); 调用原来的方法 long last=system.currenttimemillis(); 如果每个方法都人工加入实在有点不爽,动态代理出场了.动态代理利用字节码技在原来对应的类的子节码进行重写,添加对应的逻辑. 主流的动态代理实现技术主流如下:JDK 和C

mybatis快速入门(八)-spring-mybatis动态代理整合

将上一节的代码改造下就好了,不过这一章会加一个basedaomapper.废话不多说直接上代码了. 创建一个BaseDaoMapper.java package cn.my.sm.mapper; /** * 将公用方法提取出来 * @author Administrator * * @param <T> */ public interface BaseDaoMapper<T> { public T findById(int id); } 为了防止和上个UserDao有冲突,我们创建

ORM简介 &amp;&amp; MyBatis和Hibernate的不同 &amp;&amp; 动态代理简单实现Mybatis基本使用

ORM简介 对象关系映射. ORM(Object Relational Mapping)框架采用元数据来描述对象一关系映射细节,元数据一般采用XML格式,并且存放在专门的对象一映射文件中. 只要提供了持久化类与表的映射关系,ORM框架在运行时就能参照映射文件的信息,把对象持久化到数据库中.当前ORM框架主要有五种:Hibernate(Nhibernate),iBATIS,mybatis,EclipseLink,JFinal. ORM是通过使用描述对象和数据库之间映射的元数据,在我们想到描述的时候

动态代理-实例解析

转自http://weixiaolu.iteye.com/blog/1477774 动态代理实例如下所示: package cn.xiaolu; import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; import java.lang.reflect.Proxy; /** * 动态代理类使用到了一个接口InvocationHandler和一个代理类Proxy ,这两个类配合使用实现了动态代理的功能.

java动态代理原理解析

DamInterface; public interface DamInterface { public void sayHello(); public int sayHelloa(); } DamImpl; public class DamImpl implements DamInterface { @Override   public void sayHello() {     System.out.println("Hello");   } @Override   public 

JAVA动态代理机制解析

1. 概述 首先,我们来思考如下两个问题: 什么是代理模式?为什么要使用代理模式? 简单总结一下,所谓的代理模式就是在原有的服务上多加一个占位,通过这个占位去控制服务的访问.通过代理模式,一方面可以控制如何访问真正的服务对象,提供额外服务:另一方面,有机会通过重写一些类来满足特定的需要. 在java的动态代理机制中,有两个重要的类或接口,一个是 InvocationHandler(Interface).另一个则是 Proxy(Class),这一个类和接口是实现动态代理所必须用到的.下面我们具体分