欢迎来到尧图网

客户服务 关于我们

您的位置:首页 > 文旅 > 游戏 > SpringAOP面向切面编程的概念和使用

SpringAOP面向切面编程的概念和使用

2025/4/26 4:00:09 来源:https://blog.csdn.net/weixin_71072718/article/details/141032951  浏览:    关键词:SpringAOP面向切面编程的概念和使用

目录

1、什么是Spring AOP?

2、AOP基于动态代理实现

3、AOP的通知类型

4、AOP的核心概念

5、SpringAOP+AspectJ实现步骤

6、SpringAOP和注解的实现


1、什么是Spring AOP?

        AOP(Aspect-Oriented Programming: 面向切面编程):将那些与业务无关, 却为业务模块所共同调用的逻辑(例如事务处理、日志管理、权限控制等)封装抽取成一个可重用的模块,这个模块被命名为“切面”(Aspect),便于减少系统的重复代码降低模块间的耦合度,并有利于未来的可拓展性和可维护性;

2、AOP基于动态代理实现

  • 如果被代理的对象,已经实现某个接口,则 Spring AOP 会使用 JDK Proxy(反射),基于接口的方式,创建代理对象(JDK动态代理的核心是InvocationHandler接口和Proxy类);
  • 如果被代理的对象,没有实现某个接口,就无法使用 JDK Proxy 去进行代理了,这时候 Spring AOP 会使用 Cglib,基于继承的方式,生成一个被代理对象的子类来作为代理(Cglib动态代理的核心是MethodInterceptor接口和Enhancer类);

3、AOP的通知类型

        AOP将抽取出来的共性功能称为通知,通知类型:以通知在上下文中的具体位置作为划分:

  • 前置通知(Before)
  • 返回通知(After-returning)
  • 异常通知(After-throwing)
  • 后置通知(After)
  • 环绕通知(Around)

4、AOP的核心概念

        AOP连接点(Join point):AOP将所有的方法都视为连接点,不管是接口里面的抽象方法,还是实现类里面的重写方法,都是连接点。连接点是 AOP 切面(Aspect)可以插入的地方。

        AOP切点(Pointcut):AOP将可能被抽取共性功能的方法称为切入点。切入点是连接点的子集。只有运行成功的方法才能是切点

        AOP目标对象(Target):就是挖掉功能的方法对应的类生的对象,这种对象是无法直接完成最终工作的。比如之前的借钱的业务,如果只有方法没有事务管理的话,这种就是挖掉功能的方法。(没有横切关注点如事务管理、日志记录、权限控制等没有直接实现)。

        AOP织入(Weaving):就是将挖掉的功能回填的动态过程,通过织入,AOP 将切面(Aspect)中的通知(Advice)插入到目标对象的指定切点(Pointcut)中。

        AOP切面(Aspect)切点+通知,它定义了横切关注点的实现(通知)以及应用的地方(切点)。切面将横切关注点与业务逻辑分离,定义了需要在哪里以及如何应用这些关注点。

5、SpringAOP+AspectJ实现步骤

        首先是service接口类,定义了增删改查的方法但是没有事务管理和日志。

public interface IAccountService {/*** 新增* */public void save(int i);/*** 修改* */public void update();/*** 删除* */public int delete();
}

        然后是service层实现类,重写了接口中的方法:

public class AccountServiceImp implements IAccountService {@Overridepublic void save(int i) {System.out.println("业务层的新增方法:" + i);}@Overridepublic void update() {System.out.println("业务层的修改方法");int a = 10/0;}@Overridepublic int delete() {System.out.println("业务层的删除方法");return 0;}
}

        紧接着我们在util包下,创建日志记录类

public class Logger {public void beforeMethod(){System.out.println("日志类logger中调用beforeMethod方法进行日志记录");}public void returnMethod(){System.out.println("日志类logger中调用returnMethod方法进行日志记录");}public void throwMethod(){System.out.println("日志类logger中调用throwMethod方法进行日志记录");}public void afterMethod(){System.out.println("日志类logger中调用afterMethod方法进行日志记录");}public Object arroundMethod(ProceedingJoinPoint pjp){Object obj = null;try{System.out.println("环绕通知===前置通知");//切点方法Object[] ars = pjp.getArgs();obj = pjp.proceed(ars);System.out.println("环绕通知===返回通知");}catch (Throwable e){e.printStackTrace();System.out.println("环绕通知===异常通知");}finally {System.out.println("环绕通知===后置通知");return obj;}}
}

