11、Spring之基于注解的AOP

这篇具有很好参考价值的文章主要介绍了11、Spring之基于注解的AOP。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

11.1、环境搭建

创建名为spring_aop_annotation的新module,过程参考9.1节

11.1.1、配置打包方式和依赖

11、Spring之基于注解的AOP

注意:AOP需要在IOC的基础上实现,因此需要导入IOC的依赖

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>org.rain</groupId>
    <artifactId>spring_aop_annotation</artifactId>
    <version>1.0-SNAPSHOT</version>
    <packaging>jar</packaging>

    <dependencies>
        <!-- Spring-IOC的依赖 -->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-context</artifactId>
            <version>5.3.1</version>
        </dependency>

        <!-- spring-AOP的依赖 -->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-aspects</artifactId>
            <version>5.3.1</version>
        </dependency>

        <!-- junit测试 -->
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.12</version>
            <scope>test</scope>
        </dependency>
    </dependencies>

</project>

11.1.2、创建Calculator接口及实现类

11、Spring之基于注解的AOP

package org.rain.spring.aop.annotation;

/**
 * @author liaojy
 * @date 2023/8/12 - 17:43
 */
public interface Calculator {

    int add(int i, int j);
    int sub(int i, int j);
    int mul(int i, int j);
    int div(int i, int j);

}

11、Spring之基于注解的AOP

package org.rain.spring.aop.annotation;

import org.springframework.stereotype.Component;

/**
 * @author liaojy
 * @date 2023/8/12 - 17:45
 */

// @Component注解保证这个目标类能够放入IOC容器
@Component
public class CalculatorImpl implements Calculator {
    public int add(int i, int j) {

        int result = i + j;
        System.out.println("方法内部 result = " + result);
        return result;

    }

    public int sub(int i, int j) {

        int result = i - j;
        System.out.println("方法内部 result = " + result);
        return result;

    }

    public int mul(int i, int j) {

        int result = i * j;
        System.out.println("方法内部 result = " + result);
        return result;

    }

    public int div(int i, int j) {

        int result = i / j;
        System.out.println("方法内部 result = " + result);
        return result;

    }
}

11.1.3、创建切面类LoggerAspect

11、Spring之基于注解的AOP

package org.rain.spring.aop.annotation;

import org.aspectj.lang.annotation.Aspect;
import org.springframework.stereotype.Component;

/**
 * @author liaojy
 * @date 2023/8/12 - 17:56
 */

// @Aspect表示这个类是一个切面类
@Aspect
// @Component注解保证这个切面类能够放入IOC容器
@Component
public class LoggerAspect {
}

11.1.4、创建spring配置文件

11、Spring之基于注解的AOP

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:context="http://www.springframework.org/schema/context"
       xmlns:aop="http://www.springframework.org/schema/aop"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/aop https://www.springframework.org/schema/aop/spring-aop.xsd">

    <!--
        对指定的package进行扫描,将使用组件注解的类的对象(本示例是目标对象和切面对象),交给spring的ioc容器来管理
    -->
    <context:component-scan base-package="org.rain.spring.aop.annotation"></context:component-scan>

    <!--
        开启基于注解的AOP功能,该功能会为目标对象自动生成代理
    -->
    <aop:aspectj-autoproxy></aop:aspectj-autoproxy>

</beans>

11.2、前置通知的使用

11.2.1、基本示例

11.2.1.1、配置前置方法

11、Spring之基于注解的AOP

    /*
    * @Before注解:用于将方法标识为前置通知(方法)
    * @Before注解的value属性值为切入点表达式,其作用是将该前置通知(方法)安插到对应目标方法的连接点上
    * */
    @Before("execution(public int org.rain.spring.aop.annotation.CalculatorImpl.add(int , int))")
    public void beforeMethod(){
        System.out.println("LoggerAspect,前置通知");
    }

11.2.1.2、测试使用效果

11、Spring之基于注解的AOP

由控制台日志可知,切面类的前置通知(方法),通过切入点表达式,作用到了目标方法的连接点上

    @Test
    public void testAOPByAnnotation(){

        ApplicationContext ioc = new ClassPathXmlApplicationContext("spring-aop-annotation.xml");

        // 注意:这里不能直接获取目标对象来使用;因为使用了AOP之后,IOC容器中就只有对应目标对象的代理对象;
        // 如果强行获取目标对象,则报错:NoSuchBeanDefinitionException
        //Calculator calculator = ioc.getBean(CalculatorImpl.class);

        // 虽然不知道代理对象的类名,但可以通过代理对象和目标对象共同实现的接口类型来从ioc容器中获取代理对象
        Calculator calculator = ioc.getBean(Calculator.class);

        // 只能通过代理对象来访问目标对象中的方法
        calculator.add(1,2);

    }

