Spring Aop的简单记录
1、Aop:面向切面编程
项目可以粗略划分为两个模块:核心业务、周边功能。所谓切面,就是将周边功能抽离出来,做相应的处理的一种定义。
切面可以带来的好处是,减少了重复的代码,将核心业务与周边功能解耦。
2、Aop的一些定义
- 连接点:程序执行的具体操作,例如:执行一个方法
- 切入点(Pointcut):在哪些类,哪些方法上切入,一般是一组连接点匹配表达式
- 通知(Advice):在方法执行的时间段(around、before、after、exception、return)做一些增强的功能
- 切面(Aspect):切面 = 切入点 + 通知,通俗点就是:在什么时机,什么地方,做什么增强!
- 织入(Weaving):把切面加入到对象,并创建出代理对象的过程。(由 Spring 来完成)
- Aop代理(aop proxy) : aop框架创建的对象,有两种jdk动态代理、CGLIB代理。
3、Aop织入的时期
编译期:切面在目标类编译时织入,AspectJ就是这种方式
类加载期:切面在目标类加载到JVM时织入
运行期:切面在程序运行的某个时期被织入,一般在切面织入时:AOP容器会对目标对象动态创建一个代理对象。Spring Aop就是采用这种织入方式。
4、Aop的一些注解
@Ascept,定义切面类
@Pointcut("execution(* com.example.springboot.controller.UserController.getVerify(..))"),标注切面的点
Advice的几个分类:
@Before 前置通知,在连接点方法前调用
@Around 环绕通知,它将覆盖原有方法,但是允许你通过反射调用原有方法,
@After 后置通知,在连接点方法后调用
@AfterReturning 返回通知,在连接点方法执行并正常返回后调用,要求连接点方法在执行过程中没有发生异常
@AfterThrowing 异常通知,当连接点方法异常时调用
5、Aop+注解+Redis实现Controller层简单的缓存读取
缺陷的地方:缓存数据与db一致性、缓存读取数据类型转换出错、缓存键id问题
定义注解:
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface CacheTarget {
/**
* 默认键
* @return
*/
String key() default "-1";
/**
* 转换的对象类型
* @return
*/
Class type() default Object.class;
}
定义Aop:
@Slf4j
@Component
@Aspect
public class CacheAop {
@Autowired
private RedisUtil redisUtil;
@Pointcut("@annotation(com.example.springboot.annotation.CacheTarget)")
public void point(){}
@Around(value = "point()")
public Object around(ProceedingJoinPoint joinPoint) throws Throwable {
System.out.println("==around==");
//得到其方法签名
MethodSignature methodSignature = (MethodSignature) joinPoint.getSignature();
CacheTarget cacheTarget = methodSignature.getMethod().getAnnotation(CacheTarget.class);
String key = cacheTarget.key();
//后续可以做缓存中数据类型相关操作
Class type = cacheTarget.type();
//参数名
String[] parameterNames = methodSignature.getParameterNames();
//参数值
Object[] args = joinPoint.getArgs();
//获取id的值
int index = ArrayUtils.indexOf(parameterNames,"id");
int id = (int)args[index];
Object cacheValue = redisUtil.get(key + id);
if(cacheValue != null){
return cacheValue;
}
System.out.println("执行proceed");
//执行方法
Object result = joinPoint.proceed(args);
return result;
}
}
方法上使用该注解:
/**
* 根据id获取用户 + 缓存处理
* @param id
* @return
*/
@CacheTarget(key = "user:", type = User.class)
@GetMapping(value = "/redis/{id}")
public User getUser(@PathVariable int id){
log.info("执行mapper查询db");
User user = userMapper.selectById(id);
String cacheKey = USER_KEY + id;
redisUtil.setEx(cacheKey,user,100);
return user;
}
关于学习到的一些记录与知识总结