Spring 框架(Spring Framework)之事务管理、单元测试、单例的高并发安全问题等

本文涉及的产品
云数据库 RDS MySQL,集群系列 2核4GB
推荐场景:
搭建个人博客
RDS MySQL Serverless 基础系列,0.5-2RCU 50GB
云数据库 RDS PostgreSQL,集群系列 2核4GB
简介: Spring 框架(Spring Framework)之事务管理、单元测试、单例的高并发安全问题等

Spring 中的事务管理

Spring支持两种事务管理方式:编程式事务和声明式事务。官方大力推荐使用声明式事务。

  • 编程式事务:将业务代码和事务代码放在一起书写,它的耦合性太高,开发中不使用
  • 声明式事务:

    • 声明式事务是建立在 AOP 的基础上的。

      其本质是在方法前后进行拦截,在方法开始之前创建事务,在方法结束后根据执行情况提交或回滚事务。

    • 声明式事务的优点:可以将事务代码和业务代码完全分离开发,然后通过配置的方式实现运行时组装运行。
    • 声明式事务的不足:只能作用到方法级别,无法像编程式事务那样可以作用到代码块级别


Spring 中事务管理相关 API

Spring中的事务控制主要就是通过这三个API实现的:

  • PlatformTransactionManager 接口:事务管理器,负责事务的管理,其子类负责具体工作
  • TransactionDefinition 接口:定义了事务的一些相关参数
  • TransactionStatus 接口:代表事务运行的一个实时状态

三者的关系:事务管理器 通过读取 事务定义参数 进行事务管理,然后会产生一系列的 事务状态


PlatformTransactionManager 接口:

  • Spring进行事务管理的一个根接口,使用其实现类做事务管理器(增强的事务处理的功能)
  • 接口方法:

    // 获取事务的状态信息
    TransactionStatus getTransaction(TransactionDefinition def)
    // 提交事务
    void commit(TransactionStatus status)
    // 回滚事务
    void rollback(TransactionStatus status)
  • 常用实现类:

    • DataSourceTransactionManager :使用 JDBC 和 MyBatis 进行持久化数据时使用
    • JpaTransactionManager :使用 JPA 进行持久化时使用(jpa,hibernate)
    • HibernateTransactionManager :使用Hibernate进行持久化数据时使用
    • JtaTransactionManager :事务跨越多个事务管理源【分布式事务】时使用


TransactionStatus 接口 :

  • 事务运行的一个实时状态
  • 接口方法

    // 是否是新事物
    boolean isNewTransaction()
    // 是否有回滚点
    boolean hasSavepoint()
    // 设置为只回滚事务
    void setRollbackOnly()
    // 是否是只回滚事务
    boolean isRollbackOnly()
    // 刷新事务状态 
    void flush()
    // 事务是否完成
    boolean isCompleted()


TransactionDefinition 接口:

  • 事务的定义信息(事事务隔离级别,传播行为,是否只读事务,超时时间等)

