By Dae-jun Zu

Source: my.oschina.net/zudajun/blog/666223Copy the code

Dynamic proxy functionality: Enhances the target method with an interceptor method callback.

The implication is to enhance the target method. This is true, but don’t take it as truth. However, dynamic proxies and the hegemony of the lash, even the target of science fiction model.

Note: By default, this article assumes that readers understand the principles of dynamic agents. If you do not understand the meaning of target, it is difficult to understand this article. It is recommended to understand dynamic agents first.

1. Custom JDK dynamic proxy cast stream to achieve automatic Mapper

Start by defining 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
}
Copy the code

Define another interface, userMapper.java.

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

Let’s look at how to instantiate the interface and call the interface methods to return data using the dynamic proxy’s flag-stream.

Customize an 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> 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 { // Methods such as hashCode(), toString(), and equals() point target to the current object. This return method.invoke(this, args); } catch (Throwable t) {}} return new User((Integer) args[0], "zhangsan", 18); }}Copy the code

In the above code, target is referred to this when executing methods in object.java. Target has become a puppet, symbol, or placeholder. The target is no longer available for flag-flow intercepts.

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());
}
Copy the code

The output:

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

This is the underlying implementation principle of Mybatis automatic Mapper.

The reader may ask: How do you write code like a beginner? There is no structure and no beauty.

It must be stated that, as an experienced master, can write the program as beginners write, it must be the master of the master. This can make beginners feel kind, comfortable, in line with their Style, let them or them, feel that the code written by Daniel is no more than so, even write better than these Daniel, from now on confident, enthusiastic, think that the gap between Daniel and only three minutes.

2. Mybatis automatic Mapper source analysis

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(); }}Copy the code

A Mapper looks like this:

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

Org. Apache. Ibatis. Binding. MapperProxy. Part of Java 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); } / /...Copy the code

Org. Apache. Ibatis. Binding. MapperProxyFactory. Part of Java 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);
  }
Copy the code

This is Mybatis cast flow using dynamic proxies.

3. Can I overLoad the methods in Mapper? (important)

Something like this:

public User getUserById(Integer id);
public User getUserById(Integer id, String name);
Copy the code

Answer: No.

Reason: Mybatis uses package+Mapper+method full name as key to find unique SQL to execute in XML when casting stream. Similar: key = X.Y.U serMapper getUserById, so, when overloaded methods will lead to conflict. For Mapper interfaces, Mybatis disables method overLoad.

Note: when learning, is the first study of the source code, understand the principle. When writing a blog post, explain the principles first and then read the source code. In reverse order, I hope that readers do not doubt that I am strong enough to predict.