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 底层就是动态代理


相关文章
|
3天前
|
XML 监控 安全
Spring特性之一——AOP面向切面编程
Spring特性之一——AOP面向切面编程
15 1
|
3天前
|
运维 Java 程序员
Spring5深入浅出篇:基于注解实现的AOP
# Spring5 AOP 深入理解:注解实现 本文介绍了基于注解的AOP编程步骤,包括原始对象、额外功能、切点和组装切面。步骤1-3旨在构建切面,与传统AOP相似。示例代码展示了如何使用`@Around`定义切面和执行逻辑。配置中,通过`@Aspect`和`@Around`注解定义切点,并在Spring配置中启用AOP自动代理。 进一步讨论了切点复用,避免重复代码以提高代码维护性。通过`@Pointcut`定义通用切点表达式,然后在多个通知中引用。此外,解释了AOP底层实现的两种动态代理方式:JDK动态代理和Cglib字节码增强,默认使用JDK,可通过配置切换到Cglib
|
1天前
|
前端开发 Java 关系型数据库
使用IDEA搭建一个Spring + AOP (权限管理 ) + Spring MVC
使用IDEA搭建一个Spring + AOP (权限管理 ) + Spring MVC
|
3天前
|
JSON 前端开发 Java
【JavaEE】Spring全家桶实现AOP-统一处理
【JavaEE】Spring全家桶实现AOP-统一处理
5 0
|
3天前
|
前端开发 Java 开发者
【JavaEE】面向切面编程AOP是什么-Spring AOP框架的基本使用
【JavaEE】面向切面编程AOP是什么-Spring AOP框架的基本使用
10 0
|
3天前
|
Java Spring 容器
Spring AOP浅谈
Spring AOP浅谈
10 1
|
3天前
|
XML Java 数据格式
Spring高手之路18——从XML配置角度理解Spring AOP
本文是全面解析面向切面编程的实践指南。通过深入讲解切面、连接点、通知等关键概念,以及通过XML配置实现Spring AOP的步骤。
22 6
Spring高手之路18——从XML配置角度理解Spring AOP
|
3天前
|
XML Java 数据格式
Spring使用AOP 的其他方式
Spring使用AOP 的其他方式
16 2
|
3天前
|
XML Java 数据格式
Spring 项目如何使用AOP
Spring 项目如何使用AOP
25 2
|
3天前
|
Java 开发者 Spring
Spring AOP的切点是通过使用AspectJ的切点表达式语言来定义的。
【5月更文挑战第1天】Spring AOP的切点是通过使用AspectJ的切点表达式语言来定义的。
25 5