clip_image058

  • 事务隔离级别

    • Spring中配置事务,支持所有的4中隔离级别
    • 默认值:自动选择当前数据库合适的配置项
    • // 事务隔离级别相关【不设置事务隔离级别,可能引发脏读、不可重复读、幻读】
      ISOLATION_READ_UNCOMMITTED    读未提交    mysq1支持四种,默认可重复度
      ISOLATION_READ COMMITTED    读已提交    oracle支持两种(读己提交和串行化),默认是读已提交
      ISOLATION_REPEATABLE READ    可重复度
      ISOLATION SERIALIZABLE        串行化
  • 事务传播行为:描述多个方法嵌套调用时,被调用方法对事务的支持

    • PROPAGATION_REQUIRED = 0(必须有事务,这是默认值)

      如果存在一个事务,则加入到当前事务。如果没有事务则开启一个新的事务。

    • PROPAGATION_SUPPORTS = 1(支持有事务)

      如果存在一个事务,则加入到当前事务。如果没有事务则非事务运行。

    • PROPAGATION_MANDATORY = 2(强制有事务,自己还不负责创建)

      如果存在一个事务,则加入到当前事务。如果没有事务,则抛出异常。

    • PROPAGATION_REQUIRES_NEW = 3(必须有新的)

      总是开启一个新的事务。如果存在一个事务,则将这个存在的事务挂起,重新开启一个新的事务。

    • PROPAGATION_NOT_SUPPORTED = 4(不支持有事务)

      总是非事务地执行,并挂起任何存在的事务。

    • PROPAGATION_NEVER = 5(强制不要事务,自己还不负责挂起)

      总是非事务地执行,如果存在一个活动事务,则抛出异常

    • PROPAGATION_NESTED = 6(嵌套事务)

      如果一个活动的事务存在,则运行在一个嵌套的事务中。如果没有活动事务, 则开启一个新的事务。

      内层事务依赖于外层事务。外层事务失败时,会回滚内层事务所做的动作。

      而内层事务操作失败并不会引起外层事务的回滚

  • 是否只读事务

    isReadOnly : true | false(默认)

    只读事务: 只能查询,不能增 删 改。只读事务只能用于查询方法

  • 事务超时时长

    TIMEOUT_DEFAULT = -1 :事务的超时时间,需要底层数据库支持才能使用此配置,默认值是 -1,代表无限制。

    超时时间:使用默认值


声明式事务(xml 配置事务)

基于XML的方式完成声明式事务配置:

  • 配置事务管理器交给Spring容器管理(切面类)
  • 配置事务通知
  • 配置事务的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"
       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/aop
             http://www.springframework.org/schema/aop/spring-aop.xsd
             http://www.springframework.org/schema/tx
             http://www.springframework.org/schema/tx/spring-tx.xsd">
    <!--包扫描-->
    <context:component-scan base-package="cn.test"></context:component-scan>
    <!--自定义的java对象:注解-->

    <!--第三方jar包中的对象:xml-->
    <bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
        <property name="dataSource" ref="dataSource"></property>
    </bean>
    <bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
        <property name="username" value="root"></property>
        <property name="password" value="root"></property>
        <property name="driverClassName" value="com.mysql.jdbc.Driver"></property>
        <property name="url" value="jdbc:mysql:///heima23"></property>
    </bean>

    <!--配置Spring中的事务-->
    <!--1、事务管理器交给容器管理-->
    <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
        <property name="dataSource" ref="dataSource"></property>
    </bean>
    <!-- 2、配置事务通知。配置service层中所有类中所有方法,对事务的要求(支持) 
            id="advice" :表示IOC容器中真正的通知对象的id
            transaction-manager="transactionManager" :表示指定当前要对哪个事务管理器进行配置
            如果事务管理器在IOC容器中的id为transactionManager,此配置可以省略。
    -->
    <tx:advice id="txAdvice" transaction-manager="transactionManager">
        <tx:attributes>
            <!--
                <tx:method :指定目标对象中切入点的方法名指定方法对事务的要求
                    name :方法名称。支持通配符 *
                    isolation :事务的隔离级别
                    timeout :超时时间
                    propagation :传播行为(REQUIRED)
                    read-only :是否只读事务(false)
            -->
            <tx:method name="save*" propagation="REQUIRED" read-only="false"></tx:method>
            <tx:method name="update*"></tx:method>
            <tx:method name="delete*"></tx:method>
            <tx:method name="find*" propagation="SUPPORTS" read-only="true"></tx:method>
            <tx:method name="*"></tx:method>
        </tx:attributes>
    </tx:advice>
    <!--3、事务的AOP配置-->
    <aop:config>
        <!--切入点表达式-->
        <aop:pointcut id="pt" expression="execution(* cn.test.service.impl.*.*(..))"/>
        <!--配置切面。<aop:advisor只有在spring的声明式事务配置时才能使用-->
        <aop:advisor advice-ref="txAdvice" pointcut-ref="pt"></aop:advisor>
    </aop:config>
