JAVA测开|二、Spring AOP面向切面编程

参考:
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





    推荐阅读