欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

Redis結(jié)合AOP與自定義注解實(shí)現(xiàn)分布式緩存流程詳解

 更新時(shí)間:2022年11月21日 10:55:46   作者:小學(xué)生波波  
項(xiàng)目中如果查詢數(shù)據(jù)是直接到MySQL數(shù)據(jù)庫中查詢的話,會查磁盤走IO,效率會比較低,所以現(xiàn)在一般項(xiàng)目中都會使用緩存,目的就是提高查詢數(shù)據(jù)的速度,將數(shù)據(jù)存入緩存中,也就是內(nèi)存中,這樣查詢效率大大提高

1、背景

項(xiàng)目中如果查詢數(shù)據(jù)是直接到MySQL數(shù)據(jù)庫中查詢的話,會查磁盤走IO,效率會比較低,所以現(xiàn)在一般項(xiàng)目中都會使用緩存,目的就是提高查詢數(shù)據(jù)的速度,將數(shù)據(jù)存入緩存中,也就是內(nèi)存中,這樣查詢效率大大提高

分布式緩存方案

優(yōu)點(diǎn):

  • 使用Redis作為共享緩存 ,解決緩存不同步問題
  • Redis是獨(dú)立的服務(wù),緩存不用占應(yīng)用本身的內(nèi)存空間

什么樣的數(shù)據(jù)適合放到緩存中呢?

同時(shí)滿足下面兩個(gè)條件的數(shù)據(jù)就適合放緩存:

  • 經(jīng)常要查詢的數(shù)據(jù)
  • 不經(jīng)常改變的數(shù)據(jù)

接下來我們使用 AOP技術(shù) 來實(shí)現(xiàn)分布式緩存,這樣做的好處是避免重復(fù)代碼,極大減少了工作量

2、目標(biāo)

我們希望分布式緩存能幫我們達(dá)到這樣的目標(biāo):

  • 對業(yè)務(wù)代碼無侵入(或侵入性較小)
  • 使用起來非常方便,最好是打一個(gè)注解就可以了,可插拔式的
  • 對性能影響盡可能的小
  • 要便于后期維護(hù)

3、方案

此處我們選擇的方案就是:AOP+自定義注解+Redis

  • 自定義一個(gè)注解,需要做緩存的接口打上這個(gè)注解即可
  • 使用Spring AOP的環(huán)繞通知增強(qiáng)被自定義注解修飾的方法,把緩存的存儲和刪除都放這里統(tǒng)一處理
  • 那么需要用到分布式鎖的接口,只需要打一個(gè)注解即可,這樣才夠靈活優(yōu)雅

4、實(shí)戰(zhàn)編碼

4.1、環(huán)境準(zhǔn)備

首先我們需要一個(gè)簡單的SpringBoot項(xiàng)目環(huán)境,這里我寫了一個(gè)基礎(chǔ)Demo版本,地址如下:

https://gitee.com/colinWu_java/spring-boot-base.git

大家可以先下載下來,本文就是基于這份主干代碼進(jìn)行修改的

4.2、pom依賴

pom.xml中需要新增以下依賴:

<!-- aop -->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-aop</artifactId>
</dependency>
<!--redis-->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
<!--jackson-->
<dependency>
    <groupId>com.fasterxml.jackson.core</groupId>
    <artifactId>jackson-databind</artifactId>
    <version>2.10.5.1</version>
</dependency>
<dependency>
    <groupId>com.fasterxml.jackson.core</groupId>
    <artifactId>jackson-core</artifactId>
    <version>2.11.1</version>
</dependency>

4.3、自定義注解

添加緩存的注解

package org.wujiangbo.annotation;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
 * @desc 自定義注解:向緩存中添加數(shù)據(jù)
 */
@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
public @interface MyCache {
    String cacheNames() default "";
    String key() default "";
    //緩存時(shí)間(單位:秒,默認(rèn)是無限期)
    int time() default -1;
}

刪除緩存注解:

