commit:微服务工程目录

This commit is contained in:
Jerry
2021-09-28 16:28:10 +08:00
parent 13ba3386dc
commit a6ae0d1a79
1012 changed files with 0 additions and 0 deletions

View File

@@ -0,0 +1,234 @@
<?xml version="1.0" encoding="UTF-8"?>
<module org.jetbrains.idea.maven.project.MavenProjectsManager.isMavenModule="true" type="JAVA_MODULE" version="4">
<component name="FacetManager">
<facet type="Spring" name="Spring">
<configuration />
</facet>
<facet type="web" name="Web">
<configuration>
<webroots />
</configuration>
</facet>
</component>
<component name="NewModuleRootManager" LANGUAGE_LEVEL="JDK_1_8">
<output url="file://$MODULE_DIR$/target/classes" />
<output-test url="file://$MODULE_DIR$/target/test-classes" />
<content url="file://$MODULE_DIR$">
<sourceFolder url="file://$MODULE_DIR$/src/main/java" isTestSource="false" />
<sourceFolder url="file://$MODULE_DIR$/src/main/resources" type="java-resource" />
<sourceFolder url="file://$MODULE_DIR$/target/generated-sources/annotations" isTestSource="false" generated="true" />
<excludeFolder url="file://$MODULE_DIR$/target" />
</content>
<orderEntry type="inheritedJdk" />
<orderEntry type="sourceFolder" forTests="false" />
<orderEntry type="module" module-name="common-core" />
<orderEntry type="library" name="Maven: org.springframework.boot:spring-boot-starter-web:2.3.10.RELEASE" level="project" />
<orderEntry type="library" name="Maven: org.springframework.boot:spring-boot-starter-json:2.3.10.RELEASE" level="project" />
<orderEntry type="library" name="Maven: com.fasterxml.jackson.datatype:jackson-datatype-jdk8:2.11.4" level="project" />
<orderEntry type="library" name="Maven: com.fasterxml.jackson.module:jackson-module-parameter-names:2.11.4" level="project" />
<orderEntry type="library" name="Maven: org.springframework.boot:spring-boot-starter-tomcat:2.3.10.RELEASE" level="project" />
<orderEntry type="library" name="Maven: org.apache.tomcat.embed:tomcat-embed-core:9.0.45" level="project" />
<orderEntry type="library" name="Maven: org.glassfish:jakarta.el:3.0.3" level="project" />
<orderEntry type="library" name="Maven: org.apache.tomcat.embed:tomcat-embed-websocket:9.0.45" level="project" />
<orderEntry type="library" name="Maven: org.springframework:spring-webmvc:5.2.14.RELEASE" level="project" />
<orderEntry type="library" name="Maven: com.google.guava:guava:29.0-jre" level="project" />
<orderEntry type="library" name="Maven: com.google.guava:failureaccess:1.0.1" level="project" />
<orderEntry type="library" name="Maven: com.google.guava:listenablefuture:9999.0-empty-to-avoid-conflict-with-guava" level="project" />
<orderEntry type="library" name="Maven: com.google.code.findbugs:jsr305:3.0.2" level="project" />
<orderEntry type="library" name="Maven: org.checkerframework:checker-qual:2.11.1" level="project" />
<orderEntry type="library" name="Maven: com.google.errorprone:error_prone_annotations:2.3.4" level="project" />
<orderEntry type="library" name="Maven: com.google.j2objc:j2objc-annotations:1.3" level="project" />
<orderEntry type="library" name="Maven: org.apache.commons:commons-lang3:3.10" level="project" />
<orderEntry type="library" name="Maven: commons-codec:commons-codec:1.14" level="project" />
<orderEntry type="library" name="Maven: commons-io:commons-io:2.6" level="project" />
<orderEntry type="library" name="Maven: commons-fileupload:commons-fileupload:1.3.3" level="project" />
<orderEntry type="library" name="Maven: joda-time:joda-time:2.9.9" level="project" />
<orderEntry type="library" name="Maven: org.apache.commons:commons-collections4:4.4" level="project" />
<orderEntry type="library" name="Maven: org.apache.commons:commons-csv:1.8" level="project" />
<orderEntry type="library" name="Maven: cn.hutool:hutool-all:5.6.4" level="project" />
<orderEntry type="library" name="Maven: io.jsonwebtoken:jjwt:0.9.1" level="project" />
<orderEntry type="library" name="Maven: com.alibaba:fastjson:1.2.76" level="project" />
<orderEntry type="library" name="Maven: com.github.ben-manes.caffeine:caffeine:2.8.8" level="project" />
<orderEntry type="library" name="Maven: cn.jimmyshi:bean-query:1.1.5" level="project" />
<orderEntry type="library" name="Maven: org.hamcrest:hamcrest-all:1.3" level="project" />
<orderEntry type="library" name="Maven: commons-beanutils:commons-beanutils:1.9.3" level="project" />
<orderEntry type="library" name="Maven: commons-collections:commons-collections:3.2.2" level="project" />
<orderEntry type="library" name="Maven: org.slf4j:jcl-over-slf4j:1.7.30" level="project" />
<orderEntry type="library" name="Maven: org.apache.poi:poi-ooxml:3.17" level="project" />
<orderEntry type="library" name="Maven: org.apache.poi:poi:3.17" level="project" />
<orderEntry type="library" name="Maven: org.apache.poi:poi-ooxml-schemas:3.17" level="project" />
<orderEntry type="library" name="Maven: org.apache.xmlbeans:xmlbeans:2.6.0" level="project" />
<orderEntry type="library" name="Maven: stax:stax-api:1.0.1" level="project" />
<orderEntry type="library" name="Maven: com.github.virtuald:curvesapi:1.04" level="project" />
<orderEntry type="library" scope="RUNTIME" name="Maven: mysql:mysql-connector-java:8.0.23" level="project" />
<orderEntry type="library" name="Maven: com.alibaba:druid-spring-boot-starter:1.2.6" level="project" />
<orderEntry type="library" name="Maven: com.alibaba:druid:1.2.6" level="project" />
<orderEntry type="library" name="Maven: org.springframework.boot:spring-boot-autoconfigure:2.3.10.RELEASE" level="project" />
<orderEntry type="library" name="Maven: tk.mybatis:mapper-spring-boot-starter:2.1.5" level="project" />
<orderEntry type="library" name="Maven: org.springframework.boot:spring-boot-starter-jdbc:2.3.10.RELEASE" level="project" />
<orderEntry type="library" name="Maven: com.zaxxer:HikariCP:3.4.5" level="project" />
<orderEntry type="library" name="Maven: org.springframework:spring-jdbc:5.2.14.RELEASE" level="project" />
<orderEntry type="library" name="Maven: org.springframework:spring-tx:5.2.14.RELEASE" level="project" />
<orderEntry type="library" name="Maven: org.mybatis:mybatis:3.4.6" level="project" />
<orderEntry type="library" name="Maven: org.mybatis:mybatis-spring:1.3.2" level="project" />
<orderEntry type="library" name="Maven: tk.mybatis:mapper-core:1.1.5" level="project" />
<orderEntry type="library" name="Maven: javax.persistence:persistence-api:1.0" level="project" />
<orderEntry type="library" name="Maven: tk.mybatis:mapper-base:1.1.5" level="project" />
<orderEntry type="library" name="Maven: tk.mybatis:mapper-weekend:1.1.5" level="project" />
<orderEntry type="library" name="Maven: tk.mybatis:mapper-spring:1.1.5" level="project" />
<orderEntry type="library" name="Maven: tk.mybatis:mapper-extra:1.1.5" level="project" />
<orderEntry type="library" name="Maven: tk.mybatis:mapper-spring-boot-autoconfigure:2.1.5" level="project" />
<orderEntry type="library" name="Maven: com.github.pagehelper:pagehelper-spring-boot-starter:1.3.0" level="project" />
<orderEntry type="library" name="Maven: org.mybatis.spring.boot:mybatis-spring-boot-starter:2.1.3" level="project" />
<orderEntry type="library" name="Maven: org.mybatis.spring.boot:mybatis-spring-boot-autoconfigure:2.1.3" level="project" />
<orderEntry type="library" name="Maven: com.github.pagehelper:pagehelper-spring-boot-autoconfigure:1.3.0" level="project" />
<orderEntry type="library" name="Maven: com.github.pagehelper:pagehelper:5.2.0" level="project" />
<orderEntry type="library" name="Maven: com.github.jsqlparser:jsqlparser:3.2" level="project" />
<orderEntry type="library" name="Maven: org.redisson:redisson:3.15.4" level="project" />
<orderEntry type="library" name="Maven: io.netty:netty-common:4.1.63.Final" level="project" />
<orderEntry type="library" name="Maven: io.netty:netty-codec:4.1.63.Final" level="project" />
<orderEntry type="library" name="Maven: io.netty:netty-buffer:4.1.63.Final" level="project" />
<orderEntry type="library" name="Maven: io.netty:netty-transport:4.1.63.Final" level="project" />
<orderEntry type="library" name="Maven: io.netty:netty-resolver:4.1.63.Final" level="project" />
<orderEntry type="library" name="Maven: io.netty:netty-resolver-dns:4.1.63.Final" level="project" />
<orderEntry type="library" name="Maven: io.netty:netty-codec-dns:4.1.63.Final" level="project" />
<orderEntry type="library" name="Maven: io.netty:netty-handler:4.1.63.Final" level="project" />
<orderEntry type="library" name="Maven: javax.cache:cache-api:1.1.1" level="project" />
<orderEntry type="library" name="Maven: io.projectreactor:reactor-core:3.3.16.RELEASE" level="project" />
<orderEntry type="library" name="Maven: org.reactivestreams:reactive-streams:1.0.3" level="project" />
<orderEntry type="library" name="Maven: io.reactivex.rxjava3:rxjava:3.0.12" level="project" />
<orderEntry type="library" name="Maven: org.jboss.marshalling:jboss-marshalling-river:2.0.11.Final" level="project" />
<orderEntry type="library" name="Maven: org.jboss.marshalling:jboss-marshalling:2.0.11.Final" level="project" />
<orderEntry type="library" name="Maven: org.slf4j:slf4j-api:1.7.30" level="project" />
<orderEntry type="library" name="Maven: org.yaml:snakeyaml:1.26" level="project" />
<orderEntry type="library" name="Maven: com.fasterxml.jackson.dataformat:jackson-dataformat-yaml:2.11.4" level="project" />
<orderEntry type="library" name="Maven: com.fasterxml.jackson.core:jackson-core:2.11.4" level="project" />
<orderEntry type="library" name="Maven: com.fasterxml.jackson.core:jackson-databind:2.11.4" level="project" />
<orderEntry type="library" name="Maven: com.fasterxml.jackson.core:jackson-annotations:2.11.4" level="project" />
<orderEntry type="library" name="Maven: net.bytebuddy:byte-buddy:1.10.22" level="project" />
<orderEntry type="library" name="Maven: org.jodd:jodd-bean:5.1.6" level="project" />
<orderEntry type="library" name="Maven: org.jodd:jodd-core:5.1.6" level="project" />
<orderEntry type="library" name="Maven: org.springframework.boot:spring-boot-starter-freemarker:2.3.10.RELEASE" level="project" />
<orderEntry type="library" name="Maven: org.springframework.boot:spring-boot-starter:2.3.10.RELEASE" level="project" />
<orderEntry type="library" name="Maven: org.springframework.boot:spring-boot:2.3.10.RELEASE" level="project" />
<orderEntry type="library" name="Maven: jakarta.annotation:jakarta.annotation-api:1.3.5" level="project" />
<orderEntry type="library" name="Maven: org.freemarker:freemarker:2.3.31" level="project" />
<orderEntry type="library" name="Maven: org.springframework:spring-context-support:5.2.14.RELEASE" level="project" />
<orderEntry type="library" name="Maven: javax.servlet:javax.servlet-api:4.0.1" level="project" />
<orderEntry type="library" name="Maven: org.springframework.boot:spring-boot-starter-log4j2:2.3.10.RELEASE" level="project" />
<orderEntry type="library" name="Maven: org.apache.logging.log4j:log4j-slf4j-impl:2.13.3" level="project" />
<orderEntry type="library" name="Maven: org.apache.logging.log4j:log4j-api:2.13.3" level="project" />
<orderEntry type="library" name="Maven: org.apache.logging.log4j:log4j-core:2.13.3" level="project" />
<orderEntry type="library" name="Maven: org.apache.logging.log4j:log4j-jul:2.13.3" level="project" />
<orderEntry type="library" name="Maven: org.slf4j:jul-to-slf4j:1.7.30" level="project" />
<orderEntry type="library" name="Maven: org.springframework.boot:spring-boot-starter-aop:2.3.10.RELEASE" level="project" />
<orderEntry type="library" name="Maven: org.springframework:spring-aop:5.2.14.RELEASE" level="project" />
<orderEntry type="library" name="Maven: org.aspectj:aspectjweaver:1.9.6" level="project" />
<orderEntry type="library" name="Maven: org.springframework.boot:spring-boot-starter-cache:2.3.10.RELEASE" level="project" />
<orderEntry type="library" name="Maven: org.springframework.boot:spring-boot-configuration-processor:2.3.10.RELEASE" level="project" />
<orderEntry type="library" name="Maven: org.springframework.boot:spring-boot-starter-actuator:2.3.10.RELEASE" level="project" />
<orderEntry type="library" name="Maven: org.springframework.boot:spring-boot-actuator-autoconfigure:2.3.10.RELEASE" level="project" />
<orderEntry type="library" name="Maven: org.springframework.boot:spring-boot-actuator:2.3.10.RELEASE" level="project" />
<orderEntry type="library" name="Maven: com.fasterxml.jackson.datatype:jackson-datatype-jsr310:2.11.4" level="project" />
<orderEntry type="library" name="Maven: io.micrometer:micrometer-core:1.5.13" level="project" />
<orderEntry type="library" name="Maven: org.hdrhistogram:HdrHistogram:2.1.12" level="project" />
<orderEntry type="library" scope="RUNTIME" name="Maven: org.latencyutils:LatencyUtils:2.0.3" level="project" />
<orderEntry type="library" name="Maven: de.codecentric:spring-boot-admin-starter-client:2.3.1" level="project" />
<orderEntry type="library" name="Maven: de.codecentric:spring-boot-admin-client:2.3.1" level="project" />
<orderEntry type="library" name="Maven: com.alibaba.cloud:spring-cloud-starter-alibaba-nacos-config:2.2.5.RELEASE" level="project" />
<orderEntry type="library" name="Maven: com.alibaba.spring:spring-context-support:1.0.10" level="project" />
<orderEntry type="library" name="Maven: com.alibaba.nacos:nacos-client:1.4.1" level="project" />
<orderEntry type="library" name="Maven: com.alibaba.nacos:nacos-common:1.4.1" level="project" />
<orderEntry type="library" name="Maven: org.apache.httpcomponents:httpasyncclient:4.1.4" level="project" />
<orderEntry type="library" name="Maven: org.apache.httpcomponents:httpcore-nio:4.4.14" level="project" />
<orderEntry type="library" name="Maven: com.alibaba.nacos:nacos-api:1.4.1" level="project" />
<orderEntry type="library" name="Maven: io.prometheus:simpleclient:0.5.0" level="project" />
<orderEntry type="library" name="Maven: org.springframework.cloud:spring-cloud-commons:2.2.6.RELEASE" level="project" />
<orderEntry type="library" name="Maven: org.springframework.security:spring-security-crypto:5.3.9.RELEASE" level="project" />
<orderEntry type="library" name="Maven: org.springframework.cloud:spring-cloud-context:2.2.6.RELEASE" level="project" />
<orderEntry type="library" name="Maven: org.springframework.cloud:spring-cloud-starter-openfeign:2.2.6.RELEASE" level="project" />
<orderEntry type="library" name="Maven: org.springframework.cloud:spring-cloud-starter:2.2.6.RELEASE" level="project" />
<orderEntry type="library" name="Maven: org.springframework.security:spring-security-rsa:1.0.9.RELEASE" level="project" />
<orderEntry type="library" name="Maven: org.bouncycastle:bcpkix-jdk15on:1.59" level="project" />
<orderEntry type="library" name="Maven: org.bouncycastle:bcprov-jdk15on:1.59" level="project" />
<orderEntry type="library" name="Maven: org.springframework.cloud:spring-cloud-openfeign-core:2.2.6.RELEASE" level="project" />
<orderEntry type="library" name="Maven: io.github.openfeign.form:feign-form-spring:3.8.0" level="project" />
<orderEntry type="library" name="Maven: io.github.openfeign.form:feign-form:3.8.0" level="project" />
<orderEntry type="library" name="Maven: org.springframework:spring-web:5.2.14.RELEASE" level="project" />
<orderEntry type="library" name="Maven: io.github.openfeign:feign-core:10.10.1" level="project" />
<orderEntry type="library" name="Maven: io.github.openfeign:feign-slf4j:10.10.1" level="project" />
<orderEntry type="library" name="Maven: io.github.openfeign:feign-hystrix:10.10.1" level="project" />
<orderEntry type="library" name="Maven: com.netflix.archaius:archaius-core:0.7.6" level="project" />
<orderEntry type="library" name="Maven: io.github.openfeign:feign-httpclient:10.10.1" level="project" />
<orderEntry type="library" name="Maven: org.apache.httpcomponents:httpclient:4.5.13" level="project" />
<orderEntry type="library" name="Maven: org.apache.httpcomponents:httpcore:4.4.14" level="project" />
<orderEntry type="library" name="Maven: org.springframework.cloud:spring-cloud-starter-netflix-hystrix:2.2.6.RELEASE" level="project" />
<orderEntry type="library" name="Maven: org.springframework.cloud:spring-cloud-netflix-hystrix:2.2.6.RELEASE" level="project" />
<orderEntry type="library" name="Maven: org.springframework.cloud:spring-cloud-netflix-ribbon:2.2.6.RELEASE" level="project" />
<orderEntry type="library" name="Maven: org.springframework.cloud:spring-cloud-netflix-archaius:2.2.6.RELEASE" level="project" />
<orderEntry type="library" name="Maven: org.springframework.cloud:spring-cloud-starter-netflix-archaius:2.2.6.RELEASE" level="project" />
<orderEntry type="library" name="Maven: commons-configuration:commons-configuration:1.8" level="project" />
<orderEntry type="library" name="Maven: commons-lang:commons-lang:2.6" level="project" />
<orderEntry type="library" name="Maven: com.netflix.hystrix:hystrix-core:1.5.18" level="project" />
<orderEntry type="library" name="Maven: io.reactivex:rxjava:1.3.8" level="project" />
<orderEntry type="library" name="Maven: com.netflix.hystrix:hystrix-serialization:1.5.18" level="project" />
<orderEntry type="library" scope="RUNTIME" name="Maven: com.fasterxml.jackson.module:jackson-module-afterburner:2.11.4" level="project" />
<orderEntry type="library" name="Maven: com.netflix.hystrix:hystrix-metrics-event-stream:1.5.18" level="project" />
<orderEntry type="library" name="Maven: com.netflix.hystrix:hystrix-javanica:1.5.18" level="project" />
<orderEntry type="library" scope="RUNTIME" name="Maven: org.ow2.asm:asm:5.0.4" level="project" />
<orderEntry type="library" name="Maven: io.reactivex:rxjava-reactive-streams:1.2.1" level="project" />
<orderEntry type="library" name="Maven: org.hibernate.validator:hibernate-validator:6.2.0.Final" level="project" />
<orderEntry type="library" name="Maven: jakarta.validation:jakarta.validation-api:2.0.2" level="project" />
<orderEntry type="library" name="Maven: org.jboss.logging:jboss-logging:3.4.1.Final" level="project" />
<orderEntry type="library" name="Maven: com.fasterxml:classmate:1.5.1" level="project" />
<orderEntry type="library" name="Maven: org.mapstruct:mapstruct:1.4.2.Final" level="project" />
<orderEntry type="library" scope="PROVIDED" name="Maven: org.mapstruct:mapstruct-processor:1.4.2.Final" level="project" />
<orderEntry type="library" scope="PROVIDED" name="Maven: org.projectlombok:lombok:1.18.20" level="project" />
<orderEntry type="library" name="Maven: org.apache.curator:curator-recipes:4.3.0" level="project" />
<orderEntry type="library" name="Maven: org.apache.curator:curator-framework:4.0.1" level="project" />
<orderEntry type="library" name="Maven: org.apache.curator:curator-client:4.0.1" level="project" />
<orderEntry type="library" name="Maven: org.apache.zookeeper:zookeeper:3.5.3-beta" level="project" />
<orderEntry type="library" name="Maven: commons-cli:commons-cli:1.4" level="project" />
<orderEntry type="library" name="Maven: org.apache.kafka:kafka-clients:2.4.0" level="project" />
<orderEntry type="library" name="Maven: com.github.luben:zstd-jni:1.4.3-1" level="project" />
<orderEntry type="library" name="Maven: org.lz4:lz4-java:1.6.0" level="project" />
<orderEntry type="library" name="Maven: org.xerial.snappy:snappy-java:1.1.7.3" level="project" />
<orderEntry type="library" name="Maven: org.scala-lang:scala-library:2.12.10" level="project" />
<orderEntry type="library" name="Maven: com.lmax:disruptor:3.4.3" level="project" />
<orderEntry type="library" name="Maven: org.springframework.plugin:spring-plugin-core:2.0.0.RELEASE" level="project" />
<orderEntry type="library" name="Maven: org.springframework:spring-beans:5.2.14.RELEASE" level="project" />
<orderEntry type="library" name="Maven: org.springframework:spring-context:5.2.14.RELEASE" level="project" />
<orderEntry type="library" name="Maven: org.springframework:spring-expression:5.2.14.RELEASE" level="project" />
<orderEntry type="library" name="Maven: org.springframework.plugin:spring-plugin-metadata:2.0.0.RELEASE" level="project" />
<orderEntry type="library" scope="TEST" name="Maven: org.springframework.boot:spring-boot-starter-test:2.3.10.RELEASE" level="project" />
<orderEntry type="library" scope="TEST" name="Maven: org.springframework.boot:spring-boot-test:2.3.10.RELEASE" level="project" />
<orderEntry type="library" scope="TEST" name="Maven: org.springframework.boot:spring-boot-test-autoconfigure:2.3.10.RELEASE" level="project" />
<orderEntry type="library" scope="TEST" name="Maven: com.jayway.jsonpath:json-path:2.4.0" level="project" />
<orderEntry type="library" scope="TEST" name="Maven: net.minidev:json-smart:2.3" level="project" />
<orderEntry type="library" scope="TEST" name="Maven: net.minidev:accessors-smart:1.2" level="project" />
<orderEntry type="library" scope="TEST" name="Maven: jakarta.xml.bind:jakarta.xml.bind-api:2.3.3" level="project" />
<orderEntry type="library" scope="TEST" name="Maven: jakarta.activation:jakarta.activation-api:1.2.2" level="project" />
<orderEntry type="library" scope="TEST" name="Maven: org.assertj:assertj-core:3.16.1" level="project" />
<orderEntry type="library" scope="TEST" name="Maven: org.hamcrest:hamcrest:2.2" level="project" />
<orderEntry type="library" scope="TEST" name="Maven: org.junit.jupiter:junit-jupiter:5.6.3" level="project" />
<orderEntry type="library" scope="TEST" name="Maven: org.junit.jupiter:junit-jupiter-api:5.6.3" level="project" />
<orderEntry type="library" scope="TEST" name="Maven: org.opentest4j:opentest4j:1.2.0" level="project" />
<orderEntry type="library" scope="TEST" name="Maven: org.junit.platform:junit-platform-commons:1.6.3" level="project" />
<orderEntry type="library" scope="TEST" name="Maven: org.junit.jupiter:junit-jupiter-params:5.6.3" level="project" />
<orderEntry type="library" scope="TEST" name="Maven: org.junit.jupiter:junit-jupiter-engine:5.6.3" level="project" />
<orderEntry type="library" scope="TEST" name="Maven: org.junit.vintage:junit-vintage-engine:5.6.3" level="project" />
<orderEntry type="library" scope="TEST" name="Maven: org.apiguardian:apiguardian-api:1.1.0" level="project" />
<orderEntry type="library" scope="TEST" name="Maven: org.junit.platform:junit-platform-engine:1.6.3" level="project" />
<orderEntry type="library" scope="TEST" name="Maven: junit:junit:4.13.2" level="project" />
<orderEntry type="library" scope="TEST" name="Maven: org.mockito:mockito-core:3.3.3" level="project" />
<orderEntry type="library" scope="TEST" name="Maven: net.bytebuddy:byte-buddy-agent:1.10.22" level="project" />
<orderEntry type="library" scope="TEST" name="Maven: org.objenesis:objenesis:2.6" level="project" />
<orderEntry type="library" scope="TEST" name="Maven: org.mockito:mockito-junit-jupiter:3.3.3" level="project" />
<orderEntry type="library" scope="TEST" name="Maven: org.skyscreamer:jsonassert:1.5.0" level="project" />
<orderEntry type="library" scope="TEST" name="Maven: com.vaadin.external.google:android-json:0.0.20131108.vaadin1" level="project" />
<orderEntry type="library" name="Maven: org.springframework:spring-core:5.2.14.RELEASE" level="project" />
<orderEntry type="library" name="Maven: org.springframework:spring-jcl:5.2.14.RELEASE" level="project" />
<orderEntry type="library" scope="TEST" name="Maven: org.springframework:spring-test:5.2.14.RELEASE" level="project" />
<orderEntry type="library" scope="TEST" name="Maven: org.xmlunit:xmlunit-core:2.7.0" level="project" />
</component>
</module>

