2024年9月26日--- Spring-AOP

news2024/9/27 9:46:51

SpringAOP

在学习编程过程中,我们对于公共方法的处理应该是这样的一个过程,初期阶段如下

f1(){
   Date now = new Date();
   System.out.println("功能执行之前的各种前置工作"+now)
   //...功能代码
   //...功能代码
   System.out.println("功能执行之前的各种后置工作")
}
​
f2(){
   System.out.println("功能执行之前的各种前置工作")
   //...功能代码
   //...功能代码
   System.out.println("功能执行之前的各种后置工作")
}
.....

然后中期阶段,我们学会了封装

public class AspectConfig{
   static void before(){
      System.out.println("功能执行之前的各种前置工作")
   }
   static void after(){
      System.out.println("功能执行之前的各种后置工作")
   }
}   
AspectConfig.before()
f1()
AspectConfig.after()
   
AspectConfig.before()---切面--  块  -份--模块
f2()
AspectConfig.after()

现在,我们可以采用另外一种更简单的方式,AOP思想,对代码进行无侵入式实现,这样更高级了。

public class AspectConfig{
   @express(*f*)
   static void before(){
      System.out.println("功能执行之前的各种前置工作")
   }
   //@expres(*f*)
   static void after(){
      System.out.println("功能执行之前的各种后置工作")
   }
}   
m(){
    f1()
}
n(){
   f2()
}

1.1 AOP思想概述

1.1.1 思想简介

AOP(Aspect Oriented Programming)意为:面向切面编程,它是一种思想,是对某⼀类事情的集中处理。

它的核心思想是 将方法在执行过程中切分为多个部分,也就是多个横切关注点,这样可以与业务逻辑代码分离开来,使代码模块化,进而使得业务逻辑各部分之间的耦合度降低,提高程序的可重用性,同时提高了开发的效率。

AOP是OOP(面向对象编程)的一个补充,它允许开发者以声明方式实现关注点,而不是通过在业务逻辑代码中散布大量重复代码。

1.1.2 AOP的实现方式

AOP 可以通过多种方式实现,包括:

  • 编译时增强:在编译期间通过修改字节码来实现AOP。

  • 类加载时增强:在类加载到JVM时通过字节码操作实现AOP。

  • 动态代理:在程序运行时,通过代理对象来实现AOP。

1.1.3 AOP的应用场景

1)日志记录

2)声明式事务管理

3)登录安全验证

4)统一异常处理

1.1.3 AOP中的核心概念

1)横切关注点

在程序中,可以跨越多个模块的方法或功能。即是,与我们业务逻辑无关的,但是我们需要关注的部分,就是横切关注点。如日志 , 安全 , 缓存 , 事务等等 … 简单来说,就是程序员要关注的事情,抽象概念

2)切面(Aspect)

横切关注点被模块化的一个体现,它是一个类。这个类由通知(Advice)和切点(Pointcut)组成,既包含了横切逻辑的定义,也包含了连接点的定义。

3)通知(Advice)

通知,实际上是一个拦截器,它定义了切面是做什么以及何时使用,即在某个特定的连接点上执行的动作,它是切面的具体实现

以目标方法为参照点,根据放置位置的地方不同,通知分为如下5种类型通知:

  • 前置通知(Before):在方法执行前执行。

  • 后置通知(After):在方法执行后执行。

  • 返回通知(After Returning):在方法成功返回后执行。

  • 异常通知(After Throwing):在方法抛出异常后执行。

  • 环绕通知(Around):包围方法执行的前后。

4)切入点(PointCut)

Advice 执行的 “地点”的定义。 简单理解:就是用来定义位置

5)连接点(JointPoint)

与切入点匹配的执行点。这个点可以是方法调用时,抛出异常时,甚至修改字段时。切面代码可以利用这些点插入到应用的正常流程之中,并添加新的行为。 简单理解:就是具体切入的位置

6)目标(Target)

就是被切面作用的对象,包含连接点的对象

1.2 Spring AOP简介

AOP是一个思想,是一个编程范式。SpringAOP是对这个思想的实现。

Spring AOP 是构建在动态代理基础上的。因此 Spring 对 AOP 的⽀持局限于⽅法级别的拦截。 Spring AOP ⽀持 JDK ProxyCGLIB ⽅式实现动态代理。默认情况下,实现了接⼝的类,使⽤ AOP 会基于 JDK ⽣成代理类,没有实现接⼝的类,会基于 CGLIB ⽣成代理类。

