AOP(Aspect Oriented Programming),即面向切面编程,是spring框架的核心内容之一。
在使用时,需要将aspectj-1.8.5.jar和spring-aop.jar,aspectjweaver-1.8.5.jar一起使用,以便在Spring应用程序中实现AOP功能。
1、AOP的全称(aspect oriented programming),面向切面编程
2、一张示意图说明AOP的相关概念
概念
1)日志
2) 连接点:切面类可以通过连接点,获取到目标类的目标方法签名
3)通知(五种)
4)切入表达式
3、aop的实现方式
(1)基于动态代理的方式[内置aop实现]
(2)使用框架aspectj来实现
4、aop编程的基本说明
基本说明:
1.需要引入核心的aspect包, 即aop的核心包
2.在切面类中声明通知方法
前置通知:@Before
返回通知:@AfterReturning
异常通知:@AfterThrowing
后置通知:@After
环绕通知:@Around // 环绕通知可以完成另外四个通知的所有事情
执行顺序
3.五种通知和前面写的动态代理类方法的对应关系
5、关于@After与@AfterReturning的区别 (区别!!!!!)
-
@After
:@After
用于在目标方法执行之后执行一个通知(advice)。无论目标方法是否成功,@After
通知都会被执行。- 这是一种通用的后期通知,适用于任何返回类型的方法。
-
@AfterReturning
:@AfterReturning
用于在目标方法成功执行并返回值之后执行一个通知。只有当目标方法正常返回时,@AfterReturning
通知才会被执行。如果目标方法抛出异常,@AfterReturning
通知不会被执行。- 与
@After
不同,@AfterReturning
需要指定一个参数来接收从目标方法返回的值。这个参数的类型和目标方法的返回类型应该匹配。 - 这是一种更细粒度的后期通知,通常用于在方法返回特定值之后执行某些操作。
以下是一个简单的示例来说明这两个注解的区别:
假设有一个名为 ServiceMethod
的类,其中有一个名为 execute
的方法:
public class ServiceMethod {
public String execute(String input) {
// 处理输入并返回结果
return "处理结果";
}
}
如果你想在 execute
方法执行之后打印一些信息,可以使用 @After
:
@Aspect
public class LoggingAspect {
@After("execution(* com.example.ServiceMethod.execute(..))")
public void logAfterExecution(JoinPoint joinPoint) {
System.out.println("execute 方法执行后:");
}
}
如果你想在 execute
方法成功返回结果之后打印返回值,可以使用 @AfterReturning
:
@Aspect
public class LoggingAspect {
@AfterReturning(pointcut = "execution(* com.example.ServiceMethod.execute(..))", returning = "result")
public void logAfterExecution(JoinPoint joinPoint, String result) {
System.out.println("execute 方法返回结果:" + result);
}
}
在这个例子中,@After
会无条件地打印信息,而 @AfterReturning
只有在 execute
方法成功返回结果时才会打印信息。
5.aop编程入门案例
1>先开发两个类
package com.spring.aop;
public interface SmartAnimalable {
float getSum(float i,float j);
float getSub(float i, float j);
}
package com.spring.aop;
import org.springframework.stereotype.Component;
@Component(value = "dog")
public class SmartDog implements SmartAnimalable{
float result ;
@Override
public float getSum(float i, float j) {
result = i+j;
System.out.println("method执行完");
return result;
}
@Override
public float getSub(float i, float j) {
result = i-j;
return result;
}
}
2>在容器中开启基于注解的aop功能
<!-- 开启基于注解的aop功能-->
<aop:aspectj-autoproxy/>
<context:component-scan base-package="com.spring.*"/>
3>开发切面类, 注意 通配 写法
package com.spring.aop;
import org.aspectj.lang.annotation.*;
import org.springframework.stereotype.Component;
@Aspect
@Component
public class SmartAspect {
//开发一个方法, 在目标类的目标方法之前执行
/***@Before:前置通知
*value=值后面写的时候切入表达式execution(public float com.spring.aop.SmartDog.*(..))
*execution:关键字不要改
*public float com.spring.aop.SmartDog·getSum(float,float)你有对哪个类的哪个方法切面编码
*特别强调回显 public float 【访问修饰符和返回值要有..】
*/
@Before(value="execution(public float com.spring.aop.SmartDog.getSum(float,float))")
public void showSmartlog() {
System.out.println("前置通知");
}
@AfterReturning(value="execution(public float com.spring.aop.SmartDog.getSum(float,float))")
public void showSuccesslog() {
System.out.println("返回通知");
}
@AfterThrowing(value="execution(public float com.spring.aop.SmartDog.getSum(float,float))")
public void showExceptionlog() {
System.out.println("异常通知");
}
@After(value="execution(public float com.spring.aop.SmartDog.getSum(float,float))")
public void showFinallylog() {
System.out.println("最终通知");
}
}
4、测试
package com.spring.test;
import com.spring.aop.SmartAnimalable;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class AopTest {
public static void main(String[] args) {
ClassPathXmlApplicationContext applicationContext = new ClassPathXmlApplicationContext("beans.xml");
SmartAnimalable smartAnimalable = applicationContext.getBean(SmartAnimalable.class);
// SmartDog smartDog = (SmartDog) applicationContext.getBean("dog"); //会报错,因为”dog“
//已经被代理类代理了,代理类也实现了SmartAnimalable接口。 具体原因是动态代理
smartAnimalable.getSum(3.4f,3.2f);
}
}
5、run
6、细节:切入表达式的更多说明
细节说明:
1.对切入表达式的基本使用
@Before(value="execution(public float com.spring.aop.SmartDog.getSum(float, float))"
2.切入表达式的更多配置,比如使用模糊配置
//@Before(value="execution(*com.spring.aop.SmartDog.*(..))")
//表示所有访问权限,的所有包的下所有类的所有方法,都会被执行该前置通知方法@Before(value="execution(* *.*(..))")
7、AOP-JoinPoint
JoinPoint连接点, 这个类可以获取到调用函数的签名。
可以在前置通知,后置通知,任意通知当中使用连接点。
具体示例
package com.spring.aop;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.Signature;
import org.aspectj.lang.annotation.*;
import org.springframework.stereotype.Component;
import java.util.Arrays;
import java.util.List;
@Aspect
@Component
public class SmartAspect {
@Before(value="execution(public float com.spring.aop.SmartDog.getSum(float,float))")
public void showSmartlog(JoinPoint joinPoint) {
System.out.println("前置通知");
//获取函数签名
Signature signature = joinPoint.getSignature();
//获取函数名
String name = signature.getName();
//获取方法参数
Object[] args = joinPoint.getArgs();
List<Object> list = Arrays.asList(args);
System.out.println("目标方法的名称"+name+"参数"+list);
}
8、在返回通知方法获取返回结果
用法示例
其中,需要在切入表达式后,再加 returning = "abc", 切入的方法参数加 Object abc
@AfterReturning(value="execution(public float com.spring.aop.SmartDog.getSum(float,float))",returning = "abc")
public void showSuccesslog(JoinPoint joinPoint,Object abc) {
System.out.println("返回通知");
System.out.println("拿到的调用函数的返回值"+abc);
}
9、在异常通知,获取异常对象
@AfterThrowing(value="execution(public float com.spring.aop.SmartDog.getSum(float,float))",throwing = "except")
public void showExceptionlog(JoinPoint joinPoint,Throwable except) {
System.out.println("异常通知");
System.out.println("异常是 "+except);
}
10、环绕通知 (可以做完其他四个通知的所有事情)
@Around(value="execution(public float com.spring.aop.SmartDog.getSum(float,float))")
public Object showAroundlog(ProceedingJoinPoint joinPoint) {
Object result = null;
//获取调用函数的签名
String name = joinPoint.getSignature().getName();
//获取调用函数的参数
Object[] args = joinPoint.getArgs();
try{
//输出前置通知的内容,
System.out.println("前置通知 调用函数名称" +name+ "参数"+Arrays.asList(args));
//调用目标方法,获取返回值
result = joinPoint.proceed();
System.out.println("返回通知"+result); //干的是@AfterReturning的事儿
}catch (Throwable exp){
System.out.println("异常通知"+ "异常消息" + exp.getMessage());
}finally {
System.out.println("最终通知"); //干的是@After的事儿
}
return result;
}
11、切面表达式重用
为了统一管理切面表达式, 可以使用切面表达式重用技术。
切面表达式重用: 类似于在一个类当中声明一个属性,各个方法可以调用这个属性
具体示例:
使用
@PointCut(value= "切面表达式")
public void myPointCut() {
}
其他通知的切面表达式直接引用 即可, 例如
@Before(value="myPointCut")
public void showSmartlog(JoinPoint joinPoint) {
........
}
package com.spring.aop;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.Signature;
import org.aspectj.lang.annotation.*;
import org.springframework.stereotype.Component;
import java.util.Arrays;
import java.util.List;
@Aspect
@Component
public class SmartAspect {
@Before(value="myPointCut()")
public void showSmartlog(JoinPoint joinPoint) {
System.out.println("前置通知");
//获取函数签名
Signature signature = joinPoint.getSignature();
//获取函数名
String name = signature.getName();
//获取方法参数
Object[] args = joinPoint.getArgs();
List<Object> list = Arrays.asList(args);
System.out.println("目标方法的名称"+name+"参数"+list);
}
@Pointcut(value="execution(public float com.spring.aop.SmartDog.getSum(float,float))")
public void myPointCut() {
}
}
12、切面表达式优先级
如果同一个函数,有多个切面在同一个切入点切入,那么执行的优先级如何控制?
基本语法:
@Order(value=n) // n值越小,代表优先级越高
示例:
package com.spring.aop;
@Aspect
@Order(value = 1)
@Component
public class SmartAspect {
@Before(value="myPointCut()")
public void showSmartlog(JoinPoint joinPoint) {
System.out.println("前置通知");
//获取函数签名
Signature signature = joinPoint.getSignature();
//获取函数名
String name = signature.getName();
//获取方法参数
Object[] args = joinPoint.getArgs();
List<Object> list = Arrays.asList(args);
// System.out.println("目标方法的名称"+name+"参数"+list);
}
@AfterReturning(value="execution(public float com.spring.aop.SmartDog.getSum(float,float))",returning = "abc")
public void showSuccesslog(JoinPoint joinPoint,Object abc) {
System.out.println("最终通知"+"拿到的调用函数的返回值"+abc);
}
@After(value="execution(public float com.spring.aop.SmartDog.getSum(float,float))")
public void showFinallylog() {
System.out.println("返回通知");
}
@Pointcut(value="execution(public float com.spring.aop.SmartDog.getSum(float,float))")
public void myPointCut() {
}
}
复制粘贴一个新的切面类, 改输出为**通知2, 优先级设置为2
package com.spring.aop;
@Aspect
@Order(value = 2)
@Component
public class SmartAspect2 {
@Before(value="myPointCut()")
public void showSmartlog(JoinPoint joinPoint) {
System.out.println("前置通知2");
//获取函数签名
Signature signature = joinPoint.getSignature();
//获取函数名
String name = signature.getName();
//获取方法参数
Object[] args = joinPoint.getArgs();
List<Object> list = Arrays.asList(args);
// System.out.println("目标方法的名称"+name+"参数"+list);
}
@AfterReturning(value="execution(public float com.spring.aop.SmartDog.getSum(float,float))",returning = "abc")
public void showSuccesslog(JoinPoint joinPoint,Object abc) {
System.out.println("最终通知2"+"拿到的调用函数的返回值"+abc);
}
@After(value="execution(public float com.spring.aop.SmartDog.getSum(float,float))")
public void showFinallylog() {
System.out.println("返回通知2");
}
@Pointcut(value="execution(public float com.spring.aop.SmartDog.getSum(float,float))")
public void myPointCut() {
}
=
}
public class AopTest {
public static void main(String[] args) {
ClassPathXmlApplicationContext applicationContext = new ClassPathXmlApplicationContext("beans.xml");
SmartAnimalable smartAnimalable = applicationContext.getBean(SmartAnimalable.class);
smartAnimalable.getSum(3.4f,3.2f);
}
}
结果:
结果分析:
1、容易看到切面1的before通知被执行,然后才到切面2的before通知被执行。
2、但是返回通知 却是切面2先执行, 为什么呢?因为
想像 切面一的所有方法为一个整体, 切面二的所有方法为一个整体, 然后切面1的优先级高, 先压入栈中, 切面2后被压入栈中。 那么, 切面一的前置通知先被执行, 切面2的前置通知后执行。
在出栈时,是切面2先出栈, 所以切面2的返回通知,最终通知先执行, 而切面1的后执行。
栈:后进先出
13、AOP的xml配置
像配置bean一样, AOP不仅可以使用注解配置, 也可以使用xml配置。 注意 AOP使用xml配置, 那么相关的 切面类 就是一个普通类,需要去掉任何注解
1、首先是, xml配置
<!-- 开启基于注解的aop功能-->
<aop:aspectj-autoproxy/>
<context:component-scan base-package="com.spring.*"/>
<!-- 配置一个切面类的bean-->
<bean id="smartAspect" class="com.spring.aop.SmartAspect"/>
<!-- 配置切面与切面表达式-->
<aop:config>
<aop:pointcut id="myPointCut" expression
="execution(public float com.spring.aop.SmartDog.getSum(float,float))"/>
<aop:aspect ref="smartAspect" order="1">
<aop:before method="showSmartlog" pointcut-ref="myPointCut"/>
<aop:after-returning method="showSuccesslog" pointcut-ref="myPointCut" returning="abc"/>
<aop:after-throwing method="showExceptionlog" pointcut-ref="myPointCut" throwing="except"/>
<aop:after method="showFinallylog" pointcut-ref="myPointCut" />
<!-- 此处也可以配置环绕通知-->
</aop:aspect>
</aop:config>
2、 切面类, 注意此时是一个普通类
package com.spring.aop;
import java.util.Arrays;
import java.util.List;
public class SmartAspect {
public void showSmartlog(JoinPoint joinPoint) {
System.out.println("前置通知");
//获取函数签名
Signature signature = joinPoint.getSignature();
//获取函数名
String name = signature.getName();
//获取方法参数
Object[] args = joinPoint.getArgs();
List<Object> list = Arrays.asList(args);
System.out.println("目标方法的名称"+name+"参数"+list);
}
// @AfterReturning(value="execution(public float com.spring.aop.SmartDog.getSum(float,float))",returning = "abc")
public void showSuccesslog(JoinPoint joinPoint,Object abc) {
System.out.println("返回通知"+"拿到的调用函数的返回值"+abc);
}
public void showExceptionlog(JoinPoint joinPoint,Throwable except) {
System.out.println("异常通知");
System.out.println("异常是 "+except);
}
public void showFinallylog() {
System.out.println("最终通知");
}
}
3、测试
package com.spring.test;
import com.spring.aop.SmartAnimalable;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class AopTest {
public static void main(String[] args) {
ClassPathXmlApplicationContext applicationContext = new ClassPathXmlApplicationContext("beans.xml");
SmartAnimalable smartAnimalable = applicationContext.getBean(SmartAnimalable.class);
// SmartDog smartDog = (SmartDog) applicationContext.getBean("dog"); //会报错,因为”dog“
//已经被代理类代理了,代理类也实现了SmartAnimalable接口。 具体原因是动态代理
smartAnimalable.getSum(3.4f,3.2f);
}
}
4、结果