Skip to content
This repository has been archived by the owner on Mar 4, 2022. It is now read-only.

Commit

Permalink
add cache support
Browse files Browse the repository at this point in the history
  • Loading branch information
itning committed Apr 1, 2019
1 parent fd62179 commit a357f7d
Show file tree
Hide file tree
Showing 13 changed files with 483 additions and 6 deletions.
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@
2. 运行镜像

```shell
sudo docker run -p 80:8080 -e MYSQL_URL=192.168.66.1:3306 -e MYSQL_USERNAME=root -e MYSQL_PASSWORD=root -it registry.cn-beijing.aliyuncs.com/itning/shw_server:latest
sudo docker run -p 80:8080 -e MYSQL_URL=192.168.66.1:3306 -e MYSQL_USERNAME=root -e MYSQL_PASSWORD=root -e REDIS_HOST=192.168.66.1 -e REDIS_PORT=6379 -it registry.cn-beijing.aliyuncs.com/itning/shw_server:latest
```

**其中MYSQL_URL参数值为MySql数据库服务器地址(带端口号)**
Expand Down
10 changes: 9 additions & 1 deletion pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
</parent>
<groupId>top.yunshu</groupId>
<artifactId>shw_server</artifactId>
<version>1.6.6-RELEASE</version>
<version>1.7.0-RELEASE</version>
<name>shw_server</name>
<description>Student HomeWork Management System</description>