织⼊(Weaving):代理的⽣成时机

织入就是什么时候把代理的代码放进运行的代码中。在⽬标对象的⽣命周期⾥有多个点可以进⾏织⼊:

编译期(编译阶段):

切⾯在⽬标类编译时被织⼊。这种⽅式需要特殊的编译器。AspectJ的织⼊编译器就 是以这种⽅式织⼊切⾯的。

类加载期:

切⾯在⽬标类加载到JVM时被织⼊。这种⽅式需要特殊的类加载器 (ClassLoader),它可以在⽬标类被引⼊应⽤之前增强该⽬标类的字节码。AspectJ5的加载 时织⼊(load-time weaving. LTW)就⽀持以这种⽅式织⼊切⾯。

运⾏期:

切⾯在应⽤运⾏的某⼀时刻被织⼊。⼀般情况下,在织⼊切⾯时,AOP容器会为⽬标对象动态创建⼀个代理对象。SpringAOP就是以这种⽅式织⼊切⾯的。

1.3 XML配置方式

1.3.1 导入依赖

<dependency>
   <groupId>org.aspectj</groupId>
   <artifactId>aspectjweaver</artifactId>
   <version>1.9.4</version>
</dependency>

1.3.2 案例1

UserService.java

public interface UserService {
     void add();
     void delete();
     void update();
     void search();
}

UserServiceImpl.java

public class UserServiceImpl implements UserService{
    @Override
    public void add() {
        System.out.println("---增加用户---");
    }
​
    @Override
    public void delete() {
        System.out.println("---删除用户---");
    }
​
    @Override
    public void update() {
        System.out.println("---修改用户---");
    }
​
    @Override
    public void search() {
        System.out.println("---查询用户---");
    }
}
​

编写两个通知类型,一个前置通知,一个后置通知

import org.springframework.aop.MethodBeforeAdvice;
import java.lang.reflect.Method;
public class Log implements MethodBeforeAdvice {
    /**
     *
     * @param method    目标对象的方法
     * @param objects   被调用的方法的参数
     * @param o             目标对象
     * @throws Throwable
     */
    @Override
    public void before(Method method, Object[] objects, Object o) throws Throwable {
        System.out.println(o.getClass().getName()+"的"+method.getName()+"正在执行····");
    }
}
import org.springframework.aop.AfterReturningAdvice;
import java.lang.reflect.Method;
​
public class AfterLog implements AfterReturningAdvice {
    /**
     *
     * @param o             方法的返回值
     * @param method        目标对象的方法
     * @param objects       目标对象的方法的参数
     * @param o1            目标对象
     * @throws Throwable
     */
    @Override
    public void afterReturning(Object o, Method method, Object[] objects, Object o1) throws Throwable {
        System.out.println("执行了"+o1.getClass().getName()+"的"+method.getName()+"方法," +
                "返回值:"+o);
    }
}

注册bean,实现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">
​
    <!--注册-->
    <bean id="userService" class="com.shuilidianli.service.UserServiceImpl"></bean>
    <bean id="log" class="com.shuilidianli.aop.Log"></bean>
    <bean id="afterLog" class="com.shuilidianli.aop.AfterLog"></bean>
​
    <!--aop配置-->
    <aop:config>
        <!--切入点 expression: 表达式匹配要执行的方法-->
        <aop:pointcut id="pointcut" expression="execution(* com.shuilidianli.service.UserServiceImpl.*(..))"/>
        <!--执行环绕:advice-ref执行方法,pointcut-ref:切入点-->
        <aop:advisor advice-ref="log" pointcut-ref="pointcut"/>
        <aop:advisor advice-ref="afterLog" pointcut-ref="pointcut"/>
    </aop:config>
</beans>
​

测试

import com.shuilidianli.service.UserService;
import com.shuilidianli.web.EmpController;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
​
public class AopTest2 {
    @Test
    public void test1(){
        ApplicationContext ctx =
                new ClassPathXmlApplicationContext("beans.xml");
        UserService us =
                (UserService) ctx.getBean("userService");
        us.search();
​
    }
}

测试结果:

Aop的重要性 : 很重要 . 一定要理解其中的思路 , 主要是思想的理解这一块 .

