AOP本质:在不改变原有业务逻辑的情况下增强横切逻辑,横切逻辑代码往往是权限校验代码、⽇志代 码、事务控制代码、性能监控代码。
在讲解AOP术语之前,我们先来看⼀下下⾯这两张图,它们就是第三部分案例需求的扩展(针对这些扩 展的需求,我们只进⾏分析,在此基础上去进⼀步回顾AOP,不进⾏实现)
上图描述的就是未采⽤AOP思想设计的程序,当我们红⾊框中圈定的⽅法时,会带来⼤量的重复劳动。 程序中充斥着⼤量的重复代码,使我们程序的独⽴性很差。⽽下图中是采⽤了AOP思想设计的程序,它 把红框部分的代码抽取出来的同时,运⽤动态代理技术,在运⾏期对需要使⽤的业务逻辑⽅法进⾏增强。
它指的是那些 可以 ⽤于把增强代码加⼊到业务主线中的点,那么由上图中我们可 以看出,这些点指的就是⽅法。在⽅法执⾏的前后通过动态代理技术加⼊增强的 代码。在Spring框架AOP思想的技术实现中,也只⽀持⽅法类型的连接点。
Pointcut( 切
⼊点 )
它指的是那些已经把增强代码加⼊到业务主线进来之后的连接点。由上图中,我 们看出表现层 transfer⽅法就只是连接点,因为判断访问权限的功能并没有对其增强。
Advice( 通 知 / 增强 )
它指的是切⾯类中⽤于提供增强功能的⽅法。并且不同的⽅法增强的时机是不⼀ 样的。⽐如,开启事务肯定要在业务⽅法执⾏之前执⾏;提交事务要在业务⽅法 正常执⾏之后执⾏,⽽回滚事务要在业务⽅法执⾏产⽣异常之后执⾏等等。那么 这些就是通知的类型。其分类有: 前置通知 后置通知 异常通知 最终通知 环绕通 知 。
Target( ⽬标 对象 )
它指的是代理的⽬标对象。即被代理对象。
它指的是⼀个类被AOP织⼊增强后,产⽣的代理类。即代理对象。
Weaving( 织
⼊ )
它指的是把增强应⽤到⽬标对象来创建新的代理对象的过程。spring采⽤动态代 理织⼊,⽽AspectJ采⽤编译期织⼊和类装载期织⼊。
Aspect( 切
⾯ )
它指定是增强的代码所关注的⽅⾯,把这些相关的增强代码定义到⼀个类中,这 个类就是切⾯类。例如,事务切⾯,它⾥⾯定义的⽅法就是和事务相关的,像开 启事务,提交事务,回滚事务等等,不会定义其他与事务⽆关的⽅法。我们前⾯ 的案例中 TrasnactionManager就是⼀个切⾯。
连接点:⽅法开始时、结束时、正常运⾏完毕时、⽅法异常时等这些特殊的时机点,我们称之为连接 点,项⽬中每个⽅法都有连接点,连接点是⼀种候选点
切⼊点:指定AOP思想想要影响的具体⽅法是哪些,描述感兴趣的⽅法
Advice增强:
第⼀个层次:指的是横切逻辑
第⼆个层次:⽅位点(在某⼀些连接点上加⼊横切逻辑,那么这些连接点就叫做⽅位点,描述的是具体 的特殊时机)
Aspect切⾯:切⾯概念是对上述概念的⼀个综合Aspect切⾯= 切⼊点+增强
= 切⼊点(锁定⽅法) + ⽅位点(锁定⽅法中的特殊时机)+ 横切逻辑
第 2 节 Spring 中 AOP 的代理选择
Spring 实现AOP思想使⽤的是动态代理技术
默认情况下,Spring会根据被代理对象是否实现接⼝来选择使⽤JDK还是CGLIB。当被代理对象没有实现任何接⼝时,Spring会选择CGLIB。当被代理对象实现了接⼝,Spring会选择JDK官⽅的代理技术,不过我们可以通过配置的⽅式,让Spring强制使⽤CGLIB。
在Spring的AOP配置中,也和IoC配置⼀样,⽀持3类配置⽅式。第⼀类:使⽤XML配置
第⼆类:使⽤XML+注解组合配置
第三类:使⽤纯注解配置
需求:横切逻辑代码是打印⽇志,希望把打印⽇志的逻辑织⼊到⽬标⽅法的特定位置(service层transfer
⽅法)
Spring是模块化开发的框架,使⽤aop就引⼊aop的jar
坐标
AOP 核⼼配置
细节
关于切⼊点表达式
上述配置实现了对 TransferServiceImpl的 updateAccountByCardNo⽅法进⾏增强,在其执⾏之前,输出了记录⽇志的语句。这⾥⾯,我们接触了⼀个⽐较陌⽣的名称: 切⼊点表达 式 ,它是做什么的呢?我们往下看。
概念及作⽤
切⼊点表达式,也称之为AspectJ切⼊点表达式, 指的是遵循特定语法结构的字符串,其 作⽤是⽤于对符合语法格式的连接点进⾏增强。 它是AspectJ表达式的⼀部分。
关于AspectJ
AspectJ是⼀个基于Java语⾔的AOP框架,Spring框架从2.0版本之后集成了AspectJ框架中切⼊点表达式的部分,开始⽀持AspectJ切⼊点表达式。
切⼊点表达式使⽤示例
改变代理⽅式的配置
在前⾯我们已经说了,Spring在选择创建代理对象时,会根据被代理对象的实际情况来选择 的。被代理对象实现了接⼝,则采⽤基于接⼝的动态代理。当被代理对象没有实现任何接⼝ 的时候,Spring会⾃动切换到基于⼦类的动态代理⽅式。
但是我们都知道,⽆论被代理对象是否实现接⼝,只要不是final修饰的类都可以采⽤cglib提 供的⽅式创建代理对象。所以Spring也考虑到了这个情况,提供了配置的⽅式实现强制使⽤ 基于⼦类的动态代理(即cglib的⽅式),配置的⽅式有两种
使⽤aop:config标签配置
使⽤aop:aspectj-autoproxy标签配置
五种通知类型
前置通知
前置通知永远都会在切⼊点⽅法(业务核⼼⽅法)执⾏之前执⾏。
前置通知可以获取切⼊点⽅法的参数,并对其进⾏增强。
正常执⾏时通知
异常通知
异常通知的执⾏时机是在切⼊点⽅法(业务核⼼⽅法)执⾏产⽣异常之后,异常通知执⾏。如果切
⼊点⽅法执⾏没有产⽣异常,则异常通知不会执⾏。
异常通知不仅可以获取切⼊点⽅法执⾏的参数,也可以获取切⼊点⽅法执⾏产⽣的异常信息。
最终通知
最终通知的执⾏时机是在切⼊点⽅法(业务核⼼⽅法)执⾏完成之后,切⼊点⽅法返回之前执⾏。 换句话说,⽆论切⼊点⽅法执⾏是否产⽣异常,它都会在返回之前执⾏。
最终通知执⾏时,可以获取到通知⽅法的参数。同时它可以做⼀些清理操作。
环绕通知
4.2 XML+ 注解模式
XML 中开启 Spring 对注解 AOP 的⽀持
示例
*第⼀步:编写⼀个⽅法
*第⼆步:在⽅法使⽤@Pointcut注解
*第三步:给注解的value属性提供切⼊点表达式
*细节:
*1.在引⽤切⼊点表达式时,必须是⽅法名+(),例如"pointcut()"。
*2.在当前切⾯中使⽤,可以直接写⽅法名。在其他切⾯中使⽤必须是全限定⽅法名。
*/
@Pointcut("execution(* com.lagou.service.impl.*.*(..))") public void pointcut(){}
@Before("pointcut()")
public void beforePrintLog(JoinPoint jp){ Object[] args = jp.getArgs();
System.out.println("前置通知:beforePrintLog,参数是:"+
Arrays.toString(args));
}
@AfterReturning(value = "pointcut()",returning = "rtValue") public void afterReturningPrintLog(Object rtValue){
System.out.println("后置通知:afterReturningPrintLog,返回值
是:"+rtValue);
}
@AfterThrowing(value = "pointcut()",throwing = "e") public void afterThrowingPrintLog(Throwable e){
System.out.println("异常通知:afterThrowingPrintLog,异常是:"+e);
}
@After("pointcut()")
public void afterPrintLog(){ System.out.println("最终通知:afterPrintLog");
}
/**
*环绕通知
*@parampjp
*@return
*/ @Around("pointcut()")
public Object aroundPrintLog(ProceedingJoinPoint pjp){
//定义返回值
Object rtValue = null; try{
//前置通知
System.out.println("前置通知");
//1.获取参数
Object[] args = pjp.getArgs();
在使⽤注解驱动开发aop时,我们要明确的就是,是注解替换掉配置⽂件中的下⾯这⾏配置:
在配置类中使⽤如下注解进⾏替换上述配置