09.AOP-尚硅谷Spring零基础入门到进阶,一套搞定spring6全套视频教程(源码级讲解)

现有代码缺陷
针对带日志功能的实现类,我们发现有如下缺陷:

  • 对核心业务功能有干扰,导致程序员在开发核心业务功能时分散了精力
  • 附加功能分散在各个业务功能方法中,不利于统一维护
    解决思路
    解决核心:解耦。把附加功能从业务功能代码中抽取出来。
    困难
    解决问题的困难:要抽取的代码在方法内部,靠以前把子类中的重复代码抽取到父类的方式没法解决。所以需要引
    入新的技术。

代理模式

概念

介绍
二十三种设计模式中的一种,属于结构型模式。它的作用就是通过提供一个代理类,让我们在调用目标方法的时
候,不再是直接对目标方法进行调用,而是通过代理类间接调用。让不属于目标方法核心逻辑的代码从目标方法中
剥离出来一一解耦。调用目标方法时先调用代理对象的方法,减少对目标方法的调用和打扰,同时让附加功能能够
集中在一起也有利于统一维护。
相关术语
,代理:将非核心逻辑剥离出来以后,封装这些非核心逻辑的类、对象、方法。
·目标:被代理“套用”了非核心逻辑代码的类、对象、方法。

场景模拟

  1. 声明计算器接口Calculator,包含加减乘除的抽象方法

    package org.example;  public interface Calculator {  public int add(int i, int j);  public int sub(int i, int j);  public int mul(int i, int j);  public int div(int i, int j);  
    }
    
  2. 写一个实现Calculator业务的实现类

    package org.example;
    public class CalculatorImpl implements Calculator {@Overridepublic int add(int i, int j) {int result = i + j;System.out.println("result=" + result);return result;}@Overridepublic int sub(int i, int j) {int result = i - j;System.out.println("result=" + result);return result;}@Overridepublic int mul(int i, int j) {int result = i * j;System.out.println("result=" + result);return result;}@Overridepublic int div(int i, int j) {int result = i / j;System.out.println("result=" + result);return result;}
    }
    
  3. 写一个实现Calculator业务的带有日志功能的实现类

    package org.example;  
    public class CalculatorLogImpl implements Calculator {@Overridepublic int add(int i, int j) {System.out.println("计算开始,i=" + i + "j=" + j);int result = i + j;System.out.println("计算结束,i=" + i + "j=" + j + "result=" + result);System.out.println("result=" + result);return result;}@Overridepublic int sub(int i, int j) {System.out.println("计算开始,i=" + i + "j=" + j);int result = i - j;System.out.println("计算结束,i=" + i + "j=" + j + "result=" + result);System.out.println("result=" + result);return result;}@Overridepublic int mul(int i, int j) {System.out.println("计算开始,i=" + i + "j=" + j);int result = i * j;System.out.println("计算结束,i=" + i + "j=" + j + "result=" + result);System.out.println("result=" + result);return result;}@Overridepublic int div(int i, int j) {System.out.println("计算开始,i=" + i + "j=" + j);int result = i / j;System.out.println("计算结束,i=" + i + "j=" + j + "result=" + result);System.out.println("result=" + result);return result;}
    }
    

静态代理

静态代理确实实现了解耦,但是由于代码都写死了,完全不具备任何的灵活性。就拿日志功能来说,将来其
他地方也需要附加日志,那还得再声明更多个静态代理类,那就产生了大量重复的代码,日志功能还是分散
的,没有统一管理。
提出进一步的需求:将日志功能集中到一个代理类中,将来有任何日志需求,都通过这一个代理类来实现。
这就需要使用动态代理技术了。

动态代理

使用java.lang.reflect.Proxy类实现动态代理
官方示例代码

InvocationHandler handler = new MyInvocationHandler(...);  
Foo f = (Foo) Proxy.newProxyInstance(Foo.class.getClassLoader(),  new class<?>[]{Foo.class},  handler);

创建一个代理工厂类

