Spring学习笔记2

news2025/1/10 14:38:13

目录

  • 1. 代理
    • 1.1 **JDK 动态代理**
    • 1.2 **CGLIB 代理**
    • 1.3 JDK 动态代理与 CGLIB 的对比
  • 2. AOP思想
    • 2.1 基本概念
    • 2.2 AspectJ 技术
    • 2.3 **AspectJ 使用 XML 配置**
    • 2.4 **AspectJ 使用注解配置**
    • 2.5 通知
  • 3. JdbcTemplate
    • 3.1 **主要特点**
    • 3.2 **核心方法**
    • 3.3 **使用步骤**
    • 3.4 **优点**
  • 4. 配置平台事务管理器和事务管理模板
    • 4.1 配置平台事务管理器
    • 4.2 配置事务管理模板
      • 4.2.1 编程式事务管理
      • 4.2.2 声明式事务管理

1. 代理

1.1 JDK 动态代理

JDK 动态代理是 Java 标准库自带的一种代理机制,基于 接口 实现。它的核心是 java.lang.reflect.Proxy 类和 java.lang.reflect.InvocationHandler 接口。

特点

  • 基于接口:JDK 动态代理只能代理实现了接口的目标对象。
  • 性能较低:相较于 CGLIB,JDK 动态代理在运行时的性能稍低。
  • 灵活性高:可以在运行时动态生成代理类,适用于需要动态增强接口方法的场景。

实现步骤

  1. 定义接口:目标对象需要实现一个接口。
  2. 实现 InvocationHandler:用于处理代理类的方法调用。
  3. 生成代理对象:通过 Proxy.newProxyInstance() 方法生成代理对象。

示例代码

  1. 定义接口
public interface BankService {
    void transferMoney(Long fromAccountId, Long toAccountId, double amount);
}
  1. 实现目标类
public class BankServiceImpl implements BankService {
    @Override
    public void transferMoney(Long fromAccountId, Long toAccountId, double amount) {
        System.out.println("Transferring money from account " + fromAccountId + " to account " + toAccountId);
    }
}
  1. 实现 InvocationHandler
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;

public class TransactionHandler implements InvocationHandler {

    private Object target; // 目标对象

    public TransactionHandler(Object target) {
        this.target = target;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        // 事务控制逻辑
        System.out.println("Transaction started...");
        try {
            Object result = method.invoke(target, args); // 调用目标方法
            System.out.println("Transaction committed.");
            return result;
        } catch (Exception e) {
            System.out.println("Transaction rolled back.");
            throw e;
        }
    }
}
  1. 生成代理对象
import java.lang.reflect.Proxy;

public class Main {
    public static void main(String[] args) {
        // 目标对象
        BankService target = new BankServiceImpl();

        // 创建 InvocationHandler
        TransactionHandler handler = new TransactionHandler(target);

        // 生成代理对象
        BankService proxy = (BankService) Proxy.newProxyInstance(
            target.getClass().getClassLoader(), // 类加载器
            target.getClass().getInterfaces(), // 目标类实现的接口
            handler // InvocationHandler
        );

        // 调用代理对象的方法
        proxy.transferMoney(1L, 2L, 100.0);
    }
}

输出结果

Transaction started...
Transferring money from account 1 to account 2
Transaction committed.

1.2 CGLIB 代理

CGLIB(Code Generation Library)是一种基于 字节码生成 的代理技术,允许代理 没有实现接口的类。它的核心是 net.sf.cglib.proxy.Enhancer 类和 net.sf.cglib.proxy.MethodInterceptor 接口。

特点

  • 基于类:CGLIB 可以代理没有实现接口的类。
  • 性能较高:CGLIB 生成的代理类直接基于目标类的字节码,性能通常优于 JDK 动态代理。
  • 动态生成子类:CGLIB 通过生成目标类的子类来实现代理。
    实现步骤
  1. 定义目标类:目标类不需要实现接口。
  2. 实现 MethodInterceptor:用于处理代理类的方法调用。
  3. 生成代理对象:通过 Enhancer 类生成代理对象。

示例代码

  1. 定义目标类
public class BankService {
    public void transferMoney(Long fromAccountId, Long toAccountId, double amount) {
        System.out.println("Transferring money from account " + fromAccountId + " to account " + toAccountId);
    }
}
  1. 实现 MethodInterceptor
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;
import java.lang.reflect.Method;

