面向切面:单元测试、事务、资源操作

news2024/11/13 9:24:02

目录

  • 一、单元测试
  • 二、事务
    • 2.1、概述
      • 2.1.1、编程式事务
      • 2.1.2、声明式事务
    • 2.2、JdbcTemplate
    • 2.3、基于注解的声明式事务
      • 2.3.1、基本用例-实现注解式的声明事务
      • 2.3.2、事务属性:只读
      • 2.3.3、事务属性:超时
      • 2.3.4、事务属性:回滚策略
      • 2.3.5、事务属性:隔离级别
      • 2.3.6、事务属性:传播行为
      • 2.3.7、全注解配置事务(重点)
      • 2.3.8、底层原理
  • 三、资源操作:Resources
    • 3.1、Resources接口
      • 3.1.1常用方法
    • 3.2、Resources的实现类
      • 3.2.1、UrlResource访问网络资源
      • 3.2.2、ClassPathResource 访问类路径下资源
      • 3.2.3、FileSystemResource 访问文件系统资源
      • 3.2.4、ServletContextResource、InputStreamResource、ByteArrayResource
    • 3.3、ResourceLoader接口
      • 3.3.1、常用方法
      • 3.3.2、实现类
      • 3.3.3、基本用例
    • 3.4、ResourceLoaderAware接口
    • 3.5、动态获取Resource资源
      • 3.5.1、含义
      • 3.5.2、作用
      • 3.5.3、基本用例
    • 3.6、确定资源访问路径
      • 3.6.1、概述
      • 3.6.2、实现类指定访问路径
      • 3.6.3、前缀指定访问路径

一、单元测试

  • @SpringJUnitConfig注解是用于指定Spring的配置文件。在测试方法运行前,Spring会先根据该配置文件创建出需要的Bean,然后注入到测试类中。这样就可以在测试类中直接使用Spring的Bean进行测试了
  • 类似于:
    ApplicationContext context = new ClassPathXmlApplicationContext("xxx.xml");
    Xxxx xxx = context.getBean(Xxxx.class);
    

步骤一:引入依赖

<!--spring对junit的支持相关依赖-->
<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-test</artifactId>
    <version>6.0.2</version>
</dependency>

<!--junit5测试-->
<dependency>
    <groupId>org.junit.jupiter</groupId>
    <artifactId>junit-jupiter-api</artifactId>
    <version>5.9.0</version>
</dependency>

步骤二:配置文件

<?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"
       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">
    <context:component-scan base-package="com.atguigu.spring6.bean"/>
</beans>

配置类扫描,用于自动装配Bean

步骤三:创建类

@Component
public class User {

    public User() {
        System.out.println("run user");
    }
}

步骤四:演示

@SpringJUnitConfig(locations = "classpath:beans.xml")
public class SpringJUnit5Test {

    @Autowired
    private User user;

    @Test
    public void testUser(){
        System.out.println(user);
    }
}

二、事务

2.1、概述

事务(Transaction)是指一个或多个操作序列组成的逻辑工作单元,这些操作要么全部成功,要么全部失败回滚。

特性:

  1. 原子性(Atomicity):事务是一个不可分割的工作单元,要么全部成功,要么全部失败,不允许出现部分成功部分失败的情况。
  2. 一致性(Consistency):事务执行前后,数据库的状态应该保持一致,如果一个事务执行失败,那么数据库应该恢复到执行前的状态。
  3. 隔离性(Isolation):多个事务之间应该互相隔离,事务之间不能互相干扰,避免脏读、不可重复读、幻读等问题。
  4. 持久性(Durability):事务完成后,对数据库的修改应该持久化保存,即使系统故障或崩溃,数据也不应该丢失。

2.1.1、编程式事务

编程式事务是通过在代码中编写事务管理代码来实现的,需要手动控制事务的开始、提交和回滚,需要在每个需要事务管理的方法中编写相关代码,这样代码耦合度高,且事务管理代码重复出现,不便于维护

Connection conn = ...;
    
try {
    
    // 开启事务:关闭事务的自动提交
    conn.setAutoCommit(false);
    
    // 核心操作
    
    // 提交事务
    conn.commit();
    
}catch(Exception e){
    
    // 回滚事务
    conn.rollBack();
    
}finally{
    
    // 释放数据库连接
    conn.close();
    
}

2.1.2、声明式事务

声明式事务则是通过AOP的方式实现,将事务管理代码从业务逻辑中分离出来,通过在配置文件中声明事务管理,实现事务的自动管理,开发人员只需要在需要事务管理的方法上添加注解或者配置即可,大大简化了代码的编写和维护工作

public interface UserService {
    void updateUser(User user);
}

@Service
@Transactional
public class UserServiceImpl implements UserService {
    @Autowired
    private UserDao userDao;

    @Override
    public void updateUser(User user) {
        userDao.update(user);
    }
}

说明:

  • 在这个例子中,@Transactional 注解被用于服务实现类的类级别上。这意味着,当调用 updateUser 方法时,Spring 将会创建一个事务,方法执行结束时,事务将被提交或回滚(如果出现异常)
  • 这样,在调用 updateUser 方法时,我们无需显式地开启和提交事务,Spring 框架将会自动处理事务的提交和回滚

2.2、JdbcTemplate

步骤一:加入依赖

<dependencies>
    <!--spring jdbc  Spring 持久化层支持jar包-->
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-jdbc</artifactId>
        <version>6.0.2</version>
    </dependency>
    <!-- MySQL驱动 -->
    <dependency>
        <groupId>mysql</groupId>
        <artifactId>mysql-connector-java</artifactId>
        <version>8.0.30</version>
    </dependency>
    <!-- 数据源 -->
    <dependency>
        <groupId>com.alibaba</groupId>
        <artifactId>druid</artifactId>
        <version>1.2.15</version>
    </dependency>
</dependencies>

步骤二:创建jdbc.properties

jdbc.user=root
jdbc.password=root
jdbc.url=jdbc:mysql://localhost:3306/spring?characterEncoding=utf8&useSSL=false
jdbc.driver=com.mysql.cj.jdbc.Driver

