AOP
(Aspect Oriented Programing)面向切面编程,一种编程范式,隶属于软工范畴,指导开发者如何组织程序结构
目标:将软件开发由手工制作走向半自动化/全自动化阶段,实现“插拔式组件体系结构”搭建
AOP优势
-
提高代码的可重用性
-
业务代码编码更简洁
-
业务代码维护更高效
-
业务功能扩展更便捷
-
AOP入门
-
Joinpoint(连接点):就是方法
-
Pointcut(切入点):就是挖掉共性功能的方法
-
Advice(通知):就是共性功能,最终以一个方法的形式呈现
-
Aspect(切面):就是共性功能与挖的位置的对应关系
-
Target(目标对象):就是挖掉功能的方法对应的类产生的对象,这种对象是无法直接完成最终工作的
-
Weaving(织入):就是将挖掉的功能回填的动态过程
-
Proxy(代理):目标对象无法直接完成工作,需要对其进行功能回填,通过创建原始对象的代理对象实现
-
Introduction(引入/引介) :就是对原始对象无中生有的添加成员变量或成员方法
-
AOP开发过程
-
开发阶段(开发者完成)
-
正常的制作程序
-
将非共性功能开发到对应的目标对象类中,并制作成切入点方法
-
将共性功能独立开发出来,制作成通知
-
在配置文件中,声明切入点
-
在配置文件中,声明切入点与通知间的关系(含通知类型),即切面
-
-
运行阶段(AOP完成)
-
Spring容器加载配置文件,监控所有配置的切入点方法的执行
-
当监控到切入点方法被运行,使用代理机制,动态创建目标对象的代理对象,根据通知类别,在代理对象的对应位置将通知对应的功能织入,完成完整的代码逻辑并运行
-
AOP开发方式
-
XML方式
-
XML+注解方式
-
注解方式
入门案例步骤
1.导入相关坐标
2.确认要抽取的功能,并将其制作成方法保存到专用的类中,删除原始业务中对应的功能
3.将所有进行AOP操作的资源加载到IoC容器中
4.使用配置的方式描述被抽取功能的位置,并描述被抽取功能与对应位置的关系
5.运行程序
步骤一 导入坐标
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.9.4</version>
</dependency>
步骤二 在业务层抽取通用代码
步骤三 把通知加入spring容器管理
步骤四 在配置文件中配置aop的配置
<!--aop配置-->
<aop:config>
<!--配置切入点-->
<aop:pointcut id="pt" expression="execution(* *..*())"/>
<!--配置切面-->
<aop:aspect ref="myAdvice">
<!—通知与切入点之间的关系-->
<aop:before method="logAdvice" pointcut-ref="pt"/>
</aop:aspect>
</aop:config>
AOP配置(XML)
aop:config
3.2.3)aop:pointcut
-
名称:aop:config
-
类型:标签
-
归属:beans标签
-
作用:设置AOP
-
格式:
-
<beans> <aop:config>……</aop:config> <aop:config>……</aop:config> </beans>
-
说明:一个beans标签中可以配置多个aop:config标签
-
名称: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: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 :切入点表达式
-
切入点表达式的组成
3.7.2)aop:after
<aop:aspect ref="adviceId">
<aop:before method="methodName" pointcut="……"/>
</aop:aspect>
3.7.3)aop:after-returning
<aop:aspect ref="adviceId">
<aop:after-returning method="methodName" pointcut="……"/>
</aop:aspect>
3.7.4)aop:after-throwing
<aop:aspect ref="adviceId">
<aop:after-throwing method="methodName" pointcut="……"/>
</aop:aspect>
3.7.5)aop:around
<aop:aspect ref="adviceId">
<aop:around method="methodName" pointcut="……"/>
</aop:aspect>
环绕通知的开发方式
public Object around(ProceedingJoinPoint pjp) throws Throwable {
Object ret = pjp.proceed();
return ret;
}
-
切入点描述的是某个方法
-
切入点表达式是一个快速匹配方法描述的通配格式,类似于正则表达式
-
关键字(访问修饰符 返回值 包名.类名.方法名(参数)异常名)
关键字:描述表达式的匹配模式(参看关键字列表)
访问修饰符:方法的访问控制权限修饰符
类名:方法所在的类(此处可以配置接口名称)
异常:方法定义中指定抛出的异常
-
范例:
execution(public User com.itheima.service.UserService.findById(int))
切入点表达式——通配符
-
*:单个独立的任意符号,可以独立出现,也可以作为前缀或者后缀的匹配符出现
-
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的通知类型共5种
-
前置通知:原始方法执行前执行,如果通知中抛出异常,阻止原始方法运行
应用:数据校验
-
后置通知:原始方法执行后执行,无论原始方法中是否出现异常,都将执行通知
应用:现场清理
-
返回后通知:原始方法正常执行完毕并返回结果后执行,如果原始方法中抛出异常,无法执行
应用:返回值相关数据处理
-
抛出异常后通知:原始方法抛出异常后执行,如果原始方法没有抛出异常,无法执行
应用:对原始方法中出现的异常信息进行处理
-
环绕通知:在原始方法执行前后均有对应执行执行,还可以阻止原始方法的执行
应用:十分强大,可以做任何事情
-
-
aop:before
-
名称:aop:before
-
类型:标签
-
归属:aop:aspect标签
-
作用:设置前置通知
-
格式:
-
<aop:aspect ref="adviceId"> <aop:before method="methodName" pointcut="……"/> </aop:aspect>
-
说明:一个aop:aspect标签中可以配置多个aop:before标签
-
基本属性:
-
method :在通知类中设置当前通知类别对应的方法
-
pointcut :设置当前通知对应的切入点表达式,与pointcut-ref属性冲突
-
pointcut-ref :设置当前通知对应的切入点id,与pointcut属性冲突
-
-
名称:aop:after
-
类型:标签
-
归属:aop:aspect标签
-
作用:设置后置通知
-
格式:
<aop:aspect ref="adviceId"> <aop:after method="methodName" pointcut="……"/> </aop:aspect>
-
说明:一个aop:aspect标签中可以配置多个aop:after标签
-
基本属性:
-
method :在通知类中设置当前通知类别对应的方法
-
pointcut :设置当前通知对应的切入点表达式,与pointcut-ref属性冲突
-
pointcut-ref :设置当前通知对应的切入点id,与pointcut属性冲突
-
-
名称:aop:after-returning
-
类型:标签
-
归属:aop:aspect标签
-
作用:设置返回后通知
-
格式:
<aop:aspect ref="adviceId"> <aop:after-returning method="methodName" pointcut="……"/> </aop:aspect>
-
说明:一个aop:aspect标签中可以配置多个aop:after-returning标签
-
基本属性:
-
method :在通知类中设置当前通知类别对应的方法
-
pointcut :设置当前通知对应的切入点表达式,与pointcut-ref属性冲突
-
pointcut-ref :设置当前通知对应的切入点id,与pointcut属性冲突
-
-
名称:aop:after-throwing
-
类型:标签
-
归属:aop:aspect标签
-
作用:设置抛出异常后通知
-
格式:
<aop:aspect ref="adviceId"> <aop:after-throwing method="methodName" pointcut="……"/> </aop:aspect>
-
说明:一个aop:aspect标签中可以配置多个aop:after-throwing标签
-
基本属性:
-
method :在通知类中设置当前通知类别对应的方法
-
pointcut :设置当前通知对应的切入点表达式,与pointcut-ref属性冲突
-
pointcut-ref :设置当前通知对应的切入点id,与pointcut属性冲突
-
-
名称:aop:around
-
类型:标签
-
归属:aop:aspect标签
-
作用:设置环绕通知
-
格式:
-
<aop:aspect ref="adviceId"> <aop:around method="methodName" pointcut="……"/> </aop:aspect>
-
说明:一个aop:aspect标签中可以配置多个aop:around标签
-
基本属性:
-
method :在通知类中设置当前通知类别对应的方法
-
pointcut :设置当前通知对应的切入点表达式,与pointcut-ref属性冲突
-
pointcut-ref :设置当前通知对应的切入点id,与pointcut属性冲突
-
-
环绕通知是在原始方法的前后添加功能,在环绕通知中,存在对原始方法的显式调用
-
public Object around(ProceedingJoinPoint pjp) throws Throwable { Object ret = pjp.proceed(); return ret; }