Mybatis动态代理应用

# 开始

mybatis底层封装使用JDK动态代理。在探讨mybatis动态代理之前先看看JDK动态代理。

  1. 被代理接口
  2. 被代理实现类
  3. 实现InvocationHandler

实例

interface UserInterface {
    void sayHello();
}

public class UserRemote implements UserInterface {

    @Override
    public void sayHello() {
        System.out.println("hello world...");
    }
}

public class SimpleInvocationHandler implements InvocationHandler {

    private Object target;

    public SimpleInvocationHandler(Object target) {
        this.target = target;
    }

    /**
     * 被代理类执行方法后 实际上该方法在invoke内部执行,在执行前后可以添加处理,达到增强作用
     */
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("代理开始" + method.getName());
        method.invoke(target);
        System.out.println("代理结束");
        return target;
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31

测试代理执行器的效果,

public class Main {
    public static void main(String[] args) {
        //1.创建被代理对象
        UserRemote user = new UserRemote();
        //2.创建被代理方法执行器(依赖被代理对象)
        SimpleInvocationHandler handler = new SimpleInvocationHandler(user);
        //动态生成代理类的class文件保存在文件系统中
        System.getProperties().put("sun.misc.ProxyGenerator.saveGeneratedFiles", "true");
        //3.动态代理生成被代理对象 (通过断点或打印 proxy为$Proxy-数字 证明该对象是JVM动态在内存中创建)
        final UserInterface proxy = (UserInterface) Proxy.newProxyInstance(Main.class.getClassLoader(), user.getClass().getInterfaces(), handler);
        proxy.sayHello();
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13

生成代理类class文件

final class $Proxy0 extends Proxy implements UserInterface {
    private static Method m1;
    private static Method m3;
    private static Method m2;
    private static Method m0;

    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);
        }
    }

    public final void sayHello() throws {
        try {
            //proxy.sayHello()方法真正执行者是上面实现的InvocationHandler
            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 {
        try {
            m1 = Class.forName("java.lang.Object").getMethod("equals", Class.forName("java.lang.Object"));
            m3 = Class.forName("com.xxx.UserInterface").getMethod("sayHello");
            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());
        }
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64

总结一下,JDK动态代理生成的代理类继承了Proxy类,实现了被代理的接口。所以JDK动态代理功能必须依赖于接口。执行流程如下

1.proxy#sayHello-> 2.h#invoke(this)-> 3.method#invoke-> 4.target#sayHello
proxy:动态代理生成的对象 自定义实现的handler 动态代理调用的对象 target:目标对象即被代理对象

# mybatis动态代理

言归正传,回到开头抛出的问题,mybatis只有接口,没有实现类,也可以实现动态代理?

# 在没有数据库持久层框架操作数据库的方式

public class JDBCUtils {
    //省略部分代码
    public static void query(String sql) {
        //1.装载驱动到DriverManager 
        Class.forName("com.mysql.jdbc.Driver");

        //2.创建连接
        String jdbc_url = "jdbc:mysql://localhost:3306/ssm?userUnicode=true&Encoding=utf-8";
        String jdbc_username = "root";
        String jdbc_password = "123456";
        Connection connection = DriverManager.getConnection(jdbc_url, jdbc_username, jdbc_password);

        //4.创建查询对象
        Statement statement = connection.createStatement();

        //5.执行操作
        ResultSet resultSet = statement.executeQuery(sql);
    }
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18

# 假设接口没有实现类,实验JDK动态代理能否正常代理

public interface PayService {
    /**
     * pay
     */
    String pay();
}

public class NoImplementInvocationHandler implements InvocationHandler {
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        //连接jdbc
        System.out.println("数据库连接执行前置操作");
        //execute sql
        return "success result";
    }

    public static void main(String[] args) {
        final PayService proxy = (PayService) Proxy.newProxyInstance(PayService.class.getClassLoader(),
                new Class[]{PayService.class},
                new NoImplementInvocationHandler());
        String result = proxy.pay();
        System.out.println(result);
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24

打印结果:数据库连接执行前置操作 success result

所以总结有无实现类动态代理的区别:

  1. 传参class数组-需要使用PayService.class

基于无具体实现接口,大胆猜测,mapper没有实现类,所有的JDBC具体操作都是在mybatis源码实现的InvocationHandler中。

查看mybatis源码包

package org.apache.ibatis.binding;

import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

import org.apache.ibatis.binding.MapperProxy.MapperMethodInvoker;

/**
 * 此Factory的作用是创建代理对象 1. classLoader 2. 被代理接口class 3. InvocationHandler
 */
public class MapperProxyFactory<T> {

    //重点1:被代理的接口 
    private final Class<T> mapperInterface;
    private final Map<Method, MapperMethodInvoker> methodCache = new ConcurrentHashMap<>();

    public MapperProxyFactory(Class<T> mapperInterface) {
        this.mapperInterface = mapperInterface;
    }

    @SuppressWarnings("unchecked")
    protected T newInstance(MapperProxy<T> mapperProxy) {
        return (T) Proxy.newProxyInstance(mapperInterface.getClassLoader(), new Class[]{mapperInterface}, mapperProxy);
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25

被代理mapperInterface是如何被赋值

  1. 启动扫描配置的xml文件的位置,解析namespace标签,得到所有的接口mapper
  2. 通过Class.forName(mapper)得到class对象
  3. 将得到的class对象传给MapperProxyFactory得到代理对象
  //org.apache.ibatis.builder.xml.XMLMapperBuilder.java
private void bindMapperForNamespace(){
        String namespace=builderAssistant.getCurrentNamespace();
        if(namespace!=null){
        Class<?> boundType=null;
        try{
        boundType=Resources.classForName(namespace);
        }catch(ClassNotFoundException e){
        // ignore, bound type is not required
        }
        if(boundType!=null&&!configuration.hasMapper(boundType)){
        // Spring may not know the real resource name so we set a flag
        // to prevent loading again this resource from the mapper interface
        // look at MapperAnnotationBuilder#loadXmlResource
        configuration.addLoadedResource("namespace:"+namespace);
        configuration.addMapper(boundType);
        }
        }
        }
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19

# 至于抽象类能否动态代理

/*
 * Verify that the Class object actually represents an
 * interface.
 */
 if(!interfaceClass.isInterface()){
         throw new IllegalArgumentException(
         interfaceClass.getName()+" is not an interface");
         }

public native boolean isInterface();
1
2
3
4
5
6
7
8
9
10

结论:不能。调用native方法判读newInstance参数是否是interface