【Java框架】Spring框架(二)——Spring基本核心(AOP)

目录

  • 面向切面编程AOP
    • AOP的目标:让我们可以“专心做事”
      • 专心做事
      • 专心做事解决方案1.0
      • 专心做事解决方案2.0
        • 蓝图
    • AOP应用场景
    • AOP原理
    • AOP相关术语
      • 术语理解
  • AOP案例实现
    • 前置/后置/异常/最终增强的配置实现
      • 1.依赖
      • 2.业务类
      • 3.日志类
      • 4.配置
        • 切入点表达式匹配规则举例
    • 环绕增强的配置实现(1替4)
      • 1.service类不变
      • 2.日志类
      • 3.配置
    • Spring AOP配置元素
    • 注解实现AOP
      • 1.service类不变
      • 2.日志类
      • 3.配置

面向切面编程AOP

AOP的目标:让我们可以“专心做事”

专心做事

  • 我们作为开发系统可以分为两大部分:业务功能和辅助业务的隐性功能。
  • 例如开发一个商城系统,商品模块的商品添加业务就是业务功能;
  • 而保证商品添加不出问题,如程序在执行商品添加时的入参/出参记录、事务处理等就属于辅助业务的隐性功能
  • 通常这些隐性的辅助功能一来都比较通用,二来跟业务平没有什么联系。
  • 因此就需要考虑将这些通用功能集中处理,来简化编码、专心做事。

专心做事解决方案1.0

解决方案:把公共代码抽取到一个父类的BaseService,让具体的模块Service继承BaseService,然后调用父类的功能,这样做即:通过继承的方式实现对业务方法的功能的增强。如:
通用功能类

public class BaseService {    
    // 新增事务
    public void transaction(){
        System.out.println("事务功能");
    }   
    // 入参日志
    public void before(){
        System.out.println("记录入参日志功能");
    }
    // 出参日志
    public void afterReturnLog(){
        System.out.println("记录出参日志功能");
    }
    // 资源最终关闭
    public void afterLog(){
        System.out.println("资源close功能...");
    }
}

业务类

public class UserServiceImpl extends BaseService{
    public void save(User user){
        super.before();
        super.transaction();
        // 调用Dao新增用户
        super.afterReturnLog();
        super.after();
    }
}
public class GoodsServiceImpl extends BaseService{
    public void save(Goods goods){
        super.before();
        super.transaction();
        // 调用Dao新增商品
        super.afterReturnLog();
        super.after();
    }
}

但是这样做还有一个问题,虽然我们其他模块也需要此功能时,可以采用继承的方式来做,但是一个很严重的问题是:我们的业务功能在去调用的时候,对业务功能的增强实际上还是硬编码了。还是没有解决方便维护的问题,那我们期望能够解决的问题是:能否“神不知鬼不觉”的在不改变源代码的情况下去扩展功能呢? 答案肯定是可以的,那这就是AOP,同时,这个也是我们学习AOP的原因所在,也是AOP的作用所在。

专心做事解决方案2.0

蓝图

在这里插入图片描述
如果从系统的横向角度来看,我们的日志功能,事务功能、资源关闭功能是把系统的各个模块中的各个业务方法在执行之前从前面拦截了,好像拿了一把刀把一个完整的苹果切成一半,形成了一个切面。这个也就是 "面向切面编程 中切面的含义

AOP应用场景

日志记录、性能统计、安全控制、事务处理、异常处理

AOP原理

  • 将复杂的需求分解出不同方面,将散布在系统中的公共功能集中解决
  • 采用代理机制组装起来运行,在不改变原程序的基础上对代码段进行增强处理,增加新的功能
  • 核心:动态代理
    在这里插入图片描述

