动态代理 invoke 方法
- 问题
- mapperProxy.findByCondition(1); 是怎么完成的增删改查操作?
- 当通过 JDK 代理方式生成代理对象后,可以通过代理对象执行代理方法
public class MybatisTest {@Testpublic void test2() throws IOException {...User user = mapperProxy.findByCondition(1);...}
}
- 因为 mapperProxy 是个代理对象,所以会执行 invoke() 方法。对于 Object 的方法会直接跳过代理,否则会使用 PlainMethodInvoker 执行代理调用逻辑,同时从这里还能看出 MapperProxy 是实现了 InvocationHandler 接口,所以可以
public class MapperProxy<T> implements InvocationHandler, Serializable {private static final int ALLOWED_MODES = MethodHandles.Lookup.PRIVATE | MethodHandles.Lookup.PROTECTED| MethodHandles.Lookup.PACKAGE | MethodHandles.Lookup.PUBLIC;private final SqlSession sqlSession;private final Class<T> mapperInterface;private final Map<Method, MapperMethodInvoker> methodCache;...@Overridepublic Object invoke(Object proxy, Method method, Object[] args) throws Throwable {try {if (Object.class.equals(method.getDeclaringClass())) {return method.invoke(this, args);} else {return cachedInvoker(method).invoke(proxy, method, args, sqlSession);}} catch (Throwable t) {throw ExceptionUtil.unwrapThrowable(t);}}private MapperMethodInvoker cachedInvoker(Method method) throws Throwable {try {return MapUtil.computeIfAbsent(methodCache, method, m -> {if (m.isDefault()) {...} else {return new PlainMethodInvoker(new MapperMethod(mapperInterface, method, sqlSession.getConfiguration()));}});} catch (RuntimeException re) {Throwable cause = re.getCause();throw cause == null ? re : cause;}}
}
- 从上面代码 3 中看到,在执行 PlainMethodInvoker 的 invoke() 前,首先初始化构造 MapperMethod,特别要注意创建 SqlCommand 对象,这个属性等等会用到
public class MapperMethod {private final SqlCommand command;private final MethodSignature method;public MapperMethod(Class<?> mapperInterface, Method method, Configuration config) {this.command = new SqlCommand(config, mapperInterface, method);this.method = new MethodSignature(config, mapperInterface, method);}...
}
- SqlCommand 创建会获取当前调用的方法,以及对应的接口名称,通过接口名称 + 方法名称的方式,就能拿到 MappedStatement,从而得知 SqlCommand 的执行类型
public static class SqlCommand {private final String name;private final SqlCommandType type;...public SqlCommand(Configuration configuration, Class<?> mapperInterface, Method method) {final String methodName = method.getName();final Class<?> declaringClass = method.getDeclaringClass();MappedStatement ms = resolveMappedStatement(mapperInterface, methodName, declaringClass,configuration);if (ms == null) {if (method.getAnnotation(Flush.class) != null) {name = null;type = SqlCommandType.FLUSH;} else {throw new BindingException("Invalid bound statement (not found): "+ mapperInterface.getName() + "." + methodName);}} else {name = ms.getId();type = ms.getSqlCommandType();if (type == SqlCommandType.UNKNOWN) {throw new BindingException("Unknown execution method for: " + name);}}}
}
- 完成初始化 MapperMethod 之后,就开始真正执行 PlainMethodInvoker 的 invoke() 方法,实际也是交由 MapperMethod 执行 execute() 方法
public class MapperProxy<T> implements InvocationHandler, Serializable {...private static class PlainMethodInvoker implements MapperMethodInvoker {private final MapperMethod mapperMethod;public PlainMethodInvoker(MapperMethod mapperMethod) {super();this.mapperMethod = mapperMethod;}@Overridepublic Object invoke(Object proxy, Method method, Object[] args, SqlSession sqlSession) throws Throwable {return mapperMethod.execute(sqlSession, args);}}
}
- 在 MapperMethod 的 execute() 方法中,它会根据 SqlCommand 的方法类型,选择调用 SqlSession 的不同方法,对于 INSERT、UPDATE、DELETE 类型,其实最后都是执行 update 操作,SELECT 类型会根据方法返回的类型,执行不同的处理方法。根据我们的实验,最终是会执行查询单条 sqlSession.selectOne() 方法,并且会把结果返回
public class MapperMethod {private final SqlCommand command;private final MethodSignature method;...public Object execute(SqlSession sqlSession, Object[] args) {Object result;switch (command.getType()) {case INSERT: {Object param = method.convertArgsToSqlCommandParam(args);result = rowCountResult(sqlSession.insert(command.getName(), param));break;}case UPDATE: {Object param = method.convertArgsToSqlCommandParam(args);result = rowCountResult(sqlSession.update(command.getName(), param));break;}case DELETE: {Object param = method.convertArgsToSqlCommandParam(args);result = rowCountResult(sqlSession.delete(command.getName(), param));break;}case SELECT:if (method.returnsVoid() && method.hasResultHandler()) {executeWithResultHandler(sqlSession, args);result = null;} else if (method.returnsMany()) {result = executeForMany(sqlSession, args);} else if (method.returnsMap()) {result = executeForMap(sqlSession, args);} else if (method.returnsCursor()) {result = executeForCursor(sqlSession, args);} else {Object param = method.convertArgsToSqlCommandParam(args);result = sqlSession.selectOne(command.getName(), param);if (method.returnsOptional()&& (result == null || !method.getReturnType().equals(result.getClass()))) {result = Optional.ofNullable(result);}}break;case FLUSH:result = sqlSession.flushStatements();break;default:throw new BindingException("Unknown execution method for: " + command.getName());}if (result == null && method.getReturnType().isPrimitive() && !method.returnsVoid()) {throw new BindingException("Mapper method '" + command.getName()+ " attempted to return null from a method with a primitive return type (" + method.getReturnType() + ").");}return result;}
}
- 总结