当前位置: 首页 > news >正文

做网站要用什么编程语言自贡网站推广

做网站要用什么编程语言,自贡网站推广,合肥建设干部学校网站,wordpress 微商网站Mybatis映射接口的动态代理实现原理 在上一节中#xff0c;我们介绍了MyBatis的核心配置文件加载流程#xff0c;Mybatis核心配置文件加载流程详解 在文中#xff0c;我们介绍了MyBatis在加载配置文件的过程中会针对每个接口类都生成一个相应的MapperProxyFactory动态代理工…Mybatis映射接口的动态代理实现原理 在上一节中我们介绍了MyBatis的核心配置文件加载流程Mybatis核心配置文件加载流程详解 在文中我们介绍了MyBatis在加载配置文件的过程中会针对每个接口类都生成一个相应的MapperProxyFactory动态代理工厂类。 在MapperRegistry类中有一个叫做knownMappers的map缓存其键为映射接口的Class对象值为MapperProxyFactory对象其有一个mapperInterface属性用来保存需要创建代理对象的接口类。 在MyBatis中我们通过调用sqlSession.getMapper方法可以获取映射接口的动态代理对象然后调用该映射接口定义的方法执行数据库操作时就会去映射配置文件中找到该方法对应的SQL语句进行执行。 那么MyBatis是如何实现上述流程的呢接下来我们就从源码中来一探究竟。 下面列出了测试主方法 public class MybatisDemo {private static UserMapper userMapper;public static void main(String[] args) throws IOException {InputStream resourceAsStream Resources.getResourceAsStream(sqlMapConfig.xml);SqlSessionFactory sqlSessionFactory new SqlSessionFactoryBuilder().build(resourceAsStream);SqlSession sqlSession sqlSessionFactory.openSession();userMapper sqlSession.getMapper(UserMapper.class);User u1 userMapper.selectUserById(1);System.out.println(u1);} } 因为动态代理的生成步骤是在sqlSession.getMapper方法中因此我们先进入DefaultSqlSession类下的到getMapper方法的源码中来看一下 public T T getMapper(ClassT type) {return configuration.getMapper(type, this); }这里会调用Configuration类下的getMapper方法type即为映射接口类。然后我们进入到Configuration类下的getMapper方法 public T T getMapper(ClassT type, SqlSession sqlSession) {return mapperRegistry.getMapper(type, sqlSession); }这里调用了本文初所提及的MapperRegistry类下的getMapper方法再进入 public T T getMapper(ClassT type, SqlSession sqlSession) {final MapperProxyFactoryT mapperProxyFactory (MapperProxyFactoryT) 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);} }这里我们就看到了本文初介绍的MyBatis在MapperRegistry类中会维护一个knownMappers的Map缓存用来存储在解析核心配置文件的过程中为每个映射接口生成的代理工厂类对象。 因此这里就是根据被调用的映射接口直接从该缓存中获取相应的MapperProxyFactory动态代理工厂类对象如果获取不到则直接报错提示。 在获取到mapperProxyFactory后调用newInstance方法来创建一个mapperProxy动态代理对象。接下来我们进入到MapperProxyFactory类中观察一下它的实现逻辑以及newInstance方法 public class MapperProxyFactoryT {private final ClassT mapperInterface;private final MapMethod, MapperMethod methodCache new ConcurrentHashMap();public MapperProxyFactory(ClassT mapperInterface) {this.mapperInterface mapperInterface;}public ClassT getMapperInterface() {return mapperInterface;}public MapMethod, MapperMethod getMethodCache() {return methodCache;}SuppressWarnings(unchecked)protected T newInstance(MapperProxyT mapperProxy) {return (T) Proxy.newProxyInstance(mapperInterface.getClassLoader(), new Class[] { mapperInterface }, mapperProxy);}public T newInstance(SqlSession sqlSession) {final MapperProxyT mapperProxy new MapperProxy(sqlSession, mapperInterface, methodCache);return newInstance(mapperProxy);}} 在MapperProxyFactory中包含了两个属性 mapperInterface需要创建代理对象的映射接口methodCache是一个map缓存其键为映射接口的方法对象值为这个方法对应的MapperMethodInvoker。实际上SQL的执行最终会由MapperMethodInvoker方法来完成后面会详细说明。 再观察MapperProxyFactory中的两个重载的newInstance方法可以看到最终重载方法会通过执行Proxy.newProxyInstance来创建映射接口的动态代理对象。而基于JDK动态代理的原理我们知道代理对象方法的执行最终是在其实现的InvocationHandler接口的invoke方法中被执行的。而观察上面的代码不难发现MapperProxy类必定是InvocationHandler接口的实现类 果然MapeprProxy类实现了InvocationHandler接口并在创建MapperProxy时MapperProxyFactory会将其持有的methodCache传递给MapperProxy因此methodCache的实际读写是由MapperProxy来完成的。 下面来看一下MapperProxy实现的invoke方法 Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {try {if (Object.class.equals(method.getDeclaringClass())) {return method.invoke(this, args);} else if (method.isDefault()) {return invokeDefaultMethod(proxy, method, args);}} catch (Throwable t) {throw ExceptionUtil.unwrapThrowable(t);}// 从methodCache中根据方法对象获取MapperMethodInvoker来执行sql// 如果获取不到则创建一个MapperMethodInvoker并添加到methodCache中再执行Sqlfinal MapperMethod mapperMethod cachedMapperMethod(method);return mapperMethod.execute(sqlSession, args); }private MapperMethod cachedMapperMethod(Method method) {return methodCache.computeIfAbsent(method, k - new MapperMethod(mapperInterface, method, sqlSession.getConfiguration())); }通过上面的代码我们可以知道之前提及的methodCache属性的作用其实就是缓存了每个方法相应的MapperMethod对象而MapperMethod对象是做什么用的呢接下来我们继续进入到execute方法 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; }喔这里我们看到几个case标签不就是对应到我们执行SQL的几种类别的操作吗看来代理对象方法的执行最终会进入到MapperMethod的execute方法里并根据方法对应SQL的类别执行相应的处理逻辑。那么是如何根据映射接口的方法来得到SQL的执行类别呢 很明显就是要根据映射接口的方法来获取到映射配置文件中相应的SQL标签而这种关联就要利用到MappedStatement对象了。在前文MyBatis的核心配置文件加载过程中我们详细分析了在MyBatis加载的过程中会对映射配置文件中的每一个select、insert、update、delete标签根据其namespaceid值生成一个MappedStatement对象存储在Configuration中。因此要实现上面的关联只需要根据映射接口类名方法名从Configuration中获取到相应的MappedStatement对象即可。 那么MyBatis的实际实现是不是正如我们上面的分析所料呢答案就在MapperMethod的构造方法中: public MapperMethod(Class? mapperInterface, Method method, Configuration config) {this.command new SqlCommand(config, mapperInterface, method);this.method new MethodSignature(config, mapperInterface, method); }在该构造方法中会根据传入的映射接口的Class对象映射接口被调用方法以及配置类Configuration来创建SqlCommand和MethodSignature对象。 SqlCommand主要是保存和映射接口被调用方法所关联的MappedStatement的信息MethodSignature主要是存储映射接口被调用方法的参数信息和返回值信息。 来看一下SqlCommand的构造方法 public SqlCommand(Configuration configuration, Class? mapperInterface, Method method) {// 获取映射接口被调用方法的方法名final String methodName method.getName();// 获取被调用方法的接口的Class对象final Class? declaringClass method.getDeclaringClass();// 获取和映射接口被调用方法关联的MappedStatement对象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 {// 将MappedStatement的id值赋值给SqlCommand的name字段name ms.getId();// 将MappedStatement的Sql命令类别赋值给SqlCommand的type字段// 比如SELECT、INSERT、UPDATE、DELETEtype ms.getSqlCommandType();if (type SqlCommandType.UNKNOWN) {throw new BindingException(Unknown execution method for: name);}} }构造函数中主要做了这些事情 先获取和被调用方法关联的MappedStatement对象然后将MappedStatement的id字段赋值给SqlCommand的name字段最后将MappedStatement的sqlCommandType字段赋值给SqlCommand的type字段。 这样一来SqlCommand就具备了和被调用方法关联的MappedStatement的信息。那么如何获取和被调用方法关联的MappedStatement对象呢继续看resolveMappedStatement() 的实现如下所示 private MappedStatement resolveMappedStatement(Class? mapperInterface, String methodName,Class? declaringClass, Configuration configuration) {// 根据接口全限定类名.方法名拼接出MappedStatement的idString statementId mapperInterface.getName() . methodName;if (configuration.hasStatement(statementId)) {// 根据statementId获取configuration中保存的MappedStatement对象return configuration.getMappedStatement(statementId);} else if (mapperInterface.equals(declaringClass)) {return null;}for (Class? superInterface : mapperInterface.getInterfaces()) {if (declaringClass.isAssignableFrom(superInterface)) {// 递归调用 MappedStatement ms resolveMappedStatement(superInterface, methodName,declaringClass, configuration);if (ms ! null) {return ms;}}}return null;} }终于在这里我们就可以利用源码来佐证我们上面的分析了实际上MyBatis就是根据被调用映射接口的类名方法名拼接成statementId并利用该statementId从Configuration对象中获取到相应的MappedStatement对象。这也就是为什么在MyBatis中老生常谈的问题映射配置文件中的select等标签id要跟映射接口的方法名保持一致且映射配置文件的mappers namespacexxxnamespace属性要写成映射接口的全限定类名。 上面代码中有一处递归调用逻辑可能比较难以理解。这里简单介绍下实际上这么做的目的是 在MyBatis的3.4.2及以前的版本只会根据映射接口的全限定名 “.” 方法名和声明被调用方法的接口的全限定名 “.” 方法名去Configuration的mappedStatements缓存中获取MappedStatement那么按照这样的逻辑如果是某些继承映射接口的子接口类是无法获取到MappedStatement的因此在MyBatis的3.4.3及之后的版本中采用了resolveMappedStatement() 方法中的逻辑以支持继承了映射接口的接口对应的SqlCommand也能和映射接口对应的MappedStatement相关联。 对于SqlCommand的分析到此为止。 至此回到上面定位到MapperMethod类中的execute方法中我们就知道了MyBatis对于映射接口的动态代理实现原理以及执行流程。最终在execute方法中会根据被调用方法关联的SQL执行类别将方法传入的参数进行转换最终在SqlSession中调用相应的方法来执行SQL语句。 总结一下本文的主要内容如下 动态代理实现原理 在MyBatis启动加载核心配置文件时会对每个映射接口生成一个MapperProxyFactory对象并保存在MapperRegistry对象的knownMappers缓存中便于后续使用。当调用SqlSession.getMapper方法来获取映射接口的代理对象时会从MapperRegistry对象的knownMappers缓存中获取该映射接口对应的动态代理工厂类该工厂类会通过Proxy.newProxyInstance方法来创建一个代理对象。同时会创建一个MapperProxy对象在invoke方法中执行真正的被调用方法执行逻辑。在invoke方法中会获取到该调用方法对应的MapperMethod对象并在其execute方法中结合SqlSession来执行具体的增删改查逻辑。 MyBatis中的动态代理是对接口的代理 在MyBatis的JDK动态代理中是不存在被代理对象的是对接口的代理。MapperProxy实现了InvocationHandler接口因此MapperProxy在MyBatis的JDK动态代理中扮演调用处理器的角色即调用映射接口的方法时实际上是调用的MapperProxy实现的invoke() 方法又因为不存在被代理对象所以在MapperProxy的invoke() 方法中并没有去调用被代理对象的方法而是会基于映射接口和被调用方法的方法对象生成MapperMethod并执行MapperMethod的execute() 方法即调用映射接口的方法的请求会发送到MapperMethod。 可以理解为映射接口的方法由MapperMethod代理。 最后用一张图归纳一下MyBatis中的动态代理执行流程如下所示
http://www.zqtcl.cn/news/986079/