package org.wujiangbo.annotation;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
 * @desc 自定義注解:從緩存中刪除數(shù)據(jù)
 */
@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
public @interface MyCacheEvict {
    String cacheNames() default "";
    String key() default "";
}

4.4、切面處理類

下面兩個(gè)切面類實(shí)際上是可以寫在一個(gè)類中的,但是為了方便理解和觀看,我分開寫了

package org.wujiangbo.aop;

import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.stereotype.Component;
import org.wujiangbo.annotation.MyCache;
import org.wujiangbo.service.RedisService;
import javax.annotation.Resource;
import java.util.concurrent.TimeUnit;
/**
 * @desc 切面類,處理分布式緩存添加功能
 */
@Aspect
@Component
@Slf4j
public class MyCacheAop {
    @Resource
    private RedisService redisService;
    /**
     * 定義切點(diǎn)
     */
    @Pointcut("@annotation(myCache)")
    public void pointCut(MyCache myCache){
    }
    /**
     * 環(huán)繞通知
     */
    @Around("pointCut(myCache)")
    public Object around(ProceedingJoinPoint joinPoint, MyCache myCache) {
        String cacheNames = myCache.cacheNames();
        String key = myCache.key();
        int time = myCache.time();
        /**
         * 思路:
         * 1、拼裝redis中存緩存的key值
         * 2、看redis中是否存在該key
         * 3、如果存在,直接取出來返回即可,不需要執(zhí)行目標(biāo)方法了
         * 4、如果不存在,就執(zhí)行目標(biāo)方法,然后將緩存放一份到redis中
         */
        String redisKey = new StringBuilder(cacheNames).append(":").append(key).toString();
        String methodPath = joinPoint.getTarget().getClass().getName() + "." + joinPoint.getSignature().getName();
        Object result ;
        if (redisService.exists(redisKey)){
            log.info("訪問接口:[{}],直接從緩存獲取數(shù)據(jù)", methodPath);
            return redisService.getCacheObject(redisKey);
        }
        try {
            //執(zhí)行接口
            result = joinPoint.proceed();
            //接口返回結(jié)果存Redis
            redisService.setCacheObject(redisKey, result, time, TimeUnit.SECONDS);
            log.info("訪問接口:[{}],返回值存入緩存成功", methodPath);
        } catch (Throwable e) {
            log.error("發(fā)生異常:{}", e);
            throw new RuntimeException(e);
        }
        return result;
    }
}

還有一個(gè):

package org.wujiangbo.aop;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.stereotype.Component;
import org.wujiangbo.annotation.MyCacheEvict;
import org.wujiangbo.service.RedisService;
import javax.annotation.Resource;
/**
 * @desc 切面類,處理分布式緩存刪除功能
 */
@Aspect
@Component
@Slf4j
public class MyCacheEvictAop {
    @Resource
    private RedisService redisService;
    /**
     * 定義切點(diǎn)
     */
    @Pointcut("@annotation(myCache)")
    public void pointCut(MyCacheEvict myCache){
    }
    /**
     * 環(huán)繞通知
     */
    @Around("pointCut(myCache)")
    public Object around(ProceedingJoinPoint joinPoint, MyCacheEvict myCache) {
        String cacheNames = myCache.cacheNames();
        String key = myCache.key();
        /**
         * 思路:
         * 1、拼裝redis中存緩存的key值
         * 2、刪除緩存
         * 3、執(zhí)行目標(biāo)接口業(yè)務(wù)代碼
         * 4、再刪除緩存
         */
        String redisKey = new StringBuilder(cacheNames).append(":").append(key).toString();
        String methodPath = joinPoint.getTarget().getClass().getName() + "." + joinPoint.getSignature().getName();
        Object result ;
        //刪除緩存
        redisService.deleteObject(redisKey);
        try {
            //執(zhí)行接口
            result = joinPoint.proceed();
            //刪除緩存
            redisService.deleteObject(redisKey);
            log.info("訪問接口:[{}],緩存刪除成功", methodPath);
        } catch (Throwable e) {
            log.error("發(fā)生異常:{}", e);
            throw new RuntimeException(e);
        }
        return result;
    }
}