AOP相关术语

  • 增强处理/通知(Advice)
    所谓通知/增强是指拦截到 Joinpoint 之后所要做的事情就是通知。说白了,就是一段功能性的代码。
    • 前置增强
    • 后置增强
    • 环绕增强、异常抛出增强、最终增强等类型
  • 切入点(Pointcut):
    切入点就是对连接点中哪些点进行拦截的定义,对连接点(一般所有方法都可做连接点)进行条件筛选。
  • 连接点(Join Point):
    连接点就是可以被拦截的点,在程序中,通常指的是方法,在Spring中只支持方法类型的连接点。在其他的地方可能支持类,这里记住方法就行了。
  • 切面(Aspect):
    是切入点和通知/增强的结合。
  • 目标对象(Target object)
  • AOP代理(AOP proxy)
  • 织入(Weaving):
    指的是在把增强的方法加入到目标对象(切点方法的拥有者)来创建新的对象的过程,spring采用的是动态代理织入(jdk动态代理和子类动态代理都有),AspectJ采用编译期织入和类装载织入。

术语理解

用LOL中远古龙BUFF来理解吧:比如你现在在蓝色方,蓝色方刚刚跟对面打了一波5V5团战,1换5,蓝色方辅助挂了,这个时候已经30分钟后了,你们开始去打远古龙,打掉远古龙的时候辅助还没有复活,因此只有你们四个人获得了远古龙BUFF。

  • Joint point(连接点): 在上面的故事里,你们四个有了远古龙BUFF,就是都能被增强,可以看到远古龙BUFF是可以作用在上面所有人的身上,因为如果辅助不挂他也会有BUFF。在Spring中,这些人就等于可以被增强的方法。
  • Pointcut(切点): 在上面的故事里,只有你们四个有远古龙BUFF,但辅助没有。可以看出来Pointcut(切点)是有条件的Joint point(连接点),活着的人被增强了。
  • Advice(通知/增强): 你们四个在打掉远古龙的时候活着,那么你们四个就被增强了。
  • Aspect(切面): 切点和通知的结合,上面的切点就是活着的你们四个(四个点),通知是打掉远古龙后获得的BUFF,切点有很多,连载一起就像一个面一样。

AOP案例实现

在这里插入图片描述在这里插入图片描述

前置/后置/异常/最终增强的配置实现

1.依赖

	<dependency>
      <groupId>org.projectlombok</groupId>
      <artifactId>lombok</artifactId>
      <version>1.18.22</version>
      <scope>provided</scope>
    </dependency>


    <!-- aop依赖 -->
    <dependency>
      <groupId>org.aspectj</groupId>
      <artifactId>aspectjweaver</artifactId>
      <version>1.9.6</version>
    </dependency>
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-jdbc</artifactId>
      <version>5.1.9.RELEASE</version>
    </dependency>

2.业务类

@Slf4j
@Service
public class UserServiceImpl implements UserService {
    @Resource
    private UserMapper userMapper;
    @Override
    public Object saveUser(String userName) {
        //log.info("调用org.aop.service.impl.UserServiceImpl的saveUser(),入参是{}",user);
        boolean result = userMapper.insertUser(userName) > 0;
        //log.info("调用org.aop.service.impl.UserServiceImpl的saveUser()完毕,返回值是{}",result);
        return result;
    }

    @Override
    public boolean updateUser(User user) {
        //log.info("调用org.aop.service.impl.UserServiceImpl的updateUser(),入参是{}",user);
        boolean result = userMapper.updateUser(user) > 0;
        //log.info("调用org.aop.service.impl.UserServiceImpl的updateUser()完毕,返回值是{}",result);
        return result;
    }
}

3.日志类

package org.aop.log;

import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.springframework.stereotype.Component;

import java.util.Arrays;

/**
 * @author: zjl
 * @datetime: 2024/3/30
 * @desc:
 */
