AOP切面编程:让你的代码更优雅

一、AOP切面编程原理

AOP全称为Aspect-Oriented Programming,中文翻译为“面向切面编程”。AOP和OOP(Object-Oriented Programming)作为两种编程范式,都追求实现代码的模块化、可重用性和可维护性。AOP是通过将横切关注点抽象出来来实现这一点,将其称为“切面”(Aspect) 。AOP采用的是将这些关注点与原方法进行分离的方式来降低代码的耦合度的。

在Java中,AOP动态代理的实现,使得我们可以在执行目标方法之前和之后,插入一些特定的处理,而不是在主业务中添加大量冗余代码。AOP在实现中使用到了反射技术,对java层面的代码进行拦截和切入操作。

二、AOP切面编程是什么

AOP切面编程指的是一种面向切面的编程思想和设计模式,它将各个业务逻辑之间相同或类似的处理抽象成一个切面,达到了代码重用,简化程序设计的目的。

例如,当我们需要在应用程序中统计某个方法的运行时间,如果在每次调用该方法时都手动添加代码,代码将变得冗余且不易维护。但是,使用AOP切面编程,我们可以将计时的代码组织成一个切面,并将其织入到目标方法中,达到代码重用的目的。

三、AOP切面编程三种实现方式

1. 静态代理

静态代理是通过在编译期间,手动编写代理类的方式进行的,需要我们手动为每一个类编写代理类,存在大量的冗余代码。其原理是在代理类中生成一个目标类的对象并对其进行包装,通过调用代理类中的方法来实现对目标类方法的调用。

2. JDK动态代理

JDK动态代理是面向接口代理,实现InvocationHandler接口,重写invoke方法。在运行期间,使用Java反射机制,在内存中生成代理类,并且实现了目标类的接口,直接调用代理类中的方法会触发InvocationHandler的invoke方法,我们可以在这个方法中添加需要的操作。JDK动态代理可以通过proxy.newProxyInstance()方法来动态的生成代理类,支持多个目标类的代理。

3. CGLIB动态代理

CGLIB动态代理是直接生成目标类的子类,因而可以对类进行代理(而不仅仅是接口),不过,需要引入cglib-nodep依赖。CGLIB动态代理同样实现了InvocationHandler接口,通过创建Enhancer来动态生成目标类的子类,然后在子类中织入相关增强逻辑,最后创建子类对象。

四、切面编程是什么意思

切面编程是AOP编程的实现方式之一,它将通用或者常见的功能,像日志记录、性能分析、权限控制等,称为切面。这些切面可以纵向地贯穿应用的各层,比如Controller,Service,Dao等,也可以横向地贯穿应用的多个模块和组件。

切面可以看做是一组针对特定类某一类方法的函数,利用指定的切面可以更方便、更直观、更快捷地处理代码,它对代码的可读性和维护性都有很好的帮助。

五、AOP面向切面编程应用场景

通过AOP编程实现的切面,可以在任何地方被调用,可以方便地通过合适的实现方式,实现各种不同的应用场景,包括:

1. 日志记录

记录关键方法的执行情况,比如接口调用、运行时间、日志级别等,例如下面是一个简单的打印日志的AOP切面:

public class LogAspect {
    private static final Logger logger = LoggerFactory.getLogger(LogAspect.class);

    @Before("execution(* com.example.demo.service.*.*(..))")
    public void doBefore() {
        logger.info("method start...");
    }

    @After("execution(* com.example.demo.service.*.*(..))")
    public void doAfter() {
        logger.info("method end...");
    }
}

2. 权限管理

处理用户权限相关问题,比如用户是否具有操作某项资源的权限、用户是否处于登录状态等。在使用Spring Security等安全框架时,会经常使用这种方式对请求进行认证和授权。

3. 数据缓存

对于一些数据的计算和处理非常耗费时间,可以将结果缓存起来,再次用到时直接从缓存中获取数据,提高效率。例如通过redis工具实现数据缓存:

@Around("execution(* com.example.demo.service.*.*(..))")
public Object around(ProceedingJoinPoint pjp) throws Throwable {
    String methodName = pjp.getSignature().getName();
    Object[] args = pjp.getArgs();
    //生成redis的key
    String key = RedisKeyUtils.getKey(methodName, Arrays.toString(args));
    //先从redis中查找数据
    Object obj = stringRedisTemplate.opsForValue().get(key);
    if (obj != null) {
        logger.info("Redis exists key - " + key);
        return obj;
    }
    logger.info("Redis not exists key - " + key);
    //如果redis中不存在数据则进行计算,然后添加到redis中
    Object result = pjp.proceed();
    if (result != null) {
        stringRedisTemplate.opsForValue().set(key, result.toString(), 300, TimeUnit.SECONDS);
        logger.info("Redis add key - " + key);
    }
    return result;
}

4. 声明式事务

通过AOP的方式来帮助我们完成事务的操作,比如管理数据库连接、开启或关闭事务、定义事务的边界等。在Spring整合Mybatis时,经常会用到这种方式来实现声明式事务,只需要在需要使用事务的方法上添加@Transactional注解即可:

@Transactional
public void insertUser(User user) {
    userDao.insert(user);
}

六、AOP切面编程思想

AOP切面编程思想,是其在设计模式上的具体落地。通过将不同的功能划分为不同的切面或者模块,使其更具有可读性、可维护性和可扩展性。大大减少了重复代码和代码冗余,使得代码更加简介,减轻了程序员的工作量,提高开发效率。

七、AOP切面编程架构

AOP切面编程架构通常有三个核心模块,即切面模块、切入点模块和通知模块。

1. 切面模块

