MyBatis动态代理
我们知道MyBatis通过动态代理的方式,实现了只通过Mapper接口而无接口的实现类的方式操作数据库。
熟悉源码的同学应该知道如下代码(MapperProxy.invoke()):
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
try {
//如果目标方法继承自Object,则直接调用目标方法
if (Object.class.equals(method.getDeclaringClass())) {
return method.invoke(this, args);
} else if (isDefaultMethod(method)) {
return invokeDefaultMethod(proxy, method, args);
}
} catch (Throwable t) {
throw ExceptionUtil.unwrapThrowable(t);
}
//从缓存中取出MapperMethod对象,如果缓存中没有,则创建新的MapperMethod对象并添加到缓存中
final MapperMethod mapperMethod = cachedMapperMethod(method);
return mapperMethod.execute(sqlSession, args);
}
这种方式类似于截获了真正的代理对象,而去执行其他的逻辑,通过MapperMethod实现数据库的访问操作!
代理对象的获取
MyBatis中的代理对象的获取是通过如下代码实现的:
public T newInstance(SqlSession sqlSession) {
//创建MapperProxy对象,每次调用都会创建新的MapperProxy对象
final MapperProxy<T> mapperProxy = new MapperProxy<T>(sqlSession, mapperInterface, methodCache);
return newInstance(mapperProxy);
}
/**
* 创建实现了mapperInterface接口的代理对象
* @param mapperProxy
* @return
*/
@SuppressWarnings("unchecked")
protected T newInstance(MapperProxy<T> mapperProxy) {
T t = (T) Proxy.newProxyInstance(mapperInterface.getClassLoader(), new Class[]{mapperInterface}, mapperProxy);
return (T) Proxy.newProxyInstance(mapperInterface.getClassLoader(), new Class[]{mapperInterface}, mapperProxy);
}
返回的记过是这样的:
也就是说,无论调用的是哪一个Mapper接口,返回的代理对象都是MapperProxy类型的。
自己实现MyBatis动态代理模拟上述过程
public class MapperRegistry {
private final Map<Class<?>, MapperProxyFactory<?>> knownMappers = new HashMap<>();
public <T> T getMapper(Class<T> type) {
//模拟初始化knownMappers集合
knownMappers.put(type, new MapperProxyFactory<T>(type));
final MapperProxyFactory<T> mapperProxyFactory = (MapperProxyFactory<T>) knownMappers.get(type);
return (T) mapperProxyFactory.newInstance();
}
}
public class MapperProxyFactory<T> {
private final Class<T> mapperInterface;
public MapperProxyFactory(Class<T> mapperInterface) {
this.mapperInterface = mapperInterface;
}
public T newInstance() {
final MapperProxy<T> mapperProxy = new MapperProxy<>(mapperInterface);
return newInstance(mapperProxy);
}
protected T newInstance(MapperProxy<T> mapperProxy) {
return (T) Proxy.newProxyInstance(mapperInterface.getClassLoader(), new Class[]{mapperInterface}, mapperProxy);
}
}
public class MapperProxy<T> implements InvocationHandler, Serializable {
private static final long serialVersionUID = -6424540398559729838L;
//真正的业务对象,也就是RealSubject对象
private Class<T> mapperInterface;
/**
* 构造函数
*/
public MapperProxy(Class<T> target) {
this.mapperInterface = target;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
final MapperMethod mapperMethod = new MapperMethod();
return mapperMethod.execute(args);
}
}
public class MapperMethod {
public Object execute(Object[] args) {
return new User("123", "234");
}
}
public class DefaultSqlSession {
private Configuration configuration = new Configuration();
public <T> T getMapper(Class<T> type) {
return configuration.getMapper(type);
}
}
public class Configuration {
final MapperRegistry mapperRegistry = new MapperRegistry();
public <T> T getMapper(Class<T> type) {
return mapperRegistry.getMapper(type);
}
}
public class UserService {
public static void main(String[] args) {
final DefaultSqlSession sqlSession = new DefaultSqlSession();
UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
User user = userMapper.selectUser();
System.out.println(user.toString());
}
}
public interface UserMapper {
User selectUser();
}
自己写的代码中抽取了MyBatis下binding包的主要内容实现的一个简单的动态获取实例,但是获取代理对象的时候,返回的记过是这样的:
可以看出,上述的过程的代理返回的类型并不是MapperProxy,虽然可以获取到正确的执行结果,但是返回的代理的对象类型是出现了转换异常的。
问题描述
1、为什么sqlSession.getMapper()返回的代理对象结果都是MapperProxy类型的,设计的初衷?
2、为什么我按照上述相同的代码过程自己实现的动态代理,为什么会出现转换异常?
还请各位大哥哥大姐姐帮忙研究一下,谢谢!