步骤三:配置Spring的配置文件
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"
       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">

    <!-- 导入外部属性文件 -->
    <context:property-placeholder location="classpath:jdbc.properties" />

    <!-- 配置数据源 -->
    <bean id="druidDataSource" class="com.alibaba.druid.pool.DruidDataSource">
        <property name="url" value="${jdbc.url}"/>
        <property name="driverClassName" value="${jdbc.driver}"/>
        <property name="username" value="${jdbc.user}"/>
        <property name="password" value="${jdbc.password}"/>
    </bean>

    <!-- 配置 JdbcTemplate -->
    <bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
        <!-- 装配数据源 -->
        <property name="dataSource" ref="druidDataSource"/>
    </bean>

</beans>

步骤四:准备数据库与测试表

CREATE DATABASE `spring`;

use `spring`;

CREATE TABLE `t_emp` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `name` varchar(20) DEFAULT NULL COMMENT '姓名',
  `age` int(11) DEFAULT NULL COMMENT '年龄',
  `sex` varchar(2) DEFAULT NULL COMMENT '性别',
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;

步骤五:创建测试类,整合JUnit,注入JdbcTemplate

package com.atguigu.spring6;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.test.context.junit.jupiter.SpringJUnitConfig;

@SpringJUnitConfig(locations = "classpath:beans.xml")
public class JDBCTemplateTest {

    @Autowired
    private JdbcTemplate jdbcTemplate;
    
}

步骤六:测试增删改功能

@Test
//测试增删改功能
public void testUpdate(){
    //添加功能
	String sql = "insert into t_emp values(null,?,?,?)";
	int result = jdbcTemplate.update(sql, "张三", 23, "男");
    
    //修改功能
	String sql = "update t_emp set name=? where id=?";
    int result = jdbcTemplate.update(sql, "张三atguigu", 1);

    //删除功能
	String sql = "delete from t_emp where id=?";
	int result = jdbcTemplate.update(sql, 1);
}

步骤七:

public class Emp {

    private Integer id;
    private String name;
    private Integer age;
    private String sex;

    //生成get和set方法
    //......

    @Override
    public String toString() {
        return "Emp{" +
                "id=" + id +
                ", name='" + name + '\'' +
                ", age=" + age +
                ", sex='" + sex + '\'' +
                '}';
    }
}
//查询:返回对象
@Test
public void testSelectObject() {
    //写法一
//        String sql = "select * from t_emp where id=?";
//        Emp empResult = jdbcTemplate.queryForObject(sql,
//                (rs, rowNum) -> {
//                    Emp emp = new Emp();
//                    emp.setId(rs.getInt("id"));
//                    emp.setName(rs.getString("name"));
//                    emp.setAge(rs.getInt("age"));
//                    emp.setSex(rs.getString("sex"));
//                    return emp;
//                }, 1);
//        System.out.println(empResult);

    //写法二
    String sql = "select * from t_emp where id=?";
    Emp emp = jdbcTemplate.queryForObject(sql,new BeanPropertyRowMapper<>(Emp.class),1);
    System.out.println(emp);
}
@Test
//查询数据返回list集合
public void testSelectList(){
    String sql = "select * from t_emp";
    List<Emp> list = jdbcTemplate.query(sql, new BeanPropertyRowMapper<>(Emp.class));
    System.out.println(list);
}
@Test
//查询返回单个的值
public void selectCount(){
    String sql = "select count(id) from t_emp";
    Integer count = jdbcTemplate.queryForObject(sql, Integer.class);
    System.out.println(count);
}

2.3、基于注解的声明式事务

笔记小结:

  1. 基本用例:
    步骤一:添加tx命名空间、添加事务管理器、开启事务的注解驱动
    步骤二:添加事务注解:@Transactional
  2. 事务属性:
    1. 只读:@Transactional(readOnly = true),告诉数据库,此操作只能读不能改写
    2. 超时:@Transactional(timeout = 3),当此操作超时时,做出提示
    3. 回滚策略
      • rollbackFor属性:当事务方法抛出指定类型的异常时,事务会回滚
      • rollbackForClassName属性:与rollbackFor属性相似,但是指定异常类型时使用字符串的方式
      • noRollbackFor属性:指定当事务方法抛出指定类型的异常时,事务不回滚
      • noRollbackForClassName属性:与noRollbackFor属性相似,但是指定异常类型使用字符串的方式
    4. 隔离级别:
      • READ_UNCOMMITTED:表示一个事务可以读取另一个未提交事务的数据。此级别会>出现脏读、不可重复读、幻读的问题,一般不建议使用。
      • READ_COMMITTED:表示一个事务只能读取另一个已经提交的事务的数据,可以避免>脏读问题,但不可重复读和幻读问题仍可能发生。
      • REPEATABLE_READ:表示一个事务在执行期间可以多次读取同一行数据,可以避免>脏读和不可重复读问题,但幻读问题仍可能发生。
      • SERIALIZABLE:表示一个事务在执行期间对所涉及的所有数据加锁,避免了脏读、不可重复读、幻读问题,但并发性能非常差。
    5. 传播行为:事务的传播行为是指在多个事务方法相互调用时,控制事务如何传播和影响彼此的行为的规则
    6. 全注解配置事务:@EnableTransactionManagement 开启注解式事务管理

2.3.1、基本用例-实现注解式的声明事务

准备工作:
创建BookController:

package com.atguigu.spring6.controller;

@Controller
public class BookController {

    @Autowired
    private BookService bookService;

    public void buyBook(Integer bookId, Integer userId){
        bookService.buyBook(bookId, userId);
    }
}

创建接口BookService:

package com.atguigu.spring6.service;
public interface BookService {
    void buyBook(Integer bookId, Integer userId);
}

创建实现类BookServiceImpl:

package com.atguigu.spring6.service.impl;
@Service
public class BookServiceImpl implements BookService {

    @Autowired
    private BookDao bookDao;

    @Override
    public void buyBook(Integer bookId, Integer userId) {
        //查询图书的价格
        Integer price = bookDao.getPriceByBookId(bookId);
        //更新图书的库存
        bookDao.updateStock(bookId);
        //更新用户的余额
        bookDao.updateBalance(userId, price);
    }
}

创建接口BookDao:

