Spring AOP

目录

一、AOP 核心概念解析

1.1 什么是 AOP?

1.2 AOP 的优势

1.3 底层实现原理

二、Spring AOP 配置文件方式实现

2.1 核心术语

2.2 开发步骤

2.3 切入点表达式详解

三、Spring AOP 注解方式实现

3.1 注解开发步骤

3.2 通知类型注解

四、最佳实践建议

五、总结


        在软件开发中,我们经常会遇到需要在不修改源代码的前提下增强方法功能的需求(如日志、事务、权限校验等)。传统面向对象编程(OOP)通过继承或组合实现功能扩展,但面对横切关注点时显得力不从心。面向切面编程(AOP)正是解决这类问题的优雅方案,本文将通过代码示例详解 Spring AOP 的核心概念与实现方式。

一、AOP 核心概念解析

1.1 什么是 AOP?

        AOP(Aspect-Oriented Programming)即面向切面编程,是一种通过预编译或动态代理技术,将横切关注点(如日志、安全校验)与业务逻辑分离的编程范式。

核心思想

  • 将分散在多个类中的相同逻辑(如事务管理)抽取到切面(Aspect)中
  • 通过切入点(Pointcut)定义需要增强的方法
  • 使用通知(Advice)描述切面逻辑的执行时机

1.2 AOP 的优势

优势项传统 OOP 实现方式AOP 实现方式
代码复用继承/组合(冗余代码)横切关注点统一管理
维护成本修改多处代码修改单一切面
系统扩展性纵向继承体系臃肿横向切面灵活扩展

1.3 底层实现原理

Spring AOP 主要通过两种技术实现动态代理:

JDK 动态代理

  • 基于接口创建代理对象
  • 要求被代理类必须实现接口

CGLIB 代理

  • 通过生成子类实现代理
  • 支持无接口类的代理

二、Spring AOP 配置文件方式实现

