Spring AOP(面向切面)介绍以及示例(@Before @Around @After @AfterReturning @AfrerThrowing)

1、什么是面向切面(AOP)

在使用面向切面之前我们要理解一下面向对象和面向切面这两个概念。

面向对象就是提取公共特征进行抽象化,比如,不管是男人还是女人,它们都有年龄、名字等属性,那么就可以把这些属性以及相同的方法(如get、set)抽取到父类中,是一种纵向的抽取。
在这里插入图片描述
面向切面,就是将一些逻辑通过横向切割的方式抽取到一个独立的模块,业务只需要关注自身的逻辑,在运行业务时,由程序将这些逻辑织入,是一种横向的抽取。比如假设数据库操作之前需要编写日志,这时可以对修改每个函数添加写日志操作,为了提升代码复用可以进行公共代码抽取,然后调用,这时还是需要进行函数调用,后面假如需要添加参数检测功能,那么提取公共代码后依然需要再次调用,会影响原本的业务逻辑类(修改了核心业务代码)。面向切面则不需要在业务核心代码添加这些非核心业务代码,这些交给Spring AOP去处理就可以。

下图所示,抽取写日志1、写日志2到单独的模块,运行干事情之前,面向切面会在干事情前后运行写日志1和写日志2。后面假如添加写日志3,那么也不用去重新封装,一样让面向切面去织入。
在这里插入图片描述

2、面向切面术语

参考https://blog.csdn.net/q982151756/article/details/80513340https://blog.csdn.net/ysl19910806/article/details/91898875

  • 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。

  • 1
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值