Spring的Aop就是将公共的业务 (日志 , 安全等) 和领域业务结合起来 , 当执行领域业务时 , 将会把公共业务加进来 . 实现公共业务的重复利用 . 领域业务更纯粹 , 程序员专注领域业务 , 其本质还是动态代理

1.3.2 案例2

自定义类来实现Aop

目标业务类不变依旧是userServiceImpl

1)自己定义一个切入类

package com.shuilidianli.aop;
​
public class DiyPointcut {
   public void before(){
      System.out.println("---------方法执行前---------");
   }
   public void after(){
      System.out.println("---------方法执行后---------");
   }
}

2)注册配置

<!--第二种方式自定义实现-->
<!--注册bean-->
​
<bean id="diy" class="com.shuilidianli.aop.DiyPointcut"></bean>
<!--aop的配置-->
<aop:config>
   <!--第二种方式:使用AOP的标签实现-->
   <aop:aspect ref="diy">
      <aop:pointcut id="diyPointcut" expression="execution(* com.shuilidianli.service.UserServiceImpl.*(..))"/>
      <aop:before method="before" pointcut-ref="diyPointcut"/>
      <aop:after method="after" pointcut-ref="diyPointcut"/>
   </aop:aspect>
</aop:config>

3)测试

package com.shuilidianli.test;
​
import com.shuilidianli.service.UserService;
import com.shuilidianli.web.EmpController;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
​
public class AopTest2 {
    @Test
    public void test1(){
        ApplicationContext ctx =
                new ClassPathXmlApplicationContext("beans.xml");
        UserService us =
                (UserService) ctx.getBean("userService");
        us.search();
​
    }
}

测试结果:

1.4 注解方式

1.4.1 导入依赖,开启注解扫描

<dependency>
   <groupId>org.aspectj</groupId>
   <artifactId>aspectjweaver</artifactId>
   <version>1.9.4</version>
</dependency>
<context:component-scan base-package="com"/>
​
<!-- 开启aop组件注解扫描 -->
<aop:aspectj-autoproxy proxy-target-class="true"></aop:aspectj-autoproxy>

aop:aspectj-autoproxy:说明

通过aop命名空间的<aop:aspectj-autoproxy />声明自动为spring容器中那些配置@aspect切面的bean创建代理,织入切面。
​
当然,spring 在内部依旧采用AnnotationAwareAspectJAutoProxyCreator进行自动代理的创建工作,但具体实现的细节已经被<aop:aspectj-autoproxy />隐藏起来了
​
<aop:aspectj-autoproxy />有一个proxy-target-class属性,默认为false,表示使用jdk动态代理织入增强,
​
当配为<aop:aspectj-autoproxy  poxy-target-class="true"/>时,表示使用CGLib动态代理技术织入增强。
​
不过即使proxy-target-class设置为false,如果目标类没有声明接口,则spring将自动使用CGLib动态代理。
​

1.4.2 切入点表达式语法

1)语法如下:

"execution(修饰词 返回值类型 类全限定名.方法名(形参列表) 异常)"
   
public int f1(int a,int b)throw Exception{
   
}

2)Spring AOP的切入点表达式非常灵活,支持模糊配置。

eg1 : execution(* 全类名.*(..))
   第一个 "*" 表示支持任意修饰符及返回值类型;第二个 "*" 表示支持该类中的任意方法;形参列表中的".."则表示可以匹配任意数量和类型的参数。(PS : 若目标类、接口与当前切面类在同一个包下,可以省略包名,只写类名)
​
eg2 : execution(public * 全类名.*(..))
   表示支持该类中的所有公有的方法
​
eg3 : execution(public double 全类名.*(..))
   表示支持该类中所有公有的且返回值为double的方法
​
eg4 : execution(public double 全类名.*(double, ..))
   表示支持该类中所有形参列表第一个参数为double类型,且后续参数可以是任意数量任意类型的,公有的返回值为double的方法。
​
eg5 : execution(double 全类名.*(double, double)
     表示支持该类中所有形参列表为两个double类型,公有的且返回值为double类型的方法。

3) 在AspectJ(另一个框架)中,切入点表达式可以通过"&&","||","!"等操作符结合起来。

eg : execution(* *.add(int, ..)) || execution(* *.subtract(int, ..))——表示支持任意类中的任意访问修饰符和任意返回值类型的,且形参列表第一个参数为int类型的add 或 subtract方法。