切面模块是AOP切面编程的核心模块,它由一个或多个切面组成。每一个切面都包含了一个或多个通知和一个或多个切点。

2. 切入点模块

切入点模块用于定位在目标类中的切点位置。在整个AOP过程中,先针对具体的方法进行拦截,拦截到方法后再根据用户定义的切面进行处理。

3. 通知模块

通知模块则包括了具体的增强代码,而这些增强代码都是用户自己定义的。通知可以包括Before Advice、After Advice、Around Advice、After Throwing Advice和After Returning Advice等。

八、AOP切面编程作用

AOP切面编程能够让我们的代码更加优雅、简洁、易于维护、扩展和复用,并且可以大大降低多个组件之间的耦合度,并且大大减少重复开发、代码冗余以及出错几率。性能方面也有所提高,例如Redis缓存、声明式事务等。

九、AOP切面编程代码示例

下面是一个简单的Spring Boot应用,使用AOP切面编程来记录用户新增和删除操作的日志。

1. 新增记录日志切面

@Aspect
@Component
public class UserAddAspect {

    @Autowired
    private HttpServletRequest request;

    private static final Logger logger = LoggerFactory.getLogger(UserAddAspect.class);

    @Before("@annotation(com.example.demo.annotation.UserAdd)")
    public void addLog() {
        String methodName = "新增用户";
        String params = request.getParameter("username");
        logger.info("执行 " + methodName + " 方法,参数为:" + params);
    }

}

2. 删除记录日志切面

@Aspect
@Component
public class UserDeleteAspect {

    @Autowired
    private HttpServletRequest request;

    private static final Logger logger = LoggerFactory.getLogger(UserDeleteAspect.class);

    @Before("@annotation(com.example.demo.annotation.UserDelete)")
    public void deleteLog() {
        String methodName = "删除用户";
        String params = request.getParameter("userId");
        logger.info("执行 " + methodName + " 方法,参数为:" + params);
    }

}

3. 控制器中使用

@RestController
public class UserController {

    @Autowired
    private UserService userService;

    @UserAdd //记录新增用户日志
    @PostMapping("/add")
    public String addUser(@RequestParam String username, @RequestParam String password) {
        userService.addUser(username, password);
        return "success";
    }

    @UserDelete //记录删除用户日志
    @DeleteMapping("/delete/{userId}")
    public String deleteUser(@PathVariable String userId) {
        userService.deleteUser(userId);
        return "success";
    }

}

将注解UserAdd和UserDelete,分别标记在新增用户和删除用户的方法上。当我们调用这两个方法时,将会在控制台上打印出相应的日志,从而实现了记录用户操作的目的。

原创文章,作者:小蓝,如若转载,请注明出处:https://www.506064.com/n/227400.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
小蓝小蓝
上一篇 2024-12-09 16:29
下一篇 2024-12-09 16:29

相关推荐

  • Python周杰伦代码用法介绍

    本文将从多个方面对Python周杰伦代码进行详细的阐述。 一、代码介绍 from urllib.request import urlopen from bs4 import Bea…

    编程 2025-04-29
  • Python官网中文版:解决你的编程问题

    Python是一种高级编程语言,它可以用于Web开发、科学计算、人工智能等领域。Python官网中文版提供了全面的资源和教程,可以帮助你入门学习和进一步提高编程技能。 一、Pyth…

    编程 2025-04-29
  • Python字符串宽度不限制怎么打代码

    本文将为大家详细介绍Python字符串宽度不限制时如何打代码的几个方面。 一、保持代码风格的统一 在Python字符串宽度不限制的情况下,我们可以写出很长很长的一行代码。但是,为了…

    编程 2025-04-29
  • Python基础代码用法介绍

    本文将从多个方面对Python基础代码进行解析和详细阐述,力求让读者深刻理解Python基础代码。通过本文的学习,相信大家对Python的学习和应用会更加轻松和高效。 一、变量和数…

    编程 2025-04-29
  • 掌握magic-api item.import,为你的项目注入灵魂

    你是否曾经想要导入一个模块,但却不知道如何实现?又或者,你是否在使用magic-api时遇到了无法导入的问题?那么,你来到了正确的地方。在本文中,我们将详细阐述magic-api的…

    编程 2025-04-29
  • 仓库管理系统代码设计Python

    这篇文章将详细探讨如何设计一个基于Python的仓库管理系统。 一、基本需求 在着手设计之前,我们首先需要确定仓库管理系统的基本需求。 我们可以将需求分为以下几个方面: 1、库存管…

    编程 2025-04-29
  • Python满天星代码:让编程变得更加简单

    本文将从多个方面详细阐述Python满天星代码,为大家介绍它的优点以及如何在编程中使用。无论是刚刚接触编程还是资深程序员,都能从中获得一定的收获。 一、简介 Python满天星代码…

    编程 2025-04-29
  • 写代码新手教程

    本文将从语言选择、学习方法、编码规范以及常见问题解答等多个方面,为编程新手提供实用、简明的教程。 一、语言选择 作为编程新手,选择一门编程语言是很关键的一步。以下是几个有代表性的编…

    编程 2025-04-29
  • Python实现简易心形代码

    在这个文章中,我们将会介绍如何用Python语言编写一个非常简单的代码来生成一个心形图案。我们将会从安装Python开始介绍,逐步深入了解如何实现这一任务。 一、安装Python …

    编程 2025-04-29
  • 怎么写不影响Python运行的长段代码

    在Python编程的过程中,我们不可避免地需要编写一些长段代码,包括函数、类、复杂的控制语句等等。在编写这些代码时,我们需要考虑代码可读性、易用性以及对Python运行性能的影响。…

    编程 2025-04-29

发表回复

登录后才能评论