11.2.2、高级示例

11.2.2.1、改进前置方法

11、Spring之基于注解的AOP

该示例中(前置)通知方法引入了连接点参数,通过连接点参数,可以动态获取(切入点表达式)对应的目标方法的名称和参数列表

    /*
    * @Before注解:用于将方法标识为前置通知(方法)
    * @Before注解的value属性值为切入点表达式,其作用是将该前置通知(方法)安插到对应目标方法的连接点上
    * */
    @Before("execution(public int org.rain.spring.aop.annotation.CalculatorImpl.add(int , int))")
    // joinPoint参数:可以获取(通过切入点表达式定位出的)连接点的相关信息
    public void beforeMethod(JoinPoint joinPoint){
        // 获取连接点所对应目标方法的名称
        String methodName = joinPoint.getSignature().getName();
        // 获取连接点所对应目标方法的参数列表
        Object[] args = joinPoint.getArgs();
        System.out.println("LoggerAspect-->前置通知,方法名:"+methodName+",参数:"+ Arrays.toString(args));
    }

11.2.2.2、测试使用效果

11、Spring之基于注解的AOP

11.3、切入点表达式的进阶用法

11.3.1、高频用法示例

11、Spring之基于注解的AOP

    // @Before("execution(public int org.rain.spring.aop.annotation.CalculatorImpl.add(int , int))")

    /**
     * 第一个*表示任意访问修饰符和返回值类型,
     * 第二个*表示该类的任意方法名称,
     * (..)表示方法的任意参数列表
     * 在类的位置也可以使用*,表示当前包下所有的类,
     * 在包的位置也可以使用*,表示当前包下所有的子包,
     */
    @Before("execution(* org.rain.spring.aop.annotation.CalculatorImpl.*(..))")
    public void beforeMethod(JoinPoint joinPoint){
        String methodName = joinPoint.getSignature().getName();
        Object[] args = joinPoint.getArgs();
        System.out.println("LoggerAspect-->前置通知,方法名:"+methodName+",参数:"+ Arrays.toString(args));
    }

11.3.2、详细语法图解

11、Spring之基于注解的AOP

11.3.3、复用切入点表达式

11.3.3.1、声明公共的切入点表达式

11、Spring之基于注解的AOP

    @Pointcut("execution(* org.rain.spring.aop.annotation.CalculatorImpl.*(..))")
    public void pointCutOne(){}

11.3.3.2、在同一个切面类中复用

11、Spring之基于注解的AOP

    // @Before注解的value属性值,可以设置为使用了@Pointcut注解标识的方法名,从而复用该@Pointcut注解定义的切入点表达式
    @Before("pointCutOne()")
    public void beforeMethod(JoinPoint joinPoint){
        String methodName = joinPoint.getSignature().getName();
        Object[] args = joinPoint.getArgs();
        System.out.println("LoggerAspect-->前置通知,方法名:"+methodName+",参数:"+ Arrays.toString(args));
    }

11.3.3.3、在不同一个切面类中复用

    // 复用其他切面类中@Pointcut注解定义的切入点表达式,
	// @Before注解的value属性值,需要设置为使用了@Pointcut注解标识的(全限定类名+)方法名
    @Before("org.rain.spring.aop.annotation.LoggerAspect.pointCutOne()")
    public void beforeMethod(JoinPoint joinPoint){
        String methodName = joinPoint.getSignature().getName();
        Object[] args = joinPoint.getArgs();
        System.out.println("LoggerAspect-->前置通知,方法名:"+methodName+",参数:"+ Arrays.toString(args));
    }

11.4、其他通知的使用

11.4.1、后置通知

11.4.1.1、配置后置方法

11、Spring之基于注解的AOP

    // @After注解:用于将方法标识为后置通知(方法)
    @After("pointCutOne()")
    public void afterMethod(JoinPoint joinPoint){
        String methodName = joinPoint.getSignature().getName();
        Object[] args = joinPoint.getArgs();
        System.out.println("LoggerAspect-->后置通知,方法名:"+methodName+",参数:"+ Arrays.toString(args));
    }

