# SpringBoot2核心技术与响应式编程 **Repository Path**: orehapeinn/springboot2 ## Basic Information - **Project Name**: SpringBoot2核心技术与响应式编程 - **Description**: SpringBoot2核心技术与响应式编程 - **Primary Language**: Java - **License**: Not specified - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 0 - **Forks**: 1202 - **Created**: 2021-02-17 - **Last Updated**: 2021-06-03 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README # SpringBoot2核心技术与响应式编程 [前面几张笔记链接](https://gitee.com/orehapeinn/springboot2.git) [视频链接](https://www.bilibili.com/video/BV19K4y1L7MT) ## Spring整合篇 ### spring cache Spring cache 是一种基于注解的可以对某些方法做缓存(主要可以用在dao层方法),可以用来减少数据库压力,spring cache并不提供缓存实现,而是对缓存使用地抽象,支持本地缓存,Redis缓存等. #### 核心注解 - @Cachable 查询方法 每次调用指定方法前会从缓存中检查指定的数据是否在缓存中,如果存在,直接返回缓存中的数据,不再指定该方法.否则指定该方法获取到结果放入缓存中后返回 注解属性 String key() > Spring Expression Language (SpEL) expression for computing the key dynamically. > Default is "", meaning all method parameters are considered as a key, unless a custom keyGenerator has been configured. > The SpEL expression evaluates against a dedicated context that provides the following meta-data: > #root.method, #root.target, and #root.caches for references to the method, target object, and affected cache(s) respectively. > Shortcuts for the method name (#root.methodName) and target class (#root.targetClass) are also available. > Method arguments can be accessed by index. For instance the second argument can be accessed via #root.args[1], #p1 or #a1. Arguments can also be accessed by name if that information is available. String keyGenerator() 可以自定义key生成器,与以上的key互斥,两者不可并存,可以往容器中注入自定义keyGenerator填入它的beanName就可以让spring使用自定的key生成策略 ```java @Bean public KeyGenerator keyGenerator() { // return new SimpleKeyGenerator(); //spring提供的简单的KeyGenerator return (target, method, params) -> target.getClass().getSimpleName() + method.getName() + "[" + StringUtils.arrayToCommaDelimitedString(params) + "]"; } // user::$Proxy61selectByIdAndName[2,郑思淼], 这个获取到的 target 是一个代理类,也合理,因为我们用的是mybatis ``` > The bean name of the custom org.springframework.cache.interceptor.KeyGenerator to use. > Mutually exclusive with the key attribute. > See Also: > CacheConfig.keyGenerator - CachePut 修改方法 每次调用指定方法都会执行,并将结果存入缓存 - CacheEvict 删除或修改 boolean beforeInvocation() default false; 方法执行前还是执行后进行缓存驱逐 默认方法执行后 指定方法执行之前或后会清除对应的缓存 核心执行逻辑 ```java // CacheInteceptor.invoke -> CacheAspectSupport.execute() private Object execute(final CacheOperationInvoker invoker, Method method, CacheOperationContexts contexts) { // Special handling of synchronized invocation if (contexts.isSynchronized()) { CacheOperationContext context = contexts.get(CacheableOperation.class).iterator().next(); if (isConditionPassing(context, CacheOperationExpressionEvaluator.NO_RESULT)) { Object key = generateKey(context, CacheOperationExpressionEvaluator.NO_RESULT); Cache cache = context.getCaches().iterator().next(); try { return wrapCacheValue(method, handleSynchronizedGet(invoker, key, cache)); } catch (Cache.ValueRetrievalException ex) { // Directly propagate ThrowableWrapper from the invoker, // or potentially also an IllegalArgumentException etc. ReflectionUtils.rethrowRuntimeException(ex.getCause()); } } else { // No caching required, only call the underlying method return invokeOperation(invoker); } } // Process any early evictions 是否先删除缓存 processCacheEvicts(contexts.get(CacheEvictOperation.class), true, CacheOperationExpressionEvaluator.NO_RESULT); // Check if we have a cached item matching the conditions Cache.ValueWrapper cacheHit = findCachedItem(contexts.get(CacheableOperation.class)); // Collect puts from any @Cacheable miss, if no cached item is found // 如果有 上面 @Cachable 缓存没有命中,再来处理 @CachePut注解,这里只进行预处理,没有实质查询 List cachePutRequests = new ArrayList<>(); if (cacheHit == null) { collectPutRequests(contexts.get(CacheableOperation.class), CacheOperationExpressionEvaluator.NO_RESULT, cachePutRequests); } Object cacheValue; Object returnValue; if (cacheHit != null && !hasCachePut(contexts)) { // If there are no put requests, just use the cache hit 缓存命中切没有 @CachePut cacheValue = cacheHit.get(); returnValue = wrapCacheValue(method, cacheValue); } else { // Invoke the method if we don't have a cache hit 调用被注解标识的方法 returnValue = invokeOperation(invoker); cacheValue = unwrapReturnValue(returnValue); } // Collect any explicit @CachePuts collectPutRequests(contexts.get(CachePutOperation.class), cacheValue, cachePutRequests); // Process any collected put requests, either from @CachePut or a @Cacheable miss // 往缓存中插入数据 for (CachePutRequest cachePutRequest : cachePutRequests) { cachePutRequest.apply(cacheValue); } // Process any late evictions 抑或是后删除缓存 processCacheEvicts(contexts.get(CacheEvictOperation.class), false, cacheValue); return returnValue; } ``` ### WebSocket Websocket 是全双工通信协议, STOMP(simple text oriented messaging protocol) 是简单文本协议, 让传输内容更加简单,socketJS是为了解决浏览器不支持websocket协议做的模拟websocket来工作