Spring之AOP

本文涉及的产品
日志服务 SLS,月写入数据量 50GB 1个月
简介: Spring之AOP

1.Spring AOP(面向切面编程)是什么?



面向切面编程(AOP)和面向对象编程(OOP)类似,也是一种编程模式。Spring AOP 是基于 AOP 编程模式的一个框架,它的使用有效减少了系统间的重复代码,达到了模块间的松耦合目的。


AOP 的全称是“Aspect Oriented Programming”,即面向切面编程,它将业务逻辑的各个部分进行隔离,使开发人员在编写业务逻辑时可以专心于核心业务,从而提高了开发效率。


AOP 采取横向抽取机制,取代了传统纵向继承体系的重复性代码,其应用主要体现在事务处理、日志管理、权限控制、异常处理等方面。


目前最流行的 AOP 框架有两个,分别为 Spring AOP 和 AspectJ。


Spring AOP 使用纯 Java 实现,不需要专门的编译过程和类加载器,在运行期间通过代理方式向目标类植入增强的代码。


AspectJ 是一个基于 Java 语言的 AOP 框架,从 Spring 2.0 开始,Spring AOP 引入了对 AspectJ 的支持。AspectJ 扩展了 Java 语言,提供了一个专门的编译器,在编译时提供横向代码的植入。


为了更好地理解 AOP,就需要对 AOP 的相关术语有一些了解,这些专业术语主要包含 Joinpoint、Pointcut、Advice、Target、Weaving、Proxy 和 Aspect,它们的含义如下表所示。

名称 说明
Joinpoint(连接点) 指那些被拦截到的点,在 Spring 中,可以被动态代理拦截目标类的方法。
Pointcut(切入点) 指要对哪些 Joinpoint 进行拦截,即被拦截的连接点。


Advice(通知) 指拦截到 Joinpoint 之后要做的事情,即对切入点增强的内容。
Target(目标) 指代理的目标对象。
Weaving(植入) 指把增强代码应用到目标上,生成代理对象的过程。
Proxy(代理) 指生成的代理对象。
Aspect(切面) 切入点和通知的结合。


2.Spring AOP的基本概念和原理


Spring AOP(Aspect-Oriented Programming)是Spring框架中的一个重要特性,用于实现面向切面编程。它通过在不修改原有代码的情况下,将横切关注点(Cross-cutting Concerns)从业务逻辑中分离出来,实现了模块化的开发和更好的代码重用。


AOP的概念和作用


AOP是一种编程范式,旨在解决横切关注点的代码重复问题。

AOP通过将横切关注点从主业务逻辑中分离出来,实现了更好的代码模块化和可维护性。

AOP可以在不改变原有代码的情况下,通过织入(Weaving)切面(Aspect)来实现对横切关注点的处理。

Spring AOP的基本原理和实现方式


Spring AOP基于动态代理(Dynamic Proxy)实现。

Spring AOP提供了两种代理方式:JDK动态代理和CGLIB代理。

JDK动态代理适用于基于接口的代理,而CGLIB代理适用于类级别的代理。

Spring AOP通过代理对象将切面织入到目标对象的方法调用中,实现对横切关注点的处理。

AOP中的主要概念


切面(Aspect):横切关注点的模块化单元,它包含了通知和切点。

连接点(Join Point):在程序执行过程中能够被切面织入的特定点。

切点(Pointcut):用于定义连接点的表达式,指定了哪些连接点将被切面织入。

通知(Advice):切面在连接点上执行的动作,包括前置通知、后置通知、异常通知、环绕通知和引入通知。

织入(Weaving):将切面应用到目标对象中的过程,可以在编译时、类加载时或运行时进行。

总结:Spring AOP通过代理和织入的方式,实现了对横切关注点的处理。它的基本原理是通过动态代理来生成代理对象,并将切面织入到目标对象的方法调用中。在AOP中,切面、连接点、切点、通知和织入是重要的概念,它们共同构成了Spring AOP的基本框架。

3.Spring AOP的实现方式


Spring AOP的实现方式主要有三种:


基于动态代理的方式:利用Java反射机制,在运行时动态生成代理对象,对被代理对象的方法进行增强。Spring默认采用JDK动态代理实现AOP。


基于CGLIB的方式:利用CGLIB库,在运行时动态生成被代理对象的子类,对需要增强的方法进行重写。CGLIB方式比JDK动态代理方式更加强大,可代理不实现任何接口的普通Java类。


基于AspectJ的方式:AspectJ是一个独立的AOP框架,Spring可以通过整合AspectJ实现AOP。AspectJ提供了更加灵活、强大的AOP功能,支持更多的切面表达式和增强方式。


在实际开发中,一般采用JDK动态代理方式实现AOP。当需要增强的对象没有实现接口时,才会考虑CGLIB方式。而使用AspectJ方式则较少见,一般用于需要特定的AOP功能的场景。

