什么是AOP |
AOP:(Aspect Oriented Programming),面向切面编程,是一个概念,并没有设定具体语言的实现
AOP的作用 |
AOP哪些与业务无关,却为业务模块共同调用的逻辑或责任封装起来,便于减少系统的重复代码,降低模块间的耦合行,并有利于未来的可操作行和可维护性。
#
AOP与OOP的区别 |
- OOP引入封装,继承和多态等概念,建立一种对象层次结构,是针对业务处理过程的实体及其属性和行为进行抽象封装,以获得更加清晰高效的逻辑单元划分;OOP定义的是竖向的关系,即从上到下;
- AOP是OOP(面向对象编程)的延续,是为分散的对象引入公共行为,是针对业务处理过程中的切面进行提取,它所面对的是处理过程中的某个步骤或阶段,以获得逻辑过程中各部分之间低耦合性的隔离效果。这两种设计思想在目标上有着本质的差异;AOP定义的是横向的关系,即从左到右
如何实现 |
实现AOP的技术,主要分为两大类:
- 一是采用动态代理技术,利用截取消息的方式,对该消息进行装饰,以取代原有对象行为的执行;
- 二是采用静态织入的方式,引入特定的语法创建“方面”,从而使得编译器可以在编译期间织入有关“方面”的代码。
相关概念 |
- Cross Cutting Concern 横切性关注点:
是一种独立服务,它会遍布在系统的处理流程之中。我理解的是:横切性关注点是一个抽象的概念,日志处理通知,事物处理通知,安全处理通知等统称为横切性关注点,和通知的概念有点混淆,还有待理解。。。
- Aspect 切面
对横切性关注点的模块话。我理解的是:这说的模块化我理解的就是类,即将通知应用到哪些类上
- Advice通知:
对横切行关注点的具体实现。我理解的是:具体的实现,如在执行添加方法前,要执行日志操作,该日志具体的实现就称为通知
- Pointcut 切入点:
它定义类Advice应用到哪些JpinPoint上,对Spring来说是方法调用。我理解的是:一些相似操作的集合,比如说应用到所有的添加方法上,这些添加方法就可以理解为切入点
- JoinPoint 连接点:
Advice在应用程序上执行的点或时机,Spring只支持方法的JpinPoint。我理解的是:通知具体应用到的具体方法上,如应用到添加用户方法上,方法是具体的
- Weave
将Advice应用到Target Object上的过程叫织入,Spring支持的是动态织入。我理解的是:将通知应用到具体的连接点上,也即具体应用到哪个对象上
- Target Object
Advice被应用的对象。还有待理解。。。
- Proxy
Spring AOP默认使用JDK动态代理,它的代理是运行时创建,也可使用CGLIB代理。还有待理解。。。
- Introduction
动态得为类添加方法。还有待理解。。。
#
切入的5种方式 |
- Before —在所拦截方法执行前执行;
- After —在所拦截方法执行后执行;
- AfterReturning —在所拦截方法返回值后,执行;
- AfterThrowing —当所拦截方法抛出异常时,执行;
- Around —最为复杂的切入方式,可以包括上述4个方式,如错误处理为 AfterThrowing,权限管理为Before(最大区别就是:可以通过ProceedingJoinPoint获取参数或方法名,如getSignature())可获取修饰符+ 包名+组件名(类名) +方法名)
应用 |
权限、错误处理、检查安全性、记录日志等
实现 |
过程:
- 引入spring相关依赖包
- 将横切行关注点模块化,即写为一个类
- 指定Aspect,定义Advice和Pointcut
有两种实现方式:注解方式和配置文件方式
1.注解方式:
在配置文件中,启用Aspect对Annotation的支持
<aop:aspectj-autoproxy />
Aspect模块:
//声明Aspect切面
@Aspect
public class SecurityHandler {
//定义切入点
//该方法不是实际调用的,只是为了提供注解Pointcut,因为注解在类上,Pointcut的名称为addMethod()
//此方法没有返回值和参数,该方法就是个标识,不进行调用
//第一个*:返回值
//第二个*:所有的add方法
//...:方法参数
@Pointcut("execution(* add*(..))")
private void addAddMethod(){};
//定义advice通知,before切入方式
@Before("addAddMethod()")
private void checkSecurity(){
System.out.println("_______checkSecurity___________");
}
}
这样就实现了在所有的以add开头的方法执行前都会先执行,在此定义的checkSecurity()方法作为通知,即:
@Before("addAddMethod()")
private void checkSecurity(){
System.out.println("_______checkSecurity___________");
}
可以在Advice方法中加入JoinPoint参数,来获取客户端调用的方法名和方法的参数
2.配置文件方式:
<bean id="securityHandler" class="com.bjpowernode.spring.SecurityHandler"></bean>
<aop:config>
<!-- 定义切面 -->
<aop:aspect id="securityAspect" ref="securityHandler">
<!-- 定义切入点 -->
<!-- 所有以add开头的方法 -->
<!-- <aop:pointcut id ="addMethod" expression="execution(* add*(..))"/> -->
<!-- 该包下的所有方法 -->
<!-- <aop:pointcut id ="addMethod" expression="execution(* com.bjpowernode.spring.*.*(..))"/> -->
<!-- 该包下所有类的add开后或del开头的方法 -->
<aop:pointcut id ="addMethod" expression="execution(* com.bjpowernode.spring.*.add*(..)) || execution(* com.bjpowernode.spring.*.del*(..))"/>
<!-- 定义通知 -->
<aop:before method="checkSecurity" pointcut-ref="addMethod"/>
</aop:aspect>
</aop:config>
总结spring对AOP的支持:
- 如果目标对象实现了接口,在默认情况下会采用JDK动态代理实现AOP,也可强制使用CGLIB生成代理实现AOP
- 如果目标对象没有实现接口,就必须引入CGLIB,spring会在JKD的动态代理和CGLIB代理之间切换
JDK动态代理和CGLIB代理的区别:
- JDK动态代理实现了接口的类进行代理
- CGLIB代理可实现对类的代理,主要对指定的类生成一个子类
如何强制使用CGLIB代理:
- 加入CGLIB库
- 在配置文件中声明强制使用CGLIB代理(默认是使用的JDK代理)
<aop:aspectj-autoproxy proxy-target-class="true" />
总结 |
AOP就是给多个不相关的同等级(所以是横切性)的类提供共同的服务,而这些服务与具体的类的业务逻辑是不相关的,这样减少了代码的书写,降低程序耦合