4)注意事项:

(1) 当切入点表达式直接指向了接口某个实现类的方法(非实现类特有方法),这时切入点表达式仅会对该实现类生效(动态代理 + 反射),即接口的其他实现类不会生效(不会得到代理对象,即使你以接口类型作为接收)。
​
(2) 当切入点表达式指向接口的方法时,切入表达式会对该接口的所有实现类生效。
​
(3) 切入点表达式也可以切入到没有实现接口的类的横切关注点中。(CGlib动态代理模式)
​
PS : JDK Proxy动态代理和CGlib动态代理的区别
​
- JDK动态代理是面向接口的,只能增强实现类中重写了接口中的方法。而CGlib是面向父类的,可以增强父类的所有方法。
- JDK得到的对象是JDK代理对象实例,而CGlib得到的对象是被代理对象的子类。

1.4.3 案例3

AnnotationPointcut.java

package com.shuilidianli.aop;
​
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.springframework.stereotype.Component;
​
@Component
@Aspect
public class AnnotationPointcut {
   
    @Before("execution(* com.shuilidianli.service.UserServiceImpl.*(..))")
    public void before(){
        System.out.println("---------方法执行前---------");
    }
   
    @After("execution(* com.shuilidianli.service.UserServiceImpl.*(..))")
    public void after(){
        System.out.println("---------方法执行后---------");
    }
   
    @Around("execution(* com.shuilidianli.service.UserServiceImpl.*(..))")
    public void around(ProceedingJoinPoint joinPoint) throws Throwable {
        System.out.println("---环绕前---");
        System.out.println("调用的方法:"+joinPoint.getSignature());
        //执行目标方法
        Object proceed = joinPoint.proceed();
        System.out.println("---环绕后---");
        System.out.println(proceed);
    }
}
​

测试: 注意给UserServiceImpl添加注解

package com.shuilidianli.test;
​
import com.shuilidianli.service.UserService;
import com.shuilidianli.web.EmpController;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
​
public class AopTest2 {
    @Test
    public void test1(){
        ApplicationContext ctx =
                new ClassPathXmlApplicationContext("beans.xml");
        UserService us =
                (UserService) ctx.getBean("userServiceImpl");
        us.search();
​
    }
}
​

测试结果:

1.4.4 案例4

1)EmpController.java

package com.sldl.controller;
​
import org.springframework.stereotype.Controller;
​
@Controller   //添加Bean注解
public class EmpController {
​
    public void findAll(){
        System.out.println("---正在查询所有员工信息---");
    }
​
    public void addEmp(){
        System.out.println("---正在添加一个员工信息---");
        String str = null;
        System.out.println(str.length());
    }
}
​

2)Operation.java

package com.sldl.log;
​
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.AfterThrowing;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.springframework.stereotype.Component;
​
@Component   // 添加Bean注解
@Aspect      // 添加Aop注解
public class OperationLog {
    //后置通知注解
    @After("within(com.sldl.controller..*)")
    public void log(){
        System.out.println("记录日志");
    }
    //环绕通知注解
    @Around("within(com.sldl.controller..*)")
    public Object log1(ProceedingJoinPoint p) throws Throwable{
        //获取目标组件的名字
        String className = p.getTarget().getClass().getName();
        //获取目标组件里执行的方法名
        String methodName = p.getSignature().getName();
        System.out.println("------------");
        //执行目标组件
        Object obj = p.proceed();
        System.out.println(
                "xxx正在执行"+className
                        +"里的"+methodName+"方法");
        return obj;
    }
    //异常抛出通知
    @AfterThrowing(pointcut="within(com.sldl.controller..*)",throwing="e")
    public void log2(Exception e){
        System.out.println(e.toString());
        StackTraceElement[] eles =
                e.getStackTrace();
        System.out.println(eles[0]);
        System.out.println(eles[1]);
    }
}
​

3)beans.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.sldl"/>
​
    <!--开启AOP注解扫描-->
    <aop:aspectj-autoproxy proxy-target-class="true"/>
​
</beans>

4)AOPTest

package com.sldl.test;
​
import com.sldl.controller.EmpController;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
​
public class AOPTest {
    @Test
    public void test1(){
        ApplicationContext ctx =
            new ClassPathXmlApplicationContext("beans.xml");
​
        EmpController ec =
                ctx.getBean("empController", EmpController.class);
​
       //ec.findAll();
        ec.addEmp();
​
    }
}

