Spring AOP深度解析:从概念到实战

在企业级Java开发中,Spring框架无疑是最重要的开发工具之一。而Spring AOP(面向切面编程)作为Spring框架的核心功能之一,为开发者提供了强大的代码增强和功能扩展能力。本文将深入解析Spring AOP的核心概念、底层原理以及实战应用,帮助读者全面掌握这一强大工具。

一、AOP概念的引入

在传统的面向对象编程(OOP)中,我们通常按照模块化的方式进行功能开发,但会遇到一些横切关注点(如日志记录、权限验证、事务管理等)的问题,这些功能需要重复编写代码,增加了代码的耦合度和维护成本。而AOP(Aspect Oriented Programming)正是为了解决这类问题而生。

AOP通过横向抽取机制,将这些横切关注点从业务逻辑中分离出来,形成一个可复用的模块,这个模块被称为切面(Aspect)。在运行时,AOP框架会将这些切面应用到指定的业务逻辑中,从而实现功能的增强。

举个例子,假设我们有一个用户登录功能,现在需要添加权限校验。如果不使用AOP,我们可能需要在登录方法中添加权限校验的代码。而使用AOP,我们可以在不修改登录方法的情况下,通过切面的方式在登录方法执行前进行权限校验。

二、AOP相关的概念

1. AOP的概述

AOP(Aspect Oriented Programming),即面向切面编程,是一种编程范式,它通过预编译方式或者运行期动态代理实现程序功能的统一维护。AOP是OOP的延续,是软件开发中的一个热点,也是Spring框架中的一个重要内容。

2. AOP的优势

  • 减少重复代码:将横切关注点抽取出来,避免在多个地方重复编写相同代码。

  • 提高开发效率:专注于业务逻辑的开发,而不必关心横切关注点的实现。

  • 维护方便:修改横切关注点时,只需修改对应的切面,无需修改业务逻辑代码。

3. AOP的底层原理

Spring AOP的底层实现主要依赖于JDK的动态代理技术和CGLIB代理技术。

  • JDK动态代理:为接口创建代理类的字节码文件,使用ClassLoader将字节码文件加载到JVM,创建代理类实例对象,执行对象的目标方法。适用于实现了接口的类。

  • CGLIB代理:为类生成代理对象,被代理类有没有接口都无所谓,底层是生成子类,继承被代理类。适用于没有实现接口的类。

三、Spring的AOP技术

1. AOP相关的术语

  • Joinpoint(连接点):类里面有哪些方法可以增强,这些方法称为连接点。

  • Pointcut(切入点):所谓切入点是指我们要对哪些Joinpoint进行拦截的定义。

  • Advice(通知/增强):所谓通知是指拦截到Joinpoint之后所要做的事情就是通知。通知分为前置通知、后置通知、异常通知、最终通知、环绕通知。

  • Aspect(切面):是切入点+通知的结合,以后咱们自己来编写和配置的。

2. Spring的AOP技术-配置文件方式

基本准备工作

创建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>
AOP配置文件方式的入门

创建被增强的类:

public class User {
    public void add() {
        System.out.println("add......");
    }

    public void update() {
        System.out.println("update......");
    }
}

将目标类配置到Spring中:

<bean id="user" class="com.aopImpl.User"></bean>

定义切面类:

public class UserProxy {
    public void before() {
        System.out.println("before.............");
    }
}

在配置文件中定义切面类:

<bean id="userProxy" class="com.aopImpl.UserProxy"></bean>

在配置文件中完成aop的配置:

<aop:config>
    <aop:aspect ref="userProxy">
        <aop:before method="before" pointcut="execution(public void com.aopImpl.User.add())"/>
    </aop:aspect>
</aop:config>

完成测试:

public class DemoTest {
    @Test
    public void aopTest1() {
        ApplicationContext applicationContext = new ClassPathXmlApplicationContext("applicationContext.xml");
        User user = (User) applicationContext.getBean("user");
        user.add();
    }
}

3. 切入点的表达式

在配置切入点的时候,需要定义表达式,具体格式如下:

execution([修饰符] [返回值类型] [类全路径] [方法名 ( [参数] )])
  • 修饰符可以省略不写。

  • 返回值类型不能省略不写,可以使用*代替。

  • 包名、类名、方法名、参数的规则:

    • 包名、类名、方法名不能省略不写,但可以使用*代替。

    • 中间的包名可以使用*号代替。

    • 类名也可以使用*号代替。

    • 方法也可以使用*号代替。

    • 参数如果是一个参数可以使用*号代替,如果想代表任意参数使用..

