AOP总结笔记
Posted summer
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了AOP总结笔记相关的知识,希望对你有一定的参考价值。
为什么使用AOP?
问题1: 将缓存的代码直接写到业务中,只对某个业务有效,如果其他业务也需要缓存,则需要重复编辑…
问题2: 如果将缓存代码直接写死在业务层,如果后期代码需要更新时,代码的耦合性高. 不便于扩展…
解决方案: 使用AOP方式实现…
名称: 面向切面编程.
核心作用: 在不改变原有代码的条件下 对方法进行功能扩展.
AOP构造要素 = 切入点表达式 + 通知方法.
spring中的两大核心机制:
1:控制反转(IOC)+依赖注入(DI)
控制反转:将对象创建的权力交给容器,由容器控制对象的生命周期
注入方式一: 通过bean的id进行注入。
bean:被spring容器管理的对象称之为bean。
bean的id为类名首小字母小写。(@Autowired+@Qualifier(“itemCatServiceImpl”))
使用注解@Qualifier
注入方式二: 通过类型注入。 spring规定一般的接口都是单实现
2:AOP
常见切入点表达式
名称解释: 被Spring容器管理的对象称之为bean.
1). bean(“bean的Id”) 一般类名首字母小写 按照某个类匹配
2). within(“包名.类名”) 按照指定的类目录(多个类) 上述2个操作粒度较粗 按类匹配
3). execution(返回值类型 包名.类名.方法名(参数列表…)) 控制方法参数级别 粒度较细
4). annotation(“包名.注解名”) 按照特定的注解进行拦截. 主流语法
通知方法
1)@Before 目标方法执行前执行
2)@AfterReturning目标方法执行之后执行
3)@AfterThrowing 目标方法抛出异常时执行
4)@After 程序最后执行
上述四大通知类型 一般用来记录程序的执行的状态, 为监控系统提供数据的支持的 . 只能监控不能处理.
5)@Around 目标方法执行前后都要执行
环绕通知是5大通知中功能最为强大的. 可以控制目标方法是否执行.(控制业务是否操作).
@Component //交给Spring容器管理
@Aspect //标识AOP切面类
public class RedisAOP {
//1.定义切入点表达式 粗粒度(类) 细粒度(方法)
@Pointcut("bean(itemCatServiceImpl)")
public void pointCut(){
}
//joinPoint连接点
@Before("pointCut()")
public void before(JoinPoint joinPoint){
Class targetClass = joinPoint.getTarget().getClass();
Object[] args = joinPoint.getArgs();
String methodName = joinPoint.getSignature().getName();
String ClassName = joinPoint.getSignature().getDeclaringTypeName();
System.out.println("获取目标对象的类型:"+targetClass);
System.out.println("获取目标参数:"+ Arrays.toString(args));
System.out.println("获取目标方法名称:"+ methodName);
System.out.println("获取目标类的路径:"+ ClassName);
System.out.println("我是前置通知!!!");
}
}
JoinPoint 用在非环绕通知中
ProceedingJoinPoint 只能用在环绕通知(@Around)中
ProceedingJoinPoint extends JoinPoint
ProceedingJoinPoint 有proceed()方法,控制目标方法或其他通知方法的执行。
package com.wxz.anno;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface CacheFind {
String key(); //没有设置默认值,使用该注解时,该属性必须添加值
int seconds() default 0;//设定超时时间,默认不超时
}
package com.wxz.aop;
import com.jt.annotation.CacheFind;
import com.jt.util.ObjectMapperUtil;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
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.aspectj.lang.reflect.MethodSignature;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import redis.clients.jedis.Jedis;
import java.util.Arrays;
@Component //交给Spring容器管理
@Aspect //标识AOP切面类
public class RedisAOP {
@Autowired
private Jedis jedis;
//1. 定义切入点表达式 2.定义通知方法
/**
* 实现AOP缓存:
* 1.准备key = 获取key的前缀 + "动态拼接参数"
* 2.从redis中获取数据
* 结果1: 没有数据,查询数据库,之后将数据保存到缓存中
* 结果2: 有数据, 直接将缓存数据返回
* @param joinPoint
* @return
* @throws Throwable
*/
@Around("@annotation(cacheFind)")
public Object around(ProceedingJoinPoint joinPoint, CacheFind cacheFind) throws Throwable {
String perkey = cacheFind.key();
String args = Arrays.toString(joinPoint.getArgs());
String key = perkey + "::" + args;
Object result = null;
//2.判断redis中是否有数据
if(jedis.exists(key)){
String json = jedis.get(key);
//利用工具API动态获取返回值类型
MethodSignature methodSignature = (MethodSignature) joinPoint.getSignature();
Class returnType = methodSignature.getReturnType();
result = ObjectMapperUtil.toObj(json,returnType);
System.out.println("查询Redis缓存!!!");
}else{
//表示缓存中没有数据,应该查询数据库动态获取
result = joinPoint.proceed(); //调用下一个通知/目标方法
//应该将数据保存到缓存中
String json = ObjectMapperUtil.toJSON(result);
if(cacheFind.seconds()>0){
jedis.setex(key, cacheFind.seconds(), json);
}else{
jedis.set(key,json);
}
System.out.println("AOP查询数据库!!!");
}
return result;
}
说明:
SpringMVC中,在环绕通知方法中,加入新的参数(注解对象)后可以很方便的拿到注解的属性值。且@annotation中的注解全限定名可写成参数中的对象名。
这个参数的位置一定要在ProceedingJoinPoint 之后(ProceedingJoinPoint 在参数列表中的第一个位置)。
以上是关于AOP总结笔记的主要内容,如果未能解决你的问题,请参考以下文章