AOP 简略介绍
名称: 面向切面编程
作用: 升高零碎中代码的耦合性, 并且在不扭转原有代码的条件下对原有的办法进行性能的扩大.
公式: AOP = 切入点表达式 + 告诉办法
告诉类型
1.前置告诉 指标办法执行之前执行
2. 后置告诉 指标办法执行之后执行
3. 异样告诉 指标办法执行过程中抛出异样时执行
4. 最终告诉 无论什么时候都要执行的告诉
特点: 上述的四大告诉类型 不能干涉指标办法是否执行.个别用来做程序运行状态的记录.【监控】
5.盘绕告诉 在指标办法执行前后都要执行的告诉办法 该办法能够控制目标办法是否运行.joinPoint.proceed(); 性能作为弱小的.
切入点表达式
切入点表达式就是一个程序是否进入告诉的一个判断 (IF)。
作用:当程序运行过程中,满足了切入点表达式时才回去执行告诉办法,实现业务的扩大。
品种(写法):
1.bean(bean 的名称 bean 的 ID) 只能拦挡具体的某个 bean 对象,只能匹配一个对象。
例如:bean(“itemServiceImpl”)
2.within(包名. 类名) within(“com.jt.service.*”)示意能够匹配多个对象。
3.execution(返回值类型 包名. 类名. 办法名 (参数列表)) 最为弱小的用法
例如 : execution( com.jt.service...*(..))返回值类型任意 com.jt.service 包下的所有的类的所有的办法都会被拦挡.
4.@annotation(包名. 注解名称) 依照注解匹配.
AOP 入门案例
package com.jt.aop;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.springframework.stereotype.Component;
@Aspect // 我是一个 AOP 切面类
@Component // 将类交给 spring 容器治理
public class CacheAOP {
// 公式:切入点表达式 + 告诉办法
/**
* 对于切入点表达式的应用阐明
* 粗粒度
* 1.bean(bean 的 id)一个类
* 2.within(包名. 类名)多个类
* 细粒度
* 1.
*/
//@Pointcut("bean(itemCatSercviceImpl)")
// .* 示意以后包上面的一级目录。// ..* 示意以后包上面的多级目录
/*@Pointcut("within(com.jt.service..*)")
public void pointCut(){// 定义切入点表达式,为了占位}*/
// 定义前置告诉,与切入点进行绑定 @Before("pointCut()")
// 或者用更简略的办法:不必定义切入点办法 pointCut(),// 间接绑定 bean:@before(“bean(itemCatSercviceImpl)”)/* 两者的区别:* 1.@Before("pointCut()")示意切入点表达式的援用,实用于多个告诉同用切入点的状况
* 2.@before(“bean(itemCatSercviceImpl)”)实用于单个告诉,不须要复用的
* */
/**@Before("pointCut()")
public void before(){System.out.println("我是前置告诉");
}
@AfterReturning("pointCut()")
public void afterreturn(){System.out.println("我是后置告诉");
}*/
@Before("pointCut()")
public void before(JoinPoint joinPoint){
//JoinPoint:连接点办法
//getStaticPart()代表连接点的办法签名
//getName()办法的名称
String methodName = joinPoint.getSignature().getName();
//getDeclaringTypeName()获取类的门路全名 com.jt.service.ItemCatSercviceImpl
String className = joinPoint.getSignature().getDeclaringTypeName();
System.out.println(className+"."+methodName);
//getTarget()代表指标对象
//getTarget().getClass()获取指标对象的类型
Class<?> targetClass = joinPoint.getTarget().getClass();
// 获取对象的参数
Object[] args = joinPoint.getArgs();
// 获取执行工夫
Long startTime= System.currentTimeMillis();
System.out.println("指标办法类型"+targetClass);
System.out.println("执行工夫:"+startTime+"毫秒");
}
/**
* 盘绕告诉阐明
* 注意事项:* ProceedingJoinPoint 只能用在盘绕告诉里
* 1. 盘绕告诉必须增加参数 ProceedingJoinPoint
* 2.ProceedingJoinPoint 只能盘绕告诉应用
* 3.ProceedingJoinPoint 如果当做参数则必须位于参数的第一位
*/
@Around("pointCut()")
public Object around(ProceedingJoinPoint joinPoint){System.out.println("盘绕告诉开始!!!");
Object result=null;
try {result=joinPoint.proceed();// 执行下一个告诉或者指标办法
} catch (Throwable throwable) {throwable.printStackTrace();
}
System.out.println("盘绕告诉完结!!!");
return result;
}
}
对于 AOP 实现 Redis
自定义缓存注解
问题:如何管制,哪些办法须要应用缓存?【cacheFind()查询方法】
解决方案:采纳自定义注解的模式进行定义,如果办法执行须要应用缓存,则标识注解即可。
对于注解的阐明:
1. 注解名称:cacheFind
2. 属性参数:
1.key: 应该由用户本人手动增加,个别增加业务名称之后动静拼接造成惟一的 key。2.seconds:用户能够指定数据的超时工夫。
// 自定义一个注解
@Target(ElementType.METHOD)// 该注解只对办法无效
@Retention(RetentionPolicy.RUNTIME)// 运行期无效
public @interface CacheFind {String preKey();// 用户标识 key 的前缀。int seconds() default 0;// 如果用户不写示意不须要超时,如果写了就以用户为准。}
编辑 CacheAOP
package com.jt.aop;
import com.jt.anno.CacheFind;
import com.jt.util.ObjectMapperUtil;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
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;
@Aspect // 我是一个 AOP 切面类
@Component // 将类交给 spring 容器治理
public class CacheAOP {
@Autowired
private Jedis jedis;
/** AOP 实现 Redis 缓存
* 切面 = 切入点 + 告诉办法
* 注解相干 + 盘绕告诉 控制目标办法是否执行
*
* 难点:* 1. 获取注解的对象, 将注解作为参数传过来
* 2. 动静生成 key prekey+ 用户参数数组
* 3. 如何获取办法的返回值类型
*/
@Around("@annotation(cacheFind)")
public Object around(ProceedingJoinPoint joinPoint,CacheFind cacheFind){System.out.println("注解拦挡失效");
Object result=null;
try {
// 拼接 redis 存储数据的 key
Object[] args=joinPoint.getArgs();
String key=cacheFind.preKey()+"::"+ Arrays.toString(args);
// 查问 redis 之后判断是否有数据
if(jedis.exists(key)){
// 示意 redis 中有数据,无需执行指标办法
String json = jedis.get(key);
// 动静获取办法的返回值类型 向上转型 向下转型
MethodSignature methodSignature=(MethodSignature)joinPoint
.getSignature();
// 示意客户端传来什么类型就返回什么类型
Class returnType = methodSignature.getReturnType();
// 须要将 json 串转化为对象
result=ObjectMapperUtil.toObj(json, returnType);
System.out.println("redis 缓存中的数据");
}else{
// 示意数据不存在,须要在数据库中查问
result=joinPoint.proceed();// 执行指标办法和告诉
// 将查问的数据存储到 redis 缓存中
String json = ObjectMapperUtil.toJson(result);
// 判断是否须要超时工夫
if(cacheFind.seconds()>0){jedis.setex(key, cacheFind.seconds(), json);
}else{jedis.set(key, json);
}
System.out.println("aop 执行指标办法查询数据库");
}
} catch (Throwable throwable) {throwable.printStackTrace();
}
return result;
}
}