public class TransactionInterceptor implements MethodInterceptor {

    @Override
    public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
        // 事务控制逻辑
        System.out.println("Transaction started...");
        try {
            Object result = proxy.invokeSuper(obj, args); // 调用目标方法
            System.out.println("Transaction committed.");
            return result;
        } catch (Exception e) {
            System.out.println("Transaction rolled back.");
            throw e;
        }
    }
}
  1. 生成代理对象
import net.sf.cglib.proxy.Enhancer;

public class Main {
    public static void main(String[] args) {
        // 创建 Enhancer 对象
        Enhancer enhancer = new Enhancer();

        // 设置目标类的父类
        enhancer.setSuperclass(BankService.class);

        // 设置 MethodInterceptor
        enhancer.setCallback(new TransactionInterceptor());

        // 生成代理对象
        BankService proxy = (BankService) enhancer.create();

        // 调用代理对象的方法
        proxy.transferMoney(1L, 2L, 100.0);
    }
}

输出结果

Transaction started...
Transferring money from account 1 to account 2
Transaction committed.

1.3 JDK 动态代理与 CGLIB 的对比

特性JDK 动态代理CGLIB 代理
基于接口
性能较低(基于反射)较高(基于字节码生成)
目标对象必须实现接口可以是任意类
代理对象代理接口生成目标类的子类
适用场景需要代理接口方法需要代理没有接口的类
依赖JDK 自带需要额外依赖 CGLIB 库

2. AOP思想

2.1 基本概念

AOP(Aspect-Oriented Programming) 是一种编程范式,旨在通过将横切关注点(Cross-Cutting Concerns)从核心业务逻辑中分离出来,提高代码的模块化程度。横切关注点是指那些在多个模块中都会用到的功能,例如日志记录、事务管理、安全性检查等。AOP 的核心思想是将这些横切关注点从核心业务逻辑中解耦,封装成独立的模块(切面),并通过配置或注解的方式,将它们动态地应用到需要增强的类或方法上。

AOP 的关键术语

  1. 切面(Aspect):模块化的横切关注点,例如日志记录、事务管理等。
  2. 连接点(Join Point):程序执行过程中的某个点,例如方法调用、异常抛出等。
  3. 通知(Advice):切面在特定的连接点上执行的动作,例如在方法调用前后执行的日志记录。
  4. 切点(Pointcut):定义了通知应该应用到哪些连接点的规则,例如所有以 transferMoney 开头的方法。
  5. 引入(Introduction):允许向现有的类添加新方法或属性。
  6. 织入(Weaving):将切面应用到目标对象的过程,可以在编译时、类加载时或运行时进行。

2.2 AspectJ 技术

AspectJ 是一个成熟的 AOP 框架,提供了丰富的语法和工具,支持在 Java 应用中实现 AOP。AspectJ 可以在编译时或运行时将切面织入到目标类中。

AspectJ 的主要特点

  1. 强大的定义切点的能力:AspectJ 提供了灵活的切点表达式,可以精确地指定通知应该应用到哪些方法或代码块。
  2. 多种通知类型:支持多种通知类型,包括前置通知(Before)、后置通知(After)、环绕通知(Around)等。
  3. 编译时织入:可以在编译时将切面织入到目标类中,提高运行时性能。
  4. 运行时织入:也可以在运行时通过代理或字节码操作将切面织入到目标类中。
  5. 丰富的注解和 XML 配置:支持通过注解或 XML 配置来定义切面和切点。

2.3 AspectJ 使用 XML 配置

添加 AspectJ 依赖
如果使用 Maven,在 pom.xml 中添加 AspectJ 的依赖。

<dependency>
    <groupId>org.aspectj</groupId>
    <artifactId>aspectjweaver</artifactId>
    <version>1.9.7</version>
</dependency>
<dependency>
    <groupId>org.aspectj</groupId>
    <artifactId>aspectjrt</artifactId>
    <version>1.9.7</version>
</dependency>

实例代码

1. 切面类

public class LoggingAspect {

    // 前置通知
    public void logBefore() {
        System.out.println("Logging before method execution");
    }
}

2. 目标类

package com.example.service;

public class BankService {

    public void transferMoney(Long fromAccountId, Long toAccountId, double amount) {
        System.out.println("Transferring money from account " + fromAccountId + " to account " + toAccountId);
    }
}

