写点什么

工厂模式(四)泛型工厂之 MyBatis Mapper 代理

用户头像
LSJ
关注
发布于: 2020 年 05 月 31 日

在MyBatis框架中,为了能操作数据库,执行sql语句,需要定义Mapper接口文件和对应的xml文件,比如UserMapper.java和UserMapper.xml。然后就可以在Service层直接注入UserMapper实例。其实UserMapper对象是代理对象。代理对象要实现的功能就是执行xml文件中定义的sql语句。



其实这里也涉及到动态代理模式



如果我们要自己创建的话大概是这样的:



UserMapper userMapper =sqlSession.getMapper(UserMapper.class);



通过SqlSession.getMapper()方法获取到的就是UserMapper接口的一个代理对象,这里便用到了动态代理,内部实现大概是这样的(仍然使用了最无意义的代码):



class User {}
interface UserMapper {
List<User> queryAllUser();
}
class UserMapperProxyHandler implements InvocationHandler {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
if (method.getName().equals("queryAllUser")) {
return Arrays.asList(new User(),new User());
}
return null;
}
}
public class Demo {
public static void main(String[] args) {
UserMapper userMapper = (UserMapper) Proxy.newProxyInstance(UserMapper.class.getClassLoader(),new Class[]{UserMapper.class},new UserMapperProxyHandler());
System.out.println(userMapper.queryAllUser());
}
}



以上就是动态代理的基本使用方法,其实这个例子少了一个要代理的真实对象,但是此处不需要真实对象,因为我们代理的是Mapper接口,在MyBatis中是不需要定义具体的Mapper接口实现的。



SqlSession.getMapper()内部实现就犹如在示例代码main()方法所展示的那样:创建代理对象。只不过在MyBatis中并不会直接创建代理对象,而是通过工厂去创建,这个工厂便是我要讲的 泛型工厂



public class MapperProxyFactory<T> {
private final Class<T> mapperInterface;
public MapperProxyFactory(Class<T> mapperInterface) {
this.mapperInterface = mapperInterface;
}
@SuppressWarnings("unchecked")
protected T newInstance(MapperProxy<T> mapperProxy) {
return (T) Proxy.newProxyInstance(mapperInterface.getClassLoader(), new Class[] { mapperInterface }, mapperProxy);
}
public T newInstance(SqlSession sqlSession) {
return new MapperProxy<>(sqlSession, mapperInterface, methodCache);
}
} //为了节省空间省略部分代码 不过该类代码并不多,有兴趣可以查看源码



该工厂直接作为类来使用,不像前面讲的那样:一个工厂接口和多个工厂实现类。因为这个地方只需一个工厂类就可以搞定。该类可以接收任何不同类型的对象,因为使用了类型参数。



这个泛型工厂创建的对象就是我们Mapper接口的动态代理对象,因为有了类型参数,所以它可以创建任意类型的对象。



具体的代理是 MapperProxy。 所有Mapper接口的代理对象都是该类的实例。调用任何的Mapper接口中定义的方法都会被代理到MapperProxy类的invoke()方法,然后在invoke()中执行具体的SQL语句。



同时MyBatis提供了工厂注册中心(具体类:MapperRegistry),MyBatis在初始化过程中会读取映射配置文件以及Mapper接口中的注解信息,然后去填充注册工厂,该注册工厂维护了一个映射关系表:



Map<Class<?>, MapperProxyFactory<?>> knownMappers = new HashMap<>();



就是把工厂和Class的信息填充到此Map中,这样我们在使用时就可以通过注册中心的getMapper()方法去获取具体类型的代理对象:



public <T> T getMapper(Class<T> type, SqlSession sqlSession) {
final MapperProxyFactory<T> mapperProxyFactory = (MapperProxyFactory<T>) knownMappers.get(type);
if (mapperProxyFactory == null) {
throw new BindingException("Type " + type + " is not known to the MapperRegistry.");
}
try {
return mapperProxyFactory.newInstance(sqlSession);
} catch (Exception e) {
throw new BindingException("Error getting mapper instance. Cause: " + e, e);
}
}



刚开始提到的sqlSession.getMapper(UserMapper.class)内部调用的就是这里的getMapper方法。



工厂注册中心免得我们手动去创建维护此关系表。



以上是泛型工厂和工厂注册中心搭配使用的例子



发布于: 2020 年 05 月 31 日阅读数: 86
用户头像

LSJ

关注

微笑面对每一天 2018.11.11 加入

一个具有N年编程功力却早已拥有2N年工作经验的boy

评论

发布
暂无评论
工厂模式(四)泛型工厂之MyBatis Mapper代理