4.<cache>标签解析剖析

本文详细解析了MyBatis中的二级缓存机制,包括其构建过程、与Mapper的关系及配置方式。通过源码分析,展示了从SqlSessionFactory创建到MappedStatement组装的全过程。

1.首先进入sqlSessionFactoryBuilder.build()方法

public SqlSessionFactory build(InputStream inputStream, String environment, Properties properties) {
        SqlSessionFactory var5;
        try {
        // 核心配置Configuration的XML解析
            XMLConfigBuilder parser = new XMLConfigBuilder(inputStream, environment, properties);
            var5 = this.build(parser.parse());
        } catch (Exception var14) {
            throw ExceptionFactory.wrapException("Error building SqlSession.", var14);
        } finally {
            ErrorContext.instance().reset();

            try {
                inputStream.close();
            } catch (IOException var13) {
            }

        }

2.进入XMLConfigBuilder的parse()方法,再进入parseConfiguration(XNode root)方法,这里利用构建者模式的依次解析核心XML中的各个标签构建Configuration对象

private void parseConfiguration(XNode root) {
        try {
            this.propertiesElement(root.evalNode("properties"));
            Properties settings = this.settingsAsProperties(root.evalNode("settings"));
            this.loadCustomVfs(settings);
            this.typeAliasesElement(root.evalNode("typeAliases"));
            this.pluginElement(root.evalNode("plugins"));
            this.objectFactoryElement(root.evalNode("objectFactory"));
            this.objectWrapperFactoryElement(root.evalNode("objectWrapperFactory"));
            this.reflectorFactoryElement(root.evalNode("reflectorFactory"));
            this.settingsElement(settings);
            this.environmentsElement(root.evalNode("environments"));
            this.databaseIdProviderElement(root.evalNode("databaseIdProvider"));
            this.typeHandlerElement(root.evalNode("typeHandlers"));
            this.mapperElement(root.evalNode("mappers"));
        } catch (Exception var3) {
            throw new BuilderException("Error parsing SQL Mapper Configuration. Cause: " + var3, var3);
        }
    }

3.进入mappers解析方法并找到cache标签的解析方法进入即XMLMapperBuilder的cacheElement(XNode context)方法,这里获取cache标签的各个配置属性,并调用builderAssistant.useNewCache()进行构建。

private void cacheElement(XNode context) throws Exception {
        if (context != null) {
            String type = context.getStringAttribute("type", "PERPETUAL");
            Class<? extends Cache> typeClass = this.typeAliasRegistry.resolveAlias(type);
            String eviction = context.getStringAttribute("eviction", "LRU");
            Class<? extends Cache> evictionClass = this.typeAliasRegistry.resolveAlias(eviction);
            Long flushInterval = context.getLongAttribute("flushInterval");
            Integer size = context.getIntAttribute("size");
            boolean readWrite = !context.getBooleanAttribute("readOnly", false);
            boolean blocking = context.getBooleanAttribute("blocking", false);
            Properties props = context.getChildrenAsProperties();
            this.builderAssistant.useNewCache(typeClass, evictionClass, flushInterval, size, readWrite, blocking, props);
        }

4.进入useNewCache()方法发现,创建一个Cache接口的实现类即二级缓存对象,并分别存入核心配置类Configuration对对象,和MapperBuilderAssistant对象中,由于MapperBuilderAssistant属于XMLMapperBuilder,即XMLMapperBuilder中存在一个二级缓存(又源码得知一个mapper标签一个XMLMapperBuilder对象)。

public Cache useNewCache(Class<? extends Cache> typeClass, Class<? extends Cache> evictionClass, Long flushInterval, Integer size, boolean readWrite, boolean blocking, Properties props) {
        Cache cache = (new CacheBuilder(this.currentNamespace)).implementation((Class)this.valueOrDefault(typeClass, PerpetualCache.class)).addDecorator((Class)this.valueOrDefault(evictionClass, LruCache.class)).clearInterval(flushInterval).size(size).readWrite(readWrite).blocking(blocking).properties(props).build();
        this.configuration.addCache(cache);
        this.currentCache = cache;
        return cache;
    }

5.继续回到XMLConfigBuilder解析mapperXML的mapperElement(XNode parent)方法,然后进入解析mapperXML的具体方法即XMLMapperBuilder的parse()方法。

XMLConfigBuilder.class

private void mapperElement(XNode parent) throws Exception {
        if (parent != null) {
            Iterator var2 = parent.getChildren().iterator();

            while(true) {
                while(var2.hasNext()) {
                    XNode child = (XNode)var2.next();
                    String resource;
                    if ("package".equals(child.getName())) {
                        resource = child.getStringAttribute("name");
                        this.configuration.addMappers(resource);
                    } else {
                        resource = child.getStringAttribute("resource");
                        String url = child.getStringAttribute("url");
                        String mapperClass = child.getStringAttribute("class");
                        XMLMapperBuilder mapperParser;
                        InputStream inputStream;
                        if (resource != null && url == null && mapperClass == null) {
                            ErrorContext.instance().resource(resource);
                            inputStream = Resources.getResourceAsStream(resource);
                            mapperParser = new XMLMapperBuilder(inputStream, this.configuration, resource, this.configuration.getSqlFragments());
                            //解析mapperXML的具体方法
                            mapperParser.parse();
                        } else if (resource == null && url != null && mapperClass == null) {
                            ErrorContext.instance().resource(url);
                            inputStream = Resources.getUrlAsStream(url);
                            mapperParser = new XMLMapperBuilder(inputStream, this.configuration, url, this.configuration.getSqlFragments());
                            mapperParser.parse();
                        } else {
                            if (resource != null || url != null || mapperClass == null) {
                                throw new BuilderException("A mapper element may only specify a url, resource or class, but not more than one.");
                            }

                            Class<?> mapperInterface = Resources.classForName(mapperClass);
                            this.configuration.addMapper(mapperInterface);
                        }
                    }
                }

                return;
            }
        }
    }
XMLMapperBuilder.class
public void parse() {
        if (!this.configuration.isResourceLoaded(this.resource)) {
            this.configurationElement(this.parser.evalNode("/mapper"));
            this.configuration.addLoadedResource(this.resource);
            this.bindMapperForNamespace();
        }

        this.parsePendingResultMaps();
        this.parsePendingCacheRefs();
        this.parsePendingStatements();
    }

6.找到解析select,insert,update,delete标签的方法,即组装MappedStatement的方法MapperBuilderAssistant的addMappedStatement()方法

public MappedStatement addMappedStatement( String id, ...) { if (unresolvedCacheRef) { throw new IncompleteElementException("Cache-ref not yet resolved"); }id = applyCurrentNamespace(id, false); boolean isSelect = sqlCommandType == SqlCommandType.SELECT; 
//创建MappedStatement对象
 MappedStatement.Builder statementBuilder = new MappedStatement.Builder(configuration, id, sqlSource, sqlCommandType) .flushCacheRequired(valueOrDefault(flushCache, !isSelect)) .useCache(valueOrDefault(useCache, isSelect)) 
 // 在这里将之前放到MapperBuilderAssistant中的二级缓存Cache封装到MappedStatement
 .cache(currentCache);
 

总结:
同一个mapperXML中各个增删改查标签公用一个二级缓存,所以说二级缓存是Mapper级别的。
陶某*____*

<?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <name>Timo后台管理系统</name> <description>快速搭建后台管理的系统</description> <groupId>com.linln</groupId> <artifactId>timo</artifactId> <version>2.0.3</version> <packaging>pom</packaging> <modules> <module>admin</module> <module>common</module> <module>component</module> <module>devtools</module> <module>modules</module> </modules> <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>2.6.7</version> <relativePath/> </parent> <properties> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding> <java.version>1.8</java.version> <!--shiro权限管理框架版本:component.shiro--> <shiro.version>1.9.0</shiro.version> <lombok.version>1.18.24</lombok.version> <ehcache.version>2.10.9.2</ehcache.version> <mysql.connector.version>5.1.46</mysql.connector.version> <jsoup.version>1.14.3</jsoup.version> <!--shiro模板视图权限标签扩展:component.thymeleaf--> <thymeleaf-shiro.version>2.1.0</thymeleaf-shiro.version> <!--excel工具框架版本:component.excel--> <poi.version>4.1.2</poi.version> <!--jwt处理框架版本:component.jwt--> <jwt.version>3.19.1</jwt.version> <!--swagger-knife4j接口文档页面:component.jwt--> <knife4j.version>3.0.3</knife4j.version> <google.findbugs.version>3.0.1</google.findbugs.version> <skipTests>true</skipTests> </properties> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-validation</artifactId> </dependency> <!--spring data jpa持久层框架--> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-jpa</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-configuration-processor</artifactId> <optional>true</optional> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-cache</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-devtools</artifactId> <scope>runtime</scope> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <scope>test</scope> </dependency> <!--mysql连接驱动--> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>${mysql.connector.version}</version> <scope>runtime</scope> </dependency> <!--lombok语法糖--> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <version>${lombok.version}</version> <optional>true</optional> </dependency> <!--ehcache缓存框架--> <dependency> <groupId>net.sf.ehcache</groupId> <artifactId>ehcache</artifactId> <version>${ehcache.version}</version> </dependency> <!--html解析工具,处理xss攻击--> <dependency> <groupId>org.jsoup</groupId> <artifactId>jsoup</artifactId> <version>${jsoup.version}</version> </dependency> <!--knife4j 接口文档--> <dependency> <groupId>com.github.xiaoymin</groupId> <artifactId>knife4j-spring-boot-starter</artifactId> <version>${knife4j.version}</version> </dependency> <!--解决编译时javax.annotation.meta.When不存在问题--> <dependency> <groupId>com.google.code.findbugs</groupId> <artifactId>annotations</artifactId> <version>${google.findbugs.version}</version> </dependency> </dependencies> <!--多环境配置--> <profiles> <!--开发环境--> <profile> <id>dev</id> <activation> <!--默认配置--> <activeByDefault>true</activeByDefault> </activation> <properties> <profile.name>dev</profile.name> </properties> </profile> <!--生产环境--> <profile> <id>prod</id> <properties> <profile.name>prod</profile.name> </properties> </profile> </profiles> </project>
06-28
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

躺平程序猿

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值