SpringAOP面向切面编程
一、概念
Spring AOP(Aspect-Oriented Programming,面向切面编程)是 Spring 框架的核心特性之一,本质是通过预编译和运行期动态代理,在不修改原有代码的前提下,为程序添加统一功能(如日志、事务、权限校验)的技术。
假如我100个方法,此时我想给这100个方法都添加日志。我难道要重新编写这100种方法的源码吗?这太麻烦了。SpringAOP提供了一种新的方向,即通过xml配置文件或注解的方式在指定方法前后运行增强方法。
SpringAOP的设级思想不仅降低了代码的运行复杂性,还减少了代码之间的耦合度。大大增强了代码的可读性和可维护性。
二、AOP术语
名称 | 代指 |
1、连接点 | 当前类中哪个方法可以被增强,这些方法称为连接点 |
2、切入点 | 实际增强的方法 |
3、增强(通知) | 实际增加的逻辑部分 |
4、切面 | 将通知应用到切入点的过程 |
通知的类型:
1、前置通知(before) | 方法执行前执行 |
2、后置通知(after-returning) | 方法执行后执行 |
3、环绕通知(around) | 方法执行前后都会执行 |
4、最终通知(after) | 无论方法执行是否成功,最终都会执行 |
5、异常通知(after-throwing) | 发生异常时执行 |
三、基于动态代理的实现机制
“需要被增强的方法” 对应 目标对象(Target) 的核心业务方法(比如 Service 里的业务逻辑)。
“增强方法(power)” 对应 切面(Aspect) 里的通用功能(比如日志、事务的逻辑)。
“动态代理公司” 是 Spring 底层的动态代理机制(JDK 或 CGLIB),它负责把“增强方法”和“需要被增强的方法”结合起来。
最终生成的“代理对象”对外提供服务,当调用“执行代理方法”时,会先执行“增强方法”的逻辑,再执行原业务方法的逻辑(或根据通知类型调整执行顺序)。
四、xml配置文件形式实现
1.在maven包中导入AOP的依赖:
<dependencies><dependency><groupId>org.springframework</groupId><artifactId>spring-context</artifactId><version>5.0.2.RELEASE</version></dependency><dependency><groupId>commons-logging</groupId><artifactId>commons-logging</artifactId><version>1.2</version></dependency><dependency><groupId>org.springframework</groupId><artifactId>spring-test</artifactId><version>5.0.2.RELEASE</version></dependency><dependency><groupId>log4j</groupId><artifactId>log4j</artifactId><version>1.2.12</version></dependency><dependency>
<!--单元测试--><groupId>junit</groupId><artifactId>junit</artifactId><version>4.12</version><scope>test</scope></dependency><!--AOP联盟(接口规范)--><dependency><groupId>aopalliance</groupId><artifactId>aopalliance</artifactId><version>1.0</version></dependency><!--Spring Aspects(springAOP实现)--><dependency><groupId>org.springframework</groupId><artifactId>spring-aspects</artifactId><version>5.0.2.RELEASE</version></dependency><!--aspectj实现面向切面的核心--><dependency><groupId>org.aspectj</groupId><artifactId>aspectjweaver</artifactId><version>1.8.3</version></dependency></dependencies>
2.配置切面:
<!--将user映射为bean对象--><bean id="user" class="com.newFile.User"></bean><bean id="power" class="com.notice.Power"></bean><!--配置切面--><aop:config><!--配置切面 = 切入点 + 通知组成--><!--由哪一个bean承担切面通知(增强类)逻辑--><aop:aspect ref="power"><!--判断是那种情况,再执行那种增强方法,是那个方法执行时调用的--><aop:after-throwing method="afterThrowing" pointcut="execution(public void com.newFile.User.add())"/></aop:aspect></aop:config>
1.ref="power"表示增强类。
2.method表示增强类中的哪个增强方法。
3.pointcut:后边是执行哪个切入点方法,作用是知道对切面的哪个方法进行增强。
4.aop:表示通知类型。
后续只需要在power类中写增强方法,在User类中写被增强方法(例如前面提到的那100种方法)即可。
五、注解形式实现
1.开启注解扫描、注解形式实现AOP:
<!--使用注解实现-->
<!--扫描com包下所有文件(开启注解扫描)--><context:component-scan base-package="com"></context:component-scan>
<!--启动注解方式实现SpringAOP--><aop:aspectj-autoproxy></aop:aspectj-autoproxy>
aop:aspectj-autoproxy
“aop:aspectj-autoproxy”的核心作用是:让 Spring 自动识别被 @Aspect 注解标记的“切面类”,并为匹配“切入点”的目标对象动态生成代理对象,从而使“通知逻辑”(如日志、权限校验)能自动织入到目标方法的执行流程中。
简单来说,“aop:aspectj-autoproxy”是基于注解方式实现的“开关”,有了它,你在代码中通过 @Aspect 、 @Before 、 @Pointcut 等注解编写的切面逻辑才能生效。
2.在增强类和切入类添加@Component,将普通的类标注为Spring的管理组件(相当于Bean,默认id为类名首字母小写),再在增强类上标注@Aspect表示增强类。
增强类:
//component将普通的类标注为Spring容器管理的Bean类
//Controller表示控制层
//Service表示服务层
//Repository表示持久层
//后三者均为Component的的特殊化形式
@Component
//Aspect表示增强类
@Aspect
public class Power {//前置通知@Before(value = "execution(public void com.newFile.User.add())")//让before在add执行前执行//在xml中完成AOP的设置public void beforeaa(){System.out.println("before........");}public void beforebb(){System.out.println("xxxxxx........");}// 环绕通知 入参代表的是切入点@Around(value = "execution(public void com.newFile.User.add())")public void around(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {System.out.println("before.............around1");// 执行被增强的方法(由配置文件配置)proceedingJoinPoint.proceed();System.out.println("after.............around2");}// 最终通知@After(value = "execution(public void com.newFile.User.add())")public void after() {System.out.println("after.............");}//后置通知@AfterReturning(value = "execution(public void com.newFile.User.add())")public void afterReturning() {System.out.println("afterReturning.............");}//异常通知@AfterThrowing(value = "execution(public void com.newFile.User.add())")public void afterThrowing() {System.out.println("afterThrowing.............");}
}
execution(public void com.newFile.User.add())表示执行类路径下的哪个切入点时调用此通知方法。