Expand Down Expand Up @@ -50,6 +50,14 @@
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-websocket</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-cache</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
Expand Down
338 changes: 338 additions & 0 deletions src/main/java/org/springframework/data/redis/cache/RedisCache.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,338 @@
/*
* Copyright 2017-2019 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.redis.cache;

import org.springframework.cache.support.AbstractValueAdaptingCache;
import org.springframework.cache.support.NullValue;
import org.springframework.cache.support.SimpleValueWrapper;
import org.springframework.core.convert.ConversionService;
import org.springframework.core.convert.TypeDescriptor;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.RedisSerializer;
import org.springframework.data.redis.serializer.StringRedisSerializer;
import org.springframework.data.redis.util.ByteUtils;
import org.springframework.lang.Nullable;
import org.springframework.util.Assert;
import org.springframework.util.ObjectUtils;
import org.springframework.util.ReflectionUtils;
import top.yunshu.shw.server.config.SpringContextHelper;

import java.lang.reflect.Method;
import java.nio.ByteBuffer;
import java.util.Set;
import java.util.concurrent.Callable;

/**
* {@link org.springframework.cache.Cache} implementation using for Redis as underlying store.
* <p/>
* Use {@link RedisCacheManager} to create {@link RedisCache} instances.
*
* @author Christoph Strobl
* @author Mark Paluch
* @see RedisCacheConfiguration
* @see RedisCacheWriter
* @since 2.0
*/
@SuppressWarnings("all")
public class RedisCache extends AbstractValueAdaptingCache {
private static final byte[] BINARY_NULL_VALUE = RedisSerializer.java().serialize(NullValue.INSTANCE);

private final String name;
private final RedisCacheWriter cacheWriter;
private final RedisCacheConfiguration cacheConfig;
private final ConversionService conversionService;
private final RedisTemplate redisTemplate;

{
redisTemplate = SpringContextHelper.getBean("redisTemplate", RedisTemplate.class);
redisTemplate.setKeySerializer(new StringRedisSerializer());
}

/**
* Create new {@link RedisCache}.
*
* @param name must not be {@literal null}.
* @param cacheWriter must not be {@literal null}.
* @param cacheConfig must not be {@literal null}.
*/
protected RedisCache(String name, RedisCacheWriter cacheWriter, RedisCacheConfiguration cacheConfig) {

super(cacheConfig.getAllowCacheNullValues());

Assert.notNull(name, "Name must not be null!");
Assert.notNull(cacheWriter, "CacheWriter must not be null!");
Assert.notNull(cacheConfig, "CacheConfig must not be null!");

this.name = name;
this.cacheWriter = cacheWriter;
this.cacheConfig = cacheConfig;
this.conversionService = cacheConfig.getConversionService();
}

/*
* (non-Javadoc)
* @see org.springframework.cache.support.AbstractValueAdaptingCache#lookup(java.lang.Object)
*/
@Override
protected Object lookup(Object key) {

byte[] value = cacheWriter.get(name, createAndConvertCacheKey(key));

if (value == null) {
return null;
}

return deserializeCacheValue(value);
}

/*
* (non-Javadoc)
* @see org.springframework.cache.Cache#getName()
*/
@Override
public String getName() {
return name;
}

/*
* (non-Javadoc)
* @see org.springframework.cache.Cache#getNativeCache()
*/
@Override
public RedisCacheWriter getNativeCache() {
return this.cacheWriter;
}

/*
* (non-Javadoc)
* @see org.springframework.cache.Cache#get(java.lang.Object, java.util.concurrent.Callable)
*/
@Override
@SuppressWarnings("unchecked")
public synchronized <T> T get(Object key, Callable<T> valueLoader) {

ValueWrapper result = get(key);

if (result != null) {
return (T) result.get();
}

T value = valueFromLoader(key, valueLoader);
put(key, value);
return value;
}

/*
* (non-Javadoc)
* @see org.springframework.cache.Cache#put(java.lang.Object, java.lang.Object)
*/
@Override
public void put(Object key, @Nullable Object value) {

Object cacheValue = preProcessCacheValue(value);

if (!isAllowNullValues() && cacheValue == null) {

throw new IllegalArgumentException(String.format(
"Cache '%s' does not allow 'null' values. Avoid storing null via '@Cacheable(unless=\"#result == null\")' or configure RedisCache to allow 'null' via RedisCacheConfiguration.",
name));
}

cacheWriter.put(name, createAndConvertCacheKey(key), serializeCacheValue(cacheValue), cacheConfig.getTtl());
}

/*
* (non-Javadoc)
* @see org.springframework.cache.Cache#putIfAbsent(java.lang.Object, java.lang.Object)
*/
@Override
public ValueWrapper putIfAbsent(Object key, @Nullable Object value) {

Object cacheValue = preProcessCacheValue(value);

if (!isAllowNullValues() && cacheValue == null) {
return get(key);
}

byte[] result = cacheWriter.putIfAbsent(name, createAndConvertCacheKey(key), serializeCacheValue(cacheValue),
cacheConfig.getTtl());

if (result == null) {
return null;
}

return new SimpleValueWrapper(fromStoreValue(deserializeCacheValue(result)));
}

/*
* (non-Javadoc)
* @see org.springframework.cache.Cache#evict(java.lang.Object)
*/
@Override
public void evict(Object key) {
if (key instanceof String) {
String keyStr = (String) key;
if (keyStr.startsWith("regex:")) {
String regex = keyStr.substring(6);
if ("".equals(regex)) {
throw new RuntimeException("must write regex");
}
Set keys = redisTemplate.keys(name + "::" + regex);
redisTemplate.delete(keys);
return;
}
}

cacheWriter.remove(name, createAndConvertCacheKey(key));
}

/*
* (non-Javadoc)
* @see org.springframework.cache.Cache#clear()
*/
@Override
public void clear() {
byte[] pattern = conversionService.convert(createCacheKey("*"), byte[].class);
cacheWriter.clean(name, pattern);
}

/**
* Get {@link RedisCacheConfiguration} used.
*
* @return immutable {@link RedisCacheConfiguration}. Never {@literal null}.
*/
public RedisCacheConfiguration getCacheConfiguration() {
return cacheConfig;
}

/**
* Customization hook called before passing object to
* {@link org.springframework.data.redis.serializer.RedisSerializer}.
*
* @param value can be {@literal null}.
* @return preprocessed value. Can be {@literal null}.
*/
@Nullable
protected Object preProcessCacheValue(@Nullable Object value) {

if (value != null) {
return value;
}

return isAllowNullValues() ? NullValue.INSTANCE : null;
}

/**
* Serialize the key.
*
* @param cacheKey must not be {@literal null}.
* @return never {@literal null}.
*/
protected byte[] serializeCacheKey(String cacheKey) {
return ByteUtils.getBytes(cacheConfig.getKeySerializationPair().write(cacheKey));
}

/**
* Serialize the value to cache.
*
* @param value must not be {@literal null}.
* @return never {@literal null}.
*/
protected byte[] serializeCacheValue(Object value) {

if (isAllowNullValues() && value instanceof NullValue) {
return BINARY_NULL_VALUE;
}

return ByteUtils.getBytes(cacheConfig.getValueSerializationPair().write(value));
}

/**
* Deserialize the given value to the actual cache value.
*
* @param value must not be {@literal null}.
* @return can be {@literal null}.
*/
@Nullable
protected Object deserializeCacheValue(byte[] value) {

if (isAllowNullValues() && ObjectUtils.nullSafeEquals(value, BINARY_NULL_VALUE)) {
return NullValue.INSTANCE;
}

return cacheConfig.getValueSerializationPair().read(ByteBuffer.wrap(value));
}

/**
* Customization hook for creating cache key before it gets serialized.
*
* @param key will never be {@literal null}.
* @return never {@literal null}.
*/
protected String createCacheKey(Object key) {

String convertedKey = convertKey(key);

if (!cacheConfig.usePrefix()) {
return convertedKey;
}

return prefixCacheKey(convertedKey);
}

/**
* Convert {@code key} to a {@link String} representation used for cache key creation.
*
* @param key will never be {@literal null}.
* @return never {@literal null}.
* @throws IllegalStateException if {@code key} cannot be converted to {@link String}.
*/
protected String convertKey(Object key) {

TypeDescriptor source = TypeDescriptor.forObject(key);
if (conversionService.canConvert(source, TypeDescriptor.valueOf(String.class))) {
return conversionService.convert(key, String.class);
}

Method toString = ReflectionUtils.findMethod(key.getClass(), "toString");

if (toString != null && !Object.class.equals(toString.getDeclaringClass())) {
return key.toString();
}

throw new IllegalStateException(
String.format("Cannot convert %s to String. Register a Converter or override toString().", source));
}

private byte[] createAndConvertCacheKey(Object key) {
return serializeCacheKey(createCacheKey(key));
}

private String prefixCacheKey(String key) {

// allow contextual cache names by computing the key prefix on every call.
return cacheConfig.getKeyPrefixFor(name) + key;
}

private static <T> T valueFromLoader(Object key, Callable<T> valueLoader) {

try {
return valueLoader.call();
} catch (Exception e) {
throw new ValueRetrievalException(key, valueLoader, e);
}
}
}
2 changes: 2 additions & 0 deletions src/main/java/top/yunshu/shw/server/ShwServerApplication.java
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
import org.slf4j.LoggerFactory;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cache.annotation.EnableCaching;
import org.springframework.scheduling.annotation.EnableScheduling;
import springfox.documentation.swagger2.annotations.EnableSwagger2;

Expand All @@ -16,6 +17,7 @@
@SpringBootApplication
@EnableSwagger2
@EnableScheduling
@EnableCaching
public class ShwServerApplication {
private static final Logger logger = LoggerFactory.getLogger(ShwServerApplication.class);

Expand Down
Loading

0 comments on commit a357f7d

Please sign in to comment.