3. XML 配置文件

在 XML 配置文件中定义切面、切点和通知:

<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       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/aop
                           http://www.springframework.org/schema/aop/spring-aop.xsd">

    <!-- 定义目标 bean -->
    <bean id="bankService" class="com.example.service.BankService" />

    <!-- 定义切面 bean -->
    <bean id="loggingAspect" class="com.example.aspect.LoggingAspect" />

    <!-- 定义切面 -->
    <aop:config>
        <!-- 定义切点 -->
        <aop:pointcut id="serviceMethods" expression="execution(* com.example.service.*.*(..))"/>

        <!-- 定义切面和通知 -->
        <aop:aspect ref="loggingAspect">
            <aop:before pointcut-ref="serviceMethods" method="logBefore"/>
        </aop:aspect>
    </aop:config>
</beans>

4. 测试代码

import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class Main {
    public static void main(String[] args) {
        ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
        BankService bankService = context.getBean(BankService.class);
        bankService.transferMoney(1L, 2L, 100.0);
    }
}

输出结果

Logging before method execution
Transferring money from account 1 to account 2

2.4 AspectJ 使用注解配置

添加 AspectJ 依赖
如果使用 Maven,在 pom.xml 中添加 AspectJ 的依赖。

<dependency>
    <groupId>org.aspectj</groupId>
    <artifactId>aspectjweaver</artifactId>
    <version>1.9.7</version>
</dependency>
<dependency>
    <groupId>org.aspectj</groupId>
    <artifactId>aspectjrt</artifactId>
    <version>1.9.7</version>
</dependency>

实例代码

1. 切面类

import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;

@Aspect  // 标记这是一个切面
public class LoggingAspect {

    // 定义切点
    @Pointcut("execution(* com.example.service.*.*(..))")
    public void serviceMethods() {}

    // 定义前置通知
    @Before("serviceMethods()")
    public void logBefore() {
        System.out.println("Logging before method execution");
    }
}

2. 目标类

package com.example.service;

public class BankService {

    public void transferMoney(Long fromAccountId, Long toAccountId, double amount) {
        System.out.println("Transferring money from account " + fromAccountId + " to account " + toAccountId);
    }
}

3. Spring 配置文件

如果使用 Spring,需要在 Spring 的 XML 配置文件中启用注解支持:

<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       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/aop
                           http://www.springframework.org/schema/aop/spring-aop.xsd">

    <!-- 启用 AspectJ 注解支持 -->
    <aop:aspectj-autoproxy/>

    <!-- 定义切面 bean -->
    <bean id="loggingAspect" class="com.example.aspect.LoggingAspect" />

    <!-- 定义目标 bean -->
    <bean id="bankService" class="com.example.service.BankService" />
</beans>

4. 测试代码

import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class Main {
    public static void main(String[] args) {
        ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
        BankService bankService = context.getBean(BankService.class);
        bankService.transferMoney(1L, 2L, 100.0);
    }
}

输出结果

Logging before method execution
Transferring money from account 1 to account 2

2.5 通知

AspectJ 提供了多种通知(Advice)类型,每种通知类型在不同的连接点(Join Point)上执行不同的逻辑。以下是 AspectJ 中常见的通知类型及其作用和语法。

1. Before 通知(前置通知)

  • 作用:在目标方法执行之前执行。
  • 语法
@Aspect
public class BeforeAspect {

    @Pointcut("execution(* com.example.service.*.*(..))")  // 定义切点
    public void serviceMethods() {}

    @Before("serviceMethods()")  // 前置通知
    public void beforeAdvice() {
        System.out.println("Before advice executed");
    }
}

2. After 通知(后置通知)

  • 作用:在目标方法执行之后执行(无论方法是否抛出异常)。
  • 语法
@Aspect
public class AfterAspect {

    @Pointcut("execution(* com.example.service.*.*(..))")  // 定义切点
    public void serviceMethods() {}

    @After("serviceMethods()")  // 后置通知
    public void afterAdvice() {
        System.out.println("After advice executed");
    }
}

3. AfterReturning 通知(返回通知)

  • 作用:在目标方法成功返回且没有抛出异常时执行。
  • 语法
@Aspect
public class AfterReturningAspect {

    @Pointcut("execution(* com.example.service.*.*(..))")  // 定义切点
    public void serviceMethods() {}

