【Java面试】第十天

news2024/9/22 6:55:32

🌟个人主页:时间会证明一切.


目录

  • Spring 中的 Bean 是线程安全的吗?
    • 有状态的Bean如何解决线程安全问题
  • SpringBoot和Spring的区别是什么?
  • SpringBoot的启动流程是怎么样的?
    • new SpringApplication()
    • SpringApplication.run

Spring 中的 Bean 是线程安全的吗?

Spring的Bean是否线程安全,这个要取决于他的作用域。Spring的Bean有多种作用域,其中用的比较多的就是Singleton和Prototype。

默认情况下,Spring Bean 是单例的(Singleton)。这意味着在整个 Spring 容器中只存在一个 Bean 实例。如果将 Bean 的作用域设置为原型的(Prototype) ,那么每次从容器中获取 Bean 时都会创建一个新的实例。

对于Prototype这种作用域的Bean,他的Bean 实例不会被多个线程共享,所以不存在线程安全的问题。

但是对于Singleton的Bean,就可能存在线程安全问题了,但是也不绝对,要看这个Bean中是否有共享变量。

如以下Bean:

@Service
public class CounterService {
    private int count = 0;

    public int increment() {
        return ++count;
    }
}

默认情况下,Spring Bean 是单例的,count字段是一个共享变量,那么如果多个线程同时调用 increment 方法,可能导致计数器的值不正确。那么这段代码就不是线程安全的。

我们通常把上面这种Bean叫做有状态的Bean,有状态的Bean就是非线程安全的,我们需要自己来考虑他的线程安全性问题。

那如果一个Singleton的Bean中是无状态的,即没有成员变量,或者成员变量只读不写,那么他就是个线程安全的。

@Service
public class CounterService {
    
    public int increment(int a) {
        return ++a;
    }
}

所以,总结一下就是:

Prototype的Bean是线程安全的,无状态的Singleton的Bean是线程安全的。有状态的Singleton的Bean是非线程安全的。

有状态的Bean如何解决线程安全问题

想要让一个有状态的Bean变得线程安全,有以下几个做法:

1、修改作用域为Prototype,这样的Bean就可以避免线程安全问题。

@Scope("prototype")
@Service
public class CounterService {

  	private int count = 0;
    // ...
}

但是需要注意,Prototype的bean,每次从容器中请求一个 Prototype Bean 时,都会创建一个新的实例。这可能导致性能开销,特别是在需要频繁创建对象的情况下。 而且,每个 Prototype Bean 的实例都需要占用一定的内存,可能会导致内存资源的消耗较大。

2、加锁

想要实现线程安全,有一个有效的办法就是加锁,在并发修改共享变量的地方加锁:

@Service
public class CounterService {
    private int count = 0;

    public synchronized int increment() {
        return ++count;
    }
}

但是加锁的话会影响并发,降低系统的吞吐量,所以使用的时候需要谨慎,不建议用这个方案。

3、使用并发工具类

可以使用并发包中提供的工具类,如原子类,线程安全的集合等。

import java.util.concurrent.atomic.AtomicInteger;

public class CounterService {
    private AtomicInteger count = new AtomicInteger(0);

    public int increment() {
        return count.incrementAndGet();
    }
}

建议使用这种,既能保证线程安全,又有比较好的性能。

SpringBoot和Spring的区别是什么?

Spring是一个非常强大的企业级Java开发框架(Java的腾飞他居功至伟),提供了一系列模块来支持不同的应用需求,如依赖注入、面向切面编程、事务管理、Web应用程序开发等。而SpringBoot的出现,主要是起到了简化Spring应用程序的开发和部署,特别是用于构建微服务和快速开发的应用程序。

相比于Spring,SpringBoot主要在这几个方面来提升了我们使用Spring的效率,降低开发成本:

1、自动配置:Spring Boot通过Auto-Configuration来减少开发人员的配置工作。我们可以通过依赖一个starter就把一坨东西全部都依赖进来,使开发人员可以更专注于业务逻辑而不是配置。

2、内嵌Web服务器:Spring Boot内置了常见的Web服务器(如Tomcat、Jetty),这意味着您可以轻松创建可运行的独立应用程序,而无需外部Web服务器。