View File

@@ -0,0 +1,29 @@
<?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">
<parent>
<artifactId>common</artifactId>
<groupId>com.orange.demo</groupId>
<version>1.0.0</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>common-redis</artifactId>
<version>1.0.0</version>
<name>common-redis</name>
<packaging>jar</packaging>
<dependencies>
<dependency>
<groupId>com.orange.demo</groupId>
<artifactId>common-core</artifactId>
<version>1.0.0</version>
</dependency>
<dependency>
<groupId>org.redisson</groupId>
<artifactId>redisson</artifactId>
<version>${redisson.version}</version>
</dependency>
</dependencies>
</project>

View File

@@ -0,0 +1,412 @@
package com.orange.demo.common.redis.cache;
import com.alibaba.fastjson.JSON;
import com.orange.demo.common.core.cache.DictionaryCache;
import com.orange.demo.common.core.constant.ApplicationConstant;
import com.orange.demo.common.core.exception.RedisCacheAccessException;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.CollectionUtils;
import org.redisson.api.RMap;
import org.redisson.api.RedissonClient;
import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
import java.util.concurrent.locks.ReadWriteLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;
import java.util.function.Function;
import java.util.stream.Collectors;
/**
* 字典数据Redis缓存对象。
*
* @param <K> 字典表主键类型。
* @param <V> 字典表对象类型。
* @author Jerry
* @date 2020-08-08
*/
@Slf4j
public class RedisDictionaryCache<K, V> implements DictionaryCache<K, V> {
/**
* redisson客户端。
*/
protected final RedissonClient redissonClient;
/**
* 数据存储对象。
*/
protected final RMap<K, String> dataMap;
/**
* 字典值对象类型。
*/
protected final Class<V> valueClazz;
/**
* 由于大部分场景是读取操作,所以使用读写锁提高并发的伸缩性。
*/
protected final ReadWriteLock lock;
/**
* 获取字典主键数据的函数对象。
*/
protected final Function<V, K> idGetter;
/**
* 超时时长。单位毫秒。
*/
protected static final long TIMEOUT = 2000L;
/**
* 当前对象的构造器函数。
*
* @param redissonClient Redisson的客户端对象。
* @param dictionaryName 字典表的名称。等同于redis hash对象的key。
* @param valueClazz 值对象的Class对象。
* @param idGetter 获取当前类主键字段值的函数对象。
* @param <K> 字典主键类型。
* @param <V> 字典对象类型
* @return 实例化后的字典内存缓存对象。
*/
public static <K, V> RedisDictionaryCache<K, V> create(
RedissonClient redissonClient,
String dictionaryName,
Class<V> valueClazz,
Function<V, K> idGetter) {
if (idGetter == null) {
throw new IllegalArgumentException("IdGetter can't be NULL.");
}
return new RedisDictionaryCache<>(redissonClient, dictionaryName, valueClazz, idGetter);
}
/**
* 构造函数。
*
* @param redissonClient Redisson的客户端对象。
* @param dictionaryName 字典表的名称。等同于redis hash对象的key。确保全局唯一。
* @param valueClazz 值对象的Class对象。
* @param idGetter 获取当前类主键字段值的函数对象。
*/
public RedisDictionaryCache(
RedissonClient redissonClient,
String dictionaryName,
Class<V> valueClazz,
Function<V, K> idGetter) {
this.redissonClient = redissonClient;
this.dataMap = redissonClient.getMap(dictionaryName + ApplicationConstant.DICT_CACHE_NAME_SUFFIX);
this.lock = new ReentrantReadWriteLock();
this.valueClazz = valueClazz;
this.idGetter = idGetter;
}
/**
* 按照数据插入的顺序返回全部字典对象的列表。
*
* @return 全部字段数据列表。
*/
@Override
public List<V> getAll() {
Collection<String> dataList;
String exceptionMessage;
try {
if (lock.readLock().tryLock(TIMEOUT, TimeUnit.MILLISECONDS)) {
try {
dataList = dataMap.readAllValues();
} finally {
// 如果上面的操作时间超过redisson.lockWatchdogTimeout的时长
// redis会将与该锁关联的键删除此后调用unlock的时候就会抛出运行时异常。
lock.readLock().unlock();
}
} else {
throw new TimeoutException();
}
} catch (Exception e) {
exceptionMessage = String.format(
"LOCK Operation of [RedisDictionaryCache::getAll] encountered EXCEPTION [%s] for DICT [%s].",
e.getClass().getSimpleName(), valueClazz.getSimpleName());
log.warn(exceptionMessage);
throw new RedisCacheAccessException(exceptionMessage, e);
}
if (CollectionUtils.isEmpty(dataList)) {
return new LinkedList<>();
}
return dataList.stream()
.map(data -> JSON.parseObject(data, valueClazz))
.collect(Collectors.toCollection(LinkedList::new));
}
/**
* 获取缓存中与键列表对应的对象列表。
*
* @param keys 主键集合。
* @return 对象列表。
*/
@Override
public List<V> getInList(Set<K> keys) {
if (CollectionUtils.isEmpty(keys)) {
return new LinkedList<>();
}
Collection<String> dataList;
String exceptionMessage;
try {
if (lock.readLock().tryLock(TIMEOUT, TimeUnit.MILLISECONDS)) {
try {
dataList = dataMap.getAll(keys).values();
} finally {
lock.readLock().unlock();
}
} else {
throw new TimeoutException();
}
} catch (Exception e) {
exceptionMessage = String.format(
"LOCK Operation of [RedisDictionaryCache::getInList] encountered EXCEPTION [%s] for DICT [%s].",
e.getClass().getSimpleName(), valueClazz.getSimpleName());
log.warn(exceptionMessage);
throw new RedisCacheAccessException(exceptionMessage, e);
}
if (dataList == null) {
return new LinkedList<>();
}
return dataList.stream()
.map(data -> JSON.parseObject(data, valueClazz))
.collect(Collectors.toCollection(LinkedList::new));
}
/**
* 从缓存中获取指定的数据。
*
* @param id 数据的key。
* @return 获取到的数据如果没有返回null。
*/
@Override
public V get(K id) {
if (id == null) {
return null;
}
String data;
String exceptionMessage;
try {
if (lock.readLock().tryLock(TIMEOUT, TimeUnit.MILLISECONDS)) {
try {
data = dataMap.get(id);
} finally {
lock.readLock().unlock();
}
} else {
throw new TimeoutException();
}
} catch (Exception e) {
exceptionMessage = String.format(
"LOCK Operation of [RedisDictionaryCache::get] encountered EXCEPTION [%s] for DICT [%s].",
e.getClass().getSimpleName(), valueClazz.getSimpleName());
log.warn(exceptionMessage);
throw new RedisCacheAccessException(exceptionMessage, e);
}
if (data == null) {
return null;
}
return JSON.parseObject(data, valueClazz);
}
/**
* 获取缓存中数据条目的数量。
*
* @return 返回缓存的数据数量。
*/
@Override
public int getCount() {
return dataMap.size();
}
/**
* 将参数List中的数据保存到缓存中同时保证getAll返回的数据列表与参数列表中数据项的顺序保持一致。
*
* @param dataList 待缓存的数据列表。
*/
@Override
public void putAll(List<V> dataList) {
if (CollectionUtils.isEmpty(dataList)) {
return;
}
Map<K, String> map = dataList.stream()
.collect(Collectors.toMap(idGetter, JSON::toJSONString));
String exceptionMessage;
try {
if (lock.writeLock().tryLock(TIMEOUT, TimeUnit.MILLISECONDS)) {
try {
dataMap.putAll(map, 1000);
} finally {
lock.writeLock().unlock();
}
} else {
throw new TimeoutException();
}
} catch (Exception e) {
exceptionMessage = String.format(
"LOCK Operation of [RedisDictionaryCache::putAll] encountered EXCEPTION [%s] for DICT [%s].",
e.getClass().getSimpleName(), valueClazz.getSimpleName());
log.warn(exceptionMessage);
throw new RedisCacheAccessException(exceptionMessage, e);
}
}
/**
* 将数据存入缓存。
*
* @param id 通常为字典数据的主键。
* @param data 字典数据对象。
*/
@Override
public void put(K id, V data) {
if (id == null || data == null) {
return;
}
String exceptionMessage;
try {
if (lock.writeLock().tryLock(TIMEOUT, TimeUnit.MILLISECONDS)) {
try {
dataMap.fastPut(id, JSON.toJSONString(data));
} finally {
lock.writeLock().unlock();
}
} else {
throw new TimeoutException();
}
} catch (Exception e) {
exceptionMessage = String.format(
"LOCK Operation of [RedisDictionaryCache::put] encountered EXCEPTION [%s] for DICT [%s].",
e.getClass().getSimpleName(), valueClazz.getSimpleName());
log.warn(exceptionMessage);
throw new RedisCacheAccessException(exceptionMessage, e);
}
}
/**
* 重新加载先清空原有数据在执行putAll的操作。
*
* @param dataList 待缓存的数据列表。
* @param force true则强制刷新如果false当缓存中存在数据时不刷新。
*/
@Override
public void reload(List<V> dataList, boolean force) {
Map<K, String> map = null;
if (CollectionUtils.isNotEmpty(dataList)) {
map = dataList.stream().collect(Collectors.toMap(idGetter, JSON::toJSONString));
}
String exceptionMessage;
try {
if (lock.writeLock().tryLock(TIMEOUT, TimeUnit.MILLISECONDS)) {
try {
// 如果不强制刷新,需要先判断缓存中是否存在数据。
if (!force && this.getCount() > 0) {
return;
}
dataMap.clear();
if (map != null) {
dataMap.putAll(map, 1000);
}
} finally {
lock.writeLock().unlock();
}
} else {
throw new TimeoutException();
}
} catch (Exception e) {
exceptionMessage = String.format(
"LOCK Operation of [RedisDictionaryCache::reload] encountered EXCEPTION [%s] for DICT [%s].",
e.getClass().getSimpleName(), valueClazz.getSimpleName());
log.warn(exceptionMessage);
throw new RedisCacheAccessException(exceptionMessage, e);
}
}
/**
* 删除缓存中指定的键。
*
* @param id 待删除数据的主键。
* @return 返回被删除的对象如果主键不存在返回null。
*/
@Override
public V invalidate(K id) {
if (id == null) {
return null;
}
String data;
String exceptionMessage;
try {
if (lock.writeLock().tryLock(TIMEOUT, TimeUnit.MILLISECONDS)) {
try {
data = dataMap.remove(id);
} finally {
lock.writeLock().unlock();
}
} else {
throw new TimeoutException();
}
} catch (Exception e) {
exceptionMessage = String.format(
"LOCK Operation of [RedisDictionaryCache::invalidate] encountered EXCEPTION [%s] for DICT [%s].",
e.getClass().getSimpleName(), valueClazz.getSimpleName());
log.warn(exceptionMessage);
throw new RedisCacheAccessException(exceptionMessage, e);
}
if (data == null) {
return null;
}
return JSON.parseObject(data, valueClazz);
}
/**
* 删除缓存中,参数列表中包含的键。
*
* @param keys 待删除数据的主键集合。
*/
@SuppressWarnings("unchecked")
@Override
public void invalidateSet(Set<K> keys) {
if (CollectionUtils.isEmpty(keys)) {
return;
}
Object[] keyArray = keys.toArray(new Object[]{});
String exceptionMessage;
try {
if (lock.writeLock().tryLock(TIMEOUT, TimeUnit.MILLISECONDS)) {
try {
dataMap.fastRemove((K[]) keyArray);
} finally {
lock.writeLock().unlock();
}
} else {
throw new TimeoutException();
}
} catch (Exception e) {
exceptionMessage = String.format(
"LOCK Operation of [RedisDictionaryCache::invalidateSet] encountered EXCEPTION [%s] for DICT [%s].",
e.getClass().getSimpleName(), valueClazz.getSimpleName());
log.warn(exceptionMessage);
throw new RedisCacheAccessException(exceptionMessage, e);
}
}
/**
* 清空缓存。
*/
@Override
public void invalidateAll() {
String exceptionMessage;
try {
if (lock.writeLock().tryLock(TIMEOUT, TimeUnit.MILLISECONDS)) {
try {
dataMap.clear();
} finally {
lock.writeLock().unlock();
}
} else {
throw new TimeoutException();
}
} catch (Exception e) {
exceptionMessage = String.format(
"LOCK Operation of [RedisDictionaryCache::invalidateAll] encountered EXCEPTION [%s] for DICT [%s].",
e.getClass().getSimpleName(), valueClazz.getSimpleName());
log.warn(exceptionMessage);
throw new RedisCacheAccessException(exceptionMessage, e);
}
}
}

