Official account: Java Xiaokaxiu, website: Javaxks.com

Source: author: fathers in jung, my.oschina.net/zudajun/blo…

Declaring an interface that has no implementation class but requires the instantiation of interface objects and the ability to call interface methods to return business data is a ridiculous claim from the older generation of IT revolutionaries.

One day, I asked my friend: Declare an interface interface in Mybatis, without writing any implementation class, Mybatis can return the interface instance and call the interface method to return the database data, do you know why not? My friend was very surprised: yes, I was also puzzled. Our leader told us to write in this mode, and my colleague also felt very strange. Although I don’t know exactly how it was implemented, I think it must be… But it’s not true, is it… (Again, leaving out the vague).

This inspired me to write this article.

Dynamic proxy functionality: The target method is enhanced through interceptor method callbacks.

The implication is to enhance the target target method. The above statement is true, but don’t take it as truth, but dynamic agents have the power to beat the flow of hegemony, not even the target of science fiction mode.

Note: This article assumes that the reader understands the principle of dynamic proxy. If you do not understand the meaning of target, you are advised to understand dynamic proxy first.

1. Customized JDK dynamic proxy 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; }}Copy the code

Define another interface, usermapper.java.

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

Next, let’s look at how to use the dynamic proxy to whip out the flow to instantiate the interface and call the interface methods to return data.

Create a custom 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 {
				
				return method.invoke(this, args);
			} catch (Throwable t) {
			}
		}
		
		return new User((Integer) args[0], "zhangsan", 18);
	}
}
Copy the code

In the code above, target is referred to as this when the method in Object.java is executed. Target has become a puppet, symbol, placeholder. At the time of the flagellate intercept, there is 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());
}
Copy the code

The output:

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

This is the basic implementation principle of Mybatis automatic Mapper.

Some readers may wonder: How do you write code like a beginner? There is no structure and no aesthetic.

It must be said that an experienced master who can write programs like beginners must be the master of the master. In this way, beginners can feel friendly, comfortable and in line with their own Style. They or they can feel that the code written by Daniel is no more than this, and they even write better than those written by Daniel. From then on, they are full of confidence and enthusiasm, believing that the gap between them and Daniel is only three minutes.

2. Mybatis automatic Mapper source analysis

Start by writing 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 using dynamic proxy to whip out the flow.

3. Can methods in Mapper overLoad? (important)

Something like the following:

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

Answer: No.

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

Note: learning, is the first study of the source code, understand the principle. When writing a blog post, explain the principle first, then read the source code. The order is reversed, so I hope the reader doesn’t get confused and think I’m too powerful to know.