4.Spring AOP的注意事项


AOP只是一种编程思想,需要和具体的框架结合使用。


Spring AOP只支持方法级别的切面,不支持构造函数级别的切面。


了解切点表达式的语法和用法,以便编写出正确的切点表达式。


在进行AOP配置时,需要注意切面的优先级和顺序,以确保切面的正确顺序。


在使用AOP时,需要注意性能问题。过多的切面会增加方法调用的开销,影响应用程序的性能。


在进行AOP配置时,需要考虑到目标对象的生命周期,以避免出现因切面的生命周期问题导致的应用程序异常。


AOP可以用来解耦代码,但是过度的切面会将应用程序的控制权交给AOP框架,影响应用程序的可读性和可维护性。          

5.Spring AOP的扩展

Spring AOP的扩展有:


1. 自定义切点:可以创建自定义切点,让切面在更细粒度地地方切入。


2. 异常通知:可以在方法抛出异常时执行通知,让代码更健壮。


3. 环绕通知:可以在方法调用前后执行通知,可以对方法的参数和返回值进行修改。


4. 注解驱动:可以使用注解来定义切面和通知,让代码更简洁和易于维护。


5. AspectJ集成:可以使用AspectJ的注解和表达式来定义切面和通知,更加灵活和强大。


6. 注入切面:可以将切面作为Spring的Bean注入到容器中,让切面更加可重用和可定制。

6.Spring AOP的应用场景

Spring AOP的应用场景包括但不限于以下几个方面:


日志记录:可以使用AOP拦截方法,记录方法的执行时间、参数、返回值等信息,实现日志的自动记录。


安全控制:可以使用AOP拦截访问操作,实现对用户访问权限的控制,例如检查用户是否有权限访问某些资源。


性能监控:可以使用AOP拦截方法,监控方法的执行时间、调用次数等信息,实现对系统性能的监控和优化。


缓存管理:可以使用AOP拦截方法,实现缓存管理,例如缓存对象的读取、写入、删除等操作。


事务管理:可以使用AOP拦截方法,实现事务管理,例如对数据库操作进行事务控制。


异常处理:可以使用AOP拦截方法,实现异常处理,例如捕获异常并记录日志或者发送通知。


总之,AOP可以在很多领域进行应用,可以简化程序的编写和维护,提高程序的可重用性和可扩展性。

7.Spring AOP的优缺点

Spring AOP是Spring框架提供的一种面向切面编程的实现方式,它有以下优点和缺点:


优点:

1. 降低了代码的重复性:通过将横切关注点(如日志记录、事务管理等)从业务逻辑中分离出来,可以减少代码的重复性,提高代码的可维护性和可读性。


2. 提高了代码的模块化和可重用性:将横切关注点封装成切面,可以将其应用到多个目标对象中,提高了代码的模块化和可重用性。


3. 简化了业务逻辑的编写:通过使用切面,可以将与业务逻辑无关的代码(如事务管理、异常处理等)从业务逻辑中抽离出来,使业务逻辑更加清晰和简洁。


4. 提供了更好的代码结构和可扩展性:通过使用切面,可以将不同关注点的代码分离,使代码结构更加清晰,并且可以灵活地添加、修改或删除切面,以满足不同的需求。


缺点:

1. 仅支持方法级别的切面:Spring AOP只能对方法进行切面织入,对于其他级别的切面(如字段访问、对象创建等),需要使用其他的AOP框架。


2. 无法拦截私有方法和静态方法:由于Spring AOP基于动态代理实现,无法拦截私有方法和静态方法。


3. 对性能有一定的影响:由于Spring AOP使用动态代理来生成代理对象,并在方法调用时进行切面织入,会增加一定的性能开销。


4. 需要依赖Spring框架:使用Spring AOP需要依赖Spring框架,对于不使用Spring框架的项目来说,引入Spring框架可能会增加项目的复杂性。


总结:Spring AOP具有降低代码重复性、提高代码模块化和可重用性、简化业务逻辑编写和提供更好的代码结构和可扩展性等优点。然而,它也存在仅支持方法级别的切面、无法拦截私有方法和静态方法、对性能有一定的影响和需要依赖Spring框架等缺点。在使用Spring AOP时,需要根据具体的需求和场景来权衡其优缺点。

8.Spring AOP与其他技术的比较

Spring AOP与其他常见的技术进行比较如下:


1. Java原生的动态代理:

  - Spring AOP基于动态代理实现,可以在目标对象的方法调用前后织入切面逻辑。而Java原生的动态代理只能代理接口,无法代理类,且只能在方法调用前后进行拦截,无法实现更复杂的切面逻辑。


