谈谈SpringBoot(二)

news2024/11/25 23:33:57

1. Spring Boot缓存

1.1 JSR-107

Spring从3.1开始定义了org.springframework.cache.Cache
和org.springframework.cache.CacheManager接口来统一不同的缓存技术;
并支持使用JCache(JSR-107)注解简化我们开发。

  • Cache接口为缓存的组件规范定义,包含缓存的各种操作集合;
  • Cache接口下Spring提供了各种xxxCache的实现;如RedisCache,EhCacheCache, ConcurrentMapCache等;
  • 每次调用需要缓存功能的方法时,Spring会检查检查指定参数的指定的目标方法是否已经被调用过;如果有就直接从缓存中获取方法调用后的结果,如果没有就调用方法并缓存结果后返回给用户。下次调用直接从缓存中获取;
  • 使用Spring缓存抽象时我们需要关注以下两点:
    1、确定方法需要被缓存以及他们的缓存策略
    2、从缓存中读取之前缓存存储的数据

 1.2 Springboot缓存抽象的缓存注解

Cache缓存接口,定义缓存操作。实现有:RedisCache、EhCacheCache、ConcurrentMapCache等
CacheManager缓存管理器,管理各种缓存(Cache)组件
@Cacheable主要针对方法配置,能够根据方法的请求参数对其结果进行缓存
@CacheEvict清空缓存
@CachePut保证方法被调用,又希望结果被缓存
@EnableCaching开启基于注解的缓存
keyGenerator缓存数据时key生成策略
serialize缓存数据时value序列化策略

    @Cacheable/@CachePut/@CacheEvict 主要的参数

value缓存的名称,在spring 配置文件中定义,必须指定至少一个例如:@Cacheable(value=”mycache”) 或者@Cacheable(value={”cache1”,”cache2”}
key缓存的key,可以为空,如果指定要按照SpEL 表达式编写,如果不指定,则缺省按照方法的所有参数进行组合例如:@Cacheable(value=”testcache”,key=”#userName”)
condition缓存的条件,可以为空,使用SpEL 编写,返回true 或者false,只有为true 才进行缓存/清除缓存例如:@Cacheable(value=”testcache”,condition=”#userName.length()>2”)
allEntries
(@CacheEvict )
是否清空所有缓存内容,缺省为false,如果指定为true,则方法调用后将立即清空所有缓存例如:@CachEvict(value=”testcache”,allEntries=true)
beforeInvocation
(@CacheEvict)
是否在方法执行前就清空,缺省为false,如果指定为true,则在方法还没有执行的时候就清空缓存,缺省情况下,如果方法执行抛出异常,则不会清空缓存例如:
@CachEvict(value=”testcache”,beforeInvocation=true)

1.3 Springboot缓存注解的使用

1.3.1 搭建环境

参照:谈谈SpringBoot(一)9. SpringBoot与数据访问

/resources/application.properties

#开启驼峰命名
#mybatis.configuration.map-underscore-to-camel-case=true
#将日志级别设置为debug
logging.level.com.zmj.springboot.mapper=debug
#打开自动配置报告
debug=true

pom.xml依赖

    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-cache</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-jdbc</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>org.mybatis.spring.boot</groupId>
            <artifactId>mybatis-spring-boot-starter</artifactId>
            <version>2.1.4</version>
        </dependency>
        <dependency>
            <groupId>com.mysql</groupId>
            <artifactId>mysql-connector-j</artifactId>
            <version>8.0.31</version>
            <scope>runtime</scope>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>druid</artifactId>
            <version>1.2.15</version>
        </dependency>
        <dependency>
            <groupId>org.apache.log4j</groupId>
            <artifactId>com.springsource.org.apache.log4j</artifactId>
            <version>1.2.16</version>
        </dependency>
    </dependencies>

​​不使用时,每次查询都要进入数据库

​​

1.3.2  使用缓存

@Service
public class EmployeeService {
    @Autowired
    private EmployeeMapper employeeMapper;

    @Cacheable(cacheNames = {"emp"})//缓存组件名称为emp
    public Employee getEmpById(Integer id){
        Employee emp = employeeMapper.getEmpById(id);
        return emp;
    }
}
@RestController
public class EmployeeController {
    @Autowired
    private EmployeeService employeeService;

    @GetMapping("/emp/{id}")
    public Employee queryEmp(@PathVariable("id") Integer id){
        return employeeService.getEmpById(id);
    }
}
@EnableCaching //开启缓存
@MapperScan("com.zmj.springboot.mapper")
@SpringBootApplication
public class SpringbootCacheApplication {
    public static void main(String[] args) {
        SpringApplication.run(SpringbootCacheApplication.class, args);
    }
}

使用缓存后,第一次查询进入数据库,后面如果是相同的数据,直接从缓存中获取

1.4 @Cacheable的工作原理和流程

1.4.1) 服务启动,执行SpringApplication.run方法

1.4.2) 首先找到缓存的自动配置类,根据命名规范可知是CacheAutoConfiguration

@Configuration
@ConditionalOnClass({CacheManager.class})
@ConditionalOnBean({CacheAspectSupport.class})
@ConditionalOnMissingBean(
    value = {CacheManager.class},
    name = {"cacheResolver"}
)
@EnableConfigurationProperties({CacheProperties.class})
@AutoConfigureBefore({HibernateJpaAutoConfiguration.class})
@AutoConfigureAfter({CouchbaseAutoConfiguration.class, HazelcastAutoConfiguration.class, RedisAutoConfiguration.class})
@Import({CacheAutoConfiguration.CacheConfigurationImportSelector.class})
public class CacheAutoConfiguration {
}

1.4.3) 通过@Import得知,需要导入CacheConfigurationImportSelector类