3、约定大于配置:SpringBoot中有很多约定大于配置的思想的体现,通过一种约定的方式,来降低开发人员的配置工作。如他默认读取spring.factories来加载Starter、读取application.properties或application.yml文件来进行属性配置等。

SpringBoot的启动流程是怎么样的?

以下就是一个SpringBoot启动的入口,想要了解SpringBoot的启动过程,就从这里开始。

@SpringBootApplication
public class Application {
    public static void main(String[] args) {
    	SpringApplication.run(Application.class, args);  也可简化调用静态方法
    }
}

这里我们直接看重重点的SpringApplication.run(Application.class, args);方法。他的实现细节如下:

public static ConfigurableApplicationContext run(Object source, String... args) {
    return run(new Object[] { source }, args);
}

public static ConfigurableApplicationContext run(Object[] sources, String[] args) {
    return new SpringApplication(sources).run(args);
}

最终就是new SpringApplication(sources).run(args)这部分代码了。那么接下来就需要分两方面介绍SpringBoot的启动过程。一个是new SpringApplication的初始化过程,一个是SpringApplication.run的启动过程。

new SpringApplication()

在SpringApplication的构造函数中,调用了一个initialize方法,所以他的初始化逻辑直接看这个initialize方法就行了。流程图及代码如下:

image.png

public SpringApplication(Object... sources) {
    initialize(sources);
}

private void initialize(Object[] sources) {
    // 添加源:如果 sources 不为空且长度大于 0,则将它们添加到应用的源列表中
    if (sources != null && sources.length > 0) {
        this.sources.addAll(Arrays.asList(sources));
    }

    // 设置web环境:推断并设置 Web 环境(例如,检查应用是否应该运行在 Web 环境中)
    this.webEnvironment = deduceWebEnvironment();

    // 加载初始化器:设置 ApplicationContext 的初始化器,从 'spring.factories' 文件中加载 ApplicationContextInitializer 实现
    setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class));

    // 设置监听器:从 'spring.factories' 文件中加载 ApplicationListener 实现
    setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));

    // 确定主应用类:通常是包含 main 方法的类
    this.mainApplicationClass = deduceMainApplicationClass();
}

  1. 添加源:将提供的源(通常是配置类)添加到应用的源列表中。
  2. 设置 Web 环境:判断应用是否应该运行在 Web 环境中,这会影响后续的 Web 相关配置。
  3. 加载初始化器:从 spring.factories 文件中加载所有列出的 ApplicationContextInitializer 实现,并将它们设置到 SpringApplication 实例中,以便在应用上下文的初始化阶段执行它们。
  4. 设置监听器:加载和设置 ApplicationListener 实例,以便应用能够响应不同的事件。
  5. 确定主应用类:确定主应用类,这个主应用程序类通常是包含 public static void main(String[] args) 方法的类,是启动整个 Spring Boot 应用的入口点。

这里面第三步,加载初始化器这一步是Spring Boot的自动配置的核心,因为在这一步会从 spring.factories 文件中加载并实例化指定类型的类。

具体实现的代码和流程如下:
image.png

private <T> Collection<? extends T> getSpringFactoriesInstances(Class<T> type, Class<?>[] parameterTypes, Object... args) {
    // 获取当前线程的上下文类加载器
    ClassLoader classLoader = Thread.currentThread().getContextClassLoader();

    // 从spring.factories加载指定类型的工厂名称,并使用LinkedHashSet确保名称的唯一性,以防重复
    Set<String> names = new LinkedHashSet<String>(SpringFactoriesLoader.loadFactoryNames(type, classLoader));

    // 创建指定类型的实例。这里使用反射来实例化类,并传入任何必要的参数
    List<T> instances = createSpringFactoriesInstances(type, parameterTypes, classLoader, args, names);

    // 对实例进行排序,这里使用的是Spring的注解感知比较器,可以处理@Order注解和Ordered接口
    AnnotationAwareOrderComparator.sort(instances);

    // 返回实例集合
    return instances;
}

以下就是new SpringApplication的主要流程,主要依赖initialize 方法初始化 Spring Boot 应用的关键组件和配置。

image.png