package com.atguigu.spring6.dao;
public interface BookDao {
    Integer getPriceByBookId(Integer bookId);

    void updateStock(Integer bookId);

    void updateBalance(Integer userId, Integer price);
}

创建实现类BookDaoImpl:

package com.atguigu.spring6.dao.impl;
@Repository
public class BookDaoImpl implements BookDao {

    @Autowired
    private JdbcTemplate jdbcTemplate;

    @Override
    public Integer getPriceByBookId(Integer bookId) {
        String sql = "select price from t_book where book_id = ?";
        return jdbcTemplate.queryForObject(sql, Integer.class, bookId);
    }

    @Override
    public void updateStock(Integer bookId) {
        String sql = "update t_book set stock = stock - 1 where book_id = ?";
        jdbcTemplate.update(sql, bookId);
    }

    @Override
    public void updateBalance(Integer userId, Integer price) {
        String sql = "update t_user set balance = balance - ? where user_id = ?";
        jdbcTemplate.update(sql, price, userId);
    }
}

步骤一:添加配置文件中的tx命名空间以及配置

  • 添加tx命名空间
  • 添加事务管理器
  • 开启事务的注解驱动
<?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:tx="http://www.springframework.org/schema/tx"
       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/tx
                           http://www.springframework.org/schema/tx/spring-tx.xsd">

    <!--事务管理器-->
    <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
        <property name="dataSource" ref="druidDataSource"/>
    </bean>

    <!--
		开启事务的注解驱动。通过注解@Transactional所标识的方法或标识的类中所有的方法,都会被事务管理器管理事务-->
    <!-- transaction-manager属性的默认值是transactionManager,如果事务管理器bean的id正好就是这个默认值,则可以省略这个属性 -->
    <tx:annotation-driven transaction-manager="transactionManager" />
</beans>

步骤二:添加事务注解

  • Service层表示业务逻辑层,注解通常添加在业务层的功能上,可达到事务管理的目的
  • @Transactional注解标识在方法上,只会影响此方法被事务管理利器进行管理。
  • @Transactional注解标识在类上,则会影响该类中的所有方法被事务管理利器进行管理
@Transactional
public void buyBook(Integer bookId, Integer userId) {
    //查询图书的价格
    Integer price = bookDao.getPriceByBookId(bookId);
    //更新图书的库存
    bookDao.updateStock(bookId);
    //更新用户的余额
    bookDao.updateBalance(userId, price);
    //System.out.println(1/0);
}

2.3.2、事务属性:只读

​ 对一个查询操作来说,如果我们把它设置成只读,就能够明确告诉数据库,这个操作不涉及写操作。这样数据库就能够针对查询操作来进行优化

@Transactional(readOnly = true)
public void buyBook(Integer bookId, Integer userId) {
    //查询图书的价格
    Integer price = bookDao.getPriceByBookId(bookId);
    //更新图书的库存
    bookDao.updateStock(bookId);
    //更新用户的余额
    bookDao.updateBalance(userId, price);
    //System.out.println(1/0);
}

对增删改操作设置只读会抛出下面异常:

Caused by: java.sql.SQLException: Connection is read-only. Queries leading to data >modification are not allowed

2.3.3、事务属性:超时

​ 事务在执行过程中,有可能因为遇到某些问题,导致程序卡住,从而长时间占用数据库资源。而长时间占用资源,大概率是因为程序运行出现了问题(可能是Java程序或MySQL数据库或网络连接等等)。此时这个很可能出问题的程序应该被回滚,撤销它已做的操作,事务结束,把资源让出来,让其他正常程序可以执行

//超时时间单位秒
@Transactional(timeout = 3)
public void buyBook(Integer bookId, Integer userId) {
    try {
        TimeUnit.SECONDS.sleep(5);
    } catch (InterruptedException e) {
        e.printStackTrace();
    }
    //查询图书的价格
    Integer price = bookDao.getPriceByBookId(bookId);
    //更新图书的库存
    bookDao.updateStock(bookId);
    //更新用户的余额
    bookDao.updateBalance(userId, price);
    //System.out.println(1/0);
}

说明:

此时,已为事务添加了事务超时的属性,因此进行查改操作超出单位时间则会抛出异常:

org.springframework.transaction.TransactionTimedOutException: Transaction timed >out: deadline was Fri Jun 04 16:25:39 CST 2022

2.3.4、事务属性:回滚策略

​ 事务属性中的回滚策略指的是当事务出现异常时,应该如何处理事务的提交或回滚

属性分类:

  1. rollbackFor属性:当事务方法抛出指定类型的异常时,事务会回滚

    @Transactional(rollbackFor = {SQLException.class, IOException.class})
    
  2. rollbackForClassName属性:与rollbackFor属性相似,但是指定异常类型时使用字符串的方式

    @Transactional(rollbackForClassName = {"java.sql.SQLException", "java.io.IOException"})
    
  3. noRollbackFor属性:指定当事务方法抛出指定类型的异常时,事务不回滚。

    @Transactional(noRollbackFor = {NullPointerException.class, IllegalArgumentException.class})
    
  4. noRollbackForClassName属性:与noRollbackFor属性相似,但是指定异常类型时使用字符串的方式

    @Transactional(noRollbackForClassName = {"java.lang.NullPointerException", "java.lang.IllegalArgumentException"})
    

基本用例:

@Transactional(noRollbackFor = ArithmeticException.class)
//@Transactional(noRollbackForClassName = "java.lang.ArithmeticException")
public void buyBook(Integer bookId, Integer userId) {
    //查询图书的价格
    Integer price = bookDao.getPriceByBookId(bookId);
    //更新图书的库存
    bookDao.updateStock(bookId);
    //更新用户的余额
    bookDao.updateBalance(userId, price);
    System.out.println(1/0);
}

说明:

此时,@Transactional注解配置的属性为noRollbackFor,因此当出现ArithmeticException.class此类异常后,事务不会进行回滚

2.3.5、事务属性:隔离级别

​ 事务隔离级别是指在多个事务并发执行时,为了保证事务之间的数据一致性,数据库采用的一种隔离机制

隔离级别脏读不可重复读幻读
READ UNCOMMITTED
READ COMMITTED
REPEATABLE READ
SERIALIZABLE