View File

@@ -0,0 +1,428 @@
package com.orange.demo.common.redis.cache;
import com.alibaba.fastjson.JSON;
import com.orange.demo.common.core.cache.DictionaryCache;
import com.orange.demo.common.core.constant.ApplicationConstant;
import com.orange.demo.common.core.exception.RedisCacheAccessException;
import com.orange.demo.common.core.object.TokenData;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.CollectionUtils;
import org.redisson.api.RMap;
import org.redisson.api.RedissonClient;
import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
import java.util.concurrent.locks.ReadWriteLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;
import java.util.function.Function;
import java.util.stream.Collectors;
/**
* 租户字典数据Redis缓存对象。
*
* @param <K> 字典表主键类型。
* @param <V> 字典表对象类型。
* @author Jerry
* @date 2020-08-08
*/
@Slf4j
public class RedisTenantDictionaryCache<K, V> implements DictionaryCache<K, V> {
/**
* redisson客户端。
*/
protected final RedissonClient redissonClient;
/**
* 字典名称。
*/
protected final String dictionaryName;
/**
* 字典值对象类型。
*/
protected final Class<V> valueClazz;
/**
* 由于大部分场景是读取操作,所以使用读写锁提高并发的伸缩性。
*/
protected final ReadWriteLock lock;
/**
* 获取字典主键数据的函数对象。
*/
protected final Function<V, K> idGetter;
/**
* 超时时长。单位毫秒。
*/
protected static final long TIMEOUT = 2000L;
/**
* 当前对象的构造器函数。
*
* @param redissonClient Redisson的客户端对象。
* @param valueClazz 值对象的Class对象。
* @param idGetter 获取当前类主键字段值的函数对象。
* @param <K> 字典主键类型。
* @param <V> 字典对象类型
* @return 实例化后的字典内存缓存对象。
*/
public static <K, V> RedisTenantDictionaryCache<K, V> create(
RedissonClient redissonClient,
String dictionaryName,
Class<V> valueClazz,
Function<V, K> idGetter) {
if (idGetter == null) {
throw new IllegalArgumentException("IdGetter can't be NULL.");
}
return new RedisTenantDictionaryCache<>(redissonClient, dictionaryName, valueClazz, idGetter);
}
/**
* 构造函数。
*
* @param redissonClient Redisson的客户端对象。
* @param valueClazz 值对象的Class对象。
* @param idGetter 获取当前类主键字段值的函数对象。
*/
public RedisTenantDictionaryCache(
RedissonClient redissonClient,
String dictionaryName,
Class<V> valueClazz,
Function<V, K> idGetter) {
this.redissonClient = redissonClient;
this.lock = new ReentrantReadWriteLock();
this.dictionaryName = dictionaryName;
this.valueClazz = valueClazz;
this.idGetter = idGetter;
}
/**
* 获取租户的字典数据Map。
*
* @return 当前租户的字典数据Map。
*/
protected RMap<K, String> getTenantDataMap() {
Long tenantId = TokenData.takeFromRequest().getTenantId();
StringBuilder s = new StringBuilder(64);
s.append(dictionaryName).append("-")
.append(tenantId).append(ApplicationConstant.TREE_DICT_CACHE_NAME_SUFFIX);
return redissonClient.getMap(s.toString());
}
/**
* 按照数据插入的顺序返回全部字典对象的列表。
*
* @return 全部字段数据列表。
*/
@Override
public List<V> getAll() {
Collection<String> dataList;
String exceptionMessage;
try {
if (lock.readLock().tryLock(TIMEOUT, TimeUnit.MILLISECONDS)) {
try {
dataList = this.getTenantDataMap().readAllValues();
} finally {
// 如果上面的操作时间超过redisson.lockWatchdogTimeout的时长
// redis会将与该锁关联的键删除此后调用unlock的时候就会抛出运行时异常。
lock.readLock().unlock();
}
} else {
throw new TimeoutException();
}
} catch (Exception e) {
exceptionMessage = String.format(
"LOCK Operation of [RedisDictionaryCache::getAll] encountered EXCEPTION [%s] for DICT [%s].",
e.getClass().getSimpleName(), valueClazz.getSimpleName());
log.warn(exceptionMessage);
throw new RedisCacheAccessException(exceptionMessage, e);
}
if (CollectionUtils.isEmpty(dataList)) {
return new LinkedList<>();
}
return dataList.stream()
.map(data -> JSON.parseObject(data, valueClazz))
.collect(Collectors.toCollection(LinkedList::new));
}
/**
* 获取缓存中与键列表对应的对象列表。
*
* @param keys 主键集合。
* @return 对象列表。
*/
@Override
public List<V> getInList(Set<K> keys) {
if (CollectionUtils.isEmpty(keys)) {
return new LinkedList<>();
}
Collection<String> dataList = null;
String exceptionMessage;
try {
if (lock.readLock().tryLock(TIMEOUT, TimeUnit.MILLISECONDS)) {
try {
Map<K, String> m = this.getTenantDataMap().getAll(keys);
if (m != null) {
dataList = m.values();
}
} finally {
lock.readLock().unlock();
}
} else {
throw new TimeoutException();
}
} catch (Exception e) {
exceptionMessage = String.format(
"LOCK Operation of [RedisDictionaryCache::getInList] encountered EXCEPTION [%s] for DICT [%s].",
e.getClass().getSimpleName(), valueClazz.getSimpleName());
log.warn(exceptionMessage);
throw new RedisCacheAccessException(exceptionMessage, e);
}
if (dataList == null) {
return new LinkedList<>();
}
return dataList.stream()
.map(data -> JSON.parseObject(data, valueClazz))
.collect(Collectors.toCollection(LinkedList::new));
}
/**
* 从缓存中获取指定的数据。
*
* @param id 数据的key。
* @return 获取到的数据如果没有返回null。
*/
@Override
public V get(K id) {
if (id == null) {
return null;
}
String data;
String exceptionMessage;
try {
if (lock.readLock().tryLock(TIMEOUT, TimeUnit.MILLISECONDS)) {
try {
data = this.getTenantDataMap().get(id);
} finally {
lock.readLock().unlock();
}
} else {
throw new TimeoutException();
}
} catch (Exception e) {
exceptionMessage = String.format(
"LOCK Operation of [RedisDictionaryCache::get] encountered EXCEPTION [%s] for DICT [%s].",
e.getClass().getSimpleName(), valueClazz.getSimpleName());
log.warn(exceptionMessage);
throw new RedisCacheAccessException(exceptionMessage, e);
}
if (data == null) {
return null;
}
return JSON.parseObject(data, valueClazz);
}
/**
* 获取缓存中数据条目的数量。
*
* @return 返回缓存的数据数量。
*/
@Override
public int getCount() {
return this.getTenantDataMap().size();
}
/**
* 将参数List中的数据保存到缓存中同时保证getAll返回的数据列表与参数列表中数据项的顺序保持一致。
*
* @param dataList 待缓存的数据列表。
*/
@Override
public void putAll(List<V> dataList) {
if (CollectionUtils.isEmpty(dataList)) {
return;
}
Map<K, String> map = dataList.stream()
.collect(Collectors.toMap(idGetter, JSON::toJSONString));
String exceptionMessage;
try {
if (lock.writeLock().tryLock(TIMEOUT, TimeUnit.MILLISECONDS)) {
try {
this.getTenantDataMap().putAll(map, 1000);
} finally {
lock.writeLock().unlock();
}
} else {
throw new TimeoutException();
}
} catch (Exception e) {
exceptionMessage = String.format(
"LOCK Operation of [RedisDictionaryCache::putAll] encountered EXCEPTION [%s] for DICT [%s].",
e.getClass().getSimpleName(), valueClazz.getSimpleName());
log.warn(exceptionMessage);
throw new RedisCacheAccessException(exceptionMessage, e);
}
}
/**
* 将数据存入缓存。
*
* @param id 通常为字典数据的主键。
* @param data 字典数据对象。
*/
@Override
public void put(K id, V data) {
if (id == null || data == null) {
return;
}
String exceptionMessage;
try {
if (lock.writeLock().tryLock(TIMEOUT, TimeUnit.MILLISECONDS)) {
try {
this.getTenantDataMap().fastPut(id, JSON.toJSONString(data));
} finally {
lock.writeLock().unlock();
}
} else {
throw new TimeoutException();
}
} catch (Exception e) {
exceptionMessage = String.format(
"LOCK Operation of [RedisDictionaryCache::put] encountered EXCEPTION [%s] for DICT [%s].",
e.getClass().getSimpleName(), valueClazz.getSimpleName());
log.warn(exceptionMessage);
throw new RedisCacheAccessException(exceptionMessage, e);
}
}
/**
* 重新加载先清空原有数据在执行putAll的操作。
*
* @param dataList 待缓存的数据列表。
* @param force true则强制刷新如果false当缓存中存在数据时不刷新。
*/
@Override
public void reload(List<V> dataList, boolean force) {
Map<K, String> map = null;
if (CollectionUtils.isNotEmpty(dataList)) {
map = dataList.stream().collect(Collectors.toMap(idGetter, JSON::toJSONString));
}
String exceptionMessage;
try {
if (lock.writeLock().tryLock(TIMEOUT, TimeUnit.MILLISECONDS)) {
try {
// 如果不强制刷新,需要先判断缓存中是否存在数据。
if (!force && this.getCount() > 0) {
return;
}
RMap<K, String> tenantDataMap = this.getTenantDataMap();
tenantDataMap.clear();
if (map != null) {
tenantDataMap.putAll(map, 1000);
}
} finally {
lock.writeLock().unlock();
}
} else {
throw new TimeoutException();
}
} catch (Exception e) {
exceptionMessage = String.format(
"LOCK Operation of [RedisDictionaryCache::reload] encountered EXCEPTION [%s] for DICT [%s].",
e.getClass().getSimpleName(), valueClazz.getSimpleName());
log.warn(exceptionMessage);
throw new RedisCacheAccessException(exceptionMessage, e);
}
}
/**
* 删除缓存中指定的键。
*
* @param id 待删除数据的主键。
* @return 返回被删除的对象如果主键不存在返回null。
*/
@Override
public V invalidate(K id) {
if (id == null) {
return null;
}
String data = null;
String exceptionMessage;
try {
if (lock.writeLock().tryLock(TIMEOUT, TimeUnit.MILLISECONDS)) {
try {
this.getTenantDataMap().remove(id);
} finally {
lock.writeLock().unlock();
}
} else {
throw new TimeoutException();
}
} catch (Exception e) {
exceptionMessage = String.format(
"LOCK Operation of [RedisDictionaryCache::invalidate] encountered EXCEPTION [%s] for DICT [%s].",
e.getClass().getSimpleName(), valueClazz.getSimpleName());
log.warn(exceptionMessage);
throw new RedisCacheAccessException(exceptionMessage, e);
}
if (data == null) {
return null;
}
return JSON.parseObject(data, valueClazz);
}
/**
* 删除缓存中,参数列表中包含的键。
*
* @param keys 待删除数据的主键集合。
*/
@SuppressWarnings("unchecked")
@Override
public void invalidateSet(Set<K> keys) {
if (CollectionUtils.isEmpty(keys)) {
return;
}
Object[] keyArray = keys.toArray(new Object[]{});
String exceptionMessage;
try {
if (lock.writeLock().tryLock(TIMEOUT, TimeUnit.MILLISECONDS)) {
try {
this.getTenantDataMap().fastRemove((K[]) keyArray);
} finally {
lock.writeLock().unlock();
}
} else {
throw new TimeoutException();
}
} catch (Exception e) {
exceptionMessage = String.format(
"LOCK Operation of [RedisDictionaryCache::invalidateSet] encountered EXCEPTION [%s] for DICT [%s].",
e.getClass().getSimpleName(), valueClazz.getSimpleName());
log.warn(exceptionMessage);
throw new RedisCacheAccessException(exceptionMessage, e);
}
}
/**
* 清空缓存。
*/
@Override
public void invalidateAll() {
String exceptionMessage;
try {
if (lock.writeLock().tryLock(TIMEOUT, TimeUnit.MILLISECONDS)) {
try {
this.getTenantDataMap().clear();
} finally {
lock.writeLock().unlock();
}
} else {
throw new TimeoutException();
}
} catch (Exception e) {
exceptionMessage = String.format(
"LOCK Operation of [RedisDictionaryCache::invalidateAll] encountered EXCEPTION [%s] for DICT [%s].",
e.getClass().getSimpleName(), valueClazz.getSimpleName());
log.warn(exceptionMessage);
throw new RedisCacheAccessException(exceptionMessage, e);
}
}
}

