Spring5系列(十一) | 基于注解的AOP编程

简介: Spring5系列(十一) | 基于注解的AOP编程

概述: 本篇文章很重要! 工作中我们经常会遇到给我们的项目写一个切面,很多开发工程师刚开始的时候都不知道切面应该怎么写,本篇文章就会教大家如何开发一个切面。

我们前面讲解了Spring的AOP编程,本质就是给spring的对象通过创建代理对象的方式添加额外功能。我们前面的方式都是通过在xml配置的方式实现的。我们简单回顾一下之前的步骤。

  1. 原始对象
  2. 额外功能
  3. 切入点
  4. 组装

一、 开发步骤

1. 额外功能:之前写法
      public class MyArround implements MythodInterceptor{
  public Object  invoke(MethodInvocation invocation){...}
  }
2. 切入点: 之前写法
<aop:confg>
  <aop:pointcut id="pc" expression="executionn(* com.xxx..*.*(..))" />
    <aop:advisor advice-ref="around" pointcut-ref="pc" />
</aop:confg> 

Spring本身为我们提供了注解的方式,来实现AOP的编程,我们来看下代码.

  1. 创建切面类,通过切面类定义额外功能和切入点。
/**
1. 额外功能:之前写法
      public class MyArround implements MythodInterceptor{
          public Object  invoke(MethodInvocation invocation){}
     }
2. 切入点: 之前写法
    <aop: config>
        <aop:pointcut id="pc" expression="execution(* login(..))" />
    </aop:config>
*/
@Aspect // 指定是切入类
public class MyAspect{
    @Arround("execution(* login(..))") // 指定额外功能和切入点表达式
    public Object arround(ProceedingJoinPoint joinPoint) throws Throwable{
    System.out.println("---log---")
    Object ret = joinPoint.proceed();
    return ret;
  }
}
  1. 配置切面:
<bean id="arround" class="com.xxx.MyAspect" />
<!-- 告知spring基于注解进行切面开发 -->
<aop:aspectj-autoproxy />

这样就完成了我们之前的那四个步骤,现在我们在从工厂中获取的对象就是代理对象,调用方法时,就会执行额外功能(注意: 要符合切入点表达式的方法)。

二、细节分析

  1. 切入点复用

切入点复用: 在切面中定义一个函数,上面加上@Pointcut注解,通过这种方式定义切入点表达式,实现了切入点的复用,相当于把切入点抽取了出来,方便切入点增加多个额外功能冗余的问题。这样我们就可以灵活的将切入点和额外功能进行自由组合。

@Aspect// 指定是切入类publicclassMyAspect{
@Pointcut("execution(* login(..))")
publicvoidmyPointCut(){}
@Arround(value="myPointcut()"))
publicObjectarround(ProceedingJoinPointjoinPoint) throwsThrowable{
System.out.println("---log---")
Objectret=joinPoint.proceed();
returnret;
  }
@Arround(value="myPointcut()")
publicObjectarround1(ProceedingJoinPointjoinPoint) throwsThrowable{
System.out.println("---tx---")
Objectret=joinPoint.proceed();
returnret;
  }
}
  1. 动态代理的创建方式
我们前面说到了Spring底层动态代理的两种方式: 
1. JDK动态代理:通过实现接口方式,创建代理对象
2. cglib动态代理: 通过继承父类的方式创建代理对象
那么我们上述代码所创建的代理对象是通过哪种方式创建的呢?
**默认情况下,AOP编程底层应用jdk的动态代理方式**
如果我们想要指定cglib进行动态代理创建,可以做如下设置
<aop:aspectj-autoproxy proxy-target-class=true />
复制代码

设置后我们可以通过断点的方式观察:

网络异常,图片无法展示
|

那么我们之前没用注解的时候,如何设置使用cglib动态代理呢:

<aop:confgproxy-target-class="true"><aop:pointcutid="pc"expression="@annocation(com.xxx.Log)"/><aop:advisoradvice-ref="around"pointcut-ref="pc"/></aop:confg>

三、AOP开发中的一个坑

我们在使用代理开发的过程中,有时候会遇到一个问题,就是额外功能失效的问题。我们先来看下这个问题是怎么出现的。

  1. 首先设置一个切面,增加额外功能。
@Aspect// 指定是切入类publicclassMyAspect{
@Pointcut("execution(* *..UserServiceImpl.*(..))")
publicvoidmyPointCut(){}
@Arround(value="myPointcut()"))
publicObjectarround(ProceedingJoinPointjoinPoint) throwsThrowable{
System.out.println("---log---")
Objectret=joinPoint.proceed();
returnret;
  }
@Arround(value="myPointcut()")
publicObjectarround1(ProceedingJoinPointjoinPoint) throwsThrowable{
System.out.println("---tx---")
Objectret=joinPoint.proceed();
returnret;
  }
}
复制代码
  1. 我们在目标方法中做调用:
publicclassUserServiceImplimplementsUserService {
@Overridepublicvoidregister(Useruser){
System.out.println("registe----")
// 调用的是原始对象的login方法,---核心功能,切面功能不执行// 设计目的是: 调用代理对象的login方法this.login("abc", "123456");
  }
@Overridepublicbooleanlogin(Stringname, Stringpassword){
System.out.println("login-----")
  }
}

此时要注意,当我们通过工厂获取UserService对象,并调用register方法的时候, register方法在执行的时候,是有额外功能执行的,但是由于register方法中又使用this调用了login,这个时候login方法是不会执行额外功能的。原因就是login方法是用this调用的,获得的并不是代理对象所以不会执行对应的额外功能。相当于是直接调用了原始类中的方法。如果此时让login方法也带上额外功能该怎么办呢,就是我们要通过代理对象去调用login方法。