在这里插入图片描述

说明:

  • READ_UNCOMMITTED:表示一个事务可以读取另一个未提交事务的数据。此级别会出现脏读、不可重复读、幻读的问题,一般不建议使用。
  • READ_COMMITTED:表示一个事务只能读取另一个已经提交的事务的数据,可以避免脏读问题,但不可重复读和幻读问题仍可能发生。
  • REPEATABLE_READ:表示一个事务在执行期间可以多次读取同一行数据,可以避免脏读和不可重复读问题,但幻读问题仍可能发生。
  • SERIALIZABLE:表示一个事务在执行期间对所涉及的所有数据加锁,避免了脏读、不可重复读、幻读问题,但并发性能非常差。
    基本用法
@Transactional(isolation = Isolation.DEFAULT)//使用数据库默认的隔离级别
@Transactional(isolation = Isolation.READ_UNCOMMITTED)//读未提交
@Transactional(isolation = Isolation.READ_COMMITTED)//读已提交
@Transactional(isolation = Isolation.REPEATABLE_READ)//可重复读
@Transactional(isolation = Isolation.SERIALIZABLE)//串行化

2.3.6、事务属性:传播行为

​ 事务的传播行为是指在多个事务方法相互调用时,控制事务如何传播和影响彼此的行为的规则。在Spring框架中,事务的传播行为由Propagation枚举类定义,常用的传播行为包括如下:

  1. REQUIRED(默认):如果当前存在一个事务,则加入该事务;如果当前没有事务,则创建一个新的事务。换句话说,没有就新建,有就加入
  2. SUPPORTS:如果当前存在一个事务,则加入该事务;如果当前没有事务,则以非事务的方式继续运行。换句话说,有就加入,没有就不管了
  3. MANDATORY:如果当前存在一个事务,则加入该事务;如果当前没有事务,则抛出异常。换句话说,有就加入,没有就抛异常
  4. REQUIRES_NEW:创建一个新的事务,如果当前存在事务,则将当前事务挂起。换句话说,不管有没有,直接开启一个新事务,开启的新事务和之前的事务不存在嵌套关系,之前事务被挂起
  5. NOT_SUPPORTED:以非事务方式运行,如果当前存在事务,则将当前事务挂起。换句话说,不支持事务,存在就挂起
  6. NEVER:以非事务方式运行,如果当前存在事务,则抛出异常。换句话说,不支持事务,存在就抛异常
  7. NESTED:如果当前存在一个事务,则在嵌套事务内执行;如果当前没有事务,则按照 PROPAGATION_REQUIRED 执行。换句话说,有事务的话,就在这个事务里再嵌套一个完全独立的事务,嵌套的事务可以独立的提交和回滚。没有事务就和REQUIRED一样
@Transactional(propagation = Propagation.REQUIRED)
public void transactionalMethod() {
    // ...
}

2.3.7、全注解配置事务(重点)

步骤一:添加配置类

package com.atguigu.spring6.config;

import com.alibaba.druid.pool.DruidDataSource;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.datasource.DataSourceTransactionManager;
import org.springframework.transaction.annotation.EnableTransactionManagement;

import javax.sql.DataSource;

@Configuration // 表示该类为配置类
@ComponentScan("com.atguigu.spring6") // 开启组件扫描,扫描com.atguigu.spring6包下的组件
@EnableTransactionManagement // 开启注解式事务管理
public class SpringConfig {

    @Bean // 声明一个Bean对象
    public DataSource getDataSource(){
        DruidDataSource dataSource = new DruidDataSource(); // 创建Druid连接池
        dataSource.setDriverClassName("com.mysql.cj.jdbc.Driver"); // 配置驱动类名
        dataSource.setUrl("jdbc:mysql://localhost:3306/spring?characterEncoding=utf8&useSSL=false"); // 配置数据库URL
        dataSource.setUsername("root"); // 配置数据库用户名
        dataSource.setPassword("root"); // 配置数据库密码
        return dataSource; // 返回配置好的数据源对象
    }

    @Bean(name = "jdbcTemplate") // 声明一个Bean对象并命名为"jdbcTemplate"
    public JdbcTemplate getJdbcTemplate(DataSource dataSource){
        JdbcTemplate jdbcTemplate = new JdbcTemplate(); // 创建JdbcTemplate对象
        jdbcTemplate.setDataSource(dataSource); // 设置JdbcTemplate的数据源
        return jdbcTemplate; // 返回配置好的JdbcTemplate对象
    }

    @Bean // 声明一个Bean对象
    public DataSourceTransactionManager getDataSourceTransactionManager(DataSource dataSource){
        DataSourceTransactionManager dataSourceTransactionManager = new DataSourceTransactionManager(); // 创建DataSourceTransactionManager对象
        dataSourceTransactionManager.setDataSource(dataSource); // 设置数据源
        return dataSourceTransactionManager; // 返回配置好的DataSourceTransactionManager对象
    }
}


说明:

当使用全注解配置事务时,需要声明一个事务管理器,并使用注解@EnableTransactionManagement开启事务管理器

步骤二:演示

@Test
public void testTxAllAnnotation(){
    ApplicationContext applicationContext = new AnnotationConfigApplicationContext(SpringConfig.class);
    BookController accountService = applicationContext.getBean("bookController", BookController.class);
    accountService.buyBook(1, 1);
}

2.3.8、底层原理

​ 在 Java 中,使用 @Transactional 注解实现方法级别的事务管理是基于 Spring 框架的特性和 AOP(面向切面编程)的原理。

底层原理如下:

  1. Spring 通过 AOP 功能拦截带有 @Transactional 注解的方法的执行。
  2. 当方法被调用时,Spring 在运行时会为该方法创建一个代理对象。
  3. 代理对象在方法执行前后会插入事务相关的逻辑。
  4. 在方法开始时,事务管理器会开启一个新的数据库事务。
  5. 如果方法执行成功(无异常抛出),事务管理器会提交事务,将对数据库的修改持久化到数据库。
  6. 如果方法执行出现异常,事务管理器会回滚事务,撤销对数据库的修改,恢复到事务开始之前的状态。
  7. 方法执行完成后,事务管理器会关闭事务。