View File

@@ -0,0 +1,369 @@
package com.orange.demo.common.redis.cache;
import com.alibaba.fastjson.JSON;
import com.orange.demo.common.core.constant.ApplicationConstant;
import com.orange.demo.common.core.exception.RedisCacheAccessException;
import com.orange.demo.common.core.object.TokenData;
import com.google.common.collect.LinkedListMultimap;
import com.google.common.collect.Multimap;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.CollectionUtils;
import org.redisson.api.RListMultimap;
import org.redisson.api.RMap;
import org.redisson.api.RedissonClient;
import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
import java.util.function.Function;
import java.util.stream.Collectors;
/**
* 租户树形字典数据Redis缓存对象。
*
* @param <K> 字典表主键类型。
* @param <V> 字典表对象类型。
* @author Jerry
* @date 2020-08-08
*/
@Slf4j
public class RedisTenantTreeDictionaryCache<K, V> extends RedisTenantDictionaryCache<K, V> {
/**
* 获取字典父主键数据的函数对象。
*/
protected final Function<V, K> parentIdGetter;
/**
* 当前对象的构造器函数。
*
* @param redissonClient Redisson的客户端对象。
* @param dictionaryName 字典表的名称。等同于redis hash对象的key。
* @param valueClazz 值对象的Class对象。
* @param idGetter 获取当前类主键字段值的函数对象。
* @param parentIdGetter 获取当前类父主键字段值的函数对象。
* @param <K> 字典主键类型。
* @param <V> 字典对象类型
* @return 实例化后的树形字典内存缓存对象。
*/
public static <K, V> RedisTenantTreeDictionaryCache<K, V> create(
RedissonClient redissonClient,
String dictionaryName,
Class<V> valueClazz,
Function<V, K> idGetter,
Function<V, K> parentIdGetter) {
if (idGetter == null) {
throw new IllegalArgumentException("IdGetter can't be NULL.");
}
if (parentIdGetter == null) {
throw new IllegalArgumentException("ParentIdGetter can't be NULL.");
}
return new RedisTenantTreeDictionaryCache<>(
redissonClient, dictionaryName, valueClazz, idGetter, parentIdGetter);
}
/**
* 构造函数。
*
* @param redissonClient Redisson的客户端对象。
* @param dictionaryName 字典表的名称。等同于redis hash对象的key。
* @param valueClazz 值对象的Class对象。
* @param idGetter 获取当前类主键字段值的函数对象。
* @param parentIdGetter 获取当前类父主键字段值的函数对象。
*/
public RedisTenantTreeDictionaryCache(
RedissonClient redissonClient,
String dictionaryName,
Class<V> valueClazz,
Function<V, K> idGetter,
Function<V, K> parentIdGetter) {
super(redissonClient, dictionaryName, valueClazz, idGetter);
this.parentIdGetter = parentIdGetter;
}
protected RListMultimap<K, String> getTenantTreeDataMap() {
Long tenantId = TokenData.takeFromRequest().getTenantId();
StringBuilder s = new StringBuilder(64);
s.append(dictionaryName).append("-")
.append(tenantId).append(ApplicationConstant.TREE_DICT_CACHE_NAME_SUFFIX);
return redissonClient.getListMultimap(s.toString());
}
/**
* 获取该父主键的子数据列表。
*
* @param parentId 父主键Id。如果parentId为null则返回所有一级节点数据。
* @return 子数据列表。
*/
public List<V> getListByParentId(K parentId) {
List<String> dataList;
String exceptionMessage;
Long tenantId = TokenData.takeFromRequest().getTenantId();
try {
if (lock.readLock().tryLock(TIMEOUT, TimeUnit.MILLISECONDS)) {
try {
dataList = this.getTenantTreeDataMap().get(parentId);
} finally {
lock.readLock().unlock();
}
} else {
throw new TimeoutException();
}
} catch (Exception e) {
exceptionMessage = String.format(
"LOCK Operation of [RedisTreeDictionaryCache::getListByParentId] encountered EXCEPTION [%s] for DICT [%s].",
e.getClass().getSimpleName(), valueClazz.getSimpleName());
log.warn(exceptionMessage);
throw new RedisCacheAccessException(exceptionMessage, e);
}
if (CollectionUtils.isEmpty(dataList)) {
return new LinkedList<>();
}
List<V> resultList = new LinkedList<>();
dataList.forEach(data -> resultList.add(JSON.parseObject(data, valueClazz)));
return resultList;
}
/**
* 将参数List中的数据保存到缓存中同时保证getAll返回的数据列表与参数列表中数据项的顺序保持一致。
*
* @param dataList 待缓存的数据列表。
*/
@Override
public void putAll(List<V> dataList) {
if (CollectionUtils.isEmpty(dataList)) {
return;
}
// 锁外执行数据结构组装,降低锁的粒度,提高并发性。
Map<K, String> map = dataList.stream()
.collect(Collectors.toMap(idGetter, JSON::toJSONString));
Multimap<K, String> treeMap = LinkedListMultimap.create();
for (V data : dataList) {
treeMap.put(parentIdGetter.apply(data), JSON.toJSONString(data));
}
Set<Map.Entry<K, Collection<String>>> entries = treeMap.asMap().entrySet();
String exceptionMessage;
Long tenantId = TokenData.takeFromRequest().getTenantId();
try {
if (this.lock.writeLock().tryLock(TIMEOUT, TimeUnit.MILLISECONDS)) {
try {
this.getTenantDataMap().putAll(map, 1000);
RListMultimap<K, String> allTenantTreeMap = this.getTenantTreeDataMap();
for (Map.Entry<K, Collection<String>> entry : entries) {
allTenantTreeMap.removeAll(entry.getKey());
allTenantTreeMap.putAll(entry.getKey(), entry.getValue());
}
} finally {
lock.writeLock().unlock();
}
} else {
throw new TimeoutException();
}
} catch (Exception e) {
exceptionMessage = String.format(
"LOCK Operation of [RedisTreeDictionaryCache::putAll] encountered EXCEPTION [%s] for DICT [%s].",
e.getClass().getSimpleName(), valueClazz.getSimpleName());
log.warn(exceptionMessage);
throw new RedisCacheAccessException(exceptionMessage, e);
}
}
/**
* 将数据存入缓存。
*
* @param id 通常为字典数据的主键。
* @param data 字典数据对象。
*/
@Override
public void put(K id, V data) {
if (id == null || data == null) {
return;
}
String stringData = JSON.toJSONString(data);
K parentId = parentIdGetter.apply(data);
String exceptionMessage;
Long tenantId = TokenData.takeFromRequest().getTenantId();
try {
if (this.lock.writeLock().tryLock(TIMEOUT, TimeUnit.MILLISECONDS)) {
try {
RMap<K, String> tenantDataMap = this.getTenantDataMap();
String oldData = tenantDataMap.put(id, stringData);
if (oldData != null) {
tenantDataMap.remove(parentId, oldData);
}
getTenantDataMap().put(parentId, stringData);
} finally {
lock.writeLock().unlock();
}
} else {
throw new TimeoutException();
}
} catch (Exception e) {
exceptionMessage = String.format(
"LOCK Operation of [RedisTreeDictionaryCache::put] encountered EXCEPTION [%s] for DICT [%s].",
e.getClass().getSimpleName(), valueClazz.getSimpleName());
log.warn(exceptionMessage);
throw new RedisCacheAccessException(exceptionMessage, e);
}
}
/**
* 行为等同于接口中的描述。这里之所以重写是因为不确定redisson的读写锁
* 是否为可重入锁。
*
* @param dataList 待缓存的数据列表。
* @param force true则强制刷新如果false当缓存中存在数据时不刷新。
*/
@Override
public void reload(List<V> dataList, boolean force) {
// 锁外执行数据结构组装,降低锁的粒度,提高并发性。
Map<K, String> map = null;
Set<Map.Entry<K, Collection<String>>> entries = null;
if (CollectionUtils.isNotEmpty(dataList)) {
map = dataList.stream().collect(Collectors.toMap(idGetter, JSON::toJSONString));
Multimap<K, String> treeMap = LinkedListMultimap.create();
for (V data : dataList) {
treeMap.put(parentIdGetter.apply(data), JSON.toJSONString(data));
}
entries = treeMap.asMap().entrySet();
}
String exceptionMessage;
Long tenantId = TokenData.takeFromRequest().getTenantId();
try {
if (lock.writeLock().tryLock(TIMEOUT, TimeUnit.MILLISECONDS)) {
try {
// 如果不强制刷新,需要先判断缓存中是否存在数据。
if (!force && this.getCount() > 0) {
return;
}
RMap<K, String> tenantDataMap = this.getTenantDataMap();
tenantDataMap.clear();
RListMultimap<K, String> allTenantTreeMap = this.getTenantTreeDataMap();
allTenantTreeMap.clear();
if (map != null) {
tenantDataMap.putAll(map, 1000);
for (Map.Entry<K, Collection<String>> entry : entries) {
allTenantTreeMap.removeAll(entry.getKey());
allTenantTreeMap.putAll(entry.getKey(), entry.getValue());
}
}
} finally {
lock.writeLock().unlock();
}
} else {
throw new TimeoutException();
}
} catch (Exception e) {
exceptionMessage = String.format(
"LOCK Operation of [RedisDictionaryCache::reload] encountered EXCEPTION [%s] for DICT [%s].",
e.getClass().getSimpleName(), valueClazz.getSimpleName());
log.warn(exceptionMessage);
throw new RedisCacheAccessException(exceptionMessage, e);
}
}
/**
* 删除缓存中指定的键。
*
* @param id 待删除数据的主键。
* @return 返回被删除的对象如果主键不存在返回null。
*/
@Override
public V invalidate(K id) {
if (id == null) {
return null;
}
V data = null;
String exceptionMessage;
Long tenantId = TokenData.takeFromRequest().getTenantId();
try {
if (this.lock.writeLock().tryLock(TIMEOUT, TimeUnit.MILLISECONDS)) {
try {
String stringData = this.getTenantDataMap().remove(id);
if (stringData != null) {
data = JSON.parseObject(stringData, valueClazz);
K parentId = parentIdGetter.apply(data);
this.getTenantTreeDataMap().remove(parentId, stringData);
}
} finally {
lock.writeLock().unlock();
}
} else {
throw new TimeoutException();
}
} catch (Exception e) {
exceptionMessage = String.format(
"LOCK Operation of [RedisTreeDictionaryCache::invalidate] encountered EXCEPTION [%s] for DICT [%s].",
e.getClass().getSimpleName(), valueClazz.getSimpleName());
log.warn(exceptionMessage);
throw new RedisCacheAccessException(exceptionMessage, e);
}
return data;
}
/**
* 删除缓存中,参数列表中包含的键。
*
* @param keys 待删除数据的主键集合。
*/
@Override
public void invalidateSet(Set<K> keys) {
if (CollectionUtils.isEmpty(keys)) {
return;
}
String exceptionMessage;
Long tenantId = TokenData.takeFromRequest().getTenantId();
try {
if (lock.writeLock().tryLock(TIMEOUT, TimeUnit.MILLISECONDS)) {
try {
keys.forEach(id -> {
if (id != null) {
String stringData = this.getTenantDataMap().remove(id);
if (stringData != null) {
K parentId = parentIdGetter.apply(JSON.parseObject(stringData, valueClazz));
this.getTenantTreeDataMap().remove(parentId, stringData);
}
}
});
} finally {
lock.writeLock().unlock();
}
} else {
throw new TimeoutException();
}
} catch (Exception e) {
exceptionMessage = String.format(
"LOCK Operation of [RedisTreeDictionaryCache::invalidateSet] encountered EXCEPTION [%s] for DICT [%s].",
e.getClass().getSimpleName(), valueClazz.getSimpleName());
log.warn(exceptionMessage);
throw new RedisCacheAccessException(exceptionMessage, e);
}
}
/**
* 清空缓存。
*/
@Override
public void invalidateAll() {
String exceptionMessage;
Long tenantId = TokenData.takeFromRequest().getTenantId();
try {
if (lock.writeLock().tryLock(TIMEOUT, TimeUnit.MILLISECONDS)) {
try {
this.getTenantDataMap().clear();
this.getTenantTreeDataMap().clear();
} finally {
lock.writeLock().unlock();
}
} else {
throw new TimeoutException();
}
} catch (Exception e) {
exceptionMessage = String.format(
"LOCK Operation of [RedisTreeDictionaryCache::invalidateAll] encountered EXCEPTION [%s] for DICT [%s].",
e.getClass().getSimpleName(), valueClazz.getSimpleName());
log.warn(exceptionMessage);
throw new RedisCacheAccessException(exceptionMessage, e);
}
}
}

