一、 Spring中AOP的配置方式
在Spring的AOP配置中,也和IoC配置一样,支持3类配置方式。
第一类:使用XML配置
第二类:使用XML+注解组合配置
第三类:使用纯注解配置
二、Spring中三种配置AOP方法代码实现
需求:横切逻辑代码是打印日志,希望把打印日志的逻辑织入到目标方法的特定位置(service层transfer方法)
本文参考资料《Spring高级源码笔记》,需要的同学添加助理VX:
C18173184271,备注一下从哪个平台加我的工作几年了!
免费获取
1.XML 模式
Spring
是模块化开发的框架,使用aop就引入aop的jar
- 坐标
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aop</artifactId>
<version>5.1.12.RELEASE</version>
</dependency>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.9.4</version>
</dependency>
- AOP 核心配置
<!--
Spring基于XML的AOP配置前期准备:在spring的配置文件中加入aop的约束
xmlns:aop="http://www.springframework.org/schema/aop" http://www.springframework.org/schema/aop https://www.springframework.org/schema/aop/spring-aop.xsd
Spring基于XML的AOP配置步骤:
第一步:把通知Bean交给Spring管理
第二步:使用aop:config开始aop的配置
第三步:使用aop:aspect配置切面
第四步:使用对应的标签配置通知的类型
入门案例采用前置通知,标签为aop:before
-->
<!--把通知bean交给spring来管理-->
<bean id="logUtil" class="com.lagou.utils.LogUtil"></bean>
<!--开始aop的配置-->
<aop:config>
<!--配置切⾯-->
<aop:aspect id="logAdvice" ref="logUtil">
<!--配置前置通知-->
<aop:before method="printLog" pointcut="execution(public *
com.lagou.service.impl.TransferServiceImpl.updateAccountByCardNo(com.lagou
.pojo.Account))"></aop:before>
</aop:aspect>
</aop:config>
- 细节
- 关于切入点表达式
上述配置实现了对TransferServiceImpl
的updateAccountByCardNo
方法进行增强,在其执行之前,输出了记录日志的语句。这里面,我们接触了一个比较陌生的名称:切入点表达式,它是做什么的呢?我们往下看。 - 概念及作用
切入点表达式,也称之为AspectJ切入点表达式,指的是遵循特定语法结构的字符串,其作用是用于对符合语法格式的连接点进行增强。它是AspectJ表达式的一部分。 - 关于AspectJ
AspectJ是一个基于Java语言的AOP框架,Spring框架从2.0版本之后集成了AspectJ框架中切入点表达式的部分,开始支持AspectJ切入点表达式。 - 切入点表达式使用示例
- 关于切入点表达式
全限定法名 访问修饰符 返回值 包名.包名.包名.类名.方法名(参数列表)
全匹配方式:
public void
com.lagou.service.impl.TransferServiceImpl.updateAccountByCardNo(c
om.lagou.pojo.Account)
访问修饰符可以省略
void
com.lagou.service.impl.TransferServiceImpl.updateAccountByCardNo(c
om.lagou.pojo.Account)
返回值可以使用*,表示任意返回值
*
com.lagou.service.impl.TransferServiceImpl.updateAccountByCardNo(c
om.lagou.pojo.Account)
包名可以使用.表示任意包,但是有几级包,必须写几个
*
....TransferServiceImpl.updateAccountByCardNo(com.lagou.pojo.Accou
nt)
包名可以使用..表示当前包及其子包
*
..TransferServiceImpl.updateAccountByCardNo(com.lagou.pojo.Account
)
类名和方法名,都可以使用.表示任意类,任意方法
* ...(com.lagou.pojo.Account)
参数列表,可以使用具体类型
基本类型直接写类型名称 : int
引用类型必须写全限定类名:java.lang.String
参数列表可以使用*,表示任意参数类型,但是必须有参数
* *..*.*(*)
参数列表可以使用..,表示有无参数均可。有参数可以是任意类型
* *..*.*(..)
全通配方式:
* *..*.*(..)
- 改变代理方式的配置
在前面我们已经说了,Spring在选择创建代理对象时,会根据被代理对象的实际情况来选择的。被代理对象实现了接口,则采用基于接口的动态代理。当被代理对象没有实现任何接口的时候,Spring会自动切换到基于子类的动态代理方式。
但是我们都知道,无论被代理对象是否实现接口,只要不是final修饰的类都可以采用cglib提供的方式创建代理对象。所以Spring也考虑到了这个情况,提供了配置的方式实现强制使用基于子类的动态代理(即cglib的方式),配置的方式有两种 - 使用
aop:config
标签配置
<aop:config proxy-target-class="true">
- 使用
aop:aspectj-autoproxy
标签配置
<!--此标签是基于XML和注解组合配置AOP时的必备标签,表示Spring开启注解配置AOP的支持-->
<aop:aspectj-autoproxy proxy-target-class="true"></aop:aspectjautoproxy>
- 五种通知类型
- 前置通知
配置方式:aop:before
标签
- 前置通知
<!--
作用:
用于配置前置通知。
出现位置:
它只能出现在aop:aspect标签内部
属性:
method:用于指定前置通知的方法名称
pointcut:用于指定切入点表达式
pointcut-ref:用于指定切入点表达式的引用
-->
<aop:before method="printLog" pointcut-ref="pointcut1">
</aop:before>
执行时机
前置通知永远都会在切入点方法(业务核心方法)执行之前执行。
细节
前置通知可以获取切入点方法的参数,并对其进行增强。
- 正常执行时通知
配置方式
<!--
作用:
用于配置正常执行时通知
出现位置:
它只能出现在aop:aspect标签内部
属性:
method:用于指定后置通知的方法名称
pointcut:用于指定切入点表达式
pointcut-ref:用于指定切入点表达式的引用
-->
<aop:after-returning method="afterReturningPrintLog" pointcutref="pt1"></aop:after-returning>
- 异常通知
配置方式
<!--
作用:
用于配置异常通知。
出现位置:
它只能出现在aop:aspect标签内部
属性:
method:用于指定异常通知的方法名称
pointcut:用于指定切入点表达式
pointcut-ref:用于指定切入点表达式的引用
-->
<aop:after-throwing method="afterThrowingPrintLog" pointcut-ref="pt1"
></aop:after-throwing>
执行时机
异常通知的执行时机是在切入点方法(业务核心方法)执行产生异常之后,异常通知执行。如果切入点方法执行没有产生异常,则异常通知不会执行。
细节
异常通知不仅可以获取切入点方法执行的参数,也可以获取切入点方法执行产生的异常信息。
- 最终通知
配置方式
<!--
作用:
用于配置最终通知。
出现位置:
它只能出现在aop:aspect标签内部
属性:
method:用于指定最终通知的方法名称
pointcut:用于指定切入点表达式
pointcut-ref:用于指定切入点表达式的引用
-->
<aop:after method="afterPrintLog" pointcut-ref="pt1"></aop:after>
执行时机
最终通知的执行时机是在切入点方法(业务核心方法)执行完成之后,切入点方法返回之前执行。换句话说,无论切入点方法执行是否产生异常,它都会在返回之前执行。
细节
最终通知执行时,可以获取到通知方法的参数。同时它可以做一些清理操作。
- 环绕通知
配置方式
<!--
作用:
用于配置环绕通知。
出现位置:
它只能出现在aop:aspect标签内部
属性:
method:用于指定环绕通知的方法名称
pointcut:用于指定切入点表达式
pointcut-ref:用于指定切入点表达式的引用
-->
<aop:around method="aroundPrintLog" pointcut-ref="pt1"></aop:around>
2.XML+注解模式
- XML 中开启 Spring 对注解 AOP 的支持
<!--开启spring对注解aop的支持-->
<aop:aspectj-autoproxy/>
- 示例
/**
* 模拟记录日志
* @author 应癫
*/
@Component
@Aspect
public class LogUtil {
/**
* 我们在xml中已经使用了通用切入点表达式,供多个切面使用,那么在注解中如何使用呢?
* 第一步:编写一个方法
* 第二步:在方法使用@Pointcut注解
* 第三步:给注解的value属性提供切入点表达式
* 细节:
* 1.在引用切入点表达式时,必须是方法名+(),例如"pointcut()"。
* 2.在当前切面中使用,可以直接写方法名。在其他切面中使用必须是全限定方法名。
*/
@Pointcut("execution(* com.lagou.service.impl.*.*(..))")
public void pointcut(){}
@Before("pointcut()")
public void beforePrintLog(JoinPoint jp){
Object[] args = jp.getArgs();
System.out.println("前置通知:beforePrintLog,参数是:"+ Arrays.toString(args));
}
@AfterReturning(value = "pointcut()",returning = "rtValue")
public void afterReturningPrintLog(Object rtValue){
System.out.println("后置通知:afterReturningPrintLog,返回值是:"+rtValue);
}
@AfterThrowing(value = "pointcut()",throwing = "e")
public void afterThrowingPrintLog(Throwable e){
System.out.println("异常通知:afterThrowingPrintLog,异常是:"+e);
}
@After("pointcut()")
public void afterPrintLog(){
System.out.println("最终通知:afterPrintLog");
}
/**
* 环绕通知
* @param pjp
* @return
*/
@Around("pointcut()")
public Object aroundPrintLog(ProceedingJoinPoint pjp){
//定义返回值
Object rtValue = null;
try{
//前置通知
System.out.println("前置通知");
//1.获取参数
Object[] args = pjp.getArgs();
//2.执⾏切⼊点⽅法
rtValue = pjp.proceed(args);
//后置通知
System.out.println("后置通知");
}catch (Throwable t){
//异常通知
System.out.println("异常通知");
t.printStackTrace();
}finally {
//最终通知
System.out.println("最终通知");
}
return rtValue;
}
}
3.注解模式
在使用注解驱动开发aop时,我们要明确的就是,是注解替换掉配置文件中的下面这行配置:
<!--开启spring对注解aop的支持-->
<aop:aspectj-autoproxy/>
在配置类中使用如下注解进行替换上述配置
/**
* @author 应癫
*/
@Configuration
@ComponentScan("com.lagou")
@EnableAspectJAutoProxy //开启spring对注解AOP的支持
public class SpringConfiguration {
}
如果你需要这份完整版的《Spring高级源码笔记》
,只需你多多支持我这篇文章。
多多支持,即可免费获取资料——三连之后(承诺:100%免费)
快速入手通道:添加助理VX:C18173184271,备注一下从哪个平台加我的工作几年了!
免费获取!诚意满满!!!
Spring面试专题文章点击这里!!!