11.4.1.2、测试使用效果

11、Spring之基于注解的AOP

由控制台日志可知,后置通知在目标对象方法的finally子句中执行(一般用于释放资源)

    @Test
    public void testAOPByAnnotation(){

        ApplicationContext ioc = new ClassPathXmlApplicationContext("spring-aop-annotation.xml");

        // 虽然不知道代理对象的类名,但可以通过代理对象和目标对象共同实现的接口类型来从ioc容器中获取代理对象
        Calculator calculator = ioc.getBean(Calculator.class);

        // 只能通过代理对象来访问目标对象中的方法
        calculator.div(1,0);

    }

11.4.2、返回通知

11.4.2.1、配置返回通知

11、Spring之基于注解的AOP

    /**
     * @AfterReturning注解:用于将方法标识为返回通知(方法)
     *  returning属性:指定(返回)通知方法中的某个参数(名),用于接收目标对象方法的返回值
     */
    @AfterReturning(value = "pointCutOne()",returning = "result")
    public void afterReturningMethod(JoinPoint joinPoint,Object result){
        String methodName = joinPoint.getSignature().getName();
        Object[] args = joinPoint.getArgs();
        System.out.println("LoggerAspect-->返回通知,方法名:"+methodName+",结果:"+ result);
    }

11.4.2.2、测试使用效果

11、Spring之基于注解的AOP

由控制台日志可知,返回通知在目标对象方法的返回值之后执行

    @Test
    public void testAOPByAnnotation(){

        ApplicationContext ioc = new ClassPathXmlApplicationContext("spring-aop-annotation.xml");

        // 虽然不知道代理对象的类名,但可以通过代理对象和目标对象共同实现的接口类型来从ioc容器中获取代理对象
        Calculator calculator = ioc.getBean(Calculator.class);

        // 只能通过代理对象来访问目标对象中的方法
        calculator.div(1,1);

    }

11.4.3、异常通知

11.4.3.1、配置异常通知

11、Spring之基于注解的AOP

    /**
     * @AfterThrowing注解:用于将方法标识为异常通知(方法)
     *  throwing属性:指定(异常)通知方法中的某个参数(名),用于接收目标对象方法出现的异常
     */
    @AfterThrowing(value = "pointCutOne()",throwing = "ex")
    public void afterThrowingMethod(JoinPoint joinPoint,Exception ex){
        String methodName = joinPoint.getSignature().getName();
        Object[] args = joinPoint.getArgs();
        System.out.println("LoggerAspect-->异常通知,方法名:"+methodName+",异常:"+ ex);
    }

11.4.3.2、测试使用效果

11、Spring之基于注解的AOP

由控制台日志可知,异常通知在目标对象方法的catch子句中执行

    @Test
    public void testAOPByAnnotation(){

        ApplicationContext ioc = new ClassPathXmlApplicationContext("spring-aop-annotation.xml");

        // 虽然不知道代理对象的类名,但可以通过代理对象和目标对象共同实现的接口类型来从ioc容器中获取代理对象
        Calculator calculator = ioc.getBean(Calculator.class);

        // 只能通过代理对象来访问目标对象中的方法
        calculator.div(1,0);

    }

11.4.4、通知的执行顺序

11.4.4.1、Spring版本5.3.x以前

  • 前置通知

  • 目标操作

  • 后置通知

  • 返回通知或异常通知

11.4.4.2、Spring版本5.3.x以后

本示例

  • 前置通知

  • 目标操作

  • 返回通知或异常通知

  • 后置通知

11.5、环绕通知

11.5.1、配置环绕通知

11、Spring之基于注解的AOP