@Import({CacheAutoConfiguration.CacheConfigurationImportSelector.class})
public class CacheAutoConfiguration {
    static class CacheConfigurationImportSelector implements ImportSelector {
        CacheConfigurationImportSelector() {
        }
        public String[] selectImports(AnnotationMetadata importingClassMetadata) {
            CacheType[] types = CacheType.values();
            String[] imports = new String[types.length];

            for(int i = 0; i < types.length; ++i) {
                imports[i] = CacheConfigurations.getConfigurationClass(types[i]);
            }

            return imports;
        }
    }
}

​1.4.4) 缓存的自动配置类中,默认SimpleCacheConfiguration类生效

----通过打印出来的自动配置报告获得   

SimpleCacheConfiguration matched:
      - Cache org.springframework.boot.autoconfigure.cache.SimpleCacheConfiguration automatic cache type (CacheCondition)
      - @ConditionalOnMissingBean (types: org.springframework.cache.CacheManager; SearchStrategy: all) did not find any beans (OnBeanCondition)

@Configuration
@ConditionalOnMissingBean({CacheManager.class})
@Conditional({CacheCondition.class})
class SimpleCacheConfiguration {

    @Bean
    public ConcurrentMapCacheManager cacheManager() {
        ConcurrentMapCacheManager cacheManager = new ConcurrentMapCacheManager();
        List<String> cacheNames = this.cacheProperties.getCacheNames();
        if (!cacheNames.isEmpty()) {
            cacheManager.setCacheNames(cacheNames);
        }
        return (ConcurrentMapCacheManager)this.customizerInvoker.customize(cacheManager);
    }
}

1.4.5) SimpleCacheConfiguration的ConcurrentMapCacheManager

ConcurrentMapCacheManager中有一个cacheMap,后来的缓存数据就是保存到这里

public class ConcurrentMapCacheManager implements CacheManager, BeanClassLoaderAware {
    private final ConcurrentMap<String, Cache> cacheMap = new ConcurrentHashMap(16);
}

1.4.6)浏览器发送第一次请求

http://localhost:8080/emp/1

