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

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.mfbz.cn/a/883576.html

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

相关文章

Fyne ( go跨平台GUI )中文文档-入门(一)

本文档注意参考官网(developer.fyne.io/) 编写, 只保留基本用法go代码展示为Go 1.16 及更高版本, ide为goland2021.2 这是一个系列文章&#xff1a; Fyne ( go跨平台GUI )中文文档-入门(一)-CSDN博客 Fyne ( go跨平台GUI )中文文档-Fyne总览(二)-CSDN博客 Fyne ( go跨平台GUI )…

SpringSecurity-用户认证

1、用户认证 1.1 用户认证核心组件 我们系统中会有许多用户&#xff0c;确认当前是哪个用户正在使用我们系统就是登录认证的最终目的。这里我们就提取出了一个核心概念&#xff1a;当前登录用户/当前认证用户。整个系统安全都是围绕当前登录用户展开的&#xff0c;这个不难理…

【若依RuoYi-Vue | 项目实战】帝可得后台管理系统(二)

文章目录 一、人员管理1、需求说明2、生成基础代码&#xff08;1&#xff09;创建目录菜单&#xff08;2&#xff09;添加数据字典&#xff08;3&#xff09;配置代码生成信息&#xff08;4&#xff09;下载代码并导入项目 3、人员列表改造&#xff08;1&#xff09;基础页面&a…

Latex和Vscode安装和配置

一、Latex安装教程 打开清华大学开源软件镜像站&#xff0c;下载texlive.iso文件 右键点击ios文件&#xff0c;点击装载 配置latex安装 4. 安装过程 二、VScode安装和配置教程 打开Vscode官网&#xff0c;下载安装包 2.右键&#xff0c;以管理员身份运行VSCode安装包&#…

基于深度学习的药品三期OCR字符识别

在药品生产线上,药品三期的喷码与条形码识别是保证药品追溯和安全管理的重要环节。传统的识别方法依赖于人工操作,不仅效率低下且容易出错。随着深度学习技术的不断发展,基于OCR(Optical Character Recognition,光学字符识别)的自动化识别系统逐渐成为主流。本文将以哪吒…

微服务注册中⼼1

1. 微服务的注册中⼼ 注册中⼼可以说是微服务架构中的”通讯录“ &#xff0c;它记录了服务和服务地址的映射关系。在分布式架构中&#xff0c; 服务会注册到这⾥&#xff0c;当服务需要调⽤其它服务时&#xff0c;就这⾥找到服务的地址&#xff0c;进⾏调⽤。 1.1 注册中⼼的…

linux信号 | 学习信号三步走 | 全解析信号的产生方式

前言&#xff1a;本节内容是信号&#xff0c; 主要讲解的是信号的产生。信号的产生是我们学习信号的第二个阶段。 我们已经学习过第一个阶段——信号的概念与预备知识&#xff08;没有学过的友友可以查看我的前一篇文章&#xff09;。 以及我们还没有学习信号的第三个阶段——信…

CleanMyMac X 评价、介绍、使用教学|Mac系统最推荐的系统优化和清理软件工具!

本篇文章要带大家看一款知名的Mac系统优化、清理工具– CleanMyMac X &#xff0c;并且也会附上详细的介绍和使用教学。 链接: https://pan.baidu.com/s/1_TFnrIVH1NGsZPsA3lpwAA 提取码: dpjw CleanMyMac X-安装包&#xff1a;https://souurl.cn/QUYb57 为什么Mac电脑需要装系…

Cocos 3.8.3 实现外描边效果(逃课玩法)

本来想着用Cocos 的Shader Graph照搬Unity的思路来加外描边&#xff0c;发现不行&#xff0c;然后我就想弄两个物体不就行了吗&#xff0c;一个是放大的版本&#xff0c;再放大的版本上加一个材质&#xff0c;这个材质面剔除选择前面的面剔除就行了&#xff0c;果不其然还真行。…

前端开发必备:实用Tool封装工具类方法大全

程序员必备宝典网站https://tmxkj.top/#/ 1.判断空值 /*** 判断是否是空值*/ export function isEmpty(value) {if (typeof value string) {return value.length 0 || value "";} else if (typeof value number) {return value 0;} else if (Array.isArray(va…