    @AfterReturning(pointcut = "serviceMethods()", returning = "result")  // 返回通知
    public void afterReturningAdvice(Object result) {
        System.out.println("AfterReturning advice executed. Result: " + result);
    }
}
  • 参数说明
    • returning = "result":将方法返回值绑定到通知方法的参数 result

4. AfterThrowing 通知(异常通知)

  • 作用:在目标方法抛出异常时执行。
  • 语法
@Aspect
public class AfterThrowingAspect {

    @Pointcut("execution(* com.example.service.*.*(..))")  // 定义切点
    public void serviceMethods() {}

    @AfterThrowing(pointcut = "serviceMethods()", throwing = "ex")  // 异常通知
    public void afterThrowingAdvice(Exception ex) {
        System.out.println("AfterThrowing advice executed. Exception: " + ex.getMessage());
    }
}
  • 参数说明
    • throwing = "ex":将异常对象绑定到通知方法的参数 ex

5. Around 通知(环绕通知)

  • 作用:在目标方法执行前后执行,并且可以完全控制方法的执行流程。
  • 语法
@Aspect
public class AroundAspect {

    @Pointcut("execution(* com.example.service.*.*(..))")  // 定义切点
    public void serviceMethods() {}

    @Around("serviceMethods()")  // 环绕通知
    public Object aroundAdvice(ProceedingJoinPoint joinPoint) throws Throwable {
        System.out.println("Around advice: Before method execution");

        // 调用目标方法
        Object result = joinPoint.proceed();

        System.out.println("Around advice: After method execution");
        return result;
    }
}
  • 关键点
    • ProceedingJoinPoint 是关键对象,调用其 proceed() 方法以执行目标方法。
    • 可以修改方法的返回值,或完全阻止方法的执行。

3. JdbcTemplate

JdbcTemplate 是 Spring 框架提供的一个核心工具类,用于简化 JDBC 操作。它封装了 JDBC 的繁琐操作(如连接管理、异常处理、资源释放等),使开发者能够更专注于 SQL 语句和业务逻辑的实现。

引入依赖

<dependencies>
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-context</artifactId>
        <version>5.0.2.RELEASE</version>
    </dependency>
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-jdbc</artifactId>
        <version>5.0.2.RELEASE</version>
    </dependency>
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-tx</artifactId>
        <version>5.0.2.RELEASE</version>
    </dependency>
    <dependency>
        <groupId>mysql</groupId>
        <artifactId>mysql-connector-java</artifactId>
        <version>5.1.6</version>
    </dependency>
</dependencies>

3.1 主要特点

  1. 简化 JDBC 操作

    • 自动管理数据库连接、语句和结果集的创建与释放。
    • 减少样板代码,提高开发效率。
  2. 统一的异常处理

    • 将 JDBC 的 SQLException 转换为 Spring 的 DataAccessException,提供更清晰的异常层次结构。
  3. 支持多种操作

    • 支持查询、更新、批量操作、存储过程调用等。
  4. 与 Spring 集成

    • 与 Spring 的事务管理、数据源等无缝集成。

3.2 核心方法

1. 更新操作(增删改)

  • update():用于执行 INSERT、UPDATE、DELETE 等 SQL 语句。

    int update(String sql, Object... args);
    

    示例:

    String sql = "UPDATE users SET name = ? WHERE id = ?";
    int rows = jdbcTemplate.update(sql, "John", 1);
    

2. 查询操作

  • queryForObject():查询单行数据并映射为对象。

    <T> T queryForObject(String sql, RowMapper<T> rowMapper, Object... args);
    

    示例:

    String sql = "SELECT * FROM users WHERE id = ?";
    User user = jdbcTemplate.queryForObject(sql, new BeanPropertyRowMapper<>(User.class), 1);
    
  • query():查询多行数据并映射为对象列表。

    <T> List<T> query(String sql, RowMapper<T> rowMapper, Object... args);
    

    示例:

    String sql = "SELECT * FROM users";
    List<User> users = jdbcTemplate.query(sql, new BeanPropertyRowMapper<>(User.class));
    

3. 执行任意 SQL

  • execute():用于执行任意 SQL 语句(如 DDL 语句)。

    void execute(String sql);
    

    示例:

    jdbcTemplate.execute("CREATE TABLE users (id INT, name VARCHAR(100))");
    

