在开始使用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>
一、编写公共代码制作成通知
package com.itheima.utils
/**
* 用于记录日志的工具类,它里面提供了公共的代码
*/
public class Logger {
/**
* 用于打印日志:计划让其在切入点方法执行之前执行(切入点方法就是业务层方法)
*/
public void printLog(){
System.out.println("Logger类中的pringLog方法开始记录日志了。。。");
}
}
二、编写配置文件
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>