public class ConcurrentMapCacheManager implements CacheManager, BeanClassLoaderAware {
    private final ConcurrentMap<String, Cache> cacheMap = new ConcurrentHashMap(16);
    public Cache getCache(String name) {
        Cache cache = (Cache)this.cacheMap.get(name);//name=emp
        if (cache == null && this.dynamic) {//第一次请求缓存为null且this.dynamic为true
            synchronized(this.cacheMap) {
                cache = (Cache)this.cacheMap.get(name);
                if (cache == null) {//再次检测,还是为null
                    cache = this.createConcurrentMapCache(name);//创建新缓存
                    this.cacheMap.put(name, cache);
                }
            }
        }
        return cache;
    }
}

 1.4.7)创建新缓存对象

public class ConcurrentMapCacheManager implements CacheManager, BeanClassLoaderAware {
    protected Cache createConcurrentMapCache(String name) {
        SerializationDelegate actualSerialization = this.isStoreByValue() ? this.serialization : null;
        return new ConcurrentMapCache(name, new ConcurrentHashMap(256), this.isAllowNullValues(), actualSerialization);
    }
}

 1.4.8)生成key和value(cache对象)

key是默认按照keyGenerator策略生成的,默认是使用SimpleKeyGenerator生成key

value是通过回调函数,调用EmployeeService.getEmpById()拿到实例对象

public abstract class CacheAspectSupport extends AbstractCacheInvoker implements BeanFactoryAware, InitializingBean, SmartInitializingSingleton {
    private Object execute(final CacheOperationInvoker invoker, Method method, CacheAspectSupport.CacheOperationContexts contexts) {
        if (contexts.isSynchronized()) {
            CacheAspectSupport.CacheOperationContext context = (CacheAspectSupport.CacheOperationContext)contexts.get(CacheableOperation.class).iterator().next();
            if (this.isConditionPassing(context, CacheOperationExpressionEvaluator.NO_RESULT)) {
                Object key = this.generateKey(context, CacheOperationExpressionEvaluator.NO_RESULT);
                Cache cache = (Cache)context.getCaches().iterator().next();

                try {
                    return this.wrapCacheValue(method, cache.get(key, new Callable<Object>() {
                        public Object call() throws Exception {
                            return CacheAspectSupport.this.unwrapReturnValue(CacheAspectSupport.this.invokeOperation(invoker));
                        }
                    }));
                } catch (ValueRetrievalException var10) {
                    throw (ThrowableWrapper)var10.getCause();
                }
            } else {
                return this.invokeOperation(invoker);
            }
        } else {
            this.processCacheEvicts(contexts.get(CacheEvictOperation.class), true, CacheOperationExpressionEvaluator.NO_RESULT);
            ValueWrapper cacheHit = this.findCachedItem(contexts.get(CacheableOperation.class));
            List<CacheAspectSupport.CachePutRequest> cachePutRequests = new LinkedList();
            if (cacheHit == null) {
                this.collectPutRequests(contexts.get(CacheableOperation.class), CacheOperationExpressionEvaluator.NO_RESULT, cachePutRequests);
            }

            Object cacheValue;
            Object returnValue;
            if (cacheHit != null && cachePutRequests.isEmpty() && !this.hasCachePut(contexts)) {
                cacheValue = cacheHit.get();
                returnValue = this.wrapCacheValue(method, cacheValue);
            } else {
                returnValue = this.invokeOperation(invoker);
                cacheValue = this.unwrapReturnValue(returnValue);
            }

            this.collectPutRequests(contexts.get(CachePutOperation.class), cacheValue, cachePutRequests);
            Iterator var8 = cachePutRequests.iterator();

            while(var8.hasNext()) {
                CacheAspectSupport.CachePutRequest cachePutRequest = (CacheAspectSupport.CachePutRequest)var8.next();
                cachePutRequest.apply(cacheValue);
            }

            this.processCacheEvicts(contexts.get(CacheEvictOperation.class), false, cacheValue);
            return returnValue;
        }
    }
    protected class CacheOperationContext implements CacheOperationInvocationContext<CacheOperation> {
        public CacheOperationContext(CacheAspectSupport.CacheOperationMetadata metadata, Object[] args, Object target) {
        protected Object generateKey(Object result) {
            if (StringUtils.hasText(this.metadata.operation.getKey())) {
                EvaluationContext evaluationContext = this.createEvaluationContext(result);
                return CacheAspectSupport.this.evaluator.key(this.metadata.operation.getKey(), this.methodCacheKey, evaluationContext);
            } else {
                return this.metadata.keyGenerator.generate(this.target, this.metadata.method, this.args);
            }
        }
    }
}