比较通用的表达式:

execution(* com.qcby.*.ServiceImpl.save(..))

4. AOP的通知类型

  • 前置通知:目标方法执行前,进行增强。

  • 环绕通知:目标方法执行前后,都可以进行增强。目标对象的方法需要手动执行。

  • 最终通知:目标方法执行成功或者失败,进行增强。

  • 后置通知:目标方法执行成功后,进行增强。

  • 异常通知:目标方法执行失败后,进行增强。

5. Spring的AOP技术-注解方式

AOP注解方式入门程序

创建Maven工程,导入坐标,编写接口,完成IOC的操作。

编写切面类,给切面类添加注解@Aspect,编写增强的方法,使用通知类型注解声明:

@Component
@Aspect
public class UserProxy {
    @Before(value = "execution(* com.*.User.add(..))")
    public void before() {
        System.out.println("before.............");
    }

    @Around(value = "execution(* com.*.User.add(..))")
    public void around(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
        System.out.println("before.............");
        proceedingJoinPoint.proceed();
        System.out.println("after.............");
    }

    @After(value = "execution(* com.*.User.add(..))")
    public void after() {
        System.out.println("after.............");
    }

    @AfterThrowing(value = "execution(* com.*.User.add(..))")
    public void afterThrowing() {
        System.out.println("afterThrowing.............");
    }

    @AfterReturning(value = "execution(* com.*.User.add(..))")
    public void afterReturning() {
        System.out.println("afterReturning.............");
    }
}

在配置文件中开启自动代理:

<aop:aspectj-autoproxy></aop:aspectj-autoproxy>

测试类:

@Test
public void aopTest1() {
    ApplicationContext applicationContext = new ClassPathXmlApplicationContext("applicationContext.xml");
    User user = (User) applicationContext.getBean("user");
    user.add();
}

四、Spring AOP的实战应用

1. 日志记录

在实际开发中,日志记录是一个常见的横切关注点。我们可以通过AOP来实现日志记录功能,而无需在每个方法中手动添加日志代码。

@Aspect
@Component
public class LogAspect {
    private Logger logger = LoggerFactory.getLogger(this.getClass());

    @Before("execution(* com.example.service.*.*(..))")
    public void before(JoinPoint joinPoint) {
        logger.info("方法开始:" + joinPoint.getSignature().getName());
    }

    @AfterReturning("execution(* com.example.service.*.*(..))")
    public void afterReturning(JoinPoint joinPoint) {
        logger.info("方法结束:" + joinPoint.getSignature().getName());
    }
}

2. 权限验证

在用户登录后,需要对用户进行权限验证。通过AOP可以在方法执行前进行权限验证,确保用户有权限执行该操作。

@Aspect
@Component
public class AuthAspect {
    @Before("execution(* com.example.controller.*.*(..))")
    public void before(JoinPoint joinPoint) {
        // 获取用户信息
        User user = (User) SecurityContextHolder.getContext().getAuthentication().getPrincipal();
        // 验证用户权限
        if (!user.hasPermission("permission")) {
            throw new AccessDeniedException("权限不足");
        }
    }
}

3. 性能监控

在系统性能优化过程中,需要对方法的执行时间进行监控。通过AOP可以在方法执行前后记录时间,计算方法的执行时间。

@Aspect
@Component
public class PerformanceAspect {
    @Around("execution(* com.example.service.*.*(..))")
    public Object around(ProceedingJoinPoint joinPoint) throws Throwable {
        long start = System.currentTimeMillis();
        Object result = joinPoint.proceed();
        long end = System.currentTimeMillis();
        System.out.println("方法执行时间:" + (end - start) + "ms");
        return result;
    }
}

五、总结

Spring AOP是Spring框架中的一个重要功能,它通过面向切面编程的方式,将横切关注点从业务逻辑中分离出来,实现了代码的复用和功能的增强。本文深入解析了Spring AOP的核心概念、底层原理以及实战应用,帮助读者全面掌握这一强大工具。在实际开发中,我们可以通过Spring AOP实现日志记录、权限验证、性能监控等功能,提高开发效率和代码质量。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值