参考:
Spring AOP——Spring 中面向切面编程 - SharpCJ - 博客园
一、背景 AOP (Aspect Orient Programming),直译过来就是 面向切面编程。
AOP 是一种编程思想,是面向对象编程(OOP)的一种补充。面向对象编程将程序抽象成各个层次的对象,而面向切面编程是将程序抽象成各个切面。
1.1 为什么需要 AOP 想象下面的场景,开发中在多个模块间有某段重复的代码,我们通常是怎么处理的?显然,没有人会靠“复制粘贴”吧。在传统的面向过程编程中,我们也会将这段代码,抽象成一个方法,然后在需要的地方分别调用这个方法,这样当这段代码需要修改时,我们只需要改变这个方法就可以了。然而需求总是变化的,有一天,新增了一个需求,需要再多出做修改,我们需要再抽象出一个方法,然后再在需要的地方分别调用这个方法,又或者我们不需要这个方法了,我们还是得删除掉每一处调用该方法的地方。实际上涉及到多个地方具有相同的修改的问题我们都可以通过 AOP 来解决。
1.2 AOP 实现分类 AOP 要达到的效果是,保证开发者不修改源代码的前提下,去为系统中的业务组件添加某种通用功能。AOP 的本质是由 AOP 框架修改业务组件的多个方法的源代码,AOP 其实就是代理模式的典型应用。
按照 AOP 框架修改源代码的时机,可以将其分为两类:
- 静态 AOP 实现, AOP 框架在编译阶段对程序源代码进行修改,生成了静态的 AOP 代理类(生成的 *.class 文件已经被改掉了,需要使用特定的编译器),比如 AspectJ。
- 动态 AOP 实现, AOP 框架在运行阶段对动态生成代理对象(在内存中以 JDK 动态代理,或 CGlib 动态地生成 AOP 代理类),如 SpringAOP。
二、AOP 术语 AOP 领域中的特性术语:
- 通知(Advice): AOP 框架中的增强处理。通知描述了切面何时执行以及如何执行增强处理。
- 连接点(join point): 连接点表示应用执行过程中能够插入切面的一个点,这个点可以是方法的调用、异常的抛出。在 Spring AOP 中,连接点总是方法的调用。
- 切点(PointCut): 可以插入增强处理的连接点。
- 切面(Aspect): 切面是通知和切点的结合。
- 引入(Introduction):引入允许我们向现有的类添加新的方法或者属性。
- 织入(Weaving): 将增强处理添加到目标对象中,并创建一个被增强的对象,这个过程就是织入。
如: 我想在 所有调用 HelloServiceImpl.Hi 方法 的场景,这个Hi方法之前或之后,执行我新添加的一些方法
一共两步:
1、通过@Aspect 设置成切面类,里面包含自己设置的切点与想要添加的逻辑
切点就是指定,想在哪写方法加想要的逻辑
通过@Pointcut 来设置切点
2、 在执行入口,添加@EnableAspectJAutoProxy 来启动 切面
栗子
接口HelloService
package com.bt.study_spring.demo12;
public interface HelloService {
String hi(String name);
void hello();
}
接口实现类HelloServiceImpl
package com.bt.study_spring.demo12;
import org.springframework.stereotype.Component;
@Component
public class HelloServiceImpl implements HelloService {
@Override
public String hi(String name) {
System.out.println("大家好");
return "hi,"+name;
}@Override
public void hello() {
System.out.println("hello");
}
}
运行入口APP
package com.bt.study_spring.demo12;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.EnableAspectJAutoProxy;
@Configuration
@ComponentScan
@EnableAspectJAutoProxy
public class App {
public static void main(String[] args) {ApplicationContext applicationContext = new AnnotationConfigApplicationContext(App.class);
HelloService helloService = applicationContext.getBean(HelloService.class);
String xiaoxinxin = helloService.hi("xiaoxinxin");
helloService.hello();
Hello1 hello1 = applicationContext.getBean(Hello1.class);
hello1.hello();
System.out.println(xiaoxinxin);
}
}
--------------------------
想要在所有调用HelloServiceImpl 类下任意方法的场景下,插入一段逻辑
新建切面类LogAspect
package com.bt.study_spring.demo12;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.*;
import org.springframework.stereotype.Component;
@Component
@Aspect // 切面
public class LogAspect {
// 切点
@Pointcut("execution(public * com.bt.study_spring.demo12.*.*(..))")
public void log(){}/*
@Before 前置通知,在执行方法之前,先执行before里的方法
execution()固定写法
public * com.bt.study_spring.demo12.HelloServiceImpl.*(..))
public 指的是,你要控制的那个方法的访问权限
* 返回值,不限制
com.bt.study_spring.demo12.HelloServiceImpl 你要控制的那个方法的全类名
* 这个类下的任意方法
(..) 参数列表任意
public * com.bt.study_spring.demo12.HelloServiceImpl.*(..) 代表控制到类
public * com.bt.study_spring.demo12.*.*(..) 控制的包*/
//@Before("execution(public * com.bt.study_spring.demo12.*.*(..))")
@Before("log()")
public void before(JoinPoint point){
// 通过 JoinPoint point 来获取 类、方法的一些信息
String name = point.getSignature().getName();
String declaringTypeName = point.getSignature().getDeclaringTypeName();
System.out.println("方法名"+name);
System.out.println("类名"+declaringTypeName );
System.out.println("before AAAA");
}/*
@After 后置通知,在执行方法之后,再执行after里的方法
*/
@After("execution(public * com.bt.study_spring.demo12.HelloServiceImpl.*(..))")
public void after(){
System.out.println("After BBB");
}// 异常通知,出现异常的时候写它
@AfterThrowing("log()")
public void error(){
System.out.println("error");
}// 最终通知,obj用来接收返回值的
@AfterReturning(value = "https://www.it610.com/article/log()",returning = "obj")
public void afterReturning(Object obj){
System.out.println(">>>>:"+obj);
System.out.println("afterReturning DDD");
}}
a、通过注解@Aspect 使这个类成为 切面,
@Component 把这个类交给Spring管理
b、 通过@Pointcut设置切点。就是指定哪些方法来添加新逻辑
@Pointcut("execution(public * com.bt.study_spring.demo12.*.*(..))")
public void log(){}
这里就跟正则有点像,去匹配对应的类/方法
execution()固定写法
public * com.bt.study_spring.demo12.HelloServiceImpl.*(..))
public 指的是,你要控制的那个方法的访问权限
* 返回值,不限制
com.bt.study_spring.demo12.HelloServiceImpl 你要控制的那个方法的全类名
* 这个类下的任意方法
(..) 参数列表任意
public * com.bt.study_spring.demo12.HelloServiceImpl.*(..) 代表控制到类
public * com.bt.study_spring.demo12.*.*(..) 控制的包
c、通知类型:前置通知、后置通知、异常通知、环绕通知
前置通知 @Before
后置通知@After
异常通知@AfterThrowing
最终通知@AfterReturning
环绕通知 @Around
后置通知
@After("log()")
public void after(){
System.out.println("After BBB");
}
@After() 注解成为后置通知
("log()") 标明切点,(指定实现范围)
前置通知
@Before("log()")
public void before(JoinPoint point){
// 通过 JoinPoint point 来获取 类、方法的一些信息
String name = point.getSignature().getName();
String declaringTypeName = point.getSignature().getDeclaringTypeName();
System.out.println("方法名"+name);
System.out.println("类名"+declaringTypeName );
System.out.println("before AAAA");
}
我们想获取,我们匹配的那个方法/类的一些信息,可以在传参里加
JoinPoint point
获取方法名
String name = point.getSignature().getName();
获取方法的类名
String declaringTypeName = point.getSignature().getDeclaringTypeName();
d、启动的时候,在入口加@EnableAspectJAutoProxy
【JAVA测开|二、Spring AOP面向切面编程】
四、依赖包:
org.springframework
spring-aop
4.3.30.RELEASE
org.aspectj
aspectjweaver
1.8.9
五、报错处理: 运行代码报错
警告: Exception encountered during context initialization - cancelling refresh attempt: org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'org.springframework.aop.config.internalAutoProxyCreator': Initialization of bean failed; nested exception is java.lang.NoSuchMethodError: org.springframework.util.ReflectionUtils$MethodFilter.and(Lorg/springframework/util/ReflectionUtils$MethodFilter; )Lorg/springframework/util/ReflectionUtils$MethodFilter;
Exception in thread "main" org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'org.springframework.aop.config.internalAutoProxyCreator': Initialization of bean failed; nested exception is java.lang.NoSuchMethodError: org.springframework.util.ReflectionUtils$MethodFilter.and(Lorg/springframework/util/ReflectionUtils$MethodFilter; )Lorg/springframework/util/ReflectionUtils$MethodFilter;
因为缺少这个依赖包:
org.aspectj
aspectjweaver
1.8.9
推荐阅读
- spring|Spring AOP(面向切面编程)
- 电脑系统知识|Win11游戏模式怎么开启(Win11开启游戏模式的方法)
- Java从入门到入土|[Java] 神秘的IO流 (上)
- Java从入门到入土|[Java] 异常的使用
- Java从入门到入土|[Java] 神秘的IO流 (下)
- docker|云原生时代下的容器镜像安全(上)
- 程序人生|微服务的构建环境比较--Spring Cloud和Kubernetes
- 公司新来一个同事,把 @Transactional 事务注解运用得炉火纯青。。
- python|UNIX文件系统命令