Spring面向切面编程(AOP)详解
面向切面编程(AOP)是Spring框架的另外一个重要的核心内容。
而在讲AOP之前,先来了解一下动态代理这个概念,因为AOP基于动态代理。
动态代理概念:在程序执行的过程中,创建代理对象。通过代理对象执行目标方法,在不影响原来的目标方法的前提下,增加额外的功能。就简单来说,就是在目标方法内部执行代码不变的情况下,增加额外的功能。
面向切面编程(AOP)概念:面向切面编程基于动态代理,其实就是动态代理的规范化,把动态代理的实现步骤、方法都定义好了,让开发人员使用一套统一的方式去使用动态代理。
前面的概念是不是很抽象呢?莫慌,下面会详细去理解面向切面编程。
面向切面编程,这个切面到底是什么呢?可以这样子去理解,就是需要添加到目标方法的额外功能,就像三明治,火腿片和鸡蛋片中间需要加入面包片一样,而目标方法就像火腿片和鸡蛋片,切面相当于面包片。这样子应该就可以更好的去理解面向切面编程了。
AOP中涉及到的术语
(1)切面(Aspect):表示增强功能,而非业务方法,常见的切面需要完成的功能有:日志,事务,统计信息,参数检查,权限验证。
(2)连接点(JoinPoint):连接业务方法和增强功能的位置,就是业务方法。
(3)切入点(Pointcut):指连接方法的集合。
(4)目标对象:给哪个类添加增强功能,哪个类就是目标对象。
(5)通知(Advice):通知表示切面功能执行的时间。
切面三要素:
(1)切面的功能代码
(2)切面的指向位置(Pointcut)
(3)切面的指向时间(Advice)
AOP实现使用到的框架
(1)Spring框架内部实现的AOP规范,当在pom.xml文件中加入spring依赖的时候,就已经添加进来。
(2)aspectJ:一个开源的专门做AOP的框架,而Spring框架中又集成了该框架,所以通过Spring框架就可以使用该框架的功能。
下面以切面三要素展开解释aspectJ框架的使用
(1)声明切面类
创建了切面类之后,若没有使用声明切面类的注解@Aspect的话,该类和普通的类没有任何的区别,所以需要在切面类的上面使用注解@Aspect声明切面类。
/**
* @Aspect:该注解声明下面的类为切面类,这个类当中有切面功能代码
* 位置:在类的上面定义
*/
//切面类
@Aspect
public class MyAspect {
}
(2)切面类的执行时间,aspectJ提供五个注解(三个掌握,两个了解)来代表执行时间
1)(掌握)@Before(前置通知)
语法格式:
@Before(value = "切入点表达式")
2)(掌握)@AfterReturning(后置通知)
语法格式
@AfterReturning(value = "切入点表达式",returning = "目标方法的返回值")
3)(掌握)@Around(环绕通知)
语法格式
@Around(value = "切入点表达式")
4)(了解)@AfterThrow(异常通知)
5)(了解)@After (最终通知)
这里需要解释的还有切入点表达式:
格式:
execution(访问权限 方法返回值 方法声明(参数)异常类型)
其中方法返回值和方法声明是必须要的,其他两个一般可以不写。
为了方便写切入点表达式,提供如下方便的写法:
(1)符号*:代表0到任意多个字符
(2)符号…:用在方法参数当中,表示任意多个参数,用在包名后,代表当前包及其子包路径。
使用aspectJ框架实现AOP的步骤如下:
(1)新建一个maven项目
(2)加入依赖(spring依赖和aspectJ依赖)
<!--spring依赖-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>4.3.7.RELEASE</version>
</dependency>
<!--aspectJ依赖-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aspects</artifactId>
<version>4.3.7.RELEASE</version>
</dependency>
(3)创建目标接口和实现类
(4)创建切面类
(5)创建spring配置文件
1)声明目标类对象
2)声明切面类对象
3)声明自动代理生成器标签
<!-- 声明自动代理生成器-->
<aop:aspectj-autoproxy />
(3)切入点
@Pointcut:定义和管理切入点,如果项目中有多个相同的切入点表达式,就可以使用该注解
该如何使用@Pointcut标签呢?
需要定义一个方法,该方法为私有,并且不需要方法体,此时这个方法的名称就是切入点表达式的别名,其他需要使用该切入点表达式的注解,只需要使用该别名即可。