测试结果:

1.4.5 多个通知的执行顺序

五个通知都有的情况下,先后执行顺序

  1. 一定限制性环绕前一步:

  2. 再执行前置通知

  3. 目标方法

  4. 环绕通知的后一部分

  5. 在执行后置通知

  6. 在执行返回通知

  7. 最后执行异常通知

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

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

相关文章

局域网广域网,IP地址和端口号,TCP/IP 4层协议,协议的封装和分用

前言 在古老的年代&#xff0c;如果我们要实现两台机器进行数据传输&#xff0c; A员工就得去B员工的办公电脑传数据&#xff08;B休息&#xff0c;等A传完&#xff09;&#xff0c;这样就很浪费时间 所以能不能不去B的工位的同时&#xff0c;还能传数据。这时候网络通信就出来…

springboot异常(三):异常处理原理

&#x1f345;一、BasicErrorController ☘️1.1 描述 BasicErrorController是Springboot中默认的异常处理方法&#xff0c;无需额外的操作&#xff0c;当程序发生了异常之后&#xff0c;Springboot自动捕获异常&#xff0c;重新请求到BasicErrorController中&#xff0c;在B…

JS设计模式之桥接模式:搭建跨越维度的通路

引言 在软件开发中&#xff0c;我们经常遇到需要对不同的抽象类进行不同的实现的情况&#xff0c;而传统的对象嵌套并不是一个优雅且可扩展的解决方案&#xff0c;因此这正是桥接模式的用武之地。桥接模式通过将抽象与实现分离&#xff0c;使得它们可以独立变化&#xff0c;从…

前缀和(5)_和为k的子数组

个人主页&#xff1a;C忠实粉丝 欢迎 点赞&#x1f44d; 收藏✨ 留言✉ 加关注&#x1f493;本文由 C忠实粉丝 原创 前缀和(5)_和为k的子数组 收录于专栏【经典算法练习】 本专栏旨在分享学习算法的一点学习笔记&#xff0c;欢迎大家在评论区交流讨论&#x1f48c; 目录 1. 题目…

列表控件QListWidget

显示模式 有两种显示模式&#xff0c;列表模式和图标模式 // 获取和设置显示模式 QListView::ViewMode viewMode() const void setViewMode(QListView::ViewMode mode) QListView::ViewMode有两个取值 QListView::ListMode 列表模式 QListView::IconMode 图标模式 交替背…

《银河战星:僵局》风灵月影修改器使用指南,轻松驾驭宇宙战场

在策略射击游戏《银河战星&#xff1a;僵局》中&#xff0c;合理利用风灵月影修改器能极大提升你的游戏体验。 以下是简明操作步骤&#xff0c;助你迅速上手&#xff0c;遨游星际&#xff1a; 1.下载安装&#xff1a; 首先&#xff0c;确保从正规渠道获取风灵月影修改器&…

冒泡排序-C语言

1.问题&#xff1a; 从小到大对10个数进行排序&#xff0c;要求使用冒泡排序实现。 2.解答&#xff1a; 排序规律有两种&#xff1a;一种是“升序”&#xff0c;从小到大&#xff1b;另一种是“降序”&#xff0c;从大到小。 3.代码&#xff1a; #include<stdio.h>//头…

保护您的网络:入侵检测系统 IDS 终极指南

IDS 和 IPS&#xff1a;了解异同-CSDN博客 IDS(入​​侵检测系统) 和 IPS(入​​侵防御系统) 之间的区别-CSDN博客 即便是今天&#xff0c;互联网仍与黑客肆意横行的网络无异。 但不必害怕&#xff01;我们有解决您问题的完美解决方案&#xff1a;入侵检测系统 I DS。 IDS就…

本地打开打包后的dist文件报错的解决方法

出现问题 本地直接打开dist文件夹会报错&#xff0c;报错信息类似于&#xff1a; Access to script at file:///D:/assets/index.9cb0ffcc.js from origin null has been blocked by CORS policy: Cross origin requests are only supported for protocol schemes: chrome, ch…

TypeScript基础语法与面对对象

