Spring 全家桶之 Spring Framework 5.3(五)- AOP(一)

简介: Spring 全家桶之 Spring Framework 5.3(五)- AOP

一、动态代理

AOP 即 Aspect Oriented Programming面向切面编程,它是基于面向对象编程之上的新的编程思想,是指将某段代码动态的切入到指定方法的指定位置并运行。

新建一个maven项目spring-bean-aop,导入依赖

<properties>
    <spring-version>5.3.13</spring-version>
</properties>
<dependencies>
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-beans</artifactId>
        <version>${spring-version}</version>
    </dependency>
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-context</artifactId>
        <version>${spring-version}</version>
    </dependency>
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-test</artifactId>
        <version>${spring-version}</version>
    </dependency>
    <dependency>
        <groupId>junit</groupId>
        <artifactId>junit</artifactId>
        <version>4.12</version>
        <scope>test</scope>
    </dependency>
</dependencies>
复制代码

在util包定义一个计算器接口类,定义四个方法加减乘除,增加一个实现类,实现加减乘除四个方法

public interface Calculator {
    int add(int x, int y);
    int sub(int x, int y);
    int mul(int x, int y);
    int div(int x, int y);
}
复制代码

在util.impl包中增加实现类

@Component
public class AppleCalculator implements Calculator {
    @Override
    public int add(int x, int y) {
        int result = x + y;
        return result;
    }
    @Override
    public int sub(int x, int y) {
        int result = x - y;
        return result;
    }
    @Override
    public int mul(int x, int y) {
        int result = x * y;
        return result;
    }
    @Override
    public int div(int x, int y) {
        int result = x / y;
        return result;
    }
}
复制代码

增加application.xml,将组件注入容器中

<?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:util="http://www.springframework.org/schema/util"
       xmlns:p="http://www.springframework.org/schema/p" xmlns:context="http://www.springframework.org/schema/context"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
        https://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/util
        http://www.springframework.org/schema/util/spring-util-4.3.xsd http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd">
    <context:component-scan base-package="com.citi">
    </context:component-scan>
</beans>
复制代码

生成Spring Test测试类AppleCalculatorTest

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = "classpath:application.xml")
public class AppleCalculatorTest {
    @Autowired
    private AppleCalculator appleCalculator;
    @Test
    public void add() {
    }
    @Test
    public void sub() {
    }
    @Test
    public void mul() {
    }
    @Test
    public void div() {
    }
}
复制代码

给每个方法运行前后增加日志输出,如何在不改变代码的前提下完成?

动态代理方式解决

首先创建一个代理CalculatorProxy,包含一个方法,可以返回代理对象,给代理对象加一些方法,通过代理对象执行目标方法

创建一个代理,静态方法getProxy用户获取Calculator的代理对象,

public class CalculatorProxy {
    public static Calculator getProxy(Calculator calculator){
        // 方法执行器
        InvocationHandler invocationHandler = (Object proxy, Method method, Object[] args) -> {
            System.out.println("动态代理执行目标方法:" + method.getName());
            // 反射执行目标方法
            Object result = method.invoke(calculator, args);
            return result;
        };
        // 类接口
        Class<?>[] interfaces = calculator.getClass().getInterfaces();
        // 类加载器
        ClassLoader classLoader = calculator.getClass().getClassLoader();
        Object proxyInstance = Proxy.newProxyInstance(classLoader, interfaces, invocationHandler);
        return (Calculator) proxyInstance;
    }
}
复制代码

该方法需要传入被代理的对象,通过newProxyInstance()创建一个代理,再强转为目标类返回,其中参数invocationHandler为目标方法执行器,通过invoke方法执行目标方法并使用Object接收保存后返回目标方法执行后的返回值, intefaces为被代理类的类接口列表,classLoader为被代理类的类加载器

在测试类中通过getProxy方法获取Calculator的代理对象,使用代理对象执行方法

@Test
public void add() {
    Calculator calculator = CalculatorProxy.getProxy(appleCalculator);
    calculator.add(2,1);
}
复制代码

根据控制台打印可以看出,代理类成功执行了add方法

b12daddb19e540f3a54fbeebff02b5e5_tplv-k3u1fbpfcp-zoom-in-crop-mark_4536_0_0_0.png

那么要实现在方法执行前后增加日志记录,就可以修改代理类CalculatorProxy的getProxy方法,在调用invoke方法前后增加日志输出,包括对方法执行异常时的处理