环绕通知和动态代理的形式,非常相似

    /**
     * @Around注解:用于将方法标识为环绕通知(方法)
     *  环绕通知(方法)使用的参数是ProceedingJoinPoint类型
     *  环绕通知(方法)的返回值,必须和目标对象方法的返回值一致
     */
    @Around("pointCutOne()")
    public Object aroundMethod(ProceedingJoinPoint proceedingJoinPoint){
        String methodName = proceedingJoinPoint.getSignature().getName();
        Object[] args = proceedingJoinPoint.getArgs();
        Object result = null;
        try {
            System.out.println("LoggerAspect-->环绕前置通知,方法名:"+methodName+",参数:"+ Arrays.toString(args));
            // 表示目标对象方法的执行
            result = proceedingJoinPoint.proceed();
            System.out.println("LoggerAspect-->环绕返回通知,方法名:"+methodName+",结果:"+ result);
        } catch (Throwable throwable) {
            throwable.printStackTrace();
            System.out.println("LoggerAspect-->环绕异常通知,方法名:"+methodName+",异常:"+ throwable);
        }finally {
            System.out.println("LoggerAspect-->环绕后置通知,方法名:"+methodName+",参数:"+ Arrays.toString(args));
        }
        return result;
    }

11.5.2、测试使用效果

11、Spring之基于注解的AOP

注意:因为环绕通知包括了其他四种通知,所以一般要么配置其他四种通知,要么只配置环绕通知;本示例为了展示效果才同时配置

    @Test
    public void testAOPByAnnotation(){

        ApplicationContext ioc = new ClassPathXmlApplicationContext("spring-aop-annotation.xml");

        // 虽然不知道代理对象的类名,但可以通过代理对象和目标对象共同实现的接口类型来从ioc容器中获取代理对象
        Calculator calculator = ioc.getBean(Calculator.class);

        // 只能通过代理对象来访问目标对象中的方法
        calculator.div(1,1);

    }

11.6、切面的优先级

11.6.1、创建其他切面类ValidateAspect

11、Spring之基于注解的AOP

package org.rain.spring.aop.annotation;

import org.aspectj.lang.annotation.Aspect;
import org.springframework.stereotype.Component;

/**
 * @author liaojy
 * @date 2023/8/15 - 7:49
 */
@Aspect
@Component
public class ValidateAspect {
}

11.6.2、配置前置通知方法

11、Spring之基于注解的AOP

    @Before("org.rain.spring.aop.annotation.LoggerAspect.pointCutOne()")
    public void beforeMethod(){
        System.out.println("ValidateAspect-->前置通知");
    }

11.6.3、测试使用效果

11、Spring之基于注解的AOP

由控制台日志可知,ValidateAspect切面的前置通知方法生效了,但执行顺序在LoggerAspect切面的前置通知方法的后面

    @Test
    public void testAOPByAnnotation(){

        ApplicationContext ioc = new ClassPathXmlApplicationContext("spring-aop-annotation.xml");

        // 虽然不知道代理对象的类名,但可以通过代理对象和目标对象共同实现的接口类型来从ioc容器中获取代理对象
        Calculator calculator = ioc.getBean(Calculator.class);

        // 只能通过代理对象来访问目标对象中的方法
        calculator.div(1,1);

    }

11.6.4、调整切面的优先级

11、Spring之基于注解的AOP

package org.rain.spring.aop.annotation;

import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;

/**
 * @author liaojy
 * @date 2023/8/15 - 7:49
 */
@Aspect
@Component
// @Order注解:用于设置切面的优先级,value属性值越小,优先级越高,默认值为Integer的最大值
@Order(2023)
public class ValidateAspect {

    @Before("org.rain.spring.aop.annotation.LoggerAspect.pointCutOne()")
    public void beforeMethod(){
        System.out.println("ValidateAspect-->前置通知");
    }

}

11.6.5、测试调整后的效果

11、Spring之基于注解的AOP

由控制台日志可知,ValidateAspect切面的前置通知方法的执行顺序,在LoggerAspect切面的前置通知方法的前面

这是因为ValidateAspect切面的@Order注解的value属性值已设为2023,要小于LoggerAspect切面所使用的默认值(Integer的最大值2147483647)

11.7、扩展知识

11、Spring之基于注解的AOP

  • AspectJ本质上是静态代理,将代理逻辑“织入”被代理的目标类编译得到的字节码文件,但最终效果是动态的。

  • weaver就是织入器,Spring只是借用了AspectJ中的注解。

11、Spring之基于注解的AOP文章来源地址https://www.toymoban.com/news/detail-648385.html

