中文亚洲精品无码_熟女乱子伦免费_人人超碰人人爱国产_亚洲熟妇女综合网

當(dāng)前位置: 首頁(yè) > news >正文

新網(wǎng)站 不穩(wěn)定網(wǎng)頁(yè)設(shè)計(jì)素材網(wǎng)站

新網(wǎng)站 不穩(wěn)定,網(wǎng)頁(yè)設(shè)計(jì)素材網(wǎng)站,江北網(wǎng)站建設(shè),如何做一個(gè)網(wǎng)站平臺(tái)快速導(dǎo)航 <1> 5分鐘快速創(chuàng)建一個(gè)springboot web項(xiàng)目 <2> 5分鐘集成好最新版本的開源swagger ui&#xff0c;并使用ui操作調(diào)用接口 <3> 5分鐘集成好druid并使用druid自帶監(jiān)控工具監(jiān)控sql請(qǐng)求 <4> 5分鐘集成好mybatisplus并使用mybatisplus generator自…

快速導(dǎo)航

<1> 5分鐘快速創(chuàng)建一個(gè)springboot web項(xiàng)目
<2> 5分鐘集成好最新版本的開源swagger ui,并使用ui操作調(diào)用接口
<3> 5分鐘集成好druid并使用druid自帶監(jiān)控工具監(jiān)控sql請(qǐng)求
<4> 5分鐘集成好mybatisplus并使用mybatisplus generator自動(dòng)生成代碼
<5> 5分鐘集成好caffeine并使用注解操作緩存
<6> 5分鐘集成好前端頁(yè)面,使用vue開發(fā)前端

目錄

  • 一、準(zhǔn)備工作
    • 1.1 maven 安裝caffeine和相關(guān)依賴
    • 1.2 配置caffeine
      • 1.2.1 javaconfig配置caffeine
      • 1.2.2 緩存代碼編寫
    • 1.3 注解說明
  • 二、測(cè)試
  • 三、注解操作緩存原理以及一些難點(diǎn)排查
    • 3.1 注解操作緩存原理
    • 3.2 難點(diǎn)排查
  • 總結(jié)

在 Spring Boot 中,注解操作緩存的原理基于 Spring 的緩存抽象 (Spring Cache Abstraction)。使用緩存注解時(shí),Spring 會(huì)自動(dòng)處理與緩存相關(guān)的邏輯,而開發(fā)者只需要專注于業(yè)務(wù)代碼。常用的緩存注解有 @Cacheable、@CachePut 和 @CacheEvict。

一、準(zhǔn)備工作

1.1 maven 安裝caffeine和相關(guān)依賴

<dependency><groupId>com.github.ben-manes.caffeine</groupId><artifactId>caffeine</artifactId><version>3.1.8</version> <!-- 確保版本兼容 -->
</dependency>
<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-cache</artifactId>
</dependency>

1.2 配置caffeine

有兩種配置方式

1.2.1 javaconfig配置caffeine

