MyBatis underlying implementation principle: the use of dynamic proxies

Reprinted: https: //mp.weixin.qq.com/s/_6nyhaWX15mh3mkj8Lb0Zw

Mybatis declare an interface in the interface, not to write any implementation class, will be able to return Mybatis interface instance, and calls the interface method returns the database data, you know why not?

 

Dynamic proxy function: callback interceptor method, the target target enhancement methods.

The implication is to enhance target target method.

Above this sentence is true, but do not think it is the truth, everyone knows, there is a dynamic proxy vote whip drying up of hegemony, even target target are not sci-fi mode.

Note: This article is the default view, the reader is to understand the principle of dynamic proxies, if you do not understand the meaning of the target, it is difficult to read this article, it is recommended to understand the dynamic proxy.

1. JDK custom dynamic administered agents of the automatic shut whip mapper Mapper

First define a pojo.

public class User {
    private Integer id;
    private String name;
    private int age;

    public User(Integer id, String name, int age) {
        this.id = id;
        this.name = name;
        this.age = age;
    }
    // getter setter
}

And then define an interface UserMapper.java.

public interface UserMapper {
    public User getUserById(Integer id);    
}

Next we look at how to use the whip to vote cutout dynamic agency, the implementation instance of interface and call interface methods return data.

Customize a InvocationHandler.

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

public class MapperProxy implements InvocationHandler {

    @SuppressWarnings("unchecked")
    public <T> newInstance(Class<T> clz) {
        return (T) Proxy.newProxyInstance(clz.getClassLoader(), new Class[] { clz }, this);
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        if (Object.class.equals(method.getDeclaringClass())) {
            try {
                // 诸如hashCode()、toString()、equals()等方法,将target指向当前对象this
                return method.invoke(this, args);
            } catch (Throwable t) {
            }
        }
        // 投鞭断流
        return new User((Integer) args[0], "zhangsan", 18);
    }
}

Code above target, when performing the method in Object.java, target is pointing this, target has become a puppet, a symbol, a placeholder. When drying up investment and whip-like interception, it has no target.

Write a test code:

public static void main(String[] args) {
    MapperProxy proxy = new MapperProxy();

    UserMapper mapper = proxy.newInstance(UserMapper.class);
    User user = mapper.getUserById(1001);

    System.out.println("ID:" + user.getId());
    System.out.println("Name:" + user.getName());
    System.out.println("Age:" + user.getAge());

    System.out.println(mapper.toString());
}

output:

ID:1001
Name:zhangsan
Age:18
x.y.MapperProxy@6bc7c054

This is the underlying Mybatis automatic mapper Mapper implementation principle.

Readers may ask: how do you write the code as written as a beginner? No structure, and the lack of beauty.

Must be declared as an experienced seasoned expert, able to write a program like beginners to write the same, that must be the cream of the crop. This allows beginners to feel warm, comfortable, in line with its own Style, or they make them, feel Daniel wrote the code, but also so that he even wrote better than those written by Daniel, from confidence, enthusiasm, that the gap between large cattle, leaving only three minutes.

2. Mybatis automatic analysis of the source code mapper Mapper

First write a test class:

public static void main(String[] args) {
        SqlSession sqlSession = MybatisSqlSessionFactory.openSession();
        try {
            StudentMapper studentMapper = sqlSession.getMapper(StudentMapper.class);
            List<Student> students = studentMapper.findAllStudents();
            for (Student student : students) {
                System.out.println(student);
            }
        } finally {
            sqlSession.close();
        }
    }

Mapper long like this:

public interface StudentMapper {
    List<Student> findAllStudents();
    Student findStudentById(Integer id);
    void insertStudent(Student student);
}

org.apache.ibatis.binding.MapperProxy.java part of the source code.

public class MapperProxy<T> implements InvocationHandler, Serializable {

  private static final long serialVersionUID = -6424540398559729838L;
  private final SqlSession sqlSession;
  private final Class<T> mapperInterface;
  private final Map<Method, MapperMethod> methodCache;

  public MapperProxy(SqlSession sqlSession, Class<T> mapperInterface, Map<Method, MapperMethod> methodCache) {
    this.sqlSession = sqlSession;
    this.mapperInterface = mapperInterface;
    this.methodCache = methodCache;
  }

  @Override
  public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
    if (Object.class.equals(method.getDeclaringClass())) {
      try {
        return method.invoke(this, args);
      } catch (Throwable t) {
        throw ExceptionUtil.unwrapThrowable(t);
      }
    }
    // 投鞭断流
    final MapperMethod mapperMethod = cachedMapperMethod(method);
    return mapperMethod.execute(sqlSession, args);
  }
  // ...

org.apache.ibatis.binding.MapperProxyFactory.java part of the source code.

public class MapperProxyFactory<T> {

  private final Class<T> mapperInterface;

  @SuppressWarnings("unchecked")
  protected T newInstance(MapperProxy<T> mapperProxy) {
    return (T) Proxy.newProxyInstance(mapperInterface.getClassLoader(), new Class[] { mapperInterface }, mapperProxy);
  }

This is Mybatis use of dynamic proxy to vote whip drying up.

3. The method within the interface Mapper can reload (overLoad) do? (important)

Similar to the following:

public User getUserById(Integer id);
public User getUserById(Integer id, String name);

Answer: can not.

Cause: When you cast whip drying up, Mybatis use package + Mapper + method fully qualified name as a key, go inside to find the only sql xml performed.

Similar: key = xyUserMapper.getUserById, then, would lead to conflicts when overloaded methods. For Mapper interfaces, Mybatis prohibited method overloading (overLoad).

 

Guess you like

Origin www.cnblogs.com/renjiaqi/p/11854925.html