相关文章:

  • 义乌网站制作是什么交互式网站
  • 淘宝客api调用到网站世界足球排名前100名
  • 网站建设合作方案wordpress 付费主题 高级功能编辑器
  • 用cms做网站的具体步骤北京市网站备案查询
  • 中国设计师网站WordPress添加live2d
  • 我是做网站的云溪网络建站宝盒
  • 为什么没人做团购网站子域名的网站放到哪里去
  • 成都做网站设企业建一个网站需要多少钱
  • 淮南建设网站菏泽兼职网站建设
  • 品牌做网站公司做网站需要一些什么东西
  • 网页制作软件三剑客网站优化排名的方法
  • 购物网站开发背景及目的做百度推广网站咱们做
  • 漳州最专业的网站建设公司网站建设工作方案
  • 江西省建设厅网站官网网站备案期间可以用二级域名访问网站吗
  • 三丰云做网站步骤php网站建设视频教程
  • 赤峰网站开发公司wordpress电子商务主题 中文
  • 网站建设运营工作业绩怎样查看网站备案号
  • 江苏常州网站建设公司外贸网站建设盲区
  • 响应式网站设计教程wordpress 医院主题
  • 手机上怎么上传网站吗舟山做网站
  • 程序员做个网站要多少钱呢网站开发设计技术路线
  • 企业网站优化与推广哪个网站seo做的最好
  • 学做网站 软件合肥市建设投资有限公司
  • 网站开发优势用php制作一个个人信息网站
  • wordpress百度推送代码兰州网站关键字优化
  • 有了域名怎么建设网站在线crm免费将夜2
  • 网站建设 技术方案模板长沙手机网站公司
  • 游戏网站建设免费版百度只更新快照不收录网站
  • html小清新类型网站网站建设中应注意哪些问题
  • 网站开发技术和seo的联系精品课程网站建设 公司