 1.4.9)浏览器发送第二次请求

http://localhost:8080/emp/1

public class ConcurrentMapCacheManager implements CacheManager, BeanClassLoaderAware {
    public Cache getCache(String name) {
        Cache cache = (Cache)this.cacheMap.get(name);//name=emp
        if (cache == null && this.dynamic) {//此时cache不为空,条件不成立
            synchronized(this.cacheMap) {
                cache = (Cache)this.cacheMap.get(name);
                if (cache == null) {
                    cache = this.createConcurrentMapCache(name);
                    this.cacheMap.put(name, cache);
                }
            }
        }
        return cache;//直接返回缓存数据
    }
}

1.5 @Cacheable的其他属性

1.5.1 第一种方式

@Service
public class EmployeeService {
    @Autowired
    private EmployeeMapper employeeMapper;

    //@Cacheable(cacheNames = {"emp"})//缓存组件名称为emp
    //@Cacheable(cacheNames = {"emp"},condition = "#id > 0")//缓存的条件是id > 0
    @Cacheable(cacheNames = {"emp"},key="#root.methodName+'['+#id+']'")//key=方法名+id
    public Employee getEmpById(Integer id){
        Employee emp = employeeMapper.getEmpById(id);
        return emp;
    }
}

1.5.2 第二种方式

@Configuration
public class MyCacheConfig {

    @Bean("myKeyGenerator")
    public KeyGenerator keyGenerator() {//自定义key的生成策略
        return new KeyGenerator(){
            @Override
            public Object generate(Object target, Method method, Object... params) {
                return method.getName()+ "["+Arrays.asList(params).toString()+"]";
            }
        };
    }
}
@Service
public class EmployeeService {
    @Autowired
    private EmployeeMapper employeeMapper;

    @Cacheable(cacheNames = {"emp"},keyGenerator = "myKeyGenerator")//key为自定义策略
    //@Cacheable(cacheNames = {"emp"},keyGenerator="myKeyGenerator",condition = "#id > 1")
    //@Cacheable(cacheNames = {"emp"},keyGenerator = "myKeyGenerator",condition = "#id > 1",unless = "#a0==2")
    public Employee getEmpById(Integer id){
        Employee emp = employeeMapper.getEmpById(id);
        return emp;
    }
}

1.6 其他缓存注解

@CacheConfig(cacheNames = {"emp"})//全局配置缓存名字都是emp
@Service
public class EmployeeService {
    @Autowired
    private EmployeeMapper employeeMapper;

    @Cacheable()
    //@Cacheable(cacheNames = {"emp"},keyGenerator = "myKeyGenerator",condition = "#id > 1",unless = "#a0==2")
    public Employee getEmpById(Integer id){
        Employee emp = employeeMapper.getEmpById(id);
        return emp;
    }
    //更新缓存,key为员工ID
    @CachePut(key = "#employee.id")
    public Employee updateEmp(Employee employee) {
        employeeMapper.updateEmp(employee);
        return employee;
    }
    //清除缓存,key为员工ID
    @CacheEvict(key = "#id")
    public void deleteEmp(Integer id) {
        employeeMapper.deleteEmp(id);
    }

    //@Caching定义复杂的缓存规则
    @Caching(
            cacheable = {
                    @Cacheable(key = "#lastName")
            },
            put = {
                   @CachePut(key = "#result.id"),
                   @CachePut(key = "#result.email")
            }
    )
    public Employee getEmpByLastName(String lastName){
        Employee emp = employeeMapper.getEmpByLastName(lastName);
        return emp;
    }
}

2. Springboot整合Redis

2.1 Docker中启动Redis

参照:谈谈Docker

docker run -d -p 16379:6379 --name myredis8 16ecd2772934

2.2  依赖和配置

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-redis</artifactId>
        </dependency>
spring.redis.host=192.168.1.200
spring.redis.port=16379

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/181207.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