        前四个方法是用来定义AOP通知的方法,包括前置通知、后置通知、异常通知、最终通知。

        最后一个方法为环绕通知,可以完全控制目标的方法执行,不需要写上面四个通知方法,一个就全部解决。

        其中的ProceedingJoinPoint 是 AspectJ 和 Spring AOP 中的一种特殊类型的 JoinPoint,提供了继续执行连接点的功能。与普通的 JoinPoint 相比,ProceedingJoinPoint 允许在环绕通知中控制目标方法的执行

        首先调用了前置通知,然后通过.getArgs()方法获得目标方法的参数,再通过.proceed(objs)方法将参数传入并调用方法。然后调用后置通知。最后调用后置通知,并返回方法的返回值。

        最后在XML配置文件中:

<!--注入日志记录层(通知)-->
<bean id="logger" class="com.apesource.util.Logger"></bean>

        首先注入日志记录层logger。

<!--配置AOP--><aop:config><!--配置切面 定义了横切关注点(如日志记录、事务管理、安全性等--><aop:aspect id="aopAspect" ref="logger"><!--切点 指定了在哪些连接点(Join Point)上执行通知。--><!--expression 是切点表达式--><!--execution(* com.apesource.service.*.*(..)):这个表达式定义了切点,匹配 com.apesource.service 包下的所有类的所有方法。* 代表返回类型和方法名,(..) 代表任意参数。--><aop:pointcut id="dian" expression="execution(* com.apesource.service.*.*(..))"/><!--通知 通知定义了在切点处执行的具体操作。-->
<!--            <aop:before method="beforeMethod" pointcut-ref="dian"></aop:before>--><!--执行成功后-->
<!--            <aop:after-returning method="returnMethod" pointcut-ref="dian"></aop:after-returning>-->
<!--            <aop:after-throwing method="throwMethod" pointcut-ref="dian"></aop:after-throwing>--><!--执行后-->
<!--            <aop:after method="afterMethod" pointcut-ref="dian"></aop:after>--><!--环绕通知--><aop:around method="arroundMethod" pointcut-ref="dian"></aop:around></aop:aspect></aop:config>

        然后定义一个AOP切面,并配置了一个环绕通知。

        <aop:config> 标签:表示这是 AOP 配置的开始。所有的 AOP 配置元素都应在这个标签内。

        <aop:aspect> 标签: 定义了一个切面(Aspect)。 id="aopAspect":为该切面指定一个唯一的标识符 aopAspect。 ref="logger":表示这个切面引用了一个名为 logger 的 bean,该 bean 中包含了实际的通知方法。

        <aop:pointcut> 标签:定义了一个切点(Pointcut)。expression="execution(* com.apesource.service.*.*(..))":切点表达式,定义了哪些连接点(Join Point)应该被拦截

        切点表达式配置语法:execution(修饰符 返回值 包名称.类名称.方法名称(参数列表))

        示例代码:

execution(public void com.apesource.service.ServiceImp.findAll())

        修饰符可以省略代表任意:

execution(返回值 包名称.类名称.方法名称(参数列表))

        返回值可以使用“*”代表任意:

execution(* 包名称.类名称.方法名称(参数列表))

        包名可以使用“*”代表任意名称:

execution(* *.*.*.类名称.方法名称(参数列表))

         包名可以使用“..”代表任意个数:

execution(* *...类名称.方法名称(参数列表))

        类名与方法名可以使用“*”代表任意:

execution(* *...*.*(参数列表))

        参数列表可以使用".."代表任意个数任意类型:

execution(* *...*.*(..))

        如果有参数(int======>int、String===>java.lang.String)

        后面的通知标签中,method代表的是在目标方法返回结果后要调用的通知方法,pointcut-ref代表的是这个通知方法应用在哪个切点上。

        <aop:after-returning>:定义一个后置通知(After Returning Advice),在目标方法成功执行并返回结果之后调用。

        <aop:after-throwing>:定义一个异常通知(After Throwing Advice),在目标方法执行过程中抛出异常时调用。

        <aop:after>:定义一个最终通知(After Advice),无论目标方法是否成功执行或抛出异常,都会在目标方法执行后调用。

        <aop:around>:定义一个环绕通知(Around Advice),包裹目标方法的执行。可以在目标方法之前和之后执行自定义逻辑,并决定是否执行目标方法。

        总体来说的步骤为:

1. 添加依赖,aop与aspectj表达式的依赖

2. 创建spring的主配置文件,bean内的命名空间要添加aop的

3. 创建业务代码并编写日志记录代码(事务管理代码)

4. 将业务层与日志记录层注入spring容器

5. <aop:config>--aop配置 aop:aspect--aop切面 aop:before--通知内容与通知类型

6、SpringAOP和注解的实现

        和上面的配置类比较的话就是一个是通过扫描注解来完成AOP织入,而一个是通过配置类来实现AOP织入。

        logger工具类实现:

@Component
@Aspect //切面
public class Logger {@Pointcut("execution(* com.apesource.service.AccountServiceImp.update())")public void dian(){}//前置通知@Before("dian()")public void BeforeLogger(){System.out.println("前置通知==>日志类logger中调用BeforeLogger方法进行日志记录");}//返回通知@AfterReturning("dian()")public void AfterReturningLogger(){System.out.println("返回通知==>日志类logger中调用AfterReturningLogger方法进行日志记录");}//异常通知@AfterThrowing("dian()")public void AfterThrowingLogger(){System.out.println("异常通知==>日志类logger中调用AfterThrowingLogger方法进行日志记录");}//后置通知@After("dian()")public void AfterLogger(){System.out.println("后置通知==>日志类logger中调用AfterLogger方法进行日志记录");}//环绕通知//@Around("dian()")public Object AroundLogger(ProceedingJoinPoint pjp){Object returnobj = null;//保存主业务方法的返回值try{//1.前置通知System.out.println("环绕通知===》前置通知");Object[] objs = pjp.getArgs();//主业务方法的参数returnobj =  pjp.proceed(objs);//调用主业务方法//3.后置通知System.out.println("环绕通知===》后置通知");}catch (Throwable tw){//4.异常通知System.out.println("环绕通知===》异常通知");}finally{//5.最终通知System.out.println("环绕通知===》最终通知");}return returnobj;}
}

        @Aspect 注解注解表明 Logger 类是一个切面类。

        @Pointcut 注解定义了一个切入点,表示在 com.apesource.service.AccountServiceImp 类的 update 方法执行时,切面会触发。

        前置通知@Before("dian()") 这个注解表明 BeforeLogger 方法会在 dian() 定义的切入点之前执行。

        返回通知 (@AfterReturning) 这个注解表明 AfterReturningLogger 方法会在 dian() 定义的切入点成功返回之后执行。

        异常通知 (@AfterThrowing) 这个注解表明 AfterThrowingLogger 方法会在 dian() 定义的切入点抛出异常时执行。

        后置通知 (@After) 这个注解表明 AfterLogger 方法会在 dian() 定义的切入点方法执行之后(无论是否成功)执行。

        环绕通知 (@Around) 方法会在 dian() 定义的切入点的前后执行,并且可以控制目标方法的执行。

        然后在XML中扫描并开启aop代理即可:

<!--扫描注解--><context:component-scan base-package="com.apesource"></context:component-scan><!--开启spring注解的aop动态代理--><aop:aspectj-autoproxy></aop:aspectj-autoproxy>

版权声明:

本网仅为发布的内容提供存储空间,不对发表、转载的内容提供任何形式的保证。凡本网注明“来源:XXX网络”的作品,均转载自其它媒体,著作权归作者所有,商业转载请联系作者获得授权,非商业转载请注明出处。

我们尊重并感谢每一位作者,均已注明文章来源和作者。如因作品内容、版权或其它问题,请及时与我们联系,联系邮箱:809451989@qq.com,投稿邮箱:809451989@qq.com

热搜词