面向切面编程
基本概念
AOP : (Aspect Oriented Programming )面向切面编程 ;
静态代理(编译时增强——AspectJ)
动态代理(运行时增强——Spring AOP)
面向切面编程 : 基于OOP基础之上新的编程思想 ;
指在程序运行期间, 将某段代码动态的切入到指定方法的指定位置进行运行的这种编程方式, 面向切面编程 ;
核心概念:
切面(Aspect) :官方的抽象定义为“一个关注点的模块化,这个关注点可能会横切多个对象”。
连接点(Joinpoint) :程序执行过程中的某一行为。
通知(Advice) :“切面”对于某个“连接点”所产生的动作。
切入点(Pointcut) :匹配连接点的断言,在AOP中通知和一个切入点表达式关联。
目标对象(Target Object) :被一个或者多个切面所通知的对象。
AOP代理(AOP Proxy) 在Spring AOP中有两种代理方式,JDK动态代理和CGLIB代理。
配置:
- 将目标类和切面类加入到ioc容器中
- 声明切面
- 配置运行何时何地
通知类型
* @Before, 在目标之前运行, 前置通知
* @After, 在目标方法结束之后, 后置通知
* @AfterReturning, 在目标方法正常返回之后, 返回通知
* @AfterThrowing, 在目标方法抛出异常之后. 异常通知
* @Around, 环绕通知, 上面四个通知的联合
* try {
* @Before
* XXX
* @AfterReturning
* }catch{
* @AfterThrowing
* }finally{
* @After
* }
基于注解的配置
@Aspect: 声明一个切面类
@Component: 让Spring能够扫描到
切入点表达式: 固定格式: execution(访问权限 返回值类型 方法全类名(参数列表))
- 通配符:
’ * ’ :
- 匹配一个或者多个字符
- 匹配任意一个参数
- 匹配路径: 只能匹配一层路径
权限位置(public void)不写就是任意权限, 不能使用*去匹配
’ … ’ : - 匹配任意多个参数, 任意类型参数
- 匹配任意多层路径
- 运算符
’ && ’ : 我们要切入的位置满足这两个表达式
’ || ’ : 满足任意一个表达式就行
’ ! ': 只要不是这个位置就都切
execution 详细: https://blog.csdn.net/lk7688535/article/details/51989746
@Pointcut: 抽取可重用的切入点表达式
- 随便声明一个没有实现的返回void的空方法
- 给方法(pointcut)上标注@Pointcut注解, 使用时: @After(“poincut()”)
JoinPoint: 封装了当前目标方法的详细信息
前置通知
@Before("execution(public int com.xian.impl.MyMathCalculator.*(int, int))")
public static void logStart(JoinPoint joinPoint) {//Method method, Object... args) {
//获取到目标方法运行使用的参数
Object[] args = joinPoint.getArgs();
//获取到方法签名
Signature signature = joinPoint.getSignature();
String name = signature.getName();
System.out.println("[" + name + "]方法开始执行, 用的参数列表" + Arrays.asList(args));
}
后置通知:
@After("poincut()")
public static void logEnd(JoinPoint joinPoint){ ... }
返回通知
//returning = "result": 告诉Spring, result是用来接收返回值的
@AfterReturning(value = "execution(public int com.xian.impl.MyMathCalculator.*(int, int))"
, returning = "result" )
public static void logReturn(JoinPoint joinPoint, Object result) {
Signature signature = joinPoint.getSignature();
String name = signature.getName();
System.out.println("[" + name + "]方法执行完成, 计算结果是:[" + result + "]");
}
异常通知
//告诉Spring那个参数用来接收异常
@AfterThrowing(value = "execution(public int com.xian.impl.MyMathCalculator.*(int, int))",
throwing = "exception")
public static void logException(JoinPoint joinPoint, Exception exception) {
Object[] args = joinPoint.getArgs();
Signature signature = joinPoint.getSignature();
String name = signature.getName();
System.out.println("[" + name + "]出现异常:" + exception);
}
环绕通知:
//ProceedingJoinPoint 继承于 JoinPoint
@Around("poincut()")
public static Object logAround(ProceedingJoinPoint pjp) {
Object proceed = null;
try {
//环绕前置
System.out.println("before");
Object[] args = pjp.getArgs();
//利用反射推进调用目标方法执行
proceed = pjp.proceed(args);
//环绕返回
System.out.println("return");
return proceed;
} catch (Throwable e) {
//环绕异常
System.out.println("error");
}finally {
//环绕后置
System.out.println("after");
}
System.out.println("============");
//返回反射调用的返回值
System.out.println(proceed);
return proceed; //这里的返回值即是调用目标方法的返回值.
}
开启基于注解的AOP功能
<!-- 扫描包 -->
<context:component-scan base-package="com.xian"></context:component-scan>
<!-- 开启基于注解的AOP功能, aop 名称空间 -->
<aop:aspectj-autoproxy></aop:aspectj-autoproxy>
执行顺序
try{(环绕前置/普通前置) ->目标执行 ->环绕返回} -> catch(){环绕异常} -> finally(){环绕后置} } - > 普通后置 -> 普通返回/异常
(环绕前置/普通前置) : 此处环绕前置与普通前置的执行顺序不固定.
XML配置
<!-- 基于配置的AOP -->
<bean class="com.xian.impl.MyMathCalculator" id="myMathCalculator"></bean>
<bean class="com.xian.utils.validateApsect" id="validateApsect"></bean>
<bean class="com.xian.utils.LogUtils" id="logUtils"></bean>
<!-- 需要AOP名称空间 -->
<aop:config>
<aop:pointcut expression="" id=""/>
<!-- 指定切面: @Aspect -->
<aop:aspect ref="logUtils" order="3">
<aop:around method="logAround" pointcut-ref="s"/>
<aop:pointcut expression="execution(* com.xian.impl.*.*(..))" id="s"/>
<aop:before method="logStart" pointcut-ref="s"/>
<aop:after method="logEnd" pointcut-ref="s"/>
<aop:after-returning method="logReturn" pointcut-ref="s" returning="result"/>
<aop:after-throwing method="logException" pointcut-ref="s" throwing="exception"/>
</aop:aspect>
</aop:config>
AOP使用场景
AOP使用场景:
- AOP加日志保存到数据库;
- AOP去做权限验证;
- AOP做安全检查;
- AOP做事务控制;
注解: 快速方便
XML 配置: 功能完善, 重要的用XML配置, 不重要的用注释;