package org.example;  import lombok.val;  import javax.print.attribute.standard.JobKOctets;  
import java.lang.reflect.InvocationHandler;  
import java.lang.reflect.Method;  
import java.lang.reflect.Proxy;  public class ProxyFactory {  Object target;  public ProxyFactory(Object target) {  this.target = target;  }  public Object getProxy() {  
/*      有三个参数  第一个参数:CLassLoader:加载动态生成代理类的来加载器  第二个参数:CLass[]interfaces:目录对象实现的所有接口cLass类型数组  第三个参数:InvocationHandler:设置代理对象实现目标对象方法的过程*/  ClassLoader cLassLoader = target.getClass().getClassLoader();  Class[] classes = target.getClass().getInterfaces();  InvocationHandler invocationHandler = new InvocationHandler() {  @Override  public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {  //调用方法前日志  System.out.println("[动态代理][调用前日志]" + method.getName() + "参数:" + args);  //调用目标方法  Object result = method.invoke(target, args);  //调用方法后日志  System.out.println("[动态代理][调用后日志]" + method.getName() + "参数:" + args);  return result;  }  };  return Proxy.newProxyInstance(cLassLoader, classes, invocationHandler);  }  
}

编写测试类

@Test  
public void calculatorTest(){  ProxyFactory proxyFactory=new ProxyFactory(new CalculatorImpl());  Calculator proxy=(Calculator) proxyFactory.getProxy();  proxy.add(1,1);  
}

输出结果

[动态代理][调用前日志]add参数:[Ljava.lang.Object;@7d0587f1
result=2
[动态代理][调用后日志]add参数:[Ljava.lang.Object;@7d0587f1

基于注解的AOP

动态代理分类:JDK动态代理和cglib动态代理
JDK动态代理生成接口实现类代理对象
cglib动态代理继承被代理的目标类,生成子类代理对象,不需要目标类实现接口

  • 有接口可以使用JDK动态代理和cblib动态代理
  • 没有接口只能使用cblib动态代理
    Aspect:是AOP思想的一种实现。本质上是静态代理,将代理逻辑“织入"被代理的目标类编译得到的字节码
    文件,所以最终效果是动态的。weaver就是织入器。Spring只是借用了Aspect)中的注解。

使用AOP步骤

  1. 引入aop相关依赖

    <!--spring aop依赖-->  
    <dependency>  <groupId>org.springframework</groupId>  <artifactId>spring-aop</artifactId>  <version>6.0.2</version>  
    </dependency>  
    <!--spring aspects依赖-->  
    <dependency>  <groupId>org.springframework</groupId>  <artifactId>spring-aspects</artifactId>  <version>6.0.2</version>  
    </dependency>
    
  2. 创建目标资源

    1. 接口

      package com.example.annoAOP;  public interface Calculator {  public int add(int i, int j);  public int sub(int i, int j);  public int mul(int i, int j);  public int div(int i, int j);  
      }
      
    2. 实现类

      package com.example.annoAOP;  import org.springframework.stereotype.Component;  @Component  
      public class CalculatorImpl implements Calculator {  @Override  public int add(int i, int j) {  int result = i + j;  System.out.println("result=" + result);  return result;  }  @Override  public int sub(int i, int j) {  int result = i - j;  System.out.println("result=" + result);  return result;  }  @Override  public int mul(int i, int j) {  int result = i * j;  System.out.println("result=" + result);  return result;  }  @Override  public int div(int i, int j) {  int result = i / j;  System.out.println("result=" + result);  return result;  }  
      }
      

第三步创建切面类

  1. 创建bean.xml,使用AOP约束,开启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: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">  <!-- 开启组件扫描           -->  <context:component-scan base-package="com.example"/>  <!--开启aspectj自动代理,为目标对象生成代理-->  <aop:aspectj-autoproxy></aop:aspectj-autoproxy>  
    </beans>
    
  2. 创建LogAscept类,增加一个方法的前置切入点

    package com.example.annoAOP;  import org.aspectj.lang.annotation.Aspect;  
    import org.aspectj.lang.annotation.Before;  
    import org.springframework.stereotype.Component;  @Aspect//表明这是一个AOP文件  
    @Component//让IoC进行管理  
    public class LogAspect {  //设置切入点和通知类型  //通知类型:  // 前置   @Before(value="切入点表达式")  // 返回   @AfterReturning    // 异常   @AfterThrowing    // 后置   @After()    // 环绕   @Around()    //切入点表达式写法:execution(权限修饰 方法返回值 方法所在全类名.方法名 (参数列表))  //execution:固定语法  //权限修饰:这里写*表示权限修饰符和返回值任意  //方法所在全类名:写*表示任意包名;写*...表示包名任意同时包层次深度任意  //类名用*号代替表示类名任意,部分用*代替,如*Service,表示匹配以Service结尾的列或接口  //方法名:用*号代替表示方法名任意;部分用*代替,如get*,表示匹配以get开头的方法  //参数列表可以使用(...)形式表示参数列表任意  @Before(value = "execution(public int com.example.annoAOP.CalculatorImpl.add (int,int))")  public void beforeAdd() {  System.out.println("[前置通知][add()]计算开始");  }  
    }
    

    方法表达式写法:

    在这里插入图片描述

  3. 创建测试方法

    @Test
    public void testAOPAdd(){ApplicationContext applicationContext=new ClassPathXmlApplicationContext("bean.xml");Calculator calculator=applicationContext.getBean(Calculator.class);calculator.add(1,1);
    }
    
  4. 输出结果

    [前置通知][add()]计算开始
    result=2
    

通知类型

  • 前置通知:在被代理的目标方法前执行
  • 返回通知:在被代理的目标方法成功结束后执行(寿终正寝)
  • 异常通知:在被代理的目标方法异常结束后执行(死于非命)
  • 后置通知:在被代理的目标方法最终结束后执行(盖棺定论)
  • 环绕通知:使用try.catch.finally结构围绕整个被代理的目标方法,包括上面四种通知对应的所有位置
    修改LogAspect类,添加五种通知方法
package com.example.annoAOP;  import org.aspectj.lang.JoinPoint;  
import org.aspectj.lang.ProceedingJoinPoint;  
import org.aspectj.lang.annotation.*;  
import org.springframework.stereotype.Component;  @Aspect//表明这是一个AOP文件  
@Component//让IoC进行管理  
public class LogAspect {  //前置通知  @Before(value = "execution(* com.example.annoAOP.CalculatorImpl.* (..))")  public void beforeMethod(JoinPoint joinPoint) {  String MethodName = joinPoint.getSignature().getName();  Object[] args = joinPoint.getArgs();  System.out.println("[前置通知][CalculatorImpl.MethodName=" + MethodName + "()");  System.out.println("Args[]=" + args);  }  //后置通知  @After(value = "execution(* com.example.annoAOP.CalculatorImpl.* (..))")  public void afterMethod(JoinPoint joinPoint) {  String MethodName = joinPoint.getSignature().getName();  Object[] args = joinPoint.getArgs();  System.out.println("[后置通知][CalculatorImpl.MethodName=" + MethodName + "()");  System.out.println("Args[]=" + args);  }  //返回通知  @AfterReturning(value = "execution(* com.example.annoAOP.CalculatorImpl.* (..))", returning = "result")  public void afterReturnMethod(JoinPoint joinPoint, Object result) {  String MethodName = joinPoint.getSignature().getName();  System.out.println("[返回通知][CalculatorImpl.MethodName=" + MethodName + "()");  System.out.println("[返回通知]result=" + result);  }  //异常通知  @AfterThrowing(value = "execution(* com.example.annoAOP.CalculatorImpl.* (..))", throwing = "exp")  public void afterThrowing(JoinPoint joinPoint, Throwable exp) {  String MethodName = joinPoint.getSignature().getName();  System.out.println("[异常通知][CalculatorImpl.MethodName=" + MethodName + "()");  System.out.println(exp);  }  //环绕通知  @Around("execution(* com.example.annoAOP.CalculatorImpl.* (..))")  //ProceedingJoinPoint继承JoinPoint,比JoinPoint功能更强大,可以更好的调用目标方法  public Object around(ProceedingJoinPoint joinPoint) {  Object result = null;  try {  System.out.println("环绕通知-目标方法执行前");  result = joinPoint.proceed();  System.out.println("环绕通知-目标方法执行后");  } catch (Throwable throwable) {  System.out.println("环绕通知-目标方法执行异常");  } finally {  System.out.println("环绕通知-目标方法执行完成");  }  return result;  }  
}

输出结果

环绕通知-目标方法执行前
[前置通知][CalculatorImpl.MethodName=add()
Args[]=[Ljava.lang.Object;@62727399
result=2
[返回通知][CalculatorImpl.MethodName=add()
[返回通知]result=2
[后置通知][CalculatorImpl.MethodName=add()
Args[]=[Ljava.lang.Object;@62727399
环绕通知-目标方法执行后
环绕通知-目标方法执行完成

编写测试方法,使测试方法引发异常

@Test  
public void testAOPexp(){  ApplicationContext applicationContext=new ClassPathXmlApplicationContext("bean.xml");  Calculator calculator=applicationContext.getBean(Calculator.class);  calculator.div(1,0);  
}

运行结果

环绕通知-目标方法执行前
[前置通知][CalculatorImpl.MethodName=div()
Args[]=[Ljava.lang.Object;@4d9ac0b4
[异常通知][CalculatorImpl.MethodName=div()
java.lang.ArithmeticException: / by zero
[后置通知][CalculatorImpl.MethodName=div()
Args[]=[Ljava.lang.Object;@4d9ac0b4
环绕通知-目标方法执行异常
环绕通知-目标方法执行完成[之后是异常报错信息]

重用切入点

  1. 定义一个切入点

    	package com.example.annoAOP;
    @Pointcut(value = "execution(* com.example.annoAOP.CalculatorImpl.* (..))")  
    public void pointCut() {}
    
  2. 使用切入点

    1. 内部使用切入点

      @After(value = "pointCut")  
      public void afterMethod(JoinPoint joinPoint) {  String MethodName = joinPoint.getSignature().getName();  Object[] args = joinPoint.getArgs();  System.out.println("[后置通知][CalculatorImpl.MethodName=" + MethodName + "()");  System.out.println("Args[]=" + args);  
      }  
      
    2. 外部使用切入点

      @After(value = "com.example.annoAOP.pointCut")  
      public void afterMethod(JoinPoint joinPoint) {  String MethodName = joinPoint.getSignature().getName();  Object[] args = joinPoint.getArgs();  System.out.println("[后置通知][CalculatorImpl.MethodName=" + MethodName + "()");  System.out.println("Args[]=" + args);  
      }  
      

切面的优先级

相同目标方法上同时存在多个切面时,切面的优先级控制切面的内外嵌套顺序。

  • 优先级高的切面:外面
  • 优先级低的切面:里面
    使用@Order注解可以控制切面的优先级:
  • @Order(较小的数):优先级高
  • @Order(较大的数):优先级低

XML形式配置AOP

  1. 创建新包xmlaop,复制上文接口、实现类、AOP配置类

  2. 删除LogAspect类的@Aspect注解和AOP注解

  3. 新建XmlAop.xml配置文件

    <?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">  <!-- 开启组件扫描           -->  <context:component-scan base-package="com.example.xmlAOP"/>  <!--配置AOP-->  <aop:config>  <!-- 配置切面类       -->  <aop:aspect ref="logAspect">  <!-- 配置切入点       -->  <aop:pointcut id="cutpoint" expression="execution(* com.example.xmlAOP.CalculatorImpl.* (..))"/>  <!-- 配置方法执行前通知       -->  <aop:before method="beforeMethod" pointcut-ref="cutpoint"/>  <!-- 配置方法执行后通知       -->  <aop:after method="afterMethod" pointcut-ref="cutpoint"/>  <!-- 配置方法返回后通知       -->  <aop:after-returning method="afterReturnMethod" pointcut-ref="cutpoint" returning="result"/>  <!-- 配置环绕通知       -->  <aop:around method="around" pointcut-ref="cutpoint"/>  <!-- 配置异常通知       -->  <aop:after-throwing method="afterThrowing" pointcut-ref="cutpoint" throwing="exp"/>  </aop:aspect>  </aop:config>  </beans>
    
  4. 编写测试方法

    @Test  
    public void testXML_AOP(){ApplicationContext applicationContext=new ClassPathXmlApplicationContext("XmlAop.xml");//本项目存在两个Calculator,需要注意使用的是哪个Calculator类com.example.xmlAOP.Calculator calculator=applicationContext.getBean(com.example.xmlAOP.Calculator.class);calculator.add(1,1);
    }
    
  5. 输出结果

[前置通知][CalculatorImpl.MethodName=add()
Args[]=[Ljava.lang.Object;@eda25e5
环绕通知-目标方法执行前
result=2
环绕通知-目标方法执行后
环绕通知-目标方法执行完成
[返回通知][CalculatorImpl.MethodName=add()
[返回通知]result=2
[后置通知][CalculatorImpl.MethodName=add()
Args[]=[Ljava.lang.Object;@eda25e5

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

glm-4 联网搜索 api 测试

今天测试了一下 glm-4 的联网搜索 web_search tool 调用&#xff0c;发现了 web_search 的网页检索返回结果中几个比较诡异的事情&#xff0c;特此记录&#xff1a; 有些检索结果没有 icon、link、media 字段&#xff0c;但从内容上看确实是联网搜索出来的结果&#xff0c;不知…

从零开始读RocketMq源码(三)Broker存储Message流程解析

目录 前言 准备 消息载体CommitLog 文件持久化位置 源码解析 broker消息对象MessageExtBrokerInner 异步存储message CommitLog的真相 创建MappedFile文件 加入异步刷盘队列 Message异步存储MappedByteBuffer 总结 前言 在面试中我们经常会听到这样的回答&#x…

国产化趋势下源代码数据防泄密的信创沙盒的方案分享

随着国产化的大力推进&#xff0c;越来越多的企事业单位在逐步替换Windows、Linux等操作系统的使用。那么什是国产化了&#xff1f;国产化是指在产品或服务中采用国内自主研发的技术和标注&#xff0c;替代过去依赖的他国的产品和服务&#xff0c;国产化又被称之为“信创”&…

GitLab CI/CD实现项目自动化部署

1 GitLab CI/CD介绍 GitLab CI/CD 是 GitLab 中集成的一套用于软件开发的持续集成&#xff08;Continuous Integration&#xff09;、持续交付&#xff08;Continuous Delivery&#xff09;和持续部署&#xff08;Continuous Deployment&#xff09;工具。这套系统允许开发团队…

vue里实现点击按钮回到页面顶部功能,博客必备!

效果 步骤 1-标签结构 动态绑定样式style&#xff0c;监听点击事件&#xff0c;后续控制opacity透明度。和滚动距离 <div class"toTop" :style"dynamicStyles" click"toTop"><!--<i class"fa fa-arrow-up"></i>…

Django ORM中的F 对象

F 对象非常强大&#xff0c;可以在查询和更新操作中进行复杂的字段间运算。 假设我们有一个包含商品信息的模型 Product&#xff1a; from django.db import modelsclass Product(models.Model):name models.CharField(max_length100)price models.DecimalField(max_digits…

MySQL向自增列插入0失败问题

问题 在一次上线时&#xff0c;发现通过脚本添加的状态表中&#xff0c;待提交的状态不正确&#xff0c;本来应该是0&#xff0c;线上是101。 原因 默认情况下&#xff0c;MySQL对应自增列&#xff0c;认为0和null等价&#xff08;因为mysql认为0不是最佳实践不推荐使用&…

超简单的通配证书签发工具,免费,无需安装任何插件到本地

常见的acme.sh 或者 lego等工具需要配置&#xff0c;安装不灵活&#xff0c;续签需要配置计划任务&#xff0c;签发单域名证书或者通配证书需要不同的指令和配置&#xff0c;繁琐&#xff0c;如果自己程序想要对接签发证书的api有的不支持&#xff0c;有的用起来繁琐。 最近发…

[手机Linux PostmarketOS]三, Alpine Linux命令使用

Alpine Linux 一些常用的指令&#xff1a; 添加国内源下载链接&#xff1a; 编译以下文件&#xff0c;添加链接进去&#xff1a; sudo vi /etc/apk/repositories##清华源&#xff1a; https://mirror.tuna.tsinghua.edu.cn/alpine/latest-stable/main https://mirror.tuna.tsi…

【VIVADO SDK调试遇到DataAbortHandler】

问题 SDK调试遇到DataAbortHandler问题。 运行后不显示结果&#xff0c;debug模式下发现进入DataAbortHandler异常函数。程序中存在大数组。 原因:SDK默认的堆栈为1024bytes,需要将堆栈调大。 修改方法&#xff1a; 解决:对application中src下的lscript.ld双击&#xff0c;…

android 添加一个水平线

在Android中&#xff0c;添加一个水平线通常可以通过几种方式实现&#xff0c;最常见的是使用View组件或者自定义的Drawable。下面是一个简单的例子&#xff0c;展示如何在布局文件中添加一个水平线&#xff1a; 使用View组件 在你的布局XML文件中&#xff0c;你可以添加一个…

Linux 程序卡死的特殊处理

一、前言 Linux环境。 我们在日常编写的程序中&#xff0c;可能会出现一些细节问题&#xff0c;导致程序卡死&#xff0c;即程序没法正常运行&#xff0c;界面卡住&#xff0c;也不会闪退... 当这种问题出现在客户现场&#xff0c;那就是大问题了。。。 当我们暂时还无法排…

Python如何调用C++

ctypes 有以下优点: Python内建&#xff0c;不需要单独安装Python可以直接调用C/C 动态链接库(.dll 或 .so)在Python一侧&#xff0c;不需要了解 c/c dll 内部的工作方式提供了 C/C 数据类型与Python类型的相互映射&#xff0c;以及转换&#xff0c;包括指针类型。 在使用cty…

如何定量选择孔销基准?-DTAS来帮你!

在当今快速发展的工程领域&#xff0c;公差仿真的作用日渐重要&#xff0c;在公差仿真中&#xff0c;基准体系的选择对于最终结果更是至关重要。基准体系不同可能导致仿真过程中的参数计算、误差分析以及最终的工程设计都有所不同。基准体系作为评估和比较的参照&#xff0c;直…

Suricata引擎二次开发之命中规则定位

二开背景 suricata是一款高性能的开源网络入侵检测防御引擎&#xff0c;旨在检测、预防和应对网络中的恶意活动和攻击。suricata引擎使用多线程技术&#xff0c;能够快速、准确地分析网络流量并识别潜在的安全威胁&#xff0c;是众多IDS和IPS厂商的底层规则检测模块。 前段时间…

强制升级最新系统,微软全面淘汰Win10和部分11用户

说出来可能不信&#xff0c;距离 Windows 11 正式发布已过去整整三年时间&#xff0c;按理说现在怎么也得人均 Win 11 水平了吧&#xff1f; 然而事实却是&#xff0c;三年时间过去 Win 11 占有率仅仅突破到 29%&#xff0c;也就跳起来摸 Win 10 屁股的程度。 2024 年 6 月 Wi…

【Linux】磁盘性能压测-FIO工具

一、FIO工具介绍 fio&#xff08;Flexible I/O Tester&#xff09;是一个用于评估计算机系统中 I/O 性能的强大工具。 官网&#xff1a;fio - fio - Flexible IO Tester 注意事项&#xff01; 1、不要指定文件系统名称&#xff08;如/dev/mapper/centos-root)&#xff0c;避…

react启用mobx @decorators装饰器语法

react如果没有经过配置&#xff0c;直接使用decorators装饰器语法会报错&#xff1a; Support for the experimental syntax ‘decorators’ isn’t currently enabled 因为react默认是不支持装饰器语法&#xff0c;需要做一些配置来启用装饰器语法。 step1: 在 tsconfig.js…

【学术会议征稿】第三届能源互联网及电力系统国际学术会议(ICEIPS 2024)

第三届能源互联网及电力系统国际学术会议&#xff08;ICEIPS 2024&#xff09; 2024 3rd International Conference on Energy Internet and Power Systems 能源互联网是实现新一代电力系统智能互动、开放共享的重要支撑技术之一&#xff0c;也是提升能源调度效率&#xff0…

SQL 存储过程

SQL&#xff08;Structured Query Language&#xff09;的存储过程&#xff08;Stored Procedure&#xff09;是一组为了完成特定功能的SQL语句集&#xff0c;它经编译后存储在数据库中&#xff0c;用户通过指定存储过程的名字并给它传递参数&#xff08;如果有的话&#xff09…