3.3 使用步骤

  1. 配置数据源

    • 在 Spring 配置文件中配置数据源(如 DataSource)。

    • 示例:

      <bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
          <property name="driverClassName" value="com.mysql.cj.jdbc.Driver"/>
          <property name="url" value="jdbc:mysql://localhost:3306/test"/>
          <property name="username" value="root"/>
          <property name="password" value="password"/>
      </bean>
      
  2. 创建 JdbcTemplate

    • 将数据源注入到 JdbcTemplate 中。

    • 示例:

      <bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
          <property name="dataSource" ref="dataSource"/>
      </bean>
      
  3. 在代码中使用

    • 通过依赖注入获取 JdbcTemplate 实例并调用其方法。

    • 示例:

      @Autowired
      private JdbcTemplate jdbcTemplate;
      
      public void addUser(User user) {
          String sql = "INSERT INTO users (name, age) VALUES (?, ?)";
          jdbcTemplate.update(sql, user.getName(), user.getAge());
      }
      

3.4 优点

  1. 简化代码
    • 减少 JDBC 样板代码,提高开发效率。
  2. 异常处理
    • 提供统一的异常处理机制,避免繁琐的 try-catch 块。
  3. 与 Spring 集成
    • 与 Spring 的事务管理、数据源等无缝集成。
  4. 灵活性
    • 支持自定义 RowMapperResultSetExtractor,满足复杂需求。

4. 配置平台事务管理器和事务管理模板

在 Spring 框架中,事务管理是一个非常重要的功能,用于确保数据库操作的完整性。Spring 提供了多种事务管理方式,其中最常见的两种是声明式事务管理和编程式事务管理。

4.1 配置平台事务管理器

平台事务管理器是 Spring 事务管理的核心接口,它负责事务的开始、提交和回滚。最常见的实现是 DataSourceTransactionManager,用于管理 JDBC 数据源的事务。

  1. 配置数据源

首先,需要配置一个数据源。这里我们使用 DriverManagerDataSource 作为示例数据源:

<bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
    <property name="driverClassName" value="com.mysql.cj.jdbc.Driver"/>
    <property name="url" value="jdbc:mysql://localhost:3306/test"/>
    <property name="username" value="root"/>
    <property name="password" value="password"/>
</bean>
  1. 配置平台事务管理器

接下来,配置 DataSourceTransactionManager,将数据源注入到事务管理器中:

<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
    <property name="dataSource" ref="dataSource"/>
</bean>

4.2 配置事务管理模板

事务管理模板(TransactionTemplate)是一个辅助类,用于简化事务管理的编程式操作。它可以自动管理事务的开始、提交和回滚。

配置事务管理模板

在 Spring 配置文件中配置 TransactionTemplate,并将平台事务管理器注入到模板中:

<bean id="transactionTemplate" class="org.springframework.transaction.support.TransactionTemplate">
    <property name="transactionManager" ref="transactionManager"/>
</bean>

4.2.1 编程式事务管理

使用 TransactionTemplate 进行编程式事务管理,可以通过 lambda 表达式或 TransactionCallback 接口来实现。

示例代码

@Autowired
private TransactionTemplate transactionTemplate;

@Autowired
private JdbcTemplate jdbcTemplate;

public void addUser(User user) {
    transactionTemplate.execute(status -> {
        try {
            String sql = "INSERT INTO users (name, age) VALUES (?, ?)";
            jdbcTemplate.update(sql, user.getName(), user.getAge());
            // 模拟异常
            // int i = 1 / 0;
            return null;
        } catch (Exception e) {
            // 回滚事务
            status.setRollbackOnly();
            throw e;
        }
    });
}

4.2.2 声明式事务管理

声明式事务管理通过在 Spring 配置文件中使用 AOP 配置事务,实现更简洁的事务管理。

  1. 配置事务管理器

确保已经配置了 DataSourceTransactionManager

  1. 配置事务管理器的 AOP 支持

使用 tx:annotation-driven 开启基于注解的事务管理:

<tx:annotation-driven transaction-manager="transactionManager"/>
  1. 使用 @Transactional 注解

在需要事务管理的类或方法上使用 @Transactional 注解:

import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

@Service
public class UserService {

    @Autowired
    private JdbcTemplate jdbcTemplate;