View File

@@ -0,0 +1,354 @@
package com.orange.demo.common.redis.cache;
import com.alibaba.fastjson.JSON;
import lombok.extern.slf4j.Slf4j;
import com.orange.demo.common.core.constant.ApplicationConstant;
import com.orange.demo.common.core.exception.RedisCacheAccessException;
import com.google.common.collect.LinkedListMultimap;
import com.google.common.collect.Multimap;
import org.apache.commons.collections4.CollectionUtils;
import org.redisson.api.RListMultimap;
import org.redisson.api.RedissonClient;
import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
import java.util.function.Function;
import java.util.stream.Collectors;
/**
* 树形字典数据Redis缓存对象。
*
* @param <K> 字典表主键类型。
* @param <V> 字典表对象类型。
* @author Jerry
* @date 2020-08-08
*/
@Slf4j
public class RedisTreeDictionaryCache<K, V> extends RedisDictionaryCache<K, V> {
/**
* 树形数据存储对象。
*/
private final RListMultimap<K, String> allTreeMap;
/**
* 获取字典父主键数据的函数对象。
*/
protected final Function<V, K> parentIdGetter;
/**
* 当前对象的构造器函数。
*
* @param redissonClient Redisson的客户端对象。
* @param dictionaryName 字典表的名称。等同于redis hash对象的key。
* @param valueClazz 值对象的Class对象。
* @param idGetter 获取当前类主键字段值的函数对象。
* @param parentIdGetter 获取当前类父主键字段值的函数对象。
* @param <K> 字典主键类型。
* @param <V> 字典对象类型
* @return 实例化后的树形字典内存缓存对象。
*/
public static <K, V> RedisTreeDictionaryCache<K, V> create(
RedissonClient redissonClient,
String dictionaryName,
Class<V> valueClazz,
Function<V, K> idGetter,
Function<V, K> parentIdGetter) {
if (idGetter == null) {
throw new IllegalArgumentException("IdGetter can't be NULL.");
}
if (parentIdGetter == null) {
throw new IllegalArgumentException("ParentIdGetter can't be NULL.");
}
return new RedisTreeDictionaryCache<>(
redissonClient, dictionaryName, valueClazz, idGetter, parentIdGetter);
}
/**
* 构造函数。
*
* @param redissonClient Redisson的客户端对象。
* @param dictionaryName 字典表的名称。等同于redis hash对象的key。
* @param valueClazz 值对象的Class对象。
* @param idGetter 获取当前类主键字段值的函数对象。
* @param parentIdGetter 获取当前类父主键字段值的函数对象。
*/
public RedisTreeDictionaryCache(
RedissonClient redissonClient,
String dictionaryName,
Class<V> valueClazz,
Function<V, K> idGetter,
Function<V, K> parentIdGetter) {
super(redissonClient, dictionaryName, valueClazz, idGetter);
this.allTreeMap = redissonClient.getListMultimap(
dictionaryName + ApplicationConstant.TREE_DICT_CACHE_NAME_SUFFIX);
this.parentIdGetter = parentIdGetter;
}
/**
* 获取该父主键的子数据列表。
*
* @param parentId 父主键Id。如果parentId为null则返回所有一级节点数据。
* @return 子数据列表。
*/
public List<V> getListByParentId(K parentId) {
List<String> dataList;
String exceptionMessage;
try {
if (lock.readLock().tryLock(TIMEOUT, TimeUnit.MILLISECONDS)) {
try {
dataList = allTreeMap.get(parentId);
} finally {
lock.readLock().unlock();
}
} else {
throw new TimeoutException();
}
} catch (Exception e) {
exceptionMessage = String.format(
"LOCK Operation of [RedisTreeDictionaryCache::getListByParentId] encountered EXCEPTION [%s] for DICT [%s].",
e.getClass().getSimpleName(), valueClazz.getSimpleName());
log.warn(exceptionMessage);
throw new RedisCacheAccessException(exceptionMessage, e);
}
if (CollectionUtils.isEmpty(dataList)) {
return new LinkedList<>();
}
List<V> resultList = new LinkedList<>();
dataList.forEach(data -> resultList.add(JSON.parseObject(data, valueClazz)));
return resultList;
}
/**
* 将参数List中的数据保存到缓存中同时保证getAll返回的数据列表与参数列表中数据项的顺序保持一致。
*
* @param dataList 待缓存的数据列表。
*/
@Override
public void putAll(List<V> dataList) {
if (CollectionUtils.isEmpty(dataList)) {
return;
}
// 锁外执行数据结构组装,降低锁的粒度,提高并发性。
Map<K, String> map = dataList.stream()
.collect(Collectors.toMap(idGetter, JSON::toJSONString));
Multimap<K, String> treeMap = LinkedListMultimap.create();
for (V data : dataList) {
treeMap.put(parentIdGetter.apply(data), JSON.toJSONString(data));
}
Set<Map.Entry<K, Collection<String>>> entries = treeMap.asMap().entrySet();
String exceptionMessage;
try {
if (this.lock.writeLock().tryLock(TIMEOUT, TimeUnit.MILLISECONDS)) {
try {
dataMap.putAll(map, 1000);
for (Map.Entry<K, Collection<String>> entry : entries) {
allTreeMap.removeAll(entry.getKey());
allTreeMap.putAll(entry.getKey(), entry.getValue());
}
} finally {
lock.writeLock().unlock();
}
} else {
throw new TimeoutException();
}
} catch (Exception e) {
exceptionMessage = String.format(
"LOCK Operation of [RedisTreeDictionaryCache::putAll] encountered EXCEPTION [%s] for DICT [%s].",
e.getClass().getSimpleName(), valueClazz.getSimpleName());
log.warn(exceptionMessage);
throw new RedisCacheAccessException(exceptionMessage, e);
}
}
/**
* 将数据存入缓存。
*
* @param id 通常为字典数据的主键。
* @param data 字典数据对象。
*/
@Override
public void put(K id, V data) {
if (id == null || data == null) {
return;
}
String stringData = JSON.toJSONString(data);
K parentId = parentIdGetter.apply(data);
String exceptionMessage;
try {
if (this.lock.writeLock().tryLock(TIMEOUT, TimeUnit.MILLISECONDS)) {
try {
String oldData = dataMap.put(id, stringData);
if (oldData != null) {
allTreeMap.remove(parentId, oldData);
}
allTreeMap.put(parentId, stringData);
} finally {
lock.writeLock().unlock();
}
} else {
throw new TimeoutException();
}
} catch (Exception e) {
exceptionMessage = String.format(
"LOCK Operation of [RedisTreeDictionaryCache::put] encountered EXCEPTION [%s] for DICT [%s].",
e.getClass().getSimpleName(), valueClazz.getSimpleName());
log.warn(exceptionMessage);
throw new RedisCacheAccessException(exceptionMessage, e);
}
}
/**
* 行为等同于接口中的描述。这里之所以重写是因为不确定redisson的读写锁
* 是否为可重入锁。
*
* @param dataList 待缓存的数据列表。
* @param force true则强制刷新如果false当缓存中存在数据时不刷新。
*/
@Override
public void reload(List<V> dataList, boolean force) {
// 锁外执行数据结构组装,降低锁的粒度,提高并发性。
Map<K, String> map = null;
Set<Map.Entry<K, Collection<String>>> entries = null;
if (CollectionUtils.isNotEmpty(dataList)) {
map = dataList.stream().collect(Collectors.toMap(idGetter, JSON::toJSONString));
Multimap<K, String> treeMap = LinkedListMultimap.create();
for (V data : dataList) {
treeMap.put(parentIdGetter.apply(data), JSON.toJSONString(data));
}
entries = treeMap.asMap().entrySet();
}
String exceptionMessage;
try {
if (lock.writeLock().tryLock(TIMEOUT, TimeUnit.MILLISECONDS)) {
try {
// 如果不强制刷新,需要先判断缓存中是否存在数据。
if (!force && this.getCount() > 0) {
return;
}
dataMap.clear();
allTreeMap.clear();
if (map != null) {
dataMap.putAll(map, 1000);
for (Map.Entry<K, Collection<String>> entry : entries) {
allTreeMap.removeAll(entry.getKey());
allTreeMap.putAll(entry.getKey(), entry.getValue());
}
}
} finally {
lock.writeLock().unlock();
}
} else {
throw new TimeoutException();
}
} catch (Exception e) {
exceptionMessage = String.format(
"LOCK Operation of [RedisDictionaryCache::reload] encountered EXCEPTION [%s] for DICT [%s].",
e.getClass().getSimpleName(), valueClazz.getSimpleName());
log.warn(exceptionMessage);
throw new RedisCacheAccessException(exceptionMessage, e);
}
}
/**
* 删除缓存中指定的键。
*
* @param id 待删除数据的主键。
* @return 返回被删除的对象如果主键不存在返回null。
*/
@Override
public V invalidate(K id) {
if (id == null) {
return null;
}
V data = null;
String exceptionMessage;
try {
if (this.lock.writeLock().tryLock(TIMEOUT, TimeUnit.MILLISECONDS)) {
try {
String stringData = dataMap.remove(id);
if (stringData != null) {
data = JSON.parseObject(stringData, valueClazz);
K parentId = parentIdGetter.apply(data);
allTreeMap.remove(parentId, stringData);
}
} finally {
lock.writeLock().unlock();
}
} else {
throw new TimeoutException();
}
} catch (Exception e) {
exceptionMessage = String.format(
"LOCK Operation of [RedisTreeDictionaryCache::invalidate] encountered EXCEPTION [%s] for DICT [%s].",
e.getClass().getSimpleName(), valueClazz.getSimpleName());
log.warn(exceptionMessage);
throw new RedisCacheAccessException(exceptionMessage, e);
}
return data;
}
/**
* 删除缓存中,参数列表中包含的键。
*
* @param keys 待删除数据的主键集合。
*/
@Override
public void invalidateSet(Set<K> keys) {
if (CollectionUtils.isEmpty(keys)) {
return;
}
String exceptionMessage;
try {
if (lock.writeLock().tryLock(TIMEOUT, TimeUnit.MILLISECONDS)) {
try {
keys.forEach(id -> {
if (id != null) {
String stringData = dataMap.remove(id);
if (stringData != null) {
K parentId = parentIdGetter.apply(JSON.parseObject(stringData, valueClazz));
allTreeMap.remove(parentId, stringData);
}
}
});
} finally {
lock.writeLock().unlock();
}
} else {
throw new TimeoutException();
}
} catch (Exception e) {
exceptionMessage = String.format(
"LOCK Operation of [RedisTreeDictionaryCache::invalidateSet] encountered EXCEPTION [%s] for DICT [%s].",
e.getClass().getSimpleName(), valueClazz.getSimpleName());
log.warn(exceptionMessage);
throw new RedisCacheAccessException(exceptionMessage, e);
}
}
/**
* 清空缓存。
*/
@Override
public void invalidateAll() {
String exceptionMessage;
try {
if (lock.writeLock().tryLock(TIMEOUT, TimeUnit.MILLISECONDS)) {
try {
dataMap.clear();
allTreeMap.clear();
} finally {
lock.writeLock().unlock();
}
} else {
throw new TimeoutException();
}
} catch (Exception e) {
exceptionMessage = String.format(
"LOCK Operation of [RedisTreeDictionaryCache::invalidateAll] encountered EXCEPTION [%s] for DICT [%s].",
e.getClass().getSimpleName(), valueClazz.getSimpleName());
log.warn(exceptionMessage);
throw new RedisCacheAccessException(exceptionMessage, e);
}
}
}

