第八章 Spring AOP
8.1 Spring AOP介绍
Spring AOP(面向切面编程)是Spring框架的一个重要模块,它通过在应用程序中定义切面(Aspect)和切点(Pointcut)来实现横向关注点(cross-cutting concerns)的处理。这样,应用程序的业务逻辑就可以专注于核心功能,而不需要处理与横向关注点相关的问题,例如事务管理、安全性、缓存、日志记录等。
8.1.1 Spring AOP概述
AOP和OOP的区别在于它们解决问题的角度不同。OOP从面向对象的角度出发,将程序中的功能划分为对象,通过封装、继承、多态等手段来实现代码的重用和扩展。而AOP则从横向关注点的角度出发,将程序中的功能划分为**切面,**通过将切面织入到目标对象的方法中,实现对目标方法的增强。
在传统的业务处理代码中,通常都会进行一些通用的操作,如事务处理、日志记录等。虽然使用OOP可以通过组合或继承来实现代码的重用,但是如果要实现某个功能(如日志记录),同样的代码仍然会分散到各个方法中。如果想要关闭某个功能或者对其进行修改,就必须修改所有相关的方法,增加了开发人员的工作量和代码的出错率。
AOP可以为此类问题提供完美的解决方案。它将通用功能抽取到独立的模块中,通过将这些切面织入到目标方法中,实现对目标方法的增强,从而降低横向业务逻辑之间的耦合,减少重复代码。例如,在订单系统中,添加订单、更新订单和删除订单的方法中都包含事务管理业务代码,这会带来一定数量的重复代码和维护成本。使用AOP可以将事务管理业务逻辑从这些方法中抽取到一个可重用的模块中,提高程序的可重用性和开发效率。
AOP的使用使开发人员在编写业务逻辑时可以专注于核心业务,而不必过多地关注其他业务逻辑的实现,这提高了开发效率并且增强了代码的可维护性。因此,AOP和OOP都是提高程序可重用性和可维护性的重要手段,它们可以互相补充,共同使用。
想增强一个方法,又不想修改这个方法里的内容,因为我增强的部分可能是和业务逻辑无关的(比如打印日志),在不修改原方法的内容的情况下给其增加功能,这就是切面编程的作用。
8.1.2 Spring AOP术语
AOP并不是一个新的概念,在Java 语言中早就出现了类似的机制。
AOP的实现机制与Java平台的EJB规范、Servlet规范和Struts2框架中的拦截器机制非常相似,都是把通用的功能抽象成切面,然后在目标对象的方法执行前后添加增强处理,以实现对目标对象的增强。下面是AOP中的一些常用术语的简单介绍:
-
切面(Aspect):切面是一个模块化的横切关注点(cross-cutting concern),它包含一组通知和切入点,在目标对象的方法执行前后或者抛出异常时执行特定的操作。
-
连接点(Join point):连接点是指那些被拦截到的点,在Java中,这些点指的是方法的调用或者异常的处理等。
-
切入点(Pointcut):切入点是指那些我们要对哪些连接点进行拦截的定义,通常使用表达式来定义切入点。
-
通知/增强处理(Advice):通知是指在切面的某个特定连接点上执行的动作,它包括了Before、After、Around、AfterReturning和AfterThrowing等类型。
-
目标对象(Target):目标对象是指那些被切面通知的对象,也就是我们要对哪些对象进行增强的定义。
-
织入(Weaving):织入是指把切面应用到目标对象并创建新的代理对象的过程。
-
代理(Proxy):代理是指对目标对象进行包装,以便实现在目标对象执行前后添加增强处理的功能。
-
引介(Introduction):引介是一种特殊的通知类型,它允许向现有的类添加新的方法或属性,类似于Java中的接口实现。
这8个术语的应用例子:
假设我们有一个简单的图形计算器程序,其中有一个计算矩形面积的方法,我们想在该方法执行前后记录日志,并且在矩形对象的属性发生变化时自动更新矩形对象的面积,可以使用AOP来实现这个功能。下面是一个简单的实现示例:
1. 定义切面类
@Component
@Aspect
public class LoggingAndCalculatingAspect {
@Before("execution(* com.example.calculator.RectangleCalculator.calculateArea(..))")
public void logBefore(JoinPoint joinPoint) {
System.out.println("Before calculating area...");
}
@AfterReturning("execution(* com.example.calculator.RectangleCalculator.calculateArea(..))")
public void logAfterReturning(JoinPoint joinPoint) {
System.out.println("Area calculated successfully.");
}
@Around("execution(* com.example.calculator.Rectangle.get*(..))")
public Object calculateAndUpdateArea(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
Object result = proceedingJoinPoint.proceed();
if (proceedingJoinPoint.getTarget() instanceof Rectangle) {
Rectangle rectangle = (Rectangle) proceedingJoinPoint.getTarget();
rectangle.setArea(rectangle.getLength() * rectangle.getWidth());
}
return result;
}
@DeclareParents(value = "com.example.calculator.Rectangle+", defaultImpl = com.example.calculator.RectangleImpl.class)
public static RectangleMixin rectangleMixin;
}
interface RectangleMixin {
double getArea();
void setArea(double area);
}
class RectangleImpl implements RectangleMixin {
private double area;
public double getArea() {
return area;
}
public void setArea(double area) {
this.area = area;
}
}
2. 定义业务逻辑类
@Service
public class RectangleCalculator {
public double calculateArea(Rectangle rectangle) {
return rectangle.getLength() * rectangle.getWidth();
}
}
class Rectangle {
private double length;
private double width;
public Rectangle(double length, double width) {
this.length = length;
this.width = width;
}
public double getLength() {
return length;
}