Initial commit
This commit is contained in:
52
lingniu-framework-plugin/cache/lingniu-framework-plugin-jetcache/pom.xml
vendored
Normal file
52
lingniu-framework-plugin/cache/lingniu-framework-plugin-jetcache/pom.xml
vendored
Normal file
@@ -0,0 +1,52 @@
|
||||
<?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>
|
||||
<parent>
|
||||
<groupId>cn.lingniu.framework</groupId>
|
||||
<artifactId>lingniu-framework-dependencies</artifactId>
|
||||
<version>1.0.0-SNAPSHOT</version>
|
||||
<relativePath>../../../lingniu-framework-dependencies/pom.xml</relativePath>
|
||||
</parent>
|
||||
<artifactId>lingniu-framework-plugin-jetcache</artifactId>
|
||||
<name>lingniu-framework-plugin-jetcache</name>
|
||||
<url>http://maven.apache.org</url>
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>cn.lingniu.framework</groupId>
|
||||
<artifactId>lingniu-framework-plugin-core</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.redisson</groupId>
|
||||
<artifactId>redisson</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-aop</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>com.alicp.jetcache</groupId>
|
||||
<artifactId>jetcache-anno</artifactId>
|
||||
<exclusions>
|
||||
<exclusion>
|
||||
<groupId>com.alibaba</groupId>
|
||||
<artifactId>fastjson</artifactId>
|
||||
</exclusion>
|
||||
</exclusions>
|
||||
</dependency>
|
||||
|
||||
|
||||
<dependency>
|
||||
<groupId>com.alibaba</groupId>
|
||||
<artifactId>fastjson</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.google.guava</groupId>
|
||||
<artifactId>guava</artifactId>
|
||||
</dependency>
|
||||
|
||||
</dependencies>
|
||||
</project>
|
||||
@@ -0,0 +1,95 @@
|
||||
package cn.lingniu.framework.plugin.jetcache.autoconfigure;
|
||||
|
||||
import com.alicp.jetcache.AbstractCacheBuilder;
|
||||
import com.alicp.jetcache.CacheBuilder;
|
||||
import com.alicp.jetcache.anno.KeyConvertor;
|
||||
import com.alicp.jetcache.anno.support.ParserFunction;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.beans.factory.InitializingBean;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.core.env.ConfigurableEnvironment;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* Created on 2016/11/29.
|
||||
*
|
||||
* @author <a href="mailto:areyouok@gmail.com">huangli</a>
|
||||
*/
|
||||
public abstract class AbstractCacheAutoInit implements InitializingBean {
|
||||
|
||||
private static Logger logger = LoggerFactory.getLogger(AbstractCacheAutoInit.class);
|
||||
|
||||
@Autowired
|
||||
protected ConfigurableEnvironment environment;
|
||||
|
||||
@Autowired
|
||||
protected AutoConfigureBeans autoConfigureBeans;
|
||||
|
||||
protected String[] typeNames;
|
||||
|
||||
private volatile boolean inited = false;
|
||||
|
||||
public AbstractCacheAutoInit(String... cacheTypes) {
|
||||
Objects.requireNonNull(cacheTypes, "cacheTypes can't be null");
|
||||
Assert.isTrue(cacheTypes.length > 0, "cacheTypes length is 0");
|
||||
this.typeNames = cacheTypes;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void afterPropertiesSet() {
|
||||
if (!inited) {
|
||||
synchronized (this) {
|
||||
if (!inited) {
|
||||
process("framework.lingniu.jetcache.local.", autoConfigureBeans.getLocalCacheBuilders(), true);
|
||||
process("framework.lingniu.jetcache.remote.", autoConfigureBeans.getRemoteCacheBuilders(), false);
|
||||
inited = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void process(String prefix, Map cacheBuilders, boolean local) {
|
||||
ConfigTree resolver = new ConfigTree(environment, prefix);
|
||||
Map<String, Object> m = resolver.getProperties();
|
||||
Set<String> cacheAreaNames = resolver.directChildrenKeys();
|
||||
for (String cacheArea : cacheAreaNames) {
|
||||
final Object configType = m.get(cacheArea + ".type");
|
||||
boolean match = Arrays.stream(typeNames).anyMatch((tn) -> tn.equals(configType));
|
||||
if (!match) {
|
||||
continue;
|
||||
}
|
||||
ConfigTree ct = resolver.subTree(cacheArea + ".");
|
||||
logger.info("init cache area {} , type= {}", cacheArea, typeNames[0]);
|
||||
CacheBuilder c = initCache(ct, local ? "local." + cacheArea : "remote." + cacheArea);
|
||||
cacheBuilders.put(cacheArea, c);
|
||||
}
|
||||
}
|
||||
|
||||
protected void parseGeneralConfig(CacheBuilder builder, ConfigTree ct) {
|
||||
AbstractCacheBuilder acb = (AbstractCacheBuilder) builder;
|
||||
acb.keyConvertor(new ParserFunction(ct.getProperty("keyConvertor", KeyConvertor.FASTJSON2)));
|
||||
|
||||
String expireAfterWriteInMillis = ct.getProperty("expireAfterWriteInMillis");
|
||||
if (expireAfterWriteInMillis == null) {
|
||||
// compatible with 2.1
|
||||
expireAfterWriteInMillis = ct.getProperty("defaultExpireInMillis");
|
||||
}
|
||||
if (expireAfterWriteInMillis != null) {
|
||||
acb.setExpireAfterWriteInMillis(Long.parseLong(expireAfterWriteInMillis));
|
||||
}
|
||||
|
||||
String expireAfterAccessInMillis = ct.getProperty("expireAfterAccessInMillis");
|
||||
if (expireAfterAccessInMillis != null) {
|
||||
acb.setExpireAfterAccessInMillis(Long.parseLong(expireAfterAccessInMillis));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
protected abstract CacheBuilder initCache(ConfigTree ct, String cacheAreaWithPrefix);
|
||||
}
|
||||
@@ -0,0 +1,45 @@
|
||||
package cn.lingniu.framework.plugin.jetcache.autoconfigure;
|
||||
|
||||
import com.alicp.jetcache.CacheBuilder;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* Created on 2016/12/28.
|
||||
*
|
||||
* @author <a href="mailto:areyouok@gmail.com">huangli</a>
|
||||
*/
|
||||
public class AutoConfigureBeans {
|
||||
|
||||
private Map<String, CacheBuilder> localCacheBuilders = new HashMap<>();
|
||||
|
||||
private Map<String, CacheBuilder> remoteCacheBuilders = new HashMap<>();
|
||||
|
||||
private Map<String, Object> customContainer = Collections.synchronizedMap(new HashMap<>());
|
||||
|
||||
public Map<String, CacheBuilder> getLocalCacheBuilders() {
|
||||
return localCacheBuilders;
|
||||
}
|
||||
|
||||
public void setLocalCacheBuilders(Map<String, CacheBuilder> localCacheBuilders) {
|
||||
this.localCacheBuilders = localCacheBuilders;
|
||||
}
|
||||
|
||||
public Map<String, CacheBuilder> getRemoteCacheBuilders() {
|
||||
return remoteCacheBuilders;
|
||||
}
|
||||
|
||||
public void setRemoteCacheBuilders(Map<String, CacheBuilder> remoteCacheBuilders) {
|
||||
this.remoteCacheBuilders = remoteCacheBuilders;
|
||||
}
|
||||
|
||||
public Map<String, Object> getCustomContainer() {
|
||||
return customContainer;
|
||||
}
|
||||
|
||||
public void setCustomContainer(Map<String, Object> customContainer) {
|
||||
this.customContainer = customContainer;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
package cn.lingniu.framework.plugin.jetcache.autoconfigure;
|
||||
|
||||
import org.springframework.beans.BeansException;
|
||||
import org.springframework.beans.factory.config.BeanDefinition;
|
||||
import org.springframework.beans.factory.config.BeanFactoryPostProcessor;
|
||||
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
|
||||
|
||||
import java.util.Arrays;
|
||||
|
||||
/**
|
||||
* Created on 2017/5/5.
|
||||
*
|
||||
* @author <a href="mailto:areyouok@gmail.com">huangli</a>
|
||||
*/
|
||||
public class BeanDependencyManager implements BeanFactoryPostProcessor {
|
||||
|
||||
@Override
|
||||
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
|
||||
String[] autoInitBeanNames = beanFactory.getBeanNamesForType(AbstractCacheAutoInit.class, false, false);
|
||||
if (autoInitBeanNames != null) {
|
||||
BeanDefinition bd = beanFactory.getBeanDefinition(JetCacheAutoConfiguration.GLOBAL_CACHE_CONFIG_NAME);
|
||||
String[] dependsOn = bd.getDependsOn();
|
||||
if (dependsOn == null) {
|
||||
dependsOn = new String[0];
|
||||
}
|
||||
int oldLen = dependsOn.length;
|
||||
dependsOn = Arrays.copyOf(dependsOn, dependsOn.length + autoInitBeanNames.length);
|
||||
System.arraycopy(autoInitBeanNames, 0, dependsOn, oldLen, autoInitBeanNames.length);
|
||||
bd.setDependsOn(dependsOn);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
package cn.lingniu.framework.plugin.jetcache.autoconfigure;
|
||||
|
||||
import com.alicp.jetcache.CacheBuilder;
|
||||
import com.alicp.jetcache.embedded.CaffeineCacheBuilder;
|
||||
import org.springframework.context.annotation.Conditional;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
/**
|
||||
* Created on 2016/12/2.
|
||||
*
|
||||
* @author <a href="mailto:areyouok@gmail.com">huangli</a>
|
||||
*/
|
||||
@Component
|
||||
@Conditional(CaffeineAutoConfiguration.CaffeineCondition.class)
|
||||
public class CaffeineAutoConfiguration extends EmbeddedCacheAutoInit {
|
||||
public CaffeineAutoConfiguration() {
|
||||
super("caffeine");
|
||||
}
|
||||
|
||||
@Override
|
||||
protected CacheBuilder initCache(ConfigTree ct, String cacheAreaWithPrefix) {
|
||||
CaffeineCacheBuilder builder = CaffeineCacheBuilder.createCaffeineCacheBuilder();
|
||||
parseGeneralConfig(builder, ct);
|
||||
return builder;
|
||||
}
|
||||
|
||||
public static class CaffeineCondition extends JetCacheCondition {
|
||||
public CaffeineCondition() {
|
||||
super("caffeine");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,106 @@
|
||||
package cn.lingniu.framework.plugin.jetcache.autoconfigure;
|
||||
|
||||
import org.springframework.core.env.ConfigurableEnvironment;
|
||||
import org.springframework.core.env.EnumerablePropertySource;
|
||||
import org.springframework.core.env.PropertySource;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* Created on 2017/11/20.
|
||||
*
|
||||
* @author <a href="mailto:areyouok@gmail.com">huangli</a>
|
||||
*/
|
||||
public class ConfigTree {
|
||||
private ConfigurableEnvironment environment;
|
||||
private String prefix;
|
||||
|
||||
public ConfigTree(ConfigurableEnvironment environment, String prefix) {
|
||||
Assert.notNull(environment, "environment is required");
|
||||
Assert.notNull(prefix, "prefix is required");
|
||||
this.environment = environment;
|
||||
this.prefix = prefix;
|
||||
}
|
||||
|
||||
public ConfigTree subTree(String prefix) {
|
||||
return new ConfigTree(environment, fullPrefixOrKey(prefix));
|
||||
}
|
||||
|
||||
private String fullPrefixOrKey(String prefixOrKey) {
|
||||
return this.prefix + prefixOrKey;
|
||||
}
|
||||
|
||||
public Map<String, Object> getProperties() {
|
||||
Map<String, Object> m = new HashMap<>();
|
||||
for (PropertySource<?> source : environment.getPropertySources()) {
|
||||
if (source instanceof EnumerablePropertySource) {
|
||||
for (String name : ((EnumerablePropertySource<?>) source)
|
||||
.getPropertyNames()) {
|
||||
if (name != null && name.startsWith(prefix)) {
|
||||
String subKey = name.substring(prefix.length());
|
||||
m.put(subKey, environment.getProperty(name));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return m;
|
||||
}
|
||||
|
||||
public boolean containsProperty(String key) {
|
||||
key = fullPrefixOrKey(key);
|
||||
return environment.containsProperty(key);
|
||||
}
|
||||
|
||||
public String getProperty(String key) {
|
||||
key = fullPrefixOrKey(key);
|
||||
return environment.getProperty(key);
|
||||
}
|
||||
|
||||
public String getProperty(String key, String defaultValue) {
|
||||
if (containsProperty(key)) {
|
||||
return getProperty(key);
|
||||
} else {
|
||||
return defaultValue;
|
||||
}
|
||||
}
|
||||
|
||||
public boolean getProperty(String key, boolean defaultValue) {
|
||||
if (containsProperty(key)) {
|
||||
return Boolean.parseBoolean(getProperty(key));
|
||||
} else {
|
||||
return defaultValue;
|
||||
}
|
||||
}
|
||||
|
||||
public int getProperty(String key, int defaultValue) {
|
||||
if (containsProperty(key)) {
|
||||
return Integer.parseInt(getProperty(key));
|
||||
} else {
|
||||
return defaultValue;
|
||||
}
|
||||
}
|
||||
|
||||
public long getProperty(String key, long defaultValue) {
|
||||
if (containsProperty(key)) {
|
||||
return Long.parseLong(getProperty(key));
|
||||
} else {
|
||||
return defaultValue;
|
||||
}
|
||||
}
|
||||
|
||||
public String getPrefix() {
|
||||
return prefix;
|
||||
}
|
||||
|
||||
public Set<String> directChildrenKeys() {
|
||||
Map<String, Object> m = getProperties();
|
||||
return m.keySet().stream().map(
|
||||
s -> s.indexOf('.') >= 0 ? s.substring(0, s.indexOf('.')) : null)
|
||||
.filter(s -> s != null)
|
||||
.collect(Collectors.toSet());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
package cn.lingniu.framework.plugin.jetcache.autoconfigure;
|
||||
|
||||
import com.alicp.jetcache.CacheBuilder;
|
||||
import com.alicp.jetcache.anno.CacheConsts;
|
||||
import com.alicp.jetcache.embedded.EmbeddedCacheBuilder;
|
||||
|
||||
/**
|
||||
* Created on 2016/12/2.
|
||||
*
|
||||
* @author <a href="mailto:areyouok@gmail.com">huangli</a>
|
||||
*/
|
||||
public abstract class EmbeddedCacheAutoInit extends AbstractCacheAutoInit {
|
||||
|
||||
public EmbeddedCacheAutoInit(String... cacheTypes) {
|
||||
super(cacheTypes);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void parseGeneralConfig(CacheBuilder builder, ConfigTree ct) {
|
||||
super.parseGeneralConfig(builder, ct);
|
||||
EmbeddedCacheBuilder ecb = (EmbeddedCacheBuilder) builder;
|
||||
|
||||
ecb.limit(Integer.parseInt(ct.getProperty("limit", String.valueOf(CacheConsts.DEFAULT_LOCAL_LIMIT))));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
package cn.lingniu.framework.plugin.jetcache.autoconfigure;
|
||||
|
||||
import com.alicp.jetcache.CacheBuilder;
|
||||
import com.alicp.jetcache.anno.CacheConsts;
|
||||
import com.alicp.jetcache.anno.support.ParserFunction;
|
||||
import com.alicp.jetcache.external.ExternalCacheBuilder;
|
||||
|
||||
/**
|
||||
* Created on 2016/11/29.
|
||||
*
|
||||
* @author <a href="mailto:areyouok@gmail.com">huangli</a>
|
||||
*/
|
||||
public abstract class ExternalCacheAutoInit extends AbstractCacheAutoInit {
|
||||
public ExternalCacheAutoInit(String... cacheTypes) {
|
||||
super(cacheTypes);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void parseGeneralConfig(CacheBuilder builder, ConfigTree ct) {
|
||||
super.parseGeneralConfig(builder, ct);
|
||||
ExternalCacheBuilder ecb = (ExternalCacheBuilder) builder;
|
||||
ecb.setKeyPrefix(ct.getProperty("keyPrefix"));
|
||||
ecb.setBroadcastChannel(parseBroadcastChannel(ct));
|
||||
ecb.setValueEncoder(new ParserFunction(ct.getProperty("valueEncoder", CacheConsts.DEFAULT_SERIAL_POLICY)));
|
||||
ecb.setValueDecoder(new ParserFunction(ct.getProperty("valueDecoder", CacheConsts.DEFAULT_SERIAL_POLICY)));
|
||||
}
|
||||
|
||||
protected String parseBroadcastChannel(ConfigTree ct) {
|
||||
String broadcastChannel = ct.getProperty("broadcastChannel");
|
||||
if (broadcastChannel != null && !"".equals(broadcastChannel.trim())) {
|
||||
return broadcastChannel.trim();
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,83 @@
|
||||
package cn.lingniu.framework.plugin.jetcache.autoconfigure;
|
||||
|
||||
import com.alicp.jetcache.CacheManager;
|
||||
import com.alicp.jetcache.SimpleCacheManager;
|
||||
import com.alicp.jetcache.anno.support.EncoderParser;
|
||||
import com.alicp.jetcache.anno.support.GlobalCacheConfig;
|
||||
import com.alicp.jetcache.anno.support.JetCacheBaseBeans;
|
||||
import com.alicp.jetcache.anno.support.KeyConvertorParser;
|
||||
import com.alicp.jetcache.anno.support.SpringConfigProvider;
|
||||
import com.alicp.jetcache.support.StatInfo;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
|
||||
import org.springframework.boot.context.properties.EnableConfigurationProperties;
|
||||
import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.context.annotation.Import;
|
||||
|
||||
import java.util.function.Consumer;
|
||||
|
||||
/**
|
||||
* Created on 2016/11/17.
|
||||
*
|
||||
* @author <a href="mailto:areyouok@gmail.com">huangli</a>
|
||||
*/
|
||||
@Configuration
|
||||
@ConditionalOnClass(GlobalCacheConfig.class)
|
||||
@ConditionalOnMissingBean(GlobalCacheConfig.class)
|
||||
@EnableConfigurationProperties(JetCacheProperties.class)
|
||||
@Import({CaffeineAutoConfiguration.class,
|
||||
MockRemoteCacheAutoConfiguration.class,
|
||||
LinkedHashMapAutoConfiguration.class,
|
||||
RedissonAutoConfiguration.class})
|
||||
public class JetCacheAutoConfiguration {
|
||||
|
||||
public static final String GLOBAL_CACHE_CONFIG_NAME = "globalCacheConfig";
|
||||
|
||||
@Bean
|
||||
@ConditionalOnMissingBean
|
||||
public SpringConfigProvider springConfigProvider(
|
||||
@Autowired ApplicationContext applicationContext,
|
||||
@Autowired GlobalCacheConfig globalCacheConfig,
|
||||
@Autowired(required = false) EncoderParser encoderParser,
|
||||
@Autowired(required = false) KeyConvertorParser keyConvertorParser,
|
||||
@Autowired(required = false) Consumer<StatInfo> metricsCallback) {
|
||||
return new JetCacheBaseBeans().springConfigProvider(applicationContext, globalCacheConfig,
|
||||
encoderParser, keyConvertorParser, metricsCallback);
|
||||
}
|
||||
|
||||
@Bean(name = "jcCacheManager")
|
||||
@ConditionalOnMissingBean
|
||||
public CacheManager cacheManager(@Autowired SpringConfigProvider springConfigProvider) {
|
||||
SimpleCacheManager cacheManager = new SimpleCacheManager();
|
||||
cacheManager.setCacheBuilderTemplate(springConfigProvider.getCacheBuilderTemplate());
|
||||
return cacheManager;
|
||||
}
|
||||
|
||||
@Bean
|
||||
public AutoConfigureBeans autoConfigureBeans() {
|
||||
return new AutoConfigureBeans();
|
||||
}
|
||||
|
||||
@Bean
|
||||
public static BeanDependencyManager beanDependencyManager() {
|
||||
return new BeanDependencyManager();
|
||||
}
|
||||
|
||||
@Bean(name = GLOBAL_CACHE_CONFIG_NAME)
|
||||
public GlobalCacheConfig globalCacheConfig(AutoConfigureBeans autoConfigureBeans, JetCacheProperties props) {
|
||||
GlobalCacheConfig _globalCacheConfig = new GlobalCacheConfig();
|
||||
_globalCacheConfig = new GlobalCacheConfig();
|
||||
_globalCacheConfig.setHiddenPackages(props.getHiddenPackages());
|
||||
_globalCacheConfig.setStatIntervalMinutes(props.getStatIntervalMinutes());
|
||||
_globalCacheConfig.setAreaInCacheName(props.isAreaInCacheName());
|
||||
_globalCacheConfig.setPenetrationProtect(props.isPenetrationProtect());
|
||||
_globalCacheConfig.setEnableMethodCache(props.isEnableMethodCache());
|
||||
_globalCacheConfig.setLocalCacheBuilders(autoConfigureBeans.getLocalCacheBuilders());
|
||||
_globalCacheConfig.setRemoteCacheBuilders(autoConfigureBeans.getRemoteCacheBuilders());
|
||||
return _globalCacheConfig;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
package cn.lingniu.framework.plugin.jetcache.autoconfigure;
|
||||
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionOutcome;
|
||||
import org.springframework.boot.autoconfigure.condition.SpringBootCondition;
|
||||
import org.springframework.context.annotation.ConditionContext;
|
||||
import org.springframework.core.env.ConfigurableEnvironment;
|
||||
import org.springframework.core.type.AnnotatedTypeMetadata;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.Set;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* Created on 2016/11/28.
|
||||
*
|
||||
* @author <a href="mailto:areyouok@gmail.com">huangli</a>
|
||||
*/
|
||||
public abstract class JetCacheCondition extends SpringBootCondition {
|
||||
|
||||
private String[] cacheTypes;
|
||||
|
||||
protected JetCacheCondition(String... cacheTypes) {
|
||||
Objects.requireNonNull(cacheTypes, "cacheTypes can't be null");
|
||||
Assert.isTrue(cacheTypes.length > 0, "cacheTypes length is 0");
|
||||
this.cacheTypes = cacheTypes;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ConditionOutcome getMatchOutcome(ConditionContext conditionContext, AnnotatedTypeMetadata annotatedTypeMetadata) {
|
||||
ConfigTree ct = new ConfigTree((ConfigurableEnvironment) conditionContext.getEnvironment(), "framework.lingniu.jetcache.");
|
||||
if (match(ct, "local.") || match(ct, "remote.")) {
|
||||
return ConditionOutcome.match();
|
||||
} else {
|
||||
return ConditionOutcome.noMatch("no match for " + cacheTypes[0]);
|
||||
}
|
||||
}
|
||||
|
||||
private boolean match(ConfigTree ct, String prefix) {
|
||||
Map<String, Object> m = ct.subTree(prefix).getProperties();
|
||||
Set<String> cacheAreaNames = m.keySet().stream().map((s) -> s.substring(0, s.indexOf('.'))).collect(Collectors.toSet());
|
||||
final List<String> cacheTypesList = Arrays.asList(cacheTypes);
|
||||
return cacheAreaNames.stream().anyMatch((s) -> cacheTypesList.contains(m.get(s + ".type")));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,68 @@
|
||||
package cn.lingniu.framework.plugin.jetcache.autoconfigure;
|
||||
|
||||
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||
|
||||
/**
|
||||
* Created on 2016/11/23.
|
||||
*
|
||||
* @author <a href="mailto:areyouok@gmail.com">huangli</a>
|
||||
*/
|
||||
@ConfigurationProperties(prefix = "jetcache")
|
||||
public class JetCacheProperties {
|
||||
|
||||
private String[] hiddenPackages;
|
||||
private int statIntervalMinutes;
|
||||
private boolean areaInCacheName = true;
|
||||
private boolean penetrationProtect = false;
|
||||
private boolean enableMethodCache = true;
|
||||
|
||||
public JetCacheProperties() {
|
||||
}
|
||||
|
||||
public String[] getHiddenPackages() {
|
||||
// keep same with GlobalCacheConfig
|
||||
return hiddenPackages;
|
||||
}
|
||||
|
||||
public void setHiddenPackages(String[] hiddenPackages) {
|
||||
// keep same with GlobalCacheConfig
|
||||
this.hiddenPackages = hiddenPackages;
|
||||
}
|
||||
|
||||
public void setHidePackages(String[] hidePackages) {
|
||||
// keep same with GlobalCacheConfig
|
||||
this.hiddenPackages = hidePackages;
|
||||
}
|
||||
|
||||
public int getStatIntervalMinutes() {
|
||||
return statIntervalMinutes;
|
||||
}
|
||||
|
||||
public void setStatIntervalMinutes(int statIntervalMinutes) {
|
||||
this.statIntervalMinutes = statIntervalMinutes;
|
||||
}
|
||||
|
||||
public boolean isAreaInCacheName() {
|
||||
return areaInCacheName;
|
||||
}
|
||||
|
||||
public void setAreaInCacheName(boolean areaInCacheName) {
|
||||
this.areaInCacheName = areaInCacheName;
|
||||
}
|
||||
|
||||
public boolean isPenetrationProtect() {
|
||||
return penetrationProtect;
|
||||
}
|
||||
|
||||
public void setPenetrationProtect(boolean penetrationProtect) {
|
||||
this.penetrationProtect = penetrationProtect;
|
||||
}
|
||||
|
||||
public boolean isEnableMethodCache() {
|
||||
return enableMethodCache;
|
||||
}
|
||||
|
||||
public void setEnableMethodCache(boolean enableMethodCache) {
|
||||
this.enableMethodCache = enableMethodCache;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
package cn.lingniu.framework.plugin.jetcache.autoconfigure;
|
||||
|
||||
import com.alicp.jetcache.CacheBuilder;
|
||||
import com.alicp.jetcache.embedded.LinkedHashMapCacheBuilder;
|
||||
import org.springframework.context.annotation.Conditional;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
/**
|
||||
* Created on 2016/12/2.
|
||||
*
|
||||
* @author <a href="mailto:areyouok@gmail.com">huangli</a>
|
||||
*/
|
||||
@Component
|
||||
@Conditional(LinkedHashMapAutoConfiguration.LinkedHashMapCondition.class)
|
||||
public class LinkedHashMapAutoConfiguration extends EmbeddedCacheAutoInit {
|
||||
public LinkedHashMapAutoConfiguration() {
|
||||
super("linkedhashmap");
|
||||
}
|
||||
|
||||
@Override
|
||||
protected CacheBuilder initCache(ConfigTree ct, String cacheAreaWithPrefix) {
|
||||
LinkedHashMapCacheBuilder builder = LinkedHashMapCacheBuilder.createLinkedHashMapCacheBuilder();
|
||||
parseGeneralConfig(builder, ct);
|
||||
return builder;
|
||||
}
|
||||
|
||||
public static class LinkedHashMapCondition extends JetCacheCondition {
|
||||
public LinkedHashMapCondition() {
|
||||
super("linkedhashmap");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
package cn.lingniu.framework.plugin.jetcache.autoconfigure;
|
||||
|
||||
import com.alicp.jetcache.CacheBuilder;
|
||||
import com.alicp.jetcache.anno.CacheConsts;
|
||||
import com.alicp.jetcache.external.MockRemoteCacheBuilder;
|
||||
import org.springframework.context.annotation.Conditional;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
/**
|
||||
* Created on 2016/12/2.
|
||||
*
|
||||
* @author <a href="mailto:areyouok@gmail.com">huangli</a>
|
||||
*/
|
||||
@Component
|
||||
@Conditional(MockRemoteCacheAutoConfiguration.MockRemoteCacheCondition.class)
|
||||
public class MockRemoteCacheAutoConfiguration extends ExternalCacheAutoInit {
|
||||
public MockRemoteCacheAutoConfiguration() {
|
||||
super("mock");
|
||||
}
|
||||
|
||||
@Override
|
||||
protected CacheBuilder initCache(ConfigTree ct, String cacheAreaWithPrefix) {
|
||||
MockRemoteCacheBuilder builder = MockRemoteCacheBuilder.createMockRemoteCacheBuilder();
|
||||
parseGeneralConfig(builder, ct);
|
||||
return builder;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void parseGeneralConfig(CacheBuilder builder, ConfigTree ct) {
|
||||
super.parseGeneralConfig(builder, ct);
|
||||
MockRemoteCacheBuilder b = (MockRemoteCacheBuilder) builder;
|
||||
b.limit(Integer.parseInt(ct.getProperty("limit", String.valueOf(CacheConsts.DEFAULT_LOCAL_LIMIT))));
|
||||
}
|
||||
|
||||
public static class MockRemoteCacheCondition extends JetCacheCondition {
|
||||
public MockRemoteCacheCondition() {
|
||||
super("mock");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,73 @@
|
||||
package cn.lingniu.framework.plugin.jetcache.autoconfigure;
|
||||
|
||||
import cn.lingniu.framework.plugin.jetcache.redisson.RedissonCacheBuilder;
|
||||
import com.alicp.jetcache.CacheBuilder;
|
||||
import com.alicp.jetcache.CacheConfigException;
|
||||
import com.alicp.jetcache.external.ExternalCacheBuilder;
|
||||
import org.redisson.api.RedissonClient;
|
||||
import org.springframework.beans.BeansException;
|
||||
import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.context.ApplicationContextAware;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Conditional;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
|
||||
/**
|
||||
* Created on 2022/7/12.
|
||||
*
|
||||
* @author <a href="mailto:jeason1914@qq.com">yangyong</a>
|
||||
*/
|
||||
@Configuration
|
||||
@Conditional(RedissonAutoConfiguration.RedissonCondition.class)
|
||||
public class RedissonAutoConfiguration {
|
||||
private static final String CACHE_TYPE = "redisson";
|
||||
|
||||
public static class RedissonCondition extends JetCacheCondition {
|
||||
public RedissonCondition() {
|
||||
super(CACHE_TYPE);
|
||||
}
|
||||
}
|
||||
|
||||
@Bean
|
||||
public RedissonAutoInit redissonAutoInit() {
|
||||
return new RedissonAutoInit();
|
||||
}
|
||||
|
||||
public static class RedissonAutoInit extends ExternalCacheAutoInit implements ApplicationContextAware {
|
||||
private ApplicationContext context;
|
||||
|
||||
public RedissonAutoInit() {
|
||||
super(CACHE_TYPE);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected CacheBuilder initCache(final ConfigTree ct, final String cacheAreaWithPrefix) {
|
||||
final Map<String, RedissonClient> beans = this.context.getBeansOfType(RedissonClient.class);
|
||||
if (beans.isEmpty()) {
|
||||
throw new CacheConfigException("no RedissonClient in spring context");
|
||||
}
|
||||
RedissonClient client = beans.values().iterator().next();
|
||||
if (beans.size() > 1) {
|
||||
final String redissonClientName = ct.getProperty("redissonClient");
|
||||
if (Objects.isNull(redissonClientName) || redissonClientName.isEmpty()) {
|
||||
throw new CacheConfigException("redissonClient is required, because there is multiple RedissonClient in Spring context");
|
||||
}
|
||||
if (!beans.containsKey(redissonClientName)) {
|
||||
throw new CacheConfigException("there is no RedissonClient named " + redissonClientName + " in Spring context");
|
||||
}
|
||||
client = beans.get(redissonClientName);
|
||||
}
|
||||
final ExternalCacheBuilder<?> builder = RedissonCacheBuilder.createBuilder().redissonClient(client);
|
||||
parseGeneralConfig(builder, ct);
|
||||
return builder;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setApplicationContext(final ApplicationContext context) throws BeansException {
|
||||
this.context = context;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,70 @@
|
||||
package cn.lingniu.framework.plugin.jetcache.redisson;
|
||||
|
||||
import com.alicp.jetcache.CacheManager;
|
||||
import com.alicp.jetcache.CacheResult;
|
||||
import com.alicp.jetcache.support.BroadcastManager;
|
||||
import com.alicp.jetcache.support.CacheMessage;
|
||||
import com.alicp.jetcache.support.SquashedLogger;
|
||||
import org.redisson.api.RedissonClient;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.util.Objects;
|
||||
|
||||
/**
|
||||
* Created on 2022/7/12.
|
||||
*
|
||||
* @author <a href="mailto:jeason1914@qq.com">yangyong</a>
|
||||
*/
|
||||
public class RedissonBroadcastManager extends BroadcastManager {
|
||||
private static final Logger logger = LoggerFactory.getLogger(RedissonBroadcastManager.class);
|
||||
private final RedissonCacheConfig<?, ?> config;
|
||||
private final String channel;
|
||||
private final RedissonClient client;
|
||||
private volatile int subscribeId;
|
||||
|
||||
public RedissonBroadcastManager(final CacheManager cacheManager, final RedissonCacheConfig<?, ?> config) {
|
||||
super(cacheManager);
|
||||
checkConfig(config);
|
||||
this.config = config;
|
||||
this.channel = config.getBroadcastChannel();
|
||||
this.client = config.getRedissonClient();
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized void startSubscribe() {
|
||||
if (this.subscribeId == 0 && Objects.nonNull(this.channel) && !this.channel.isEmpty()) {
|
||||
this.subscribeId = this.client.getTopic(this.channel)
|
||||
.addListener(byte[].class, (channel, msg) -> processNotification(msg, this.config.getValueDecoder()));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public synchronized void close() {
|
||||
final int id;
|
||||
if ((id = this.subscribeId) > 0 && Objects.nonNull(this.channel)) {
|
||||
this.subscribeId = 0;
|
||||
try {
|
||||
this.client.getTopic(this.channel).removeListener(id);
|
||||
} catch (Throwable e) {
|
||||
logger.warn("unsubscribe {} fail", this.channel, e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public CacheResult publish(final CacheMessage cacheMessage) {
|
||||
try {
|
||||
if (Objects.nonNull(this.channel) && Objects.nonNull(cacheMessage)) {
|
||||
final byte[] msg = this.config.getValueEncoder().apply(cacheMessage);
|
||||
this.client.getTopic(this.channel).publish(msg);
|
||||
return CacheResult.SUCCESS_WITHOUT_MSG;
|
||||
}
|
||||
return CacheResult.FAIL_WITHOUT_MSG;
|
||||
} catch (Throwable e) {
|
||||
SquashedLogger.getLogger(logger).error("jetcache publish error", e);
|
||||
return new CacheResult(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,180 @@
|
||||
package cn.lingniu.framework.plugin.jetcache.redisson;
|
||||
|
||||
import com.alicp.jetcache.CacheConfig;
|
||||
import com.alicp.jetcache.CacheGetResult;
|
||||
import com.alicp.jetcache.CacheResult;
|
||||
import com.alicp.jetcache.CacheResultCode;
|
||||
import com.alicp.jetcache.CacheValueHolder;
|
||||
import com.alicp.jetcache.MultiGetResult;
|
||||
import com.alicp.jetcache.external.AbstractExternalCache;
|
||||
import org.redisson.api.RBatch;
|
||||
import org.redisson.api.RBucket;
|
||||
import org.redisson.api.RedissonClient;
|
||||
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
/**
|
||||
* Created on 2022/7/12.
|
||||
*
|
||||
* @author <a href="mailto:jeason1914@qq.com">yangyong</a>
|
||||
*/
|
||||
public class RedissonCache<K, V> extends AbstractExternalCache<K, V> {
|
||||
private final RedissonClient client;
|
||||
private final RedissonCacheConfig<K, V> config;
|
||||
|
||||
public RedissonCache(final RedissonCacheConfig<K, V> config) {
|
||||
super(config);
|
||||
this.config = config;
|
||||
this.client = config.getRedissonClient();
|
||||
}
|
||||
|
||||
protected String getCacheKey(final K key) {
|
||||
final byte[] newKey = buildKey(key);
|
||||
return new String(newKey, StandardCharsets.UTF_8);
|
||||
}
|
||||
|
||||
@Override
|
||||
public CacheConfig<K, V> config() {
|
||||
return this.config;
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> T unwrap(final Class<T> clazz) {
|
||||
throw new UnsupportedOperationException("RedissonCache does not support unwrap");
|
||||
}
|
||||
|
||||
@Override
|
||||
@SuppressWarnings({"unchecked"})
|
||||
protected CacheGetResult<V> do_GET(final K key) {
|
||||
try {
|
||||
final RBucket<CacheValueHolder<V>> rb = this.client.getBucket(getCacheKey(key));
|
||||
final CacheValueHolder<V> holder = rb.get();
|
||||
if (Objects.nonNull(holder)) {
|
||||
final long now = System.currentTimeMillis(), expire = holder.getExpireTime();
|
||||
if (expire > 0 && now >= expire) {
|
||||
return CacheGetResult.EXPIRED_WITHOUT_MSG;
|
||||
}
|
||||
return new CacheGetResult<>(CacheResultCode.SUCCESS, null, holder);
|
||||
}
|
||||
return CacheGetResult.NOT_EXISTS_WITHOUT_MSG;
|
||||
} catch (Throwable e) {
|
||||
logError("GET", key, e);
|
||||
return new CacheGetResult<>(e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
@SuppressWarnings({"unchecked"})
|
||||
protected MultiGetResult<K, V> do_GET_ALL(final Set<? extends K> keys) {
|
||||
try {
|
||||
final Map<K, CacheGetResult<V>> retMap = new HashMap<>(1 << 4);
|
||||
if (Objects.nonNull(keys) && !keys.isEmpty()) {
|
||||
final Map<K, String> keyMap = new HashMap<>(keys.size());
|
||||
for (K k : keys) {
|
||||
if (Objects.nonNull(k)) {
|
||||
final String key = getCacheKey(k);
|
||||
if (Objects.nonNull(key)) {
|
||||
keyMap.put(k, key);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!keyMap.isEmpty()) {
|
||||
final Map<String, Object> kvMap = this.client.getBuckets().get(keyMap.values().toArray(new String[0]));
|
||||
final long now = System.currentTimeMillis();
|
||||
for (K k : keys) {
|
||||
final String key = keyMap.get(k);
|
||||
if (Objects.nonNull(key) && Objects.nonNull(kvMap)) {
|
||||
final CacheValueHolder<V> holder = (CacheValueHolder<V>) kvMap.get(key);
|
||||
if (Objects.nonNull(holder)) {
|
||||
final long expire = holder.getExpireTime();
|
||||
final CacheGetResult<V> ret = (expire > 0 && now >= expire) ? CacheGetResult.EXPIRED_WITHOUT_MSG :
|
||||
new CacheGetResult<>(CacheResultCode.SUCCESS, null, holder);
|
||||
retMap.put(k, ret);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
retMap.put(k, CacheGetResult.NOT_EXISTS_WITHOUT_MSG);
|
||||
}
|
||||
}
|
||||
}
|
||||
return new MultiGetResult<>(CacheResultCode.SUCCESS, null, retMap);
|
||||
} catch (Throwable e) {
|
||||
logError("GET_ALL", "keys(" + (Objects.nonNull(keys) ? keys.size() : 0) + ")", e);
|
||||
return new MultiGetResult<>(e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected CacheResult do_PUT(final K key, final V value, final long expireAfterWrite, final TimeUnit timeUnit) {
|
||||
try {
|
||||
final CacheValueHolder<V> holder = new CacheValueHolder<>(value, timeUnit.toMillis(expireAfterWrite));
|
||||
this.client.getBucket(getCacheKey(key)).set(holder, expireAfterWrite, timeUnit);
|
||||
return CacheGetResult.SUCCESS_WITHOUT_MSG;
|
||||
} catch (Throwable e) {
|
||||
logError("PUT", key, e);
|
||||
return new CacheResult(e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected CacheResult do_PUT_ALL(final Map<? extends K, ? extends V> map, final long expireAfterWrite, final TimeUnit timeUnit) {
|
||||
try {
|
||||
if (Objects.nonNull(map) && !map.isEmpty()) {
|
||||
final long expire = timeUnit.toMillis(expireAfterWrite);
|
||||
final RBatch batch = this.client.createBatch();
|
||||
map.forEach((k, v) -> {
|
||||
final CacheValueHolder<V> holder = new CacheValueHolder<>(v, expire);
|
||||
batch.getBucket(getCacheKey(k)).setAsync(holder, expireAfterWrite, timeUnit);
|
||||
});
|
||||
batch.execute();
|
||||
}
|
||||
return CacheResult.SUCCESS_WITHOUT_MSG;
|
||||
} catch (Throwable e) {
|
||||
logError("PUT_ALL", "map(" + map.size() + ")", e);
|
||||
return new CacheResult(e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected CacheResult do_REMOVE(final K key) {
|
||||
try {
|
||||
final boolean ret = this.client.getBucket(getCacheKey(key)).delete();
|
||||
return ret ? CacheResult.SUCCESS_WITHOUT_MSG : CacheResult.FAIL_WITHOUT_MSG;
|
||||
} catch (Throwable e) {
|
||||
logError("REMOVE", key, e);
|
||||
return new CacheResult(e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected CacheResult do_REMOVE_ALL(final Set<? extends K> keys) {
|
||||
try {
|
||||
if (Objects.nonNull(keys) && !keys.isEmpty()) {
|
||||
final RBatch batch = this.client.createBatch();
|
||||
keys.forEach(key -> batch.getBucket(getCacheKey(key)).deleteAsync());
|
||||
batch.execute();
|
||||
}
|
||||
return CacheResult.SUCCESS_WITHOUT_MSG;
|
||||
} catch (Throwable e) {
|
||||
logError("REMOVE_ALL", "keys(" + keys.size() + ")", e);
|
||||
return new CacheResult(e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected CacheResult do_PUT_IF_ABSENT(final K key, final V value, final long expireAfterWrite, final TimeUnit timeUnit) {
|
||||
try {
|
||||
final CacheValueHolder<V> holder = new CacheValueHolder<>(value, timeUnit.toMillis(expireAfterWrite));
|
||||
final boolean success = this.client.getBucket(getCacheKey(key)).trySet(holder, expireAfterWrite, timeUnit);
|
||||
return success ? CacheResult.SUCCESS_WITHOUT_MSG : CacheResult.EXISTS_WITHOUT_MSG;
|
||||
} catch (Throwable e) {
|
||||
logError("PUT_IF_ABSENT", key, e);
|
||||
return new CacheResult(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,52 @@
|
||||
package cn.lingniu.framework.plugin.jetcache.redisson;
|
||||
|
||||
import com.alicp.jetcache.CacheManager;
|
||||
import com.alicp.jetcache.external.ExternalCacheBuilder;
|
||||
import com.alicp.jetcache.support.BroadcastManager;
|
||||
import org.redisson.api.RedissonClient;
|
||||
|
||||
/**
|
||||
* Created on 2022/7/12.
|
||||
*
|
||||
* @author <a href="mailto:jeason1914@qq.com">yangyong</a>
|
||||
*/
|
||||
public class RedissonCacheBuilder<T extends ExternalCacheBuilder<T>> extends ExternalCacheBuilder<T> {
|
||||
|
||||
public static class RedissonDataCacheBuilderImpl extends RedissonCacheBuilder<RedissonDataCacheBuilderImpl> {
|
||||
|
||||
}
|
||||
|
||||
public static RedissonDataCacheBuilderImpl createBuilder() {
|
||||
return new RedissonDataCacheBuilderImpl();
|
||||
}
|
||||
|
||||
@SuppressWarnings({"all"})
|
||||
protected RedissonCacheBuilder() {
|
||||
buildFunc(config -> new RedissonCache((RedissonCacheConfig) config));
|
||||
}
|
||||
|
||||
@Override
|
||||
@SuppressWarnings({"all"})
|
||||
public RedissonCacheConfig getConfig() {
|
||||
if (this.config == null) {
|
||||
this.config = new RedissonCacheConfig();
|
||||
}
|
||||
return (RedissonCacheConfig) this.config;
|
||||
}
|
||||
|
||||
public T redissonClient(final RedissonClient client) {
|
||||
this.getConfig().setRedissonClient(client);
|
||||
return self();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean supportBroadcast() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public BroadcastManager createBroadcastManager(final CacheManager cacheManager) {
|
||||
final RedissonCacheConfig<?, ?> c = (RedissonCacheConfig<?, ?>) this.getConfig().clone();
|
||||
return new RedissonBroadcastManager(cacheManager, c);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
package cn.lingniu.framework.plugin.jetcache.redisson;
|
||||
|
||||
import com.alicp.jetcache.external.ExternalCacheConfig;
|
||||
import org.redisson.api.RedissonClient;
|
||||
|
||||
/**
|
||||
* Created on 2022/7/12.
|
||||
*
|
||||
* @author <a href="mailto:jeason1914@qq.com">yangyong</a>
|
||||
*/
|
||||
public class RedissonCacheConfig<K, V> extends ExternalCacheConfig<K, V> {
|
||||
private RedissonClient redissonClient;
|
||||
|
||||
public RedissonClient getRedissonClient() {
|
||||
return redissonClient;
|
||||
}
|
||||
|
||||
public void setRedissonClient(final RedissonClient redissonClient) {
|
||||
this.redissonClient = redissonClient;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
|
||||
cn.lingniu.framework.plugin.jetcache.autoconfigure.JetCacheAutoConfiguration
|
||||
@@ -0,0 +1,122 @@
|
||||
# 【重要】jetcache详细资料---参考官网
|
||||
|
||||
## 概述 (Overview)
|
||||
|
||||
1. 基于 JetCache 封装的企业级二级缓存解决方案,提供本地 JVM 本地缓存和远程 Redis 缓存的组合缓存能力
|
||||
2. 核心能力
|
||||
* 二级缓存机制:
|
||||
|
||||
- 一级缓存:本地 JVM 缓存(基于 Caffeine等)
|
||||
- 二级缓存:远程 Redis 缓存(redisson 模式)
|
||||
|
||||
* 多样化缓存配置:
|
||||
- 本地缓存:支持 Caffeine 类型,可配置缓存数量限制和过期时间
|
||||
- 远程缓存:支持 redisson 类型
|
||||
- 多实例支持:可配置多个缓存分组(如 default、employee 等)
|
||||
* 注解式缓存操作:
|
||||
- @Cached:标记方法结果需要缓存,支持缓存名称、过期时间等配置
|
||||
- @CacheRefresh:支持缓存自动刷新机制
|
||||
- @EnableMethodCache:启用 JetCache Bean 扫描
|
||||
* 灵活的缓存策略:
|
||||
- 支持不同的 Key 生成策略
|
||||
- 可配置缓存过期时间和时间单位
|
||||
- 支持不同的缓存类型(本地、远程、双向)
|
||||
|
||||
3. 适用场景:该组件特别适用于需要高性能缓存访问的企业级应用,通过本地+远程的二级缓存架构,在保证缓存访问速度的同时,确保缓存数据的共享和一致性。通过注解方式简化了缓存的使用。
|
||||
* 高并发读场景:需要减少数据库访问压力,提升系统响应速度的业务场景
|
||||
* 高可用访问:需要自动感知 Redis 集群变化以及故障自动切换能力的应用场景
|
||||
* 复杂查询结果缓存:对于计算复杂或关联查询较多的数据结果进行缓存
|
||||
* 数据一致性要求较高:需要通过二级缓存机制保证数据访问性能和一致性的场景
|
||||
* 需要缓存自动刷新:对于有一定时效性要求但不需要实时更新的数据
|
||||
|
||||
## 如何配置--更多参数参考:更多配置请参考官网jetcache
|
||||
|
||||
* 此处为了统一中间件配置前缀framework.lingniu.jetcache 和更好的个性化扩展, 其他jetcache所有配置方式未改变
|
||||
* 请先依赖:lingniu-framework-plugin-redisson
|
||||
redissonClient 就是 RedissonConfig配置的key名称
|
||||
|
||||
```yaml
|
||||
framework:
|
||||
lingniu:
|
||||
jetcache:
|
||||
# 需要隐藏的包路径(数组),默认为空
|
||||
hiddenPackages: [ ]
|
||||
# 统计信息输出间隔(分钟),默认为 0(不输出)
|
||||
statIntervalMinutes: 10
|
||||
# 是否在缓存名称中包含区域(area)信息,默认为 true
|
||||
areaInCacheName: true
|
||||
# 是否开启缓存穿透保护,默认为 false
|
||||
penetrationProtect: false
|
||||
# 本地缓存配置
|
||||
local:
|
||||
# cache area 配置, 使用CacheManager或@Cache注解时 默认area为default
|
||||
ca2:
|
||||
# 本地缓存实现类类型,支持:linkedhashmap/caffeine
|
||||
type: linkedhashmap
|
||||
# 缓存key数量限制, 默认100
|
||||
limit: 100
|
||||
default:
|
||||
# 本地缓存实现类类型,支持:linkedhashmap/caffeine
|
||||
type: caffeine
|
||||
# 缓存key数量限制, 默认100
|
||||
limit: 100
|
||||
# 过期时间,0为不失效
|
||||
expireAfterAccessInMillis: 0
|
||||
# key 转换器,用于序列化,目前支持:NONE、FASTJSON、JACKSON、FASTJSON2
|
||||
keyConvertor: FASTJSON2
|
||||
remote:
|
||||
# cache area 配置, 使用CacheManager或@Cache注解时 默认area为default
|
||||
ca2:
|
||||
# 远程缓存实现类类型,目前支持:redisson
|
||||
type: redisson
|
||||
# 远程缓存客户端名称,默认r1 -- todo 需要在application-redisson.yml中配置
|
||||
redissonClient: r1
|
||||
# key 前缀
|
||||
keyPrefix: "cacheKeyPrefix:"
|
||||
# 连接超时时间ms
|
||||
timeout: 2000
|
||||
# 重试次数
|
||||
maxAttempt: 5
|
||||
# 连接池配置
|
||||
poolConfig:
|
||||
maxTotal: 8
|
||||
maxIdle: 8
|
||||
minIdle: 0
|
||||
# key 转换器,用于序列化,目前支持:NONE、FASTJSON、JACKSON、FASTJSON2
|
||||
keyConvertor: FASTJSON2
|
||||
# JAVA, KRYO,KRYO5,自定义spring beanName
|
||||
valueEncoder: JAVA
|
||||
# JAVA, KRYO,KRYO5,自定义spring beanName
|
||||
valueDecoder: JAVA
|
||||
# cache area 配置, 使用CacheManager或@Cache注解时 默认area为default
|
||||
default:
|
||||
# 远程缓存实现类类型,目前支持:redisson
|
||||
type: redisson
|
||||
# 远程缓存客户端名称,默认r1 -- todo 需要在application-redisson.yml中配置
|
||||
redissonClient: r1
|
||||
|
||||
```
|
||||
|
||||
## 如何使用-
|
||||
|
||||
```java
|
||||
//启动 扫描类添加注解
|
||||
@EnableMethodCache(basePackages = "cn.lingniu.*")
|
||||
@SpringBootApplication
|
||||
public class CacheApplication {
|
||||
public static void main(String[] args) {
|
||||
SpringApplication.run(CacheApplication.class, args);
|
||||
}
|
||||
}
|
||||
// 多种使用方式参考
|
||||
// 1. 注解方式
|
||||
@CachePenetrationProtect
|
||||
@Cached(name = "orderCache:", key = "#orderId", expire = 3600, cacheType = CacheType.BOTH)
|
||||
@CacheRefresh(refresh = 10, timeUnit = TimeUnit.MINUTES)
|
||||
public DataResult<KeyValueEntity> getOrderById(String orderId) {
|
||||
...
|
||||
}
|
||||
// 2. SimpleCacheManager方式
|
||||
Cache<Object, Object> localCache = simpleCacheManager.getOrCreateCache(QuickConfig.newBuilder(localArea, "user:").cacheType(CacheType.LOCAL).build());
|
||||
```
|
||||
|
||||
Reference in New Issue
Block a user