本篇内容主要讲解“直接调用userMapper接口的方法是什么”,感兴趣的朋友不妨来看看。本文介绍的方法操作简单快捷,实用性强。下面就让小编来带大家学习“直接调用userMapper接口的方法是什么”吧!
成都创新互联主要从事网站制作、成都网站制作、网页设计、企业做网站、公司建网站等业务。立足成都服务元谋,十余年网站建设经验,价格优惠、服务专业,欢迎来电咨询建站服务:18980820575
老规矩,先上案例代码,这样大家可以更加熟悉是如何使用的,看过Mybatis系列的小伙伴,对这段代码差不多都可以背下来了。
哈哈~,有点夸张吗?不夸张的,就这行代码。
public class MybatisApplication {         public static final String URL = "jdbc:MySQL://localhost:3306/mblog";         public static final String USER = "root";         public static final String PASSWORD = "123456";              public static void main(String[] args) {             String resource = "mybatis-config.xml";             InputStream inputStream = null;             SqlSession sqlSession = null;             try {                 inputStream = Resources.getResourceAsStream(resource);                 SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);                 sqlSession = sqlSessionFactory.openSession();                 //今天主要这行代码                 UserMapper userMapper = sqlSession.getMapper(UserMapper.class);                 System.out.println(userMapper.selectById(1));                  } catch (Exception e) {                 e.printStackTrace();             } finally {                 try {                     inputStream.close();                 } catch (IOException e) {                     e.printStackTrace();                 }                 sqlSession.close();             }         }看源码有什么用?
通过源码的学习,我们可以收获Mybatis的核心思想和框架设计,另外还可以收获设计模式的应用。
前两篇文章我们已经Mybatis配置文件解析到获取SqlSession,下面我们来分析从SqlSession到userMapper:
UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
前面那篇文章已经知道了这里的sqlSession使用的是默认实现类DefaultSqlSession。所以我们直接进入DefaultSqlSession的getMapper方法。
//DefaultSqlSession中 private final Configuration configuration; //type=UserMapper.class @Override publicT getMapper(Class type) { return configuration.getMapper(type, this); } 
这里有三个问题:

问题1:getMapper返回的是个什么对象?
上面可以看出,getMapper方法调用的是Configuration中的getMapper方法。然后我们进入Configuration中
//Configuration中 protected final MapperRegistry mapperRegistry = new MapperRegistry(this); ////type=UserMapper.class publicT getMapper(Class type, SqlSession sqlSession) { return mapperRegistry.getMapper(type, sqlSession); } 
这里也没做什么,继续调用MapperRegistry中的getMapper:
//MapperRegistry中 public class MapperRegistry {   //主要是存放配置信息   private final Configuration config;   //MapperProxyFactory 的映射   private final Map, MapperProxyFactory>> knownMappers = new HashMap<>();    //获得 Mapper Proxy 对象   //type=UserMapper.class,session为当前会话   public  T getMapper(Class type, SqlSession sqlSession) {     //这里是get,那就有add或者put     final MapperProxyFactory mapperProxyFactory = (MapperProxyFactory) 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);     }   }      //解析配置文件的时候就会调用这个方法,   //type=UserMapper.class   public  void addMapper(Class type) {     // 判断 type 必须是接口,也就是说 Mapper 接口。     if (type.isInterface()) {         //已经添加过,则抛出 BindingException 异常         if (hasMapper(type)) {             throw new BindingException("Type " + type + " is already known to the MapperRegistry.");         }         boolean loadCompleted = false;         try {             //添加到 knownMappers 中             knownMappers.put(type, new MapperProxyFactory<>(type));             //创建 MapperAnnotationBuilder 对象,解析 Mapper 的注解配置             MapperAnnotationBuilder parser = new MapperAnnotationBuilder(config, type);             parser.parse();             //标记加载完成             loadCompleted = true;         } finally {             //若加载未完成,从 knownMappers 中移除             if (!loadCompleted) {                 knownMappers.remove(type);             }         }     } } }       MapperProxyFactory对象里保存了mapper接口的class对象,就是一个普通的类,没有什么逻辑。
在MapperProxyFactory类中使用了两种设计模式:
- 鸿蒙官方战略合作共建——HarmonyOS技术社区 
- 单例模式methodCache(注册式单例模式)。 
- 工厂模式getMapper()。 
继续看MapperProxyFactory中的newInstance方法。
public class MapperProxyFactory{ private final Class mapperInterface; private final Map methodCache = new ConcurrentHashMap<>(); public MapperProxyFactory(Class mapperInterface) { this.mapperInterface = mapperInterface; } public T newInstance(SqlSession sqlSession) { //创建MapperProxy对象 final MapperProxy mapperProxy = new MapperProxy<>(sqlSession, mapperInterface, methodCache); return newInstance(mapperProxy); } //最终以JDK动态代理创建对象并返回 protected T newInstance(MapperProxy mapperProxy) { return (T) Proxy.newProxyInstance(mapperInterface.getClassLoader(), new Class[] { mapperInterface }, mapperProxy); } } 
从代码中可以看出,依然是稳稳的基于 JDK Proxy 实现的,而 InvocationHandler 参数是 MapperProxy 对象。
//UserMapper 的类加载器 //接口是UserMapper //h是mapperProxy对象 public static Object newProxyInstance(ClassLoader loader,                                           Class>[] interfaces,                                        InvocationHandler h){ }问题2:为什么就可以调用他的方法?
上面调用newInstance方法时候创建了MapperProxy对象,并且是当做newProxyInstance的第三个参数,所以MapperProxy类肯定实现了InvocationHandler。
进入MapperProxy类中:
//果然实现了InvocationHandler接口 public class MapperProxyimplements InvocationHandler, Serializable { private static final long serialVersionUID = -6424540398559729838L; private final SqlSession sqlSession; private final Class mapperInterface; private final Map methodCache; public MapperProxy(SqlSession sqlSession, Class mapperInterface, Map methodCache) { this.sqlSession = sqlSession; this.mapperInterface = mapperInterface; this.methodCache = methodCache; } //调用userMapper.selectById()实质上是调用这个invoke方法 @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { try { //如果是Object的方法toString()、hashCode()等方法 if (Object.class.equals(method.getDeclaringClass())) { return method.invoke(this, args); } else if (method.isDefault()) { //JDK8以后的接口默认实现方法 return invokeDefaultMethod(proxy, method, args); } } catch (Throwable t) { throw ExceptionUtil.unwrapThrowable(t); } //创建MapperMethod对象 final MapperMethod mapperMethod = cachedMapperMethod(method); //下一篇再聊 return mapperMethod.execute(sqlSession, args); } } 
也就是说,getMapper方法返回的是一个JDK动态代理对象(类型是$Proxy+数字)。这个代理对象会继承Proxy类,实现被代理的接口UserMpper,里面持有了一个MapperProxy类型的触发管理类。
当我们调用UserMpper的方法时候,实质上调用的是MapperProxy的invoke方法。
userMapper=$Proxy6@2355。

为什么要在MapperRegistry中保存一个工厂类?
原来他是用来创建并返回代理类的。这里是代理模式的一个非常经典的应用。

MapperProxy如何实现对接口的代理?
JDK动态代理
我们知道,JDK动态代理有三个核心角色:
- 被代理类(即就是实现类) 
- 接口 
- 实现了InvocationHanndler的触发管理类,用来生成代理对象。 
被代理类必须实现接口,因为要通过接口获取方法,而且代理类也要实现这个接口。

而Mybatis中并没有Mapper接口的实现类,怎么被代理呢?它忽略了实现类,直接对Mapper接口进行代理。
MyBatis动态代理:
在Mybatis中,JDK动态代理为什么不需要实现类呢?

这里我们的目的其实就是根据一个可以执行的方法,直接找到Mapper.xml中statement ID ,方便调用。
最后返回的userMapper就是MapperProxyFactory的创建的代理对象,然后这个对象中包含了MapperProxy对象,
问题3:到底是怎么根据Mapper.java找到Mapper.xml的?
最后我们调用userMapper.selectUserById(),本质上调用的是MapperProxy的invoke()方法。
请看下面这张图:

如果根据(接口+方法名找到Statement ID ),这个逻辑在InvocationHandler子类(MapperProxy类)中就可以完成了,其实也就没有必要在用实现类了。
到此,相信大家对“直接调用userMapper接口的方法是什么”有了更深的了解,不妨来实际操作一番吧!这里是创新互联网站,更多相关内容可以进入相关频道进行查询,关注我们,继续学习!
本文题目:直接调用userMapper接口的方法是什么
URL网址:http://www.scyingshan.cn/article/pcposc.html

 建站
建站
 咨询
咨询 售后
售后
 建站咨询
建站咨询 
 