4.5、工具類

Redis的工具類:

package org.wujiangbo.service;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.BoundSetOperations;
import org.springframework.data.redis.core.HashOperations;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.ValueOperations;
import org.springframework.stereotype.Component;
import java.util.*;
import java.util.concurrent.TimeUnit;
/**
 * @desc Redis工具類
 */
@Component  //交給Spring來管理 的自定義組件
public class RedisService {
    @Autowired
    public RedisTemplate redisTemplate;
    /**
     * 查看key是否存在
     */
    public boolean exists(String key)
    {
        return redisTemplate.hasKey(key);
    }
    /**
     * 清空Redis所有緩存數(shù)據(jù)
     */
    public void clearAllRedisData()
    {
        Set<String> keys = redisTemplate.keys("*");
        redisTemplate.delete(keys);
    }
    /**
     * 緩存基本的對象,Integer、String、實(shí)體類等
     *
     * @param key 緩存的鍵值
     * @param value 緩存的值
     */
    public <T> void setCacheObject(final String key, final T value)
    {
        redisTemplate.opsForValue().set(key, value);
    }
    /**
     * 緩存基本的對象,Integer、String、實(shí)體類等
     *
     * @param key 緩存的鍵值
     * @param value 緩存的值
     * @param timeout 時(shí)間
     * @param timeUnit 時(shí)間顆粒度
     */
    public <T> void setCacheObject(final String key, final T value, final Integer timeout, final TimeUnit timeUnit)
    {
        if(timeout == -1){
            //永久有效
            redisTemplate.opsForValue().set(key, value);
        }
        else{
            redisTemplate.opsForValue().set(key, value, timeout, timeUnit);
        }
    }
    /**
     * 設(shè)置有效時(shí)間
     *
     * @param key Redis鍵
     * @param timeout 超時(shí)時(shí)間
     * @return true=設(shè)置成功;false=設(shè)置失敗
     */
    public boolean expire(final String key, final long timeout)
    {
        return expire(key, timeout, TimeUnit.SECONDS);
    }
    /**
     * 設(shè)置有效時(shí)間
     *
     * @param key Redis鍵
     * @param timeout 超時(shí)時(shí)間
     * @param unit 時(shí)間單位
     * @return true=設(shè)置成功;false=設(shè)置失敗
     */
    public boolean expire(final String key, final long timeout, final TimeUnit unit)
    {
        return redisTemplate.expire(key, timeout, unit);
    }
    /**
     * 獲得緩存的基本對象。
     *
     * @param key 緩存鍵值
     * @return 緩存鍵值對應(yīng)的數(shù)據(jù)
     */
    public <T> T getCacheObject(final String key)
    {
        ValueOperations<String, T> operation = redisTemplate.opsForValue();
        return operation.get(key);
    }
    /**
     * 刪除單個(gè)對象
     *
     * @param key
     */
    public boolean deleteObject(final String key)
    {
        if(exists(key)){
            redisTemplate.delete(key);
        }
        return true;
    }
    /**
     * 刪除集合對象
     *
     * @param collection 多個(gè)對象
     * @return
     */
    public long deleteObject(final Collection collection)
    {
        return redisTemplate.delete(collection);
    }
    /**
     * 緩存List數(shù)據(jù)
     *
     * @param key 緩存的鍵值
     * @param dataList 待緩存的List數(shù)據(jù)
     * @return 緩存的對象
     */
    public <T> long setCacheList(final String key, final List<T> dataList)
    {
        Long count = redisTemplate.opsForList().rightPushAll(key, dataList);
        return count == null ? 0 : count;
    }
    /**
     * 獲得緩存的list對象
     *
     * @param key 緩存的鍵值
     * @return 緩存鍵值對應(yīng)的數(shù)據(jù)
     */
    public <T> List<T> getCacheList(final String key)
    {
        return redisTemplate.opsForList().range(key, 0, -1);
    }
    /**
     * 緩存Set
     *
     * @param key 緩存鍵值
     * @param dataSet 緩存的數(shù)據(jù)
     * @return 緩存數(shù)據(jù)的對象
     */
    public <T> BoundSetOperations<String, T> setCacheSet(final String key, final Set<T> dataSet)
    {
        BoundSetOperations<String, T> setOperation = redisTemplate.boundSetOps(key);
        Iterator<T> it = dataSet.iterator();
        while (it.hasNext())
        {
            setOperation.add(it.next());
        }
        return setOperation;
    }
    /**
     * 獲得緩存的set
     *
     * @param key
     * @return
     */
    public <T> Set<T> getCacheSet(final String key)
    {
        return redisTemplate.opsForSet().members(key);
    }
    /**
     * 緩存Map
     *
     * @param key
     * @param dataMap
     */
    public <T> void setCacheMap(final String key, final Map<String, T> dataMap)
    {
        if (dataMap != null) {
            redisTemplate.opsForHash().putAll(key, dataMap);
        }
    }
    /**
     * 獲得緩存的Map
     *
     * @param key
     * @return
     */
    public <T> Map<String, T> getCacheMap(final String key)
    {
        return redisTemplate.opsForHash().entries(key);
    }
    /**
     * 往Hash中存入數(shù)據(jù)
     *
     * @param key Redis鍵
     * @param hKey Hash鍵
     * @param value 值
     */
    public <T> void setCacheMapValue(final String key, final String hKey, final T value)
    {
        redisTemplate.opsForHash().put(key, hKey, value);
    }
    /**
     * 獲取Hash中的數(shù)據(jù)
     *
     * @param key Redis鍵
     * @param hKey Hash鍵
     * @return Hash中的對象
     */
    public <T> T getCacheMapValue(final String key, final String hKey)
    {
        HashOperations<String, String, T> opsForHash = redisTemplate.opsForHash();
        return opsForHash.get(key, hKey);
    }
    /**
     * 獲取多個(gè)Hash中的數(shù)據(jù)
     *
     * @param key Redis鍵
     * @param hKeys Hash鍵集合
     * @return Hash對象集合
     */
    public <T> List<T> getMultiCacheMapValue(final String key, final Collection<Object> hKeys)
    {
        return redisTemplate.opsForHash().multiGet(key, hKeys);
    }
    /**
     * 獲得緩存的基本對象列表
     *
     * @param pattern 字符串前綴
     * @return 對象列表
     */
    public Collection<String> keys(final String pattern)
    {
        return redisTemplate.keys(pattern);
    }
}