public class CalculatorProxy {
    public static Calculator getProxy(Calculator calculator){
        // 方法执行器
        InvocationHandler invocationHandler = (Object proxy, Method method, Object[] args) -> {
            Object result = null;
            try {
                // 反射执行目标方法
                // System.out.println("动态代理执行目标方法:" + method.getName());
                System.out.println(method.getName() + "方法开始执行,参数为:" + Arrays.toString(args));
                result = method.invoke(calculator, args);
                System.out.println(method.getName() + "方法执行结束,结果为:" + result);
            } catch (Exception e){
                System.out.println(method.getName() + "方法执行异常,异常信息:" + e.getCause());
            } finally {
                System.out.println(method.getName() + "方法最终执行结束");
                return result;
            }
        };
        // 类接口
        Class<?>[] interfaces = calculator.getClass().getInterfaces();
        // 类加载器
        ClassLoader classLoader = calculator.getClass().getClassLoader();
        Object proxyInstance = Proxy.newProxyInstance(classLoader, interfaces, invocationHandler);
        return (Calculator) proxyInstance;
    }
}
复制代码

再次执行测试类

image.png

定义异常情况

@Test
public void div() {
    Calculator calculator = CalculatorProxy.getProxy(appleCalculator);
    calculator.div(2,0);
}
复制代码

执行异常情况的测试方法div,输出了异常的原因

image.png

如果目标对象没有实现任何接口,是无法创建代理对象的,而Spring AOP可以解决这个问题,Spring AOP 底层就是动态代理


相关文章
|
1月前
|
XML Java 测试技术
Spring AOP—通知类型 和 切入点表达式 万字详解(通俗易懂)
Spring 第五节 AOP——切入点表达式 万字详解!
119 25
|
1月前
|
XML 安全 Java
Spring AOP—深入动态代理 万字详解(通俗易懂)
Spring 第四节 AOP——动态代理 万字详解!
91 24
|
2月前
|
XML Java 开发者
Spring Boot中的AOP实现
Spring AOP(面向切面编程)允许开发者在不修改原有业务逻辑的情况下增强功能,基于代理模式拦截和增强方法调用。Spring Boot通过集成Spring AOP和AspectJ简化了AOP的使用,只需添加依赖并定义切面类。关键概念包括切面、通知和切点。切面类使用`@Aspect`和`@Component`注解标注,通知定义切面行为,切点定义应用位置。Spring Boot自动检测并创建代理对象,支持JDK动态代理和CGLIB代理。通过源码分析可深入了解其实现细节,优化应用功能。
136 6
|
2月前
|
存储 安全 Java
Spring Boot 3 集成Spring AOP实现系统日志记录
本文介绍了如何在Spring Boot 3中集成Spring AOP实现系统日志记录功能。通过定义`SysLog`注解和配置相应的AOP切面,可以在方法执行前后自动记录日志信息,包括操作的开始时间、结束时间、请求参数、返回结果、异常信息等,并将这些信息保存到数据库中。此外,还使用了`ThreadLocal`变量来存储每个线程独立的日志数据,确保线程安全。文中还展示了项目实战中的部分代码片段,以及基于Spring Boot 3 + Vue 3构建的快速开发框架的简介与内置功能列表。此框架结合了当前主流技术栈,提供了用户管理、权限控制、接口文档自动生成等多项实用特性。
101 8
|
4月前
|
监控 安全 Java
什么是AOP?如何与Spring Boot一起使用?
什么是AOP?如何与Spring Boot一起使用?
121 5
|
4月前
|
XML 监控 安全
深入调查研究Spring AOP
【11月更文挑战第15天】
68 5
|
4月前
|
Java 开发者 Spring
Spring AOP 底层原理技术分享
Spring AOP(面向切面编程)是Spring框架中一个强大的功能,它允许开发者在不修改业务逻辑代码的情况下,增加额外的功能,如日志记录、事务管理等。本文将深入探讨Spring AOP的底层原理,包括其核心概念、实现方式以及如何与Spring框架协同工作。
|
4月前
|
Java 开发者 Spring
Spring AOP深度解析:探秘动态代理与增强逻辑
Spring框架中的AOP(Aspect-Oriented Programming,面向切面编程)功能为开发者提供了一种强大的工具,用以将横切关注点(如日志、事务管理等)与业务逻辑分离。本文将深入探讨Spring AOP的底层原理,包括动态代理机制和增强逻辑的实现。
87 4
|
4月前
|
Java 开发者 Spring
深入解析:Spring AOP的底层实现机制
在现代软件开发中,Spring框架的AOP(面向切面编程)功能因其能够有效分离横切关注点(如日志记录、事务管理等)而备受青睐。本文将深入探讨Spring AOP的底层原理,揭示其如何通过动态代理技术实现方法的增强。
136 8
|
4月前
|
XML Java 数据安全/隐私保护
Spring Aop该如何使用
本文介绍了AOP(面向切面编程)的基本概念和术语,并通过具体业务场景演示了如何在Spring框架中使用Spring AOP。文章详细解释了切面、连接点、通知、切点等关键术语,并提供了完整的示例代码,帮助读者轻松理解和应用Spring AOP。
119 2
Spring Aop该如何使用