​ 通过 AOP 的方式,Spring 能够在方法执行前后对事务进行控制。它在方法执行前开启事务,在方法执行后提交或回滚事务,从而保证了数据的一致性和完整性。
在这里插入图片描述

​ 需要注意的是,@Transactional 注解的生效还依赖于事务管理器的配置和使用环境的支持。Spring 框架提供了多种事务管理器的实现,如基于 JDBC 的事务管理器和基于 JTA 的事务管理器,可以根据具体的需求选择合适的事务管理器。同时,Spring 需要在配置文件中启用事务管理器,并确保被 @Transactional 注解修饰的方法是通过 Spring 容器获取的代理对象来调用的。这样才能使事务注解生效并正确地应用事务管理的功能。

三、资源操作:Resources

3.1、Resources接口

3.1.1常用方法

  • boolean exists():检查资源是否存在。
  • boolean isReadable():检查资源是否可读。
  • boolean isOpen():检查资源是否打开。
  • URL getURL():获取资源的URL。
  • URI getURI():获取资源的URI。
  • File getFile():获取资源对应的文件。
  • long contentLength():获取资源的长度。
  • long lastModified():获取资源的最后修改时间。
  • Resource createRelative(String relativePath):创建相对于当前资源的相对资源。
  • String getFilename():获取资源的文件名。
  • String getDescription():获取资源的描述信息。
  • InputStream getInputStream():获取资源的输入流。

3.2、Resources的实现类

Resource 接口是 Spring 资源访问策略的抽象,它本身并不提供任何资源访问实现,具体的资源访问由该接口的实现类完成。每个实现类代表一种资源访问策略。Resource一般包括这些实现类:UrlResource、ClassPathResource、FileSystemResource、ServletContextResource、InputStreamResource、ByteArrayResource

在这里插入图片描述

3.2.1、UrlResource访问网络资源

此类用于表示URL类型的资源,可以通过URL对象或String类型的URL路径进行实例化,用来访问网络资源。

  • http:------该前缀用于访问基于HTTP协议的网络资源。
  • ftp:------该前缀用于访问基于FTP协议的网络资源
  • file: ------该前缀用于从文件系统中读取资源
public class UrlResourceDemo {