@Slf4j
public class MyServiceLogger {
	//前置增强
    public void before(JoinPoint jp) {
        log.info("调用 " + jp.getTarget() + " 的 " + jp.getSignature().
                getName() + " 方法。方法入参:" + Arrays.toString(jp.getArgs()));
    }
	//后置增强
    public void afterReturning(JoinPoint jp, Object result) {
        log.info("调用 " + jp.getTarget() + " 的 " +  jp.getSignature().
                getName() + " 方法。方法返回值:" + result);
    }
	//异常处理增强
    public void afterThrowing(JoinPoint jp, Throwable e) {
        log.info("调用 " + jp.getTarget() + " 的 " +  jp.getSignature().
                getName() + " 方法。方法抛出异常:" + e);
    }
	//最终增强
    public void after(JoinPoint jp) {
        log.info("调用 " + jp.getTarget() + " 的 " +  jp.getSignature().
                getName() + " 方法。方法执行完毕。模拟关闭资源...");
    }

4.配置

在这里插入图片描述

增强处理类型特 点
Before前置增强处理,在目标方法前织入增强处理
AfterReturning后置增强处理,在目标方法正常执行(不出现异常)后织入增强处理
AfterThrowing异常增强处理,在目标方法抛出异常后织入增强处理
After最终增强处理,不论方法是否抛出异常,都会在目标方法最后织入增强处理
Around环绕增强处理,在目标方法的前后都可以织入增强处理
<?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-3.2.xsd
    http://www.springframework.org/schema/context
    http://www.springframework.org/schema/context/spring-context-3.2.xsd
    http://www.springframework.org/schema/aop
    http://www.springframework.org/schema/aop/spring-aop-3.2.xsd">

    <!--                    配置相关的AOP                -->
    <bean id="myServiceLog" class="org.aop.log.MyServiceLogger"/>
    <aop:config>
        <aop:pointcut id="pointcut"
                      expression="execution(* org.aop.service..*.*(..)))"/>
        <aop:aspect ref="myServiceLog">
            <aop:before method="before"
                        pointcut-ref="pointcut"></aop:before>
            <aop:after-returning method="afterReturning"
                                 pointcut-ref="pointcut" returning="result"/>
            <aop:after-throwing method="afterThrowing" pointcut-ref="pointcut" throwing="e"/>
            <aop:after method="after" pointcut-ref="pointcut"/>
        </aop:aspect>
    </aop:config>
</beans>
切入点表达式匹配规则举例
public * addNewUser(entity.User): “*”表示匹配所有类型的返回值。
public void *(entity.User): “*”表示匹配所有方法名。
public void addNewUser(..): “..”表示匹配所有参数个数和类型。
* com.zjl.service.*.*(..):匹配com.zjl.service包下所有类的所有方法。
* com.zjl.service..*.*(..):匹配com.zjl.service包及其子包下所有类的所有方法

环绕增强的配置实现(1替4)

1.service类不变

2.日志类

注释掉前面四种增强的方法,加入这个环绕增强的方法

    public Object around(ProceedingJoinPoint jp) throws Throwable {
        Object result = null;
        try {
            log.info("调用 " + jp.getTarget() + " 的 " +  jp.getSignature().
                    getName() + " 方法。方法入参:" + Arrays.toString(jp.getArgs()));
            result = jp.proceed();
            log.info("调用 " + jp.getTarget() + " 的 " +  jp.getSignature().
                    getName() + " 方法。方法返回值是:" + result);
        }catch (Exception e){
            log.info("调用 " + jp.getTarget() + " 的 " +  jp.getSignature().
                    getName() + " 方法。发生了异常,异常信息为:" + e);
        }finally {
            log.info("调用 " + jp.getTarget() + " 的 " +  jp.getSignature().
                    getName() + " 方法,进行最终处理,比如模拟资源关闭");
        }
        return result;
    }

3.配置

    <!--                    配置相关的AOP                -->
    <bean id="myServiceLog" class="org.aop.log.MyServiceLogger"/>
    <aop:config>
        <aop:pointcut id="pointcut"
                      expression="execution(* org.aop.service..*.*(..)))"/>
        <aop:aspect ref="myServiceLog">
            <!--<aop:before method="before"
                        pointcut-ref="pointcut"></aop:before>
            <aop:after-returning method="afterReturning"
                                 pointcut-ref="pointcut" returning="result"/>
            <aop:after-throwing method="afterThrowing" pointcut-ref="pointcut" throwing="e"/>
            <aop:after method="after" pointcut-ref="pointcut"/>-->