day23|93.复原IP地址、78.子集、90.子集II

93.复原IP地址 有效 IP 地址 正好由四个整数&#xff08;每个整数位于 0 到 255 之间组成&#xff0c;且不能含有前导 0&#xff09;&#xff0c;整数之间用 . 分隔。 例如&#xff1a;"0.1.2.201" 和 "192.168.1.1" 是 有效 IP 地址&#xff0c;但是 &q…

layui框架学习(2:颜色、图标、动画)

B站教学视频中对Layui的颜色没有专门介绍&#xff0c;而Layui官方教程中虽然有颜色章节&#xff0c;但也只是简单介绍了基色调、辅色调、中性的颜色的概念及用途&#xff0c;最后说明layui 内置了七种背景色&#xff0c;以便用于各种元素中&#xff0c;如&#xff1a;徽章、分割…

Go语言基础入门第二章

Go语言环境安装 下载地址&#xff1a;https://golang.google.cn/dl/ 下载完安装包直接安装即可&#xff0c;安装完毕后&#xff0c;打开cmd控制台&#xff0c;输入”go version“查看是否安装成功以及对应安装版本。 配置环境变量Go语言需要一个安装目录&#xff0c;还需要一个…

Spring Cloud_Eureka服务注册与发现

目录一、Eureka基础知识1.什么是服务治理2.什么是服务注册3.Eureka两组件二、单机Eureka构建步骤1.IDEA生成eurekaServer端服务注册中心2.服务提供者3.服务消费者代码链接 https://github.com/lidonglin-bit/cloud 一、Eureka基础知识 1.什么是服务治理 SpringCloud封装了Ne…

金融风控09

迁移学习 为什么要&#xff1f; 源域样本与目标域样本分布有区别&#xff0c;目标域样本量不够 平时建模用的迁移学习场景 1、新开某个消费分期场景样本量少&#xff0c;需要用其他场景的数据建模 2、业务被迫停滞3个月再重启&#xff0c;大部分训练样本比较老旧&#xff…

含分布式光伏的配电网集群划分和集群电压协调控制(Matlab代码实现)

&#x1f4a5;&#x1f4a5;&#x1f49e;&#x1f49e;欢迎来到本博客❤️❤️&#x1f4a5;&#x1f4a5; &#x1f3c6;博主优势&#xff1a;&#x1f31e;&#x1f31e;&#x1f31e;博客内容尽量做到思维缜密&#xff0c;逻辑清晰&#xff0c;为了方便读者。 ⛳️座右铭&a…

激光在大气中传输特性

在光纤通信中&#xff0c;单模光纤在波长1.55μm窗口具有巨大的潜在带宽和极低的损耗&#xff0c;传输数字信号的容量已能达到10Tb&#xff0f;s&#xff0c;每信道光源功率仅需1mW左右&#xff0c;无中间放大的距离超过100km。而且&#xff0c;光纤作为光波导&#xff0c;红外…

PostSharp Ultimate添加模式和线程安全

PostSharp Ultimate添加模式和线程安全 PostSharpUltimate允许开发人员通过将重复的工作从人身上转移到机器上&#xff0c;从而消除样板代码。它包含最常见模式的现成实现&#xff0c;并为您提供了为自己的模式构建自动化的工具。开发人员通常根据设计模式进行思考&#xff0c;…

Go语言基础入门第一章

Go语言基础入门 Go语言的logo 为什么需要一个新的语言最近十年来&#xff0c;C/C在计算机领域得到了很好的发展&#xff0c;并没有新的系统编程语言出现。对开发程度和执行效率在很多情况下并不能兼得。要么是执行效率高&#xff0c;但是低效的开发和编译&#xff0c;如C&…

Redux Toolkit + React + Tailwind CSS 学习心得

Redux Toolkit React Tailwind CSS 学习心得 预览地址&#xff1a;https://goldenaarcher.com/movie-app-home-only&#xff0c;只实现了一个简单的首页功能&#xff0c;API 用的是 the Movie Database&#xff0c;不想用 API 的也可以装一个 faker-js/faker 用来随机生成伪…