    public static void loadAndReadUrlResource(String path){
        // 创建一个 Resource 对象
        UrlResource url = null;
        try {
            url = new UrlResource(path);
            // 获取资源名
            System.out.println(url.getFilename());
            System.out.println(url.getURI());
            // 获取资源描述
            System.out.println(url.getDescription());
            //获取资源内容
            System.out.println(url.getInputStream().read());
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    public static void main(String[] args) {
        //1 访问网络资源
        //loadAndReadUrlResource("http://www.atguigu.com");

        //2 访问文件系统资源
        loadAndReadUrlResource("file:atguigu.txt");
    }
}

3.2.2、ClassPathResource 访问类路径下资源

ClassPathResource 用来访问类加载路径下的资源,相对于其他的 Resource 实现类,其主要优势是方便访问类加载路径里的资源,尤其对于 Web 应用,ClassPathResource 可自动搜索位于 classes 下的资源文件,无须使用绝对路径访问。

public class ClassPathResourceDemo {
    public static void loadAndReadUrlResource(String path) throws Exception{
        // 创建一个 Resource 对象
        ClassPathResource resource = new ClassPathResource(path);
        // 获取文件名
        System.out.println("resource.getFileName = " + resource.getFilename());
        // 获取文件描述
        System.out.println("resource.getDescription = "+ resource.getDescription());
        //获取文件内容
        InputStream in = resource.getInputStream();
        byte[] b = new byte[1024];
        while(in.read(b)!=-1) {
            System.out.println(new String(b));
        }
    }

    public static void main(String[] args) throws Exception {
        loadAndReadUrlResource("atguigu.txt");
    }
}

ClassPathResource实例可使用ClassPathResource构造器显式地创建,但更多的时候它都是隐式地创建的。当执行Spring的某个方法时,该方法接受一个代表资源路径的字符串参数,当Spring识别该字符串参数中包含classpath:前缀后,系统会自动创建ClassPathResource对象。

3.2.3、FileSystemResource 访问文件系统资源

Spring 提供的 FileSystemResource 类用于访问文件系统资源,使用 FileSystemResource 来访问文件系统资源并没有太大的优势,因为 Java 提供的 File 类也可用于访问文件系统资源。

public class FileSystemResourceDemo {

    public static void loadAndReadUrlResource(String path) throws Exception{
        //相对路径
        FileSystemResource resource = new FileSystemResource("atguigu.txt");
        //绝对路径
        //FileSystemResource resource = new FileSystemResource("C:\\atguigu.txt");
        // 获取文件名
        System.out.println("resource.getFileName = " + resource.getFilename());
        // 获取文件描述
        System.out.println("resource.getDescription = "+ resource.getDescription());
        //获取文件内容
        InputStream in = resource.getInputStream();
        byte[] b = new byte[1024];
        while(in.read(b)!=-1) {
            System.out.println(new String(b));
        }
    }

    public static void main(String[] args) throws Exception {
        loadAndReadUrlResource("atguigu.txt");
    }
}

FileSystemResource实例可使用FileSystemResource构造器显示地创建,但更多的时候它都是隐式创建。执行Spring的某个方法时,该方法接受一个代表资源路径的字符串参数,当Spring识别该字符串参数中包含file:前缀后,系统将会自动创建FileSystemResource对象。

3.2.4、ServletContextResource、InputStreamResource、ByteArrayResource

  • ServletContextResource

这是ServletContext资源的Resource实现,它解释相关Web应用程序根目录中的相对路径。它始终支持流(stream)访问和URL访问,但只有在扩展Web应用程序存档且资源实际位于文件系统上时才允许java.io.File访问。无论它是在文件系统上扩展还是直接从JAR或其他地方(如数据库)访问,实际上都依赖于Servlet容器。

  • InputStreamResource

InputStreamResource 是给定的输入流的Resource实现。它的使用场景在没有特定的资源实现的时候使用(感觉和@Component 的适用场景很相似)。与其他Resource实现相比,这是已打开资源的描述符。 因此,它的isOpen()方法返回true。如果需要将资源描述符保留在某处或者需要多次读取流,请不要使用它。

  • ByteArrayResource

字节数组的Resource实现类。通过给定的数组创建了一个ByteArrayInputStream。它对于从任何给定的字节数组加载内容非常有用,而无需求助于单次使用的InputStreamResource。

3.3、ResourceLoader接口

ResourceLoader接口是Spring框架中的一个核心接口,它定义了用于加载资源的统一访问方式。它提供了一种统一的方法,可以加载各种类型的资源,例如文件、类路径资源、URL资源等。

3.3.1、常用方法

  • Resource getResource(String location): 根据给定的资源位置(location)获取一个Resource对象。资源位置可以是文件路径、类路径、URL等。具体的资源加载策略由ResourceLoader的具体实现类决定。
  • ClassLoader getClassLoader(): 获取用于加载类的ClassLoader对象。这对于加载类路径下的资源非常有用。

3.3.2、实现类

Spring框架提供了多个实现ResourceLoader接口的类,包括:

  • DefaultResourceLoader: 默认的资源加载器,可用于加载类路径、文件系统和URL资源。
  • FileSystemResourceLoader: 用于加载文件系统中的资源。
  • ClassPathResourceLoader: 用于加载类路径下的资源。
  • ServletContextResourceLoader: 用于加载Web应用程序上下文中的资源。

3.3.3、基本用例

public static void main(String[] args) {
    // 创建Spring应用上下文,从类路径中加载配置文件
    ApplicationContext ctx = new ClassPathXmlApplicationContext();
    //        通过ApplicationContext访问资源
    //        ApplicationContext实例获取Resource实例时,
    //        默认采用与ApplicationContext相同的资源访问策略
    Resource res = ctx.getResource("atguigu.txt");
    System.out.println(res.getFilename());
}

说明:

​ Spring将采用和ApplicationContext相同的策略来访问资源。也就是说,如果ApplicationContext是ClassPathXmlApplicationContext,res就是ClassPathResource实例

public static void main(String[] args) {
    // 创建Spring应用上下文,指定文件系统路径加载配置文件
    ApplicationContext ctx = new FileSystemXmlApplicationContext();
    Resource res = ctx.getResource("atguigu.txt");
    System.out.println(res.getFilename());
}

说明:

Spring将采用和ApplicationContext相同的策略来访问资源。也就是说,果ApplicationContext是FileSystemXmlApplicationContext,res就是FileSystemResource实例;

import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.springframework.core.io.Resource;

public class ResourceLoaderExample {

    public static void main(String[] args) {
        // 创建一个ApplicationContext容器对象,该对象会读取classpath(类路径)下的名为applicationContext.xml的配置文件
        ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");

        // 使用 classpath: 前缀指定使用 ClassPathResource 实现类
        Resource resource1 = context.getResource("classpath:config.properties");
        System.out.println("Resource 1: " + resource1.getClass().getSimpleName());

        // 使用 file: 前缀指定使用 FileSystemResource 实现类
        Resource resource2 = context.getResource("file:/path/to/file.txt");
        System.out.println("Resource 2: " + resource2.getClass().getSimpleName());

        // 使用 http: 前缀指定使用 UrlResource 实现类
        Resource resource3 = context.getResource("http://www.example.com");
        System.out.println("Resource 3: " + resource3.getClass().getSimpleName());
    }
}


说明:

使用 ApplicationContext 的 getResource() 方法获取了三个不同类型的资源,使用不同的前缀指定了使用不同的 Resource 实现类

3.4、ResourceLoaderAware接口

ResourceLoaderAware 是一个 Spring Bean 接口,用于将 ResourceLoader 实例注入到实现该接口的 Bean 中。它定义了一个 setResourceLoader(ResourceLoader resourceLoader) 方法,Spring 容器在启动时将 ResourceLoader 实例作为参数传递给该方法。

作用

​ 实现ResourceLoaderAware接口的类可以获取Spring容器的ResourceLoader实例,从而利用Spring容器提供的资源加载功能

基本用例

步骤一:创建Bean

package com.atguigu.spring6.resouceloader;

import org.springframework.context.ResourceLoaderAware;
import org.springframework.core.io.ResourceLoader;

public class TestBean implements ResourceLoaderAware {

    private ResourceLoader resourceLoader;

    //实现ResourceLoaderAware接口必须实现的方法
	//如果把该Bean部署在Spring容器中,该方法将会有Spring容器负责调用。
	//SPring容器调用该方法时,Spring会将自身作为参数传给该方法。
    public void setResourceLoader(ResourceLoader resourceLoader) {
        this.resourceLoader = resourceLoader;
    }

    //返回ResourceLoader对象的应用
    public ResourceLoader getResourceLoader(){
        return this.resourceLoader;
    }

}

说明:

实现ResourceLoaderAware接口必须实现setResourceLoader方法,

步骤二:配置Bean

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">

    <bean id="testBean" class="com.atguigu.spring6.resouceloader.TestBean"></bean>
</beans>

步骤三:演示

public static void main(String[] args) {
    //Spring容器会将一个ResourceLoader对象作为该方法的参数传入
    ApplicationContext ctx = new ClassPathXmlApplicationContext("bean.xml");
    TestBean testBean = ctx.getBean("testBean",TestBean.class);
    //获取ResourceLoader对象
    ResourceLoader resourceLoader = testBean.getResourceLoader();
    System.out.println("Spring容器将自身注入到ResourceLoaderAware Bean 中 ? :" + (resourceLoader == ctx));
    //加载其他资源
    Resource resource = resourceLoader.getResource("atguigu.txt");
    System.out.println(resource.getFilename());
    System.out.println(resource.getDescription());
}

说明:

因为ApplicationContext接口的实现类ClassPathXmlApplicationContext实现了ResourceLoader接口,因此ClassPathXmlApplicationContext实例对象,也具备"ResourceLoader"的功能。因此,实现了ResourceLoaderAware此接口的对象,就可以利用Spring容器提供的资源加载功能

3.5、动态获取Resource资源

3.5.1、含义

​ Spring 框架不仅充分利用了策略模式来简化资源访问,而且还将策略模式和 IoC 进行充分地结合,最大程度地简化了 Spring 资源访问。当应用程序中的 Bean 实例需要访问资源时,Spring 有更好的解决方法:直接利用依赖注入

3.5.2、作用

​ 对于通过在代码中获取 Resource 实例,当程序获取 Resource 实例时,总需要提供 Resource 所在的位置,不管通过 FileSystemResource 创建实例,还是通过 ClassPathResource 创建实例,或者通过 ApplicationContext 的 getResource() 方法获取实例,都需要提供资源位置。这意味着:资源所在的物理位置将被耦合到代码中,如果资源位置发生改变,则必须改写程序。因此,通常建议采用依赖注入的方法,让 Spring 为 Bean 实例依赖注入资源。

3.5.3、基本用例

步骤一: 创建依赖注入类,定义属性和方法

public class ResourceBean {
    
    private Resource res;
    
    public void setRes(Resource res) {
        this.res = res;
    }
    public Resource getRes() {
        return res;
    }
    
    public void parse(){
        System.out.println(res.getFilename());
        System.out.println(res.getDescription());
    }
}

步骤二:创建spring配置文件,配置依赖注入

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">

    <bean id="resourceBean" class="com.atguigu.spring6.resouceloader.ResourceBean" >
      <!-- 可以使用file:、http:、ftp:等前缀强制Spring采用对应的资源访问策略 -->
      <!-- 如果不采用任何前缀,则Spring将采用与该ApplicationContext相同的资源访问策略来访问资源 -->
        <property name="res" value="classpath:atguigu.txt"/>
    </bean>
</beans>

步骤三:演示

public static void main(String[] args) {
    ApplicationContext ctx =
        new ClassPathXmlApplicationContext("bean.xml");
    ResourceBean resourceBean = ctx.getBean("resourceBean",ResourceBean.class);
    resourceBean.parse();
}

3.6、确定资源访问路径

3.6.1、概述

​ 不管以怎样的方式创建ApplicationContext实例,都需要为ApplicationContext指定配置文件,Spring允许使用一份或多分XML配置文件。当程序创建ApplicationContext实例时,通常也是以Resource的方式来访问配置文件的,所以ApplicationContext完全支持ClassPathResource、FileSystemResource、ServletContextResource等资源访问方式。

3.6.2、实现类指定访问路径

(1)ClassPathXMLApplicationContext : 对应使用ClassPathResource进行资源访问。

(2)FileSystemXmlApplicationContext : 对应使用FileSystemResource进行资源访问。

(3)XmlWebApplicationContext : 对应使用ServletContextResource进行资源访问。

详细步骤,请查看Resources的实现类

3.6.3、前缀指定访问路径

  • classpath前缀

    public class Demo1 {
    
        public static void main(String[] args) {
            /*
             * 通过搜索文件系统路径下的xml文件创建ApplicationContext,
             * 但通过指定classpath:前缀强制搜索类加载路径
             * classpath:bean.xml
             * */
            ApplicationContext ctx =
                    new ClassPathXmlApplicationContext("classpath:bean.xml");
            System.out.println(ctx);
            Resource resource = ctx.getResource("atguigu.txt");
            System.out.println(resource.getFilename());
            System.out.println(resource.getDescription());
        }
    }
    
    

说明:

在使用ApplicationContext中,指定路径时,使用**classpath:**为前缀

  • classpath通配符
ApplicationContext ctx = new ClassPathXmlApplicationContext("classpath*:bean.xml");

说明:

在使用ApplicationContext中,指定路径时,使用classpath*为前缀,表示Spring将会搜索类加载路径下所有满足该规则的配置文件。例如:bean.xml、beans.xml

  • 通配符其他使用
ApplicationContext ctx = new ClassPathXmlApplicationContext("classpath:bean*.xml");

说明:

一次性加载多个配置文件的方式:指定配置文件时使用通配符

ApplicationContext ctx = new ClassPathXmlApplicationContext("classpath*:bean*.xml");

说明:
​> Spring允许将classpath*:前缀和通配符结合使用

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

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

相关文章

柯桥生活英语培训之“老顾客”在英语中通常有几种表达方式?

“老顾客”用英语怎么说&#xff1f;千万不要直接翻译成“old customer”&#xff0c;因为这会让人误以为是指年龄大的顾客。 “老顾客”在英语中通常有以下几种表达方式&#xff1a; regular customer 这是最常见的表达&#xff0c;强调顾客经常光顾。这里的“regular”强调的…

RTR——Chapter5 下

锯齿和抗锯齿 想象现在有一个巨大的黑色三角形&#xff0c;正在白色的背景上缓缓移动。由于屏幕上的网格单元被三角形所覆盖&#xff0c;因此其像素值的强度应该会平滑的下降。但是通常在各种基础渲染器中发生的情况是&#xff0c;一旦网格单元的中心被三角形所覆盖&#xff0c…

【JavaScript】算法之分治、动态规划

一个大问题分成多个小问题&#xff0c;递归解决小问题&#xff0c;将结果合并从而来解决原来的问题 分治 子问题都是独立的 动态规划 把分治优化了【重复的问题&#xff0c;单独保存起来】斐波那契数列 leetcode 习题 分治、动态规划习题

Varjo在芬兰开设新工厂,以满足国防部门在XR模拟训练中的需求

在军事国防领域&#xff0c;全新技术的投入使用最看重的就是保密与安全。作为全球领先的XR头戴式显示器提供商Varjo&#xff0c;近日正式宣布将在位于芬兰的赫尔辛基开设一家新的安全制造工厂。 此次工厂扩建将使Varjo能够满足国防训练和模拟领域对其高分辨率XR解决方案日益增…

Python if 语句优化技巧

大家好&#xff01;今天我们来聊聊Python中的if语句优化技巧。if语句是Python中最基本的控制结构之一&#xff0c;它用于根据条件执行不同的代码块。虽然if语句本身非常简单&#xff0c;但通过一些小技巧&#xff0c;可以让我们的代码更加高效、简洁。接下来&#xff0c;我们将…

怎么选择合适的员工电脑监控软件,优质的电脑屏幕监控软件

在当今信息化管理的时代&#xff0c;员工电脑监控软件已成为中小公司管理的重要工具之一。无论是提升工作效率、保障公司数据安全&#xff0c;还是确保员工在工作时间专注于任务&#xff0c;选择一款合适的监控软件都至关重要。今天&#xff0c;我们将重点介绍一款国内优秀的监…

Android TV RecyclerView列表获得焦点左右换行

在TV上&#xff0c;用RecyclerView显示一个列表&#xff0c;飞鼠遥控左右遥控获得Item焦点&#xff0c;到最后一个进行右键换行&#xff0c;是不能做到的&#xff0c;因此需要监听key事件处理换行。 效果图如下 代码实现 Item.xml布局 <?xml version"1.0" enc…

24最全网最全面的Comfyui工作流原理拆解分析教程!

前言 前言 前面几篇有讲到Comfyui的安装和入门基础的文生图&#xff0c;图生图加上CN和局部重绘的工作流教程&#xff0c;这工作流是基于sd webui的工作流原理跟大家简单讲了Comfyui工作流的基本原理。 今天我们通过拆解组合的方式再稍微深入拓展给大家讲一下Comfyui的工作流…

基于springboot+vue图书管理系统的设计与实现

摘 要 传统信息的管理大部分依赖于管理人员的手工登记与管理&#xff0c;然而&#xff0c;随着近些年信息技术的迅猛发展&#xff0c;让许多比较老套的信息管理模式进行了更新迭代&#xff0c;图书信息因为其管理内容繁杂&#xff0c;管理数量繁多导致手工进行处理不能满足广…

昂科烧录器支持Senasic琻捷电子的蓝牙低功耗芯片SNP746

芯片烧录行业领导者-昂科技术近日发布最新的烧录软件更新及新增支持的芯片型号列表&#xff0c;其中Senasic琻捷电子的蓝牙低功耗芯片SNP746已经被昂科的通用烧录平台AP8000所支持。 SNP746是一款蓝牙低功耗芯片&#xff0c;集成了压力传感器和加速度传感器的测量电路。它是为…

以电子书号出版的论著有哪些优缺点?

以电子书号出版的论著具有以下优点和缺点&#xff1a; 优点&#xff1a; 1. 费用较低&#xff1a; - 电子书号的管理费用相对较低&#xff0c;因为其不需要分配 CIP 数据&#xff08;图书在版编目数据&#xff09;&#xff0c;这一项就节省了不少成本。对于一些预算有限的作者…

不小心把U盘格式化了怎么恢复?教你轻松找回数据

U盘作为我们日常工作和生活中的重要数据存储工具&#xff0c;其便携性和大容量深受用户喜爱。然而&#xff0c;不小心将U盘格式化&#xff0c;导致重要数据丢失&#xff0c;是许多人都可能遇到的问题。 当这种突发情况发生时&#xff0c;我们应该如何迅速有效地恢复被格式化的…

最佳实践 · 如何高效索引MySQL JSON字段

概述 从MySQL 5.7.8版本开始&#xff0c;MySQL引入了对JSON字段的支持&#xff0c;这为处理半结构化数据提供了极大的灵活性。然而&#xff0c;MySQL原生并不支持直接对JSON对象中的字段进行索引。本文将介绍如何利用MySQL 5.7中的虚拟字段功能&#xff0c;对JSON字段中的数据…

数据结构-排序(冒泡,选择,插入,希尔,快排,归并,堆排)

文章目录 排序冒泡排序代码实现 选择排序动图演示代码实现 插入排序动图演示代码实现 希尔排序动图演示代码实现 快速排序动图演示代码实现(递归) 归并排序动图演示代码实现 堆排序动图演示代码实现 排序 概念&#xff1a;排序就是将一组杂乱无章的数据按照一定的规律&#xff…

web基础—dvwa靶场(九)Weak Session IDs

Weak Session IDs&#xff08;弱会话&#xff09; Weak Session IDs&#xff08;弱会话&#xff09;&#xff0c;用户访问服务器的时候&#xff0c;一般服务器都会分配一个身份证 session id 给用户&#xff0c;用于标识。用户拿到 session id 后就会保存到 cookies 上&#x…

数据可视化pyecharts——数据分析(柱状图、折线图、饼图)

安装 首先确保已经安装了pyecharts库&#xff0c;如果没有&#xff0c;可以通过pip install pyecharts进行安装。 柱状图 从pyecharts.charts导入Bar&#xff0c;从pyecharts导入options。准备数据&#xff08;如类别数据x_data和对应的数值数据y_data&#xff09;。创建Bar对…

C# 动态编译

一、简介 CSharpCodeProvider 是 .NET 提供的一个强大工具&#xff0c;它允许开发人员在应用程序运行时动态地生成和执行 C# 代码。这一特性为后端开发带来了前所未有的灵活性和动态性&#xff0c;特别是在处理那些需要高度定制化或难以在编译时确定逻辑的场景时&#xff0c;尤…

【项目实训1】手把手教你使用 Dehazeformer 模型去雾:服务器租用、环境配置、自定义数据集、模型的训练与测试(全网最全的操作指导)

前言 文章性质:实操笔记 📖 代码来源:GitHub - IDKiro/DehazeFormer: [IEEE TIP] Vision Transformers for Single Image Dehazing 主要内容:本文详细记录了如何借助 Tabby 图形界面工具在 AutoDL 远程服务器上配置 Dehazeformer 所需的项目环境,并且成功运行 Dehazeform…

HeterGCL-Graph Contrastive Learning Framework on Heterophilic Graph

推荐指数: #paper/⭐⭐ 发表于:IJCAI24 类型&#xff1a;个人觉得算是图结构学习&#xff0c;部分思想不错 问题背景&#xff1a; 传统的随机增强不适合异配图。随机增强主要保留的是同配信息。这就导致在异配图用随机增强会抑制高频信息&#xff0c;直接使用时不合理的(这个…

JDBC 编程

目录 JDBC 是什么 JDBC 的工作原理 JDBC 的使用 引入驱动 使用 常用接口和类 Connection Statement ResultSet 使用总结 JDBC 是什么 JDBC&#xff08;Java Database Connectivity&#xff09;&#xff1a;Java数据库连接&#xff0c;是一种用于执行 SQL 语句的Java…