AOP:
面向切面编程,通过预编译和运行时动态生成的代理类,对一类代码进行增强的操作
核心:
在不影响原有的代码的基础上,对方法进行扩展,减少代码的耦合性
切面 = 切入点表达式 + 通知
1.目标方法:
用户调用实际的业务代码
2.通知
:在切面中扩展目标方法的方法
3.切入点表达式:
相当于是一个if判断 当用户执行目标方法时,如果满足了切入点表达式,则执行通知方法
4.连接点:
当方法满足切入点表达式时,该方法称之为连接点
AOP切入点表达式
1.bean(bean的ID) 按照bean 的id匹配 某一个
2.within(包名.类名) 按类匹配 多个
上述2项都是粗粒度.
3.execution(返回值类型 包名.类名.方法名(参数列表))
方法参数级别: 粒度较细
4.@annotation(注解名称/注解的路径) 按照注解匹配.
通知总类
1).前置通知 在目标方法执行之前执行
2).后置通知 在目标方法执行之后执行
3).异常通知 在目标方法执行之后报异常的
4).最终通知 不管什么时候都要执行的通知
5).环绕通知 在目标方法前后都要执行的通知
总结:
1.前4大通知类型一般用于程序状态的记录.
2.环绕通知是最为强大的通知方法,可以控制目标方法的执行.可以改变程序的流转的轨迹. 一般用于业务操作.
@Component //将AOP 交给spring容器管理
@Aspect //标识该类为AOP切面
public class TestAOP {
//AOP 切面 = 切入点表达式 + 通知方法
//1.bean 2.within 3.execution(返回值类型 包名.类名.方法名(参数列表))
//@Pointcut("bean(itemCatServiceImpl)")
//@Pointcut("within(com.jt.service.ItemCatServiceImpl)")
@Pointcut("execution(* com.jt..*.*(..))")
public void pointCut() {
}
//1.定义前置通知 获取参数数据
@Before("pointCut()")
public void before(JoinPoint joinPoint) {
String className =
joinPoint.getSignature().getDeclaringTypeName();
String methodName = joinPoint.getSignature().getName();
Object[] objs = joinPoint.getArgs(); //获取目标方法的参数
System.out.println(className);
System.out.println(methodName);
System.out.println(objs);
}
}
AOP缓存操作
1.AOP缓存实现策略
1).自定义缓存注解 @Cache_Find 查询注解
2).自定义属性
@Target(ElementType.METHOD) //该注解对方法有效
@Retention(RetentionPolicy.RUNTIME) //运行期有效
public @interface CacheFind {
//要求用户指定key的前缀,之后动态拼接key
public String perKey();
//如果用户有超时时间则定义超时用法
public int seconds() default 0;
}
实现缓存AOP
实现缓存AOP
@Aspect
@Component
public class CacheAOP {
@Autowired
private Jedis jedis;
/**
* 规定:ProceedingJoinPoint 参数必须位于第一位
* 1.利用切入点表达式拦截@CacheFind注解,同时获取CacheFind注解对象
* 2.自定义key
* 3.根据key查询缓存.
* 有数据: 则返回对象
* 没有数据: 则查询数据库.之后将返回结果 保存到redis中
*
*
* 反射相关:
*
* Method method =
joinPoint.getTarget() //获取目标对象
.getClass() //目标对象的class
.getMethod("findItemCatList", Long.class); //方法
//从方法对象中获取返回值类型
Class returnClass = method.getReturnType();
*
*
*/
@SuppressWarnings("unchecked")
//@Around("@annotation(com.jt.anno.CacheFind)")
@Around("@annotation(cacheFind)")
public Object around(ProceedingJoinPoint joinPoint,CacheFind cacheFind) {
Object result = null;
try {
Object[] args = joinPoint.getArgs();
//动态拼接key
String key = cacheFind.perKey()+"::"+Arrays.toString(args);
//根据key查询redis
if(!jedis.exists(key)) {
//如果key不存在,则执行目标方法
result = joinPoint.proceed();
System.out.println("AOP查询数据库!!!!!!");
//将返回值结果保存到redis中
String json = ObjectMapperUtil.toJSON(result);
int seconds = cacheFind.seconds();
if(seconds>0)
//表示用户需要设定超时时间
jedis.setex(key, seconds, json);
else
//用户不需要超时时间
jedis.set(key, json);
}else {
//如果redis中有缓存,则将json串转化为对象之后返回
String json = jedis.get(key);
//如何才能动态的获取方法的返回值类型????? 必须获取方法对象
MethodSignature methodSignature = (MethodSignature) joinPoint.getSignature(); //获取目标对象的方法
result =
ObjectMapperUtil.toObj(json,methodSignature.getReturnType());
System.out.println("AOP缓存查询!!!!!!!");
}
} catch (Throwable e) {
e.printStackTrace();
}
return result;
}
}