AOP(面向切面编程)
百度百科:面向切面编程,通过预编译方式和运行期间动态代理实现程序功能的统一维护的一种技术。AOP是OOP的延续,是软件开发中的一个热点,也是Spring框架中的一个重要内容,是函数式编程的一种衍生范型。利用AOP可以对业务逻辑的各个部分进行隔离,从而使得业务逻辑各部分之间的耦合度降低,提高程序的可重用性,同时提高了开发的效率。
一句话概括:在不改变原有代码的情况下,给程序添加额外的通用功能。(遵循OCP开闭原则)
1、核心
1、join point(连接点):是程序执行中的一个精确执行点,例如类中的一个方法。它是一个抽象的概念,在实现AOP时,并不需要去定义一个join point。
2、point cut(切入点):本质上是一个捕获连接点的结构。在AOP中,可以定义一个point cut,来捕获相关方法的调用。
3、advice(通知):是point cut的执行代码,是执行“方面”的具体逻辑。
4、aspect(方面):point cut和advice结合起来就是aspect,它类似于OOP中定义的一个类,但它代表的更多是对象间横向的关系。
5、introduce(引入):为对象引入附加的方法或属性,从而达到修改对象结构的目的。有的AOP工具又将其称为mixin。
2、公式
*AOP=切入点表达式+通知方法
3、类型
3.1、切入点PointCut
bean(bean的id )类名首字母小写 (粗粒度,类中的所有方法)
within(包名.类名)按包路径匹配 (多个类,粗粒度)
execution(返回值类型 包.类.方法(参数列表))
annotation(包名.注解名)
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.stereotype.Component;
@Component //将对象交给spring容器管理
@Aspect //标识我是一个切面
public class CacheAOP {
/**
* AOP = 切入点表达式 + 通知方法.
*
* 拦截需求:
* 1.要求拦截itemCatServiceImpl的bean
* 2.拦截com.jt.service下的所有的类
* 3.拦截com.jt.service下的所有类及方法
* 3.1拦截com.jt.service的所有的类.返回值为int类型的.并且add开头
* 的方法.并且参数一个 为String类型
*/
//@Pointcut(value = "bean(itemCatServiceImpl)")
//@Pointcut("within(com.jt.service..*)")
//拦截com.jt.service下的所有类的所有方法的任意参数类型
//@Pointcut("execution(int com.jt.service..*.add*(String))")
@Pointcut("execution(* com.jt.service..*.*(..))")
public void pointcut(){
}
//定义前置通知
@Before("pointcut()")
public void before(){
System.out.println("我是前置通知");
}
}
3.2、 通知(Advice)
1.前置通知: 目标方法执行之前执行 @Before
2.后置通知: 目标方法执行之后执行 @After
3.异常通知: 目标方法执行之后抛出异常时执行 @After-throwing
4.最终通知: 不管什么时候都要执行的方法. @After-Returning
说明:上述的四大通知类型不能控制目标方法是否执行.一般使用上述的四大通知类型,都是用来记录程序的执行状态.
5.环绕通知: 在目标方法执行前后都要执行的通知方法. 控制目标方法是否执行.并且环绕通知的功能最为强大. @Around
通知执行顺序
3.3、 连接点(JoinPoint)
1、Proceeding JoinPoint joinPoint这个只能用在环绕通知中,且放到通知方法参数列表的第一个位置。
eg:public Object around(ProceedingJoinPoint joinPoint, CacheFind cacheFind){}
2、JoinPoint joinPoint
/**
* 要求: 拦截注解方法
* 打印:
* 1.打印目标对象的类型
* 2.打印方法的参数
* 3.获取目标对象的名称及方法的名称
* @param joinPoint
*/
@Before("@annotation(com.jt.anno.CacheFind)")
public void before(JoinPoint joinPoint){
Object target = joinPoint.getTarget(); //获取目标对象
Object[] args = joinPoint.getArgs(); //获取方法参数的
String targetName =
joinPoint.getSignature().getDeclaringTypeName(); //获取目标对象的名称
//获取目标对象的类型
Class targetClass = joinPoint.getSignature().getDeclaringType();
//获取目标方法的名称
String methodName = joinPoint.getSignature().getName();
System.out.println(target);
System.out.println(args);
System.out.println(targetName);
System.out.println(targetClass);
System.out.println(methodName);
}
运行结果:
com.jt.service.ItemCatServiceImpl@2fa4313
[Ljava.lang.Object;@294e492f
com.jt.service.ItemCatServiceImpl
class com.jt.service.ItemCatServiceImpl
findItemCatList
4、Redis实现缓存过程
4.1自定义一个注解
@Retention(RetentionPolicy.RUNTIME) //该注解什么时候有效
@Target({ElementType.METHOD}) //对方法有效
public @interface CacheFind {
String key(); //该属性为必须添加
int seconds() default 0; //设定超时时间,默认不超时
}
4.2生成一个类标示为切面并交给spring容器管理(@Component)
@Component //将对象交给spring容器管理
@Aspect //标识这个类是一个切面
public class CacheAop {
//1.注入缓存redis对象
@Autowired
private Jedis jedis;
/**
-
拦截@CacheFind注解标识的方法
-
通知选择:缓存的实现应该选用环绕通知(有了缓存就不执行目标方法)
-
步骤:
-
1.动态生成key 用户填写的key+用户提交的参数
*/
@Around("@annotation(cacheFind)")
public Object around(ProceedingJoinPoint joinPoint, CacheFind cacheFind){//1.如何获取用户在注解中填写的内容呢??? String key=cacheFind.key(); //前缀 //2.如何获取目标对象的参数呢??? Object[] array=joinPoint.getArgs(); key+="::"+Arrays.toString(array); //"ITEM_CAT_PARENTID::[0]" //3.从redis中获取数据 Object result=null; if(jedis.exists(key)){ //需要获取json数据之后,直接转化为对象返回 String json=jedis.get(key); //如何获取返回值类型(通过注解标识的目标方法找到返回值类型) MethodSignature methodSignature= (MethodSignature) joinPoint.getSignature(); Class targetClass=methodSignature.getReturnType(); result=ObjectMapperUtil.toObject(json, targetClass); System.out.println("AOP实现缓存的查询!!!"); }else{ //key不存在,查询数据库 try { result=joinPoint.proceed(); //执行目标方法,获取返回值结果 String json= ObjectMapperUtil.getJson(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(); throw new RuntimeException(throwable); } } return result;
}
4.3在需要添加缓存的业务方法上添加该注解
@CacheFind(key=“ITEM_CAT_PARENTID”,seconds = 100)
设置唯一标识key和超时时间(时间可不设置,默认不超时)