使用完全注解的方式进行AOP功能实现(@Aspect+@Configuration+@EnableAspectJAutoProxy+@ComponentScan)

简介: 本文介绍了如何使用Spring框架的注解方式实现AOP(面向切面编程)。当目标对象没有实现接口时,Spring会自动采用CGLIB库进行动态代理。文中详细解释了常用的AOP注解,如`@Aspect`、`@Pointcut`、`@Before`等,并提供了完整的示例代码,包括业务逻辑类`User`、配置类`SpringConfiguration`、切面类`LoggingAspect`以及测试类`TestAnnotationConfig`。通过这些示例,展示了如何在方法执行前后添加日志记录等切面逻辑。

1、⭐⭐⭐简单介绍🌙🌙🌙

如果目标对象实现了接口,默认情况下会采用JDK的动态代理实现AOP,如果目标对象没有实现了接口,必须采用CGLIB库,spring会自动在JDK动态代理和CGLIB之间转换.本文中由于使用的是没接口的,所以会采用cglib实现动态代理。

2、⭐⭐⭐注解说明🌙🌙🌙

@Aspect表示该类是一个用于设置AOP切面相关的切面类
@Pointcut 来声明切入点表达式
@Order 注解指定切面的优先级, 值越小优先级越高
@Before在方法开始之前执行一段代码
@After在方法执行之后执行的代码. 无论该方法是否出现异常
@AfterReturning在方法法正常结束受执行的代码,返回通知是可以访问到方法的返回值的!
@AfterThrowing在目标方法出现异常时会执行的代码.可以访问到异常对象; 且可以指定在出现特定异常时在执行通知代码
@Around环绕通知需要携带 ProceedingJoinPoint 类型的参数.环绕通知类似于动态代理的全过程: ProceedingJoinPoint 类型的参数可以决定是否执行目标方法.且环绕通知必须有返回值, 返回值即为目标方法的返回值
@Configuration//用于定义配置类,可替换xml配置文件
@EnableAspectJAutoProxy(proxyTargetClass=true) //开启AspectJ 自动代理模式,如果不填proxyTargetClass=true,默认为false(代表即便你配置了@EnableAspectJAutoProxy,也不会开启代理模式,调试的时候我就被卡在这里蛮久...)
@ComponentScan(basePackages = "com.atguigu.spring5.configuration2")//扫描注入类

3、⭐⭐⭐示例代码🌙🌙🌙:

<1>简单的bean对象 User.java

package com.atguigu.spring5.configuration2;

import org.springframework.stereotype.Component;

@Component("user")
public class User {
   
    public void add(){
   
        System.out.println("add ...");
    }
}

<2>用于替换以前bean的xml配置文件的配置类 SpringConfiguration.java

package com.atguigu.spring5.configuration2;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.EnableAspectJAutoProxy;
import org.springframework.stereotype.Component;

//即使用jdk默认代理模式,AspectJ代理模式是CGLIB代理模式
//如果目标对象实现了接口,默认情况下会采用JDK的动态代理实现AOP
//如果目标对象实现了接口,可以强制使用CGLIB实现AOP (此例子我们就是强制使用cglib实现aop)
//如果目标对象没有实现了接口,必须采用CGLIB库,spring会自动在JDK动态代理和CGLIB之间转换
@Configuration//用于定义配置类,可替换xml配置文件
@EnableAspectJAutoProxy(proxyTargetClass=true) //开启AspectJ 自动代理模式,如果不填proxyTargetClass=true,默认为false
@ComponentScan(basePackages = "com.atguigu.spring5.configuration2")//扫描注入类
public class SpringConfiguration {
   
    @Bean
    public User user() {
   
        return new User();
    }
}

<3>用于替换bean的xml配置文件里AOP相关的配置类LoggingAspect .java

package com.atguigu.spring5.configuration2;

import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.*;
import org.springframework.context.annotation.EnableAspectJAutoProxy;
import org.springframework.stereotype.Component;

import java.util.Arrays;

/**
 * 可以使用 @Order 注解指定切面的优先级, 值越小优先级越高
 */
//@Order(2)
@Aspect
@Component
public class LoggingAspect {
   
    /**
     * 定义一个方法, 用于声明切入点表达式. 一般地, 该方法中再不需要添入其他的代码.
     * 使用 @Pointcut 来声明切入点表达式.
     * 后面的其他通知直接使用方法名来引用当前的切入点表达式.
     */
    @Pointcut("execution(public void com.atguigu.spring5.configuration2.User.*(..))")
    public void declareJointPointExpression(){
   }

