从动态代理看mybatis-spring底层实现(大晖带你手写mybatis-spring)哈哈哈哈

/ 默认分类 / 1 条评论 / 1225浏览

基于接口的jdk动态代理

准备好以上的代码就可以完成一个典型的jdk动态代理了 ** 上代码 **

//接口
public interface MyInterface {

    void doSomething();

}
//实现了接口的真实类
public class RealSomething implements MyInterface {
    @Override
    public void doSomething() {
        System.out.println("我是真实类中的做某事方法");
    }
}
//实现了invocationHandler的代理处理器对象
public class MyInvocation implements InvocationHandler {

    Object realObject;

    public MyInvocation(Object realObject)
    {
        this.realObject = realObject;
    }

//第一个参数是代理类对象,第二个是真实类的方法对象,第三个是真实类的方法的参数的对象
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("之前");
        method.invoke(realObject,args);
        System.out.println("之后");
        return null;
    }
}

现在开始测试代理

//在程序中设置了生产动态的文件,所以这个动态生产的代理类的字节码文件会被保存起来
//保存在当前项目的demo\com\sun\proxy下
public class DailiTest {
    public static void main(String[] args) {
        RealSomething realSomething = new RealSomething();
        Class<?>[] interfaces = realSomething.getClass().getInterfaces();
        InvocationHandler invocationHandler = new MyInvocation(realSomething);
        System.getProperties().put("sun.misc.ProxyGenerator.saveGeneratedFiles", "true");
        MyInterface proxyInstance = (MyInterface)Proxy.newProxyInstance(realSomething.getClass().getClassLoader(), interfaces, invocationHandler);
        proxyInstance.doSomething();
    }
}
//运行结果
之前
我是真实类中的做某事方法
之后

总结一下,我们可以发现,返回的代理类对象调用的doSomeThing()方法以及是被代理后的了,这是为啥呢?我们来看看刚刚保存的代理类(在idea中反编译后)

//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by Fernflower decompiler)
//

package com.sun.proxy;

import com.example.demo.mytest.ygzj.daili.MyInterface;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.lang.reflect.UndeclaredThrowableException;

//代理类对象集成了Proxy,所以要想得到真实类的信息就不能继承真实类了(单继承),所以需要实现接口(所以jdk动态代理需要接口)
public final class $Proxy0 extends Proxy implements MyInterface {
    private static Method m1;
    private static Method m3;
    private static Method m2;
    private static Method m0;

//这个方法其实是将Proxy.newProxyInstance(realSomething.getClass().getClassLoader(), interfaces, invocationHandler);传入的invocationhandler赋值给
//父类(Proxy)中的InvocationHandler属性,下面有Proxy类的源码
    public $Proxy0(InvocationHandler var1) throws  {
        super(var1);
    }

    public final boolean equals(Object var1) throws  {
        try {
            return (Boolean)super.h.invoke(this, m1, new Object[]{var1});
        } catch (RuntimeException | Error var3) {
            throw var3;
        } catch (Throwable var4) {
            throw new UndeclaredThrowableException(var4);
        }
    }

//代理类调用doSomething后发现已经是真实类的dosomething的增强版了,就是
//因为下面这个方法,我们发现dosomething已经改变了,其中调用了super.h.invoke(this, m3, (Object[])null);
//也就是我们前面赋值给父类的属性,也就是我们之前
//通过Proxy.newProxyInstance(realSomething.getClass().getClassLoader(), interfaces, invocationHandler);传值进来的
    public final void doSomething() throws  {
        try {
            super.h.invoke(this, m3, (Object[])null);
        } catch (RuntimeException | Error var2) {
            throw var2;
        } catch (Throwable var3) {
            throw new UndeclaredThrowableException(var3);
        }
    }

    public final String toString() throws  {
        try {
            return (String)super.h.invoke(this, m2, (Object[])null);
        } catch (RuntimeException | Error var2) {
            throw var2;
        } catch (Throwable var3) {
            throw new UndeclaredThrowableException(var3);
        }
    }