这个过程确保了在应用上下文被创建和启动之前,所有关键的设置都已就绪,包括环境设置、初始化器和监听器的配置,以及主应用类的识别。

SpringApplication.run

看完了new SpringApplication接下来就在看看run方法做了哪些事情。这个方法是 SpringApplication 类的核心,用于启动 Spring Boot 应用。

image.png

public ConfigurableApplicationContext run(String... args) {
    // 创建并启动一个计时器,用于记录应用启动耗时
    StopWatch stopWatch = new StopWatch();
    stopWatch.start();

    ConfigurableApplicationContext context = null;
    FailureAnalyzers analyzers = null;

    // 配置无头(headless)属性,影响图形环境的处理
    configureHeadlessProperty();

    // 获取应用运行监听器,并触发开始事件
    SpringApplicationRunListeners listeners = getRunListeners(args);
    listeners.starting();

    try {
        // 创建应用参数对象
        ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
        // 准备环境,包括配置文件和属性源
        ConfigurableEnvironment environment = prepareEnvironment(listeners, applicationArguments);
        // 打印应用的 Banner
        Banner printedBanner = printBanner(environment);
        // 创建应用上下文
        context = createApplicationContext();
        // 创建失败分析器
        analyzers = new FailureAnalyzers(context);
        // 准备上下文,包括加载 bean 定义
        prepareContext(context, environment, listeners, applicationArguments, printedBanner);
        // 刷新上下文,完成 bean 的创建和初始化
        refreshContext(context);
        // 刷新后的后置处理
        afterRefresh(context, applicationArguments);
        // 通知监听器,应用运行完成
        listeners.finished(context, null);
        // 停止计时器
        stopWatch.stop();
        // 如果启用了启动信息日志,记录应用的启动信息
        if (this.logStartupInfo) {
            new StartupInfoLogger(this.mainApplicationClass).logStarted(getApplicationLog(), stopWatch);
        }
        //触发ApplicationStartedEvent 事件
        listeners.started(context);
        //调用实现了 CommandLineRunner 和 ApplicationRunner 接口的 bean 中的 run 方法
		callRunners(context, applicationArguments);
        // 触发 ApplicationReadyEvent 事件
        listeners.running(context);
        // 返回配置好的应用上下文
        return context;
    }
    catch (Throwable ex) {
        // 处理运行失败的情况
        handleRunFailure(context, listeners, analyzers, ex);
        throw new IllegalStateException(ex);
    }
}

以上的过程太复杂了,我们挑几个关键的介绍一下他们的主要作用。

启动&停止计时器:在代码中,用到stopWatch来进行计时。所以在最开始先要启动计时,在最后要停止计时。这个计时就是最终用来统计启动过程的时长的。最终在应用启动信息输出的实时打印出来,如以下内容:

2023-11-18 09:00:05.789  INFO 12345 --- [           main] com.hollis.myapp.Application            : Started Application in 6.666 seconds (JVM running for 7.789)

**获取和启动监听器:**这一步从spring.factories中解析初始所有的SpringApplicationRunListener 实例,并通知他们应用的启动过程已经开始。

SpringApplicationRunListener 是 Spring Boot 中的一个接口,用于在应用的启动过程中的不同阶段提供回调。实现这个接口允许监听并响应应用启动周期中的关键事件。SpringApplicationRunListener 接口定义了多个方法,每个方法对应于启动过程中的一个特定阶段。这些方法包括:

  1. starting():在运行开始时调用,此时任何处理都未开始,可以用于初始化在启动过程中需要的资源。
  2. environmentPrepared():当 SpringApplication 准备好 Environment(但在创建 ApplicationContext 之前)时调用,这是修改应用环境属性的好时机。
  3. contextPrepared():当 ApplicationContext 准备好但在它加载之前调用,可以用于对上下文进行一些预处理。
  4. contextLoaded():当 ApplicationContext 被加载(但在它被刷新之前)时调用,这个阶段所有的 bean 定义都已经加载但还未实例化。
  5. started():在 ApplicationContext 刷新之后、任何应用和命令行运行器被调用之前调用,此时应用已经准备好接收请求。
  6. running():在运行器被调用之后、应用启动完成之前调用,这是在应用启动并准备好服务请求时执行某些动作的好时机。
  7. failed():如果启动过程中出现异常,则调用此方法。

