diff --git a/pom.xml b/pom.xml index 429e41d..16f8379 100644 --- a/pom.xml +++ b/pom.xml @@ -93,6 +93,7 @@ json-tutorials hibernate-tutorials commons-dbutils-tutorial + spring-cache-example spring-jdbc-tutorials spring-data-example spring-ibatis-example diff --git a/spring-cache-example/README.md b/spring-cache-example/README.md new file mode 100644 index 0000000..91d176b --- /dev/null +++ b/spring-cache-example/README.md @@ -0,0 +1,79 @@ +# spring-cache-example + +基于注解(@Cacheable)的spring cache + +需要使用``或`@EnableCaching`开启缓存 + +### 1. 使用ehcache + +``` + + + +``` + +### 2. 使用spring内置的默认实现基于JDK的ConcurrentMap + +``` + + + + + + + + +``` + +### @Cacheable、@CachePut、@CacheEvict 注释介绍 + +@Cacheable 的作用 主要针对方法配置,能够根据方法的请求参数对其结果进行缓存 + +@Cacheable 主要的参数 + +``` +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”) +``` + +@CachePut 作用和配置方法 + +@CachePut 的作用 主要针对方法配置,能够根据方法的请求参数对其结果进行缓存,和 @Cacheable 不同的是,它每次都会触发真实方法的调用 + +@CachePut 主要的参数 + +``` +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”) +``` + +@CacheEvict 作用和配置方法 + +@CachEvict 的作用 主要针对方法配置,能够根据一定的条件对缓存进行清空 + +@CacheEvict 主要的参数 + +``` +value 缓存的名称,在 spring 配置文件中定义,必须指定至少一个 例如:@CachEvict(value=”mycache”) 或者 @CachEvict(value={”cache1”,”cache2”} +key 缓存的 key,可以为空,如果指定要按照 SpEL 表达式编写,如果不指定,则缺省按照方法的所有参数进行组合 例如:@CachEvict(value=”testcache”,key=”#userName”) +condition 缓存的条件,可以为空,使用 SpEL 编写,返回 true 或者 false,只有为 true 才清空缓存 例如:@CachEvict(value=”testcache”,condition=”#userName.length()>2”) +allEntries 是否清空所有缓存内容,缺省为 false,如果指定为 true,则方法调用后将立即清空所有缓存 例如:@CachEvict(value=”testcache”,allEntries=true) +beforeInvocation 是否在方法执行前就清空,缺省为 false,如果指定为 true,则在方法还没有执行的时候就清空缓存,缺省情况下,如果方法执行抛出异常,则不会清空缓存 例如:@CachEvict(value=”testcache”,beforeInvocation=true) +``` + + + + + + + + + + + + + + + diff --git a/spring-cache-example/pom.xml b/spring-cache-example/pom.xml new file mode 100644 index 0000000..ff05c15 --- /dev/null +++ b/spring-cache-example/pom.xml @@ -0,0 +1,73 @@ + + + 4.0.0 + + net.aimeizi + maven-framework-project + 1.0.0 + + spring-cache-example + war + spring-cache-example + http://maven.apache.org + + UTF-8 + + + + + servlet-api + javax.servlet + 2.5 + provided + + + jstl + jstl + 1.2 + + + org.springframework + spring-context-support + 3.2.5.RELEASE + + + org.springframework + spring-webmvc + 3.2.5.RELEASE + + + org.slf4j + slf4j-log4j12 + 1.7.5 + + + net.sf.ehcache + ehcache + 2.7.0 + + + org.springframework + spring-test + 3.2.5.RELEASE + + + junit + junit + 4.11 + + + + + + + org.apache.tomcat.maven + tomcat7-maven-plugin + 2.1 + + + + + diff --git a/spring-cache-example/src/main/java/org/spring/cache/example/Employee.java b/spring-cache-example/src/main/java/org/spring/cache/example/Employee.java new file mode 100644 index 0000000..a5917d0 --- /dev/null +++ b/spring-cache-example/src/main/java/org/spring/cache/example/Employee.java @@ -0,0 +1,60 @@ +package org.spring.cache.example; + +import java.io.Serializable; + +@SuppressWarnings("serial") +public class Employee implements Serializable{ + + private int id; + private String empname; + private int age; + private String phone; + + public Employee() { + } + + public Employee(int id, String empname, int age, String phone) { + super(); + this.id = id; + this.empname = empname; + this.age = age; + this.phone = phone; + } + + public Employee(String empname) { + super(); + this.empname = empname; + } + + public int getId() { + return id; + } + public void setId(int id) { + this.id = id; + } + public String getEmpname() { + return empname; + } + public void setEmpname(String empname) { + this.empname = empname; + } + public int getAge() { + return age; + } + public void setAge(int age) { + this.age = age; + } + public String getPhone() { + return phone; + } + public void setPhone(String phone) { + this.phone = phone; + } + + @Override + public String toString() { + return "Employee [id=" + id + ", empname=" + empname + ", age=" + age + + ", phone=" + phone + "]"; + } + +} diff --git a/spring-cache-example/src/main/java/org/spring/cache/example/EmployeeService.java b/spring-cache-example/src/main/java/org/spring/cache/example/EmployeeService.java new file mode 100644 index 0000000..5e2d26e --- /dev/null +++ b/spring-cache-example/src/main/java/org/spring/cache/example/EmployeeService.java @@ -0,0 +1,90 @@ +package org.spring.cache.example; + +import java.util.ArrayList; +import java.util.List; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.cache.annotation.CacheEvict; +import org.springframework.cache.annotation.CachePut; +import org.springframework.cache.annotation.Cacheable; +import org.springframework.stereotype.Service; + +@Service +public class EmployeeService { + + Logger logger = LoggerFactory.getLogger(getClass()); + + /** + * @Cacheable:负责将方法的返回值加入到缓存中 + * + * @CacheEvict:负责清除缓存 + * + * @Cacheable 支持如下几个参数: + * + * value:缓存位置名称,不能为空,如果使用EHCache,就是ehcache.xml中声明的cache的name + * + * key:缓存的key,默认为空,既表示使用方法的参数类型及参数值作为key,支持SpEL + * + * condition:触发条件,只有满足条件的情况才会加入缓存,默认为空,既表示全部都加入缓存,支持SpEL + * + * @CacheEvict 支持如下几个参数: + * + * value:缓存位置名称,不能为空,同上 + * + * key:缓存的key,默认为空,同上 + * + * condition:触发条件,只有满足条件的情况才会清除缓存,默认为空,支持SpEL + * + * allEntries:true表示清除value中的全部缓存,默认为false + */ + + @Cacheable(value = "employeeCache"/*,condition="#empname.length() <= 4"*/) + // 使用了一个缓存名叫 employeeCache + public Employee getEmployeeByName(String empname) { + // 方法内部实现不考虑缓存逻辑,直接实现业务 + return getFromDB(empname); + } + + @Cacheable(value = "employeeCache", key = "#id") + // 使用了一个缓存名为employeeCache的缓存 + public String getEmployee(Integer id) { + logger.info("get employee called " + id); + return "getEmployee" + id; + } + + @Cacheable(value = "employeeCache", key = "#id", condition = "#id <= 10") + // 使用了一个缓存名为employeeCache的缓存,缓存id小于等于10 + public String findEmployee(Integer id) { + logger.info("get employee called"); + return "findEmployee" + id; + } + + @CacheEvict(value = "employeeCache", allEntries = true) + public List listEmployee() { + List emps = new ArrayList(); + return emps; + } + + @CachePut(value = "employeeCache", key = "#employee.getEmpname()") + // 清空 employeeCache 缓存 + public void updateEmployee(Employee employee) { + updateDB(employee); + } + + @CacheEvict(value = "employeeCache", allEntries = true) + // 清空 employeeCache 缓存 + public void reload() { + + } + + private Employee getFromDB(String empname) { + System.out.println("real querying db..." + empname); + return new Employee(empname); + } + + private void updateDB(Employee employee) { + System.out.println("real update db..." + employee.getEmpname()); + } + +} diff --git a/spring-cache-example/src/main/java/org/spring/cache/example/WebController.java b/spring-cache-example/src/main/java/org/spring/cache/example/WebController.java new file mode 100644 index 0000000..a07e80b --- /dev/null +++ b/spring-cache-example/src/main/java/org/spring/cache/example/WebController.java @@ -0,0 +1,22 @@ +package org.spring.cache.example; + +import java.util.HashMap; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Controller; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestParam; + +@Controller +public class WebController { + + @Autowired + EmployeeService employeeService; + + @RequestMapping("/index.htm") + public String homePage(@RequestParam(required = false) Integer id, + HashMap map) { + map.put("message", employeeService.getEmployee(id)); + return "index"; + } +} diff --git a/spring-cache-example/src/main/resources/ehcache.xml b/spring-cache-example/src/main/resources/ehcache.xml new file mode 100644 index 0000000..37cb264 --- /dev/null +++ b/spring-cache-example/src/main/resources/ehcache.xml @@ -0,0 +1,8 @@ + + + + + \ No newline at end of file diff --git a/spring-cache-example/src/main/resources/log4j.xml b/spring-cache-example/src/main/resources/log4j.xml new file mode 100644 index 0000000..61d1a88 --- /dev/null +++ b/spring-cache-example/src/main/resources/log4j.xml @@ -0,0 +1,42 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/spring-cache-example/src/main/webapp/WEB-INF/applicationContext.xml b/spring-cache-example/src/main/webapp/WEB-INF/applicationContext.xml new file mode 100644 index 0000000..f42add3 --- /dev/null +++ b/spring-cache-example/src/main/webapp/WEB-INF/applicationContext.xml @@ -0,0 +1,57 @@ + + + + + + + + + + + + org.springframework.web.servlet.view.JstlView + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/spring-cache-example/src/main/webapp/WEB-INF/views/index.jsp b/spring-cache-example/src/main/webapp/WEB-INF/views/index.jsp new file mode 100644 index 0000000..f3c5918 --- /dev/null +++ b/spring-cache-example/src/main/webapp/WEB-INF/views/index.jsp @@ -0,0 +1,12 @@ +<%@ page language="java" contentType="text/html; charset=utf-8" pageEncoding="utf-8"%> +<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %> + + + + +Cache Example + + +

