核心:横向重复,纵向抽取
AOP为Aspect Oriented Programming的缩写,意为:面向切面编程,通过预编译方式和运行期动态代理实现程序功能的统一维护的一种技术。说的简单一点就是,在你的本身逻辑代码的前后你需要做一些你需要的操作,但是又不想改变原来的代码,使其受影响,这时候AOP就可以帮你简单的进行操作。
拿一个生活中的例子来说,你现在需要吃一个苹果,但是在吃苹果之前你需要将苹果洗干净,吃之后,你需要将垃圾扔进垃圾桶,但是你原本只是吃一个苹果,其他的并不是你想做的,只想吃到苹果而已。不只是吃苹果,你会发现,你基本上吃的水果都需要这件事,这时候你就可以采用一个AOP的思想。
这就相当于你最初的逻辑代码
这是你需要在之前的逻辑代码前后进行的操作,横向的插入一些逻辑
与之相关的还有一种是OOP,就是面向对象编程(Object Oriented Programming,面向对象程序设计)是一种计算机编程架构。这种是纵向的思想(继承,接口),AOP更好的解决了横向的相同的逻辑代码的提取。
我们将自己需要插入到目标业务逻辑中的代码模块化, 通过AOP使之可以横切多个类的模块,称之为切面。
在Spring AOP配置中切面通常包含三部分:
示例:
在使用Spring中的AOP首先要添加一些必须的依赖。
<dependency>
<groupId>org.springframeworkgroupId>
<artifactId>spring-contextartifactId>
<version>5.1.5.RELEASEversion>
dependency>
<dependency>
<groupId>org.aspectjgroupId>
<artifactId>aspectjweaverartifactId>
<version>1.9.4version>
dependency>
下面是本身的逻辑代码
package com.lanou.springaop.simple.calculator;
public class calImp implements Cal {
@Override
public int add(int num1, int num2) {
/* int err = 2/0;*/
return num1+num2;
}
@Override
public int reduce(int num1, int num2) {
return num1-num2;
}
@Override
public int ride(int num1, int num2) {
return num1*num2;
}
}
这个就是你的切面的代码,想当于上面的洗水果的操作
public Object aroundM(ProceedingJoinPoint joinPoint) throws Throwable {
// 获取连接点代表的方法的签名
Signature signature = joinPoint.getSignature();
// 获取方法的名字
String methodName = signature.getName();
//获取方法的参数
Object[] args = joinPoint.getArgs();
//开始时间
long affter = System.currentTimeMillis();
//开始调用目标方法之前
log.debug("[aroundM] ---- 目标方法"+methodName+"("+ Arrays.toString(args)+")开始执行");
//调用目标方法
Object retval = joinPoint.proceed();
//插入需要的逻辑代码
long last = System.currentTimeMillis();
long timr = last-affter;
//调用目标方法之后
System.out.println("方法"+methodName+"("+ Arrays.toString(args) +")返回值为:"+retval+"耗时:"+timr);
return retval;
}
这个是你织入的操作
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop.xsd">
<bean id="cal" class="com.lanou.springaop.simple.calculator.calImp">bean>
<bean id="calimp" class="com.lanou.springaop.simple.calculator.ComputingTime">bean>
<aop:config>
<aop:pointcut id="all" expression="execution(* com.lanou.springaop.simple.calculator.calImp.* (..))"/>
<aop:aspect ref="calimp" >
<aop:around method="aroundM" pointcut-ref="all"/>
aop:aspect>
aop:config>
beans>
演示及结果
public static void main( String[] args ) {
ApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext.xml");
Cal cal = ctx.getBean(Cal.class);
cal.add(10,2);
cal.reduce(10,2);
cal.ride(5,2);
//你原本的逻辑代码只是打印一下12,8,10,
方法add([10, 2])返回值为:12耗时:2
方法reduce([10, 2])返回值为:8耗时:0
方法ride([5, 2])返回值为:10耗时:0
在 Spring AOP 中,需要使用 AspectJ 的切点表达式来定义切点。
AspectJ 指示器 | 描述 |
---|---|
execution () | 用于匹配连接点的执行方法 最常用 |
args () | 限制连接点的指定参数为指定类型的执行方法 |
@args () | 限制连接点匹配参数类型由指定注解标注的执行方法 |
在目标方法调用前通知切面, 什么参数也无法获取。也不能终止目标方法执行
只有在目标方法 正常 执行结束后才会通知, 在通知方法中可以获取到方法的返回值
在目标方法执行结束后通知切面, 什么参数也无法获取。无论目标方法是正常执行结束还是抛出异常终止,都会被通知
只有在目标方法 出现异常 才会通知, 在通知方法中可以获取到抛出的异常信息
连接点有很多种,比如方法执行期间(开始执行、执行结束、抛出异常)、字段修饰符、字段值被更改…
在Spring AOP中只支持方法连接点(因为Spring AOP底层是通过动态代理实现的)。
连接点与切入点的关系可以简单理解为: 切入点一定是连接点, 连接点不一定是切入点。
织入的过程其实就是Spring AOP帮我们把切面中的代码织入到目标代码中的过程。