</beans>


声明式事务(注解)

开启事务注解支持(xml 方式)

  • 在XML配置文件中,开启事务注解的支持:事务注解驱动
  • 在XML配置文件中,创建事务管理器交给容器管理
  • 在需要事务的类或者方法上,使用 @Transactional 注解配置事务
<?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"
       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/aop
                http://www.springframework.org/schema/aop/spring-aop.xsd
                http://www.springframework.org/schema/tx
                http://www.springframework.org/schema/tx/spring-tx.xsd">
    <!--包扫描-->
    <context:component-scan base-package="cn.test"></context:component-scan>

    <!--开启事务注解的支持-->
    <tx:annotation-driven></tx:annotation-driven>

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

    <!--自定义的java对象:注解-->

    <!--第三方jar包中的对象:xml-->
    <bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
        <property name="dataSource" ref="dataSource"></property>
    </bean>
    <bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
        <property name="username" value="root"></property>
        <property name="password" value="root"></property>
        <property name="driverClassName" value="com.mysql.jdbc.Driver"></property>
        <property name="url" value="jdbc:mysql:///test"></property>
    </bean>
</beans>


开启事务注解支持(配置类方式)

  • @EnableTransactionManagement:标注在配置类上,开启事务注解支持
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.PlatformTransactionManager;
import org.springframework.transaction.annotation.EnableTransactionManagement;
import javax.sql.DataSource;

@Configuration
@ComponentScan(basePackages = "cn.test")
@EnableTransactionManagement
public class SpringConfig {

    /**
     * 创建datasource
     */
    @Bean
    public DataSource getDataSource() {
        DruidDataSource dataSource = new DruidDataSource();
        dataSource.setUsername("root");
        dataSource.setPassword("root");
        dataSource.setUrl("jdbc:mysql:///test");
        dataSource.setDriverClassName("com.mysql.jdbc.Driver");
        return dataSource;
    }

    /**
     * 创建jdbctemplate
     *  1、从容器中获取datasource
     *  2、调用方法
     */
    @Bean
    public JdbcTemplate getJdbcTemplate(DataSource dataSource) {
        JdbcTemplate jdbcTemplate = new JdbcTemplate();
        jdbcTemplate.setDataSource(dataSource);
        return jdbcTemplate;
    }

    /**
     * 创建事务管理器
     */
    @Bean
    public PlatformTransactionManager getManager(DataSource dataSource) {
        DataSourceTransactionManager manager = new DataSourceTransactionManager();
        manager.setDataSource(dataSource);
        return manager;
    }
}


声明式事务注解的使用

  • @Transactional:配置事务

    常用属性:

    • rollbackFor 属性:设置需要进行回滚的异常类数组,当方法中抛出指定异常数组中的异常时,则进行事务回滚

      • 指定单一异常类:@Transactional(rollbackFor=Exception.class)
      • 指定多个异常类:@Transactional(rollbackFor={RuntimeException.class, Exception.class})
      • readOnly 属性:是否只读事务 ( true | false(默认值) )
      • propagation 属性:事务传播行为 ( SUPPORTS | REQUIRED(默认值) )
      • transactionManager 属性:多个事务管理器托管在 Spring 容器中时,指定事务管理器的 bean 名称
  • isolation 属性:设置底层数据库的事务隔离级别,事务隔离级别用于处理多事务并发的情况

    通常使用数据库的默认隔离级别即可,基本不需要进行设置

  • noRollbackFor 属性:设置不需要进行回滚的异常类数组,当方法中抛出指定异常数组中的异常时,不进行事务回滚

标注位置说明:

  • 标注在类上:该类中所有方法都具有相同的事务配置
  • 标注在方法上:该方法具有事务配置
  • 同时标注在类上和方法上:就近原则(方法上的事务配置生效)