2. AspectJ:

  - AspectJ是一个功能更强大的AOP框架,与Spring AOP相比,AspectJ支持更多的切面织入方式,如字段访问、对象创建等,且支持更丰富的切点表达式。但AspectJ需要在编译时进行织入,需要修改源代码或使用特定的编译器。


3. JPA(Java Persistence API):

  - JPA是Java持久化API的一种标准规范,它提供了一种面向对象的持久化解决方案。与Spring AOP不同,JPA主要关注数据持久化层面的切面,如事务管理、缓存管理等,而不是应用层面的切面。


4. Spring Transaction Management:

  - Spring的事务管理模块提供了一种声明式的事务管理方式,可以通过配置来管理事务。与Spring AOP相比,Spring的事务管理更加专注于事务相关的切面,如事务的开始、提交、回滚等,而不涉及其他横切关注点。


总结:Spring AOP相对于其他技术来说,更加专注于应用层面的切面编程,提供了一种简单、灵活的切面实现方式。与Java原生的动态代理相比,Spring AOP在功能上更加丰富,可以代理类而不仅仅是接口。与AspectJ相比,Spring AOP的配置更加简单,且不需要修改源代码或使用特定的编译器。与JPA和Spring的事务管理相比,Spring AOP关注的切面范围更广,可以应用于更多的横切关注点。在选择使用哪种技术时,需要根据具体的需求和场景来进行权衡和选择。


9.AOP通知

Spring AOP(面向切面编程)是一种编程范式,它允许我们在应用程序运行时动态地添加行为或切面。其中,前置通知、后置通知、环绕通知、异常通知和过滤通知是 Spring AOP 中的常见类型。


前置通知

前置通知(Before advice):在目标方法执行前,执行的通知。可以用来在方法执行前进行一些准备或检查操作。

spring-context.xml

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       default-autowire="byType"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
<!--目标对象-->
    <bean class="com.aop.biz.impl.BookBizImpl" name="bookBiz"></bean>
<!--通知-->
    <bean class="com.aop.biz.advice.MyMethodBeforeAdvice" id="methodBeforeAdvice"></bean>
<!--代理-->
    <bean class="org.springframework.aop.framework.ProxyFactoryBean" id="bookProxy">
<!--        配置目标对象-->
        <property name="target" ref="bookBiz"></property>
<!--        配置代理的接口-->
        <property name="proxyInterfaces">
            <list>
                <value>com.aop.biz.IBookBiz</value>
            </list>
        </property>
<!--配置通知-->
        <property name="interceptorNames">
            <list>
                <value>methodBeforeAdvice</value>
            </list>
        </property>
    </bean>
</beans>
package com.aop.biz.advice;
import org.springframework.aop.MethodBeforeAdvice;
import java.lang.reflect.Method;
import java.util.Arrays;
/**
 * 买书、评论前加系统日志
 * @author Administrator
 *
 */
public class MyMethodBeforeAdvice implements MethodBeforeAdvice {
    @Override
    public void before(Method arg0, Object[] arg1, Object arg2) throws Throwable {
//        在这里,可以获取到目标类的全路径及方法及方法参数,然后就可以将他们写到日志表里去
        String target = arg2.getClass().getName();
        String methodName = arg0.getName();
        String args = Arrays.toString(arg1);
        System.out.println("【前置通知:系统日志】:"+target+"."+methodName+"("+args+")被调用了");
    }
}

后置通知

后置通知(After advice):在目标方法执行后,执行的通知。可以用来在方法执行后进行一些清理操作,例如释放资源或记录执行结果。

package com.aop.biz.advice;
import org.springframework.aop.AfterReturningAdvice;
import java.lang.reflect.Method;
import java.util.Arrays;
/**
 * 买书返利
 * @author Administrator
 *
 */
public class MyAfterReturningAdvice implements AfterReturningAdvice {
    @Override
    public void afterReturning(Object arg0, Method arg1, Object[] arg2, Object arg3) throws Throwable {
        String target = arg3.getClass().getName();
        String methodName = arg1.getName();
        String args = Arrays.toString(arg2);
        System.out.println("【后置通知:买书返利】:"+target+"."+methodName+"("+args+")被调用了,"+"该方法被调用后的返回值为:"+arg0);
    }
}

环绕通知

环绕通知(Around advice):在目标方法执行前和执行后,执行的通知。可以用来在目标方法执行前后做一些预处理和后处理操作。

package com.zking.aop.advice;
import java.util.Arrays;
import org.aopalliance.intercept.MethodInterceptor;
import org.aopalliance.intercept.MethodInvocation;
/**
 * 环绕通知
 *     包含了前置和后置通知
 * 
 * @author Administrator
 *
 */