TypeScript 基础语法 TypeScript 与面向对象 面向对象是一种对现实世界理解和抽象的方法。TypeScript 是一种面向对象的编程语言。面向对象主要有两个概念&#xff1a;对象和类 对象&#xff1a;对象是类的一个实例&#xff08;对象不是找个女朋友&#xff09;&#xff0c;有…

【优选算法】(第四篇)

目录 三数之和&#xff08;medium&#xff09; 题目解析 讲解算法原理 编写代码 四数之和&#xff08;medium&#xff09; 题目解析 讲解算法原理 编写代码 三数之和&#xff08;medium&#xff09; 题目解析 1.题目链接&#xff1a;. - 力扣&#xff08;LeetCode&…

接口返回动态列名及动态列数据结构

接口返回动态列名及动态列数据结构代码方案 在开发过程中&#xff0c;有时我们需要处理动态表头和动态列数据的问题。特别是在一些数据统计或报表类需求中&#xff0c;列名和数据都可能随着时间或条件发生变化。本文将结合实际代码和表格展示&#xff0c;详细说明如何在接口中…

微信阅读网站小程序+ssm论文ppt源码调试讲解

第2章 开发环境与技术 微信阅读网站小程序的编码实现需要搭建一定的环境和使用相应的技术&#xff0c;接下来的内容就是对微信阅读网站小程序用到的技术和工具进行介绍。 2.1 MYSQL数据库 本课题所开发的应用程序在数据操作方面是不可预知的&#xff0c;是经常变动的&#xf…

Linux中的tr命令详解

1&#xff0c; 将小写字母转换为大写字母&#xff1a; echo "hello" | tr a-z A-Z但是限定字母的范围的话&#xff0c;是什么输出你们知道吗&#xff1f; echo "hello" | tr a-l A-L2&#xff0c;删除特定字符&#xff1a; 比如&#xff0c;删除所有的数…

【吊打面试官系列-MySQL面试题】MySQL锁的优化策略?

大家好&#xff0c;我是锋哥。今天分享关于【MySQL锁的优化策略?】面试题&#xff0c;希望对大家有帮助&#xff1b; MySQL锁的优化策略? 1、读写分离 2、分段加锁 3、减少锁持有的时间 4.多个线程尽量以相同的顺序去获取资源不能将锁的粒度过于细化&#xff0c;不然可能会…

算法工程师重生之第十六天(二叉搜索树的最小绝对差 二叉搜索树中的众数 二叉树的最近公共祖先 )

参考文献 代码随想录 一、二叉搜索树的最小绝对差 给你一个二叉搜索树的根节点 root &#xff0c;返回 树中任意两不同节点值之间的最小差值 。 差值是一个正数&#xff0c;其数值等于两值之差的绝对值。 示例 1&#xff1a; 输入&#xff1a;root [4,2,6,1,3] 输出&#x…

语义元分割学习:一种用于少量样本无线图像分类的TinyML方案

论文标题&#xff1a;Semantic Meta-Split Learning: A TinyML Scheme for Few-Shot Wireless Image Classification 中文标题&#xff1a;语义元分割学习&#xff1a;一种用于少量样本无线图像分类的TinyML方案 作者信息&#xff1a; Eslam Eldeeb, Mohammad Shehab, Hirley…

nvm,一款nodejs版本管理工具

背景 在工作中&#xff0c;我们可能同时在进行2个或者多个不同的项目开发&#xff0c;每个项目的需求不同&#xff0c;进而不同项目必须依赖不同版本的NodeJS运行环境&#xff0c;这种情况下&#xff0c;对于维护多个版本的node将会是一件非常麻烦的事情&#xff0c;nvm就是为…

Dapper 如何确保数据的安全性和防止 SQL 注入攻击?

一、什么是SQL注入攻击 SQL注入攻击是一种常见的网络攻击手段&#xff0c;它利用了应用程序中安全措施不足的问题&#xff0c;允许攻击者插入或“注入”一个或多个SQL语句到原本的查询中。这种攻击可以用于获取、篡改或删除数据库中的数据&#xff0c;甚至可以执行一些数据库管…

java:brew安装rabbitmq以及简单示例

什么是消息队列mq 可以看我之前写的这篇 消息队列MQ rabbitmq简介 RabbitMQ是由erlang语言开发&#xff0c;基于AMQP&#xff08;Advanced Message Queue 高级消息队列协议&#xff09;协议实现的消息队列&#xff0c;它是一种应用程序之间的通信方法&#xff0c;消息队列在…