4.6、配置類

package org.wujiangbo.config;
import com.fasterxml.jackson.annotation.JsonAutoDetect;
import com.fasterxml.jackson.annotation.JsonTypeInfo;
import com.fasterxml.jackson.annotation.PropertyAccessor;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.jsontype.impl.LaissezFaireSubTypeValidator;
import org.springframework.cache.annotation.CachingConfigurerSupport;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.script.DefaultRedisScript;
import org.springframework.data.redis.serializer.StringRedisSerializer;
import javax.annotation.Resource;
/**
 * @desc redis配置類
 */
@Configuration
public class RedisSerializableConfig extends CachingConfigurerSupport {
    @Resource
    private RedisConnectionFactory factory;
    @Bean
    public RedisTemplate<Object, Object> redisTemplate()
    {
        RedisTemplate<Object, Object> template = new RedisTemplate<>();
        template.setConnectionFactory(factory);
        FastJson2JsonRedisSerializer serializer = new FastJson2JsonRedisSerializer(Object.class);
        ObjectMapper mapper = new ObjectMapper();
        mapper.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
        mapper.activateDefaultTyping(LaissezFaireSubTypeValidator.instance, ObjectMapper.DefaultTyping.NON_FINAL, JsonTypeInfo.As.PROPERTY);
        serializer.setObjectMapper(mapper);
        // 使用StringRedisSerializer來序列化和反序列化redis的key值
        template.setKeySerializer(new StringRedisSerializer());
        template.setValueSerializer(serializer);
        // Hash的key也采用StringRedisSerializer的序列化方式
        template.setHashKeySerializer(new StringRedisSerializer());
        template.setHashValueSerializer(serializer);
        template.afterPropertiesSet();
        return template;
    }
    @Bean
    public DefaultRedisScript<Long> limitScript()
    {
        DefaultRedisScript<Long> redisScript = new DefaultRedisScript<>();
        redisScript.setScriptText(limitScriptText());
        redisScript.setResultType(Long.class);
        return redisScript;
    }
    /**
     * 限流腳本
     */
    private String limitScriptText()
    {
        return "local key = KEYS[1]\n" +
                "local count = tonumber(ARGV[1])\n" +
                "local time = tonumber(ARGV[2])\n" +
                "local current = redis.call('get', key);\n" +
                "if current and tonumber(current) > count then\n" +
                "    return tonumber(current);\n" +
                "end\n" +
                "current = redis.call('incr', key)\n" +
                "if tonumber(current) == 1 then\n" +
                "    redis.call('expire', key, time)\n" +
                "end\n" +
                "return tonumber(current);";
    }
}