import cn.test.dao.AccountDao;
import cn.test.domain.Account;
import cn.test.service.AccountService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;

@Service
@Transactional(propagation = Propagation.SUPPORTS)
public class AccountServiceImpl implements AccountService {

    @Autowired
    private AccountDao accountDao;

    @Transactional
    //@Transactional(propagation = Propagation.REQUIRED,readOnly = false)
    public void transfer(String sourceName, String targetName, float money) throws Exception {
        //1、根据账户名称查询两个账户
        Account sourceAccount = accountDao.findByName(sourceName); //转出账户
        Account targetAccount = accountDao.findByName(targetName); //转入账户
        //2、操作金额转换(转出账户扣除金额,转入账户添加金额)
        sourceAccount.setMoney(sourceAccount.getMoney() - money);
        targetAccount.setMoney(targetAccount.getMoney() + money);
        //3、更新账户
        accountDao.update(sourceAccount);
        int i=1/0;
        accountDao.update(targetAccount);
    }
}


拓展

Spring 整合单元测试

当在单元测试中,点击 run 的时候,底层工作的其实是一个运行器,默认是 junit 提供的 ParentRunner 运行器,它是不认识Spring的环境,这也就意味着,它无法从 Spring 的容器中获取bean。

如果想要从 Spring 的容器中获取对象,需要使用 Spring 提供的运行器。

  • 引入依赖

    <dependency>
        <groupId>junit</groupId>
        <artifactId>junit</artifactId>
        <version>4.12</version>
    </dependency>
    <!--spring-junit 整合单元测试-->
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-test</artifactId>
        <version>5.1.6.RELEASE</version>
    </dependency>
  • @RunWith 注解:设置单元测试的运行器,通过 value 属性指定单元测试运行环境

    • JUnit4.class :指定使用 JUnit4 来运行
    • SpringJUnit4ClassRunner.class :Spring 测试环境
    • SpringRunner.class:Spring 测试环境

    注:

    • SpringRunner extends SpringJUnit4ClassRunner.class
    • 使用上 JUnit4.12 或更高版本以上 SpringRunner,SpringJUnit4ClassRunner 都可以使用

      但是推荐使用 SpringRunner,final类型,安全

    • JUnit4.12 以下版本就只能使用 SpringJUnit4ClassRunner
  • @ContextConfiguration 注解:指定容器的配置信息

    • localtions 属性:配置文件路径
    • classes 属性:配置类
@RunWith(SpringRunner.class)
@ContextConfiguration(locations = "classpath:applicationContext.xml")
public class AccountJunitTest {

    @Autowired
    private AccountService accountService;

    //测试保存
    @Test
    public void testInsert() {
        //3、调用方法保存
        Account account = new Account();
        account.setMoney(100f);
        account.setName("小李1");
        accountService.saveAccount(account);
    }
}


配置文件的模块化

若配置都集中配在了一个applicationContext.xml文件中,当开发人员过多时, 如果所有bean都配置到同一个配置文件中,会使这个文件巨大,而且也不方便维护。针对这个问题,Spring提供了多配置文件的方式,也就是所谓的配置文件模块化。

  1. 并列的多个配置文件
    直接编写多个配置文件,比如说beans1.xml,beans2.xml......, 然后在创建ApplicationContext的时候,直接
    传入多个配置文件。

    ApplicationContext act = new ClassPathXmlApplicationContext("beans1.xml","beans2.xml","...");
  2. 主从配置文件
    先陪一个主配置文件,然后在里面导入其它的配置文件。

      <import resource="beans1.xml" />
      <import resource="beans2.xml" />
    
      <!--拓展:引入本地properties配置文件-->
      <context:property-placeholder location="classpath:db.properties"/>

注意事项:

  • 同一个xml文件中不能出现相同名称的bean,如果出现会报错
  • 多个xml文件如果出现相同名称的bean,不会报错,但是后加载的会覆盖前加载的bean,所以企业开发中尽
    量保证bean的名称是唯一的。


