1.概述
AOP(面向切面编程):将程序中的非业务代码抽取 , 在不修改业务代码的前提下, 为其添加功能(功能增强) ,面向切面的编程思想底层是为目标创建一个代理对象,让代理对象调用目标类中方法 , 在代理对象调用时, 可以额外的调用其他的方法(增强的方法,通知)
功能: 提高代码复用率, 灵活性, 提高开发效率, 降低业务代码和非业务代码耦合度
核心原理: 使用动态代理的方式在执行方法前后或者出现异常的时候加入相关的逻辑
注意: AOP是对OOP(面向对象)补充 , 不是spring中特有的,是Java中的 一种代理思想
2.AOP中常见的专业术语
连接点: 类中可以被增强的方法(例如新增, 修改...)
切入点: 实际被增强了的方法,
通知(Advice) : 方法被增强的功能(日志, 事务管理, 权限验证) 通知可以被分为前置通知,后置通知, 异常通知, 最终通知, 环绕通知
切面(Aspect): 把通知添加到切入点的整个过程称为切面.
目标(Target): 代理的目标对象(连接点,切入点所在类) 真正做这件事的类
代理(Proxy): 向目标对象应用通知时创建的代理对象, 代理对象,帮助我们调用通知的那个对象
3.AOP的搭建使用
3.1xml方式
1> 导入AOP的jar包
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aspects</artifactId>
<version>5.2.2.RELEASE</version>
</dependency>
2> spring.xml中配置
<!-- AOP配置引入 连接点-->
<bean id="commonUtil" class="com.ffyc.sping.util.CommonUtil"></bean>
<!--
配置通知与切入点之间的关系
-->
<aop:config>
<!--
指定切入点 *代表所有 (..) 表示方法的参数 其中的.. 表示任意参数
-->
<!-- <aop:pointcut id="allmethod" expression="execution (* com.ffyc.sping.dao.AdminDao.insertAdmin(..))"/>-->
<aop:pointcut id="allmethod" expression="execution (* com.ffyc.sping.dao.*.*(..))"/>
<!--
配置通知和切入点
before 前置通知, 在方法执行之前 被调用
after 最终通知 相当于finally 无论方法是否成功执行完成,都会被调用
after-returning 后置通知 方法正常执行后被调用
after-throwing 异常通知,当方法中出现异常时,才会被调用
-->
<aop:aspect ref="commonUtil">
<!-- <aop:before method="saveLog" pointcut-ref="allmethod"></aop:before>-->
<!-- <aop:after method="saveLog" pointcut-ref="allmethod"></aop:after>-->
<!-- <aop:after-returning method="saveLog" pointcut-ref="allmethod"></aop:after-returning>-->
<!-- <aop:after-throwing method="saveLog" pointcut-ref="allmethod" throwing="e"></aop:after-throwing>-->
<!--环绕通知 更加灵活 强大 -->
<aop:around method="saveLog" pointcut-ref="allmethod"></aop:around>
</aop:aspect>
</aop:config>
</beans>
3> CommonUtil.java
package com.ffyc.sping.util;
import org.aspectj.lang.ProceedingJoinPoint;
import java.util.Arrays;
import java.util.Date;
public class CommonUtil {
// Joinpoint 表示实际方法 连接点 其他四种通知
// public void saveLog(JoinPoint joinpoint,Throwable e) {
// Object[] objects = joinpoint.getArgs(); // 获得连接点 中的参数
// System.out.println(Arrays.toString(objects));
// System.out.println("时间:" + new Date());
// System.out.println(e.getMessage());
// }
// 环绕通知测试
public void saveLog(ProceedingJoinPoint joinpoint) {
System.out.println("前置通知");
try {
Object[] objects = joinpoint.getArgs(); // 获得连接点 中的参数
System.out.println(Arrays.toString(objects));
System.out.println("时间:" + new Date());
// 调用目标的切入点
joinpoint.proceed(); // 不写就会没有异常通知
System.out.println("后置通知");
} catch (Throwable e) {
System.out.println(e.getMessage() + " 异常通知!!!");
}
System.out.println("最终通知");
}
}
3.2注解标签方式
1> spring.xml文件中开启自动代理
<!--开启自动代理-->
<aop:aspectj-autoproxy/>
2> CommonUtil.java
package com.ffyc.sping.util;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.springframework.stereotype.Component;
import java.util.Date;
/*
@Component 是一个通用的注解,用于将一个类标记为一个组件,
表示这个类是一个受Spring容器管理的Bean。
Spring容器会自动扫描并将这个类实例化为一个Bean,并将其纳入到容器的管理中。
*/
@Component
@Aspect //用于声明切面的注解
public class CommonUtil {
//环绕通知
// @Around(value = "execution(* com.ffyc.sping.dao.*.*(..))")
// public void saveLog(ProceedingJoinPoint joinPoint){
// try{
// System.out.println("前置通知");
// joinPoint.getArgs(); // 获得方法参数
// joinPoint.proceed();//调用目标的切入点, 方便获得切入点中错误
// System.out.println("后置通知");
// }catch (Throwable e){
// System.out.println("异常通知!!! "+e.getMessage());
// }
// System.out.println("最终通知");
// }
// @Before("execution(* com.ffyc.sping.dao.*.*(..))")
@After("execution(* com.ffyc.sping.dao.*.*(..))")
// @AfterReturning("execution(* com.ffyc.sping.dao.*.*(..))")
// @AfterThrowing(value = "execution(* com.ffyc.sping.dao.*.*(..))",throwing = "e")
public void saveLog() {
System.out.println("保存时间:" + new Date());
}
}
4.使用案例
事物处理: 开启事务, 关闭事务, 出现异常后回滚事务
权限判断: 在执行方法前, 判断是否具有权限
日志: 在执行前进日志处理