            <aop:around method="around" pointcut-ref="pointcut"/>
        </aop:aspect>
    </aop:config>

Spring AOP配置元素

AOP配置元素描 述
<aop:config>AOP配置的顶层元素,大多数的<aop:*>元素必须包含在<aop:config>元素内
<aop:pointcut>定义切点
<aop:aspect>定义切面
<aop:after>定义最终增强(不管被通知的方法是否执行成功)
<aop:after-returning>定义after-returning增强
<aop:after-throwing>定义after-throwing增强
<aop:around>定义环绕增强
<aop:before>定义前置增强
<aop:aspectj-autoproxy>启动@AspectJ注解驱动的切面

注解实现AOP

1.service类不变

2.日志类

package org.aop.log;

import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.springframework.stereotype.Component;

import java.util.Arrays;

/**
 * @author: zjl
 * @datetime: 2024/3/30
 * @desc:
 */
@Slf4j
@Aspect
@Component
public class MyServiceLogger {
    //1.针对service实现类中所有方法,记录某个方法在被调用时的入参信息
    //@Before("execution(* org.aop.service..*.*(..))")
    public void before(JoinPoint jp) {
        log.info("调用 " + jp.getTarget() + " 的 " + jp.getSignature().
                getName() + " 方法。方法入参:" + Arrays.toString(jp.getArgs()));
    }


    //2.针对service实现类中所有方法,记录某个方法被调用后的返回值信息
    //@AfterReturning(returning = "result", pointcut = "execution(* org.aop.service..*.*(..))")
    public void afterReturning(JoinPoint jp, Object result) {
        log.info("调用 " + jp.getTarget() + " 的 " +  jp.getSignature().
                getName() + " 方法。方法返回值:" + result);
    }

    //@AfterThrowing(throwing = "e", pointcut = "execution(* org.aop.service..*.*(..))")
    public void afterThrowing(JoinPoint jp, Throwable e) {
        log.info("调用 " + jp.getTarget() + " 的 " +  jp.getSignature().
                getName() + " 方法。方法抛出异常:" + e);
    }

   // @After("execution(* org.aop.service..*.*(..))")
    public void after(JoinPoint jp) {
        log.info("调用 " + jp.getTarget() + " 的 " +  jp.getSignature().
                getName() + " 方法。方法执行完毕。模拟关闭资源...");
    }

    @Around("execution(* org.aop.service..*.*(..))")
    public Object around(ProceedingJoinPoint jp) throws Throwable {
        Object result = null;
        try {
            log.info("调用 " + jp.getTarget() + " 的 " +  jp.getSignature().
                    getName() + " 方法。方法入参:" + Arrays.toString(jp.getArgs()));
            result = jp.proceed();
            log.info("调用 " + jp.getTarget() + " 的 " +  jp.getSignature().
                    getName() + " 方法。方法返回值是:" + result);
        }catch (Exception e){
            log.info("调用 " + jp.getTarget() + " 的 " +  jp.getSignature().
                    getName() + " 方法。发生了异常,异常信息为:" + e);
        }finally {
            log.info("调用 " + jp.getTarget() + " 的 " +  jp.getSignature().
                    getName() + " 方法,进行最终处理,比如模拟资源关闭");
        }
        return result;
    }
}

3.配置

<?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-3.2.xsd
    http://www.springframework.org/schema/context
    http://www.springframework.org/schema/context/spring-context-3.2.xsd
    http://www.springframework.org/schema/aop
    http://www.springframework.org/schema/aop/spring-aop-3.2.xsd">