View File

@@ -0,0 +1,67 @@
package com.orange.demo.common.redis.cache;
import com.google.common.collect.Maps;
import org.redisson.api.RedissonClient;
import org.redisson.spring.cache.CacheConfig;
import org.redisson.spring.cache.RedissonSpringCacheManager;
import org.springframework.cache.CacheManager;
import org.springframework.cache.annotation.EnableCaching;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import java.util.Map;
/**
* 使用Redisson作为Redis的分布式缓存库。
*
* @author Jerry
* @date 2020-08-08
*/
@Configuration
@EnableCaching
public class RedissonCacheConfig {
private static final int DEFAULT_TTL = 3600000;
/**
* 定义cache名称、超时时长(毫秒)。
*/
public enum CacheEnum {
/**
* session下上传文件名的缓存(时间是24小时)。
*/
UPLOAD_FILENAME_CACHE(86400000),
/**
* 缺省全局缓存(时间是24小时)。
*/
GLOBAL_CACHE(86400000);
/**
* 缓存的时长(单位:毫秒)
*/
private int ttl = DEFAULT_TTL;
CacheEnum() {
}
CacheEnum(int ttl) {
this.ttl = ttl;
}
public int getTtl() {
return ttl;
}
}
/**
* 初始化缓存配置。
*/
@Bean
CacheManager cacheManager(RedissonClient redissonClient) {
Map<String, CacheConfig> config = Maps.newHashMap();
for (CacheEnum c : CacheEnum.values()) {
config.put(c.name(), new CacheConfig(c.getTtl(), 0));
}
return new RedissonSpringCacheManager(redissonClient, config);
}
}

