spring aop 可以灵活地切入目标层的方法,控制或者拦截目标方法的运行。本次通过这种机制,控制dao层读取数据库资源的方法,实现缓存。
实现目标:
查询:先从缓存区检索。若检索到则返回结果。若检索不到,则去数据库中加载数据,并将加载出的数据存入缓存区。
删除、修改、查询:与数据库交互,并且清除缓存。
具体实现:
- 实体类User
package com.ss.pojo;
public class User {
private Long id;
private String name;
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@Override
public String toString() {
return "User [id=" + id + ", name=" + name + "]";
}
public User(Long id, String name) {
super();
this.id = id;
this.name = name;
}
public User() {
super();
// TODO Auto-generated constructor stub
}
}
- 模拟的数据库接口。模拟一个数据库,有自带的增删改查的sql
package com.ss.db;
import com.ss.pojo.User;
public interface DBPr {
void insert(User user);
void delete(User user);
void update(User user);
User select(Long id);
}
- 模拟的数据库实现类
package com.ss.db;
import com.ss.pojo.User;
public class Mysql implements DBPr{
// 模拟的数据库中存放的数据
String[] users = {"大大","王五","呃呃","嘤嘤嘤","去去去","我是谁是谁","嘎嘎嘎","哈哈哈","冰冰冰","密密麻麻"};
@Override
public void insert(User user) {
System.out.println("insert method in mysql invoted successfully");
}
@Override
public void delete(User user) {
System.out.println("delete method in mysql invoted successfully");
}
@Override
public void update(User user) {
System.out.println("update method in mysql invoted successfully");
}
@Override
public User select(Long id) {
System.out.println("select method in mysql invoted successfully");
return new User(id, users[id.intValue()]) ;
}
}
- dao层接口
package com.ss.dao;
import com.ss.pojo.User;
public interface IDao {
void insert(User user);
void delete(User user);
void update(User user);
User select(Long id);
}
- dao层实现类
package com.ss.dao;
import org.junit.Test;
import com.ss.db.DBPr;
import com.ss.db.Mysql;
import com.ss.pojo.User;
/**
* 被切入的类
* @author Administrator
*
*/
public class Dao implements IDao{
DBPr db = new Mysql();
@Override
public void insert(User user) {
System.out.println("dao insert method invoked");
}
@Override
public void delete(User user) {
System.out.println("dao delete method invoked");
}
@Override
public void update(User user) {
System.out.println("dao update method invoked");
}
@Override
public User select(Long id) {
System.out.println("dao select method invoked");
return db.select(id);
}
}
- container.xml配置spring bean容器
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context"
xmlns:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-4.3.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-4.3.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop-4.3.xsd">
<context:component-scan base-package="com.ss" />
<aop:aspectj-autoproxy />
<!-- 注册需要被横切的类 -->
<bean id="dao" class="com.ss.dao.Dao" />
</beans>
- aspect实现缓存
package com.ss.spring;
import java.util.HashMap;
import java.util.Map;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.AfterThrowing;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.stereotype.Component;
import com.ss.pojo.User;
/**
* 切面类
* @author Administrator
*
*/
@Aspect
@Component
public class DaoAspect {
/**
* 标记
*/
@Pointcut("execution(* com.ss.dao..*.*(..))")
public void myPointcut(){}
/**
* 缓存区
*/
private final Map<Long,User> cache = new HashMap<Long,User>();
/**
* 切面方法。注意方法名对应
*/
@Around("myPointcut()")
public Object aroundMe(ProceedingJoinPoint jp ){
String method = jp.getSignature().getName(); // 获取被切入的方法名
Object[] args = jp.getArgs(); // 获取传入的参数
if(method.equals("select")){ // 如果是查询方法
User user = cache.get(args[0]);
if(null != user){ // 如果在缓存区检索到了
System.out.println("get user from cache");
return user;
}else{
try {
System.out.println("else");
user = (User) (jp.proceed(jp.getArgs())); // 如果在缓存区检索不到,执行原方法。
cache.put(user.getId(), user); // 将检索结果存入缓存区
System.out.println("put");
return user;
} catch (Throwable e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}else{
cache.clear(); // 如果是增、删、改方法,清空缓存区
System.out.println("clear");
try {
return jp.proceed(jp.getArgs()); // 执行原方法
} catch (Throwable e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
return null;
}
}
- 测试
package com.ss.service;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import com.ss.dao.IDao;
import com.ss.pojo.User;
public class Service {
@Test
public void test(){
// 初始化容器
ApplicationContext ctx = new ClassPathXmlApplicationContext("container.xml");
// 从容器中获取对象
IDao dao = (IDao)ctx.getBean("dao");
// 调用方法
User user1 = dao.select(1L); //第一次检索
User user2 = dao.select(1L); //第二次检索
System.out.println(user1==user2); //如果对象的地址相同,则说明第二次是从缓存区检索到的
dao.update(user1); //调用增删改方法。更新数据。
User user3 = dao.select(1L); //第三次检索
System.out.println(user2==user3); //如果不相同,则说明缓存已经被清空
}
}
- 控制台输出结果
(o)/~