学生护眼灯怎么选择?分享适合学生的护眼灯

现阶段的青少年与儿童的近视率非常高&#xff0c;选择一款好的台灯能够保证双眼的健康&#xff0c;首先先看亮度是否合理&#xff0c;不能刺眼&#xff0c;选择三基色灯管&#xff0c;光很柔和&#xff0c;看频闪&#xff0c;好的护眼台灯可以做到无可视频闪&#xff0c;是的视…

移动web适配和Less

移动web适配和Lessrem 适配rem 单位媒体查询flexible.js如何把设计稿的px转换为remLESSLess注释less 运算less 嵌套less 变量less导入less 导出控制当前Less文件导出less 禁止导出小结rem 适配 rem 单位 rem 是一个相对单位&#xff0c;1rem 就是 html 文字的大小 比如 /* …

Java基础10:常用API

Java基础10&#xff1a;常用API一、Math二、System1. currentTimeMillis2. arraycopy三、Runtime四、Object1. toString2. equals3. clone五、Objects六、BigInteger1. 构造方法&#xff08;获取BigInteger&#xff09;2. 常用方法七、BigDecimal1. 构造方法&#xff08;获取Bi…

计算机相关专业混体制的解决方案(考公务员)

文章目录序&#xff1a;编制介绍1、公务员报考要求2、公务员工作待遇3、公务员工作内容4、公务员报考复习序&#xff1a;编制介绍 编制介绍&#xff1a;编制&#xff0c;也就是常说的铁饭碗。 编制的诞生为了控制吃财政饭的人员数量无限膨胀而设置的&#xff0c;所以名额有限受…

密码学基本概念

密码学简介 密码是经过加密过后的口令&#xff0c;是指用特定的变换对数据信息进行加密保护或者安全身份认证的物质和技术&#xff0c;密码学是对安全通信技术的研究&#xff0c;要能够有效的防范潜在攻击&#xff0c;也就是对信息加密解密的过程。 密码基本性质 密码学的发展…

CSS3 选择器 :nth-child 与 :nth-of-type 区别

一、:nth-child 1.1 说明 :nth-child(n) 选择器匹配属于其父元素的第 N 个子元素&#xff0c;不论元素的类型。n 可以是数字、关键词或公式。 注意&#xff1a;如果第 N 个子元素与选择的元素类型不同则样式无效&#xff01; 1.2 示例 <style> div>p:nth-child(2…

1行Python代码识别身份证信息,还能自动告警,YYDS

大家好&#xff0c;这里是程序员晚枫。 录入身份证信息是一件繁琐的工作&#xff0c;如果可以自动识别并且录入系统&#xff0c;那可真是太好了。 今天我们就来学习一下&#xff0c;如何自动识别身份证信息并且录入系统~ 识别身份证信息 识别身份证信息的代码最简单&#x…

【金融量化】CTA策略之VeighNa量化实战笔记(1)

量化投资实战笔记 1 基本概念 1、一手股票&#xff1a;100支股票 2、收盘比开盘上涨的百分比&#xff1a;&#xff08;收盘-开盘&#xff09;/开盘 3、开盘比前日收盘的百分比&#xff1a;&#xff08;开盘-前日收盘&#xff09;/前日收盘 4、从dataframe中取每个月的第一天 …

Hgame-week 1(部分)

标题MISCSing InWhere am I神秘的海报e99p1ant_want_girlfriendWEDClassic Childhood Game改源码直接看jsBecome A MemberUser-Agent:Cookie:Referer:X-Forwarded-For:json请求方式登陆Guess Who I AmCRYPTO神秘的电话注意大小写REVERSEtest your IDAMISC Sing In aGdhbWV7V2…

【GPLT 二阶题目集】L2-027 名人堂与代金券

对于在中国大学MOOC&#xff08;http://www.icourse163.org/ &#xff09;学习“数据结构”课程的学生&#xff0c;想要获得一张合格证书&#xff0c;总评成绩必须达到 60 分及以上&#xff0c;并且有另加福利&#xff1a;总评分在 [G, 100] 区间内者&#xff0c;可以得到 50 元…