    @Transactional
    public void addUser(User user) {
        String sql = "INSERT INTO users (name, age) VALUES (?, ?)";
        jdbcTemplate.update(sql, user.getName(), user.getAge());
        // 模拟异常
        // int i = 1 / 0;
    }
}

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

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

相关文章

FreeU: Free Lunch in Diffusion U-Net 笔记

FreeU: Free Lunch in Diffusion U-Net 摘要 作者研究了 U-Net 架构对去噪过程的关键贡献&#xff0c;并发现其主干部分主要在去噪方面发挥作用&#xff0c;而其跳跃连接主要是向解码器模块引入高频特征&#xff0c;这使得网络忽略了主干部分的语义信息。基于这一发现&#…

JAVA 使用apache poi实现EXCEL文件的输出;apache poi实现标题行的第一个字符为红色;EXCEL设置某几个字符为别的颜色

设置输出文件的列宽&#xff0c;防止文件过于丑陋 Sheet sheet workbook.createSheet(FileConstants.ERROR_FILE_SHEET_NAME); sheet.setColumnWidth(0, 40 * 256); sheet.setColumnWidth(1, 20 * 256); sheet.setColumnWidth(2, 20 * 256); sheet.setColumnWidth(3, 20 * 25…

【STM32】无源蜂鸣器播放音乐《千与千寻》,HAL库

目录 一、工程链接 二、简单介绍 主要特点&#xff1a; 应用&#xff1a; 驱动电路&#xff1a; 三、原理图 四、cubeMX配置 时钟配置 五、keil配置 六、驱动编写 演奏函数 主函数编写 七、效果展示 八、驱动附录 music.h music.c 一、工程链接 STM32无源蜂鸣…

在 Vue 3 集成 e签宝电子合同签署功能

实现 Vue 3 e签宝电子合同签署功能&#xff0c;需要使用 e签宝提供的实际 SDK 或 API。 e签宝通常提供针对不同平台&#xff08;如 Web、Android、iOS&#xff09;的 SDK&#xff0c;而 Web 端一般通过 WebView 或直接使用嵌入式 iframe 来加载合同签署页面。 下面举个 &…

04、Redis深入数据结构

一、简单动态字符串SDS 无论是Redis中的key还是value&#xff0c;其基础数据类型都是字符串。如&#xff0c;Hash型value的field与value的类型&#xff0c;List型&#xff0c;Set型&#xff0c;ZSet型value的元素的类型等都是字符串。redis没有使用传统C中的字符串而是自定义了…

如何用Python编程实现自动整理XML发票文件

传统手工整理发票耗时费力且易出错&#xff0c;而 XML 格式发票因其结构化、标准化的特点&#xff0c;为实现发票的自动化整理与保存提供了可能。本文将详细探讨用python来编程实现对 XML 格式的发票进行自动整理。 一、XML 格式发票的特点 结构化数据&#xff1a;XML 格式发票…

Linux——修改USB网卡设备节点名称

修改驱动&#xff1a; 测试&#xff1a; 参考资料&#xff1a; https://blog.csdn.net/ablexu2018/article/details/144868950

(STM32笔记)十二、DMA的基础知识与用法 第三部分

我用的是正点的STM32F103来进行学习&#xff0c;板子和教程是野火的指南者。 之后的这个系列笔记开头未标明的话&#xff0c;用的也是这个板子和教程。 DMA的基础知识与用法 三、DMA程序验证1、DMA 存储器到存储器模式实验&#xff08;1&#xff09;DMA结构体解释&#xff08;2…

论文笔记(六十一)Implicit Behavioral Cloning

Implicit Behavioral Cloning 文章概括摘要1 引言2 背景&#xff1a;隐式模型的训练与推理3 隐式模型与显式模型的有趣属性4 policy学习成果5 理论见解&#xff1a;隐式模型的通用逼近性6 相关工作7 结论 文章概括 引用&#xff1a; inproceedings{florence2022implicit,titl…

高斯函数Gaussian绘制matlab

高斯 约翰卡尔弗里德里希高斯&#xff0c;&#xff08;德语&#xff1a;Johann Carl Friedrich Gau&#xff0c;英语&#xff1a;Gauss&#xff0c;拉丁语&#xff1a;Carolus Fridericus Gauss&#xff09;1777年4月30日–1855年2月23日&#xff0c;德国著名数学家、物理学家…

