在开始使用Spring的AOP之前我们需要在bean.xml中引入aop约束,并在pom.xml导入依赖
<?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"> <!--........--> </beans> <dependencies> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-context</artifactId> <version>5.0.2.RELEASE</version> </dependency> <dependency> <groupId>org.aspectj</groupId> <artifactId>aspectjweaver</artifactId> <version>1.8.7</version> </dependency> </dependencies>1.把通知类用bean标签配置起来
<!-- 配置Logger类 --> <bean id="logger" class="com.itheima.utils.Logger"></bean>2.使用aop:config申明aop配置
<!--配置AOP--> <aop:config> <!--配置代码--> </aop:config>3.使用aop:aspect配置切面
<!--配置AOP--> <aop:config> <!--配置切面 --> <aop:aspect id="logAdvice" ref="logger"> <!--配置通知类型--> </aop:aspect> </aop:config>4.使用aop:pointcut配置切入点(要增强的方法)表达式
<!--配置AOP--> <aop:config> <aop:pointcut pointcut="execution(* com.itheima.service.impl.*.*(..))" id="pt1"> <!--配置切面 --> <aop:aspect id="logAdvice" ref="logger"> <!--配置通知类型--> </aop:aspect> </aop:config>切入点表达式的写法: 关键字:execution(表达式) 表达式:访问修饰符 返回值 包名.包名.包名…类名.方法名(参数列表)
标准的表达式写法:public void com.itheima.service.impl.AccountServiceImpl.saveAccount() 访问修饰符可以省略:void com.itheima.service.impl.AccountServiceImpl.saveAccount()
返回值可以使用通配符,表示任意返回值 * com.itheima.service.impl.AccountServiceImpl.saveAccount()
包名可以使用通配符,表示任意包。但是有几级包,就需要写几个*. * *.*.*.*.AccountServiceImpl.saveAccount())
包名可以使用…表示当前包及其子包 * *…AccountServiceImpl.saveAccount()
类名和方法名都可以使用*来实现通配 * *…*.*()
参数列表:
可以直接写数据类型: 基本类型直接写名称:int引用类型写包名.类名的方式:java.lang.String 可以使用通配符*表示任意类型,但是必须有参数可以使用…表示有无参数均可,有参数可以是任意类型全通配写法: * *…*.*(…)实际开发中切入点表达式的通常写法: 切到业务层实现类下的所有方法:* com.itheima.service.impl.*.*(…)
5.使用aop:xxx配置对应的通知类型
<!--配置AOP--> <aop:config> <!--务必写在配置切面之前,这是spring规定好的--> <aop:pointcut expression="execution(* com.itheima.service.impl.*.*(..))" id="pt1"></aop:pointcut> <!--配置切面 --> <aop:aspect id="logAdvice" ref="logger"> <aop:before method="printLog" pointcut-ref="pt1"></aop:before> <!--aop:pointcut和aop:before也可以合为一句,不过在对一个切入点配置多个aop:xxx的时候,分开写的优势就体现了,即不用多次写切入点表达式--> <!-- <aop:before method="printLog" pointcut="execution(* com.itheima.service.impl.*.*(..))"></aop:before> --> </aop:aspect> </aop:config>aop:xxx: aop:before 作用:用于配置前置通知
aop:after-returning 作用:用于配置后置通知
aop:after-throwing 作用:配置异常通知
aop:after 配置最终通知
以上这些可以类比java的try-catch执行机制来理解。
环绕通知的通知类写法和上面不同,但是在xml中的配置还是差不多的,使用aop:around配置对应的通知类型。环绕通知可以手动控制增强代码什么时候执行,而不限于上面的前置、后置。通常情况下around都是单独使用的,也就是对要增强的方法不用再配置前置、后置之类的通知了。
spring 框架为我们提供了一个接口:ProceedingJoinPoint,它可以作为环绕通知的方法参数。在环绕通知执行时,spring框架会为我们提供该接口的实现类对象,我们直接使用就行。例如对上面的例子进行改写:
package com.chester.utils; import org.aspectj.lang.ProceedingJoinPoint; /** * 用于记录日志的工具类,它里面提供了公共的代码 */ public class Logger { /** * 用于打印日志:计划让其在切入点方法执行之前执行(切入点方法就是业务层方法) */ public void printLog(ProcessingJoinPoint pjp){ try{ System.out.println("Logger类中的pringLog方法开始记录日志了。。。"); pjp.proceed(); System.out.println("Logger类中的pringLog方法结束记录日志了。。。"); }catch(Throwable e){ System.out.println("运行出错"); e.printStackTrace(); }finally{ System.out.println("Logger类中的pringLog方法完成记录日志了。。。"); } } }现在在xml进行配置:
<!-- 配置Logger类 --> <bean id="logger" class="com.chester.utils.Logger"></bean> <aop:config> <aop:pointcut expression="execution(* com.chester.service.impl.*.*(..))" id="pt1"></aop:pointcut> <!--配置切面 --> <aop:aspect id="logAdvice" ref="logger"> <aop:around method="printLog" pointcut-ref="pt1"></aop:around> </aop:aspect> </aop:config>