初识Spring AOP

简介: 初识Spring AOP

概念


AOP介绍


AOP(Aspect-Oriented Programming:面向切面编程)能够将那些与业务无关,却为业务模块所共同调用的逻辑或责任(例如事务处理、日志管理、权限控制等)封装起来,便于减少系统的重复代码,降低模块间的耦合度,并有利于未来的可拓展性和可维护性。

Spring AOP 就是基于动态代理的,如果要代理的对象,实现了某个接口,那么 Spring AOP 会使用 JDK Proxy,去创建代理对象,而对于没有实现接口的对象,就无法使用 JDK Proxy 去进行代理了,,这时候 Spring AOP会使用 Cglib 生成一个被代理对象的子类来作为代理,如下图所示:


image.png


除此之外还有一种情况,如果被代理对象实现了接口,可以强制使用 Cglib 实现 AOP。

当然你也可以使用 AspectJ ,Spring AOP 已经集成了AspectJ ,AspectJ 应该算的上是 Java 生态系统中最完整的 AOP 框架了。Spring AOP 基于注解配置的情况下,需要依赖于 AspectJ 包的标准注解,但是不需要额外的编译以及 AspectJ 的织入器,而基于 XML 配置不需要,所以 Spring AOP 只是复用了 AspectJ 的注解,并没有其他依赖 AspectJ 的地方。


使用 AOP 之后我们可以把一些通用功能抽象出来,在需要用到的地方直接使用即可,这样大大简化了代码量。我们需要增加新功能时也方便,这样也提高了系统扩展性。日志功能、事务管理等等场景都用到了 AOP 。


当 Spring 需要使用 @AspectJ 注解支持时,需要在 Spring 配置文件中如下配置:


<aop:aspectj-autoproxy/>
复制代码


而关于强制使用 Cglib,可以通过在 Spring 配置文件如下实现:


<aop:aspectj-autoproxy proxy-target-class="true"/>
复制代码


proxy-target-class 属性值决定是基于接口的还是基于类的代理被创建,默认为 false。如果 proxy-target-class 属性值被设置为 true,那么基于类的代理将有效(这时需要 Cglib 库)。反之是基于接口的代理(JDK的动态代理)。


所以,虽然使用了 AspectJ 的 Annotation,但是并没有使用它的编译器和织入器。其实现原理是 JDK 动态代理或 Cglib,在运行时生成代理类。


AOP核心概念


连接点:JoinPoint,就是要被拦截的方法

切入点:PointCut,定义在哪些类,哪些方法上切入(拦截)

通知:Advice,在拦截到连接点之后要执行的代码,通知主要有五种,前置通知,后置通知,异常通知,最终通知和环绕通知。

切面:AspectJ,就是切入点加上通知

织入:weaving,把切面加入对象,并创建出代理对象的过程。


AOP标签配置元素


aop:aspect: 定义一个切面

aop:before: 定义一个前置通知

aop:after: 定义一个后置通知

aop:around: 定义一个环绕通知

aop:after-returning : 定义一个返回通知

aop:after-throwing :定义一个异常通知

aop:config: 顶层的aop配置元素

aop:pointcut: 定义切入点


AOP实现步骤

  1. 定义普通的业务组件;
  2. 定义切入点;
  3. 定义增强处理,也就是在AOP框架中为普通的业务逻辑织入的处理动作。


实例


基于XML


继续使用租房的案例,接下来新建一个切面类:

public class ProxyXML {
    public void seeHouse(){
        System.out.println("带客户看房子");
    }
    public Object getMoney(ProceedingJoinPoint p){
        System.out.println("租房.....before");
        Object o = null;
        try{
            o = p.proceed();
        }catch(Throwable e){
            e.printStackTrace();
        }
        System.out.println("租房.....after");
        return o;
    }
    public void fare(){
        System.out.println("收取中介费");
    }
}
复制代码


配置文件:


<?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:aop="http://www.springframework.org/schema/aop"
       xmlns:context="http://www.springframework.org/schema/context"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
       http://www.springframework.org/schema/beans/spring-beans.xsd
       http://www.springframework.org/schema/context
       http://www.springframework.org/schema/context/spring-context.xsd
       http://www.springframework.org/schema/aop
       http://www.springframework.org/schema/aop/spring-aop.xsd">
    <bean id="host" class="com.msdn.bean.Host" />
    <bean id="proxy" class="com.msdn.aop.ProxyXML" />
    <!--Spring基于Xml的切面-->
    <aop:config>
        <!--定义切点函数-->
        <aop:pointcut id="rentPointCut" expression="execution(* com.msdn.bean.Host.rent())"/>
        <!-- 定义切面 order 定义优先级,值越小优先级越大-->
        <aop:aspect ref="proxy" order="0">
            <!--前置通知-->
            <aop:before method="seeHouse" pointcut-ref="rentPointCut" />
            <!--环绕通知-->
            <aop:around method="getMoney" pointcut-ref="rentPointCut" />
            <!--后置通知-->
            <aop:after method="fare" pointcut-ref="rentPointCut" />
        </aop:aspect>
    </aop:config>
