How Dynamic Proxies Power MyBatis Auto‑Mapper: A Deep Dive into “投鞭断流”
This article explains how Java’s dynamic proxy mechanism, dubbed “投鞭断流”, can be used to implement MyBatis’s automatic mapper functionality, providing step‑by‑step code examples for custom InvocationHandler, proxy creation, and discusses why method overloading is prohibited in mapper interfaces.
Dynamic proxies work by intercepting method calls and enhancing the target method. Beyond simple enhancement, the "投鞭断流" approach can even bypass the target entirely, creating a sci‑fi‑like mode.
1. Custom JDK Dynamic Proxy for Automatic 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
}Then define the mapper interface:
public interface UserMapper {
User getUserById(Integer id);
}Create an InvocationHandler that implements the "投鞭断流" logic:
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 like hashCode(), toString(), equals()
return method.invoke(this, args);
} catch (Throwable t) {
// ignore
}
}
// 投鞭断流: directly return a User instance
return new User((Integer) args[0], "zhangsan", 18);
}
}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@6bc7c054This demonstrates the underlying principle of MyBatis’s automatic mapper implementation.
2. MyBatis Automatic Mapper Source Analysis
Example 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 interface example:
public interface StudentMapper {
List<Student> findAllStudents();
Student findStudentById(Integer id);
void insertStudent(Student student);
}Key parts of org.apache.ibatis.binding.MapperProxy:
public class MapperProxy<T> implements InvocationHandler, Serializable {
private final SqlSession sqlSession;
private final Class<T> mapperInterface;
private final Map<Method, MapperMethod> 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);
}
// ...
}Key parts of org.apache.ibatis.binding.MapperProxyFactory:
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);
}
}These snippets illustrate how MyBatis leverages dynamic proxies with the "投鞭断流" technique.
3. Can Mapper Interface Methods Be Overloaded?
Example of attempted overload:
public User getUserById(Integer id);
public User getUserById(Integer id, String name);Answer: No. MyBatis uses the fully qualified name package+Mapper+method as a key to locate a unique SQL statement. Overloaded methods would cause key collisions, so MyBatis forbids method overloading in mapper interfaces.
Signed-in readers can open the original source through BestHub's protected redirect.
This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactand we will review it promptly.
Programmer DD
A tinkering programmer and author of "Spring Cloud Microservices in Action"
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.