**装配环境参数:**这一步主要是用来做参数绑定的,prepareEnvironment 方法会加载应用的外部配置。这包括 application.properties 或 application.yml 文件中的属性,环境变量,系统属性等。所以,我们自定义的那些参数就是在这一步被绑定的。

**打印Banner:**这一步的作用很简单,就是在控制台打印应用的启动横幅Banner。如以下内容:

  .   ____          _            __ _ _
 /\\ / ___'_ __ _ _(_)_ __  __ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
 \\/  ___)| |_)| | | | | || (_| |  ) ) ) )
  '  |____| .__|_| |_|_| |_\__, | / / / /
 =========|_|==============|___/=/_/_/_/
 :: Spring Boot ::                (v3.1.5)

**创建应用上下文:**到这一步就真的开始启动了,第一步就是先要创建一个Spring的上下文出来,只有有了这个上下文才能进行Bean的加载、配置等工作。

准备上下文:这一步非常关键,很多核心操作都是在这一步完成的:

private void prepareContext(ConfigurableApplicationContext context, ConfigurableEnvironment environment,
        SpringApplicationRunListeners listeners, ApplicationArguments applicationArguments, Banner printedBanner) {

    // 将environment设置到应用上下文中
    context.setEnvironment(environment);

    // 对应用上下文进行后处理(可能涉及一些自定义逻辑)
    postProcessApplicationContext(context);

    // 应用所有的ApplicationContextInitializer
    applyInitializers(context);

    // 通知监听器上下文准备工作已完成
    listeners.contextPrepared(context);

    // 如果启用了启动信息日志,则记录启动信息和配置文件信息
    if (this.logStartupInfo) {
        logStartupInfo(context.getParent() == null);
        logStartupProfileInfo(context);
    }

    // 向上下文中添加特定于 Spring Boot 的单例 Bean
    context.getBeanFactory().registerSingleton("springApplicationArguments", applicationArguments);
    if (printedBanner != null) {
        context.getBeanFactory().registerSingleton("springBootBanner", printedBanner);
    }

    // 加载应用的源(如配置类)
    Set<Object> sources = getSources();
    Assert.notEmpty(sources, "Sources must not be empty");
    load(context, sources.toArray(new Object[sources.size()]));

    // 通知监听器上下文加载已完成
    listeners.contextLoaded(context);
}

在这一步,会打印启动的信息日志,主要内容如下:

2023-11-18 09:00:00.123  INFO 12345 --- [           main] com.example.myapp.Application            : Starting Application v0.1.0 on MyComputer with PID 12345 (started by user in /path/to/app)

刷新上下文:这一步,是Spring启动的核心步骤了,这一步骤包括了实例化所有的 Bean、设置它们之间的依赖关系以及执行其他的初始化任务。

@Override
public void refresh() throws BeansException, IllegalStateException {
	synchronized (this.startupShutdownMonitor) {
		// 为刷新操作准备此上下文
		prepareRefresh();

		// 告诉子类刷新内部 bean 工厂
		ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();

		// 为在此上下文中使用做好 bean 工厂的准备工作
		prepareBeanFactory(beanFactory);

		try {
			// 允许在上下文子类中对 bean 工厂进行后处理
			postProcessBeanFactory(beanFactory);

			// 调用在上下文中注册为 bean 的工厂处理器
			invokeBeanFactoryPostProcessors(beanFactory);

			// 注册拦截 bean 创建的 bean 处理器
			registerBeanPostProcessors(beanFactory);

			// 初始化此上下文的消息源
			initMessageSource();

			// 初始化此上下文的事件多播器
			initApplicationEventMulticaster();

			// 在特定上下文子类中初始化其他特殊 bean
			onRefresh();

			// 检查监听器 bean 并注册它们
			registerListeners();

			// 实例化所有剩余的(非懒加载)单例
			finishBeanFactoryInitialization(beanFactory);

			// 最后一步:发布相应的事件
			finishRefresh();
		}

		catch (BeansException ex) {
			if (logger.isWarnEnabled()) {
				logger.warn("Exception encountered during context initialization - " +
						"cancelling refresh attempt: " + ex);
			}

			// 销毁已经创建的单例以避免悬挂资源
			destroyBeans();

			// 重置“激活”标志
			cancelRefresh(ex);

			// 将异常传播给调用者
			throw ex;
		}

		finally {
			// 在 Spring 的核心中重置常见的内省缓存,因为我们可能不再需要单例 bean 的元数据...
			resetCommonCaches();
		}
	}
}