    <context:component-scan base-package="org.aop"/>
    <aop:aspectj-autoproxy />
</beans>

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mfbz.cn/a/559041.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Spring AOP (二)

本篇将介绍Spring AOP的相关原理。 一、代理模式 Spring 在实现AOP时使用了代理模式这种设计模式&#xff0c;什么是代理模式呢我们来了解一下。 代理模式可以理解为当我们需要调用某个类的方法时&#xff0c;在我们与这个目标类之间增加一个代理类&#xff0c;我们要使用目标…

vue2响应式 VS vue3响应式

Vue2响应式 存在问题&#xff1a; 新增属性&#xff0c;删除属性&#xff0c;界面不会更新。 直接通过下标修改数组界面不会自动更新。 Vue2使用object.defineProperty来劫持数据是否发生改变&#xff0c;如下&#xff1a; 能监测到获取和修改属性&#xff1a; 新增的属性…

【C++打怪之路】-- C++开篇

&#x1f308; 个人主页&#xff1a;白子寰 &#x1f525; 分类专栏&#xff1a;C打怪之路&#xff0c;python从入门到精通&#xff0c;魔法指针&#xff0c;进阶C&#xff0c;C语言&#xff0c;C语言题集&#xff0c;C语言实现游戏&#x1f448; 希望得到您的订阅和支持~ &…

【C语言】深入解析选择排序算法

一、算法原理二、算法性能分析三、C语言实现示例四、总结 一、算法原理 选择排序&#xff08;Selection Sort&#xff09;是一种简单直观的排序算法。它的工作原理是不断地选择剩余元素中的最小&#xff08;或最大&#xff09;元素&#xff0c;放到已排序的序列的末尾&#xff…

securecrt 批量登录服务器介绍

一、前言 在有一些IT环境中&#xff0c;可能存在各种情况的服务器&#xff0c;因为各种原因不能统一部署类似ansible、saltstack等批量操控软件&#xff0c;当遇到需要对这些服务器进行某项信息的排查或调整配置时&#xff0c;你是否还是通过securecrt一台一台登录后进行操作&a…

endnote21从安装到使用!文献引用!Mac版

视频学习和资源获取 新建库 选择上方导航栏处的File下的New 软件 软件界面可以分成四个部分 2是个人图书馆 3是对某一分类中文献的展示 最右侧是对具体一篇文献的摘要、编辑以及PDF 有回形针标志意味着这篇有全文&#xff0c;也就是有pdf 如果没有回形针代表它只有引文信…

社交媒体数据恢复:BF Messager

BF Messenger 数据恢复方法 一、前言 BF Messenger&#xff08;BF加密聊天软件&#xff09;是一款基于布尔式循环移位加密算法的聊天应用程序。它使用对称密钥加密技术&#xff0c;用户可以在安全的环境下进行私密聊天。除此之外&#xff0c;该应用还具有防截屏、应用锁屏、密…

LeetCode in Python 55. Jump Game (跳跃游戏)

跳跃游戏的游戏规则比较简单&#xff0c;若单纯枚举所有的跳法以判断是否能到达最后一个下标需要的时间复杂度为O()&#xff0c;为此&#xff0c;本文采用贪心策略&#xff0c;从最后一个下标开始逆着向前走&#xff0c;若能跳到第一个元素则表明可以完成跳跃游戏&#xff0c;反…

本地主机搭建服务器后如何让外网访问?快解析内网端口映射

本地主机搭建应用、部署服务器后&#xff0c;在局域网内是可以直接通过计算机内网IP网络地址进行连接访问的&#xff0c;但在外网电脑和设备如何访问呢&#xff1f;由于内网环境下&#xff0c;无法提供公网IP使用&#xff0c;外网访问内网就需要一个内外网转换的介质。这里介绍…

stm32开发之netxduo组件之mqtt客户端的使用记录

前言 1使用mqtt协议的简单示例记录 代码 MQTT服务端(C# 编写,使用MQTTnet提供的示例代码) 主程序 namespace ConsoleApp1;public class Program {public static async Task Main(string[] args){await Run_Server_With_Logging();}}public static async Task Run_Server_Wi…

CERLAB无人机自主框架: 1-环境搭建

前言&#xff1a;更多更新文章详见我的个人博客主页【MGodmonkeyの世界】 描述&#xff1a;欢迎来到CERLAB无人机自主框架&#xff0c;这是一个用于自主无人飞行器 (UAV) 的多功能模块化框架。该框架包括不同的组件 (模拟器&#xff0c;感知&#xff0c;映射&#xff0c;规划和…

后台管理系统加水印(react)

效果 代码图片 代码 window.waterMark function (config) {var defaultConfig {content: 我是水印,fontSize: 16px,opacity: 0.3,rotate: -15,color: #ADADAD,modalId: J_waterMarkModalByXHMAndDHL,};config Object.assign({}, defaultConfig, config);var existMarkModal…

亚信安全入选中国数据安全市场图谱

近日&#xff0c;全球领先的IT市场研究和咨询公司IDC发布了《IDC Market Glance&#xff1a;中国数据安全市场图谱&#xff0c;2024》报告&#xff08;以下简称“报告”&#xff09;&#xff0c;报告展示了中国数据安全市场的构成和格局&#xff0c;遴选出不同细分市场领域的主…

rabbitmq 使用SAC队列实现顺序消息

rabbitmq 使用SAC队列实现顺序消息 前提 SAC: single active consumer, 是指如果有多个实例&#xff0c;只允许其中一个实例消费&#xff0c;其他实例为空闲 目的 实现消息顺序消费&#xff0c;操作&#xff1a; 创建4个SAC队列,消息的路由key 取队列个数模&#xff0c;这…

java调用讯飞星火认知模型

前往讯飞开发平台选择产品&#xff0c;获取appId、apiKey、APISecret&#xff0c;这里我选择的是v3.0模型。 java后端实现 本项目以及实现了基本的会话功能&#xff0c;小伙伴可以自己扩充其他的例如绘画功能。 注意&#xff1a;星火模型的api使用的是websocket协议&#xf…

C++11(下篇)

文章目录 C111. 模版的可变参数1.1 模版参数包的使用 2. lambda表达式2.1 Lambda表达式语法捕获列表说明 2.2 lambda的底层 3. 包装器3.1 function包装器3.2 bind 4. 线程库4.1 thread类4.2 mutex类4.3 atomic类4.4 condition_variable类 C11 1. 模版的可变参数 C11支持模版的…

数据结构习题-- 相交链表

数据结构习题-- 相交链表 给你两个单链表的头节点 headA 和 headB &#xff0c;请你找出并返回两个单链表相交的起始节点。如果两个链表不存在相交节点&#xff0c;返回 null 如上图&#xff0c;返回c1结点 注意&#xff1a;这两个链表非环形 方法&#xff1a;集合 分析 由…

关于ERA5气压和温度垂直补偿公式的对比情况

1. 气压和温度垂直补偿对比 「谨代表给个人观点&#xff0c;杠精请自测&#xff0c;对对对&#xff0c;好好好&#xff0c;你说啥都对」。 使用2020-2022陆态网GNSS与探空站并址的48个站点实验&#xff0c;以探空站为真值&#xff0c;验证ERA5精度。怎么确定并址请看前面文章…

Django中的实时通信:WebSockets与异步视图的结合

&#x1f47d;发现宝藏 前些天发现了一个巨牛的人工智能学习网站&#xff0c;通俗易懂&#xff0c;风趣幽默&#xff0c;忍不住分享一下给大家。【点击进入巨牛的人工智能学习网站】。 在现代Web应用程序中&#xff0c;实时通信已经成为了必不可少的功能之一。无论是在线聊天、…

UKP3D,出轴 /平面图时,选项中出图比例,绘图比例,打印比例的区别

Q:用户问&#xff0c;轴测图正常&#xff0c;平面图位置不对&#xff0c;这个也需要在xml里面调整吗&#xff1f; 在此&#xff0c;先不回复上述问题&#xff0c;而是解释在出图规则里的选项意思。 1.图框比例&#xff1a;图框比例1:100&#xff0c;例如选中的图幅是A0横式&…