Spring framework Day21:Spring AOP 注解结合配置类示例

前言

Spring AOP是一种强大的面向切面编程工具,它能够帮助我们更好地处理与核心业务逻辑无关的横切关注点。通过使用注解和配置类的方式,我们可以更加简洁和灵活地实现AOP。

本文将以一个示例来介绍如何结合注解和配置类来使用Spring AOP。通过这个示例,你将了解如何使用注解来定义切点、通知和切面,并通过配置类来启用和配置AOP。

前面三章我们都是通过 xml 文件去配置,那么这章我们开始使用 Java 配置类加注解来完成案例。

注意:使用 xml 是为了让大家了解 xml 怎么去配置,现在常用的都是注解加 Java 配置类来实现。

在开始之前,请确保你已经具备了Spring框架的基础知识,并且已经正确配置了Spring环境。

接下来,我们将一步步地介绍这个示例,让我们开始吧!

一、开始学习

1、新建项目,结构如下

Spring framework Day21:Spring AOP 注解结合配置类示例_第1张图片

 2、添加 spring 依赖
 
    
    
        
        
            org.springframework
            spring-context
            5.3.23
        
 
        
            ch.qos.logback
            logback-classic
            1.4.5
        
 
         
            org.aspectj
            aspectjweaver
            1.9.8
        
 
 
    
3、在 service 包下新建一个 UserService 类(被代理的目标类)

@Slf4j
@Service
public class UserService {

    /**
     * 目标方法,也就是需要被增强的方法
     * 因此它就是一个连接点
     * @param name
     */
    public String add(String name){
        log.info("添加用户..." + name);
        return "success";
    }

}
4、在 aspect 包下新建一个 UserAspect 切面类
@Slf4j
// 将切面纳入容器管理
@Component
/**
 * 使用 @Aspect 注解标识当前类为一个切面
 */
@Aspect
public class UserAspect {

    @Pointcut("execution(* edu.nf.ch21.service.UserService.*(..))")
    public void pointcut() {
    }

    @Before("pointcut()")
    public void before(JoinPoint jp) {
        log.info("前置通知");
    }

    /**
     * 后置通知
     *
     * @param jp
     * @param returnVal
     */
    @AfterReturning(value = "pointcut()", returning = "returnVal")
    public void afterReturning(JoinPoint jp, Object returnVal) {
        log.info("后置通知,目标方法返回值:" + returnVal);
    }

    /**
     * 环绕通知
     *
     * @param jp
     * @return
     */
    @Around("pointcut()")
    public Object around(ProceedingJoinPoint jp) throws Throwable {
        log.info("环绕通知前,方法参数:" + jp.getArgs()[0]);
        Object returnVal = jp.proceed();
        log.info("环绕通知后,方法返回值:" + returnVal);
        return returnVal;
    }

    /**
     * 异常通知
     *
     * @param jp
     * @param e
     */
    @AfterThrowing(value = "pointcut()", throwing = "e")
    public void afterThrowing(JoinPoint jp, Exception e) {
        log.info("异常通知:" + e.getMessage());
    }

    /**
     * 最终通知
     * @param jp
     */
    @After("pointcut()")
    public void after(JoinPoint jp){
        log.info("最终通知");
    }

}

这个切面类定义了一个pointcut()切点方法,用于指定哪些方法需要被拦截。这里使用了execution()表达式来匹配edu.nf.ch21.service.UserService类中的所有方法。

接下来,针对这个切点方法,定义了五个通知方法:

  • before()方法是一个前置通知,在目标方法执行之前执行,并且拦截到JoinPoint参数,可以获取目标方法的信息。
  • afterReturning()方法是一个后置通知,在目标方法正常返回时执行,拦截到JoinPoint和目标方法的返回值returnVal参数。
  • around()方法是一个环绕通知,可以在目标方法执行前、执行后都做一些处理,并拦截到ProceedingJoinPoint参数,可以手动调用目标方法并获取返回值。
  • afterThrowing()方法是一个异常通知,在目标方法抛出异常时执行,拦截到JoinPointException参数。
  • after()方法是一个最终通知,在目标方法执行结束后(包括正常结束和异常结束)执行,拦截到JoinPoint参数。

 其中还使用了很多的注解,它们分别是什么意思?

  1. @Component: 这是Spring框架的注解之一,用于将类标识为一个可被Spring容器扫描和管理的组件。在这个示例中,@Component注解表示将UserAspect类作为一个切面组件纳入到Spring容器中。

  2. @Aspect: 这是AspectJ框架的注解,用于标识一个类为切面。切面是一个包含了各种通知和切点的类,用于对其他类中的方法进行拦截和增强。

  3. @Pointcut: 这是一个自定义的方法级别注解,用于定义一个切点。切点决定了哪些方法会被拦截和应用切面逻辑。在这个示例中,pointcut()方法使用了@Pointcut注解来定义切点。

  4. @Before: 这是一个前置通知注解,用于在目标方法执行之前执行某段逻辑。在这个示例中,before()方法使用了@Before注解,表示在匹配到的方法执行之前会执行before()方法中的逻辑。

  5. @AfterReturning: 这是一个后置返回通知注解,用于在目标方法正常返回时执行某段逻辑。在这个示例中,afterReturning()方法使用了@AfterReturning注解,并通过value属性指定了切点,returning属性指定了目标方法返回值的参数名,表示在匹配到的方法正常返回时会执行afterReturning()方法中的逻辑。

  6. @Around: 这是一个环绕通知注解,用于在目标方法执行前和执行后执行某段逻辑。在这个示例中,around()方法使用了@Around注解,并通过value属性指定了切点,表示在匹配到的方法执行前后会执行around()方法中的逻辑。注意,around()方法的参数类型为ProceedingJoinPoint,可以手动调用目标方法。

  7. @AfterThrowing: 这是一个异常通知注解,用于在目标方法抛出异常时执行某段逻辑。在这个示例中,afterThrowing()方法使用了@AfterThrowing注解,并通过value属性指定了切点,throwing属性指定了异常的参数名,表示在匹配到的方法抛出异常时会执行afterThrowing()方法中的逻辑。

  8. @After: 这是一个最终通知注解,用于在目标方法执行结束后执行某段逻辑,无论是否抛出异常。在这个示例中,after()方法使用了@After注解,并通过value属性指定了切点,表示在匹配到的方法执行结束后会执行after()方法中的逻辑。