Kafka 面试题

参考&#xff1a; https://javabetter.cn/interview/kafka-40.htmlhttps://javaguide.cn/high-performance/message-queue/kafka-questions-01.html Kafka 架构 名词概念 Producer&#xff08;生产者&#xff09; : 产生消息的一方。 Consumer&#xff08;消费者&#xff09; …

MySQL---创建数据库(基于SQLyog)

目录 0.前言 1.基本认识 1.1编码集 1.2检验规则 2.库的创建和销毁 2.1指令介绍 2.2你可能会出现的问题 3.查看数据库属性 4.创建指定数据库 5.创建表操作 0.前言 之前写过一篇这个关于表的创建和销毁的操作&#xff0c;但是当时是第一次学习&#xff0c;肯定有些地方…

failed to load steamui.dll的多种处理方法,steamui.dll的作用

在使用Steam平台时&#xff0c;不少玩家可能会遇到“failed to load steamui.dll”这样令人头疼的错误提示。这个错误会阻碍Steam客户端的正常运行&#xff0c;影响我们享受游戏和Steam平台的各种服务。不过&#xff0c;不必过于担心&#xff0c;因为有多种方法可以尝试解决这个…

【Linux】当前进展

驱动层日志添加了下文件目录&#xff0c;函数&#xff0c;代码行的打印&#xff08;这里要小心&#xff0c;驱动目录源代码打印日志里边添进程号可能有问题&#xff0c;因为在驱动初始化的时候&#xff0c;内核还没有创建进程&#xff0c;不过猜测可以先不打印进程相关信息&…

C++ | Leetcode C++题解之第419题棋盘上的战舰

题目&#xff1a; 题解&#xff1a; class Solution { public:int countBattleships(vector<vector<char>>& board) {int row board.size();int col board[0].size();int ans 0;for (int i 0; i < row; i) {for (int j 0; j < col; j) { if (board…

Linux下线程间的通信

为什么需要线程通信&#xff1f; 线程是操作系统调度的最小单元&#xff0c;拥有自己的栈空间。如果线程之间孤立运行&#xff0c;可能会导致资源浪费。线程需要协调工作以完成共同的任务&#xff0c;这就需要线程间相互通信 在 Linux 系统中&#xff0c;线程间通信&#xff…

MySQL数据库进阶知识(四)《视图、存储过程、触发器》

学习目标&#xff1a; 掌握数据库视图基础知识 掌握数据库存储过程原理 掌握数据库触发器相关知识 学习内容&#xff1a; 一. 视图 介绍 视图&#xff08;View&#xff09;是一种虚拟存在的表。视图中的数据并不在数据库中实际存在&#xff0c;行和列数据来自定义视图的查询…

基于GIKT深度知识追踪模型的习题推荐系统源代码+数据库+使用说明,后端采用flask,前端采用vue

基于GIKT深度知识追踪模型的习题推荐系统 目录结构 Flask-BackEnd flask后端 app 后端主体文件 alg 深度学习模块 data 数据集data_process.py 数据预处理gikt.py GIKT模型pebg.py PEBG模型params.py 一些参数train.py 仅模型训练train_test.py 模型训练和测试-五折交叉验证t…

Docker 天池代码提交

参考零基础入门Docker-cuda练习场_学习赛_天池大赛-阿里云天池的赛制 (aliyun.com) ​ 在Docker零基础入门-CSDN博客中我已经安装了docker,现在开始创建自己的镜像仓库。 1. 开通阿里云容器镜像服务(镜像仓库) 进入容器镜像服务 (aliyun.com) 1.1. 创建个人实例 点击“…

try catch 应该在for循环里面还是外面?

今天咱们就来聊聊“try catch 应该在 for 循环里面还是外面”的问题&#xff0c;别小看这句话&#xff0c;背后可是有大智慧的。 首先&#xff0c;咱得明确一点&#xff0c;try catch 是为了处理异常的。它能让你的代码在遇到问题时不至于“崩溃”&#xff0c;更像是你职场生涯…