了解Spring的缓存抽象

Spring的缓存抽象

  • 为 Java 方法增加缓存,缓存执行结果
    当第一次调用完该方法之后,Spring会把执行的结果缓存起来,当下一次再次调用的时候,直接取缓存里面的数据
  • 支持ConcurrentMap、EhCache、Caffeine、JCache(JSR-107)、Redis
    Ehcache直接在jvm虚拟机中缓存,速度快,效率高;但是缓存共享麻烦,集群分布式应用不方便
    JCACHE是一种即将公布的标准规范(JSR 107),说明了一种对Java对象临时在内存中进行缓存的方法,包括对象的创建、共享访问、假脱机(spooling)、失效、各JVM的一致性等。它可被用于缓存JSP内最经常读取的数据,如产品目录和价格列表。利用JCACHE,多数查询的反应时间会因为有缓存的数据而加快(内部测试表明反应时间大约快15倍)。、
    本章使用Redis。
  • 接口
    org.springframework.cache.Cache
    org.springframework.cache.CacheManager

需要缓存的数据

将长久不变的信息,一天之内不会变的,会在JVM内部做一个缓存,因为每次访问集中式的分布式缓存,也会用一定的网络上的开销,如果对于我们来说,这个数据,它很长时间不会变,而且它的变化我可以接收,它有一定的延时,那么这种我们可以放到JVM内部,设置一个过期的时间,让他自动过期之后,再去从后面的后端捞取数据。
如果我希望说在整个集群里面,都有一个缓存,这个缓存要求在整个集群内部,访问的时候都具备一定的一致性的,也就是说,一旦这个值发生了变化,所有机器上必须取到同一个缓存的值。不能说A机器取到一个新值,B机器取到一个旧值。这时候,就要使用Redis的分布式缓存,将缓存放到Redis中。
一般来说,对于读一次写一次的数据,就没有必要放到缓存里面了。

Spring中基于注解的缓存

@EnableCaching
开启整个缓存的支持

  • @Cacheable
    执行这个方法,如果这个方法的结果在缓存里面,直接从缓存里面取,没有的话,执行完方法之后,将结果放到缓存中
  • @CacheEvict
    做缓存的清理
  • @CachePut
    不管方法的执行情况,直接做一个缓存的设置
  • @Caching
    直接对 缓存清理、缓存设置 等方法做一个打包,我们可以放入多个操作。
  • @CacheConfig
    对缓存做一个设置,比如说 名字

通过 Spring Boot 配置 Redis 缓存

配置文件

spring.cache.type=redis
spring.cache.cache-names=coffee
spring.cache.redis.time-to-live=5000
#缓存的生命周期
spring.cache.redis.cache-null-values=false

pom文件

<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-cache</artifactId>
		</dependency>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-data-redis</artifactId>
		</dependency>
@MappedSuperclass //标注为@MappedSuperclass的类将不是一个完整的实体类,他将不会映射到数据库表,但是他的属性都将映射到其子类的数据库字段中
//标注为@MappedSuperclass的类不能再标注 @Entity或 @Table注解,也无需实现序列化接口
@Data
@NoArgsConstructor
@AllArgsConstructor
public class BaseEntity implements Serializable {
    @Id  //主键我们通过Id的注解来定义
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    //@GeneratedValue注解存在的意义主要就是为一个实体生成一个唯一标识的主键、@GeneratedValue提供了主键的生成策略。
    // @GeneratedValue注解有两个属性,分别是strategy和generator
    //AUTO主键由程序控制,是默认选项 ,不设置就是这个
    //IDENTITY 主键由数据库生成, 采用数据库自增长, Oracle不支持这种方式
    //SEQUENCE 通过数据库的序列产生主键, MYSQL  不支持
    //Table 提供特定的数据库产生主键, 该方式更有利于数据库的移植
    //generator
    //generator属性的值是一个字符串,默认为"",其声明了主键生成器的名称
    //(对应于同名的主键生成器@SequenceGenerator和@TableGenerator)