FastJson2JsonRedisSerializer類:

package org.wujiangbo.config;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.parser.ParserConfig;
import com.alibaba.fastjson.serializer.SerializerFeature;
import com.fasterxml.jackson.databind.JavaType;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.type.TypeFactory;
import org.springframework.data.redis.serializer.RedisSerializer;
import org.springframework.data.redis.serializer.SerializationException;
import org.springframework.util.Assert;
import java.nio.charset.Charset;
/**
 * @desc Redis使用FastJson序列化
 */
public class FastJson2JsonRedisSerializer<T> implements RedisSerializer<T>
{
    @SuppressWarnings("unused")
    private ObjectMapper objectMapper = new ObjectMapper();

    public static final Charset DEFAULT_CHARSET = Charset.forName("UTF-8");
    private Class<T> clazz;
    static
    {
        ParserConfig.getGlobalInstance().setAutoTypeSupport(true);
    }
    public FastJson2JsonRedisSerializer(Class<T> clazz)
    {
        super();
        this.clazz = clazz;
    }
    @Override
    public byte[] serialize(T t) throws SerializationException
    {
        if (t == null)
        {
            return new byte[0];
        }
        return JSON.toJSONString(t, SerializerFeature.WriteClassName).getBytes(DEFAULT_CHARSET);
    }
    @Override
    public T deserialize(byte[] bytes) throws SerializationException
    {
        if (bytes == null || bytes.length <= 0)
        {
            return null;
        }
        String str = new String(bytes, DEFAULT_CHARSET);

        return JSON.parseObject(str, clazz);
    }
    public void setObjectMapper(ObjectMapper objectMapper)
    {
        Assert.notNull(objectMapper, "'objectMapper' must not be null");
        this.objectMapper = objectMapper;
    }
    protected JavaType getJavaType(Class<?> clazz)
    {
        return TypeFactory.defaultInstance().constructType(clazz);
    }
}

4.7、yml配置

server:
  port: 8001
  undertow:
    # 設(shè)置IO線程數(shù), 它主要執(zhí)行非阻塞的任務(wù),它們會負(fù)責(zé)多個(gè)連接, 默認(rèn)設(shè)置每個(gè)CPU核心一個(gè)線程
    # 不要設(shè)置過大,如果過大,啟動項(xiàng)目會報(bào)錯(cuò):打開文件數(shù)過多(CPU有幾核,就填寫幾)
    io-threads: 6
    # 阻塞任務(wù)線程池, 當(dāng)執(zhí)行類似servlet請求阻塞IO操作, undertow會從這個(gè)線程池中取得線程
    # 它的值設(shè)置取決于系統(tǒng)線程執(zhí)行任務(wù)的阻塞系數(shù),默認(rèn)值是:io-threads * 8
    worker-threads: 48
    # 以下的配置會影響buffer,這些buffer會用于服務(wù)器連接的IO操作,有點(diǎn)類似netty的池化內(nèi)存管理
    # 每塊buffer的空間大小,越小的空間被利用越充分,不要設(shè)置太大,以免影響其他應(yīng)用,合適即可
    buffer-size: 1024
    # 每個(gè)區(qū)分配的buffer數(shù)量 , 所以pool的大小是buffer-size * buffers-per-region
    buffers-per-region: 1024
    # 是否分配的直接內(nèi)存(NIO直接分配的堆外內(nèi)存)
    direct-buffers: true
