Spring框架-AOP(面向切面编程)

2024-01-09 16:02:00

AOP(面向切面编程)

什么是AOP

Spring AOP(Aspect-Oriented Programming,面向切面编程)是 Spring 框架的一个重要特性。它提供了一种通过横向切割应用程序的方式来解耦和增强代码的能力。AOP 可以在不修改原有业务逻辑的情况下,通过将通用功能模块化并横向应用于多个不同的对象上,实现横切关注点的统一处理。

比如系统中有很多业务逻辑类,在每个类中都需要记录日志,那么使用 Spring AOP 可以让你只编写一次日志记录的代码,然后将这段代码应用到所有的业务逻辑类上,从而避免了大量的重复代码和修改代码的麻烦。

在Spring AOP中,核心概念有:

  1. 切面(Aspect):切面是跨越一个或多个对象的横切关注点的模块化单位。它由切点(Pointcut)和通知(Advice)组成。
  2. 切点(Pointcut):切点是指在应用程序中定义的一个或多个 Join Point 的集合。它定义了切面需要织入的连接点。
  3. 通知(Advice):通知是切面在特定切点上执行的动作。通知类型包括前置通知(Before)、后置通知(After)、返回通知(After Returning)、异常通知(After Throwing)和环绕通知(Around)。
  4. 连接点(Join Point):连接点是在应用程序执行过程中可以插入切面的点。例如方法调用、方法返回、异常抛出等。
  5. 织入(Weaving):织入是将切面应用到目标对象上的过程。它可以在编译时、类加载时或运行时进行。

SpringAOP的实现方式

基于代理的 AOP

通过代理模式实现 AOP,即创建目标对象的代理对象,在代理对象中织入切面逻辑。Spring 默认采用基于代理的 AOP 实现。

pom.xml

<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-aop</artifactId>
    <version>5.3.13</version>
</dependency>

<dependency>
    <groupId>org.aspectj</groupId>
    <artifactId>aspectjweaver</artifactId>
    <version>1.9.7</version>
</dependency>
  • spring-aop:支持面向切面编程AOP,提供了 AOP的基本功能,包含定义切点,通知和切面等,在使用SpringAOP时,需要引入该依赖来使用相关的类和注解
  • aspectjweaver:AspectJ的运行时库,用于实现AOP的底层功能,AspectJ是一个功能强大的Java AOP框架,它扩展了Java语言,提供了更丰富的AOP功能,Spring AOP在内部使用了 AspectJ 的技术来实现 AOP。

这两个依赖是 Spring AOP 的核心组成部分,通过结合使用它们,可以方便地在 Spring 应用程序中实现基于代理的 AOP。spring-aop 提供了高级的 AOP 抽象和注解,而 aspectjweaver 则提供了底层的 AOP 运行时支持。

UserService.java

package com.sin.service;

import org.springframework.stereotype.Service;

/**
 * @createTime 2024/1/3 14:37
 * @createAuthor SIN
 * @use
 */
public interface UserService {
    String getUser(String username);
    void saveUser(String username, String password);
}

UserServiceImpl.java

package com.sin.service.impl;

import com.sin.service.UserService;
import org.springframework.stereotype.Service;

/**
 * @createTime 2024/1/3 15:14
 * @createAuthor SIN
 * @use
 */
@Service
public class UserServiceImpl implements UserService {
    public String getUser(String username) {
        System.out.println("获取用户: " + username);
        return "用户: " + username;
    }

    public void saveUser(String username, String password) {
        System.out.println("保存用户: " + username + ", 密码: " + password);
    }
}

LoggingAspect.java

package com.sin.aspect;

import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.context.annotation.Bean;

/**
 * @createTime 2024/1/3 14:39
 * @createAuthor SIN
 * @use
 */
public class LoggingAspect {

    public void beforeAdvice() {
        System.out.println("在方法之前执行");
    }

    public void afterAdvice() {
        System.out.println("在方法之后执行");
    }
}

applicationContext.xml

<?xml version="1.0" encoding="UTF-8"?><!-- XML声明,xml版本和编码格式 -->