    private Long id;
    @Column(updatable = false)   //用来标识实体类中属性与数据表中字段的对应关系  updatable=false 不包含 UPDATE
    @CreationTimestamp           //创建时间戳  表示该字段为创建时间时间字段
    private Date createTime;
    @UpdateTimestamp
    private Date updateTime;     //更新时间戳   表示该字段为修改时间时间字段
}
``

```java
@Entity   //标注用于实体类声明语句之前,指出该Java 类为实体类,将映射到指定的数据库表。如声明一个实体类 Customer,它将映射到数据库中的 customer 表上
@Table(name = "T_COFFEE")
//当实体类与其映射的数据库表名不同名时需要使用 @Table 标注说明,该标注与 @Entity 标注并列使用,置于实体类声明语句之前,可写于单独语句行,也可与声明语句同行。
//@Table 标注的常用选项是 name,用于指明数据库的表名@Table标注还有一个两个选项 catalog 和 schema 用于设置表所属的数据库目录或模式,通常为数据库名。uniqueConstraints 选项用于设置约束条件,通常不须设置
@Builder //添加构造器  构造Builder模式的结构。通过内部类Builder()进行构建对象。
@Data   //提供 set get 方法  =@Setter+@Getter+@EqualsAndHashCode+@NoArgsConstructor
@ToString(callSuper = true)  //提供tostring方法  用自己的属性和从父类继承的属性
@NoArgsConstructor
@AllArgsConstructor
public class Coffee extends BaseEntity implements Serializable {
    // Serializable接口实现序列化 (Serialization)是将对象的状态信息转换为可以存储或传输的形式的过程。在序列化期间,对象将其当前状态写入到临时或持久性存储区。以后,可以通过从存储区中读取或反序列化对象的状态,重新创建该对象。
    private String name;
    @Type(type = "org.jadira.usertype.moneyandcurrency.joda.PersistentMoneyMinorAmount",
            parameters = {@org.hibernate.annotations.Parameter(name = "currencyCode", value = "CNY")}) //记录一个对象的类型
    private Money money;


}
@Entity
@Table(name = "T_ORDER")
@Data
@ToString(callSuper = true)
@NoArgsConstructor
@AllArgsConstructor
@Builder
public class CoffeeOrder extends BaseEntity implements Serializable {
    private String customer;
    @ManyToMany        //多对多关系
    @JoinTable(name = "T_ORDER_COFFEE")//关联表T_ORDER_COFFEE
    @OrderBy("id")                  //因为查询出来的数据是混乱的,所以需要根据id来排序
    private List<Coffee> items;
    @Enumerated                     //打开枚举  实体Entity中通过@Enumerated标注枚举类型,例如将CustomerEO实体中增加一个CustomerType类型的枚举型属性
    @Column(nullable = false)       //该字段不能为空
    private OrderState state;

}
public enum OrderState {
    INIT, PAID, BREWING, BREWED, TAKEN, CANCELLED
}

public interface CoffeeOrderRepository extends JpaRepository<CoffeeOrder,Long> {
}

public interface CoffeeRepository extends JpaRepository<Coffee, Long> {
}

@Slf4j
@Service         //@Service用来表示一个业务层bean  如果一个类带了@Service注解,将自动注册到Spring容器,不需要再在applicationContext.xml文件定义bean了,类似的还包括@Component、@Repository、@Controller。
@Transactional  //开启事务的注解
public class CoffeeOrderService {
    @Autowired   //取出容器中的bean
    private CoffeeOrderRepository orderRepository;

    public CoffeeOrder createOrder(String customer, Coffee... coffee) {
        CoffeeOrder order = CoffeeOrder.builder()
                .customer(customer)
                .items(new ArrayList<>(Arrays.asList(coffee)))
                .state(OrderState.INIT)
                .build();               //新建一个coffeeorder
        CoffeeOrder saved = orderRepository.save(order);  //保存到数据库
        log.info("New Order: {}", saved);
        return saved;
    }

    public boolean updateState(CoffeeOrder order, OrderState state) {   //更新订单状态
        if (state.compareTo(order.getState()) <= 0) {
            log.warn("Wrong State order: {}, {}", state, order.getState());
            return false;
        }
        order.setState(state);
        orderRepository.save(order);
        log.info("Updated Order: {}", order);
        return true;
    }
}
@Slf4j
@Service
@CacheConfig(cacheNames = "coffee")  //对缓存做一个设置,比如说 名字
public class CoffeeService {
    @Autowired
    private CoffeeRepository coffeeRepository;

    @Cacheable   //执行这个方法,如果这个方法的结果在缓存里面,直接从缓存里面取,没有的话,执行完方法之后,将结果放到缓存中
    public List<Coffee> findAllCoffee() {
        return coffeeRepository.findAll();
    }

    @CacheEvict  //做缓存的清理
    public void reloadCoffee() {
    }

    public Optional<Coffee> findOneCoffee(String name) {
        ExampleMatcher matcher = ExampleMatcher.matching()
                .withMatcher("name", exact().ignoreCase());
        //ExampleMatcher实例查询三要素
        //实体对象:在ORM框架中与Table对应的域对象,一个对象代表数据库表中的一条记录,如上例中User对象,对应user表。在构建查询条件时,一个实体对象代表的是查询条件中的“数值”部分。如:要查询姓“X”的客户,
        //实体对象只需要存储条件值“X”。
        //匹配器:ExampleMatcher对象,它是匹配“实体对象”的,表示了如何使用“实体对象”中的“值”进行查询,它代表的是“查询方式”,解释了如何去查的问题。如:要查询姓“X”的客户,即姓名以“X”开头的客户,该对象就表示了“
        //以某某开头的”这个查询方式,如上例中:
        //withMatcher(“userName”, GenericPropertyMatchers.startsWith())
        //实例:即Example对象,代表的是完整的查询条件。由实体对象(查询条件值)和匹配器(查询方式)共同创建。最终根据实例来findAll即可。

        Optional<Coffee> coffee = coffeeRepository.findOne(
                Example.of(Coffee.builder().name(name).build(), matcher));
        log.info("Coffee Found: {}", coffee);
        return coffee;
    }
}
@Slf4j
@EnableTransactionManagement  //添加开启事务的注解
@SpringBootApplication        //@SpringBootApplication = (默认属性)@Configuration + @EnableAutoConfiguration + @ComponentScan。
//@Configuration:提到@Configuration就要提到他的搭档@Bean。使用这两个注解就可以创建一个简单的spring配置类,可以用来替代相应的xml配置文件。
//@Configuration的注解类标识这个类可以使用Spring IoC容器作为bean定义的来源。@Bean注解告诉Spring,一个带有@Bean的注解方法将返回一个对象,该对象应该被注册为在Spring应用程序上下文中的bean。
//@EnableAutoConfiguration:能够自动配置spring的上下文,试图猜测和配置你想要的bean类,通常会自动根据你的类路径和你的bean定义自动配置。
//@ComponentScan:会自动扫描指定包下的全部标有@Component的类,并注册成bean,当然包括@Component下的子注解@Service,@Repository,@Controller。

@EnableJpaRepositories   //@EnableJpaRepositories用来扫描和发现指定包及其子包中的Repository定义
@EnableCaching(proxyTargetClass = true)  //拦截类的执行 即aop的拦截 用来判断方法是直接执行还是从缓存中取数据
public class SpringBucksApplication implements ApplicationRunner {
	@Autowired
	private CoffeeService coffeeService;

	public static void main(String[] args) {
		SpringApplication.run(SpringBucksApplication.class, args);
	}

	@Override
	public void run(ApplicationArguments args) throws Exception {
		log.info("Count: {}", coffeeService.findAllCoffee().size());
		for (int i = 0; i < 5; i++) {
			log.info("Reading from cache.");
			coffeeService.findAllCoffee();
		}
		Thread.sleep(5_000);
		log.info("Reading after refresh.");
		coffeeService.findAllCoffee().forEach(c -> log.info("Coffee {}", c.getName()));
	}
}
发布了59 篇原创文章 · 获赞 6 · 访问量 971

猜你喜欢

转载自blog.csdn.net/weixin_43790623/article/details/103313349