Lxxxxxxy_
Lxxxxxxy_
Published on 2019-10-22 / 48 Visits
0

SpringBoot高级应用

SpringBoot高级应用学习笔记

2019年10月17日15:30:04,老师:雷丰阳


1、缓存注解

@Cacheable:能对某个方法的返回值进行缓存。

​ 1)value属性:指定缓存的名字。

​ 2)key属性:缓存数据使用的key,默认使用参数列表的值。key对应的值是返回值。key属性可以使用SpEL表达式。

​ 3)keyGenerator:key生成器,可以自己指定key生成器。

​ 4)cacheManager:缓存管理器。

​ 5)condition:设定条件,符合条件的情况下才进行缓存。

​ 6)unless:和condition相反。但可以对返回值进行判断。

​ 7)sync:是否使用异步模式。

运行流程:
@Cacheable:
1、方法运行之前,先去查询Cache(缓存组件),按照cacheNames指定的名字获取;
(CacheManager先获取相应的缓存),第一次获取缓存如果没有Cache组件会自动创建。
2、去Cache中查找缓存的内容,使用一个key,默认就是方法的参数;key是按照某种策略生成的;默认是使用keyGenerator生成的,默认使用SimpleKeyGenerator生成key;SimpleKeyGenerator生成key的默认策略;
	如果没有参数;key=new SimpleKey();
	如果有一个参数:key=参数的值
	如果有多个参数:key=new SimpleKey(params);
3、没有查到缓存就调用目标方法;
4、将目标方法返回的结果,放进缓存中

@Cacheable标注的方法执行之前先来检查缓存中有没有这个数据,默认按照参数的值作为key去查询缓存,
如果没有就运行方法并将结果放入缓存;以后再来调用就可以直接使用缓存中的数据;

新建一个PersonServiceImpl类,对这个类里面的方法的返回值进行缓存

package cn.lixingyu.springadvanced.service.impl;

import cn.lixingyu.springadvanced.entity.Person;
import cn.lixingyu.springadvanced.mapper.PersonMapper;
import cn.lixingyu.springadvanced.service.PersonService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.stereotype.Service;

/**
 * @author Rlxy93
 * @time 2019/10/18 09:03
 */
@Service("personService")
public class PersonServiceImpl implements PersonService {

    @Autowired
    private PersonMapper personMapper;

    @Override
    //使用unless,符合条件的不缓存,#result==null 表示如果返回值为null,则不缓存
    @Cacheable(cacheNames = "temp",unless = "#result == null")
    public Person queryPerson(Integer id) {
        System.out.println("queryPerson()....");
        Person person = personMapper.queryPerson(id);
        return person;
    }
}

ps:必须给@Cacheable设置CacheNames

运行效果:

QQ截图20191018094407


@CachePut:保证方法能够被调用,并缓存返回值。

在PersonServiceImpl类中新建一个方法

@Override
    //cacheNames必须和上面查询的保持一致,否则更新缓存的时候不会更新查询到的缓存
    @CachePut(cacheNames = "temp",key = "#p.id")
    public Person updatePerson(Person p) {
        System.out.println("updatePerson()....");
        personMapper.updatePerson(p);
        return p;
    }

运行效果(保证方法能够被调用):

QQ截图20191018100106

运行结果(缓存返回值):

QQ截图20191018114945

其中,先查询一次,再更新数据库,再查询。


@CacheEvict:清空缓存。

在类中修改

@Override
    //删除缓存,beforeInvocation表示在方法执行之前删除
    @CacheEvict(cacheNames = "temp",key = "#id",beforeInvocation = true)
    public void deletePerson(Integer id) {
        System.out.println("deletePerson()....");
        personMapper.deletePerson(id);
    }

运行效果:

QQ截图20191018100431

其中,不指定key表示默认为key = "#id"。


@Caching:上文中三个注解的组合。三个参数:cacheable,put,evict。

在PersonServiceImpl类中加入

@Caching(cacheable = {@Cacheable(cacheNames = "person",key = "#id",unless = "#result==null")})
    public Person queryPerson(Integer id) {
        System.out.println("queryPerson()....");
        Person person = personMapper.queryPerson(id);
        return person;
    }

运行效果:

QQ截图20191020103529


@CacheConfig:设置整个类的变量,比如cacheNames。


2、Redis缓存

StringRedisTemplate:操作字符串。

RedisTemplate:操作其他。

ps:保存对象时,默认使用jdk提供的序列化接口,需要给保存的对象实现序列化接口才能保存。

自定义CacheManager

@Bean
    public CacheManager cacheManager(RedisConnectionFactory redisConnectionFactory) {
        //初始化一个RedisCacheWriter
        RedisCacheWriter redisCacheWriter = RedisCacheWriter.nonLockingRedisCacheWriter(redisConnectionFactory);
        //设置CacheManager的值序列化方式为json序列化
        GenericJackson2JsonRedisSerializer json = new GenericJackson2JsonRedisSerializer();
        RedisSerializationContext.SerializationPair<Object> pair = RedisSerializationContext.SerializationPair
                .fromSerializer(json);
        RedisCacheConfiguration defaultCacheConfig=RedisCacheConfiguration.defaultCacheConfig()
                .serializeValuesWith(pair);
        //设置默认超过期时间是30秒
        defaultCacheConfig.entryTtl(Duration.ofSeconds(30));
        //初始化RedisCacheManager
        return new RedisCacheManager(redisCacheWriter, defaultCacheConfig);
    }

