Java开发经验——Spring Test 常见错误
摘要
本文详细介绍了Java开发中Spring Test的常见错误和解决方案。文章首先概述了Spring中进行单元测试的多种方法,包括使用JUnit和Spring Boot Test进行集成测试,以及Mockito进行单元测试。接着,文章分析了Spring资源文件扫描不到的问题,并提供了解决方案。最后,文章探讨了Spring的Mock问题,包括Spring Context启动缓慢的原因和优化方法。
1. Spring使用的测试
在 Spring 中,进行单元测试的方式有多种,主要取决于你希望测试的对象以及使用的测试框架。Spring 提供了丰富的测试支持来帮助开发者测试其应用中的各个组件。常见的 Spring 单元测试方法包括以下几种:
1.1. 使用 JUnit 和 Spring Boot Test((多用于集成测试,启动整个 Spring 容器)
1.1.1. @SpringBootTest
@SpringBootTest
是最常用的单元测试注解之一,它会启动 Spring 容器并加载整个 Spring 上下文,适用于集成测试。通常用于测试一个较大的功能,涉及多个组件和服务。
@SpringBootTest
public class MyServiceTest {@Autowiredprivate MyService myService;@Testvoid testServiceMethod() {assertNotNull(myService);assertEquals("expected result", myService.someMethod());}
}
- 优点: 自动加载整个 Spring 应用上下文,能够进行集成测试。
- 适用场景: 测试需要 Spring 配置、服务和其他组件的复杂业务逻辑。
1.1.2. @WebMvcTest
@WebMvcTest
主要用于测试 Spring MVC 控制器。它只会启动 Web 层相关的组件,不会启动整个 Spring 上下文,因此启动速度较快。
@WebMvcTest(MyController.class)
public class MyControllerTest {@Autowiredprivate MockMvc mockMvc;@Testpublic void testController() throws Exception {mockMvc.perform(get("/api/endpoint")).andExpect(status().isOk()).andExpect(content().string("Expected response"));}
}
- 优点: 只加载 Web 层相关的配置,启动速度快,适合单元测试。
- 适用场景: 测试控制器和 Web 层的请求响应。
1.1.3. @DataJpaTest
@DataJpaTest
用于测试与数据库相关的功能。它只会启动与 JPA 相关的配置,并且自动配置一个嵌入式数据库,适合用于测试数据访问层。
@DataJpaTest
public class MyRepositoryTest {@Autowiredprivate MyRepository myRepository;@Testvoid testFindById() {Optional<MyEntity> entity = myRepository.findById(1L);assertTrue(entity.isPresent());}
}
- 优点: 快速配置并测试数据库访问,适合单元测试 Repository 层。
- 适用场景: 测试与数据库交互的功能,如 Repository 类。
1.1.4. @MockBean
@SpringBootTest
public class MyServiceTest {@Autowiredprivate MyService myService;@MockBeanprivate MyRepository myRepository;@Testvoid testServiceMethod() {when(myRepository.findById(1L)).thenReturn(Optional.of(new MyEntity()));MyEntity entity = myService.getEntity(1L);assertNotNull(entity);}
}
- 优点: 可以模拟依赖的 Bean,避免在单元测试时连接到真实的数据库或其他外部服务。
- 适用场景: 测试服务层逻辑时,不依赖实际的数据库或外部服务。
1.2. 使用 Mockito 进行单元测试
Mockito 是常用的 Java 测试框架,可以用来模拟对象(Mock)和验证方法调用。它可以与 Spring 集成,用于服务层或控制器层的单元测试。
1.2.1. @Mock
和 @InjectMocks
@Mock
用于创建一个模拟对象,@InjectMocks
会自动将模拟对象注入到被测试的类中。结合 JUnit 使用时,可以对类中的依赖进行模拟,确保只测试该类的逻辑。
java复制代码
@RunWith(MockitoJUnitRunner.class)
public class MyServiceTest {@Mockprivate MyRepository myRepository;@InjectMocksprivate MyService myService;@Testpublic void testServiceMethod() {when(myRepository.findById(1L)).thenReturn(Optional.of(new MyEntity()));MyEntity entity = myService.getEntity(1L);assertNotNull(entity);}
}
- 优点: 只测试服务方法,依赖项完全由 Mockito 模拟。
- 适用场景: 单元测试业务逻辑,模拟数据访问层或外部服务。
1.2.2. @MockBean
与 Spring 配合
在 Spring 环境下使用 Mockito,可以通过 @MockBean
注解将模拟对象注入到 Spring 应用上下文中。这样,你可以测试服务层或控制器层,模拟外部依赖。
java复制代码
@SpringBootTest
public class MyServiceTest {@MockBeanprivate MyRepository myRepository;@Autowiredprivate MyService myService;@Testvoid testServiceMethod() {when(myRepository.findById(1L)).thenReturn(Optional.of(new MyEntity()));MyEntity entity = myService.getEntity(1L);assertNotNull(entity);}
}
- 优点: 模拟 Spring 管理的 Bean,可以通过 Spring 容器注入,避免直接依赖外部组件。
- 适用场景: 测试 Spring 容器管理的组件,模拟其依赖。
1.2.3. 使用 @TestConfiguration
创建自定义配置
@TestConfiguration
允许你为测试创建一个特殊的配置类,可以在测试中替换部分 Bean 配置。
@TestConfiguration
public class MyTestConfig {@Beanpublic MyService myService() {return new MyService(new MyRepositoryMock());}
}
- 优点: 在测试中使用自定义配置,替代生产环境中的配置。
- 适用场景: 在单元测试中需要使用特定的测试配置或模拟 Bean 时。
1.2.4. JUnit 5 注解测试@BeforeEach
和 @AfterEach
这些是 JUnit 5 的生命周期注解,用于在每个测试方法之前和之后执行特定的代码。常用于初始化和清理测试环境。
@BeforeEach
void setUp() {// 初始化代码
}@AfterEach
void tearDown() {// 清理代码
}
- 优点: 每个测试方法执行之前和之后执行特定的初始化和清理逻辑。
- 适用场景: 初始化和清理测试环境
1.2.5. @TestInstance
控制生命周期
@TestInstance
是 JUnit 5 中的注解,用于控制测试类实例化的生命周期。它可以设置为 PER_CLASS
,表示测试类只实例化一次,而不是每个测试方法实例化一次。
java复制代码
@TestInstance(TestInstance.Lifecycle.PER_CLASS)
public class MyServiceTest {// 测试方法
}
- 优点: 可以避免每个测试方法都实例化测试类,适合需要在类级别共享状态的测试。
- 适用场景: 需要类级别共享状态或资源的场景。
Spring 提供了多种单元测试方法,适用于不同层次的测试需求。常用的方法包括:
@SpringBootTest
:用于集成测试,启动整个 Spring 容器。@WebMvcTest
:用于测试 Spring MVC 控制器。@DataJpaTest
:用于测试 JPA 数据访问层。@MockBean
:模拟依赖 Bean,适合服务层测试。- Mockito:用于模拟依赖和验证方法调用,适合单元测试。
根据需要的测试粒度选择合适的测试方法,可以确保高效且全面的测试。
2. SpringBootTest实现单元测试
2.1. SpringBootTest项目与源码示例
package com.zhuangxiaoyan.unit;import org.springframework.stereotype.Service;/*** CalculatorService** @author xjl* @version 2024/11/24 10:29**/
@Service
public class CalculatorService {public int add(int a, int b) {return a + b;}public int subtract(int a, int b) {return a - b;}
}
package com.zhuangxiaoyan.unit;import org.junit.jupiter.api.Test;import static org.junit.jupiter.api.Assertions.assertEquals;/*** CalculatorServiceTest** @author xjl* @version 2024/11/24 10:29**/
public class CalculatorServiceTest {private final CalculatorService calculatorService = new CalculatorService();@Testvoid testAdd() {int result = calculatorService.add(2, 3);assertEquals(5, result);}@Testvoid testSubtract() {int result = calculatorService.subtract(5, 3);assertEquals(2, result);}
}
package com.zhuangxiaoyan.unit;import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;import static org.junit.jupiter.api.Assertions.assertEquals;@SpringBootTest
class UnitApplicationTests {@Autowiredprivate CalculatorService calculatorService;@Testvoid contextLoads() {}@Testvoid testAdd() {int result = calculatorService.add(4, 6);System.out.println(result);assertEquals(10, result);}@Testvoid testSubtract() {int result = calculatorService.subtract(9, 4);System.out.println(result);assertEquals(5, result);}}
2.2. org.junit.jupiter.api.Test;和JUNIT5 的区别是什么
org.junit.jupiter.api.Test
是 JUnit 5 中的一个注解,而 JUnit 5 是 JUnit 框架的最新版本。
2.2.1. JUnit 4 vs. JUnit 5 的区别
JUnit 4:
- 使用
@Test
注解,通常在org.junit
包下。 - 没有
@BeforeEach
和@AfterEach
,而是使用@Before
和@After
注解。 - 扩展性较差,不像 JUnit 5 那样有完整的扩展机制。
JUnit 5:
- 使用
@Test
注解,位于org.junit.jupiter.api.Test
包下。 - 引入了新的注解,如
@BeforeEach
和@AfterEach
(替代@Before
和@After
)。 - 引入了新的功能,如参数化测试、条件测试、测试生命周期钩子等。
- 提供了更强大的扩展机制,允许用户编写自己的扩展(例如
@ExtendWith
)。
2.2.2. JUnit 5 的新特性
- 生命周期钩子:
-
@BeforeEach
替代了@Before
。@AfterEach
替代了@After
。@BeforeAll
和@AfterAll
用于静态方法,替代了 JUnit 4 的@BeforeClass
和@AfterClass
。
- 扩展性和条件化测试:
-
- JUnit 5 引入了扩展机制,通过
@ExtendWith
可以将自定义的扩展类添加到测试类中。 - 可以通过
@EnabledIf
和@DisabledIf
条件注解来有条件地启用或禁用测试。
- JUnit 5 引入了扩展机制,通过
- 参数化测试:
-
- JUnit 5 提供了更强大的参数化测试支持,如
@ValueSource
、@EnumSource
、@MethodSource
等。
- JUnit 5 提供了更强大的参数化测试支持,如
- 更好的报告和兼容性:
-
- 更好的报告功能,能够输出更详细的测试结果。
- 通过
JUnit Vintage
模块,JUnit 5 可以与 JUnit 3 和 JUnit 4 的测试兼容运行。
2.3. Mockito 和 JUnit 版本兼容问题
出现了 Could not initialize plugin: interface org.mockito.plugins.MockMaker 这个错误?
Mockito 插件错误通常是由于 Mockito 版本 与 JUnit 版本 不兼容,或者你的项目中的依赖版本不一致。
解决办法:确保你使用的是兼容的 Mockito 和 JUnit 版本。如果你正在使用 JUnit 5
,则需要使用兼容的 Mockito 版本。
Maven 依赖示例:如果你使用 JUnit 5 和 Mockito,你应该确保你的 pom.xml
中有以下依赖:
<dependencies><!-- JUnit 5 依赖 --><dependency><groupId>org.junit.jupiter</groupId><artifactId>junit-jupiter-api</artifactId><version>5.8.2</version> <!-- 根据需要选择版本 --><scope>test</scope></dependency><dependency><groupId>org.junit.jupiter</groupId><artifactId>junit-jupiter-engine</artifactId><version>5.8.2</version> <!-- 根据需要选择版本 --><scope>test</scope></dependency><!-- Mockito 依赖 --><dependency><groupId>org.mockito</groupId><artifactId>mockito-core</artifactId><version>4.0.0</version> <!-- 根据需要选择版本 --><scope>test</scope></dependency><dependency><groupId>org.mockito</groupId><artifactId>mockito-junit-jupiter</artifactId><version>4.0.0</version> <!-- 确保使用与 mockito-core 兼容的版本 --><scope>test</scope></dependency>
</dependencies>
确保使用与 Mockito 4.x 或 JUnit 5 兼容的版本(如上所示)。如果你使用的是 JUnit 4,那么需要使用与之兼容的 Mockito 版本。
3. Spring资源文件扫描不到
@RestController
public class HelloController {@AutowiredHelloWorldService helloWorldService;@RequestMapping(path = "hi", method = RequestMethod.GET)public String hi() throws Exception{return helloWorldService.toString() ;};}
当访问 http://localhost:8080/hi 时,上述接口会打印自动注入的HelloWorldService类型的 Bean。而对于这个 Bean 的定义,我们这里使用配置文件的方式进行。
- 定义 HelloWorldService,具体到 HelloWorldService 的实现并非本讲的重点,所以我们可以简单实现如下:
public class HelloWorldService {
}
- 定义一个 spring.xml,在这个 XML 中定义 HelloWorldServic 的Bean,并把这个 spring.xml 文件放置在/src/main/resources 中:
<?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="helloWorldService" class="com.spring.puzzle.others.test.example1.HelloWorldService"></bean>
</beans>
- 定义一个 Configuration 引入上述定义 XML,具体实现方式如下:
@Configuration
@ImportResource(locations = {"spring.xml"})
public class Config {
}
完成上述步骤后,我们就可以使用 main() 启动起来。测试这个接口,一切符合预期。那么接下来,我们来写一个测试:
@SpringBootTest()
class ApplicationTests {@Autowiredpublic HelloController helloController;@Testpublic void testController() throws Exception {String response = helloController.hi();Assert.notNull(response, "not null");}}
当我们运行上述测试的时候,会发现测试失败了,报错如下:
3.1. 问题解析
启动程序加载spring.xml
首先看下调用栈:
可以看出,它最终以 ClassPathResource 形式来加载,这个资源的情况如下:
而具体到加载实现,它使用的是 ClassPathResource#getInputStream 来加载spring.xml文件:
从上述调用及代码实现,可以看出最终是可以加载成功的。
测试加载spring.xml
首先看下调用栈:
可以看出它是按 ServletContextResource 来加载的,这个资源的情况如下:
具体到实现,它最终使用的是 MockServletContext#getResourceAsStream 来加载文件:
@Nullable
public InputStream getResourceAsStream(String path) {String resourceLocation = this.getResourceLocation(path);Resource resource = null;try {resource = this.resourceLoader.getResource(resourceLocation);return !resource.exists() ? null : resource.getInputStream();} catch (IOException | InvalidPathException var5) {if (this.logger.isWarnEnabled()) {this.logger.warn("Could not open InputStream for resource " + (resource != null ? resource : resourceLocation), var5);}return null;}
}
你可以继续跟踪它的加载位置相关代码,即 getResourceLocation():
protected String getResourceLocation(String path) {if (!path.startsWith("/")) {path = "/" + path;}//加上前缀:/src/main/resourcesString resourceLocation = this.getResourceBasePathLocation(path);if (this.exists(resourceLocation)) {return resourceLocation;} else {//{"classpath:META-INF/resources", "classpath:resources", "classpath:static", "classpath:public"};String[] var3 = SPRING_BOOT_RESOURCE_LOCATIONS;int var4 = var3.length;for(int var5 = 0; var5 < var4; ++var5) {String prefix = var3[var5];resourceLocation = prefix + path;if (this.exists(resourceLocation)) {return resourceLocation;}}return super.getResourceLocation(path);}
}
你会发现,它尝试从下面的一些位置进行加载:
classpath:META-INF/resources
classpath:resources
classpath:static
classpath:public
src/main/webapp
如果你仔细看这些目录,你还会发现,这些目录都没有spring.xml。或许你认为源文件src/main/resource下面不是有一个 spring.xml 么?那上述位置中的classpath:resources不就能加载了么?
那你肯定是忽略了一点:当程序运行起来后,src/main/resource 下的文件最终是不带什么resource的。关于这点,你可以直接查看编译后的目录(本地编译后是 target\classes 目录),示例如下:
所以,最终我们在所有的目录中都找不到spring.xml,并且会报错提示加载不了文件。报错的地方位于 ServletContextResource#getInputStream 中:
@Override
public InputStream getInputStream() throws IOException {InputStream is = this.servletContext.getResourceAsStream(this.path);if (is == null) {throw new FileNotFoundException("Could not open " + getDescription());}return is;
}
3.2. 问题修正
从上述案例解析中,我们了解到了报错的原因,那么如何修正这个问题?这里我们可以采用两种方式。
- 在加载目录上放置 spring.xml
就本案例而言,加载目录有很多,所以修正方式也不少,我们可以建立一个 src/main/webapp,然后把 spring.xml 复制一份进去就可以了。也可以在/src/main/resources 下面再建立一个 resources 目录,然后放置进去也可以。
- 在 @ImportResource 使用classpath加载方式
@Configuration
//@ImportResource(locations = {"spring.xml"})
@ImportResource(locations = {"classpath:spring.xml"})
public class Config {
}
这里,我们可以通过 Spring 的官方文档简单了解下不同加载方式的区别,参考 Chapter 4. Resources:
很明显,我们一般都不会使用本案例的方式(即locations = {“spring.xml”},无任何“前缀”的方式),毕竟它已经依赖于使用的 ApplicationContext。而 classPath 更为普适些,而一旦你按上述方式修正后,你会发现它加载的资源已经不再是 ServletContextResource,而是和应用程序一样的 ClassPathResource,这样自然可以加载到了。
4. Spring的Mock问题
有时候,我们会发现 Spring Test 运行起来非常缓慢,寻根溯源之后,你会发现主要是因为很多测试都启动了Spring Context,示例如下:
那么为什么有的测试会多次启动 Spring Context?在具体解析这个问题之前,我们先模拟写一个案例来复现这个问题。
我们先在 Spring Boot 程序中写几个被测试类:
@Service
public class ServiceOne {
}
@Service
public class ServiceTwo {
}
然后分别写出对应的测试类:
@SpringBootTest()
class ServiceOneTests {@MockBeanServiceOne serviceOne;@Testpublic void test(){System.out.println(serviceOne);}
}@SpringBootTest()
class ServiceTwoTests {@MockBeanServiceTwo serviceTwo;@Testpublic void test(){System.out.println(serviceTwo);}
}
在上述测试类中,我们都使用了@MockBean。写完这些程序,批量运行测试,你会发现Spring Context 果然会被运行多次。那么如何理解这个现象,是错误还是符合预期?接下来我们具体来解析下。
4.1. 案例解析
当我们运行一个测试的时候,正常情况是不会重新创建一个 Spring Context 的。这是因为 Spring Test 使用了 Context 的缓存以避免重复创建 Context。那么这个缓存是怎么维护的呢?我们可以通过DefaultCacheAwareContextLoaderDelegate#loadContext来看下 Context 的获取和缓存逻辑:
public ApplicationContext loadContext(MergedContextConfiguration mergedContextConfiguration) {synchronized(this.contextCache) {ApplicationContext context = this.contextCache.get(mergedContextConfiguration);if (context == null) {try {context = this.loadContextInternal(mergedContextConfiguration);//省略非关键代码this.contextCache.put(mergedContextConfiguration, context);} catch (Exception var6) {//省略非关键代码}} else if (logger.isDebugEnabled()) {//省略非关键代码}this.contextCache.logStatistics();return context;}
}
从上述代码可以看出,缓存的 Key 是 MergedContextConfiguration。所以一个测试要不要启动一个新的 Context,就取决于根据这个测试 Class 构建的 MergedContextConfiguration 是否相同。而是否相同取决于它的 hashCode() 实现:
public int hashCode() {int result = Arrays.hashCode(this.locations);result = 31 * result + Arrays.hashCode(this.classes);result = 31 * result + this.contextInitializerClasses.hashCode();result = 31 * result + Arrays.hashCode(this.activeProfiles);result = 31 * result + Arrays.hashCode(this.propertySourceLocations);result = 31 * result + Arrays.hashCode(this.propertySourceProperties);result = 31 * result + this.contextCustomizers.hashCode();result = 31 * result + (this.parent != null ? this.parent.hashCode() : 0);result = 31 * result + nullSafeClassName(this.contextLoader).hashCode();return result;
}
从上述方法,你可以看出只要上述元素中的任何一个不同都会导致一个 Context 会重新创建出来。关于这个缓存机制和 Key 的关键因素你可以参考 Spring 的官方文档,也有所提及,这里我直接给出了链接,你可以对照着去阅读。
点击获取:Redirecting...
现在回到本案例,为什么会创建一个新的 Context 而不是复用?根源在于两个测试的contextCustomizers这个元素的不同。如果你不信的话,你可以调试并对比下。
ServiceOneTests 的 MergedContextConfiguration 示例如下:
ServiceTwoTests 的 MergedContextConfiguration 示例如下:
很明显,MergedContextConfiguration(即 Context Cache 的 Key)的 ContextCustomizer 是不同的,所以 Context 没有共享起来。而追溯到 ContextCustomizer 的创建,我们可以具体来看下。
当我们运行一个测试(testClass)时,我们会使用 MockitoContextCustomizerFactory#createContextCustomizer 来创建一个 ContextCustomizer,代码示例如下:
class MockitoContextCustomizerFactory implements ContextCustomizerFactory {MockitoContextCustomizerFactory() {}public ContextCustomizer createContextCustomizer(Class<?> testClass, List<ContextConfigurationAttributes> configAttributes) {DefinitionsParser parser = new DefinitionsParser();parser.parse(testClass);return new MockitoContextCustomizer(parser.getDefinitions());}
}
创建的过程是由 DefinitionsParser 来解析这个测试 Class(例如案例中的 ServiceOneTests),如果这个测试 Class 中包含了 MockBean 或者 SpyBean 标记的情况,则将对应标记的情况转化为 MockDefinition,最终添加到 ContextCustomizer 中。解析的过程参考 DefinitionsParser#parse:
void parse(Class<?> source) {this.parseElement(source);ReflectionUtils.doWithFields(source, this::parseElement);
}private void parseElement(AnnotatedElement element) {MergedAnnotations annotations = MergedAnnotations.from(element, SearchStrategy.SUPERCLASS);
//MockBean 处理 annotations.stream(MockBean.class).map(MergedAnnotation::synthesize).forEach((annotation) -> {this.parseMockBeanAnnotation(annotation, element);});
//SpyBean 处理 annotations.stream(SpyBean.class).map(MergedAnnotation::synthesize).forEach((annotation) -> {this.parseSpyBeanAnnotation(annotation, element);});
}private void parseMockBeanAnnotation(MockBean annotation, AnnotatedElement element) {Set<ResolvableType> typesToMock = this.getOrDeduceTypes(element, annotation.value());//省略非关键代码Iterator var4 = typesToMock.iterator();while(var4.hasNext()) {ResolvableType typeToMock = (ResolvableType)var4.next();MockDefinition definition = new MockDefinition(annotation.name(), typeToMock, annotation.extraInterfaces(), annotation.answer(), annotation.serializable(), annotation.reset(), QualifierDefinition.forElement(element));//添加到 DefinitionsParser#definitionsthis.addDefinition(element, definition, "mock");}
}
那说了这么多,Spring Context 重新创建的根本原因还是在于使用了@MockBean 且不同,从而导致构建的 MergedContextConfiguration 不同,而 MergedContextConfiguration 正是作为 Cache 的 Key,Key 不同,Context 不能被复用,所以被重新创建了。这就是为什么在案例介绍部分,你会看到多次 Spring Context 的启动过程。而正因为“重启”,测试速度变缓慢了。
4.2. 问题修正
到这,你会发现其实这种缓慢的根源是使用了@MockBean 带来的一个正常现象。但是假设你非要去提速下,那么你可以尝试使用 Mockito 去手工实现类似的功能。当然你也可以尝试使用下面的方式来解决,即把相关的 MockBean 都定义到一个地方去。例如针对本案例,修正方案如下:
public class ServiceTests {@MockBeanServiceOne serviceOne;@MockBeanServiceTwo serviceTwo;}@SpringBootTest()
class ServiceOneTests extends ServiceTests{@Testpublic void test(){System.out.println(serviceOne);}}@SpringBootTest()
class ServiceTwoTests extends ServiceTests{@Testpublic void test(){System.out.println(serviceTwo);}
}
重新运行测试,你会发现 Context 只会被创建一次,速度也有所提升了。相信,你也明白这么改能工作的原因了,现在每个测试对应的 Context 缓存 Key 已经相同了。
博文参考
《Spring常见错误》
相关文章:
Java开发经验——Spring Test 常见错误
摘要 本文详细介绍了Java开发中Spring Test的常见错误和解决方案。文章首先概述了Spring中进行单元测试的多种方法,包括使用JUnit和Spring Boot Test进行集成测试,以及Mockito进行单元测试。接着,文章分析了Spring资源文件扫描不到的问题&am…...
麦肯锡报告 | 科技落地的真谛:超越技术本身的价值创造
科技创新正在以惊人的速度改变企业运作和客户体验,但实现其潜力的关键在于正确的策略、流程、文化和人才。麦肯锡强调了一个理念:Never just tech(不仅仅是技术)。这表明,成功的数字化转型不仅依赖于技术,还…...
React 常见问题解答:设置、安装、用户事件和最佳实践
在本文中,我们将回答您在开始使用 React 时可能会问的 9 个常见问题。 1、开始使用 React 需要哪些技能和知识? 在深入研究 React 之前,您应该对以下内容有深入的了解: HTML、CSS 和 JavaScript (ES6)&a…...
Mairadb 最大连接数、当前连接数 查询
目录 查询数据库 最大连接数 查询当前连接总数 环境 Mariadb 10.11.6 跳转mysql数据库: 查询数据库 最大连接数 show variables like max_connections; 注意; 这个版本不能使用 : show variables like ‘%max_connections%’; 会报错 ÿ…...
【R库包安装】R库包安装总结:conda、CRAN等
【R库包安装】R库包安装总结:conda、CRAN等 方法1:基于 R 的 CRAN 仓库安装CRAN库包查询从 CRAN 安装 方法2:使用conda安装库包确保已安装 R 和 Conda 环境使用 Conda 官网浏览是否存在相应库包Conda 安装 R 库 方法3:从 GitHub 安…...
php反序列化1_常见php序列化的CTF考题
声明: 以下多内容来自暗月师傅我是通过他的教程来学习记录的,如有侵权联系删除。 一道反序列化的CTF题分享_ctf反序列化题目_Mr.95的博客-CSDN博客 一些其他大佬的wp参考:php_反序列化_1 | dayu’s blog (killdayu.com) 序列化一个对象将…...
LabVIEW动态显示控件方案
在LabVIEW开发中,涉及到动态显示和控制界面的设计时,经常需要根据用户选择的不同参数来动态显示或隐藏相关控件。例如,某些能可能会根据“Type”控件的不同选择显示不同的参数,如“Target”、“Duration”和“EndType”等。在一个…...
游戏引擎学习第22天
移除 DllMain() 并成功重新编译 以下是对内容的详细复述与总结: 问题和解决方案: 在编译过程中遇到了一些问题,特别是如何告知编译器不要退出程序,而是继续处理。问题的根源在于编译过程中传递给链接器的参数设置不正确。原本尝试…...
GitLab|GitLab报错:PG::ConnectionBad: could not connect to server...
错误信息: PG::ConnectionBad: could not connect to server: No such file or directory Is the server running locally and accepting connections on Unix domain socket "/var/opt/gitlab/postgresql/.s.PGSQL.5432"? /opt/gitlab/embedded/service…...
Ray 和 PyTorch
Ray 和 PyTorch 的介绍 Ray 是什么? Ray 是一个用于 分布式计算和机器学习任务 的开源框架,提供了一个高效的、灵活的并行计算平台。它的核心功能是支持分布式计算,帮助开发者以简单的方式扩展 Python 应用程序。 Ray 适用于以下场景&…...
Qt之详解QLockFile 文件锁
文章目录 QLockFile 详解前言什么是 QLockFile?QLockFile 的构造函数和常用成员函数构造函数1. 指定锁文件路径的构造函数 常用成员函数1. lock2. unlock3. isLocked4. setStaleLockTime5. getLockInfo6. removeStaleLock 完整示例代码总结 QLockFile 详解 前言 在…...
从0开始学PHP面向对象内容之常用设计模式(组合,外观,代理)
二、结构型设计模式 4、组合模式(Composite) 组合模式(Composite Pattern)是一种结构型设计模式,它将对象组合成树形结构以表示”部分–整体“的层次结构。通过组合模式,客户端可以以一致的方式处理单个对…...
机械设计学习资料
免费送大家学习资源,已整理好,仅供学习 下载网址: https://www.zzhlszk.com/?qZ02-%E6%9C%BA%E6%A2%B0%E8%AE%BE%E8%AE%A1%E8%A7%84%E8%8C%83SOP.zip...
论文笔记3-XCube: Large-Scale 3D Generative Modeling using Sparse Voxel Hierarchies
目录 Abtract 相关工作 核心算法: 整体流程概述 具体流程解析 1. 输入(Input) 2. 稀疏结构 VAE(Sparse Structure VAE) 3.分层体素潜在扩散(Hierarchical Voxel Latent Diffusion)…...
【组件】前端ElementUi 下拉Tree树形组件 带模糊搜索自动展开高亮功能
【组件】前端ElementUi 下拉Tree树形组件 带模糊搜索自动展开高亮功能 https://live.csdn.net/v/435737 <template><div><el-popoverstyle"overflow-y: auto; "placement"bottom"trigger"click"><el-inputstyle"margi…...
诠视科技受邀出席“中国虚拟现实产学研大会”
2024年11月2-3日,由中国虚拟现实技术与产业创新平台举办的第十届“中国虚拟现实产学研大会” 在北京顺利召开,大会围绕“虚拟现实技术与产业创新融合”这一主题进行深入探讨。诠视科技作为虚拟现实产业的创新领军企业,CEO林瓊受邀参加本次活动…...
【达梦数据库】授权查询
目录 授权查询EXPIRED_DATECLUSTER_TYPE 更换原则更换方法 授权查询 select * from v$license;EXPIRED_DATE 过期日期,如果是正式版会显示NULL CLUSTER_TYPE 授权使用的集群类型CLUSTER_TYPE,格式为字符串“XXXX”, 每一位上 0 表示禁止…...
探索 ZED 双目 3D 相机发展:ZED 2i 的创新功能扩展之路
在科技迅速发展的当下,3D 视觉技术在众多领域的发展中有着重要作用。Stereolabs 公司研发的 ZED 相机受到广泛关注。Stereolabs 于 2010 年在美国旧金山成立,一直专注于立体视觉和 3D 深度相机技术的研究。2015 年推出的 ZED 相机为无人机、机器人等设备…...
基于卷积神经网络的白菜病虫害识别与防治系统,resnet50,mobilenet模型【pytorch框架+python源码】
更多目标检测和图像分类识别项目可看我主页其他文章 功能演示: 白菜病虫害识别与防治系统,卷积神经网络,resnet50,mobilenet【pytorch框架,python源码】_哔哩哔哩_bilibili (一)简介 基于卷…...
计算机网络 | 7.网络安全
1.网络安全问题概述 (1)计算机网络面临的安全性威胁 <1>计算机网络面临的完全性威胁 计算机网络面临的两大类安全威胁:被动攻击和主动攻击 被动攻击 截获:从网络上窃听他人的通信内容。主动攻击 篡改:故意篡改…...
Opencv+ROS自编相机驱动
目录 一、工具 二、原理 代码 标定 三、总结 参考: 一、工具 opencv2ros ubuntu18.04 usb摄像头 二、原理 这里模仿usb_cam功能包对Opencv_ros进行修饰,加上相机参数和相机状态,难点在于相机参数的读取。 对于相机参数话题 camera…...
js---函数参数是值传递还是引用传递
理解1:都是值(基本/地址值)传递 理解2:可能是值传递,也可能是引用传递(地址值) 在JavaScript中,函数参数的传递方式取决于参数的类型: 值传递(Pass by Val…...
如何解决DDoS导致服务器宕机?
分布式拒绝服务攻击(DDoS攻击)是一种常见的网络安全威胁,通过大量恶意流量使目标服务器无法提供正常服务。DDoS攻击可能导致服务器宕机,严重影响业务的正常运行。本文将详细介绍如何检测和防御DDoS攻击,防止服务器宕机…...
临床检验项目指标学习笔记
声明: 家有病人,记录此学习笔记仅为了更好照顾家人。本文不负责内容的准确性和版权。如果存在侵权、或不希望被转载的媒体或个人可与我们联系,我将立即进行删除处理。 血脂四项 [Q&A] 血脂四项 指导意义 测定血清中血脂含量,…...
代码管理之Gitlab
文章目录 Git基础概述场景本地修改未提交,拉取远程代码修改提交本地,远程已有新提交 GitIDEA引入Git拉取仓库代码最后位置 Git基础 概述 workspace 工作区:本地电脑上看到的目录; repository 本地仓库:就是工作区中隐…...
秒杀系统三层架构设计:缓存、消息队列与数据库
秒杀是一种极端高并发场景,短时间内数百万用户涌入,抢购有限库存的商品。为了保证系统稳定性和数据一致性,同时提升用户体验,我们可以设计一个三层架构:缓存层、消息队列层、数据库层。本文将详细设计这一架构并探讨其…...
tauri2.0版本开发苹果ios和安卓android应用,环境搭建和最后编译为apk
官网链接:What is Tauri? | Tauri 初始准备 rust版本一定要1.77.2以上的版本,查看版本和升级版本: 升级命名: rustup update 不然会报错: error: package tauri-plugin-shell v2.0.2 cannot be built because it r…...
SCI论文部分题目
SC20243213 通过氢和氨集成增强Power-to-X灵活性和可持续成本降低:绿色实验室Skive案例研究 SC20243211 分析同时发电的综合系统中的能量和能量效率、环境影响和经济可行性,淡水、热水和天然气凝液 SC20243208 双燃料生物质-天然气系统中的氢、…...
使用PyTorch在AMD GPU上进行INT8量化实现精简化的LLM推理
Leaner LLM Inference with INT8 Quantization on AMD GPUs using PyTorch — ROCm Blogs 随着大型语言模型(LLMs)规模达到数千亿参数,我们在这些庞大模型中表示数据的方式极大地影响了训练所需的资源(例如,用于推理的…...
Solon 拉取 maven 包很慢或拉不了,怎么办?
注意:如果在 IDEA 设置里指定了 settings.xml,下面两个方案可能会失效。(或者直接拿 "腾讯" 的镜像仓库地址,按自己的习惯配置) 1、可以在项目的 pom.xml 添加 "腾讯" 的镜像仓库 "阿里&qu…...
spring的事务隔离?
在Spring中,事务的隔离级别是指在多事务并发执行时,事务之间的隔离程度,隔离级别定义了一个事务可以看到另一个事务的哪些数据,Spring事务管理器允许通过Transactional注解或者xml配置来指定事务的隔离级别。 事务的隔离级别有以…...
13 —— 开发环境调错-source map
问题:代码被压缩后,无法正确定位源代码的位置(行数和列数) source map:准确追踪error和warning在原始代码的位置 —— webpack.config.js配置devtool选项 module.exports { devtool: inline-source-map }; inline-s…...
【大数据分析深度学习】在Hadoop上实现分布式深度学习
【作者主页】Francek Chen 【专栏介绍】 ⌈ ⌈ ⌈智能大数据分析 ⌋ ⌋ ⌋ 智能大数据分析是指利用先进的技术和算法对大规模数据进行深入分析和挖掘,以提取有价值的信息和洞察。它结合了大数据技术、人工智能(AI)、机器学习(ML&a…...
【Linux探索学习】第十六弹——进程地址空间:深入解析操作系统中的进程地址空间
Linux学习笔记: https://blog.csdn.net/2301_80220607/category_12805278.html?spm1001.2014.3001.5482 前言: 进程地址空间是操作系统进程管理的重要概念之一,它定义了进程在执行时所能访问的内存布局。理解进程地址空间不仅有助于掌握操…...
【数据结构与算法】相交链表、环形链表(判断是否有环)、环形链表(返回入环节点)
主页:HABUO🍁主页:HABUO 🍁如果再也不能见到你,祝你早安,午安,晚安🍁 1.相交链表 题目:给你两个单链表的头节点 headA 和 headB ,请你找出并返回两个单链表…...
hubuctf-2024校赛-复现wp
web easyweb1 <?php error_reporting(0); highlight_file(__FILE__);$flag getenv("GZCTF_FLAG");if(isset($_GET[num])){$num $_GET[num];if(preg_match("/[0-9]/", $num)){die("You are failed.");}if(intval($num)){echo $flag;} } 利…...
.NET Core发布网站报错 HTTP Error 500.31
报错如图: 解决办法: 打开任务管理器》》服务》》找到这仨服务,右键启动即可,如果已经启动了就重启:...
RTSP摄像头、播放器为什么需要支持H.265?
H.264还是H.265? 好多开发者在做选RTSP播放器的时候,经常问我们的问题是,用H.264好还是H.265好?本文我们就H.264 和 H.265的主要区别和适用场景,做个大概的交流。 一、压缩效率 H.265 更高的压缩比 H.265 在相同视频…...
brynet源码阅读——解决文件描述符耗尽问题mIdle
1、背景 在高并发服务器程序中,文件描述符资源是有限的。当一个程序同时处理多个网络连接时,每个连接都会占用一个文件描述符。如果系统没有足够的文件描述符可用,调用 accept()(用于接收新的连接)或其他文件操作时可…...
学习Zookeeper
Zookeeper有手就行 1. 初识ZooKeeper1.1 安装ZooKeeper1.2 ZooKeeper命令操作1.2.1 Zookeeper数据模型1.2.2 Zookeeper 服务端常用命令1.2.3 Zookeeper客户端常用命令 2. ZooKeeperJavaAPl操作2.1 Curator介绍2.2 CuratorAPI常用操作2.2.0 引入Curator支持2.2.1 建立连接2.2.2 …...
css效果
css炫彩流光圆环效果 <!DOCTYPE html> <html><head><meta charset"utf-8" /><title></title><style>*{margin: 0;padding: 0;}body{width: 100%;height: 100vh;}.container{position: relative;width: 100%;height: 100vh…...
【Python爬虫五十个小案例】爬取豆瓣电影Top250
博客主页:小馒头学python 本文专栏: Python爬虫五十个小案例 专栏简介:分享五十个Python爬虫小案例 🪲前言 在这篇博客中,我们将学习如何使用Python爬取豆瓣电影Top250的数据。我们将使用requests库来发送HTTP请求,…...
「Mac玩转仓颉内测版25」基础篇5 - 布尔类型详解
本篇将介绍 Cangjie 中的布尔类型,包括布尔值的定义、运算操作符、逻辑运算、布尔类型的常见应用场景及其在条件判断中的应用,帮助开发者理解和使用布尔类型。 关键词 布尔类型定义布尔运算逻辑运算符条件判断常见应用场景 一、布尔类型概述 布尔类型&…...
electron主进程和渲染进程之间的通信
主进程 (main.js) const { app, BrowserWindow, ipcMain } require("electron"); const path require("node:path"); // 导入fs模块 const fs require("fs");const createWindow () > {const win new BrowserWindow({width: 800,height…...
在 Spring Boot 中实现多种方式登录(用户名、手机号、邮箱等)的不正经指南
欢迎来到一场技术与幽默交织的冒险!今天,我们将跳进 Spring Boot 的世界,探索如何通过 用户名、手机号、邮箱 等多种方式实现登录。想象一下,用户在登录时可以随心所欲地选择——就像你今天早上纠结到底是要喝美式咖啡还是拿铁&am…...
JVM标量替换
JVM标量替换 简单来说 JVM 中的标量替换是一种编译优化技术,将未逃逸对象拆解成不能再分,标量在栈帧或寄存器中分配使用。将对象拆解后直接使用标量,不但避免了完整对象的创建和后续回收流程,而且能更快地获取和操作相应的数据&…...
Python深度学习框架:PyTorch、Keras、Scikit-learn、TensorFlow如何使用?学会轻松玩转AI!
前言 我们先简单了解一下PyTorch、Keras、Scikit-learn和TensorFlow都是什么。 想象一下你要盖一座大房子。你需要砖头、水泥、工具等等,对吧?机器学习也是一样,需要一些工具来帮忙。PyTorch、Keras、Scikit-learn和TensorFlow就是四种不同的…...
C语言蓝桥杯组题目
系列文章目录 文章目录 系列文章目录前言题目第一题.1, 2, 3, 4 能组成多少个互不相同且无重复数字的三位数?都是多少?思路 第二题: 一个整数,它加上100后是一个完全平方数,再加上168又是一个完全平方数,请问该数是多少…...
用Matlab和SIMULINK实现DPCM仿真和双边带调幅系统仿真
1、使用SIMULINK或Matlab实现DPCM仿真 1.1 DPCM原理 差分脉冲编码调制,简称DPCM,主要用于将模拟信号转换为数字信号,同时减少数据的冗余度以实现数据压缩。在DPCM中,信号的每个抽样值不是独立编码的,而是通过预测前一…...
真实网络安全面试场景题
1.公司内部搭建了2台DNS服务器做主辅同步,公司的业务官网地址为 www.chinaddic.com。小明作为网络管理员把域名添加至DNS服务器进行测试。 问题1:使用自己电脑可以正常访问刚添加的域名,但处于同样网络环境同事电脑却访问不了。 出现此问题原因…...