parse方法中的第二步bindMapperForNamespace()操作参考以下代码
private void bindMapperForNamespace() {//拿到namespaceString namespace = builderAssistant.getCurrentNamespace();if (namespace != null) {Class<?> boundType = null;try {//拿到相应的class对象boundType = Resources.classForName(namespace);} catch (ClassNotFoundException e) {//ignore, bound type is not required}if (boundType != null) {//判断configuration 是否有 如果没有 就添加到configuration中if (!configuration.hasMapper(boundType)) {configuration.addLoadedResource("namespace:" + namespace);// 添加到 MapperRegistry , 本质是一个 map , 里面也有 Configurationconfiguration.addMapper(boundType);}}}}public <T> void addMapper(Class<T> type) {if (type.isInterface()) {if (hasMapper(type)) {throw new BindingException("Type " + type + " is already known to the MapperRegistry.");}boolean loadCompleted = false;try {// !Map<Class<?>, MapperProxyFactory<?>> 存放的是接口类型 , 和对应的工厂类的关系//key是namespace那个接口的对象 value 对应的代理工厂类knownMappers.put(type, new MapperProxyFactory<>(type));// 注册了接口之后 , 根据接口 , 开始解析所有方法上的注解 , 例如 @Select >>MapperAnnotationBuilder parser = new MapperAnnotationBuilder(config, type);parser.parse();loadCompleted = true;} finally {if (!loadCompleted) {knownMappers.remove(type);}}}
二、创建会话拿到执行器(Excutor)如果没有传入执行器类型 , 默认就是SIMPLE , 否则就是传入的类型 。如果开启的缓存 , 将会把SimpleExecutor执行器封装成CachingExecutor创建会话最终就是为了得到SqlSession 。
源码实现@Testpublic void testSelect() throws IOException {SqlSession session = sqlSessionFactory.openSession();try {BlogMapper mapper = session.getMapper(BlogMapper.class);Blog blog = mapper.selectBlogById(1);System.out.println(blog);} finally {session.close();}}
public SqlSession openSession() {return openSessionFromDataSource(configuration.getDefaultExecutorType(), null, false);}private SqlSession openSessionFromDataSource(ExecutorType execType, TransactionIsolationLevel level, boolean autoCommit) {Transaction tx = null;try {final Environment environment = configuration.getEnvironment();// 获取事务工厂final TransactionFactory transactionFactory = getTransactionFactoryFromEnvironment(environment);// 创建事务tx = transactionFactory.newTransaction(environment.getDataSource(), level, autoCommit);// 根据事务工厂和默认的执行器类型 , 创建执行器final Executor executor = configuration.newExecutor(tx, execType);//将得到的执行器放入到默认的sqlSession中最后得到一个sqlSessionreturn new DefaultSqlSession(configuration, executor, autoCommit);} catch (Exception e) {closeTransaction(tx);throw ExceptionFactory.wrapException("Error opening session.Cause: " + e, e);} finally {ErrorContext.instance().reset();}}public Executor newExecutor(Transaction transaction, ExecutorType executorType) {executorType = executorType == null ? defaultExecutorType : executorType;executorType = executorType == null ? ExecutorType.SIMPLE : executorType;Executor executor;if (ExecutorType.BATCH == executorType) {executor = new BatchExecutor(this, transaction);} else if (ExecutorType.REUSE == executorType) {executor = new ReuseExecutor(this, transaction);} else {// 默认 SimpleExecutorexecutor = new SimpleExecutor(this, transaction);}// 二级缓存开关 , settings 中的 cacheEnabled 默认是 trueif (cacheEnabled) {executor = new CachingExecutor(executor);}// 植入插件的逻辑 , 至此 , 四大对象已经全部拦截完毕//缓存的执行器用到的是装饰器模式executor = (Executor) interceptorChain.pluginAll(executor);return executor;}
简单举例上面提到的执行器//传入执行器的方式 SqlSession session = sqlSessionFactory.openSession(ExecutorType.BATCH);public enum ExecutorType {//执行mapper.save()方法时 , 就相当于jdbc的stmt.execute(sql)SIMPLE,//会重用sql , 通过stmt传入多项参数值REUSE,//相当于stmt.addBatch(sql)这样的 , 批量BATCH;private ExecutorType() {}}
三、语句执行通过接口的类型 , 在knownMappers中去获取代理工厂 , 然后创建代理类 , 代理类的handler是MapperProxy调用方法 , 走到MapperProxy中invoke方法 , 去掉object中的方法 , 直接调用 。如果你在settings全局配置文件中配置了cacheEnabled为true,并且xml中配置了 标签 , 并且在sql语句上配置了useCache , 就会调用CachingExecutor对象的query方法CachingExecutor类中的query方法会先看二级缓存中有没有 , 如果有直接返回 , 如果没有 , 会去走到SimpleExecutor的query方法最后就和调用JDBC的流程一样 , 打开连接 , 查询数据 , 返回数据 。
推荐阅读
- 根据交换机的工作原理可知,交换机可以多个端口对之间的数据传输
- 路由交换技术—VLAN原理及配置
- Linux容器技术原理和使用
- 一文了解神经网络工作原理
- 搜索引擎抓取信息的原理
- SQL注入攻击的原理
- Redis详解:原理和机制
- Spring Boot启动原理解析
- C++ 一篇搞懂多态的实现原理
- 荧光灯如何选购 荧光灯工作原理