View File

@@ -0,0 +1,74 @@
package com.orange.demo.common.redis.cache;
import com.orange.demo.common.core.object.TokenData;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cache.Cache;
import org.springframework.cache.CacheManager;
import org.springframework.stereotype.Component;
import java.util.HashSet;
import java.util.Set;
/**
* Session数据缓存辅助类。
*
* @author Jerry
* @date 2020-08-08
*/
@SuppressWarnings("unchecked")
@Component
public class SessionCacheHelper {
@Autowired
private CacheManager cacheManager;
/**
* 缓存当前session内上传过的文件名。
*
* @param filename 通常是本地存储的文件名,而不是上传时的原始文件名。
*/
public void putSessionUploadFile(String filename) {
if (filename != null) {
Set<String> sessionUploadFileSet = null;
Cache cache = cacheManager.getCache(RedissonCacheConfig.CacheEnum.UPLOAD_FILENAME_CACHE.name());
Cache.ValueWrapper valueWrapper = cache.get(TokenData.takeFromRequest().getSessionId());
if (valueWrapper != null) {
sessionUploadFileSet = (Set<String>) valueWrapper.get();
}
if (sessionUploadFileSet == null) {
sessionUploadFileSet = new HashSet<>();
}
sessionUploadFileSet.add(filename);
cache.put(TokenData.takeFromRequest().getSessionId(), sessionUploadFileSet);
}
}
/**
* 判断参数中的文件名是否有当前session上传。
*
* @param filename 通常是本地存储的文件名,而不是上传时的原始文件名。
* @return true表示该文件是由当前session上传并存储在本地的否则false。
*/
public boolean existSessionUploadFile(String filename) {
if (filename == null) {
return false;
}
Cache cache = cacheManager.getCache(RedissonCacheConfig.CacheEnum.UPLOAD_FILENAME_CACHE.name());
Cache.ValueWrapper valueWrapper = cache.get(TokenData.takeFromRequest().getSessionId());
if (valueWrapper == null) {
return false;
}
return ((Set<String>) valueWrapper.get()).contains(filename);
}
/**
* 清除当前session的所有缓存数据。
*
* @param sessionId 当前会话的SessionId。
*/
public void removeAllSessionCache(String sessionId) {
for (RedissonCacheConfig.CacheEnum c : RedissonCacheConfig.CacheEnum.values()) {
cacheManager.getCache(c.name()).evict(sessionId);
}
}
}