Spring Bean 单例的高并发安全问题

Spring的bean默认都是单例的,某些情况下,单例是并发不安全的,以Controller举例,问题根源在于,若在Controller中定义成员变量,多个请求来临,进入的都是同一个单例的Controller对象,若对此成员变量的值进行修改操作,则会互相影响,无法达到并发安全(不同于线程隔离的概念)的效果。

抛出问题

多次访问此url,可以看到每次的结果都是自增的,所以这样的代码显然是并发不安全的。

@Controller
public class HomeController {
    private int i;
    @GetMapping("testsingleton1")
    @ResponseBody
    public int test1() {
        return ++i;
    }
}

解决方案

方案1:尽量避免使用成员变量

在业务允许的条件下,尽量避免使用成员变量,使用方法中的局部变量


方案2:使用并发安全的类

Java作为功能性超强的编程语言,API丰富,如果非要在单例bean中使用成员变量,可以考虑使用并发安全的容器,如ConcurrentHashMap、ConcurrentHashSet等,将成员变量(一般可以是当前运行中的任务列表等这类变量)包装到这些并发安全的容器中进行管理即可。


方案3:分布式或微服务的并发安全

如果还要进一步考虑到微服务或分布式服务的影响,方案2便不足以处理了,所以可以借助于可以共享某些信息的分布式缓存中间件如Redis等,这样即可保证同一种服务的不同服务实例都拥有同一份共享信息(如当前运行中的任务列表等这类变量)。


方案4:单例变原型

对web项目,可以Controller类上加注解@Scope("prototype")或@Scope("request"),对非web项目,在Component类上添加注解@Scope("prototype")。

优点:实现简单

缺点:很大程度上增大了bean创建实例化销毁的服务器资源开销


不可用方案:线程隔离类ThreadLocal

web服务器默认的请求线程池大小为10,这10个核心线程可以被之后不同的Http请求复用。

ThreadLocal的方式可以达到线程隔离,但还是无法达到并发安全。


使用 @Autowired 注解给静态变量赋值

描述:

在一些工具类中可能会用到Ioc容器中的对象,而工具类中的成员变量往往是静态的,此时使用@Autowired注解就会出现NullpointerException(空指针异常)。

原理剖析:

静态变量、类变量不是对象的属性,而是一个类的属性,所以静态方法是属于类(class)的,普通方法才是属于实体对象(也就是New出来的对象)的,spring注入是在容器中实例化对象,所以不能使用静态方法。

而使用静态变量、类变量扩大了静态方法的使用范围。静态方法在spring是不推荐使用的,依赖注入的主要目的,是让容器去产生一个对象的实例,然后在整个生命周期中使用他们,同时也让testing工作更加容易。

一旦使用静态方法,就不再需要去产生这个类的实例,这会让testing变得更加困难,同时也不能为一个给定的类,依靠注入方式去产生多个具有不同的依赖环境的实例,这种static field是隐含共享的,并且是一种global全局状态,spring同样不推荐这样去做。


解决方案1:将@Autowire注解加到set方法上

@Component
public class Test {
    
    private static SessionFactory sessionFactory;
    
    @Autowired
    public void setSessionFactory(SessionFactory sessionFactory) {
        Test.sessionFactory = sessionFactory;
    }
}

解决方案2:用@PostConstruct注解

@Component
public class Test {
    
    private static SessionFactory sessionFactory;
    
    @Autowired
    private SessionFactory sessionFactory2;
    
    @PostConstruct
    public void beforeInit() {
        SessionFactory = sessionFactory2;
    }
}

解决方案3:将@Autowire注解加到构造方法上

@Component
public class Test {
    
    private static SessionFactory sessionFactory;
    
    @Autowired
    public Test(SessionFactory sessionFactory) {
        Test.SessionFactory = SessionFactory;
    }
}


