谈一谈
AOP(面向切面编程,Aspect-Oriented Programming)能够将那些与业务无关,却为业务模块所共同调用的逻辑和责任(例如事务处理、日志管理、权限控制等)封装起来,便于减少系统的重复代码 ,降低模块间的耦合度 ,并有利于未来的可扩展性和可维护性。
Spring AOP是基于 动态代理:
- 代理对象实现了某个接口:AOP就会使用 JDK动态代理对象
- 代理对象没有实现接口:AOP就会使用 CGlib动态代理 生成一个被代理对象的子类来作为代理。
我们也可以使用AspectJ,Spring AOP中集成了AspectJ,AspectJ应该算得上是Java生态系统中最完整的AOP框架了。
AOP使用的话: 我们可以把一些通知功能抽象出来,在需要用到的地方用AOP切入即可,这样可大大简化代码量。我们需要增加新功能也方便。提高了系统的可扩展性。日志功能、事务管理和权限管理等场景都用到AOP。
Spring的使用
首先,创建一个目标对象,该对象包含一个需要增强的方法:
arduino
public class CalculatorService {
public int add(int a, int b) {
return a + b;
}
}
然后,定义一个切面来处理日志记录:
java
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.stereotype.Component;
@Aspect
@Component
public class LoggingAspect {
@Pointcut("execution(* com.example.service.*.*(..))")
public void loggingPointcut() {
}
@Before("loggingPointcut()")
public void logBefore() {
System.out.println("Before method call");
}
}
在Spring配置文件中,启用Spring AOP:
makefile
<aop:aspectj-autoproxy />
或者,如果你使用Java配置:
less
@Configuration
@EnableAspectJAutoProxy
public class AopConfig {
// Other configurations
}
现在,当调用目标对象的方法时,Spring AOP会自动应用切面并执行通知:
arduino
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
public class Main {
public static void main(String[] args) {
ApplicationContext context = new AnnotationConfigApplicationContext(AopConfig.class);
CalculatorService calculatorService = context.getBean(CalculatorService.class);
int result = calculatorService.add(1, 2);
System.out.println("Result: " + result);
}
}
在这个例子中,当CalculatorService
的add
方法被调用时,LoggingAspect
的logBefore
方法也会被调用,从而实现了日志记录的功能。
AOP核心概念
-
连接点(Join Point) :
- 连接点是应用程序执行流程中的一个点,例如方法的执行、异常抛出、类初始化等。在Spring AOP中,最常见的连接点是 方法的执行。
-
切点(Pointcut) :
- 切点是对连接点的逻辑组合,它定义了 哪些连接点会被特定的Aspect所拦截。切点通常基于方法签名、异常类型、注解等来定义。
-
通知(Advice) :
- 通知是AOP框架在特定连接点执行的动作。通知可以分为前置通知、后置通知、环绕通知、异常通知和最终通知。通知定义了在连接点执行的动作,如日志记录、事务管理等。
-
横切关注点 :
- 对哪些方法进行拦截,拦截后怎么处理,这些关注点称之为横切关注点
-
切面(Aspect) :
- 切面是关注点的模块化,它将横切关注点与业务逻辑分离。一个切面可以包含一个或多个通知,并且可以将其应用于多个连接点。
-
目标对象(Target Object) :
- 目标对象是被代理的对象,它包含实际的业务逻辑。AOP代理会拦截目标对象的方法调用,并可能将其转发给相应的切面。
-
代理(Proxy) :
- 代理是AOP框架创建的对象,它包含了对目标对象的代理。代理拦截对目标对象的调用,并可能将其转发给相应的切面。
-
织入(Weaving) :
- 在Spring AOP中,织入是 在运行时通过CGLIB或JDK动态代理完成的过程
-
引入(Introduction) :
- 在不修改代码的情况下向类添加新的方法或字段。 这可以通过在切面中定义新的方法或字段,并在织入时将它们添加到目标对象中来实现。
Spring AOP怎么实现横切关注点的模块化和分离:
-
ProxyFactoryBean:
- Spring早期版本中使用
ProxyFactoryBean
来创建代理对象。ProxyFactoryBean
可以创建基于JDK动态代理或CGLIB代理的代理。
- Spring早期版本中使用
-
JDK动态代理:
- 如果目标对象实现了接口,Spring AOP默认使用JDK动态代理。
ProxyFactoryBean
会自动选择JDK动态代理。
- 如果目标对象实现了接口,Spring AOP默认使用JDK动态代理。
-
CGLIB代理:
- 如果目标对象没有实现接口,Spring AOP会使用CGLIB来创建代理。
ProxyFactoryBean
会自动选择CGLIB代理。
- 如果目标对象没有实现接口,Spring AOP会使用CGLIB来创建代理。
-
AspectJ:
- Spring AOP使用AspectJ来定义切面、切入点、通知等概念。AspectJ提供了丰富的AOP功能,如方法拦截、环绕通知、增强方法等。
Spring AOP的执行流程:
-
定义切面:
- 开发者定义一个切面类,其中包含通知方法。切面类可以包含前置通知、后置通知、环绕通知、异常通知和最终通知等。
-
定义切入点:
- 开发者定义切入点表达式,指定哪些连接点会被切面拦截。切入点表达式基于方法签名、异常类型、注解等信息来定义。
-
配置Spring AOP:
- 在Spring配置文件中,开发者配置Spring AOP,告诉Spring在哪些切点应用切面。
-
创建代理:
- Spring AOP会根据目标对象是否实现接口以及是否强制使用CGLIB来决定使用JDK动态代理还是CGLIB。
- 代理对象会拦截对目标对象的调用,并应用相应的切面。
-
调用目标对象:
- 当通过代理对象调用方法时,代理对象会检查是否需要应用切面。如果需要,它将执行相应的通知方法,然后继续执行目标对象的方法。
-
执行通知:
- 通知方法在目标对象的方法调用前后执行,允许开发者自定义方法调用的行为,如日志记录、事务管理等。
通过这种方式,Spring AOP实现了横切关注点的模块化和分离,提高了代码的可维护性和可重用性。
面试题精粹:
Spring AOP 和 AspectJ AOP有什么区别
Spring AOP:
- 运行时增强
- 基于代理(Proxying)
AspectJ AOP:
- 编译时增强
- 基于字节码操作(Bytecode Manipulation)
AspectJ相比于Spring AOP功能更强大,但Spring AOP相对来说更简单。
如果切面比较少,两者差异不大。如果切面比较多,最好选择AspectJ,相对于会快很多。
什么是通知呢?有哪些类型呢?
通知 是这个方法在执行前和执行后要做的动作。就是程序执行时要通过Spring AOP框架触发的代码段。
Spring切面可以应用五种类型的通知:
- before:前置通知,在一个方法执行前被调用。
- after:在方法执行之后调用的通知,无论方法执行是否成功。
- after-returning:仅当方法成功完成后执行的通知。
- after-throwing:在方法抛出异常退出时执行的通知。
- around:在方法执行之前和之后调用的通知。