spring:
  #配置數(shù)據(jù)庫鏈接信息
  datasource:
    url: jdbc:mysql://127.0.0.1:3306/test1?useSSL=false&useUnicode=true&characterEncoding=utf-8&allowMultiQueries=true&rewriteBatchedStatements=true
    username: root
    password: 123456
    driver-class-name: com.mysql.jdbc.Driver
    type: com.alibaba.druid.pool.DruidDataSource
  application:
    name: springboot #服務(wù)名
  #redis配置
  redis:
    # 數(shù)據(jù)庫索引
    database: 0
    # 地址
    host: 127.0.0.1
    # 端口,默認(rèn)為6379
    port: 6379
    # 密碼
    password: 123456
    # 連接超時(shí)時(shí)間
    timeout: 10000

#MyBatis-Plus相關(guān)配置
mybatis-plus:
  #指定Mapper.xml路徑,如果與Mapper路徑相同的話,可省略
  mapper-locations: classpath:org/wujiangbo/mapper/*Mapper.xml
  configuration:
    map-underscore-to-camel-case: true #開啟駝峰大小寫自動轉(zhuǎn)換
    log-impl: org.apache.ibatis.logging.stdout.StdOutImpl #開啟控制臺sql輸出

4.8、使用

Controller中寫兩個(gè)接口分別測試一下緩存的新增和刪除

package org.wujiangbo.controller;
import lombok.extern.slf4j.Slf4j;
import org.wujiangbo.annotation.CheckPermission;
import org.wujiangbo.annotation.MyCache;
import org.wujiangbo.annotation.MyCacheEvict;
import org.wujiangbo.result.JSONResult;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
/**
 * @desc 測試接口類
 */
@RestController
@Slf4j
public class TestController {
    //測試刪除緩存
    @GetMapping("/deleteCache")
    @MyCacheEvict(cacheNames = "cacheTest", key = "userData")
    public JSONResult deleteCache(){
        System.out.println("deleteCache success");
        return JSONResult.success("deleteCache success");
    }
    //測試新增緩存
    @GetMapping("/addCache")
    @MyCache(cacheNames = "cacheTest", key = "userData")
    public JSONResult addCache(){
        System.out.println("addCache success");
        return JSONResult.success("addCache success");
    }
}

4.9、測試

瀏覽器先訪問:http://localhost:8001/addCache

然后再通過工具查看Redis中是不是添加了緩存數(shù)據(jù),正確情況應(yīng)該是緩存添加進(jìn)去了

然后再訪問:http://localhost:8001/deleteCache

再通過工具查看Redis,緩存應(yīng)該是被刪除了,沒有了

到此完全符合預(yù)期,測試成功

總結(jié)

本文主要是介紹了分布式緩存利用AOP+注解的方式處理,方便使用和擴(kuò)展希望對大家有所幫助

最后本案例代碼已全部提交到gitee中了,地址如下:

https://gitee.com/colinWu_java/spring-boot-base.git

本文新增的代碼在【RedisDistributedCache】分支中