package com.example.demo.config;import com.github.benmanes.caffeine.cache.Caffeine;
import org.springframework.cache.CacheManager;
import org.springframework.cache.annotation.EnableCaching;
import org.springframework.cache.caffeine.CaffeineCacheManager;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;import java.util.List;
import java.util.concurrent.TimeUnit;@Configuration
@EnableCaching
public class CacheConfig {@Beanpublic Caffeine<Object, Object> caffeineConfig() {return Caffeine.newBuilder().maximumSize(1000).expireAfterWrite(5, TimeUnit.MINUTES)
//                .weakKeys() // 很坑會(huì),基本上很快就把key清理掉了,緩存再也命中不了.recordStats();}@Beanpublic CacheManager cacheManager(Caffeine<Object, Object> caffeine) {CaffeineCacheManager cacheManager = new CaffeineCacheManager();cacheManager.setCaffeine(caffeine);cacheManager.setCacheNames(List.of(new String[]{"users"}));return cacheManager;}}

1.2.2 緩存代碼編寫

TestController

package com.example.demo.web;import com.example.demo.entity.User;
import com.example.demo.service.IUserService;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.annotation.Resource;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;import java.util.List;@RestController
@RequestMapping("/test")
@Tag(name = "User API", description = "用戶管理相關(guān)接口")
public class TestController {@Resourceprivate IUserService userService;@GetMapping("")@Operation(method = "test", summary = "測(cè)試接口")public String test(){return "test";}@GetMapping("/user")@Operation(method = "allUsers", summary = "獲取所有用戶")public List<User> allUsers(){return userService.list();}@GetMapping("/user/{id}")@Operation(method = "getUserCache", summary = "獲取用戶緩存")public void testGetUserCache(@PathVariable String id){User user = userService.getUserById(id);System.out.println(user);}}

UserServiceImpl

package com.example.demo.service.impl;import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.example.demo.entity.User;
import com.example.demo.mapper.UserMapper;
import com.example.demo.service.IUserService;
import org.springframework.cache.annotation.CacheConfig;
import org.springframework.cache.annotation.CacheEvict;
import org.springframework.cache.annotation.CachePut;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.stereotype.Service;/*** <p>* 測(cè)試用戶 服務(wù)實(shí)現(xiàn)類* </p>** @author allens* @since 2025-01-15*/
@Service
public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements IUserService {@Cacheable(value = "users", key = "'user_' + #id", unless = "#result == null")public User getUserById(String id) {// 模擬數(shù)據(jù)庫(kù)查詢return this.getBaseMapper().selectById(id);}@CachePut(value = "users", key = "#id")public void updateUser(String id, String name) {// 模擬更新數(shù)據(jù)庫(kù)并返回新值User user = this.getBaseMapper().selectById(id);user.setName(name);this.getBaseMapper().updateById(user);}@CacheEvict(value = "users", key = "#id")public void deleteUser(String id) {// 模擬刪除數(shù)據(jù)庫(kù)記錄System.out.println("User with id " + id + " has been deleted from database and cache.");this.getBaseMapper().deleteById(id);}@CacheEvict(value = "users", allEntries = true)public void clearCache() {System.out.println("All user cache has been cleared.");}}

1.3 注解說明

請(qǐng)參考我的這篇文章 Springboot 注解使用詳解

二、測(cè)試

在這里插入圖片描述
點(diǎn)擊發(fā)送請(qǐng)求,第一次執(zhí)行了sql,第二次在方法體中打斷點(diǎn),發(fā)現(xiàn)沒有進(jìn)入。且日志未輸出相關(guān)查詢數(shù)據(jù)庫(kù)操作。

在這里插入圖片描述

三、注解操作緩存原理以及一些難點(diǎn)排查

3.1 注解操作緩存原理

在這里插入圖片描述

工作原理總結(jié):

1. 代理模式:Spring 使用動(dòng)態(tài)代理或 CGLIB 代理來攔截帶有緩存注解的方法調(diào)用。代理會(huì)在方法調(diào)用之前或之后進(jìn)行緩存操作。

開啟注解緩存是靠 @EnableCaching 來實(shí)現(xiàn)的,那么我們從這個(gè)注解開始入手:

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Import(CachingConfigurationSelector.class)
public @interface EnableCaching {/*** Indicate whether subclass-based (CGLIB) proxies are to be created as opposed* to standard Java interface-based proxies. The default is {@code false}. <strong>* Applicable only if {@link #mode()} is set to {@link AdviceMode#PROXY}</strong>.* <p>Note that setting this attribute to {@code true} will affect <em>all</em>* Spring-managed beans requiring proxying, not just those marked with {@code @Cacheable}.* For example, other beans marked with Spring's {@code @Transactional} annotation will* be upgraded to subclass proxying at the same time. This approach has no negative* impact in practice unless one is explicitly expecting one type of proxy vs another,* for example, in tests.*/boolean proxyTargetClass() default false;/*** Indicate how caching advice should be applied.* <p><b>The default is {@link AdviceMode#PROXY}.</b>* Please note that proxy mode allows for interception of calls through the proxy* only. Local calls within the same class cannot get intercepted that way;* a caching annotation on such a method within a local call will be ignored* since Spring's interceptor does not even kick in for such a runtime scenario.* For a more advanced mode of interception, consider switching this to* {@link AdviceMode#ASPECTJ}.*/AdviceMode mode() default AdviceMode.PROXY;/*** Indicate the ordering of the execution of the caching advisor* when multiple advices are applied at a specific joinpoint.* <p>The default is {@link Ordered#LOWEST_PRECEDENCE}.*/int order() default Ordered.LOWEST_PRECEDENCE;}

@Import(CachingConfigurationSelector.class) 我們進(jìn)入CachingConfigurationSelector 看下:

在這里插入圖片描述
點(diǎn)擊進(jìn)入ProxyCachingConfiguration

@Bean(name = CacheManagementConfigUtils.CACHE_ADVISOR_BEAN_NAME)@Role(BeanDefinition.ROLE_INFRASTRUCTURE)public BeanFactoryCacheOperationSourceAdvisor cacheAdvisor(CacheOperationSource cacheOperationSource, CacheInterceptor cacheInterceptor) {BeanFactoryCacheOperationSourceAdvisor advisor = new BeanFactoryCacheOperationSourceAdvisor();advisor.setCacheOperationSource(cacheOperationSource);advisor.setAdvice(cacheInterceptor);if (this.enableCaching != null) {advisor.setOrder(this.enableCaching.<Integer>getNumber("order"));}return advisor;
}@Bean
@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
public CacheOperationSource cacheOperationSource() {// Accept protected @Cacheable etc methods on CGLIB proxies, as of 6.0.return new AnnotationCacheOperationSource(false);
}@Bean
@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
public CacheInterceptor cacheInterceptor(CacheOperationSource cacheOperationSource) {CacheInterceptor interceptor = new CacheInterceptor();interceptor.configure(this.errorHandler, this.keyGenerator, this.cacheResolver, this.cacheManager);interceptor.setCacheOperationSource(cacheOperationSource);return interceptor;
}

我們可以看到動(dòng)態(tài)代理注入了一個(gè)interceptor,我們這個(gè)時(shí)候就可以猜測(cè),所有的緩存操作都是在這個(gè)interceptor里邊進(jìn)行操作的(不熟悉動(dòng)態(tài)代理原理的可以先去看下)。

public class CacheInterceptor extends CacheAspectSupport implements MethodInterceptor, Serializable {@Override@Nullablepublic Object invoke(final MethodInvocation invocation) throws Throwable {Method method = invocation.getMethod();CacheOperationInvoker aopAllianceInvoker = () -> {try {return invocation.proceed();}catch (Throwable ex) {throw new CacheOperationInvoker.ThrowableWrapper(ex);}};Object target = invocation.getThis();Assert.state(target != null, "Target must not be null");try {return execute(aopAllianceInvoker, target, method, invocation.getArguments());}catch (CacheOperationInvoker.ThrowableWrapper th) {throw th.getOriginal();}}
}

再點(diǎn)擊進(jìn)入 CacheAspectSupport 發(fā)現(xiàn)有個(gè)方法叫execute,禮拜呢有一個(gè)findCachedValue方法,這個(gè)就是查詢緩存有沒有命中,如果有命中直接返回緩存,如果沒命中那么就會(huì)執(zhí)行員原服務(wù)方法獲取數(shù)據(jù)。

@Nullable
private Object execute(CacheOperationInvoker invoker, Method method, CacheOperationContexts contexts) {if (contexts.isSynchronized()) {// Special handling of synchronized invocationreturn executeSynchronized(invoker, method, contexts);}// Process any early evictionsprocessCacheEvicts(contexts.get(CacheEvictOperation.class), true,CacheOperationExpressionEvaluator.NO_RESULT);// Check if we have a cached value matching the conditionsObject cacheHit = findCachedValue(invoker, method, contexts); // 查詢緩存if (cacheHit == null || cacheHit instanceof Cache.ValueWrapper) { // 判斷是否命中緩存return evaluate(cacheHit, invoker, method, contexts);}return cacheHit;
}

CacheAspectSupport.findCachedValue

@Nullable
private Object findCachedValue(CacheOperationInvoker invoker, Method method, CacheOperationContexts contexts) {for (CacheOperationContext context : contexts.get(CacheableOperation.class)) {if (isConditionPassing(context, CacheOperationExpressionEvaluator.NO_RESULT)) {Object key = generateKey(context, CacheOperationExpressionEvaluator.NO_RESULT);Object cached = findInCaches(context, key, invoker, method, contexts); // 查找緩存if (cached != null) {if (logger.isTraceEnabled()) {logger.trace("Cache entry for key '" + key + "' found in cache(s) " + context.getCacheNames());}return cached;}else {if (logger.isTraceEnabled()) {logger.trace("No cache entry for key '" + key + "' in cache(s) " + context.getCacheNames());}}}}return null;
}

CacheAspectSupport.findInCaches

@Nullable
private Object findInCaches(CacheOperationContext context, Object key,CacheOperationInvoker invoker, Method method, CacheOperationContexts contexts) {for (Cache cache : context.getCaches()) {if (CompletableFuture.class.isAssignableFrom(context.getMethod().getReturnType())) {CompletableFuture<?> result = doRetrieve(cache, key);  // 重試策略if (result != null) {return result.exceptionally(ex -> {getErrorHandler().handleCacheGetError((RuntimeException) ex, cache, key);return null;}).thenCompose(value -> (CompletableFuture<?>) evaluate((value != null ? CompletableFuture.completedFuture(unwrapCacheValue(value)) : null),invoker, method, contexts));}else {continue;}}if (this.reactiveCachingHandler != null) {Object returnValue = this.reactiveCachingHandler.findInCaches(context, cache, key, invoker, method, contexts);if (returnValue != ReactiveCachingHandler.NOT_HANDLED) {return returnValue;}}Cache.ValueWrapper result = doGet(cache, key);if (result != null) {return result;}}return null;
}

CacheAspectSupport.doGet 可以看到最終拿的就是我們?cè)赾ache manager 里邊配置的cache

@Nullable
protected Cache.ValueWrapper doGet(Cache cache, Object key) {try {return cache.get(key); }catch (RuntimeException ex) {getErrorHandler().handleCacheGetError(ex, cache, key);return null;  // If the exception is handled, return a cache miss}
}

2. 緩存管理:緩存的存儲(chǔ)和取用是通過 Spring 的緩存抽象來管理的。緩存的實(shí)現(xiàn)可以是簡(jiǎn)單的內(nèi)存緩存,也可以是分布式緩存,如 Redis 等。
3. 緩存策略:開發(fā)者可以通過注解設(shè)置不同的緩存策略,例如緩存的鍵、值、過期時(shí)間、條件等。

這種基于注解的緩存方式極大地簡(jiǎn)化了緩存操作,讓開發(fā)者專注于業(yè)務(wù)邏輯的實(shí)現(xiàn),而緩存的管理由 Spring 自動(dòng)處理。

3.2 難點(diǎn)排查

在做demo的時(shí)候發(fā)現(xiàn)配置也沒錯(cuò),也沒有說是通過本文件的方法去調(diào)用提供緩存的方法(這樣不走代理,無法執(zhí)行命中緩存操作),后來發(fā)現(xiàn)是weakkey導(dǎo)致的。假如說內(nèi)存不夠的情況下,key就會(huì)被直接清楚掉,而我電腦可分配內(nèi)存很少。可能會(huì)頻繁觸發(fā)GC,導(dǎo)致cache key被清除掉了。不清楚weakReference作用的同學(xué)可以去看下我寫的另一篇文章:
WeakReference淺析

@Bean
public Caffeine<Object, Object> caffeineConfig() {return Caffeine.newBuilder().maximumSize(1000).expireAfterWrite(5, TimeUnit.MINUTES)
//                .weakKeys() // 很坑會(huì),基本上很快就把key清理掉了,緩存再也命中不了.recordStats();
}

總結(jié)

散會(huì)

http://www.risenshineclean.com/news/38411.html

相關(guān)文章:

  • 哈爾濱建站服務(wù)網(wǎng)站開發(fā)百度廣告費(fèi)一般多少錢
  • 給網(wǎng)站添加百度地圖百度關(guān)鍵字優(yōu)化價(jià)格
  • 西安模板網(wǎng)站建站魔方優(yōu)化大師官網(wǎng)
  • 成都裝修網(wǎng)站制作價(jià)格游戲推廣對(duì)接平臺(tái)
  • 南陽(yáng)建網(wǎng)站公司16種營(yíng)銷模型
  • 提供網(wǎng)站建設(shè)備案沈陽(yáng)網(wǎng)站關(guān)鍵詞優(yōu)化公司
  • 請(qǐng)問做網(wǎng)站怎么賺錢排名優(yōu)化軟件點(diǎn)擊
  • 北京網(wǎng)站建設(shè)在哪里天網(wǎng)站結(jié)構(gòu)有哪幾種
  • 房產(chǎn)備案登記信息查詢優(yōu)化大師網(wǎng)頁(yè)版
  • 做電商哪個(gè)設(shè)計(jì)網(wǎng)站比較好東莞最新消息今天
  • 微信公眾號(hào)是干什么用的紹興seo排名公司
  • 廣州建站方法南昌seo排名
  • 做網(wǎng)站跳轉(zhuǎn)怎么收費(fèi)網(wǎng)絡(luò)營(yíng)銷軟件推廣
  • 詳情頁(yè)制作網(wǎng)站seo交互論壇
  • 建立一個(gè)小程序多少錢小紅書關(guān)鍵詞排名優(yōu)化
  • 去哪找人做網(wǎng)站seo技術(shù)網(wǎng)網(wǎng)
  • 人大網(wǎng)站建設(shè)方案湖南省人民政府
  • 機(jī)械類畢業(yè)設(shè)計(jì)代做網(wǎng)站推薦官網(wǎng)seo關(guān)鍵詞排名系統(tǒng)
  • 哪里有做雜志的免費(fèi)模板下載網(wǎng)站網(wǎng)絡(luò)服務(wù)合同
  • 做網(wǎng)站買空間谷歌廣告開戶
  • 浙江建筑信息監(jiān)管平臺(tái)seo建站公司
  • 做網(wǎng)站視頻 上傳到哪兒百度手機(jī)版下載
  • 網(wǎng)站招工費(fèi)怎么做會(huì)計(jì)分錄企業(yè)推廣策劃
  • 幫人做網(wǎng)站在徐州被敲詐五萬(wàn)網(wǎng)站開發(fā)軟件
  • 網(wǎng)站收錄做關(guān)鍵詞排名百度網(wǎng)站優(yōu)化
  • 自助微信網(wǎng)站芭蕉視頻app無限次數(shù)
  • 互聯(lián)網(wǎng)網(wǎng)站建設(shè)情況統(tǒng)計(jì)表關(guān)鍵詞資源
  • 哈爾濱微網(wǎng)站建設(shè)太原seo優(yōu)化
  • 哪些網(wǎng)站做批發(fā)衣服電子商務(wù)網(wǎng)站建設(shè)與維護(hù)
  • 南通做網(wǎng)站baidu tg做網(wǎng)站公司排名