1 前言
上一篇写了Spring的xml注入
这一篇写一下Spring的AOP
2 本篇内容
AOP简述、AOP、AOP实现方式以及Aspectj的使用。
3 AOP简述
AOP,Aspect Oriented Programming,意思是面向切面编程,是Spring的一个主要特性,通过代理机制来实现。
4 AOP
4.1 AOP名词
首先了解一下一些名词。
- target 目标类,即被代理的类
- JoinPoint 连接点,即可能被拦截到的方法
- PointCut 切入点,即被增强的连接点
- advice 通知/增强,即想要加入的功能
- weaving 织入,把advice加入到target创建新的代理对象proxy的过程
- Aspect 切面,切入点与通知advice结合的地方(是一个类)
4.2 AOP理解
AOP我理解为 通过代理的方式给一个类加上一些功能,并将加上功能以后的类返回用户。
5 Aspectj实现AOP
5.1 AOP底层实现方式
AOP底层有两种实现:一种是使用JDK代理实现的,一种是通过Cglib实现的,JDK代理通过接口+实现类实现,Cglib代理通过实现类实现。但是两种实现比较麻烦,可以引入Aspectj包来简化实现AOP操作的过程。
5.2 导入Aspectj包、引入AOP
5.2.1 pom导入Aspectj依赖
<!-- https://mvnrepository.com/artifact/org.aspectj/aspectjweaver -->
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.9.5</version>
</dependency>
<!-- https://mvnrepository.com/artifact/aspectj/aspectjrt -->
<dependency>
<groupId>aspectj</groupId>
<artifactId>aspectjrt</artifactId>
<version>1.5.4</version>
</dependency>
5.2.2 引入Aop
引入aop后的beans声明如下。
<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-4.3.xsd">
5.3 Aspectj通知类型
Aspectj有五种通知类型
- before 前置通知
- after 最终通知
- afterReturning 后置通知(常规数据处理)
- afterThrowing 抛出异常通知(包装异常信息)
- around 环绕通知(方法执行前后都会执行,必须手动执行方法,可以做很多事情)
5.4 使用Aspectj
5.4.1 编写类
需要写目标接口Person,目标类PersonImpl,切面实现类MyAspectj,这里仅贴出MyAspectj的代码
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
public class MyAspectj {
public void myBefore(JoinPoint joinPoint) {
System.out.println("前置通知:"+ joinPoint.getSignature().getName());
}
public void myAfterReturning(JoinPoint joinPoint,Object ret) {
System.out.println("后置通知:"+ joinPoint.getSignature().getName()+"----->"+ret);
}
public void myAround(ProceedingJoinPoint joinPoint) throws Throwable {
System.out.println("环绕的前");
Object object=joinPoint.proceed();
System.out.println("环绕的后");
}
public void myAfterThrowing(JoinPoint joinPoint,Throwable e) {
System.out.println("抛出异常通知:"+e.getMessage());
}
public void myAfter(JoinPoint joinPoint) {
System.out.println("最终通知:"+joinPoint.getSignature().getName());
}
}
5.4.2 书写配置文件
使用时需要修改class参数为自己的路径。
<!-- 目标类 -->
<bean id="personTargetj" class="edu.spring.service.impl.PersonServiceImpl"></bean>
<!-- 切面类 -->
<bean id="personAspectj" class="edu.spring.aspect.MyAspectj"></bean>
<aop:config>
<!-- ref指向要包装的类 -->
<aop:aspect ref="personAspectj">
<!-- 通过expression确定切入点,后续通过id指定该切入点,指定的是接口中的方法 -->
<aop:pointcut expression="execution(* edu.spring.service.*.*(..))" id="aspectjCut"/>
<!-- 前置通知,传入参数:切入点,切入点通过poionCut-ref指定,填入切入点的id -->
<aop:before method="myBefore" pointcut-ref="aspectjCut"/>
<!-- 后置通知,传入参数:切入点,对象 切入点同上,对象使用returning指定,填入的值要与Aspectj实现类中的参数名一样 -->
<aop:after-returning method="myAfterReturning" pointcut-ref="aspectjCut" returning="ret"/>
<!-- 环绕通知,传入参数:切入点,切入点同上 -->
<aop:around method="myAround" pointcut-ref="aspectjCut"/>
<!-- 抛出异常通知,传入参数:切入点,异常,切入点同上,异常通过throwing指定,填入的值要与Aspectj实现类中的参数名一样 -->
<aop:after-throwing method="myAfterThrowing" pointcut-ref="aspectjCut" throwing="e"/>
<!-- 最终通知,传入参数:切入点,切入点同上 -->
<aop:after method="myAfter" pointcut-ref="aspectjCut"/>
</aop:aspect>
</aop:config>
5.4.3 运行结果
从结果可以看出各个通知的执行顺序,因为没有报错所以没有抛出异常通知。
顺序为 前置–>环绕的前–>方法执行–>最终通知–>环绕的后–>后置通知
5.4.4 execution表达式
在使用Aspectj的过程中,需要通过execution表达式来确定目标方法,适当书写execution表达式可以精准定位某个方法,当然也可以写得松散一点定位一整个类里面的所有方法。
例子:execution=(* edu.spring.service.*.*(..))
在这个例子中,第一个*号是返回类型,*表示所有类型,
edu.spring.service.*.*
这一段指定了路径,表示service包下的任意类、任意方法
(..)
表示返回值为任意返回值。
edu.spring
表示spring包
edu.spring..
表示spring及其子包
倒数第二个*
号是类名,最后一个*
号是方法名,可以用*Impl
匹配以Impl
结尾的,以User*
匹配以User
开头的。
()
中不填表示无参,填入一个int
表示一个整型参数,填入 int,int
表示两个整型参数。
6 结语
1.以上是我个人对于AOP的一些理解,如有不对之处还请指正。
2.csdn博客写星号要加上转义字符 \,或者写成代码块的形式。
3.有什么不明白的可以在评论区留言探讨一下。
4.会有一些红色的信息提示,暂时不做解决。