参考:
Spring AOP——Spring 中面向切面编程 - SharpCJ - 博客园
AOP (Aspect Orient Programming),直译过来就是 面向切面编程。
AOP 是一种编程思想,是面向对象编程(OOP)的一种补充。面向对象编程将程序抽象成各个层次的对象,而面向切面编程是将程序抽象成各个切面。
想象下面的场景,开发中在多个模块间有某段重复的代码,我们通常是怎么处理的?显然,没有人会靠“复制粘贴”吧。在传统的面向过程编程中,我们也会将这段代码,抽象成一个方法,然后在需要的地方分别调用这个方法,这样当这段代码需要修改时,我们只需要改变这个方法就可以了。然而需求总是变化的,有一天,新增了一个需求,需要再多出做修改,我们需要再抽象出一个方法,然后再在需要的地方分别调用这个方法,又或者我们不需要这个方法了,我们还是得删除掉每一处调用该方法的地方。实际上涉及到多个地方具有相同的修改的问题我们都可以通过 AOP 来解决。
AOP 要达到的效果是,保证开发者不修改源代码的前提下,去为系统中的业务组件添加某种通用功能。AOP 的本质是由 AOP 框架修改业务组件的多个方法的源代码,AOP 其实就是代理模式的典型应用。
按照 AOP 框架修改源代码的时机,可以将其分为两类:
AOP 领域中的特性术语:
我的理解就是:在不修改源代码的前提下,在某些方法 之前 或者之后 添加一些 通用的功能
如: 我想在 所有调用 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 = "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
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