到此這篇關(guān)于Redis結(jié)合AOP與自定義注解實(shí)現(xiàn)分布式緩存流程詳解的文章就介紹到這了,更多相關(guān)Redis分布式緩存內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • java讀取解析xml文件實(shí)例

    java讀取解析xml文件實(shí)例

    這篇文章主要介紹了java讀取解析xml文件實(shí)例,本文創(chuàng)建了一個(gè)XML解析類同時(shí)講解了循環(huán)節(jié)點(diǎn)輸出方式,需要的朋友可以參考下
    2015-03-03
  • Java基于JDBC實(shí)現(xiàn)事務(wù),銀行轉(zhuǎn)賬及貨物進(jìn)出庫功能示例

    Java基于JDBC實(shí)現(xiàn)事務(wù),銀行轉(zhuǎn)賬及貨物進(jìn)出庫功能示例

    這篇文章主要介紹了Java基于JDBC實(shí)現(xiàn)事務(wù),銀行轉(zhuǎn)賬及貨物進(jìn)出庫功能,較為詳細(xì)的分析了事務(wù)操作的原理、實(shí)現(xiàn)方法及java基于jdbc連接數(shù)據(jù)庫實(shí)現(xiàn)銀行事務(wù)操作的相關(guān)技巧,需要的朋友可以參考下
    2017-12-12
  • Java AOP動態(tài)代理詳細(xì)介紹

    Java AOP動態(tài)代理詳細(xì)介紹

    AOP是一種設(shè)計(jì)思想,是軟件設(shè)計(jì)領(lǐng)域中的面向切面編程,它是面向?qū)ο缶幊痰囊环N補(bǔ)充和完善。本文將用Java實(shí)現(xiàn)AOP代理的三種方式,需要的可以參考一下
    2022-08-08
  • Java實(shí)現(xiàn)表達(dá)式二叉樹

    Java實(shí)現(xiàn)表達(dá)式二叉樹

    這篇文章主要為大家詳細(xì)介紹了如何利用Java實(shí)現(xiàn)表達(dá)式二叉樹,感興趣的小伙伴們可以參考一下
    2016-08-08
  • spring-core組件詳解——PropertyResolver屬性解決器

    spring-core組件詳解——PropertyResolver屬性解決器

    這篇文章主要介紹了spring-core組件詳解——PropertyResolver屬性解決器,需要的朋友可以參考下
    2016-05-05
  • 淺析Java中靜態(tài)代理和動態(tài)代理的應(yīng)用與區(qū)別

    淺析Java中靜態(tài)代理和動態(tài)代理的應(yīng)用與區(qū)別

    代理模式在我們生活中很常見,而Java中常用的兩個(gè)的代理模式就是動態(tài)代理與靜態(tài)代理,這篇文章主要為大家介紹了二者的應(yīng)用與區(qū)別,需要的可以參考下
    2023-08-08
  • 淺談SpringBoot主流讀取配置文件三種方式

    淺談SpringBoot主流讀取配置文件三種方式

    這篇文章主要介紹了淺談SpringBoot主流讀取配置文件三種方式,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2020-09-09
  • IDEA 熱部署設(shè)置(JRebel插件激活)

    IDEA 熱部署設(shè)置(JRebel插件激活)

    這篇文章主要介紹了IDEA 熱部署設(shè)置(JRebel插件激活),文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2019-08-08
  • Java使用wait/notify實(shí)現(xiàn)線程間通信上篇

    Java使用wait/notify實(shí)現(xiàn)線程間通信上篇

    wait()和notify()是直接隸屬于Object類,也就是說所有對象都擁有這一對方法,下面這篇文章主要給大家介紹了關(guān)于使用wait/notify實(shí)現(xiàn)線程間通信的相關(guān)資料,需要的朋友可以參考下
    2022-12-12
  • Java NIO:淺析IO模型_動力節(jié)點(diǎn)Java學(xué)院整理

    Java NIO:淺析IO模型_動力節(jié)點(diǎn)Java學(xué)院整理

    在進(jìn)入Java NIO編程之前,我們今天先來討論一些比較基礎(chǔ)的知識:I/O模型。對java io nio相關(guān)知識感興趣的朋友一起學(xué)習(xí)吧
    2017-05-05

最新評論