    public final int hashCode() throws  {
        try {
            return (Integer)super.h.invoke(this, m0, (Object[])null);
        } catch (RuntimeException | Error var2) {
            throw var2;
        } catch (Throwable var3) {
            throw new UndeclaredThrowableException(var3);
        }
    }

//代理类static静态块中初始化就是将传入进来的接口的字节码对象的方法对象取出来,然后复制给全局变量m3
//这样在代理类的doSomething方法中调用的invoke方法就是可以传入真实类的方法对象了
    static {
        try {
            m1 = Class.forName("java.lang.Object").getMethod("equals", Class.forName("java.lang.Object"));
            m3 = Class.forName("com.example.demo.mytest.ygzj.daili.MyInterface").getMethod("doSomething");
            m2 = Class.forName("java.lang.Object").getMethod("toString");
            m0 = Class.forName("java.lang.Object").getMethod("hashCode");
        } catch (NoSuchMethodException var2) {
            throw new NoSuchMethodError(var2.getMessage());
        } catch (ClassNotFoundException var3) {
            throw new NoClassDefFoundError(var3.getMessage());
        }
    }
}

public class Proxy implements java.io.Serializable {

    private static final long serialVersionUID = -2222568056686623797L;

    /** parameter types of a proxy class constructor */
    private static final Class<?>[] constructorParams =
        { InvocationHandler.class };

    /**
     * a cache of proxy classes
     */
    private static final WeakCache<ClassLoader, Class<?>[], Class<?>>
        proxyClassCache = new WeakCache<>(new KeyFactory(), new ProxyClassFactory());

    /**
     * the invocation handler for this proxy instance.
     * @serial
     */
    protected InvocationHandler h;

    /**
     * Prohibits instantiation.
     */
    private Proxy() {
    }

    /**
     * Constructs a new {@code Proxy} instance from a subclass
     * (typically, a dynamic proxy class) with the specified value
     * for its invocation handler.
     *
     * @param  h the invocation handler for this proxy instance
     *
     * @throws NullPointerException if the given invocation handler, {@code h},
     *         is {@code null}.
     */
    protected Proxy(InvocationHandler h) {
        Objects.requireNonNull(h);
        this.h = h;
    }

以上就是jdk动态代理的原理了,cglib原理记得关注博客哦 下面我们来看一下mybatis-spring是如何实现仅仅凭借一个mapper接口加上@autowire就可以执行sql

mybatis中的简单演示(现实是有区别的,比如和spring结合,所以sqlSession注册,等等)

这里我写了一个简单的demo,模拟mybatis的运行

//为了稍微演示的真实一些,写一个User对象
public class User {

    String name;

    public User(String name) {
        this.name = name;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }
}

//现在需要写一个UserMapper作为一个myabatis的接口
public interface UserMapper {

    @Insert("insert into user ....")
    void insertUser(User user);

}
//接下来需要准备代理必须的代理处理器类,这个类的对象需要传入Proxy.newProxyInstance(realSomething.getClass().getClassLoader(), interfaces, invocationHandler)中然后代理类执行insertUser的时候就会去执行我们传入的这个InvocationHandler的实现类对象invoke方法,并且传入进来的参数(method)就是第二个参数interface的class得到的方法method对象
public class MyBatisInvocation implements InvocationHandler {

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        //得到注解
        Insert annotation = method.getAnnotation(Insert.class);
        //拿到注解的那条sql语句
        String[] value = annotation.value();
        String sql = value[0];
        System.out.println("待执行的sql是:"+sql);
        System.out.println("执行sql");
        System.out.println("成功保存:"+((User)args[0]).getName());
        return null;
    }
}
//模拟一个sqlSession
public class MySqlSession {
    public static UserMapper getMapper(Class clazz) throws NoSuchMethodException {
        //这样得到了"真是类"(其实没有真是类,mybatis中的jdk动态代理只有一个接口,这个接口没有实现类)
        //所以sqlSession的getMapper()方法总是会传入一个Mapper接口的class对象,这样就可以按照下面类似的方法取出"真是类"的方法对象了;
        MyBatisInvocation myBatisInvocation = new MyBatisInvocation();
        Class[] classArr = new Class[]{clazz};
        UserMapper proxyInstance = (UserMapper)Proxy.newProxyInstance(clazz.getClassLoader(),classArr,myBatisInvocation);
        return proxyInstance;
    }
}
//测试一下
public class MybatisTest {
    public static void main(String[] args) throws NoSuchMethodException {
        UserMapper mapper = MySqlSession.getMapper(UserMapper.class);
        mapper.insertUser(new User("程序员大晖"));
    }
}
运行结果:
待执行的sql是:insert into user ....
执行sql
成功保存:程序员大晖
  1. 666666