    /**
     * 在 com.atguigu.spring5.aop.User 接口的每一个实现类的每一个方法开始之前执行一段代码
     */
    @Before("declareJointPointExpression()")
    public void beforeMethod(JoinPoint joinPoint){
   
        String methodName = joinPoint.getSignature().getName();
        Object [] args = joinPoint.getArgs();
        System.out.println("The beforeMethod " + methodName + " begins with " + Arrays.asList(args));
    }

    /**
     * 在方法执行之后执行的代码. 无论该方法是否出现异常
     */
    @After("declareJointPointExpression()")
    public void afterMethod(JoinPoint joinPoint){
   
        String methodName = joinPoint.getSignature().getName();
        System.out.println("The afterMethod " + methodName + " ends");
    }

    /**
     * 在方法法正常结束受执行的代码
     * 返回通知是可以访问到方法的返回值的!
     */
    @AfterReturning(value="declareJointPointExpression()",returning="result")
    public void afterReturning(JoinPoint joinPoint, Object result){
   
        String methodName = joinPoint.getSignature().getName();
        System.out.println("The afterReturning " + methodName + " ends with " + result);
    }

    /**
     * 在目标方法出现异常时会执行的代码.
     * 可以访问到异常对象; 且可以指定在出现特定异常时在执行通知代码
     */
    @AfterThrowing(value="declareJointPointExpression()",throwing="e")
    public void afterThrowing(JoinPoint joinPoint, Exception e){
   
        String methodName = joinPoint.getSignature().getName();
        System.out.println("The afterThrowing " + methodName + " occurs excetion:" + e);
    }

    /**
     * 环绕通知需要携带 ProceedingJoinPoint 类型的参数.
     * 环绕通知类似于动态代理的全过程: ProceedingJoinPoint 类型的参数可以决定是否执行目标方法.
     * 且环绕通知必须有返回值, 返回值即为目标方法的返回值
     */
    /*
    @Around("execution(public int com.atguigu.springspring5.aop.User.*(..))")
    public Object aroundMethod(ProceedingJoinPoint pjd){

        Object result = null;
        String methodName = pjd.getSignature().getName();

        try {
            //前置通知
            System.out.println("The method " + methodName + " begins with " + Arrays.asList(pjd.getArgs()));
            //执行目标方法
            result = pjd.proceed();
            //返回通知
            System.out.println("The method " + methodName + " ends with " + result);
        } catch (Throwable e) {
            //异常通知
            System.out.println("The method " + methodName + " occurs exception:" + e);
            throw new RuntimeException(e);
        }
        //后置通知
        System.out.println("The method " + methodName + " ends");

        return result;
    }
    */
}

<4>测试类TestAnnotationConfig.java

package com.atguigu.spring5.configuration2;

import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.EnableAspectJAutoProxy;


public class TestAnnotationConfig {
   
    public static void main(String[] args) {
   
        AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext(SpringConfiguration.class);
        User user = ctx.getBean(User.class);
        user.add();
    }
}

<5>运行测试类TestAnnotationConfig.java结果如下:

在这里插入图片描述

4、核心代码逻辑及思维导图

以下是文章中提到的使用完全注解方式进行AOP功能实现的核心逻辑,转换成横向的思维导图:

graph LR
    A["AOP注解实现"] -->|定义切面类| B["@Aspect"]
    A -->|声明切入点| C["@Pointcut"]
    A -->|指定切面优先级| D["@Order"]
    A -->|方法执行前| E["@Before"]
    A -->|方法执行后| F["@After"]
    A -->|方法正常返回后| G["@AfterReturning"]
    A -->|方法抛出异常后| H["@AfterThrowing"]
    A -->|环绕方法执行| I["@Around"]
    A -->|定义配置类| J["@Configuration"]
    A -->|开启AspectJ自动代理| K["@EnableAspectJAutoProxy"]
    A -->|扫描组件包路径| L["@ComponentScan"]
    A -->|具体实现类| M["示例代码"]

    M -->|业务逻辑类| M1["User 类"]
    M -->|配置Spring环境| M2["SpringConfiguration 类"]
    M -->|定义切面逻辑| M3["LoggingAspect 类"]
    M -->|运行AOP测试| M4["TestAnnotationConfig 类"]

在这个思维导图中,每个注解和配置类都有了更详细的说明,描述了它们在AOP实现中的作用:

  • @Aspect: 定义一个类作为切面类。
  • @Pointcut: 声明一个切入点,指定哪些方法可以被拦截。
  • @Order: 指定切面的执行顺序,值越小优先级越高。
  • @Before: 在目标方法执行之前执行的逻辑。
  • @After: 在目标方法执行之后执行的逻辑,无论是否抛出异常。
  • @AfterReturning: 当目标方法正常执行完成后执行的逻辑。
  • @AfterThrowing: 当目标方法抛出异常时执行的逻辑。
  • @Around: 环绕通知,可以在方法执行前后添加逻辑。
  • @Configuration: 定义配置类,用于替代XML配置。
  • @EnableAspectJAutoProxy: 开启AspectJ的自动代理功能。
  • @ComponentScan: 指定Spring扫描组件的包路径。
  • 示例代码: 包含具体的实现类。

