前言
Spring AOP(Aspect-Oriented Programming,面向切面编程)是Spring框架中的一个重要模块,它提供了一种将横切关注点(cross-cutting concerns)从业务逻辑中分离出来的方法。横切关注点通常包括日志记录、事务管理、安全性等,这些关注点经常散落在代码的各个角落,导致代码重复和难以维护。
通过使用Spring AOP,你可以将这些横切关注点定义为切面(Aspect),然后将它们织入(weave)到目标对象的方法执行流程中。这样,你可以在不影响业务逻辑的前提下,添加或修改横切关注点的行为。
Spring AOP主要使用代理模式来实现,它为目标对象创建一个代理对象,并在代理对象上拦截方法调用。当方法被调用时,代理对象会根据切面的配置来决定是否执行额外的操作(如日志记录、事务管理等)。
Spring AOP的主要特点包括:
- 声明式编程:你可以通过注解或XML配置文件来声明切面、切点和通知(advice),而无需修改业务代码。
- 集成性强:Spring AOP与Spring的其他模块(如Spring MVC、Spring Data等)无缝集成,可以轻松地在Spring应用中实现面向切面编程。
- 可扩展性:你可以自定义切面、切点和通知的实现,以满足特定的业务需求。
需要注意的是,Spring AOP是基于代理的,因此它只能拦截通过Spring容器管理的bean之间的方法调用。对于非Spring管理的对象或方法(如静态方法、私有方法等),Spring AOP可能无法提供支持。如果需要更强大的AOP功能,可以考虑使用AspectJ等更专业的AOP框架。
一、准备
1. service
package org.example.aop.xml;
/**
* Create by zjg on 2024/4/14
*/
public interface Calculator {
int add(int i,int j);
int sub(int i,int j);
int mul(int i,int j);
int div(int i,int j);
}
2. impl
package org.example.aop.xml.impl;
import org.example.aop.xml.Calculator;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* Create by zjg on 2024/4/14
*/
public class CalculatorImpl implements Calculator {
private Logger logger = LoggerFactory.getLogger(CalculatorImpl.class);
@Override
public int add(int i, int j) {
logger.debug("add({},{})",i,j);
// int s=1/0;
return i+j;
}
@Override
public int sub(int i, int j) {
logger.debug("sub({},{})",i,j);
return i-j;
}
@Override
public int mul(int i, int j) {
logger.debug("mul({},{})",i,j);
return i*j;
}
@Override
public int div(int i, int j) {
logger.debug("div({},{})",i,j);
return i/j;
}
}
二、核心代码
1. 切面类
package org.example.aop.xml.aspect;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Aspect;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;
import java.util.Arrays;
/**
* Create by zjg on 2024/4/14
*/
@Component
@Aspect
public class LogAspect {
private Logger logger = LoggerFactory.getLogger(LogAspect.class);
public void before(JoinPoint joinPoint){
String name = joinPoint.getSignature().getName();
Object[] args = joinPoint.getArgs();
logger.debug("[{}前置通知]{}",name, Arrays.toString(args));
}
public void afterReturning(JoinPoint joinPoint,Object result){
String name = joinPoint.getSignature().getName();
logger.debug("[{}返回通知][{}]",name,result);
}
public void afterThrowing(JoinPoint joinPoint,Exception exception){
String name = joinPoint.getSignature().getName();
logger.debug("[{}异常通知][{}]",name,exception.getMessage());
}
public void after(JoinPoint joinPoint){
String name = joinPoint.getSignature().getName();
logger.debug("[{}后置通知]",name);
}
public Object around(ProceedingJoinPoint joinPoint) throws Throwable {
String name = joinPoint.getSignature().getName();
Object[] args = joinPoint.getArgs();
logger.debug("[{}环绕通知前置]{}",name,Arrays.toString(args));
Object result = null;
try {
result= joinPoint.proceed();
logger.debug("[{}环绕通知返回][{}]",name,result);
}catch (Exception exception){
logger.debug("[{}环绕通知异常]",name);
}finally {
logger.debug("[{}环绕通知后置]",name);
}
return result;
}
}
2. 配置文件
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd">
<bean id="calculator" class="org.example.aop.xml.impl.CalculatorImpl"></bean>
<bean id="logAspect" class="org.example.aop.xml.aspect.LogAspect"></bean>
<aop:aspectj-autoproxy></aop:aspectj-autoproxy>
<aop:config>
<aop:aspect id="logAspect" ref="logAspect">
<aop:pointcut id="pointcut" expression="execution(* org.example.aop.xml.*.*(..))"/>
<aop:before method="before" pointcut-ref="pointcut"></aop:before>
<aop:after-returning method="afterReturning" pointcut-ref="pointcut" returning="result"></aop:after-returning>
<aop:after-throwing method="afterThrowing" pointcut-ref="pointcut" throwing="exception"></aop:after-throwing>
<aop:after method="after" pointcut-ref="pointcut"></aop:after>
<aop:around method="around" pointcut-ref="pointcut"></aop:around>
</aop:aspect>
</aop:config>
</beans>
3. 测试类
package org.example.aop.xml;
import org.springframework.context.support.ClassPathXmlApplicationContext;
/**
* Create by zjg on 2024/4/14
*/
public class Test {
public static void main(String[] args) {
ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("springContext.xml");
Calculator calculator = context.getBean("calculator", Calculator.class);
calculator.add(1,1);
}
}
4. 测试结果
[2024-04-14 14:17:20.384][main][DEBUG]- org.example.aop.xml.aspect.LogAspect.before(LogAspect.java:27) - [add前置通知][1, 1]
[2024-04-14 14:17:20.385][main][DEBUG]- org.example.aop.xml.aspect.LogAspect.around(LogAspect.java:44) - [add环绕通知前置][1, 1]
[2024-04-14 14:17:20.385][main][DEBUG]- org.example.aop.xml.impl.CalculatorImpl.add(CalculatorImpl.java:14) - add(1,1)
[2024-04-14 14:17:20.385][main][DEBUG]- org.example.aop.xml.aspect.LogAspect.around(LogAspect.java:48) - [add环绕通知返回][2]
[2024-04-14 14:17:20.385][main][DEBUG]- org.example.aop.xml.aspect.LogAspect.around(LogAspect.java:52) - [add环绕通知后置]
[2024-04-14 14:17:20.386][main][DEBUG]- org.example.aop.xml.aspect.LogAspect.after(LogAspect.java:39) - [add后置通知]
[2024-04-14 14:17:20.386][main][DEBUG]- org.example.aop.xml.aspect.LogAspect.afterReturning(LogAspect.java:31) - [add返回通知][2]