AOP简介
-
AOP面向切面编程,一种编程范式。
·OOP面向对象编程 -
作用:在不惊动原始代码的情况下对其功能进行加强
AOP核心概念
-
连接点:程序执行过程中的任意位置,粒度为执行方法、抛出异常、设置变量等
·在SpringAOP中,理解为方法的执行 -
切入点:匹配连接点的式子
·在SpringAOP中,一个切入点可以只描述一个具体方法,也可以匹配多个方法 -
通知:在切入点处执行的操作,也就是共性功能
-
通知类:定义通知的类
-
切面:描述通知与切入点的对应关系
AOP入门案例(注解版)
①导入aop相关坐标
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.9.4</version>
</dependency>
pring-context 坐标依赖 spring-aop 坐标
②定义dao接口与实现类
public interface BookDao{
public void save();
public void updte();
}
@Repository
public class BookDaoImpl implements BookDao {
public void save() {
System.out.println(System.currentTimeMillis());
System,out.println("book dao save...");
}
public void update(){
System.out.println("book dao update...")
}
}
③定义通知类
public class MyAdvice{
public void before(){
System.out.println(System.currentTimeMillis());
}
}
④定义切入点
public class MyAdive {
@Poincut("excution (void com.mei.dao.BookDao.update())")
private void pt(){}
}
说明:切入点定义依托一个不具有实际意义的方法进行,即无参数,无返回值,方法无实际逻辑。
⑤绑定切入点与通知关系,并制定通知添加到原始连接点的具体执行位置
public class MyAdvice{
@Pointcut("execution(void com.mei.dao.BookDao.update())")
private void pt(){}
@Before("pt()")
public void before(){
System.out.println(Sysem.currentTimeMillis());
}
}
⑥定义通知类收Spring容器管理,并定义当前类为切面类
@Component
@Aspect
public class MyAdvice{
@Pointcut("execution(void com.mei.dao.BookDao.update())")
private void pt(){}
@Before("pt()")
public void before(){
System.out.println(Sysem.currentTimeMillis());
}
}
⑦开启Spring对AOP注解驱动支持
@Configuration
@ComponentScan("com.mei")
@EnableAspectJAutoProxy
public class SpringConfig{
}
AOP工作流程
1.Spring容器启动
2.读取所有切面配置中的切入点
@Component
@Aspect
public class MyAdvice{
@Pointcut("excution(void com.mei.dao.BookDao.save())")
private void ptx(){}
@Pintcut("excution(void com.mei.dao.BookDao.update())")
private void pt(){}
@Before("pt()"){
System.out.println(System.currentTimeMillis());
}
}
3.初始化bean,判定bean对应的类中方法是否匹配到任意切入点
- 匹配失败,创建对象
- 匹配成功,创建原始对象(目标对象)的代理对象
4.获取bean执行方法
- 获取bean,调用方法并执行,完成操作
- 获取的bean是代理对象时,根据代理对象的运行模式运行原始方法与增强的内容,完成操作
AOP切入点表达式
- 切入点:要增强的方法
- 切入点表达式:要进行增强的方法的描述
package com.mei.dao;
public interface BookDao{
public void update();
}
public class BookDaoImpl implements BookDao{
public void update(){
System.out.println("book dao update...")
}
}
描述方法一:执行com.mei.dao包下的BookDao接口中的无阐述update方法
excution(void com.mei.dao.BookDao.update())
描述方法二:执行com.mei.dao.impl包下的BookDaoImpl类中的无参数update方法
excution(void com.mei.dao.impl.BookDaoImpl.update())
- 切入点表达式:动作关键字(访问修饰符 返回值 包名.类/接口名.方法名(参数)异常名)
- 可以使用通配符描述切入点,快速描述
*:单个独立任意符号,可以独立出现,也可以作为前缀和后缀或者后缀的匹配符出现
excution (public * com.mei.*.UserService.find*(*))
匹配com.mei包下的任意包中的UserService类或接口中所有find开头的带有一个参数的方法
..:多个连续的任意符号,可以独立出现,常用于简化包名与参数的书写
excution (public User com..UserService.findById(..))
匹配com包下的任意包中的UserService类或接口中所有名称为findById的方法
+:专用于匹配子类类型
execution(* *..Service+0.*(..))
@Pointcut("excution(* *..*(..))") //任意包下的任意类或接口下的任意方法或参数返回值任意
AOP通知类型
五种类型:前置通知、后置通知、环绕通知、返回后通知、抛出异常后通知
- 前置通知:@Before,位置在通知方法定义上方,作用为设置当前通知方法与切入点之前的绑定关系,当前通知方法在原始切入点方法前运行
@Before("pt()")
public void before() {
System.out.println("before advice...")
}
- 后置通知:@After,位置在通知方法定义上方,作用为设置当前通知方法与切入点之前的绑定关系,当前通知方法在原始切入点方法后运行
@After("pt()")
public void after() {
System.out.println("after advice...")
}
- 环绕通知:@Around(重点,常用),位置在通知方法定义上方,作用为设置当前通知方法与切入点之前的绑定关系,当前通知方法在原始切入点方法前后运行
@Around("pt()")
public Object around(ProceedingJoinPoint pjp) throws Thorwable {
System.out.println("around before advice...")
Object ret = pjp.proceed();
System.out.println("around after advice..")
return ret;
}
注意:环绕通知必须依赖形参ProceedingJoinPoint才能实现对原始方法的调用,进而实现原始方法调用前后同时添加通知;通知中如果未使用ProceedingJoinPoint对原始方法进行调用将跳过原始方法的执行;对原始方法的调用可以不接受返回值,通知的方法设置成void即可,如果可以接收返回值,必须设定为Object类型;由于无法预知原始方法运行后是否会抛出异常,因此环绕方法必学抛出Throwable对象。
AOP通知获取数据
- 获取切入点方法的参数
·JoinPoint:适用于前置、后置、返回后、抛出异常通知
·ProceedJointPoint: 适用于环绕通知 - 获取切入点方法返回值
·返回后通知
·环绕通知 - 获取切入点方法运行异常信息
·抛出异常后通知
·环绕通知 - JoinPoint描述了连接点方法的运行状态,可以获取到原始方法的调用参数
@Before("pt()")
public void before (JoinPoint jp){
Object[] args = jp.getArgs();
System .out.println(Arrays.to String(args));
}
- ProceedJointPoint是JoinPoint的子类
@Around("pt()")
public Object round(ProceedingJoinPoint pjp) throws Throwable{
Object[] args = pjp.getArgs();
System.out.println(Arrays.to String(args));
Object ret = pjp.proceed();
return ret;
}
- 抛出异常后通知可以获取切入点方法中出现的异常信息,使用形参可以接收对应异常对象
@AfterReturning(value = "pt()",returning = "ret")
public void afterReturning(String ret){
System.out.println("afterReturning advice..."+ret)
}
- 环绕通知中可以手工书写对原始方法的调用,得到的结果即为原始方法返回值
@Around("pt()")
public Object around (ProceedingJoinPoint pjp) throws Throwable{
Object ret = pjp.proceed();
return ret;
}