</beans>
复制代码


pom.xml:


<?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">
    <parent>
        <artifactId>spring_study</artifactId>
        <groupId>com.msdn</groupId>
        <version>1.0-SNAPSHOT</version>
    </parent>
    <modelVersion>4.0.0</modelVersion>
    <artifactId>spring-chap3</artifactId>
    <dependencies>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-webmvc</artifactId>
            <version>5.2.2.RELEASE</version>
        </dependency>
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.12</version>
        </dependency>
        <dependency>
            <groupId>org.aspectj</groupId>
            <artifactId>aspectjrt</artifactId>
            <version>1.9.2</version>
        </dependency>
        <dependency>
            <groupId>org.aspectj</groupId>
            <artifactId>aspectjtools</artifactId>
            <version>1.9.2</version>
        </dependency>
        <dependency>
            <groupId>org.aspectj</groupId>
            <artifactId>aspectjweaver</artifactId>
            <version>1.9.2</version>
        </dependency>
    </dependencies>
</project>
复制代码


测试代码:


@Test
public void aopTest(){
    ApplicationContext context = new ClassPathXmlApplicationContext("spring-aop.xml");
    Object host = context.getBean("host");
    Rent o = (Rent) host;
    o.rent();
}
复制代码


执行结果为:


带客户看房子
租房.....before
房屋出租
租房.....after
收取中介费
复制代码


基于注解


切面类:


@Order(0)
@Aspect
@Component
public class ProxyAnnotation {
    @Before(value = "execution(* com.msdn.bean.Host.rent())")
    public void seeHouse(){
        System.out.println("带客户看房子");
    }
    @Around(value = "execution(* com.msdn.bean.Host.rent())")
    public Object getMoney(ProceedingJoinPoint p){
        System.out.println("租房.....before");
        Object o = null;
        try{
            o = p.proceed();
        }catch(Throwable e){
            e.printStackTrace();
        }
        System.out.println("租房.....after");
        return o;
    }
    @After(value = "execution(* com.msdn.bean.Host.rent())")
    public void fare(){
        System.out.println("收取中介费");
    }
}
复制代码


切面类版本二:


@Order(0)
@Aspect
@Component
public class ProxyAnnotation2 {
    @Pointcut("execution(* com.msdn.bean.Host.rent())")
    public void rentPointCut(){
    }
    @Before(value = "rentPointCut()")
    public void seeHouse(){
        System.out.println("带客户看房子");
    }
    @Around(value = "rentPointCut()")
    public Object getMoney(ProceedingJoinPoint p){
        System.out.println("租房.....before,优先级更高");
        Object o = null;
        try{
            o = p.proceed();
        }catch(Throwable e){
            e.printStackTrace();
        }
        System.out.println("租房.....after");
        return o;
    }
    @After(value = "rentPointCut()")
    public void fare(){
        System.out.println("收取中介费");
    }
}
复制代码


配置文件:


<?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:aop="http://www.springframework.org/schema/aop"
       xmlns:context="http://www.springframework.org/schema/context"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
       http://www.springframework.org/schema/beans/spring-beans.xsd
       http://www.springframework.org/schema/context
       http://www.springframework.org/schema/context/spring-context.xsd
       http://www.springframework.org/schema/aop
       http://www.springframework.org/schema/aop/spring-aop.xsd">
    <context:component-scan base-package="com.msdn.bean,com.msdn.aop" />
    <aop:aspectj-autoproxy proxy-target-class="true" />
</beans>
复制代码


测试代码:


@Test
    public void aopTest(){
        ApplicationContext context = new ClassPathXmlApplicationContext("spring-aop2.xml");
        Object host = context.getBean("host");
        Rent o = (Rent) host;
        o.rent();
    }
复制代码


执行结果为:


租房.....before
带客户看房子
房屋出租
租房.....after
收取中介费



