该文主要讲Spring AOP的一些概念,不会细讲如何使用AOP。
关于AOP的使用,可以参考文末链接。
AOP 概述
AOP是Spring框架的另一个基本组成部分。这一次我们就探讨一下Spring的这一重要特性。面向切面(Aspect Oriented Programming)
需要明确的是,AOP并不是Spring独有,AspectJ也实现了AOP的功能。AOP和OOP一样,是一种编程范式。而Spring实现的AOP功能比较经典。
实现AOP使用的是代理模式。通过对需要切入类的包装和增强,创建代理类。从而实现切入到执行过程中的作用。
代理主要分为两种方式:
- 静态代理,基本的代理方式,AspectJ使用。
- 动态代理,Spring使用的代理方式,使用了JDK和CGLIB的代理方式。
具体的代理实现AOP的原理,在下一期细讲。
什么是AOP?
AOP(Aspect Oriented Programming)面向切面编程,可以说是OOP(Object Oriented Programming)面向对象编程的补充和完善。
具体来说,AOP是通过 预编译方式 和 运行时的动态代理 实现程序功能的统一维护的一种技术。AOP是软件开发中的一个热点,也是Spring框架中的一个重要内容,是函数式编程的一种衍生范型。
为什么要用AOP?
随着开发的系统越来越复杂,传统OOP程序会出现一些不自然的现象。核心业务中总掺杂一些不相关的业务。例如:日志记录,权限验证,事务控制,性能检测等等。这些功能模块可以说跟核心业务并无关系,而且核心业务也不关心他们。
这些场景就是一些分散的对象,涉及到了公共的行为。OOP在这方面显得无能为力。
这里要说一下,虽然可以使用继承来对公共行为做抽象,但是继承是一个纵向的关系,并且是强耦合的一种方式。有时候子类并不需要这个行为,而在很多情况下,需要这样行为的类之间根本就毫无关系。
如果非要使用这种方式,不仅会导致大量的重复代码,维护起来也是相当麻烦的一件事情,一点都不优雅。
而AOP引入了一种 "横切" 的技术,它是一种横向的关系。AOP使得我们可以把一些公共行为抽离出来,并且把这些行为,横向地插入到业务代码执行的过程中间。所以称之为 切面(Aspect)
这些横切进去的行为与具体业务无关,又嵌入到了业务模块的执行逻辑流程中,共同封装起来。便于减少重复代码,也降低了耦合度。利于系统的扩展性和可维护性。
可以用一张图简单的说明AOP的作用
AOP的功能
- 用于横切关注点的分离 和 织入横切关注点到系统。例如日志,权限等
- 对OOP的完善
- 降低组件和模块之间的耦合性
- 使得系统更易于扩展
- 分离关注点可以获得组件更好的复用
AOP的使用场景
- Authentication 权限
- Caching 缓存
- Context passing 内容传递
- Error handling 错误处理
- Lazy loading 懒加载
- Debugging 调试
- logging, tracing, profiling and monitoring 记录跟踪 优化 校准
- Performance optimization 性能优化
- Persistence 持久化
- Resource pooling 资源池
- Synchronization 同步
- Transactions 事务
AOP 核心概念
切面(Aspect)
切面是一个关注点,或者说希望抽象出来的功能 的模块化。这个关注点或者功能可以横切多个对象。对应代码中,就是声明出来切面的类和其中的所有逻辑。
连接点(JoinPoint)
连接点是指在程序中某个特定的点,通过这个点来和切面建立连接,将切面的功能组合进去。他表示了可以组合 Advice 的位置。
也就连接点,是允许你执行通知的地方。在Spring中允许的通知有五种(接下来讲)。AspectJ还允许在构造器和属性注入时执行通知。
简单来说,在Spring中,方法的前前后后都是连接点,
通知(Advice)
通知是在切面上某个连接点上执行的特定操作。也就是我们抽离出来公共功能代码的逻辑部分。Spring切面中支持五种通知:
- 前置通知(Before)在目标方法,或者说切点被调用前执行的通知。
- 后置通知(After)在切点被调用后执行。
- 返回通知(After-returning)在切点执行成功并返回以后执行
- 异常通知(After-throwing)在方法抛出异常之后执行
- 环绕通知(Around)包围一个连接点,在被通知的方法之前和之后执行。(内部调用 proceed() 来执行通知方法)
关于通知的执行优先级。
在不同的切面中,如果有多个通知需要在同一个切点函数指定的方法上执行,那么将会按照在代码中定义的顺序依次执行。
切点(PointCut)
简单来说,切点是匹配连接点的表达式或者说断言。
在上面切面概念的基础上,我们知道,方法的前后都可以是连接点,但我们并不希望在所有这些地方都执行通知,我们需要一个自己定义执行通知的地方。也就是切点。
通过切点的定义,就是让我们可以筛选出我们想要拦截的方法。
引入(Introduction)
在不修改代码的前提下,引入可以在 运行期 动态地为类添加一些方法或者字段。
也就是把切面应用到目标类中。
目标对象(Target Object)
引入中提到的目标类,也就是被通知的对象(具体的业务逻辑)。
目标对象可以在不关心切面的情况下被引入切面的新功能,实现了功能的解耦。
AOP代理(AOP Proxy)
AOP代理指AOP框架创建的,对象和切面的契约,也可以说是切面实现的方式。
事实上,一般AOP是通过代理的方式实现的。(代理又分为静态代理和动态代理,Spring使用的是动态代理)
AOP代理的目的,就是将切面织入到目标对象。
织入(Wearving)
把切面应用到目标对象,来创建代理对象的过程。Spring采用的是运行时织入,具体有两种方式JDK和CGLIB。AspectJ采用的是编译期织入。(这个下一期AOP原理分析时细讲)
以下是Spring使用注解方式实现AOP的简单例子(当然还可以使用xml配置的方式实现AOP)
使用Spring的AOP支持需要引入Spring-aspect的包,如果要启用注解支持,还需要添加AspectJ类库。
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aspects</artifactId>
<version>5.0.1.RELEASE</version>
</dependency>
<!-- aspectJ -->
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjrt</artifactId>
<version>1.8.10</version>
</dependency>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.8.10</version>
</dependency>
/**
* @Author Antony
* @Since 2017/11/30 16:35
*/
@Aspect
@Component
public class SychLockAspect { //该类就是切面
private static final Logger logger = LoggerFactory.getLogger(SychLockAspect.class);
@Pointcut("execution (* com.antony.service.*.*(..))") //这是切点表达式
public void SychLockMethod(){} //该方法体就是通知,内部是通知的逻辑,同时可以有before after , around 等 5种通知动作。
@Before(value = "SychLockMethod()")
public void beforeAspect(JoinPoint joinPoint){
String sychKey = getSychKey(joinPoint);
logger.info("sychKey={}", sychKey);
logger.info("AspectBefore......");
}
@After(value = "SychLockMethod()")
public void afterAspect(JoinPoint joinPoint){
String sychKey = getSychKey(joinPoint);
logger.info("sychKey={}", sychKey);
logger.info("AspectAfter......");
}
@Around(value = "SychLockMethod()")
public void around(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
System.out.println("Around before...");
proceedingJoinPoint.proceed();
System.out.println("Around After...");
}
}
Spring AOP 的支持
Spring中的AOP代理,由Spring IOC 容器负责生成和管理。其依赖关系也由IOC容器负责管理。AOP代理可以直接使用容器中的其他Bean实例作为目标,这种关系可以由IOC容器的依赖注入提供。
Spring 默认使用JDK动态代理来创建AOP代理类。Spring目前仅支持将方法调用作为连接点(JoinPoint),如果需要把堆成员变量的访问和更新页作为增强处理的连接点,则可以考虑使用AspectJ。
Spring侧重于AOP实现和IOC容器之间的整合。
Spring 框架对AOP的使用
Spring 事务处理
SpringMVC(例如@ControllerAdvice)
Spring Security
(由于对Spring的各个功能模块都还没有完整的了解,目前已知的是这些,应该还有更多,后续补充)
(如果有什么错误或者建议,欢迎留言指出)
(本文内容是对各个知识点的转载整理,用于个人技术沉淀,以及大家学习交流用)
参考资料:
关于SpringAOP你该知晓的一切
Spring之AOP-简书(主要是AOP基本概念和如何使用AOP)
AOP低层实现——JDK和CGLIB的动态代理
网友评论