vue的路由守卫逻辑处理不当导致部署在nginx上无法捕捉后端异步响应消息等问题

近期对前端的路由卫士有了更多的认识。 何为路由守卫&#xff1f;这可能是一种约定俗成的名称。就是VUE中的自定义函数&#xff0c;用来处理路由跳转。 import { createRouter, createWebHashHistory } from "vue-router";const router createRouter({history: cr…

如何在 Ubuntu 22.04 上使用 LEMP 安装 WordPress 教程

简介&#xff1a; 本教程旨在指导你如何在 Ubuntu 22.04 上使用 LEMP 栈安装 WordPress。 WordPress 是一个用 PHP 编写的开源内容管理系统。LEMP 栈是 Linux&#xff0c;NGINX&#xff0c;MySQL 和 PHP 的缩写。WordPress 非常用户友好&#xff0c;并提供了多种选项&#xff…

PySide6基于QSlider实现QDoubleSlider

我在写小工具的时候&#xff0c;需要一个支持小数的滑动条。 我QSpinBox都找到了QDoubleSpinBox&#xff0c;QSlider愣是没找到对应的东西。 网上有好多对QSlider封装实现QDoubleSlider的文章。 似乎Qt真的没有这个东西&#xff0c;需要我们自行实现。 于是我也封装了一个&…

升级 Spring Boot 3 配置讲解 —— 支持断点传输的文件上传和下载功能

学会这款 &#x1f525;全新设计的 Java 脚手架 &#xff0c;从此面试不再怕&#xff01; 在现代 Web 应用中&#xff0c;文件上传和下载是非常常见的需求。然而&#xff0c;当文件较大时&#xff0c;传统的上传下载方式可能会遇到网络不稳定或传输中断的问题。为了解决这些问题…

Backend - C# EF Core 执行迁移 Migrate

目录 一、创建Postgre数据库 二、安装包 &#xff08;一&#xff09;查看是否存在该安装包 &#xff08;二&#xff09;安装所需包 三、执行迁移命令 1. 作用 2. 操作位置 3. 执行&#xff08;针对visual studio&#xff09; 查看迁移功能的常用命令&#xff1a; 查看…

KG-CoT:基于知识图谱的大语言模型问答的思维链提示

一些符号定义 知识图谱实体数量&#xff1a; n n n 知识图谱中关系类型数量&#xff1a; m m m 三元组矩阵&#xff1a; M ∈ { 0 , 1 } n n m \textbf{M} \in \{0, 1\}^{n \times n \times m} M∈{0,1}nnm&#xff0c; M i j k 1 M_{ij}^k 1 Mijk​1则说明实体 i i i和实…

HTML+CSS+JS制作中国传统节日主题网站(内附源码,含5个页面)

一、作品介绍 HTMLCSSJS制作一个中国传统节日主题网站&#xff0c;包含首页、节日介绍页、民俗文化页、节日活动页、联系我们页等5个静态页面。其中每个页面都包含一个导航栏、一个主要区域和一个底部区域。 二、页面结构 1. 顶部横幅区 包含传统中国风格的网站标题中国传统…

大模型WebUI:Gradio全解11——Chatbot:融合大模型的多模态聊天机器人(1)

大模型WebUI&#xff1a;Gradio全解11——Chatbots&#xff1a;融合大模型的聊天机器人&#xff08;1&#xff09; 前言本篇摘要11. Chatbot&#xff1a;融合大模型的多模态聊天机器人11.1 gr.ChatInterface()快速创建Chatbot11.1.1 定义聊天函数1. 随机回答“是”或“否”的聊…

springboot + vue+elementUI图片上传流程

1.实现背景 前端上传一张图片&#xff0c;存到后端数据库&#xff0c;并将图片回显到页面上。上传组件使用现成的elementUI的el-upload。、 2.前端页面 <el-uploadclass"upload-demo"action"http://xxxx.xxx.xxx:9090/file/upload" :show-file-list&q…

开源生成式物理引擎Genesis,可模拟世界万物

这是生成大模型时代 —— 它们能生成文本、图像、音频、视频、3D 对象…… 而如果将所有这些组合到一起&#xff0c;我们可能会得到一个世界&#xff01; 现在&#xff0c;不管是 LeCun 正在探索的世界模型&#xff0c;还是李飞飞想要攻克的空间智能&#xff0c;又或是其他研究…