非容器中的类调用容器中的类

描述:

使用@Autowired注入对象时,一般被注入的类都带有@Coponent、@Controller、@Service 、@repository等注解才可以。注入类和被注入类都被spring所管理,可以完成调用。但是当非容器类(没加以上注解时)使用@Autowired调用容器中的类时,注入对象为空,报空指针异常。

解决方案:

创建工具类BeanUtils,在这个工具类中的getBean可以得到容器中的类,在非容器类中使用

@Component
public class BeanUtils implements ApplicationContextAware {
    /**
     * 以静态变量保存ApplicationContext,可在任意代码中取出ApplicaitonContext.
     */
    private static ApplicationContext context;

    /**
     * 实现ApplicationContextAware接口的context注入函数, 将其存入静态变量.
     */
    @Override
    public void setApplicationContext(ApplicationContext context) {
        BeanUtils.context = context;
    }

    public static ApplicationContext getApplicationContext() {
        return context;
    }

    /**
     * 从静态变量ApplicationContext中取得Bean, 自动转型为所赋值对象的类型.  方法返回值的类型由调用者决定
     */
    public static <T> T getBean(String name) {
        return (T) context.getBean(name);
    }

    /// 获取当前环境
    public String getActiveProfile() {
        return context.getEnvironment().getActiveProfiles()[0];
    }
}

非容器类中使用容器中的类

