1、什么是面向切面(AOP)
在使用面向切面之前我们要理解一下面向对象和面向切面这两个概念。
面向对象就是提取公共特征进行抽象化,比如,不管是男人还是女人,它们都有年龄、名字等属性,那么就可以把这些属性以及相同的方法(如get、set)抽取到父类中,是一种纵向的抽取。

讯享网
面向切面,就是将一些逻辑通过横向切割的方式抽取到一个独立的模块,业务只需要关注自身的逻辑,在运行业务时,由程序将这些逻辑织入,是一种横向的抽取。比如假设数据库操作之前需要编写日志,这时可以对修改每个函数添加写日志操作,为了提升代码复用可以进行公共代码抽取,然后调用,这时还是需要进行函数调用,后面假如需要添加参数检测功能,那么提取公共代码后依然需要再次调用,会影响原本的业务逻辑类(修改了核心业务代码)。面向切面则不需要在业务核心代码添加这些非核心业务代码,这些交给Spring AOP去处理就可以。
下图所示,抽取写日志1、写日志2到单独的模块,运行干事情之前,面向切面会在干事情前后运行写日志1和写日志2。后面假如添加写日志3,那么也不用去重新封装,一样让面向切面去织入。

2、面向切面术语
参考https://blog.csdn.net/q/article/details/、https://blog.csdn.net/ysl/article/details/
- Aspect(切面): Aspect 声明类似于 Java 中的类声明,在 Aspect 中会包含着一些 Pointcut 以及相应的 Advice。
- Joint point(连接点):表示在程序中明确定义的点,典型的包括方法调用,对类成员的访问以及异常处理程序块的执行等等,它自身还可以嵌套其它 joint point。
- Pointcut(切点):表示一组 joint point,这些 joint point 或是通过逻辑关系组合起来,或是通过通配、正则表达式等方式集中起来,它定义了相应的 Advice 将要发生的地方。
- Advice(增强):Advice 定义了在 Pointcut 里面定义的程序点具体要做的操作,它通过 before、after 和 around 来区别是在每个 joint point 之前、之后还是代替执行的代码。
- Target(目标对象):织入 Advice 的目标对象.。
- Weaving(织入):将 Aspect 和其他对象连接起来, 并创建 Adviced object 的过程。
以上面的干事情为例子,Pointcutt(切点)就是干事情的前后。Advice(增强)就是就是写日志1和写日志2。Joint point(连接点)比如写日志1和干事情之间就是一个连接点。Aspect(切面)就是包含切点和增强等的定义类,即定义哪里增强等。Target(目标对面)就是干事情这件事。Weaving(织入)就是在干事情前后添加写日志1和写日志2,这个添加的过程就是织入。
Advice类型:
- 前置通知(@Before):在目标方法运行之前运行
- 后置通知(@After):在目标方法运行结束之后运行
- 返回通知(@AfterReturning):在目标方法正常返回之后运行
- 异常通知(@AfterThrowing):在目标方法出现异常之后运行
- 环绕通知(@Around):目标方法执行前后分别执行一些代码,发生异常的时候执行另外一些代码,环绕通知方法可以包含上面四种通知方法。调用joinPoint.procced()之前就是前置通知,调用joinPoint.procced()之后就是后置通知和返回通知,添加异常处理就是异常通知了(传参和返回值修改需要在@Around内,其他几种无法,个人测试)。
3、Spring AOP示例
使用Gradle项目,创建项目配置build.gradle
plugins {
id 'java' } group 'org.example' version '1.0-SNAPSHOT' sourceCompatibility = 1.8 repositories {
mavenLocal() maven {
url "http://maven.aliyun.com/nexus/content/groups/public" } mavenCentral() } dependencies {
compile group: 'org.springframework', name: 'spring-core', version: '5.2.3.RELEASE' compile group: 'org.springframework', name: 'spring-beans', version: '5.2.3.RELEASE' compile group: 'org.springframework', name: 'spring-context', version: '5.2.3.RELEASE' compile group: 'org.aspectj', name: 'aspectjweaver', version: '1.9.5' } test {
useJUnitPlatform() }
讯享网
创建接口

讯享网package com.huang.aspect; public interface IService {
String doService(String str) throws Exception; }
添加接口实现类
package com.huang.aspect; public class IServiceImpl implements IService {
public String doService(String str) {
System.out.println("doService"); return str; } }
创建切面类
讯享网package com.huang.aspect; import org.aspectj.lang.JoinPoint; import org.aspectj.lang.ProceedingJoinPoint; import org.aspectj.lang.annotation.*; import java.util.Arrays; @Aspect public class DoAspect {
//定义切入点 @Pointcut("execution(public * com.huang.aspect..*.doService(..))") public void pointCut(){
} // Around(前续) -> Before -> joinPoint.proceed -> Method -> Around(后续) -> After -> AfterReturning(AfterThrowing) @Before("pointCut()") public void doBefore(JoinPoint joinPoint) {
System.out.println("doBefore"); } @After("pointCut()") public void doAfter(JoinPoint joinPoint) {
System.out.println("doAfter"); } //object与下面参数名一样 @AfterReturning(returning = "object",pointcut = "pointCut()") public void doReturn(Object object) {
System.out.println("doReturn"); } //exception与下面参数名一样 @AfterThrowing(pointcut = "pointCut()",throwing = "exception") public void doThrowing(Exception exception) {
System.out.println("doThrowing"); } @Around("pointCut()") public Object doAround(ProceedingJoinPoint joinPoint) throws Throwable {
System.out.println("doAroundFront"); //修改joinPoint.getArgs()里的参数可以修改传入的参数 Object res = joinPoint.proceed(joinPoint.getArgs()); //对res修改可以更改返回值 System.out.println("doAroundBehind"); return res; } }
创建Bean配置文件类
package com.huang.aspect; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.EnableAspectJAutoProxy; @EnableAspectJAutoProxy @Configuration public class BeanConfiguration {
@Bean public IService createServiceBen() {
return new IServiceImpl(); } @Bean public DoAspect doAspect() {
return new DoAspect(); } }
测试切面主函数
讯享网package com.huang.aspect; import org.springframework.context.ApplicationContext; import org.springframework.context.annotation.AnnotationConfigApplicationContext; public class Main {
// 测试切面 public static void main(String[] args) throws Exception {
ApplicationContext applicationContext = new AnnotationConfigApplicationContext(BeanConfiguration.class); IService iService = applicationContext.getBean(IService.class); System.out.println("Aspect result:" + iService.doService("abcdefgh")); } }
运行结果
> Task :Main.main() doAroundFront doBefore doService doAroundBehind doAfter doReturn Aspect result:abcdefgh
这里没有抛出异常,可以看出执行结果顺序为Around(前续) -> Before -> joinPoint.proceed -> Method -> Around(后续) -> After -> AfterReturning
在@Before内抛出异常
讯享网> Task :Main.main() FAILED doAroundFront doBefore doAfter doThrowing Exception in thread "main" java.lang.Exception
因此异常时将会执行After以及AfterThrowing,如果是 AfterReturning出现异常则会执行AfterThrowing。
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容,请联系我们,一经查实,本站将立刻删除。
如需转载请保留出处:https://51itzy.com/kjqy/73270.html