AOP 面向切面编程,一种编程的范式,开发的一种思路,重点在于组织程序的结构。
AOP也可以说是
一种程序的设计思路,设计模式。是将相同的代码做平级抽取,没有什么子父类联系。
AOP弥补了OOP的不足,基于OOP基础之上进行横向开发
-
uOOP规定程序开发以类为主体模型,一切围绕对象进行,完成某个任务先构建模型
-
uAOP程序开发主要关注基于OOP开发中的共性功能,一切围绕共性功能进行,完成某个任务先构建可能遇到的所有共性功能(当所有功能都开发出来也就没有共性与非共性之分)
AOP优势
- 提高代码的可重用性;
- 业务代码编码更简洁;
- 业务代码维护更高效;
- 业务功能扩展更便捷;
AOP核心概念 及 术语含义
1.- Joinpoint(连接点):就是方法
2.- Pointcut(切入点):就是挖掉共性功能的方法(要增强的方法)
3.- Advice(通知):就是共性功能,最终以一个方法的形式呈现(怎样增强,增强的内容)
4.- Aspect(切面):就是共性功能与挖的位置的对应关系(切入点+通知=切面)
5.- Target(目标对象):就是挖掉功能的方法对应的类产生的对象,这种对象是无法直接完成最终工作的
6- Weaving(织入):就是将挖掉的功能回填的动态过程
7- Proxy(代理):目标对象无法直接完成工作,需要对其进行功能回填,通过创建原始对象的代理对象实现
8- Introduction(引入/引介) :就是对原始对象无中生有的添加成员变量或成员方法
使用AOP 开发过程
开发阶段
1.制作切入点方法,
2.将共性的代码独立抽取出来,制作成通知
3.在配置文件中,声明切入点
4.在配置文件中,声明切入点与通知的关系(含通知类型),即切面
运行阶段
- Spring容器加载配置文件,监控所有配置的切入点方法的执行
- 当监控到切入点方法被运行,使用代理机制,动态创建目标对象的代理对象,根据通知类别,在代理对象的对应位置将通知对应的功能织入,完成完整的代码逻辑并运行
AOP开发方式
- XML方式
- XML+注解方式
- 注解方式
AOP开发 XML方式
1.导入相关坐标
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.9.4</version>
</dependency>
<!--3.开启AOP命名空间-->
<bean id="userService" class="com.itheima.service.impl.UserServiceImpl"/>
<!--2.配置共性功能成功spring控制的资源-->
<bean id="myAdvice" class="com.itheima.aop.AOPAdvice"/>
表明,是从UserServiceImpl抽取出来的
2.确认要抽取的功能,并将其制作成方法保存到专用的类中,删除原始
业务中对应的功能
public class AOPAdvice {
public void function(){
System.out.println("共性功能");
}
}
3.将所有进行AOP操作的资源加载到IoC容器中
class aop类中抽取出来的通知类的全类名 id 用来获取这个bean的id名称
<bean id="myAdvice" class="com.itheima.aop.AOPAdvice"/>
4.使用配置的方式描述被抽取功能的位置,并描述被抽取功能与对应位置的关系
<!--4.配置AOP-->
<aop:config>
<!--5.配置切入点-->
<aop:pointcut id="pt" expression="execution(* *..*(..))"/>
<!--6.配置切面(切入点与通知的关系)-->
<aop:aspect ref="myAdvice">
<!--7.配置具体的切入点对应通知中那个操作方法-->
<aop:before method="function" pointcut-ref="pt"/>
</aop:aspect>
</aop:config>
pointcut-ref是 pointcut id;
method 是 aop类中抽取出来的通知类的方法名
ref=“myAdvice” 是 bean 的id 用来找到这个aop类注入的bean
5.运行程序
失败:原因:没有运行主方法、没写坐标、maven配置错误
单词写错
记得在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
AOP配置(XML)
AspectJ
-
Aspect(切面)用于描述切入点与通知间的关系,是AOP编程中的一个概念
-
AspectJ是基于java语言对Aspect的实现
aop:config 标签
- 名称:aop:config
- 类型:标签
- 归属:beans标签
- 作用:设置AOP
格式:
<beans>
<aop:config>……</aop:config>
<aop:config>……</aop:config>
</beans>
说明:一个beans标签中可以配置多个aop:config标签
aop:aspect
-
名称:aop:aspect
-
类型:标签
-
归属:aop:config标签
-
作用:设置具体的AOP通知对应的切入点
-
格式:
<aop:config>
<aop:aspect ref="beanId">……</aop:aspect>
<aop:aspect ref="beanId">……</aop:aspect>
</aop:config>
- 说明:
一个aop:config标签中可以配置多个aop:aspect标签 - 基本属性:
- ref :通知所在的bean的id
aop:pointcut
- 名称:aop:pointcut
- 类型:标签
- 归属:aop:config标签、aop:aspect标签
- 作用:设置切入点
- 格式:
<aop:config>
<aop:pointcut id="pointcutId" expression="……"/>
<aop:aspect>
<aop:pointcut id="pointcutId" expression="……"/>
</aop:aspect>
</aop:config>
-
说明:
一个aop:config标签中可以配置多个aop:pointcut标签,且该标签可以配置在aop:aspect标签内 -
基本属性:
- id :识别切入点的名称
- expression :切入点表达式
切入点**
- 切入点描述的是某个方法
- 切入点表达式是一个快速匹配方法描述的通配格式,类似于正则表达式
切入点表达式的组成**
- 切入点描述的是某个方法
- 切入点表达式是一个快速匹配方法描述的通配格式,类似于正则表达式
关键字(访问修饰符 返回值 包名.类名.方法名(参数)异常名)
关键字:描述表达式的匹配模式(参看关键字列表)
访问修饰符:方法的访问控制权限修饰符
类名:方法所在的类(此处可以配置接口名称)
异常:方法定义中指定抛出的异常
范例:
execution(public User com.itheima.service.UserService.findById(int))
切入点表达式——关键字
-
execution :匹配执行指定方法
-
args :匹配带有指定参数类型的方法
-
within :……
-
this :……
-
target :……
-
@within :……
-
@target :……
-
@args :……
-
@annotation :……
-
bean :……
-
reference pointcut :……
切入点表达式——通配符
-
*:单个独立的任意符号,可以独立出现,也可以作为前缀或者后缀的匹配符出现
execution(public * com.itheima.*.UserService.find*(*))
匹配com.itheima包下的任意包中的UserService类或接口中所有find开头的带有一个参数的方法
-
… :多个连续的任意符号,可以独立出现,常用于简化包名与参数的书写
execution(public User com..UserService.findById(..))
匹配com包下的任意包中的UserService类或接口中所有名称为findById的方法
-
+:专用于匹配子类类型
execution(* *..*Service+.*(..))
切入点表达式——逻辑运算符
-
&& :连接两个切入点表达式,表示两个切入点表达式同时成立的匹配
-
|| :连接两个切入点表达式,表示两个切入点表达式成立任意一个的匹配
-
! :连接单个切入点表达式,表示该切入点表达式不成立的匹配
切入点表达式——范例
execution(* *(..))
execution(* *..*(..))
execution(* *..*.*(..))
execution(public * *..*.*(..))
execution(public int *..*.*(..))
execution(public void *..*.*(..))
execution(public void com..*.*(..))
execution(public void com..service.*.*(..))
execution(public void com.itheima.service.*.*(..))
execution(public void com.itheima.service.User*.*(..))
execution(public void com.itheima.service.*Service.*(..))
execution(public void com.itheima.service.UserService.*(..))
execution(public User com.itheima.service.UserService.find*(..))
execution(public User com.itheima.service.UserService.*Id(..))
execution(public User com.itheima.service.UserService.findById(..))
execution(public User com.itheima.service.UserService.findById(int))
execution(public User com.itheima.service.UserService.findById(int,int))
execution(public User com.itheima.service.UserService.findById(int,*))
execution(public User com.itheima.service.UserService.findById(*,int))
execution(public User com.itheima.service.UserService.findById())
execution(List com.itheima.service.*Service+.findAll(..))
切入点的三种配置方式
<aop:config>
<!--配置公共切入点-->
<aop:pointcut id="pt1" expression="execution(* *(..))"/>
<aop:aspect ref="myAdvice">
<!--配置局部切入点-->
<aop:pointcut id="pt2" expression="execution(* *(..))"/>
<!--引用公共切入点-->
<aop:before method="logAdvice" pointcut-ref="pt1"/>
<!--引用局部切入点-->
<aop:before method="logAdvice" pointcut-ref="pt2"/>
<!--直接配置切入点-->
<aop:before method="logAdvice" pointcut="execution(* *(..))"/>
</aop:aspect>
</aop:config>
切入点配置经验**
- 企业开发命名规范严格遵循规范文档进行
- 先为方法配置局部切入点
- 再抽取类中公共切入点
- 最后抽取全局切入点
- 代码走查过程中检测切入点是否存在越界性包含
- 代码走查过程中检测切入点是否存在非包含性进驻
- 设定AOP执行检测程序,在单元测试中监控通知被执行次数与预计次数是否匹配
- 设定完毕的切入点如果发生调整务必进行回归测试