public class StationFactory {
    Map<String, StationOperation> map = new HashMap<>();
    {
        map.put("定损中心主管指标表", BeanUtils.getBean("leadDSZXOperation"));
        map.put("定损中心员工指标表", BeanUtils.getBean("empDSZXOperation"));
        map.put("视频查勘中心主管指标表", BeanUtils.getBean("leadVideoSurveyCenterOperation"));
        map.put("视频查勘中心员工指标表", BeanUtils.getBean("empVideoSurveyCenterOperation"));
        map.put("视频定损中心主管指标表", BeanUtils.getBean("leadVideoDSCenterOperation"));
    }
}
相关实践学习
如何在云端创建MySQL数据库
开始实验后,系统会自动创建一台自建MySQL的 源数据库 ECS 实例和一台 目标数据库 RDS。
全面了解阿里云能为你做什么
阿里云在全球各地部署高效节能的绿色数据中心,利用清洁计算为万物互联的新世界提供源源不断的能源动力,目前开服的区域包括中国(华北、华东、华南、香港)、新加坡、美国(美东、美西)、欧洲、中东、澳大利亚、日本。目前阿里云的产品涵盖弹性计算、数据库、存储与CDN、分析与搜索、云通信、网络、管理与监控、应用服务、互联网中间件、移动服务、视频服务等。通过本课程,来了解阿里云能够为你的业务带来哪些帮助 &nbsp; &nbsp; 相关的阿里云产品:云服务器ECS 云服务器 ECS(Elastic Compute Service)是一种弹性可伸缩的计算服务,助您降低 IT 成本,提升运维效率,使您更专注于核心业务创新。产品详情: https://www.aliyun.com/product/ecs
相关文章
|
10天前
|
数据采集 监控 前端开发
二级公立医院绩效考核系统源码,B/S架构,前后端分别基于Spring Boot和Avue框架
医院绩效管理系统通过与HIS系统的无缝对接,实现数据网络化采集、评价结果透明化管理及奖金分配自动化生成。系统涵盖科室和个人绩效考核、医疗质量考核、数据采集、绩效工资核算、收支核算、工作量统计、单项奖惩等功能,提升绩效评估的全面性、准确性和公正性。技术栈采用B/S架构,前后端分别基于Spring Boot和Avue框架。
|
14天前
|
测试技术 C# 数据库
C# 单元测试框架 NUnit 一分钟浅谈
【10月更文挑战第17天】单元测试是软件开发中重要的质量保证手段,NUnit 是一个广泛使用的 .NET 单元测试框架。本文从基础到进阶介绍了 NUnit 的使用方法,包括安装、基本用法、参数化测试、异步测试等,并探讨了常见问题和易错点,旨在帮助开发者有效利用单元测试提高代码质量和开发效率。
116 64
|
7天前
|
JavaScript 安全 Java
如何使用 Spring Boot 和 Ant Design Pro Vue 构建一个前后端分离的应用框架,实现动态路由和菜单功能
本文介绍了如何使用 Spring Boot 和 Ant Design Pro Vue 构建一个前后端分离的应用框架,实现动态路由和菜单功能。首先,确保开发环境已安装必要的工具,然后创建并配置 Spring Boot 项目,包括添加依赖和配置 Spring Security。接着,创建后端 API 和前端项目,配置动态路由和菜单。最后,运行项目并分享实践心得,帮助开发者提高开发效率和应用的可维护性。
20 2
|
6天前
|
消息中间件 NoSQL Java
springboot整合常用中间件框架案例
该项目是Spring Boot集成整合案例,涵盖多种中间件的使用示例,每个案例项目使用最小依赖,便于直接应用到自己的项目中。包括MyBatis、Redis、MongoDB、MQ、ES等的整合示例。
52 1
|
8天前
|
编解码 安全 Linux
网络空间安全之一个WH的超前沿全栈技术深入学习之路(10-2):保姆级别教会你如何搭建白帽黑客渗透测试系统环境Kali——Liinux-Debian:就怕你学成黑客啦!)作者——LJS
保姆级别教会你如何搭建白帽黑客渗透测试系统环境Kali以及常见的报错及对应解决方案、常用Kali功能简便化以及详解如何具体实现
|
8天前
|
人工智能 安全 Linux
网络空间安全之一个WH的超前沿全栈技术深入学习之路(4-2):渗透测试行业术语扫盲完结:就怕你学成黑客啦!)作者——LJS
网络空间安全之一个WH的超前沿全栈技术深入学习之路(4-2):渗透测试行业术语扫盲完结:就怕你学成黑客啦!)作者——LJS
|
8天前
|
安全 大数据 Linux
网络空间安全之一个WH的超前沿全栈技术深入学习之路(3-2):渗透测试行业术语扫盲)作者——LJS
网络空间安全之一个WH的超前沿全栈技术深入学习之路(3-2):渗透测试行业术语扫盲)作者——LJS
|
8天前
|
SQL 安全 网络协议
网络空间安全之一个WH的超前沿全栈技术深入学习之路(1-2):渗透测试行业术语扫盲)作者——LJS
网络空间安全之一个WH的超前沿全栈技术深入学习之路(1-2):渗透测试行业术语扫盲)作者——LJS
|
25天前
|
JSON 算法 数据可视化
测试专项笔记(一): 通过算法能力接口返回的检测结果完成相关指标的计算(目标检测)
这篇文章是关于如何通过算法接口返回的目标检测结果来计算性能指标的笔记。它涵盖了任务描述、指标分析(包括TP、FP、FN、TN、精准率和召回率),接口处理,数据集处理,以及如何使用实用工具进行文件操作和数据可视化。文章还提供了一些Python代码示例,用于处理图像文件、转换数据格式以及计算目标检测的性能指标。
46 0
测试专项笔记(一): 通过算法能力接口返回的检测结果完成相关指标的计算(目标检测)
|
2月前
|
移动开发 JSON Java
Jmeter实现WebSocket协议的接口测试方法
WebSocket协议是HTML5的一种新协议,实现了浏览器与服务器之间的全双工通信。通过简单的握手动作,双方可直接传输数据。其优势包括极小的头部开销和服务器推送功能。使用JMeter进行WebSocket接口和性能测试时,需安装特定插件并配置相关参数,如服务器地址、端口号等,还可通过CSV文件实现参数化,以满足不同测试需求。
212 7
Jmeter实现WebSocket协议的接口测试方法