总结起来,这些注解一起使用可以实现对目标方法的拦截和增强,例如在方法执行前后打印日志、记录返回值、处理异常等。

5、在 config 包下新建一个  AppConfig 配置类
@Configuration
@ComponentScan(basePackages = "edu.nf.ch21")
// 启用 AspectJ 注解处理器
// proxyTargetClass 指定为 true 表示强制使用 cglib 生成代理
@EnableAspectJAutoProxy(proxyTargetClass = true)
public class AppConfig {
}

这段代码是一个Spring配置类,用于配置Spring容器和启用AspectJ注解处理器。下面是对每个注解的解释:

  • @Configuration: 这是一个Spring框架的注解,用于表示该类是一个配置类。配置类通常用于定义Bean的创建和配置,以及其他的Spring配置。

  • @ComponentScan: 这是一个Spring框架的注解,用于指定要扫描的包或类的路径。在这个示例中,basePackages属性指定了要扫描的基础包路径为"edu.nf.ch21",意味着Spring容器会扫描并加载该包及其子包中的组件。

  • @EnableAspectJAutoProxy: 这是一个Spring框架的注解,用于启用AspectJ注解处理器,并开启基于AspectJ的自动代理功能。proxyTargetClass = true表示使用cglib生成代理对象而不是默认的基于接口的JDK动态代理方式。

这些注解的组合作用是将该配置类纳入Spring容器的管理,并启用AspectJ注解处理器和自动代理功能,从而实现AOP(面向切面编程)的特性。

6、测试
public class Main {

    public static void main(String[] args) {
        ApplicationContext context = new AnnotationConfigApplicationContext(AppConfig.class);
        UserService bean = context.getBean(UserService.class);
        bean.add("qiu");
    }

}

 运行结果

使用注解开发,效率是不是很高,没错,在实际开发中注解和 Java 配置类确实是很开。也是开发中最常用的一种,当然 xml 也可以,只不过是使用 Java 配置类加注解开发会比较好看,阅读性好。大家根据自己的选择使用,推荐使用的是这种方法。

 

二、使用Spring AOP 注解结合配置类和使用 xml 有什么区别

使用Spring AOP注解和配置类与使用XML方式定义AOP有以下区别:

  1. 配置方式:使用注解的方式是通过在代码中添加注解来定义切面、切点和通知等,而使用XML方式是通过编写XML配置文件来定义AOP元素。

  2. 代码可读性:使用注解方式可以直接在Java代码中看到AOP相关的配置,使得代码更加紧凑和可读。而使用XML方式需要打开独立的XML配置文件,可能会增加代码的复杂性。

  3. 配置灵活性:使用注解方式可以更灵活地进行配置,可以直接将切面和通知应用于具体的类或方法,也可以使用通配符来匹配一组类或方法。而使用XML方式则需要在配置文件中明确指定切入点和通知的目标。

  4. IDE支持:使用注解方式可以充分利用现代IDE的代码提示和自动补全功能,提升开发效率。而使用XML方式则需要手动编写XML配置文件,可能会增加书写错误的风险。

  5. 集成和维护:使用注解方式可以更加方便地进行代码集成和版本控制,同时也更容易进行维护和重构。而使用XML方式则可能导致配置文件随着系统变得越来越庞大和难以管理。

总的来说,注解方式相对于XML方式更加简洁、灵活和方便于代码维护,但对于复杂的AOP配置或需要与第三方库进行集成的情况,XML方式可能更为适用。选择使用哪种方式还取决于个人或团队的偏好和具体的项目需求。

三、gitee 案例

案例完整地址:https://gitee.com/qiu-feng1/spring-framework.git

 

你可能感兴趣的:(spring,framework,spring,java,数据库)