предисловие
Кэширование в реальных проектах имеет важное значение, рациональное использование технологии кэширования скорости доступа может значительно улучшить сайт для улучшения пользовательского опыта. Статья фильма о том, как использовать весной ботинок вehcacheЭто кеширующий фреймворк.
Статья впервые опубликована в личном блоге: [www.xiongfrblog.cn】
Введение в ehcache
существуетjavaСуществует много технологий, которые могут реализовать функцию кэширования, самая простая и понятная — использоватьjavaавтономныйMapконтейнер или просто используйте существующую структуру кэширования, такую какmemcache,ehcache, и очень популярныйredis. Представьте здесьehcacheВ основном потому, что это действительно удобно, иmemcacheиredis都需要额外搭建服务,更适合分布式部署的项目以便于各个模块之间的使用共有的缓存内容。 иehcacheВ основном это кеш памяти, который также можно кэшировать на диск.Он быстрый, эффективный и мощный и подходит для нашего общего использования в одном проекте.
Spring boot настроить ehcache
существуетspring bootСредняя конфигурацияehcahceЕсть четыре основных шага:
-
pom.xmlдобавить зависимости в - настроить
ehcache.xmlконфигурационный файл - включить кеш
- Использование кэшей с аннотациями
Ниже мы подробно опишем каждый шаг.
добавить зависимости
хочу быть вspring bootИспользовать кеш вehcacheзависимости, поэтому мыpom.xmlДобавьте следующие зависимости в:
<!--开启缓存-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-cache</artifactId>
</dependency>
<!-- EhCache -->
<dependency>
<groupId>net.sf.ehcache</groupId>
<artifactId>ehcache</artifactId>
</dependency>
написать файл конфигурации
После добавления зависимостейspring bootбудет автоматически загружен по умолчаниюsrc/mian/resourcesв каталогеehcache.xmlФайл, поэтому нам нужно вручную создать файл в этом каталоге, вот пример:
<?xml version="1.0" encoding="UTF-8"?>
<ehcache xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="http://ehcache.org/ehcache.xsd">
<!-- 磁盘缓存文件路径 -->
<diskStore path="java.io.tmpdir"/>
<!-- 默认配置 -->
<defaultCache eternal="false"
maxElementsInMemory="1000"
overflowToDisk="false"
diskPersistent="false"
timeToIdleSeconds="0"
timeToLiveSeconds="600"
memoryStoreEvictionPolicy="LRU"/>
<!-- 自定义配置 -->
<cache name="userCache"
eternal="false"
maxElementsInMemory="1000"
overflowToDisk="false"
diskPersistent="false"
timeToIdleSeconds="0"
timeToLiveSeconds="600"
memoryStoreEvictionPolicy="LRU"/>
</ehcache>
Три узла, которые появляются в образце, описаны ниже:
-
<diskStore>: Этот узел необязателен, его нужно настроить только при использовании дискового хранилища, указав путь, по которому файл кеша сохраняется на диске.pathатрибут, чтобы указать, что суффикс файла, используемый кэшем диска,*.dataи*.index, в основном имеет следующие значения:-
user.home: домашний каталог пользователя -
user.dir: текущий рабочий каталог пользователя -
java.io.tmpdir: временный путь по умолчанию -
ehcache.disk.store.dir:каталог конфигурации кэша - пользовательский абсолютный путь
-
Если вы не знакомы с этими каталогами, вы можетеjavaполучено из следующего:
public static void main(String[] args) {
System.out.println(System.getProperty("user.home"));
System.out.println(System.getProperty("user.dir"));
System.out.println(System.getProperty("java.io.tmpdir"));
}
Ниже приведен путь, напечатанный моей машиной, только для справки:
C:\Users\Administrator
D:\Program Data\eclipse-workspace\springboot-ehcache
C:\Users\Administrator\AppData\Local\Temp\
Здесь следует отметить, что для кэширования объекта на диске объект должен реализовать интерфейс сериализации.
-
<ehcache>: Пользовательская область буфера, там может быть нулевой или более, важные атрибуты следующие:-
name: Имя буферной области, обязательный атрибут, используемый для различения уникального идентификатора буферной области. -
eternal: Укажите, является ли содержимое в кеше постоянным, необязательное значение.trueилиfalse, если вы выберетеtrueзатем установитеtimeToIdleSecondsа такжеtimeToLiveSecondsбудет недействительным. -
maxElementsInMemory: максимальное количество объектов, которые можно хранить в буферной области.overflowToDiskСуществуют различные действия для значения свойства. -
overflowToDisk: Включить ли сохранение на диск, когда количество объектов кэша превышает максимальное количество, необязательное значение.trueилиfalse, значениеtrueКогда он выйдет за рамки содержимого кэша на диск, какfalseбудет основываться наmemoryStoreEvictionPolicyПолитика, настроенная атрибутом, заменяет исходное содержимое. -
diskPersistent: сохраняется ли дисковое хранилище после перезапуска виртуальной машины, по умолчаниюfalse, если этоtrueПри инициализации системы содержимое диска загружается в кэш. -
timeToIdleSeconds: Установите время простоя (единица измерения: секунды) элемента до истечения срока его действия, то есть максимальный интервал времени для доступа к элементу. По истечении этого времени элемент будет очищен. Значение по умолчанию:0, указывая на то, что элемент может бездействовать неограниченное время. -
timeToLiveSeconds: Установите время жизни элемента в области кеша (единица измерения: секунды), то есть время от создания до очистки. По истечении этого времени элемент будет очищен. Значение по умолчанию:0, указывающее, что элемент может храниться неограниченное время. -
memoryStoreEvictionPolicy: Стратегия хранения и очистки кэша. достигатьmaxElementsInMemoryограничение иoverflowToDiskзначениеfalseВремяehcacheСоответствующая стратегия очистки будет выполнена в соответствии со значением этого атрибута, который имеет следующие три значения.ehcacheТри стратегии очистки кеша для , значение по умолчанию равноLRU:-
FIFO: стратегия «первым пришел — первым вышел»(First In First Out). -
LFU: наименее используемый(Less Frequently Used)Весь элемент кэша будет иметь ряд элементов - это использование записей свойств, минимум, который будет удален при очистке элементов. -
LRU: Полезно использовать в последнее время(Least Resently Used), Все кэшированные элементы будут иметь атрибут для записи времени последнего использования, а элемент с самым ранним временем будет очищен при очистке элемента.
-
-
diskExpiryThreadIntervalSeconds: Интервал выполнения потока очистки кэша диска, по умолчанию 120 секунд. -
diskSpoolBufferSizeMB: Установите размер дискового кеша, по умолчанию 30 МБ. -
maxEntriesLocalDisk: устанавливает максимальное количество элементов, которые могут храниться в кэше диска.
-
-
<defaultCache>: Буфер по умолчанию, которыйnameсобственностьdefaultиз<ehcache>узлы, атрибуты и<ehcache>узлы одинаковые, т.ehcache.xmlВ файле может быть только один<defaultCache>узел, когда у нас нет кастомного<ehcache>, этот буфер используется по умолчанию.
Есть что-то обратить внимание на здесь значение defaultcache, потому что это особый, поэтому мы больше не можем определять кеш по умолчанию, когда мы настраиваем кэш, и мы не можем указать кэш по умолчанию по значению = по умолчанию при использовании его.,
Вот еще немного, если вы не хотите использовать путь и имя по умолчанию в проекте, мы также можем настроить егоehcacheИмя и путь файла конфигурации, вapplication.propertiesНастройте в файле конфигурации следующее:
#后边的路径可以自己指定
spring.cache.ehcache.config=classpath:ehcache.xml
включить кеш
spring bootОткрыть кеш в классе запуска очень просто, просто добавьте@EnableCachingПросто аннотируйте.
использовать аннотации
spring bootиспользуется вehcacheКэш в основном используется через аннотации, и мы обычно используемserviceУровень реализации использует функцию кэширования, а наиболее часто используемые аннотации следующие:
@Cacheable
Эта аннотация в основном используется для методов.Каждый раз, когда программа входит в метод, помеченный этой аннотацией, система сначала определяет, есть ли в кэше идентичный метод.keyЭлемент , если он существует, будет напрямую возвращать значение, хранящееся в буфере,И содержание метода, если он не существует, выполните метод и оцените, необходимо ли добавить значение возврата в буферную область, общие атрибуты:
-
value: укажите, какую область кэша использовать, что мы и настроили в файле конфигурации.<ehcache>узлаnameЗначение, соответствующее атрибуту, можно указать несколько значений.//指定一个 @Cacheable(value="userCache") //指定多个 @Cacheable(value={"userCache","userCache2"}) -
key: элемент кешаkey, нужно следитьSpELНаписание выражения, которое мы обычно определяем по параметрам указанного метода.//#p0表示将第一个参数当成key,也可以直接写参数名字例如:#id,两者表达意思一样 @Cacheable(value="userCache",key="#p0") public SysUser getById(Integer id){//内容省略...}; -
condition: Добавьте условия кэширования, нужно следоватьSpELвыражение, написанное только в том случае, если свойство возвращаетtrueкеш добавляется.//仅当id>10时才缓存 @Cacheable(value="userCache",key="#p0",condition="#p0>10") public SysUser getById(Integer id){//内容省略...};
@CachePut
Эта аннотация в основном используется на методах.О необходимости добавления кэша можно судить по параметрам и возвращаемым значениям метода и пользовательским условиям.Метод, помеченный этой аннотацией, обязательно будет выполнен, и его атрибуты совпадают в виде@CacheableПоследовательный.
@CachePut(value="userCache",key="#entity.id")
public SysUser insertSysuser(SysUser entity) {
// TODO Auto-generated method stub
//省略内容
}
@CacheEvict
Эта аннотация в основном используется для методов и может очищать кеш в соответствии с условиями. Общие атрибуты:
-
value: то же, что и выше -
key: -
condition: то же, что и выше -
allEntries: очищать ли весь кешированный контент, по умолчаниюfalse, если установленоtrue, то после выполнения метода иconditionУсловие очистит все содержимое области буфера. -
beforeInvocation: если операция очистки содержимого выполняется до выполнения метода, по умолчаниюfalse, указывающее, что операция очистки выполняется после выполнения метода. Если во время выполнения метода возникнет исключение, операция очистки не будет выполнена.true, это означает, что операция очистки выполняется до выполнения метода.
@CacheEvict(value="userCache",key="#p0",allEntries=false, beforeInvocation=true)
public int deleteByPrimarykey(Integer key) {
// TODO Auto-generated method stub
//省略内容
}
тест на эффект
представленный вышеspring bootнастроитьehcache, а затем протестируйте эффект кеша. Этот проект интегрируетMybatisА в рамках лог-фреймворка базовый код выкладываться не будет, а самые критичные будут даваться напрямую.serviceСлой реализации иcontrollerкод:
SysuserServiceImpl.java
package com.web.springbootehcache.service.impl;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cache.annotation.CacheEvict;
import org.springframework.cache.annotation.CachePut;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.stereotype.Service;
import com.web.springbootehcache.dao.SysUserMapper;
import com.web.springbootehcache.entity.SysUser;
import com.web.springbootehcache.service.IsysUserService;
/**
* @author Promise
* @createTime 2019年3月19日
* @description
*/
@Service("sysuserService")
public class SysUserServiceImpl implements IsysUserService{
private final static Logger log = LoggerFactory.getLogger(SysUserServiceImpl.class);
@Autowired
private SysUserMapper sysuserMapper;
@Override
@Cacheable(value="userCache",key="#p0")
public SysUser fingByPrimarykey(Integer key) {
// TODO Auto-generated method stub
log.debug("去数据库查询了数据!");
return sysuserMapper.selectByPrimaryKey(key);
}
@Override
@CachePut(value="userCache",key="#p0.id")
public SysUser updateSysuser(SysUser entity) {
// TODO Auto-generated method stub
log.debug("更新了数据库数据!");
int res = sysuserMapper.updateByPrimaryKey(entity);
if(res >0)
return entity;
else
return null;
}
@Override
@CachePut(value="userCache",key="#entity.id")
public SysUser insertSysuser(SysUser entity) {
// TODO Auto-generated method stub
int res = sysuserMapper.insert(entity);
log.debug("新增了数据!id为:{}",entity.getId());
if(res >0)
return entity;
else
return null;
}
@Override
@CacheEvict(value="userCache",key="#p0",beforeInvocation=true)
public int deleteByPrimarykey(Integer key) {
// TODO Auto-generated method stub
log.debug("删除了数据!");
return sysuserMapper.deleteByPrimaryKey(key);
}
}
Основы даны в этом классеCRUDОперация кэширования, соответствующая операции, конечно, не является абсолютной, и ее можно изменить в соответствии с вашими потребностями в реальном использовании.
IndexController.java
package com.web.springbootehcache.controller;
import java.util.HashMap;
import java.util.Map;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import com.web.springbootehcache.entity.SysUser;
import com.web.springbootehcache.service.IsysUserService;
/**
* @author Promise
* @createTime 2019年3月19日
* @description
*/
@RestController
public class IndexController {
private final static Logger log = LoggerFactory.getLogger(IndexController.class);
@Autowired
private IsysUserService sysuserService;
@RequestMapping(value="/select/{id}")
public Object index(@PathVariable Integer id) {
Map<String, Object> map = new HashMap<>();
SysUser sysuser = sysuserService.fingByPrimarykey(id);
log.debug("查询了id为:{}的用户信息!",sysuser.getId());
SysUser sysuser2 = sysuserService.fingByPrimarykey(id);
log.debug("查询了id为:{}的用户信息!",sysuser2.getId());
map.put("res", sysuser);
return map;
}
@RequestMapping(value="/update")
public Object update() {
Map<String, Object> map = new HashMap<>();
//第一次修改
SysUser sysuser = new SysUser(1, "eran", "eran1", 20, "M");
sysuserService.updateSysuser(sysuser);
//第一次查询
sysuser = sysuserService.fingByPrimarykey(1);
log.debug("查询了id为:{}的用户信息!",sysuser.getId());
//第2次修改
sysuser = new SysUser(1, "eran", "eran2", 20, "M");
sysuserService.updateSysuser(sysuser);
//第2次查询
sysuser = sysuserService.fingByPrimarykey(1);
log.debug("查询了id为:{}的用户信息!",sysuser.getId());
map.put("res", sysuser);
return map;
}
@RequestMapping(value="/insert")
public Object insert() {
Map<String, Object> map = new HashMap<>();
SysUser sysuser = new SysUser();
sysuser.setName("admin");
sysuser.setAge(22);
sysuser.setPass("admin");
sysuser.setSex("M");
sysuserService.insertSysuser(sysuser);
//查询
sysuser = sysuserService.fingByPrimarykey(sysuser.getId());
map.put("res", sysuser);
return map;
}
@RequestMapping(value="/delete/{id}")
public Object delete(@PathVariable Integer id) {
Map<String, Object> map = new HashMap<>();
sysuserService.deleteByPrimarykey(id);
//查询
SysUser sysuser = sysuserService.fingByPrimarykey(id);
map.put("res", sysuser);
return map;
}
}
Тестовые данные базы данных
Запустите проект, посетитеlocalhost:1188/select/1, журнал консоли выглядит следующим образом:
Ожидаемый эффект: выполнить операцию запроса дважды, доступа к базе данных один раз.
[default]2019-03-20 17:35:05,287 [http-nio-1188-exec-2 32] DEBUG >> 去数据库查询了数据! >> c.w.s.s.i.SysUserServiceImpl
[default]2019-03-20 17:35:05,327 [http-nio-1188-exec-2 110] INFO >> HikariPool-1 - Starting... >> c.z.h.HikariDataSource
[default]2019-03-20 17:35:05,332 [http-nio-1188-exec-2 68] WARN >> Registered driver with driverClassName=com.mysql.jdbc.Driver was not found, trying direct instantiation. >> c.z.h.u.DriverDataSource
[default]2019-03-20 17:35:06,106 [http-nio-1188-exec-2 123] INFO >> HikariPool-1 - Start completed. >> c.z.h.HikariDataSource
[default]2019-03-20 17:35:06,113 [http-nio-1188-exec-2 159] DEBUG >> ==> Preparing: select id, `name`, pass, sex, age from sys_user where id = ? >> c.w.s.d.S.selectByPrimaryKey
[default]2019-03-20 17:35:06,141 [http-nio-1188-exec-2 159] DEBUG >> ==> Parameters: 1(Integer) >> c.w.s.d.S.selectByPrimaryKey
[default]2019-03-20 17:35:06,176 [http-nio-1188-exec-2 159] DEBUG >> <== Total: 1 >> c.w.s.d.S.selectByPrimaryKey
[default]2019-03-20 17:35:06,185 [http-nio-1188-exec-2 33] DEBUG >> 查询了id为:1的用户信息! >> c.w.s.c.IndexController
[default]2019-03-20 17:35:06,186 [http-nio-1188-exec-2 35] DEBUG >> 查询了id为:1的用户信息! >> c.w.s.c.IndexController
Хорошо видно, что мы выполнили две операции запроса, но операция выборки данных из базы данных была выполнена один раз, видно, что есть еще один момент для прямой выборки данных из кеша, что и дало ожидаемый нами эффект.
доступlocalhost:1188/update, в коде имеемidза2Данные были изменены дважды и дважды запрошены, и данные кэшируются при выполнении операции модификации.idза2Данные еще не в кеше.
Ожидаемый эффект: выполнены две модификации, доступ к двум базам данных, а операция запроса не обращается к базе данных.
[default]2019-03-20 17:47:37,254 [http-nio-1188-exec-1 40] DEBUG >> 更新了数据库数据! >> c.w.s.s.i.SysUserServiceImpl
[default]2019-03-20 17:47:37,291 [http-nio-1188-exec-1 110] INFO >> HikariPool-1 - Starting... >> c.z.h.HikariDataSource
[default]2019-03-20 17:47:37,299 [http-nio-1188-exec-1 68] WARN >> Registered driver with driverClassName=com.mysql.jdbc.Driver was not found, trying direct instantiation. >> c.z.h.u.DriverDataSource
[default]2019-03-20 17:47:37,953 [http-nio-1188-exec-1 123] INFO >> HikariPool-1 - Start completed. >> c.z.h.HikariDataSource
[default]2019-03-20 17:47:37,964 [http-nio-1188-exec-1 159] DEBUG >> ==> Preparing: update sys_user set `name` = ?, pass = ?, sex = ?, age = ? where id = ? >> c.w.s.d.S.updateByPrimaryKey
[default]2019-03-20 17:47:38,006 [http-nio-1188-exec-1 159] DEBUG >> ==> Parameters: eran(String), eran1(String), M(String), 20(Integer), 2(Integer) >> c.w.s.d.S.updateByPrimaryKey
[default]2019-03-20 17:47:38,104 [http-nio-1188-exec-1 159] DEBUG >> <== Updates: 1 >> c.w.s.d.S.updateByPrimaryKey
[default]2019-03-20 17:47:38,237 [http-nio-1188-exec-1 48] DEBUG >> 查询了id为:2的用户信息! >> c.w.s.c.IndexController
[default]2019-03-20 17:47:38,239 [http-nio-1188-exec-1 40] DEBUG >> 更新了数据库数据! >> c.w.s.s.i.SysUserServiceImpl
[default]2019-03-20 17:47:38,239 [http-nio-1188-exec-1 159] DEBUG >> ==> Preparing: update sys_user set `name` = ?, pass = ?, sex = ?, age = ? where id = ? >> c.w.s.d.S.updateByPrimaryKey
[default]2019-03-20 17:47:38,243 [http-nio-1188-exec-1 159] DEBUG >> ==> Parameters: eran(String), eran2(String), M(String), 20(Integer), 2(Integer) >> c.w.s.d.S.updateByPrimaryKey
[default]2019-03-20 17:47:38,286 [http-nio-1188-exec-1 159] DEBUG >> <== Updates: 1 >> c.w.s.d.S.updateByPrimaryKey
[default]2019-03-20 17:47:38,287 [http-nio-1188-exec-1 54] DEBUG >> 查询了id为:2的用户信息! >> c.w.s.c.IndexController
Результаты совпали с нашими ожиданиями.
Принцип новой операции и операции обновления одинаковый@CachePutОбратите внимание, я не буду здесь повторять демонстрацию, непосредственно протестирую функцию удаления данных и очистки соответствующего кеша, доступаlocalhost:1188/delete/2, в это время естьidза1,2две части данных, мы удаляемidза2данные, а затем выполните операцию запроса.
Ожидаемый эффект: удалите данные для доступа к базе данных один раз и очистите соответствующие данные в области кеша.Поскольку содержимое области кеша очищается, данные запроса будут обращаться к базе данных один раз, но соответствующее содержимое в базе данных также были удалены, поэтому запрос данных невозможен.
[default]2019-03-20 17:57:28,337 [http-nio-1188-exec-4 64] DEBUG >> 删除了数据! >> c.w.s.s.i.SysUserServiceImpl
[default]2019-03-20 17:57:28,341 [http-nio-1188-exec-4 159] DEBUG >> ==> Preparing: delete from sys_user where id = ? >> c.w.s.d.S.deleteByPrimaryKey
[default]2019-03-20 17:57:28,342 [http-nio-1188-exec-4 159] DEBUG >> ==> Parameters: 2(Integer) >> c.w.s.d.S.deleteByPrimaryKey
[default]2019-03-20 17:57:28,463 [http-nio-1188-exec-4 159] DEBUG >> <== Updates: 1 >> c.w.s.d.S.deleteByPrimaryKey
[default]2019-03-20 17:57:28,464 [http-nio-1188-exec-4 32] DEBUG >> 去数据库查询了数据! >> c.w.s.s.i.SysUserServiceImpl
[default]2019-03-20 17:57:28,467 [http-nio-1188-exec-4 159] DEBUG >> ==> Preparing: select id, `name`, pass, sex, age from sys_user where id = ? >> c.w.s.d.S.selectByPrimaryKey
[default]2019-03-20 17:57:28,468 [http-nio-1188-exec-4 159] DEBUG >> ==> Parameters: 2(Integer) >> c.w.s.d.S.selectByPrimaryKey
[default]2019-03-20 17:57:28,494 [http-nio-1188-exec-4 159] DEBUG >> <== Total: 0 >> c.w.s.d.S.selectByPrimaryKey
Вывод журнала — это то, что мы ожидали.
Эпилог
хорошо,spring bootИнтегрироватьehcacheЭто конец контента, увидимся в следующем блоге,bye~~