This is ${message }

+ + \ No newline at end of file diff --git a/spring-cache-example/src/main/webapp/WEB-INF/web.xml b/spring-cache-example/src/main/webapp/WEB-INF/web.xml new file mode 100644 index 0000000..29a212f --- /dev/null +++ b/spring-cache-example/src/main/webapp/WEB-INF/web.xml @@ -0,0 +1,23 @@ + + + + + sample + + org.springframework.web.servlet.DispatcherServlet + + + contextConfigLocation + /WEB-INF/applicationContext.xml + + 1 + + + + sample + / + + + \ No newline at end of file diff --git a/spring-cache-example/src/test/java/org/spring/cache/example/EmployeeServiceTest.java b/spring-cache-example/src/test/java/org/spring/cache/example/EmployeeServiceTest.java new file mode 100644 index 0000000..1e662a2 --- /dev/null +++ b/spring-cache-example/src/test/java/org/spring/cache/example/EmployeeServiceTest.java @@ -0,0 +1,72 @@ +package org.spring.cache.example; + +import org.junit.Test; +import org.junit.runner.RunWith; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.test.context.ContextConfiguration; +import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; + +@ContextConfiguration(locations = "file:src/main/webapp/WEB-INF/applicationContext.xml") +@RunWith(SpringJUnit4ClassRunner.class) +public class EmployeeServiceTest { + + @Autowired + EmployeeService employeeService; + + @Test + public void getEmployee() { + employeeService.getEmployee(1); + employeeService.getEmployee(1); + employeeService.getEmployee(2); + employeeService.getEmployee(2); + employeeService.getEmployee(3); + employeeService.getEmployee(3); + } + + @SuppressWarnings("unused") + @Test + public void testCache() { + // 第一次查询,应该走数据库 + System.out.print("first query..."); + employeeService.getEmployeeByName("somebody"); + // 第二次查询,应该不查数据库,直接返回缓存的值 + System.out.print("second query..."); + employeeService.getEmployeeByName("somebody"); + System.out.println(); + + System.out.println("start testing clear cache..."); // 更新某个记录的缓存,首先构造两个账号记录,然后记录到缓存中 + Employee employee1 = employeeService.getEmployeeByName("somebody1"); + Employee employee2 = employeeService.getEmployeeByName("somebody2"); + // 开始更新其中一个 + employee1.setId(1212); + employeeService.updateEmployee(employee1); + employeeService.getEmployeeByName("somebody1");// 因为被更新了,所以会查询数据库 + employeeService.getEmployeeByName("somebody2");// 没有更新过,应该走缓存 + employeeService.getEmployeeByName("somebody1");// 再次查询,应该走缓存 // 更新所有缓存 + employeeService.reload(); + employeeService.getEmployeeByName("somebody1");// 应该会查询数据库 + employeeService.getEmployeeByName("somebody2");// 应该会查询数据库 + employeeService.getEmployeeByName("somebody1");// 应该走缓存 + employeeService.getEmployeeByName("somebody2");// 应该走缓存 + } + + @Test + public void testCacheCondition() { + employeeService.getEmployeeByName("somebody");// 长度大于 4,不会被缓存 + employeeService.getEmployeeByName("sbd");// 长度小于 4,会被缓存 + employeeService.getEmployeeByName("somebody");// 还是查询数据库 + employeeService.getEmployeeByName("sbd");// 会从缓存返回 + } + + @Test + public void testCachePut() { + Employee employee = employeeService.getEmployeeByName("someone"); + employee.setAge(18); + employeeService.updateEmployee(employee); + employee.setAge(28); + employeeService.updateEmployee(employee); + employee = employeeService.getEmployeeByName("someone"); + System.out.println(employee.getEmpname()); + } + +}