自定义序列化接口(json)

新建Config类

package cn.lixingyu.springadvanced.config;

import cn.lixingyu.springadvanced.entity.Father;
import com.alibaba.fastjson.support.spring.GenericFastJsonRedisSerializer;
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.serializer.StringRedisSerializer;

/**
 * @author Rlxy93
 * @time 2019/10/18 08:42
 */
@Configuration
public class AcConfig {

    @Bean("myRedisTemplate")
    public RedisTemplate<Object, Father> redisTemplate(RedisConnectionFactory redisConnectionFactory){
        RedisTemplate<Object, Father> temp = new RedisTemplate<>();
        temp.setConnectionFactory(redisConnectionFactory);
        GenericFastJsonRedisSerializer json = new GenericFastJsonRedisSerializer();
        temp.setDefaultSerializer(json);
        //解决存入的key有双引号的问题
        temp.setKeySerializer(new StringRedisSerializer());
        return temp;
    }

}

新建RedisPractice类

package cn.lixingyu.springadvanced;

import cn.lixingyu.springadvanced.entity.Person;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.test.context.junit4.SpringRunner;

/**
 * @author Rlxy93
 * @time 2019/10/20 10:38
 */
@RunWith(SpringRunner.class)
@SpringBootTest
public class RedisPractice {

    @Autowired
    private StringRedisTemplate stringRedisTemplate;

    @Qualifier("myRedisTemplate")
    @Autowired
    private RedisTemplate redisTemplate;

    @Test
    public void test1(){
//        stringRedisTemplate.opsForValue().append("Rlxy93","lxy");
//        String rlxy93 = stringRedisTemplate.opsForValue().get("Rlxy93");
//        System.out.println(rlxy93);
        Person person = new Person(1, "Rlxy93", 20, "重庆");
        redisTemplate.opsForValue().set("Rlxy93",person);
        Person rlxy93 = (Person) redisTemplate.opsForValue().get("Rlxy93");
        System.out.println(rlxy93);
    }

}

运行效果:

QQ截图20191020111002


思考1

Screenshot_2019-10-20-14-35-43-398_com.tencent.mobileqq

我想,在视频中,老师定义了两个实体类,就要使用两个RedisTemplate来分别对应两个类,如果有很多个类,那么就要定义很多,于是得到了个思考:RedisTemplate可不可以自定义成一个接收任何数据类型的Bean?

在经过一阵摸索之后,我发现,只要将Jackson2JsonRedisSerializer改成GenericFastJsonRedisSerializer就可以了。因为Jackson2JsonRedisSerializer需要传入一个class对象,而GenericFastJsonRedisSerializer不需要。

运行效果:

QQ截图20191020144103


3、RabbitMQ消息队列

下载

docker pull rabbitmq:3-management

运行(必须设置15672端口,否则web界面无法访问)

docker run -d -p 5672:5672 -p 15672:15672 --name rabbitmq rabbitmq
demo

新建一个RabbitPractice类

package cn.lixingyu.springadvanced;

import cn.lixingyu.springadvanced.entity.Person;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;

/**
 * @author Rlxy93
 * @time 2019/10/21 18:02
 */
@RunWith(SpringRunner.class)
@SpringBootTest
public class RabbitPractice {

    @Autowired
    private RabbitTemplate rabbitTemplate;

    //发送消息
    @Test
    public void test1(){
        rabbitTemplate.convertAndSend("exchange.direct","lixingyu",new Person(1,"Rlxy93",21,"重庆"));
    }

    //接收消息
    @Test
    public void test2(){
        Person lixingyu = (Person) rabbitTemplate.receiveAndConvert("lixingyu");
        System.out.println(lixingyu.getClass());
        System.out.println(lixingyu);
    }

}

自定义MessageConverter:

//自定义RabbitMQ的序列化工具
    @Bean("myMessageConverter")
    public MessageConverter messageConverter(){
        return new Jackson2JsonMessageConverter();
    }

运行效果:

QQ截图20191021182737

QQ截图20191021182504


使用注解
@RabbitListener(queues = "lixingyu")
    public void rabbitListener(Person lixingyu){
        System.out.println(lixingyu.getClass());
        System.out.println(lixingyu);
    }

运行效果:

QQ截图20191022110534


综合应用
@Test
    public void test3(){
        amqpAdmin.declareExchange(new DirectExchange("Rlxy93"));
        amqpAdmin.declareQueue(new Queue("Rlxy93"));
        amqpAdmin.declareBinding(new Binding("Rlxy93", Binding.DestinationType.QUEUE,"Rlxy93","lixingyu",null));
        amqpAdmin.purgeQueue("Rlxy93",true);
        System.out.println("创建成功!");
        rabbitTemplate.convertAndSend("Rlxy93","lixingyu",new Person(1,"Rlxy93",21,"重庆"));
        rabbitTemplate.convertAndSend("Rlxy93","lixingyu",new Person(1,"Rlxy93",21,"重庆"));
        System.out.println("发送成功!");
        Object rlxy93 = rabbitTemplate.receiveAndConvert("Rlxy93");
        System.out.println(rlxy93.getClass());
        System.out.println((Person)rlxy93);
        System.out.println("接收成功!");
    }

运行效果:

QQ截图20191022110332