每个示例代码的子节点也都有相应的说明:

  • User 类: 业务逻辑类,被AOP代理的目标对象。
  • SpringConfiguration 类: 配置Spring环境,包括Bean的定义和AOP的配置。
  • LoggingAspect 类: 定义切面逻辑,包括各种通知类型的实现。
  • TestAnnotationConfig 类: 运行AOP测试的入口类。
目录
相关文章
|
5天前
|
Java API 数据安全/隐私保护
(工作经验)优雅实现接口权限校验控制:基于自定义注解、AOP与@ConditionalOnProperty配置开关的通用解决方案
(工作经验)优雅实现接口权限校验控制:基于自定义注解、AOP与@ConditionalOnProperty配置开关的通用解决方案
23 1
|
1月前
Micronaut AOP与代理机制:实现应用功能增强,无需侵入式编程的秘诀
AOP(面向切面编程)能够帮助我们在不修改现有代码的前提下,为应用程序添加新的功能或行为。Micronaut框架中的AOP模块通过动态代理机制实现了这一目标。AOP将横切关注点(如日志记录、事务管理等)从业务逻辑中分离出来,提高模块化程度。在Micronaut中,带有特定注解的类会在启动时生成代理对象,在运行时拦截方法调用并执行额外逻辑。例如,可以通过创建切面类并在目标类上添加注解来记录方法调用信息,从而在不侵入原有代码的情况下增强应用功能,提高代码的可维护性和可扩展性。
49 1
|
17天前
|
缓存 NoSQL Java
Springboot自定义注解+aop实现redis自动清除缓存功能
通过上述步骤,我们不仅实现了一个高度灵活的缓存管理机制,还保证了代码的整洁与可维护性。自定义注解与AOP的结合,让缓存清除逻辑与业务逻辑分离,便于未来的扩展和修改。这种设计模式非常适合需要频繁更新缓存的应用场景,大大提高了开发效率和系统的响应速度。
40 2
|
1月前
Micronaut AOP与代理机制:实现应用功能增强,无需侵入式编程的秘诀
【9月更文挑战第9天】AOP(面向切面编程)通过分离横切关注点提高模块化程度,如日志记录、事务管理等。Micronaut AOP基于动态代理机制,在应用启动时为带有特定注解的类生成代理对象,实现在运行时拦截方法调用并执行额外逻辑。通过简单示例展示了如何在不修改 `CalculatorService` 类的情况下记录 `add` 方法的参数和结果,仅需添加 `@Loggable` 注解即可。这不仅提高了代码的可维护性和可扩展性,还降低了引入新错误的风险。
40 13
|
2月前
|
XML Java 数据库
Spring5入门到实战------10、操作术语解释--Aspectj注解开发实例。AOP切面编程的实际应用
这篇文章是Spring5框架的实战教程,详细解释了AOP的关键术语,包括连接点、切入点、通知、切面,并展示了如何使用AspectJ注解来开发AOP实例,包括切入点表达式的编写、增强方法的配置、代理对象的创建和优先级设置,以及如何通过注解方式实现完全的AOP配置。
|
3月前
|
分布式计算 Java MaxCompute
详解 Java 限流接口实现问题之在Spring框架中使用AOP来实现基于注解的限流问题如何解决
详解 Java 限流接口实现问题之在Spring框架中使用AOP来实现基于注解的限流问题如何解决
|
3月前
|
容器
springboot-自定义注解拦截ip aop和ioc
springboot-自定义注解拦截ip aop和ioc
|
4月前
|
XML Java 数据格式
Spring5系列学习文章分享---第三篇(AOP概念+原理+动态代理+术语+Aspect+操作案例(注解与配置方式))
Spring5系列学习文章分享---第三篇(AOP概念+原理+动态代理+术语+Aspect+操作案例(注解与配置方式))
44 0
|
7天前
|
Java 容器
AOP面向切面编程
AOP面向切面编程
17 0
|
2月前
|
XML Java 数据格式
Spring5入门到实战------11、使用XML方式实现AOP切面编程。具体代码+讲解
这篇文章是Spring5框架的AOP切面编程教程,通过XML配置方式,详细讲解了如何创建被增强类和增强类,如何在Spring配置文件中定义切入点和切面,以及如何将增强逻辑应用到具体方法上。文章通过具体的代码示例和测试结果,展示了使用XML配置实现AOP的过程,并强调了虽然注解开发更为便捷,但掌握XML配置也是非常重要的。
Spring5入门到实战------11、使用XML方式实现AOP切面编程。具体代码+讲解