这个时候就可以用我们之前文章中讲到的ApplicationContextAware来实现,把工厂注入进来,通过工厂去获取对象调用就可以了。

publicclassUserServiceImplimplementsUserServiceimplementsApplicationContextAware{
privateApplicationContextctx;
publicvoidsetApplicationContext(AppliactionContextapplication){
this.ctx=application;
  }
@Overridepublicvoidregister(Useruser){
System.out.println("registe----")
// 调用的是原始对象的login方法,---核心功能,切面功能不执行// 设计目的是: 调用代理对象的login方法this.login("abc", "123456");
//获取代理对象UserServiceuserService= (UserService)ctx.getBean("userService");
userService.login("abc", "123456");
  }
@Overridepublicbooleanlogin(Stringname, Stringpassword){
System.out.println("login-----")
  }
}

总结一下: 在同一个业务类中,进行业务方法间的相互调用,只有最外层方法才是加入额外功能的,内部方法通过普通方式调用,都是调用原始方法,如果想让内层的方法也调用代理对象的方法,就要通过ApplicationContextAware接口,获取现有工厂 ,进而获得代理对象

AOP总结:

image.png



目录
相关文章
|
22天前
Micronaut AOP与代理机制:实现应用功能增强,无需侵入式编程的秘诀
AOP(面向切面编程)能够帮助我们在不修改现有代码的前提下,为应用程序添加新的功能或行为。Micronaut框架中的AOP模块通过动态代理机制实现了这一目标。AOP将横切关注点(如日志记录、事务管理等)从业务逻辑中分离出来,提高模块化程度。在Micronaut中,带有特定注解的类会在启动时生成代理对象,在运行时拦截方法调用并执行额外逻辑。例如,可以通过创建切面类并在目标类上添加注解来记录方法调用信息,从而在不侵入原有代码的情况下增强应用功能,提高代码的可维护性和可扩展性。
44 1
|
2月前
|
Java 开发者 Spring
【SpringBoot 异步魔法】@Async 注解:揭秘 SpringBoot 中异步方法的终极奥秘!
【8月更文挑战第25天】异步编程对于提升软件应用的性能至关重要,尤其是在高并发环境下。Spring Boot 通过 `@Async` 注解简化了异步方法的实现。本文详细介绍了 `@Async` 的基本用法及配置步骤,并提供了示例代码展示如何在 Spring Boot 项目中创建与管理异步任务,包括自定义线程池、使用 `CompletableFuture` 处理结果及异常情况,帮助开发者更好地理解和运用这一关键特性。
135 1
|
2月前
|
缓存 Java 数据库连接
Spring Boot奇迹时刻:@PostConstruct注解如何成为应用初始化的关键先生?
【8月更文挑战第29天】作为一名Java开发工程师,我一直对Spring Boot的便捷性和灵活性着迷。本文将深入探讨@PostConstruct注解在Spring Boot中的应用场景,展示其在资源加载、数据初始化及第三方库初始化等方面的作用。
58 0
|
6天前
|
缓存 NoSQL Java
Springboot自定义注解+aop实现redis自动清除缓存功能
通过上述步骤,我们不仅实现了一个高度灵活的缓存管理机制,还保证了代码的整洁与可维护性。自定义注解与AOP的结合,让缓存清除逻辑与业务逻辑分离,便于未来的扩展和修改。这种设计模式非常适合需要频繁更新缓存的应用场景,大大提高了开发效率和系统的响应速度。
23 2
|
14天前
|
Java Spring 容器
Spring使用异步注解@Async正确姿势
Spring使用异步注解@Async正确姿势,异步任务,spring boot
|
14天前
|
XML Java 数据格式
spring复习03,注解配置管理bean
Spring框架中使用注解配置管理bean的方法,包括常用注解的标识组件、扫描组件、基于注解的自动装配以及使用注解后的注意事项,并提供了一个基于注解自动装配的完整示例。
spring复习03,注解配置管理bean
|
14天前
|
XML 前端开发 Java
控制spring框架注解介绍
控制spring框架注解介绍
|
1月前
Micronaut AOP与代理机制:实现应用功能增强,无需侵入式编程的秘诀
【9月更文挑战第9天】AOP(面向切面编程)通过分离横切关注点提高模块化程度,如日志记录、事务管理等。Micronaut AOP基于动态代理机制,在应用启动时为带有特定注解的类生成代理对象,实现在运行时拦截方法调用并执行额外逻辑。通过简单示例展示了如何在不修改 `CalculatorService` 类的情况下记录 `add` 方法的参数和结果,仅需添加 `@Loggable` 注解即可。这不仅提高了代码的可维护性和可扩展性,还降低了引入新错误的风险。
38 13
|
3天前
|
监控 Java 数据库
Spring事务中的@Transactional注解剖析
通过上述分析,可以看到 `@Transactional`注解在Spring框架中扮演着关键角色,它简化了事务管理的复杂度,让开发者能够更加专注于业务逻辑本身。合理运用并理解其背后的机制,对于构建稳定、高效的Java企业应用至关重要。
12 0
|
27天前
|
Java 数据库连接 数据格式
【Java笔记+踩坑】Spring基础2——IOC,DI注解开发、整合Mybatis,Junit
IOC/DI配置管理DruidDataSource和properties、核心容器的创建、获取bean的方式、spring注解开发、注解开发管理第三方bean、Spring整合Mybatis和Junit
【Java笔记+踩坑】Spring基础2——IOC,DI注解开发、整合Mybatis,Junit