目录
相关文章
|
3月前
|
XML 安全 Java
使用 Spring 的 @Aspect 和 @Pointcut 注解简化面向方面的编程 (AOP)
面向方面编程(AOP)通过分离横切关注点,如日志、安全和事务,提升代码模块化与可维护性。Spring 提供了对 AOP 的强大支持,核心注解 `@Aspect` 和 `@Pointcut` 使得定义切面与切入点变得简洁直观。`@Aspect` 标记切面类,集中处理通用逻辑;`@Pointcut` 则通过表达式定义通知的应用位置,提高代码可读性与复用性。二者结合,使开发者能清晰划分业务逻辑与辅助功能,简化维护并提升系统灵活性。Spring AOP 借助代理机制实现运行时织入,与 Spring 容器无缝集成,支持依赖注入与声明式配置,是构建清晰、高内聚应用的理想选择。
437 0
|
2月前
|
XML Java 数据格式
《深入理解Spring》:AOP面向切面编程深度解析
Spring AOP通过代理模式实现面向切面编程,将日志、事务等横切关注点与业务逻辑分离。支持注解、XML和编程式配置,提供五种通知类型及丰富切点表达式,助力构建高内聚、低耦合的可维护系统。
|
7月前
|
监控 安全 Java
Spring AOP实现原理
本内容主要介绍了Spring AOP的核心概念、实现机制及代理生成流程。涵盖切面(Aspect)、连接点(Join Point)、通知(Advice)、切点(Pointcut)等关键概念,解析了JDK动态代理与CGLIB代理的原理及对比,并深入探讨了通知执行链路和责任链模式的应用。同时,详细分析了AspectJ注解驱动的AOP解析过程,包括切面识别、切点表达式匹配及通知适配为Advice的机制,帮助理解Spring AOP的工作原理与实现细节。
1126 13
|
4月前
|
人工智能 监控 安全
如何快速上手【Spring AOP】?核心应用实战(上篇)
哈喽大家好吖~欢迎来到Spring AOP系列教程的上篇 - 应用篇。在本篇,我们将专注于Spring AOP的实际应用,通过具体的代码示例和场景分析,帮助大家掌握AOP的使用方法和技巧。而在后续的下篇中,我们将深入探讨Spring AOP的实现原理和底层机制。 AOP(Aspect-Oriented Programming,面向切面编程)是Spring框架中的核心特性之一,它能够帮助我们解决横切关注点(如日志记录、性能统计、安全控制、事务管理等)的问题,提高代码的模块化程度和复用性。
|
4月前
|
设计模式 Java 开发者
如何快速上手【Spring AOP】?从动态代理到源码剖析(下篇)
Spring AOP的实现本质上依赖于代理模式这一经典设计模式。代理模式通过引入代理对象作为目标对象的中间层,实现了对目标对象访问的控制与增强,其核心价值在于解耦核心业务逻辑与横切关注点。在框架设计中,这种模式广泛用于实现功能扩展(如远程调用、延迟加载)、行为拦截(如权限校验、异常处理)等场景,为系统提供了更高的灵活性和可维护性。
|
11月前
|
XML Java 开发者
Spring Boot中的AOP实现
Spring AOP(面向切面编程)允许开发者在不修改原有业务逻辑的情况下增强功能,基于代理模式拦截和增强方法调用。Spring Boot通过集成Spring AOP和AspectJ简化了AOP的使用,只需添加依赖并定义切面类。关键概念包括切面、通知和切点。切面类使用`@Aspect`和`@Component`注解标注,通知定义切面行为,切点定义应用位置。Spring Boot自动检测并创建代理对象,支持JDK动态代理和CGLIB代理。通过源码分析可深入了解其实现细节,优化应用功能。
530 6
|
10月前
|
XML Java 测试技术
Spring AOP—通知类型 和 切入点表达式 万字详解(通俗易懂)
Spring 第五节 AOP——切入点表达式 万字详解!
729 25
|
10月前
|
XML 安全 Java
Spring AOP—深入动态代理 万字详解(通俗易懂)
Spring 第四节 AOP——动态代理 万字详解!
448 24
|
9月前
|
Java API 微服务
微服务——SpringBoot使用归纳——Spring Boot中的切面AOP处理——Spring Boot 中的 AOP 处理
本文详细讲解了Spring Boot中的AOP(面向切面编程)处理方法。首先介绍如何引入AOP依赖,通过添加`spring-boot-starter-aop`实现。接着阐述了如何定义和实现AOP切面,包括常用注解如`@Aspect`、`@Pointcut`、`@Before`、`@After`、`@AfterReturning`和`@AfterThrowing`的使用场景与示例代码。通过这些注解,可以分别在方法执行前、后、返回时或抛出异常时插入自定义逻辑,从而实现功能增强或日志记录等操作。最后总结了AOP在实际项目中的重要作用,并提供了课程源码下载链接供进一步学习。
1095 0