aop原理及理解

时间:2023-03-09 02:54:10
aop原理及理解
  • 概念

Aspect Oriented Programming,面向切面编程,实际上它是一个规范、一种设计思路,总之是抽象的。

先上图

aop原理及理解

  • 使用目的

从项目结构上来说

对业务逻辑的各个部分进行隔离,降低业务逻辑的耦合度

从开发的角度上说

减少重复编码,提高程序的可重用性,提高了开发的效率

总结简单的讲:就是那些与业务无关,却为业务模块所共同调用的逻辑封装起来,便于减少系统的重复代码,降低模块之间的耦合度,并有利于未来的可操作性和可维护性。

  • 使用场景

日志记录,性能统计,安全控制,事务处理,异常处理等

  • 概念理解(仅指java中)

先上图

aop原理及理解

圆柱:原有的业务流程,从上至下执行

蓝色面:即切面,为指定的业务流程增加功能;交界处即切点,意味着将增加的功能插入到指定位置(单位是方法)

AOP核心概念

1、横切关注点

对哪些方法进行拦截,拦截后怎么处理,这些关注点称之为横切关注点,即公共封装的业务逻辑,如日志记录

2、切面(aspect)

类是对物体特征的抽象,切面就是对横切关注点的抽象

3、连接点(joinpoint)

被拦截到的点,因为Spring只支持方法类型的连接点,所以在Spring中连接点指的就是被拦截到的方法,实际上连接点还可以是字段或者构造器,在此不做深究

4、切入点(pointcut)

对连接点进行拦截的定义

5、通知(advice)

所谓通知指的就是指拦截到连接点之后要执行的代码,通知分为前置、后置、异常、最终、环绕通知五类

6、目标对象

代理的目标对象

7、织入(weave)

将切面应用到目标对象并导致代理对象创建的过程

8、引入(introduction)

在不修改代码的前提下,引入可以在运行期为类动态地添加一些方法或字段

  • spring aop实现方法

通常我们采用2种

基于注解

/**
* 系统服务组件Aspect切面Bean*/
//声明这是一个组件
@Component
//声明这是一个切面Bean
@Aspect
public class ServiceAspect { private final static Log log = LogFactory.getLog(ServiceAspect.class); //配置切入点,该方法无方法体,主要为方便同类中其他方法使用此处配置的切入点
@Pointcut("execution(* com.hyq.aop..*(..))")
public void aspect(){ } /*
* 配置前置通知,使用在方法aspect()上注册的切入点
* 同时接受JoinPoint切入点对象,可以没有该参数
*/
@Before("aspect()")
public void before(JoinPoint joinPoint){
System.out.println("执行before.....");
} //配置后置通知,使用在方法aspect()上注册的切入点
@After("aspect()")
public void after(JoinPoint joinPoint){
System.out.println("执行after.....");
} //配置环绕通知,使用在方法aspect()上注册的切入点
@Around("aspect()")
public void around(JoinPoint joinPoint){
long start = System.currentTimeMillis();
try {
((ProceedingJoinPoint) joinPoint).proceed();
long end = System.currentTimeMillis();
if(log.isInfoEnabled()){
log.info("around " + joinPoint + "\tUse time : " + (end - start) + " ms!");
}
} catch (Throwable e) {
long end = System.currentTimeMillis();
if(log.isInfoEnabled()){
log.info("around " + joinPoint + "\tUse time : " + (end - start) + " ms with exception : " + e.getMessage());
}
}
} //配置后置返回通知,使用在方法aspect()上注册的切入点
@AfterReturning("aspect()")
public void afterReturn(JoinPoint joinPoint){
if(log.isInfoEnabled()){
log.info("afterReturn " + joinPoint);
}
} //配置抛出异常后通知,使用在方法aspect()上注册的切入点
@AfterThrowing(pointcut="aspect()", throwing="ex")
public void afterThrow(JoinPoint joinPoint, Exception ex){
if(log.isInfoEnabled()){
log.info("afterThrow " + joinPoint + "\t" + ex.getMessage());
}
} }

基于配置

<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"
xmlns:context="http://www.springframework.org/schema/context"
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.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd"> <!--<context:component-scan base-package=""--> <!-- 定义目标对象 -->
<bean name="productDao" class="com.xxx.spring.springAop.dao.daoimp.ProductDaoImpl" /> <!-- 定义切面 -->
<bean name="myAspectXML" class="com.xxx.spring.springAop.AspectJ.MyAspectXML" />
<!-- 配置AOP 切面 -->
<aop:config>
<!-- 定义切点函数 -->
<aop:pointcut id="pointcut" expression="execution(* com.xxx.spring.springAop.dao.ProductDao.add(..))" /> <!-- 定义其他切点函数 -->
<aop:pointcut id="delPointcut" expression="execution(* com.xxx.spring.springAop.dao.ProductDao.delete(..))" /> <!-- 定义通知 order 定义优先级,值越小优先级越大-->
<aop:aspect ref="myAspectXML" order="0">
<!-- 定义通知
method 指定通知方法名,必须与MyAspectXML中的相同
pointcut 指定切点函数
-->
<aop:before method="before" pointcut-ref="pointcut" /> <!-- 后置通知 returning="returnVal" 定义返回值 必须与类中声明的名称一样-->
<aop:after-returning method="afterReturn" pointcut-ref="pointcut" returning="returnVal" /> <!-- 环绕通知 -->
<aop:around method="around" pointcut-ref="pointcut" /> <!--异常通知 throwing="throwable" 指定异常通知错误信息变量,必须与类中声明的名称一样-->
<aop:after-throwing method="afterThrowing" pointcut-ref="pointcut" throwing="throwable"/> <!--
method : 通知的方法(最终通知)
pointcut-ref : 通知应用到的切点方法
-->
<aop:after method="after" pointcut-ref="pointcut"/>
</aop:aspect>
</aop:config>
</beans>

重点介绍下pointcut(切入点) expression表达式解析及配置

方法参数匹配

args()

@args()

方法描述匹配

execution()

当前AOP代理对象类型匹配

this()

目标类匹配

target()

@target()

within()

@within()

标有此注解的方法匹配

@annotation()

其中execution 是用的最多的,其格式为:

execution(modifiers-pattern? ret-type-pattern declaring-type-pattern? name-pattern(param-pattern)throws-pattern?)

ret-type-pattern,name-pattern, parameters-pattern是必须的.

ret-type-pattern:可以为*表示任何返回值,全路径的类名等. 

name-pattern:指定方法名, *代表所有 

如:set*代表以set开头的所有方法. 

parameters-pattern:指定方法参数(声明的类型),(..)代表所有参数,(*)代表一个参数 

(*,String)代表第一个参数为任何值,第二个为String类型.

更多配置细节可查看官方文档:https://docs.spring.io/spring/docs/4.3.9.RELEASE/spring-framework-reference/html/aop.html#aop-introduction

  • 原理分析

简单的讲,即使用jdk或cglib的动态代理功能,对目标类生成代理类,然后在代理类中调用或执行额外的新增功能。

先上图

aop原理及理解

aop原理及理解

在此推荐看https://blog.****.net/luanlouis/article/details/51095702

详细说明了spring aop实现的大致逻辑和机制

另外推荐https://blog.****.net/luanlouis/article/details/51155821

通过解析spring源码来深入理解spring aop的实现