Spring-AOP
AOP(面向切面编程)开发思想:主要是指提取出多个功能之间的共性部分(重复)。
AOP相关概念
-
连接点:方法
-
切入点:被挖掉共性功能的方法
-
通知:共性功能提出来单独成一个类
-
切面:描述切入点和通知的关系(例如在代码的那个位置被挖走)
-
目标对象:被挖掉功能的方法所属的类
-
织入:将共性功能放回简化功能中组成完整的功能。这是一个动态过程
-
代理:织入过程运行的位置。这个位置是原始的目标对象创建的代理类
AOP开发过程
-
开发阶段(开发者完成)
-
正常的制作程序
-
将非共性功能开发到对应的目标对象类中,并制作成切入点方法
-
将共性功能独立开发出来,制作成通知
-
在配置文件中,声明切入点
-
在配置文件中,声明切入点与通知间的关系(含通知类型),即切面
-
-
运行阶段(AOP完成)
-
Spring容器加载配置文件,监控所有配置的切入点方法的执行
-
当监控到切入点方法被运行,使用代理机制,动态创建目标对象的代理对象,根据通知类别,在代理对象的对应位置将通知对应的功能织入,完成完整的代码逻辑并运行
-
AOP相关坐标
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.9.4</version>
</dependency>
<!--文件中还要添加命名空间-->
AOP配置(XML方式)
-
AOP配置简例
<!--aop配置 可以配置多个,都会生效--> <aop:config> <!--配置切入点--> <aop:pointcut id="pt" expression="execution(* *..*())"/> <!--配置切面--> <aop:aspect ref="填写通知(共性功能)的bean"> <!—通知与切入点之间的关系--> <aop:before method="通知中具体的一个方法" pointcut-ref="pt"/> </aop:aspect> </aop:config>
-
切入点(本质就是一个方法)
-
<aop:pointcut id="pt" expression="execution(* *..*())"/>
-
关键字(访问修饰符 返回值 包名.类名(接口名).方法名(参数)异常名)
-
例子:
execution(public User com.itheima.service.UserService.findById(int))
-
*:单个独立的任意符号,可以独立出现,也可以作为前缀或者后缀的匹配符出现
execution(public * com.itheima.*.UserService.find*(*))
-
… :多个连续的任意符号,可以独立出现,常用于简化包名与参数的书写
execution(public User com..UserService.findById(..))
-
+:专用于匹配子类类型
execution(* *..*Service+.*(..))
-
-
切入点的配置位置
-
<aop:config> <!--配置公共切入点--> <aop:pointcut id="pt1" expression="execution(* *(..))"/> <aop:aspect ref="myAdvice"> <!--配置局部切入点--> <aop:pointcut id="pt2" expression="execution(* *(..))"/> <!--引用公共切入点--> <aop:before method="logAdvice" pointcut-ref="pt1"/> <!--引用局部切入点--> <aop:before method="logAdvice" pointcut-ref="pt2"/> <!--直接配置切入点--> <aop:before method="logAdvice" pointcut="execution(* *(..))"/> </aop:aspect> </aop:config>
-
通知类型
-
前置通知 aop:before
-
原始方法执行前执行,如果通知中抛出异常,阻止原始方法运行
-
应用场景:数据检验
-
<aop:aspect ref="adviceId"> <aop:before method="methodName" pointcut="……"/> </aop:aspect>
-
标签内属性
- method :在通知类中设置当前通知类别对应的方法
- pointcut :设置当前通知对应的切入点表达式,与pointcut-ref属性冲突
- pointcut-ref :设置当前通知对应的切入点id,与pointcut属性冲突
-
-
后置通知 aop:after
-
原始方法执行后执行,无论原始方法中是否出现异常,都将执行通知
-
应用场景:处理执行现场
-
<aop:aspect ref="adviceId"> <aop:after method="methodName" pointcut="……"/> </aop:aspect>
-
标签内属性同上
-
-
返回后通知 aop:after-returning
-
原始方法正常执行完毕并返回结果后执行,如果原始方法中抛出异常,无法执行
-
应用场景:返回值的相关数据处理
-
<aop:aspect ref="adviceId"> <aop:after-returning method="methodName" pointcut="……"/> </aop:aspect>
-
-
抛出异常通知 aop:after-throwing
-
原始方法抛出异常后执行,如果原始方法没有抛出异常,无法执行
-
应用场景:对原始方法中出现的异常信息处理
-
<aop:aspect ref="adviceId"> <aop:after-throwing method="methodName" pointcut="……" throwing="t"/> </aop:aspect> <!--在通知方法中参数写上Throwable t,可以得到返回的异常对象-->
-
-
环绕通知 aop:around
-
在原始方法执行前后均有对应执行执行,还可以阻止原始方法的执行
-
应用场景:做任何事情
-
<aop:aspect ref="adviceId"> <aop:around method="methodName" pointcut="……"/></aop:aspect>
-
环绕通知是在原始方法的前后添加功能,在环绕通知中,存在对原始方法的显式调用
public Object around(ProceedingJoinPoint pjp) throws Throwable { Object ret = pjp.proceed(); return ret; }
- 上诉代码说明:必须设定参数ProceedingJoinPoint对象,通过该对象调用proceed()方法,实现对原始方法的调用。
- 使用proceed()方法调用原始方法时,因无法预知原 始方法运行过程中是否会出现异常,强制抛出Throwable对象,封装原始方法中可能出现的异常信息
-
通知中获取参数
获取参数方法一
使用JoinPoint对象
//通知方法
public void before(JoinPoint jp) throws Throwable {
Object[] args = jp.getArgs();
}
获取参数方法二
在通知方法中:参数和原始方法写同类型
在xml配置中稍作修改如下:
<aop:before method = "before"
arg-names = "y,a"
pointcut = "execution(* *..*(int,int)) &$amp; args(x,y)"/>
<!--使用arg-names可以交换参数顺序-->
<!--在字符中,&用转义字符&-->
AOP配置(注解)
AOP配置的注解方式,所有注解全部配置在通知上
AOP注解驱动扫描:@EnableAspectJAutoProxy配置在spring核心配置类上
AOP注解驱动扫描:aop:aspectj-autoproxy/
- @Aspect 切面注解,配置在通知类上。此类同时还要配置@Component
- @Pointcut(“execution(”),配置在一个方法上,此方法没有方法体,没有参数,没有返回值
- @Before(“此处填写切入点注解配置的那个无参无返回值的方法名”),此外还有@After @AfterReturning @AfterThrowing @Around