所以,这一步中,主要就是创建BeanFactory,然后再通过BeanFactory来实例化Bean。

但是,很多人都会忽略一个关键的步骤(网上很多介绍SpringBoot启动流程的都没提到),那就是Web容器的启动,及Tomcat的启动其实也是在这个步骤。

在refresh-> onRefresh中,这里会调用到ServletWebServerApplicationContext的onRefresh中:

@Override
protected void onRefresh() {
    super.onRefresh();
    try {
        createWebServer();
    }
    catch (Throwable ex) {
        throw new ApplicationContextException("Unable to start web server", ex);
    }
}


private void createWebServer() {
    WebServer webServer = this.webServer;
    ServletContext servletContext = getServletContext();
    if (webServer == null && servletContext == null) {
        StartupStep createWebServer = getApplicationStartup().start("spring.boot.webserver.create");
        ServletWebServerFactory factory = getWebServerFactory();
        createWebServer.tag("factory", factory.getClass().toString());
        this.webServer = factory.getWebServer(getSelfInitializer());
        createWebServer.end();
        getBeanFactory().registerSingleton("webServerGracefulShutdown",
                new WebServerGracefulShutdownLifecycle(this.webServer));
        getBeanFactory().registerSingleton("webServerStartStop",
                new WebServerStartStopLifecycle(this, this.webServer));
    }
    else if (servletContext != null) {
        try {
            getSelfInitializer().onStartup(servletContext);
        }
        catch (ServletException ex) {
            throw new ApplicationContextException("Cannot initialize servlet context", ex);
        }
    }
    initPropertySources();
}

这里面的createWebServer方法中,调用到factory.getWebServer(getSelfInitializer());的时候,factory有三种实现,分别是JettyServletWebServerFactory、TomcatServletWebServerFactory、UndertowServletWebServerFactory这三个,默认使用TomcatServletWebServerFactory。

TomcatServletWebServerFactory的getWebServer方法如下,这里会创建一个Tomcat

@Override
public WebServer getWebServer(ServletContextInitializer... initializers) {
    if (this.disableMBeanRegistry) {
        Registry.disableRegistry();
    }
    Tomcat tomcat = new Tomcat();
    File baseDir = (this.baseDirectory != null) ? this.baseDirectory : createTempDir("tomcat");
    tomcat.setBaseDir(baseDir.getAbsolutePath());
    for (LifecycleListener listener : this.serverLifecycleListeners) {
        tomcat.getServer().addLifecycleListener(listener);
    }
    Connector connector = new Connector(this.protocol);
    connector.setThrowOnFailure(true);
    tomcat.getService().addConnector(connector);
    customizeConnector(connector);
    tomcat.setConnector(connector);
    tomcat.getHost().setAutoDeploy(false);
    configureEngine(tomcat.getEngine());
    for (Connector additionalConnector : this.additionalTomcatConnectors) {
        tomcat.getService().addConnector(additionalConnector);
    }
    prepareContext(tomcat.getHost(), initializers);
    return getTomcatWebServer(tomcat);
}


在最后一步getTomcatWebServer(tomcat);的代码中,会创建一个TomcatServer,并且把他启动:

protected TomcatWebServer getTomcatWebServer(Tomcat tomcat) {
    return new TomcatWebServer(tomcat, getPort() >= 0, getShutdown());
}


public TomcatWebServer(Tomcat tomcat, boolean autoStart, Shutdown shutdown) {
    Assert.notNull(tomcat, "Tomcat Server must not be null");
    this.tomcat = tomcat;
    this.autoStart = autoStart;
    this.gracefulShutdown = (shutdown == Shutdown.GRACEFUL) ? new GracefulShutdown(tomcat) : null;
    initialize();
}


接下来在initialize中完成了tomcat的启动。

image.png

最后,SpringBoot的启动过程主要流程如下:

image.png


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

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

相关文章