<!-- spring配置文件起点  -->
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       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/aop
       https://www.springframework.org/schema/aop/spring-aop.xsd">

    <!--  配置目标对象和切面对象  -->
    <bean id="userService" class="com.sin.service.impl.UserServiceImpl"/>
    <bean id="loggingAspect" class="com.sin.aspect.LoggingAspect"/>

    <!--
        <aop:config> 用于配置整个 AOP 代理的参数和行为
          <aop:aspect> : 表示一个切面,ref属性指定了引用的切面对象
            <aop:before> :前置通知,method属性指定了切面对象中的通知方法,pointcut属性指定了切点表达式
            <aop:after> : 后置通知,
    -->
    <aop:config>
        <aop:aspect ref="loggingAspect">
            <!--
                execution(* com.sin.service.UserService.*(..))
                execution() 表示执行的方法
                * 表示匹配任意返回类型
                com.sin.service.UserService 表示UserService接口的完整路径
                * 表示匹配任意方法名
                (..) 表示任意参数列表
            -->
            <aop:before method="beforeAdvice" pointcut="execution(* com.sin.service.UserService.*(..))"/>
            <aop:after method="afterAdvice" pointcut="execution(* com.sin.service.UserService.*(..))"/>
        </aop:aspect>
    </aop:config>

</beans>

AppTest.java

package com.sin.test;

import com.sin.service.UserService;
import org.junit.Test;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

/**
 * @createTime 2024/1/3 8:41
 * @createAuthor SIN
 * @use
 */
public class AppTest {

    @Test
    public void test() {
        ApplicationContext  context =  new ClassPathXmlApplicationContext("applicationContext.xml");
        UserService userService = (UserService) context.getBean("userService");
        userService.getUser("张三");
        userService.saveUser("李四","123456");
    }
}

LoggingAspect是一个切面类,它包含了beforeAdvice()和afterAdvice()两个通知方法,这些方法将在 UserService 接口的所有方法执行前后被调用。在这些方法中,我们可以实现额外的逻辑,比如记录日志、检查权限等。

请添加图片描述

AOP实现日志记录

UserService.java

package com.sin.service;

import org.springframework.stereotype.Service;

/**
 * @createTime 2024/1/3 14:37
 * @createAuthor SIN
 * @use
 */
public interface UserService {
    String getUser(String username);
    void saveUser(String username, String password);
}

UserServiceImpl.java

package com.sin.service.impl;

import com.sin.service.UserService;
import org.springframework.stereotype.Service;

/**
 * @createTime 2024/1/3 15:14
 * @createAuthor SIN
 * @use
 */
@Service
public class UserServiceImpl implements UserService {
    public String getUser(String username) {
        System.out.println("获取用户: " + username);
        return "用户: " + username;
    }

    public void saveUser(String username, String password) {
        System.out.println("保存用户: " + username + ", 密码: " + password);
    }
}

LoggingAspect.java

package com.sin.aspect;

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

/**
 * @createTime 2024/1/3 14:39
 * @createAuthor SIN
 * @use 切面类,用于定义要插入的日志逻辑
 */
@Aspect // 声明该类为切面类
@Component // 该类作为Spring的组件进行扫描和管理
public class LoggingAspect {

    /**
     * 切面方法
     */
    @Around("execution(* com.sin.service.UserService.*(..))") // 定义切入点表达式,表示对com.sin.service.UserService中的所有方法进行环绕通知
    public Object logMethodExecutionTime(ProceedingJoinPoint joinPoint) throws Throwable {
        long startTime = System.currentTimeMillis(); // 开始时间

        Object result = joinPoint.proceed(); // 执行目标方法

        long endTime = System.currentTimeMillis(); // 结束方法
        long executedTime = endTime - startTime; // 执行方法时间

        // 输出方法执行信息和时间
        System.out.println(joinPoint.getSignature() + "执行时间"+ executedTime + "ms");

        return result;
    }
}

AppConfig.java

package com.sin.config;

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

/**
 * @createTime 2024/1/2 10:56
 * @createAuthor SIN
 * @use
 */
@Configuration
@EnableAspectJAutoProxy // 启用 AspectJ 自动代理,运行时自动创建代理对象来实现切面的功能
@ComponentScan(basePackages = "com.sin") // 扫描组件
public class AppConfig {



}

AppTest.java

package com.sin.test;


import com.sin.config.AppConfig;
import com.sin.service.UserService;
import org.junit.Test;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;

/**
 * @createTime 2024/1/4 9:17
 * @createAuthor SIN
 * @use
 */
public class AppTest {

    @Test
    public void test(){
        AnnotationConfigApplicationContext annotationConfigApplicationContext = new AnnotationConfigApplicationContext(AppConfig.class);

        UserService userService = annotationConfigApplicationContext.getBean(UserService.class);
        userService.getUser("张三");
        userService.saveUser("李四","121212");
    }
}

文章来源:https://blog.csdn.net/qq_44715376/article/details/135480244
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。