AOP即是一种编程范式,指导对类中的方法进行效果增强
比如我们要对类中的某些方法增加一个性能检测方法,这时我们不需要在一个个方法中进行代码添加,我们可以利用AOP技术进行批量的效果增加
下面以一个入门案例为例
增强的功能:在接口方法 执行前打印系统时间
1.导入aop相关坐标
<dependencies> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-context</artifactId> <version>5.2.10.RELEASE</version> </dependency> <dependency> <groupId>org.aspectj</groupId> <artifactId>aspectjweaver</artifactId> <version>1.9.4</version> </dependency> </dependencies>
2.定义dao接口和实现类
package com.itheima.dao.impl;
import com.itheima.dao.BookDao;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Repository;
@Repository
public class BookDaoImpl implements BookDao {
public void save() {
//记录程序当前执行执行(开始时间)
Long startTime = System.currentTimeMillis();
//业务执行万次
for (int i = 0;i<10000;i++) {
System.out.println("book dao save ...");
}
//记录程序当前执行时间(结束时间)
Long endTime = System.currentTimeMillis();
//计算时间差
Long totalTime = endTime-startTime;
//输出信息
System.out.println("执行万次消耗时间:" + totalTime + "ms");
}
public void update(){
System.out.println("book dao update ...");
}
public void delete(){
System.out.println("book dao delete ...");
}
public void select(){
System.out.println("book dao select ...");
}
}
3.定义通知
public Object around(ProceedingJoinPoint pjp) throws Throwable {
System.out.println("------------------------------");
Long startTime = System.currentTimeMillis();
for (int i = 0 ; i<10000 ; i++) {
//调用原始操作
pjp.proceed();
}
Long endTime = System.currentTimeMillis();
Long totalTime = endTime-startTime;
System.out.println("执行万次消耗时间:" + totalTime + "ms");
return null;
}
4.定义切入点和切面来绑定关系
注意要在配置文件中引入下面注解来激活aopp
@Configuration @ComponentScan("com.itheima") @EnableAspectJAutoProxy
package com.itheima.aop;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.stereotype.Component;
@Component
@Aspect//AOP注解,向框架表明使用了aop
public class MyAdvice {
@Pointcut("execution(* com.itheima.dao.BookDao.*d*(..))")
private void pt(){}//切入点
@Around("pt()")//切面注解,表明位置是around,也可以改为before,在切入点之前执行
public Object around(ProceedingJoinPoint pjp) throws Throwable {
System.out.println("------------------------------");
Long startTime = System.currentTimeMillis();
for (int i = 0 ; i<10000 ; i++) {
//调用原始操作
pjp.proceed();
}
Long endTime = System.currentTimeMillis();
Long totalTime = endTime-startTime;
System.out.println("执行万次消耗时间:" + totalTime + "ms");
return null;
}
}
至此,功能增加完成
注意,因为aop工作流程是代理模式,当切面与切入点可以匹配时,开启代理模式,使用的是代理对象进行业务执行。而不匹配时,使用目标对象进行业务执行
AOP切入点表达式
即是匹配被增强的方法的表达式
比如
@Pointcut("execution(* com.itheima.dao.BookDao.*d*(..))")
这里可以使用通配符来进行简化表达式子
AOP通知类型
这里主要讨论AOP通知类型的通知位置---前置,后置,环绕(重点)等
@Around("pt()")//切面注解,表明位置是around,也可以改为before,在切入点之前执行 public Object around(ProceedingJoinPoint pjp) throws Throwable { System.out.println("------------------------------"); Long startTime = System.currentTimeMillis(); for (int i = 0 ; i<10000 ; i++) { //调用原始操作 pjp.proceed(); } Long endTime = System.currentTimeMillis(); Long totalTime = endTime-startTime; System.out.println("执行万次消耗时间:" + totalTime + "ms"); return null; }
pjp.proceed方法就是调用了原方法,这样可以表示环绕的形式
注意:如果原方法有返回值,则必需接收并抛出返回值,object ret=pjp.proceed(),且在方法头中加入throw字段进行抛出
案例:测试接口万次执行效率
主要关注切入点和切面
package com.itheima.aop;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.Signature;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.stereotype.Component;
@Component
@Aspect
public class ProjectAdvice {
//匹配业务层的所有方法
@Pointcut("execution(* com.itheima.service.*Service.*(..))")
private void servicePt(){}
//设置环绕通知,在原始操作的运行前后记录执行时间
@Around("ProjectAdvice.servicePt()")
public void runSpeed(ProceedingJoinPoint pjp) throws Throwable {
//获取执行的签名对象
Signature signature = pjp.getSignature();
String className = signature.getDeclaringTypeName();
String methodName = signature.getName();
long start = System.currentTimeMillis();
for (int i = 0; i < 10000; i++) {
pjp.proceed();
}
long end = System.currentTimeMillis();
System.out.println("万次执行:"+ className+"."+methodName+"---->" +(end-start) + "ms");
}
}