【Android 13源码分析】WindowContainer窗口层级-2-构建流程

在安卓源码的设计中&#xff0c;将将屏幕分为了37层&#xff0c;不同的窗口将在不同的层级中显示。 对这一块的概念以及相关源码做了详细分析&#xff0c;整理出以下几篇。 【Android 13源码分析】WindowContainer窗口层级-1-初识窗口层级树 【Android 13源码分析】WindowCon…

ArrayList 源码解析

ArrayList是Java集合框架中的一个动态数组实现&#xff0c;提供了可变大小的数组功能。它继承自AbstractList并实现了List接口&#xff0c;是顺序容器&#xff0c;即元素存放的数据与放进去的顺序相同&#xff0c;允许放入null元素&#xff0c;底层通过数组实现。除该类未实现同…

模板替换引擎(支持富文本动态表格)

模板替换引擎&#xff08;支持富文本动态表格&#xff09; 前言功能介绍example&#xff1a; 使用方法函数扩展系统函数自定义函数 前言 分享一下自己开源的工具——模板替换引擎 https://github.com/RwTo/template-engine 可以拿来学习设计模式或使用 感兴趣的话&#xff…

网络编程基础概述

文章目录 协议网络协议栈(osi)局域网IPIP和Mac地址端口号TCP和UDP网络字节序 协议 (网络协议的)意义:为了让计算机传输之间将信息正确传输给目标机器 不同系统之间能接入网络是因为定制了一套通用的协议以便支持不同系统间的网络通信 1.网络通信的问题: 将数据可靠的从A传给B a…

AtCoder ABC369 A-D题解

比赛链接:ABC369 省流&#xff1a;A<B<D<C&#xff08;题解是按照该顺序写的&#xff09; Problem A: #include <bist/stdc.h> using namespace std; int main(){int A,B;cin>>A>>B;if(AB)cout<<1<<endl;else if(abs(A-B)%20)cout&l…

织物缺陷检测系统源码分享

织物缺陷检测检测系统源码分享 [一条龙教学YOLOV8标注好的数据集一键训练_70全套改进创新点发刊_Web前端展示] 1.研究背景与意义 项目参考AAAI Association for the Advancement of Artificial Intelligence 项目来源AACV Association for the Advancement of Computer Vis…

1405 问题 E: 世界杯

废话 这个题&#xff0c;我估计 22 22 22 年的时候写过一次&#xff0c;当时应该是搞明白了&#xff0c;现在重新写还是不会写&#xff0c;有点无奈 题目 问题 E: 世界杯&#xff1a;现在的 OJ 把题目加到一个活动里面去之后&#xff0c;感觉之后这个链接就访问不了了。题目…

伙房食堂电气安全新挑战:油烟潮湿环境下,如何筑起电气火灾“防火墙”?

近几年&#xff0c;随着我国经济的飞速发展&#xff0c;食堂餐饮也经历了一场变革&#xff0c;越来越多的电器走进了伙房食堂中&#xff0c;实现了电气化&#xff0c;为人们提供了高效便利的饮食服务&#xff0c;但同时也增加了火灾负荷。目前我国非常严重的电气火灾危害&#…

【STM32】DAC数字模拟转换

本篇博客重点在于标准库函数的理解与使用&#xff0c;搭建一个框架便于快速开发 目录 前言 DAC简介 DAC配置 DAC时钟使能 GPIO初始化 DAC配置 DAC使能 读写DAC值 驱动代码 MyDAC.h MyDAC.c main.c 前言 大容量的STM32F101xx和STM32F103xx产品才有DAC外设 大容量…

几种mfc140u.dll常见错误情况,以及mfc140u.dll文件修复的方法

如果你遇到与mfc140u.dll 文件相关的错误&#xff0c;这通常指的是该mfc140u.dll文件可能丢失、损坏或与您的应用程序不兼容。详细分析关于mfc140u.dll文件错误会对系统有什么影响&#xff0c;mfc140u.dll文件处于什么样的位置&#xff1f;以下是几种常见的错误情况及其修复方法…

R语言统计分析——功效分析(作图)