public class MyMethodInterceptor implements MethodInterceptor {
    @Override
    public Object invoke(MethodInvocation arg0) throws Throwable {
        String target = arg0.getThis().getClass().getName();
        String methodName = arg0.getMethod().getName();
        String args = Arrays.toString(arg0.getArguments());
        System.out.println("【环绕通知调用前:】:"+target+"."+methodName+"("+args+")被调用了");
//        arg0.proceed()就是目标对象的方法
        Object proceed = arg0.proceed();
        System.out.println("【环绕通知调用后:】:该方法被调用后的返回值为:"+proceed);
        return proceed;
    }
}


异常通知

异常通知(After throwing advice):在目标方法抛出异常时,执行的通知。可以用来做一些异常处理操作。

package com.aop.biz.advice;
import org.springframework.aop.ThrowsAdvice;
import com.aop.biz.Exception.PriceException;
/**
 * 出现异常执行系统提示,然后进行处理。价格异常为例
 * @author Administrator
 *
 */
public class MyThrowsAdvice implements ThrowsAdvice {
    public void afterThrowing(PriceException ex) {
        System.out.println("【异常通知】:当价格发生异常,那么执行此处代码块!!!");
    }
}



过滤通知

过滤通知(After returning advice):在目标方法执行成功并正常返回时,执行的通知。可以用来做一些返回结果的处理。

所有用到的bean
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       default-autowire="byType"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
<!--目标对象-->
    <bean class="com.aop.biz.impl.BookBizImpl" name="bookBiz"></bean>
<!--通知-->
    <bean class="com.aop.biz.advice.MyMethodBeforeAdvice" id="methodBeforeAdvice"></bean>
    <bean class="com.aop.biz.advice.MyAfterReturningAdvice" id="myAfterReturningAdvice"></bean>
    <bean class="com.aop.biz.advice.MyMethodInterceptor" id="myMethodInterceptor"></bean>
    <bean class="com.aop.biz.advice.MyThrowsAdvice" id="myThrowsAdvice"></bean>
    <bean class="org.springframework.aop.support.RegexpMethodPointcutAdvisor" id="regexpMethodPointcutAdvisor">
        <property name="advice" ref="myAfterReturningAdvice"></property>
        <property name="pattern" value=".*buy"></property>
    </bean>
<!--代理-->
    <bean class="org.springframework.aop.framework.ProxyFactoryBean" id="bookProxy">
<!--        配置目标对象-->
        <property name="target" ref="bookBiz"></property>
<!--        配置代理的接口-->
        <property name="proxyInterfaces">
            <list>
                <value>com.aop.biz.IBookBiz</value>
            </list>
        </property>
<!--配置通知-->
        <property name="interceptorNames">
            <list>
                <value>methodBeforeAdvice</value>
<!--                <value>myAfterReturningAdvice</value>-->
                <value>regexpMethodPointcutAdvisor</value>
                <value>myMethodInterceptor</value>
                <value>myThrowsAdvice</value>
            </list>
        </property>
    </bean>
</beans>


相关实践学习
日志服务之数据清洗与入湖
本教程介绍如何使用日志服务接入NGINX模拟数据,通过数据加工对数据进行清洗并归档至OSS中进行存储。
目录
相关文章
|
7天前
|
前端开发 Java 数据库
浅谈Spring AOP 面向切面编程 最通俗易懂的画图理解AOP、AOP通知执行顺序~
浅谈Spring AOP 面向切面编程 最通俗易懂的画图理解AOP、AOP通知执行顺序~
|
7天前
|
XML Java 数据格式
技术好文:Spring基础篇——AOP切面编程
技术好文:Spring基础篇——AOP切面编程
|
2天前
|
设计模式 缓存 程序员
Spring6(三):面向切面AOP(1)
Spring6(三):面向切面AOP(1)
10 1
|
2天前
|
XML 监控 Java
Java中的AOP编程:AspectJ与Spring AOP的应用
Java中的AOP编程:AspectJ与Spring AOP的应用
|
3天前
|
监控 Java 数据安全/隐私保护
Spring AOP实现原理及其在企业应用中的实际应用
Spring AOP实现原理及其在企业应用中的实际应用
|
1天前
|
Java 测试技术 数据安全/隐私保护
Spring Boot中的AOP编程实践
Spring Boot中的AOP编程实践
|
2天前
|
Java Spring
Spring AOP(面向切面编程)详解
Spring AOP(面向切面编程)详解
|
2天前
|
XML Java 数据格式
Spring6(三):面向切面AOP(3)
Spring6(三):面向切面AOP(3)
7 0
|
2天前
|
SQL 安全 Java
Spring6(三):面向切面AOP(2)
Spring6(三):面向切面AOP(2)
7 0
|
3天前
|
Java 测试技术 数据安全/隐私保护
Spring Boot中的AOP编程实践
Spring Boot中的AOP编程实践