View File

@@ -0,0 +1,102 @@
package com.orange.demo.common.redis.config;
import cn.hutool.core.util.ArrayUtil;
import cn.hutool.core.util.StrUtil;
import com.orange.demo.common.core.exception.InvalidRedisModeException;
import org.redisson.Redisson;
import org.redisson.api.RedissonClient;
import org.redisson.config.Config;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
/**
* Redisson配置类。
*
* @author Jerry
* @date 2020-08-08
*/
@Configuration
@ConditionalOnProperty(name = "redis.redisson.enabled", havingValue = "true")
public class RedissonConfig {
@Value("${redis.redisson.lockWatchdogTimeout}")
private Integer lockWatchdogTimeout;
@Value("${redis.redisson.mode}")
private String mode;
/**
* 仅仅用于sentinel模式。
*/
@Value("${redis.redisson.masterName:}")
private String masterName;
@Value("${redis.redisson.address}")
private String address;
@Value("${redis.redisson.timeout}")
private Integer timeout;
@Value("${redis.redisson.password:}")
private String password;
@Value("${redis.redisson.pool.poolSize}")
private Integer poolSize;
@Value("${redis.redisson.pool.minIdle}")
private Integer minIdle;
@Bean
public RedissonClient redissonClient() {
if (StrUtil.isBlank(password)) {
password = null;
}
Config config = new Config();
if ("single".equals(mode)) {
config.setLockWatchdogTimeout(lockWatchdogTimeout)
.useSingleServer()
.setPassword(password)
.setAddress(address)
.setConnectionPoolSize(poolSize)
.setConnectionMinimumIdleSize(minIdle)
.setConnectTimeout(timeout);
} else if ("cluster".equals(mode)) {
String[] clusterAddresses = StrUtil.splitToArray(address, ',');
config.setLockWatchdogTimeout(lockWatchdogTimeout)
.useClusterServers()
.setPassword(password)
.addNodeAddress(clusterAddresses)
.setConnectTimeout(timeout)
.setMasterConnectionPoolSize(poolSize);
} else if ("sentinel".equals(mode)) {
String[] sentinelAddresses = StrUtil.splitToArray(address, ',');
config.setLockWatchdogTimeout(lockWatchdogTimeout)
.useSentinelServers()
.setPassword(password)
.setMasterName(masterName)
.addSentinelAddress(sentinelAddresses)
.setConnectTimeout(timeout)
.setMasterConnectionPoolSize(poolSize);
} else if ("master-slave".equals(mode)) {
String[] masterSlaveAddresses = StrUtil.splitToArray(address, ',');
if (masterSlaveAddresses.length == 1) {
throw new IllegalArgumentException(
"redis.redisson.address MUST have multiple redis addresses for master-slave mode.");
}
String[] slaveAddresses = new String[masterSlaveAddresses.length - 1];
ArrayUtil.copy(masterSlaveAddresses, 1, slaveAddresses, 0, slaveAddresses.length);
config.setLockWatchdogTimeout(lockWatchdogTimeout)
.useMasterSlaveServers()
.setPassword(password)
.setMasterAddress(masterSlaveAddresses[0])
.addSlaveAddress(slaveAddresses)
.setConnectTimeout(timeout)
.setMasterConnectionPoolSize(poolSize);
} else {
throw new InvalidRedisModeException(mode);
}
return Redisson.create(config);
}
}

View File

@@ -0,0 +1,2 @@
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
com.orange.demo.common.redis.config.RedissonConfig