多说无益,直接上图,当然此项目不仅包含redis,所以其他不相关可以忽略。
主要代码分先后依次展示:源码会上传git,所以不多展示,只介绍重点
项目目录结构:
pom文件:其中版本一定要注意,低版本会报些许异常。
只要引入相关redis即可。
org.springframework.boot
spring-boot-starter-parent
2.0.8.RELEASE
org.springframework.boot
spring-boot-starter-data-redis
org.springframework.session
spring-session-data-redis
org.springframework.boot
spring-boot-starter-cache
redis.clients
jedis
application.properties
本配置文件由于涉及到其他方面,也不删了,看需要删减。
server.port=8080
spring.tomcat.uri-encoding=utf-8
spring.thymeleaf.prefix=classpath:/templates/
logging.level.com.king.mapper=debug
#DB MYSQL Configuration:
spring.datasource.mysql.driverClassName = com.mysql.jdbc.Driver
spring.datasource.mysql.url = jdbc:mysql://localhost:3306/base?useUnicode=true&characterEncoding=utf-8&relaxAutoCommit=true&zeroDateTimeBehavior=convertToNull
spring.datasource.mysql.username = root
spring.datasource.mysql.password = 123456
#spring.datasource.mysql.maximun-pool-size\u9ed8\u8ba4\u4e3a100
#\u9a8c\u8bc1\u8fde\u63a5\u7684\u6709\u6548\u6027
spring.datasource.mysql.test-while-idle=true
#\u83b7\u53d6\u8fde\u63a5\u65f6\u5019\u9a8c\u8bc1\uff0c\u4f1a\u5f71\u54cd\u6027\u80fd
spring.datasource.mysql.test-on-borrow=false
#\u5728\u8fde\u63a5\u5f52\u8fd8\u5230\u8fde\u63a5\u6c60\u65f6\u662f\u5426\u6d4b\u8bd5\u8be5\u8fde\u63a5
spring.datasource.mysql.test-on-return=false
spring.datasource.mysql.validation-query=SELECT 1 FROM DUAL
#\u7a7a\u95f2\u8fde\u63a5\u56de\u6536\u7684\u65f6\u95f4\u95f4\u9694\uff0c\u4e0etest-while-idle\u4e00\u8d77\u4f7f\u7528\uff0c\u8bbe\u7f6e5\u5206\u949f
spring.datasource.mysql.time-between-eviction-runs-millis=300000
#\u8fde\u63a5\u6c60\u7a7a\u95f2\u8fde\u63a5\u7684\u6709\u6548\u65f6\u95f4 \uff0c\u8bbe\u7f6e30\u5206\u949f
spring.datasource.mysql.min-evictable-idle-time-millis=1800000
spring.datasource.mysql.initial-size=5
#\u6307\u5b9a\u8fde\u63a5\u6c60\u4e2d\u6700\u5927\u7684\u6d3b\u8dc3\u8fde\u63a5\u6570.
spring.datasource.mysql.max-active=70
#\u6307\u5b9a\u8fde\u63a5\u6c60\u7b49\u5f85\u8fde\u63a5\u8fd4\u56de\u7684\u6700\u5927\u7b49\u5f85\u65f6\u95f4\uff0c\u6beb\u79d2\u5355\u4f4d.
spring.datasource.mysql.max-wait=60000
#\u6307\u5b9a\u5fc5\u987b\u4fdd\u6301\u8fde\u63a5\u7684\u6700\u5c0f\u503c
spring.datasource.mysql.min-idle=5
#\u6700\u5927\u7b49\u5f85\u8fde\u63a5\u4e2d\u7684\u6570\u91cf
spring.datasource.mysql.max-idle=20
#DB test Configuration:
spring.datasource.test.driverClassName = com.mysql.jdbc.Driver
spring.datasource.test.url = jdbc:mysql://localhost:3306/test?useUnicode=true&characterEncoding=utf-8&relaxAutoCommit=true&zeroDateTimeBehavior=convertToNull
spring.datasource.test.username = root
spring.datasource.test.password = 123456
#spring.datasource.opaq.maximun-pool-size\u9ed8\u8ba4\u4e3a100
#\u9a8c\u8bc1\u8fde\u63a5\u7684\u6709\u6548\u6027
spring.datasource.test.test-while-idle=true
#\u83b7\u53d6\u8fde\u63a5\u65f6\u5019\u9a8c\u8bc1\uff0c\u4f1a\u5f71\u54cd\u6027\u80fd
spring.datasource.test.test-on-borrow=false
#\u5728\u8fde\u63a5\u5f52\u8fd8\u5230\u8fde\u63a5\u6c60\u65f6\u662f\u5426\u6d4b\u8bd5\u8be5\u8fde\u63a5
spring.datasource.test.test-on-return=false
spring.datasource.test.validation-query=SELECT 1 FROM DUAL
#\u7a7a\u95f2\u8fde\u63a5\u56de\u6536\u7684\u65f6\u95f4\u95f4\u9694\uff0c\u4e0etest-while-idle\u4e00\u8d77\u4f7f\u7528\uff0c\u8bbe\u7f6e5\u5206\u949f
spring.datasource.test.time-between-eviction-runs-millis=300000
#\u8fde\u63a5\u6c60\u7a7a\u95f2\u8fde\u63a5\u7684\u6709\u6548\u65f6\u95f4 \uff0c\u8bbe\u7f6e30\u5206\u949f
spring.datasource.test.min-evictable-idle-time-millis=1800000
spring.datasource.test.initial-size=5
#\u6307\u5b9a\u8fde\u63a5\u6c60\u4e2d\u6700\u5927\u7684\u6d3b\u8dc3\u8fde\u63a5\u6570.
spring.datasource.test.max-active=70
#\u6307\u5b9a\u8fde\u63a5\u6c60\u7b49\u5f85\u8fde\u63a5\u8fd4\u56de\u7684\u6700\u5927\u7b49\u5f85\u65f6\u95f4\uff0c\u6beb\u79d2\u5355\u4f4d.
spring.datasource.test.max-wait=60000
#\u6307\u5b9a\u5fc5\u987b\u4fdd\u6301\u8fde\u63a5\u7684\u6700\u5c0f\u503c
spring.datasource.test.min-idle=5
#\u6700\u5927\u7b49\u5f85\u8fde\u63a5\u4e2d\u7684\u6570\u91cf
spring.datasource.test.max-idle=20
#redis配置
spring.redis.database=0
spring.redis.host=127.0.0.1
spring.redis.port=6379
spring.redis.password=123456
#连接超时时间
spring.redis.timeout=6000
#连接池最大连接数,负值无限制
spring.redis.jedis.pool.max-active=1000
#连接池最大阻塞等待时间,负值表示无限制
spring.redis.jedis.pool.max-wait=-1
#连接池最大空闲连接
spring.redis.jedis.pool.max-idle=10
#连接池最小空闲连接
spring.redis.jedis.pool.min-idle=5
spring.cache.type=none
#是否开启redis缓存
king.redis.open=true
redis配置类:
package com.king.common.redis;
import java.time.Duration;
import org.springframework.boot.autoconfigure.AutoConfigureAfter;
import org.springframework.boot.autoconfigure.data.redis.RedisAutoConfiguration;
import org.springframework.boot.autoconfigure.data.redis.RedisProperties;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import org.springframework.data.redis.connection.RedisPassword;
import org.springframework.data.redis.connection.RedisStandaloneConfiguration;
import org.springframework.data.redis.connection.jedis.JedisClientConfiguration;
import org.springframework.data.redis.connection.jedis.JedisConnectionFactory;
import redis.clients.jedis.JedisPoolConfig;
@Configuration
//此注解会自动获取application.properties中的redis配置值
@AutoConfigureAfter(RedisAutoConfiguration.class)
@EnableConfigurationProperties(RedisProperties.class)
public class RedisTemplateConfig {
private RedisProperties redisProperties;
public RedisTemplateConfig(RedisProperties redisProperties) {
this.redisProperties=redisProperties;
}
@Bean
@Primary
public JedisConnectionFactory jedisConnectionFactory() {
RedisStandaloneConfiguration configuration=new RedisStandaloneConfiguration();
configuration.setHostName(redisProperties.getHost());
configuration.setPort(redisProperties.getPort());
configuration.setPassword(RedisPassword.of(redisProperties.getPassword()));
configuration.setDatabase(redisProperties.getDatabase());
return new JedisConnectionFactory(configuration,getJedisClientConfiguration());
}
/**获取jedis客户端配置*/
private JedisClientConfiguration getJedisClientConfiguration() {
JedisClientConfiguration.JedisClientConfigurationBuilder builder=JedisClientConfiguration.builder();
//isssl,网络连接安全,一种安全协议
if(redisProperties.isSsl()) {
builder.useSsl();
}
//Duration,执行时间,超时时间
if(redisProperties.getTimeout()!=null) {
Duration timeOut=redisProperties.getTimeout();
builder.readTimeout(timeOut).connectTimeout(timeOut);
}
RedisProperties.Pool pool=redisProperties.getJedis().getPool();
if(pool!=null) {
builder.usePooling().poolConfig(jedisPoolConfig(pool));
}
return builder.build();
}
/**jedis连接池配置*/
private JedisPoolConfig jedisPoolConfig(RedisProperties.Pool redisProperties) {
JedisPoolConfig config=new JedisPoolConfig();
config.setMaxIdle(redisProperties.getMaxIdle());
config.setMaxTotal(redisProperties.getMaxActive());
config.setMinIdle(redisProperties.getMinIdle());
if(redisProperties.getMaxWait()!=null) {
config.setMaxWaitMillis(redisProperties.getMaxWait().toMillis());
}
return config;
}
/**redis的选择模板
* 有很多人问为什么用注解primary,不多说,这里是说明作用:自动装配时,如果出现多个bean的候选者,被注解为primary的bean将作为首选者,优先选择,否则会抛出异常。*/
@Bean(name="redisTemplate")
@Primary
public RedisSelectConfig redisSelectConfig() {
RedisSelectConfig tem=new RedisSelectConfig();
tem.setConnectionFactory(jedisConnectionFactory());
return tem;
}
}
RedisSelectSuport--redis切换库支持类
package com.king.common.redis;
/**redis切换库支持类*/
public class RedisSelectSuport {
private static final ThreadLocal SELECT_CONTEXT=new ThreadLocal<>();
public static void select(int db) {
SELECT_CONTEXT.set(db);
}
public static Integer getSelect() {
return SELECT_CONTEXT.get();
}
}
RedisSelect--自定义注解类
package com.king.common.redis;
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**自定义注解RedisSelect*/
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface RedisSelect {
//redis库,默认为0
int value() default 0;
}
RedisSelectConfig--redis切换支持类,切换执行类。
package com.king.common.redis;
import org.springframework.data.redis.connection.RedisConnection;
import org.springframework.data.redis.core.StringRedisTemplate;
public class RedisSelectConfig extends StringRedisTemplate{
@Override
protected RedisConnection createRedisConnectionProxy(RedisConnection pm) {
return super.createRedisConnectionProxy(pm);
}
@Override
protected RedisConnection preProcessConnection(RedisConnection connection, boolean existingConnection) {
Integer db;
if((db = RedisSelectSuport.getSelect()) != null){
connection.select(db);
}
return super.preProcessConnection(connection, existingConnection);
}
}
RedisAspect--切面类,重点,在此利用环绕通知,实现切面执行。
package com.king.common.redis;
import java.lang.reflect.Method;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.reflect.MethodSignature;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
import org.springframework.stereotype.Component;
@Aspect
@Component
public class RedisAspect {
private static Logger log=LoggerFactory.getLogger(RedisAspect.class);
@Value("${king.redis.open}")
private boolean open;
@Value("${spring.redis.database}")
private int defaultDatabase;
/**这里解释下point.proceed用法。
* 环绕通知,简单说就是:环绕通知=前绕通知+目标方法执行+后置通知。
* proceed方法就是用于启动目标方法执行的。
* 如果在Around中不调用point.proceed(),那@Before注解的方法不会调用,不过after还是会调用*/
@Around("execution(* com.king.util.RedisUtilsone.*(..))")
public Object around(ProceedingJoinPoint point) throws Throwable {
Object result=null;
if(open) {
try {
//调用执行目标方法(result为目标方法执行结果)
result=point.proceed();
} catch (Exception e) {
// TODO: handle exception
log.error("redis error"+e);
}
}
return result;
}
@Around("@annotation(com.king.common.redis.RedisSelect)")
@ConditionalOnBean(RedisSelectConfig.class)
public Object configRedis(ProceedingJoinPoint point) throws Throwable {
int db=defaultDatabase;
try {
MethodSignature signature=(MethodSignature) point.getSignature();
Method method=signature.getMethod();
RedisSelect config=method.getAnnotation(RedisSelect.class);
if(config!=null) {
db=config.value();
}
RedisSelectSuport.select(db);
return point.proceed();
}finally {
RedisSelectSuport.select(defaultDatabase);
log.debug("redis reset {} to {}",db,defaultDatabase);
}
}
}
RedisUtilsone--redis工具类,暂时少许操作,后期追加完善。
package com.king.util;
import java.util.concurrent.TimeUnit;
import javax.annotation.PostConstruct;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.data.redis.core.ValueOperations;
import org.springframework.stereotype.Component;
import com.alibaba.fastjson.JSON;
@Component
public class RedisUtilsone {
@Autowired
private StringRedisTemplate redisTemplate;
private ValueOperations valueOperations=null;
@PostConstruct
public void init() {
this.valueOperations=redisTemplate.opsForValue();
}
/**定义默认过期时长*/
public final static long DEFAULT_EXPIRE=60*60*24;
/**不设置过期时长*/
public final static long NOT_EXPIRE=-1;
public void set(String key,Object value,long expire) {
valueOperations.set(key, toJson(value));
if(expire!=NOT_EXPIRE) {
redisTemplate.expire(key, expire, TimeUnit.SECONDS);
}
}
public void set(String key, Object value){
set(key, value, DEFAULT_EXPIRE);
}
public T get(String key, Class clazz, long expire) {
String value = valueOperations.get(key);
if(expire != NOT_EXPIRE){
redisTemplate.expire(key, expire, TimeUnit.SECONDS);
}
return value == null ? null : fromJson(value, clazz);
}
public T get(String key, Class clazz) {
return get(key, clazz, NOT_EXPIRE);
}
public String get(String key, long expire) {
String value = valueOperations.get(key);
if(expire != NOT_EXPIRE){
redisTemplate.expire(key, expire, TimeUnit.SECONDS);
}
return value;
}
public String get(String key) {
return get(key, NOT_EXPIRE);
}
public void delete(String key) {
redisTemplate.delete(key);
}
/**
* Object转成JSON数据
*/
private String toJson(Object object){
if(object instanceof Integer || object instanceof Long || object instanceof Float ||
object instanceof Double || object instanceof Boolean || object instanceof String){
return String.valueOf(object);
}
return JSON.toJSONString(object);
}
/**
* JSON数据,转成Object
*/
private T fromJson(String json, Class clazz){
return JSON.parseObject(json, clazz);
}
}
RedisSelectController--直接测试,不搞那么多繁杂的。
package com.king.Controller;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import com.king.common.redis.RedisSelect;
import com.king.util.RedisUtilsone;
@RestController
@RequestMapping(value="/selectone")
public class RedisSelectController {
@Autowired
private StringRedisTemplate redisTemplate;
@Autowired
private RedisUtilsone utilsOne;
@RequestMapping(value="/setZhi")
@RedisSelect(1)
public String getZhi() {
//redisTemplate.opsForValue().set("hello", "测试");
/**这里调用工具类操作,会出现问题,无法进入到类中,是因为RedisAspect切面类中,有判断open(applications配置中),为false会不执行point.proceed,所以自然进不到工具类中。
* 将配置改为true,可以。*/
utilsOne.set("hello", "测试");
String hello=utilsOne.get("hello");
return hello;
}
@RequestMapping(value="/getZhi")
@RedisSelect(1)
public String getNum() {
String num=redisTemplate.opsForValue().get("hello");
return num;
}
}
SpringbootMybatisDemoApplication--启动类:
package com.king;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cache.annotation.EnableCaching;
import org.springframework.scheduling.annotation.EnableScheduling;
@SpringBootApplication
@EnableScheduling
@EnableCaching
@MapperScan("com.king.mapper")//将项目中对应的mapper类的路径加进来就可以了
public class SpringbootMybatisDemoApplication {
public static void main(String[] args) {
SpringApplication.run(SpringbootMybatisDemoApplication.class, args);
}
}
其中图片所展示项目结构,redis另一个pro配置,只是另外一个redis整合缓存和其他操作用到,这里忽略。
源码地址:敬请Fork跟Star。
https://github.com/wangdonghuihuang/HappyKing.git