AOP 是软件设计领域中的面向切面编程,它是面向对象编程的一种补充和完善
实际项目中我们通常将面向对象理解为一个静态过程(例如一个系统有多少模块,一个模块有哪些对象,对象有哪些属性),面向切面中包含一个一个动态过程(在对象运行时动态织入一些功能。)
面向切面应用案例:如图所示
实际项目中通常会将系统两大部分:核心关注点和非核心关注点
思考?
编程过程中首先要完成的是什么?核心关注点(核心业务)
非核心关注点如何切入到系统中?硬编码(违背OCP),AOP(推荐)
AOP就是要在基于OCP(开闭原则)在不改变原有系统核心业务代码的基础上动态添加一些扩展功能并可以控制对象的执行。
AOP实际项目应用场景
1.1. AOP底层原理实现分析?
AOP底层基于代理机制实现功能扩展:(了解)
1) 假如目标对象(被代理对象)实现接口,则底层默认采用JDK动态代理机制为目标对象创建代理对象(目标类和代理类会实现共同接口)
2) 假如目标对象(被代理对象)没有实现接口,则底层默认采用CGLIB代理机制为目标对象创建代理对象(默认创建的代理类会继承目标对象类型)。
1.1. AOP 相关术语
1.通知(Advice)
就是你想要的功能,也就是上面说的 安全,事物,日志等。你给先定义好把,然后在想用的地方用一下。
2.连接点(JoinPoint)
这个更好解释了,就是spring允许你使用通知的地方,那可真就多了,基本每个方法的前,后(两者都有也行),或抛出异常时都可以是连接点,spring只支持方法连接点.其他如aspectJ还可以让你在构造器或属性注入时都行,不过那不是咱关注的,只要记住,和方法有关的前前后后(抛出异常),都是连接点。
3.切入点(Pointcut)
上面说的连接点的基础上,来定义切入点,你的一个类里,有15个方法,那就有几十个连接点了对把,但是你并不想在所有方法附近都使用通知(使用叫织入,以后再说),你只想让其中的几个,在调用这几个方法之前,之后或者抛出异常时干点什么,那么就用切点来定义这几个方法,让切点来筛选连接点,选中那几个你想要的方法。
4.切面(Aspect)
切面是通知和切入点的结合。现在发现了吧,没连接点什么事情,连接点就是为了让你好理解切点,搞出来的,明白这个概念就行了。通知说明了干什么和什么时候干(什么时候通过方法名中的before,after,around等就能知道),而切入点说明了在哪干(指定到底是哪个方法),这就是一个完整的切面定义。
5.引入(introduction)
允许我们向现有的类添加新方法属性。这不就是把切面(也就是新方法属性:通知定义的)用到目标类中吗
6.目标(target)
引入中所提到的目标类,也就是要被通知的对象,也就是真正的业务逻辑,他可以在毫不知情的情况下,被咱们织入切面。而自己专注于业务本身的逻辑。
7.代理(proxy)
怎么实现整套aop机制的,都是通过代理,这个一会给细说。
8.织入(weaving)
把切面应用到目标对象来创建新的代理对象的过程。有3种方式,spring采用的是运行时,为什么是运行时,后面解释。
关键就是:切点定义了哪些连接点会得到通知
基于xml配置的实例:
实现步骤
1、spring配置文件中配置切入点和切面、通知
2、spring配置文件中切面对象和主要业务交由spring管理
将核心业务对象由spring管理
将非核心业务交由spring管理
<!-- AOP配置(切入点,切面) -->
<aop:config>
<!-- 配置切入点(bean,@annotation,within) -->
<aop:pointcut
expression="within(spring.beans.HelloServiceImpl)"
id="logPointCut"/>
<!-- 配置日志处理 -->
<aop:aspect ref="非核心业务bean名称" >
<aop:before method="beforeMethod"
pointcut-ref="切入点bean名称"/>
<aop:after method="afterMethod"
pointcut-ref="切入点bean名称"/>
</aop:aspect>
</aop:config>
通知类型如下:
@Aspect 注解用于标识此类为一个AOP横切面对象
@Pointcut 注解用于定义本类中的切入点,本案例中切入点表达式用的是bean表达式,这个表达式以bean开头,bean括号中的内容为一个spring管理的某个bean对象的id。
@Before 用于定义一个前置通知(满足切入点表达式的核心业务方法执行之前要执行的一个操作)
@After 用于定义一个后置通知(满足切入点表达式的核心业务方法执行之后要执行的一个操作)
多个切面作用于同一切入点是配置实例(要配置order,数字越小越先执行)
<aop:config>
<aop:pointcut id="pc"
expression="execution(*
com.company.spring.service..*.*(..))"/>
<aop:aspect ref="loggingAspect" order="1">
<aop:around method="aroundMethod" pointcut-ref="pc"/>
</aop:aspect>
<aop:aspect ref="txManager" order="2">
<aop:around method="aroundMethod" pointcut-ref="pc"/>
</aop:aspect>
</aop:config>
-----------------------------基于注解的实现-------------------------------
1、配置文件要开启注解
<!-- 配置对类组件的扫描 -->
<context:component-scan base-package="需要管理的文件路径"/>
<!-- 启用AOP注解(自动为目标对象创建代理对象) -->
<aop:aspectj-autoproxy/>
2、在切面类中的注解配置的切入点和通知
@Aspect
@Service
public class LogAspect {
@Pointcut("bean(orderServiceImpl)")
public void doLog(){}
@Before("doLog()")
public void doBefore(){
System.out.println("log before");
}
@After("doLog()")
public void doAfter(){
System.out.println("log after");
}
/**核心业务正常结束时执行
* 说明:假如有after,先执行after,再执行returning*/
@AfterReturning("doLog()")
public void doAfterReturning(){
System.out.println("log doAfterReturning");
}
/**核心业务出现异常时执行
说明:假如有after,先执行after,再执行Throwing*/
@AfterThrowing("doLog()")
public void doAfterThrowing(){
System.out.println("log doAfterThrowing");
}
}