2.1 核心术语

  • Joinpoint(连接点):可被增强的方法(如 User.add()
  • Pointcut(切入点):定义需要增强的方法集合(通过表达式)
  • Advice(通知):切面执行的具体逻辑(如前置通知)
  • Aspect(切面):= 切入点 + 通知

2.2 开发步骤

步骤 1:添加 Maven 依赖
<dependencies>
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-context</artifactId>
        <version>5.0.2.RELEASE</version>
    </dependency>
    <dependency>
        <groupId>commons-logging</groupId>
        <artifactId>commons-logging</artifactId>
        <version>1.2</version>
    </dependency>
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-test</artifactId>
        <version>5.0.2.RELEASE</version>
    </dependency>
    <dependency>
        <groupId>log4j</groupId>
        <artifactId>log4j</artifactId>
        <version>1.2.12</version>
    </dependency>
    <dependency>
        <groupId>junit</groupId>
        <artifactId>junit</artifactId>
        <version>4.12</version>
        <scope>test</scope>
    </dependency>
    <!--AOP联盟-->
    <dependency>
        <groupId>aopalliance</groupId>
        <artifactId>aopalliance</artifactId>
        <version>1.0</version>
    </dependency>
    <!--Spring Aspects-->
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-aspects</artifactId>
        <version>5.0.2.RELEASE</version>
    </dependency>
    <!--aspectj-->
    <dependency>
        <groupId>org.aspectj</groupId>
        <artifactId>aspectjweaver</artifactId>
        <version>1.8.3</version>
    </dependency>
</dependencies>
步骤 2:定义目标类
public class Login {

    public void login(String name,String password){
        //第一种方法:直接在login方法里写
        System.out.println("登陆成功!!!");
    }

    //在登录之前进行权限验证

}
步骤 3:配置切面类
public class Authorization {

    //正常通知
    public void authorization(){
        System.out.println("进行了权限验证!!!");
    }

    //环绕通知
    public void authorization(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
        System.out.println("在方法执行之前,进行了权限验证!!!");
        proceedingJoinPoint.proceed();
        System.out.println("在方法执行之后,进行了权限验证!!!");
    }

}
步骤 4:XML 配置 AOP
<?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/aop http://www.springframework.org/schema/aop/spring-aop.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">

    <bean id="login" class="com.qcby.Login"/>
    <bean id="auth" class="com.qcby.Authorization"/>

    <!--配置切面-->
    <aop:config>
        <!--配置切面 = 切入点 + 通知组成-->
        <aop:aspect ref="auth">
            <!--前置通知:UserServiceImpl的save方法执行前,会增强-->
            <!--pointcut:后边是切入点表达式,作用是知道对对面的那个方法进行增强-->

            <!-- 前置通知 -->
<!--            <aop:before method="authorization" pointcut="execution(public void com.qcby.Login.login(..))"/>-->

            <!-- 最终通知:无论我们的切入点是好是坏(成功执行与否)都会进行通知 -->
<!--            <aop:after method="authorization" pointcut="execution(public void com.qcby.Login.login(..))"/>-->

            <!-- 后置通知:切入点只有执行成功后才会执行 -->
<!--            <aop:after-returning method="authorization" pointcut="execution(public void com.qcby.Login.login(..))"/>-->

            <!-- 异常通知:只有在切入点出现问题的时候才会通知 -->
<!--            <aop:after-throwing method="authorization" pointcut="execution(public void com.qcby.Login.login(..))"/>-->

            <!-- 环绕通知:目标方法执行前后,都可以进行增强。目标对象的方法需要手动执行 -->
            <aop:around method="authorization" pointcut="execution(public void com.qcby.Login.login(..))"/>

        </aop:aspect>
    </aop:config>

</beans>
步骤 5:测试验证
public class LoginTest {

    @Test
    public void test(){
        ApplicationContext ac=new ClassPathXmlApplicationContext("Spring.xml");
        Login login=(Login) ac.getBean("login");
        login.login("张三","11");
    }
}

2.3 切入点表达式详解

表达式格式:

execution([修饰符] 返回值类型 包名.类名.方法名(参数列表))

常用通配符:

  1. *:匹配任意字符(不包括包分隔符)
  2. ..:匹配任意参数或子包

示例:

表达式含义
execution(* com.service.*.*(..))匹配 com.service 包下所有类的所有方法
execution(* com.service.User.add(..))精确匹配 User 类的 add 方法

三、Spring AOP 注解方式实现

3.1 注解开发步骤

步骤 1:启用注解扫描
<?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:context="http://www.springframework.org/schema/context"
       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/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">

    <!--1.配置xml扫描注解-->
    <!--开启注解扫描-->
    <context:component-scan base-package="com.aopImpl"></context:component-scan>
    <!--开启Aspect生成代理对象-->
    <aop:aspectj-autoproxy></aop:aspectj-autoproxy>

</beans>
步骤 2:定义切面类,使用注解增强方法
//2.配置注解
@Component
@Aspect //增强类
public class Authorization {

    //环绕通知
    @Around(value = "execution(public void com.qcby.Login.login(..))")
    public void authorization(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
        System.out.println("在方法执行之前,进行了权限验证!!!");
        proceedingJoinPoint.proceed();
        System.out.println("在方法执行之后,进行了权限验证!!!");
    }

    //正常通知
//    @Before(value = "execution(public void com.qcby.Login.login(..))") //前置通知
//    @After(value = "execution(public void com.qcby.Login.login(..))") //最终通知
//    @AfterReturning(value = "execution(public void com.qcby.Login.login(..))") //后置通知
//    @AfterThrowing(value = "execution(public void com.qcby.Login.login(..))") //异常通知
    public void authorization(){
        System.out.println("进行了权限验证!!!");
    }


}
步骤 3:测试验证
public class LoginTest {

    @Test
    public void test(){
        ApplicationContext ac=new ClassPathXmlApplicationContext("Spring.xml");
        Login login=(Login) ac.getBean("login");
        login.login("张三","11");
    }
}

3.2 通知类型注解

注解类型执行时机方法签名要求
@Before方法执行前无返回值,参数匹配切入点参数
@AfterReturning方法正常返回后可获取返回值
@AfterThrowing方法抛出异常后必须包含异常参数
@After方法最终执行后(无论成功与否)无特殊要求
@Around包裹方法执行必须包含 ProceedingJoinPoint

四、最佳实践建议

  1. 切入点复用:使用 @Pointcut 定义可复用的切入点表达式
  2. 代理方式选择:优先使用 JDK 动态代理(接口代理);无接口类时使用 CGLIB 代理
  3. 性能考虑:避免在通知中执行耗时操作
  4. 异常处理:在 @AfterThrowing 中处理特定异常类型

五、总结

Spring AOP 通过声明式编程简化了横切关注点的管理,其核心优势在于:

  • 非侵入式增强:无需修改原有业务代码
  • 关注点分离:将系统级功能(日志、安全)与业务逻辑解耦
  • 灵活配置:支持 XML 和注解两种配置方式
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值