参考资料&#xff1a;R语言实验【第2版】 假设对于相关系数统计显著性检验&#xff0c;我们想计算一系列效应值和功效水平下所需的样本量&#xff0c;此时可用pwr.r.test()函数和for循环来完成任务。 # 加载pwr包 library(pwr) # 设置各效应值 r<-seq(0.1,0.5,0.01) # 计算…

基于JAVA的实验室智慧管理平台的设计与实现

大型管理系统用传统的手工操作方式进行信息管理容易出现数据丢失&#xff0c;统计错误等问题&#xff0c;而且劳动强度高&#xff0c;速度慢&#xff0c;耗费人力&#xff0c;物力。使用计算机可以高效、准确地完成以上工作。因此&#xff0c;用计算机的高速度和自动化来替代手…

蓝牙模块—BLE-CC41-A

1. 蓝牙的特点 蓝牙模块采用的 TI 公司设计的 CC2541芯片&#xff0c;主要面向低功耗蓝牙通信方案&#xff0c;该模块的工作频段为 2.4Ghz&#xff0c;这个频段属于国际通用频段 注意&#xff1a;蓝牙集成了一个状态指示灯&#xff0c;LED灯如果均匀慢速闪烁&#xff0c;就表示…

接口测试从入门到精通项目实战

视频网址&#xff1a;2024最新接口测试从入门到精通项目实战&#xff08;全套接口测试教程&#xff09;_哔哩哔哩_bilibili 接口架构设计分析 http协议详解 JMeter 目录介绍 常用组件 执行接口测试 接口文档&#xff1a;tlias智能学习辅助系统接口文档-V1.0-CSDN博客 实战 前…

HTB-Lame(msf利用)

前言 各位师傅大家好&#xff0c;我是qmx_07&#xff0c;今天给大家讲解 初学者渗透路径 第一个靶机Lame 渗透过程 信息搜集 服务器开放了21FTP端口、22SSH端口、445SMB端口 利用MSF模块 登录主机 工具介绍&#xff1a;Metasploit Framework&#xff08;简称 MSF&#xf…

RK3562/3588系列之6—yolov5模型的部署

RK3562/3588系列之6—yolov5模型的部署 1.yolov5模型训练2.训练好的模型转成onnx格式3.模型从onnx格式转RKNN3.1 onnx2rknn.py3.2 onnx2rknn.py3.3 直接使用rknn.api3.4 rknn_model_zoo中的转换代码3.5 LubanCat-RK系列板卡官方资料4.RK NPU c++推理4.1交叉编译4.2 开发板执行编…

校园失物招领小程序

校园失物招领小程序 weixin167校园失物招领小程序ssm 目 录 目 录 I 摘 要 III ABSTRACT IV 1 绪论 1 1.1 课题背景 1 1.2 研究现状 1 1.3 研究内容 2 2 系统开发环境 3 2.1 JSP技术 3 2.2 JAVA技术 3 2.3 MYSQL数据库 3 2.4 B/S结构 4 2.5 SSM框架技术 4 3 系…

清理C盘缓存,如何针对Windows10系统,专业地调整和优化C盘缓存设置

在Windows10系统中&#xff0c;合理地管理C盘&#xff08;通常是系统盘&#xff09;的缓存设置&#xff0c;对于保持系统性能、提升响应速度以及避免磁盘空间不足等问题至关重要。缓存主要涉及到系统文件、临时文件、应用程序缓存等多个方面。下面将详细介绍如何针对Windows10系…

【webpack4系列】webpack进阶用法(三)

文章目录 自动清理构建目录产物PostCSS插件autoprefixer自动补齐CSS3前缀移动端CSS px自动转换成rem静态资源内联多页面应用打包通用方案使用sourcemap提取页面公共资源基础库分离利⽤ SplitChunksPlugin 进⾏公共脚本分离利⽤ SplitChunksPlugin 分离基础包利⽤ SplitChunksPl…

UVA1395 Slim Span(最小生成树)

*原题链接*(洛谷) 非常水的一道题。看见让求最小边权差值的生成树&#xff0c;很容易想到kruskal。 一个暴力的想法是以每条边为最小边跑一遍kruskal&#xff0c;然后统计答案。时间复杂度&#xff0c;再看题中很小的数据范围和3s的时限。最后还真就过了。 不过我天真的想了…