到了这里,关于11、Spring之基于注解的AOP的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 【Spring教程11】Spring框架实战:IOC/DI注解开发管理第三方bean的全面深入详解

    欢迎大家回到《 Java教程之Spring30天快速入门》,本教程所有示例均基于Maven实现,如果您对Maven还很陌生,请移步本人的博文《 如何在windows11下安装Maven并配置以及 IDEA配置Maven环境》,本文的上一篇为《 纯注解开发模式下的依赖注入和读取properties配置文件》 前面定义bean的时

    2024年02月04日
    浏览(59)
  • Spring基于注解管理bean及全注解开发

    Spring是一款主流的Java EE 轻量级开源框架,目的是用于简化Java企业级引用的开发难度和开发周期。从简单性、可测试性和松耦合度的角度而言,任何Java应用都可以从Spring中受益。Spring框架提供自己提供功能外,还提供整合其他技术和框架的能力。 Spring自诞生以来备受青睐,

    2024年02月14日
    浏览(40)
  • 注解实现(基于Spring AOP)

    切入点表达式 Spring AOP 支持的切入点主要有以下几种: execution:用于匹配方法执行的连接点。这是最常用的切入点指示器。你可以指定具体的方法,或者类来匹配。 例如: execution(* com.example.service.*.*(..)) ,这个表达式表示匹配 com.example.service 包下的所有类的所有方法。 wit

    2024年02月16日
    浏览(42)
  • Spring02-Spring注解的使用、基于注解的IOC、纯注解配置、整合Junit、AOP入门、基于配置文件的AOP、切入点表达式、基于配置的文件环绕通知

    学习基于注解的 IOC 配置,即注解配置 和 XML 配置要实现的功能都是一样的,都是要降低程序间的耦合。只是配置的形式不一样。 关于实际的开发中到底使用xml还是注解,每家公司有着不同的使用习惯 , 所以这两种配置方式我们都需要掌握。 把 Spring 的 xml 配置内容改为使用

    2024年02月03日
    浏览(74)
  • Spring——基于注解的AOP配置

    1.1.pom.xml 1.2.dao 1.3.service 1.4.applicationContext.xml 1.5.测试 2.1.applicationContext.xml 2.2.AOP配置 常用注解 @Aspect:把当前类声明为切面类 @Before:前置通知,可以指定切入点表达式 @AfterReturning:后置【try】通知,可以指定切入点表达式 @AfterThrowing:异常【catch】通知,可以指定切入点表达

    2024年01月22日
    浏览(37)
  • Spring MVC 三 :基于注解配置

    Servlet3.0 Servlet3.0是基于注解配置的理论基础。 Servlet3.0引入了基于注解配置Servlet的规范,提出了可拔插的ServletContext初始化方式,引入了一个叫ServletContainerInitializer的接口。 An instance of the ServletContainerInitializer is looked up via the jar services API by the container at container / application sta

    2024年02月11日
    浏览(37)
  • Spring FrameWork从入门到NB -基于注解配置 & @Autowired注解

    基于注解的配置 其实前面我们已经在基于注解配置了,这是由于个人比较喜欢基于注解配置。 Spring官方文档提过一个问题:基于注解配置要好于基于xml的配置吗?其实各有千秋,根据个人喜好吧。重要的是Spring可以支持基于注解配置,也可以支持基于xml配置文件配置,也可

    2024年02月10日
    浏览(53)
  • 8、Spring之基于注解的自动装配

    由控制台日志可知,组件注解+组件扫描,存在尚未实现依赖注入的问题 @Autowired:实现自动装配功能的注解,其能够标识的位置有三处 默认通过byType的方式,在IOC容器中匹配类型相同的bean,为属性赋值 如果有多个类型匹配的bean,则自动转换为通过byName的方式,在IOC容器中匹

    2024年02月14日
    浏览(36)
  • 7、Spring之基于注解管理bean

    本质上:所有一切的操作都是Java代码来完成的,XML和注解只是告诉框架中的Java代码如何执行。 创建名为spring_ioc_annotation的新module,过程参考3.1节 注解 含义 @Component 将类标识为普通组件 @Controller 将类标识为控制层组件 @Service 将类标识为业务层组件 @Repository 将类标识为持久

    2024年02月14日
    浏览(40)
  • Spring系列三:基于注解配置bean

    上文中, 我们学习到了 Spring系列二:基于XML配置bean 接下来我们学习, 通过注解配置bean 基于注解的方式配置 bean , 主要是项目开发中的组件, 比如 Controller, Service 和 Dao. 组件注解的形式有 1. @Component 表示当前注解标识的是一个组件 2. @Controller 表示当前注解标识的是一个控制器

    2024年02月13日
    浏览(36)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包