AOP核心概念
(Aspect Oriented Programming)面向切面编程,一种编程的范式,指导开发者如何组织程序结构作用:在不惊动原始设计的基础上,为其进行功能增强(代理模式可以做增强)
1. 连接点:可以进行切入的方法,在SpringAOP中理解为方法的执行
2. 切入点:即为被切入的方法(表示为需要使用Aop包下共用功能的方法)
在SpringAOP中,一个切入点可以描述一个具体的方法,也可匹配多个方法
*一个具体的方法:某个包下的无形参,无返回值的save方法
*匹配多个方法:所有的save方法,所有的get开头的方法,以Dao结尾的任意方法
3. 通知:在切入点处执行的操作,也就是共性方法
4. 通知类
5. 切面:在通知和切面之间进行绑定的关系
案例
示例:pandas 是基于NumPy 的一种工具,该工具是为了解决数据分析任务而创建的。
1.pom.xml
<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.定义通知类,制作通知
绑定切入点与通知关系,并指定通知添加到连接点的位置:靠方法名pt()绑定
//通知类必须配置成Spring管理的bean
@Component
//设置当前类为切面类类
@Aspect
public class MyAdvice {
//设置切入点,要求配置在方法上方
@Pointcut("execution(void com.itheima.dao.BookDao.update())")
private void pt(){}
@Before("pt()")
public void method(){
System.out.println(System.currentTimeMillis());
}
}
3.SpringConfig
开启注解开发AOP功能
@EnableAspectJAutoProxy
@Configuration
@ComponentScan("com.itheima")
//开启注解开发AOP功能
@EnableAspectJAutoProxy
public class SpringConfig {
}
AOP工作流程
-
- Spring容器启动
-
- 读取所有切面配置中的切入点
-
- 初始化bean,判定bean对应的类中的方法是否匹配到切入点
(1)匹配失败(创建对象)
(2)匹配成功,创建原始对象(目标对象)的代理对象
- 初始化bean,判定bean对应的类中的方法是否匹配到切入点
-
- 获取bean执行方法
(1)获取bean,调用方法执行,完成操作
(2)获取bean的是代理对象时,根据代理对象的运行模式运行原始方法与增强的内容,完成操作
- 获取bean执行方法
AOP切入点表达式
execution(void com.itheima.dao.BookDao.update())
execution(void com.itheima.dao.impl.BookDaoImpl.update())
- 动作关键字:execution
- 返回类型
- 包名
- 类/接口名
- 方法
可以使用通配符描述切入点
书写技巧
AOP通知类型
- 前置通知
- 后置通知
- 环绕通知
@Around("pt2()")
//会报错
//原始方法返回类型要与该代理方法一致
/* public void aroundSelect(ProceedingJoinPoint pjp) throws Throwable {
System.out.println("around before advice ...");
//表示对原始操作的调用
pjp.proceed();
System.out.println("around after advice ...");
}*/
public Object aroundSelect(ProceedingJoinPoint pjp) throws Throwable {
System.out.println("around before advice ...");
//表示对原始操作的调用
Integer ret = (Integer) pjp.proceed();
System.out.println("around after advice ...");
return ret;
}
- 返回后通知
- 抛出异常后通知
AOP通知获取数据
@Component
@Aspect
public class MyAdvice {
@Pointcut("execution(* com.itheima.dao.BookDao.findName(..))")
private void pt(){}
//JoinPoint:用于描述切入点的对象,必须配置成通知方法中的第一个参数,可用于获取原始方法调用的参数
//@Before("pt()")
public void before(JoinPoint jp) {
Object[] args = jp.getArgs();
System.out.println(Arrays.toString(args));
System.out.println("before advice ..." );
}
// @After("pt()")
public void after(JoinPoint jp) {
Object[] args = jp.getArgs();
System.out.println(Arrays.toString(args));
System.out.println("after advice ...");
}
//ProceedingJoinPoint:专用于环绕通知,是JoinPoint子类,可以实现对原始方法的调用
//@Around("pt()")
public Object around(ProceedingJoinPoint pjp) {
Object[] args = pjp.getArgs();
System.out.println(Arrays.toString(args));
//可以在这个地方对参数进行处理
args[0] = 666;
Object ret = null;
try {
ret = pjp.proceed(args);
} catch (Throwable t) {
t.printStackTrace();
}
return ret;
}
//设置返回后通知获取原始方法的返回值,要求returning属性值必须与方法形参名相同
@AfterReturning(value = "pt()",returning = "ret")
public void afterReturning(JoinPoint jp,String ret) {
System.out.println("afterReturning advice ..."+ret);
}
//设置抛出异常后通知获取原始方法运行时抛出的异常对象,要求throwing属性值必须与方法形参名相同
// @AfterThrowing(value = "pt()",throwing = "t")
public void afterThrowing(Throwable t) {
System.out.println("afterThrowing advice ..."+t);
}
}