SpringBoot源码分析

文章目录

  • SpringBoot 源码分析
  • 一、源码分析 - 自动装配
    • 1、@SpringBootApplication
    • 2、@EnableAutoConfiguration
    • 3、AutoConfigurationImportSelector
    • 4、SpringFactoriesLoader
    • 5、META-INF/spring.factories
    • 6、SpringMVC相关装配
  • 二、源码分析 - 启动加载
    • 1、SpringApplication - 静态run
    • 2、SpringApplication - 构造方法
    • 3、SpringApplication - 实例run
    • 4、IoC容器的初始化
    • 5、内置Tomcat原理
  • 三、自动装配 - 相关注解
    • 1、自动装配 - 条件注解
    • 2、自动装配 - 顺序注解
  • 四、自定义 starter
    • 1、pom文件
    • 2、Properties实体类
    • 3、Config配置类
    • 4、spring.factories
    • 5、使用自动装配

SpringBoot 源码分析

一、源码分析 - 自动装配

1、@SpringBootApplication

// 自动装配的开始
@SpringBootApplication
public class BasicProjectApplication {public static void main(String[] args) {SpringApplication.run(BasicProjectApplication.class, args);}
}
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
// @Configuration的子注解
@SpringBootConfiguration
// 开启自动装配
@EnableAutoConfiguration
// 开启扫描机制,扫描启动类所在包(但是不包含类型排除和自动配置的Filter)
@ComponentScan(excludeFilters = { @Filter(type = FilterType.CUSTOM, classes = TypeExcludeFilter.class),@Filter(type = FilterType.CUSTOM, classes = AutoConfigurationExcludeFilter.class) 
})
public @interface SpringBootApplication {.....}

2、@EnableAutoConfiguration

package org.springframework.boot.autoconfigure;@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@AutoConfigurationPackage
// 通过 @Import 引入 AutoConfigurationImportSelector 
@Import({AutoConfigurationImportSelector.class})
public @interface EnableAutoConfiguration {String ENABLED_OVERRIDE_PROPERTY = "spring.boot.enableautoconfiguration";Class<?>[] exclude() default {};String[] excludeName() default {};
}

3、AutoConfigurationImportSelector

ImportSelector 接口用于实现动态地选择需要被导入到容器中的配置类的逻辑。

public interface ImportSelector {/*** @param importingClassMetadata 用于描述使用了 @Import 注解的配置类的注解元数据。* @return 数组中的每个元素都是一个全限定类名,表示需要被导入到容器中的配置类。*/ String[] selectImports(AnnotationMetadata importingClassMetadata);@Nullabledefault Predicate<String> getExclusionFilter() {return null;}
}

AutoConfigurationImportSelector 实现了 ImportSelector 接口,并在 selectImports 方法中根据特定的条件或逻辑来选择需要导入的配置类,实现更加灵活和动态的配置。

public class AutoConfigurationImportSelector implements DeferredImportSelector, BeanClassLoaderAware, ResourceLoaderAware, BeanFactoryAware, EnvironmentAware, Ordered {// 这里的参数 annotationMetadata,就是 @EnableAutoConfiguration 注解的元数据public String[] selectImports(AnnotationMetadata annotationMetadata) {if (!this.isEnabled(annotationMetadata)) {return NO_IMPORTS;} else {AutoConfigurationEntry autoConfigurationEntry = // 获取自动配置类this.getAutoConfigurationEntry(annotationMetadata);return StringUtils.toStringArray(autoConfigurationEntry.getConfigurations());}}protected static class AutoConfigurationEntry {private final List<String> configurations;	// 需要导入的private final Set<String> exclusions;		// 不需要导入的}protected AutoConfigurationEntry getAutoConfigurationEntry(AnnotationMetadata annotationMetadata) {if (!this.isEnabled(annotationMetadata)) {return EMPTY_ENTRY;} else {AnnotationAttributes attributes = this.getAttributes(annotationMetadata);List<String> configurations = // 获取所有候选的配置类this.getCandidateConfigurations(annotationMetadata, attributes);configurations = this.removeDuplicates(configurations);Set<String> exclusions = this.getExclusions(annotationMetadata, attributes);this.checkExcludedClasses(configurations, exclusions);configurations.removeAll(exclusions);configurations = this.getConfigurationClassFilter().filter(configurations);this.fireAutoConfigurationImportEvents(configurations, exclusions);return new AutoConfigurationEntry(configurations, exclusions);}}protected List<String> getCandidateConfigurations(AnnotationMetadata metadata, AnnotationAttributes attributes) {// 获取所有配置类的类全名List<String> configurations = SpringFactoriesLoader.loadFactoryNames(this.getSpringFactoriesLoaderFactoryClass(),	// EnableAutoConfiguration.classthis.getBeanClassLoader());Assert.notEmpty(configurations, "No auto configuration classes found in META-INF/spring.factories. If you are using a custom packaging, make sure that file is correct.");return configurations;}protected Class<?> getSpringFactoriesLoaderFactoryClass() {return EnableAutoConfiguration.class;}}

4、SpringFactoriesLoader

// org.springframework.core.io.support.SpringFactoriesLoader/*** 这里的 factoryType 就是 EnableAutoConfiguration.class*/
public static List<String> loadFactoryNames(Class<?> factoryType, @Nullable ClassLoader classLoader) {// org.springframework.boot.autoconfigure.EnableAutoConfigurationString factoryTypeName = factoryType.getName();// 获取 META-INF/spring.factories 文件中 EnableAutoConfiguration 对应的配置类的类全名return (List)loadSpringFactories(classLoader).getOrDefault(factoryTypeName, Collections.emptyList());
}private static Map<String, List<String>> loadSpringFactories(@Nullable ClassLoader classLoader) {MultiValueMap<String, String> result = (MultiValueMap)cache.get(classLoader);if (result != null) {return result;} else {try {// 扫描jar包路径下的 META-INF/spring.factories 文件Enumeration<URL> urls = classLoader != null ? classLoader.getResources("META-INF/spring.factories") : ClassLoader.getSystemResources("META-INF/spring.factories");// 把 META-INF/spring.factories 解析成 MapMultiValueMap<String, String> result = new LinkedMultiValueMap();// ...cache.put(classLoader, result);return result;} catch (IOException var13) {throw new IllegalArgumentException("Unable to load factories from location [META-INF/spring.factories]", var13);}}
}

5、META-INF/spring.factories

下面看一下 META-INF/spring.factories 里的 EnableAutoConfiguration 对应的配置类

在这里插入图片描述

我们在里面挑一个常用的 DataSourceAutoConfiguration类看一下

@Configuration(proxyBeanMethods = false)
@ConditionalOnClass({DataSource.class, EmbeddedDatabaseType.class})
@ConditionalOnMissingBean(type = {"io.r2dbc.spi.ConnectionFactory"})
@EnableConfigurationProperties({DataSourceProperties.class})
@Import({DataSourcePoolMetadataProvidersConfiguration.class, DataSourceInitializationConfiguration.class})
public class DataSourceAutoConfiguration {}
@ConfigurationProperties(prefix = "spring.datasource")
public class DataSourceProperties implements BeanClassLoaderAware, InitializingBean {private String driverClassName;private String url;private String username;private String password;// ...
}

通过 @ConfigurationProperties + @EnableConfigurationProperties 自动读取配置文件中的配置

spring:datasource:driver-class-name: com.mysql.jdbc.Driverurl: .....username: rootpassword: root

6、SpringMVC相关装配

在这里插入图片描述

@Configuration(proxyBeanMethods = false)
@ConditionalOnWebApplication(type = Type.SERVLET)
@ConditionalOnClass({Servlet.class, DispatcherServlet.class, WebMvcConfigurer.class})
@ConditionalOnMissingBean({WebMvcConfigurationSupport.class})
@AutoConfigureOrder(-2147483638)
// 在 DispatcherServletAutoConfiguration 配置类之后配置
@AutoConfigureAfter({DispatcherServletAutoConfiguration.class, TaskExecutionAutoConfiguration.class, ValidationAutoConfiguration.class})
public class WebMvcAutoConfiguration {// MVC的相关的配置.....
}
@AutoConfigureOrder(Integer.MIN_VALUE)
@Configuration(proxyBeanMethods = false)
@ConditionalOnWebApplication(type = Type.SERVLET)
@ConditionalOnClass({DispatcherServlet.class})
// 在 ServletWebServerFactoryAutoConfiguration 配置类之后配置
@AutoConfigureAfter({ServletWebServerFactoryAutoConfiguration.class})
public class DispatcherServletAutoConfiguration {@Configuration(proxyBeanMethods = false)@Conditional({DefaultDispatcherServletCondition.class})@ConditionalOnClass({ServletRegistration.class})@EnableConfigurationProperties({WebMvcProperties.class})protected static class DispatcherServletConfiguration {protected DispatcherServletConfiguration() {}// 前端控制器 DispatcherServlet@Bean(name = {"dispatcherServlet"})public DispatcherServlet dispatcherServlet(WebMvcProperties webMvcProperties) {DispatcherServlet dispatcherServlet = new DispatcherServlet();// ...return dispatcherServlet;}// 文件上传相关bean@Bean@ConditionalOnBean({MultipartResolver.class})@ConditionalOnMissingBean(name = {"multipartResolver"})public MultipartResolver multipartResolver(MultipartResolver resolver) {return resolver;}}
}
@Configuration(proxyBeanMethods = false)
@AutoConfigureOrder(Integer.MIN_VALUE)
@ConditionalOnClass({ServletRequest.class})
@ConditionalOnWebApplication(type = Type.SERVLET)
@EnableConfigurationProperties({ServerProperties.class})
@Import({BeanPostProcessorsRegistrar.class, ServletWebServerFactoryConfiguration.EmbeddedTomcat.class,	// 内置TomcatServletWebServerFactoryConfiguration.EmbeddedJetty.class, ServletWebServerFactoryConfiguration.EmbeddedUndertow.class})
public class ServletWebServerFactoryAutoConfiguration {...}

二、源码分析 - 启动加载

1、SpringApplication - 静态run

看完了自动装配,我们继续看一下启动加载,还是从启动类开始

@SpringBootApplication
public class BasicProjectApplication {public static void main(String[] args) {SpringApplication.run(BasicProjectApplication.class, args);}
}
// org.springframework.boot.SpringApplicationpublic static ConfigurableApplicationContext run(Class<?> primarySource, String... args) {return run(new Class[]{primarySource}, args);
}public static ConfigurableApplicationContext run(Class<?>[] primarySources, String[] args) {// 构建 SpringApplication实例 并执行run方法return (new SpringApplication(primarySources)).run(args);
}

可以看到,启动类中的 SpringApplication.run ,最终会先构建 SpringApplication实例,再执行实例的run方法。

2、SpringApplication - 构造方法

// org.springframework.boot.SpringApplication// SpringApplication的构造方法
public SpringApplication(Class<?>... primarySources) {this((ResourceLoader)null, primarySources);
}public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) {this.sources = new LinkedHashSet();this.bannerMode = Mode.CONSOLE;this.logStartupInfo = true;this.addCommandLineProperties = true;this.addConversionService = true;this.headless = true;this.registerShutdownHook = true;this.additionalProfiles = new HashSet();this.isCustomEnvironment = false;this.lazyInitialization = false;// 资源加载器this.resourceLoader = resourceLoader;// primarySources 就是 启动类Assert.notNull(primarySources, "PrimarySources must not be null");this.primarySources = new LinkedHashSet(Arrays.asList(primarySources));// 判断Web程序的类型(无、基于Servlet的、基于响应式编程模型的)this.webApplicationType = WebApplicationType.deduceFromClasspath();// getSpringFactoriesInstances 就是获取 META-INF/spring.factories 中对应类的实例this.setInitializers(this.getSpringFactoriesInstances(ApplicationContextInitializer.class));this.setListeners(this.getSpringFactoriesInstances(ApplicationListener.class));// 推断 Spring Boot 应用程序的主类this.mainApplicationClass = this.deduceMainApplicationClass();
}private <T> Collection<T> getSpringFactoriesInstances(Class<T> type) {return this.getSpringFactoriesInstances(type, new Class[0]);
}private <T> Collection<T> getSpringFactoriesInstances(Class<T> type, Class<?>[] parameterTypes, Object... args) {ClassLoader classLoader = this.getClassLoader();// loadFactoryNames应该很熟悉了,在「自动装配」里边讲过Set<String> names = new LinkedHashSet(SpringFactoriesLoader.loadFactoryNames(type, classLoader));// 构建实例List<T> instances = this.createSpringFactoriesInstances(type, parameterTypes, classLoader, args, names);// 排序返回AnnotationAwareOrderComparator.sort(instances);return instances;
}

3、SpringApplication - 实例run

构建完 SpringApplication 实例之后,就开始调用实例的run方法了

// org.springframework.boot.SpringApplicationpublic static ConfigurableApplicationContext run(Class<?>[] primarySources, String[] args) {return (new SpringApplication(primarySources)).run(args);
}public ConfigurableApplicationContext run(String... args) {StopWatch stopWatch = new StopWatch();stopWatch.start();// Spring容器ConfigurableApplicationContext context = null;Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList();this.configureHeadlessProperty();// 开始监听SpringApplicationRunListeners listeners = this.getRunListeners(args);listeners.starting();Collection exceptionReporters;try {ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);// 准备EnvironmentConfigurableEnvironment environment = this.prepareEnvironment(listeners, applicationArguments);this.configureIgnoreBeanInfo(environment);// 打印程序启动时的横幅标语Banner printedBanner = this.printBanner(environment);// 构建Spring容器context = this.createApplicationContext();exceptionReporters = this.getSpringFactoriesInstances(SpringBootExceptionReporter.class, new Class[]{ConfigurableApplicationContext.class}, context);// 准备IoC容器this.prepareContext(context, environment, listeners, applicationArguments, printedBanner);// IoC容器的初始化this.refreshContext(context);// IoC容器初始化之后的一些操作this.afterRefresh(context, applicationArguments);stopWatch.stop();if (this.logStartupInfo) {(new StartupInfoLogger(this.mainApplicationClass)).logStarted(this.getApplicationLog(), stopWatch);}listeners.started(context);this.callRunners(context, applicationArguments);} catch (Throwable var10) {this.handleRunFailure(context, var10, exceptionReporters, listeners);throw new IllegalStateException(var10);}try {listeners.running(context);return context;} catch (Throwable var9) {this.handleRunFailure(context, var9, exceptionReporters, (SpringApplicationRunListeners)null);throw new IllegalStateException(var9);}
}

4、IoC容器的初始化

在执行 SpringApplication 实例的run方法时,通过refreshContext方法进行IoC容器的初始化

// org.springframework.boot.SpringApplicationprivate void refreshContext(ConfigurableApplicationContext context) {refresh((ApplicationContext) context);if (this.registerShutdownHook) {try {context.registerShutdownHook();}catch (AccessControlException ex) {// Not allowed in some environments.}}
}@Deprecated
protected void refresh(ApplicationContext applicationContext) {Assert.isInstanceOf(ConfigurableApplicationContext.class, applicationContext);refresh((ConfigurableApplicationContext) applicationContext);
}protected void refresh(ConfigurableApplicationContext applicationContext) {// IoC容器的初始化applicationContext.refresh();
}

然后,就到我们熟悉的 refresh() 方法了,总共有12大步骤(这属于Spring源码的内容,这里就不展开了)

// org.springframework.context.support.AbstractApplicationContext@Override
public void refresh() throws BeansException, IllegalStateException {synchronized (this.startupShutdownMonitor) {// Prepare this context for refreshing.prepareRefresh();// Tell the subclass to refresh the internal bean factory.ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();// Prepare the bean factory for use in this context.prepareBeanFactory(beanFactory);try {// Allows post-processing of the bean factory in context subclasses.postProcessBeanFactory(beanFactory);// Invoke factory processors registered as beans in the context.invokeBeanFactoryPostProcessors(beanFactory);// Register bean processors that intercept bean creation.registerBeanPostProcessors(beanFactory);// Initialize message source for this context.initMessageSource();// Initialize event multicaster for this context.initApplicationEventMulticaster();// Initialize other special beans in specific context subclasses.onRefresh();// Check for listener beans and register them.registerListeners();// Instantiate all remaining (non-lazy-init) singletons.finishBeanFactoryInitialization(beanFactory);// Last step: publish corresponding event.finishRefresh();} catch (BeansException ex) {// logger...// Destroy already created singletons to avoid dangling resources.destroyBeans();// Reset 'active' flag.cancelRefresh(ex);// Propagate exception to caller.throw ex;}finally {// Reset common introspection caches in Spring's core, since we// might not ever need metadata for singleton beans anymore...resetCommonCaches();}}
}

5、内置Tomcat原理

我们看到 refresh() 方法12大步骤中的 onRefresh() 方法,默认是空实现

// org.springframework.context.support.AbstractApplicationContextprotected void onRefresh() throws BeansException {// For subclasses: do nothing by default.
}

ServletWebServerApplicationContext 重写了onRefresh方法:

// org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext@Override
protected void onRefresh() {super.onRefresh();try {// tomcat容器就是在这里创建并启动的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) {ServletWebServerFactory factory = getWebServerFactory();// 创建web容器this.webServer = factory.getWebServer(getSelfInitializer());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();
}
// org.springframework.boot.web.embedded.tomcat.TomcatServletWebServerFactory@Override
public WebServer getWebServer(ServletContextInitializer... initializers) {if (this.disableMBeanRegistry) {Registry.disableRegistry();}// 创建TomcatTomcat tomcat = new Tomcat();File baseDir = (this.baseDirectory != null) ? this.baseDirectory : createTempDir("tomcat");tomcat.setBaseDir(baseDir.getAbsolutePath());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);// 启动Tomcatreturn getTomcatWebServer(tomcat);
}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();
}private void initialize() throws WebServerException {logger.info("Tomcat initialized with port(s): " + getPortsDescription(false));synchronized (this.monitor) {try {// ...this.tomcat.start();	// 启动Tomcat// ...} // ...}
}

三、自动装配 - 相关注解

1、自动装配 - 条件注解

@Conditional 是 Spring 框架中的一个元注解,根据注解的条件来决定是否应该应用某个配置类或组件。

Spring 还提供了许多子注解,用于更精细地定义条件。

在这里插入图片描述

常用注解描述
@ConditionalOnBean容器中存在指定的 Bean 时,配置才会生效。
@ConditionalOnMissingBean容器中不存在指定的 Bean 时,配置才会生效。
@ConditionalOnClass当指定的类存在时,配置才会生效。
@ConditionalOnMissingClass当指定的类不存在时,配置才会生效。
@ConditionalOnWebApplication只有在web环境下,配置才会生效。
@ConditionalOnNotWebApplication只有在非web环境下,配置才会生效。
@ConditionalOnJava系统的Java版本符合需求,配置才会生效。
@ConditionalOnExpression满足指定的SpEL表达式,配置才会生效。
@ConditionalOnProperty指定的属性有指定的值,配置才会生效。
@ConditionalOnResource类路径下存在指定资源文件,配置才会生效。
@ConditionalOnSingleCandidate容器中只有一个指定的Bean,或Bean是首选Bean

2、自动装配 - 顺序注解

注解描述
@AutoConfigureAfter在指定配置类加载之后加载
@AutoConfigureBefore在指定配置类加载之前加载
@AutoConfigureOrder指定加载配置的优先级,默认0

四、自定义 starter

看完自动装配的源码,我们可以尝试自定义 starter,来实现自动装配。

1、pom文件

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd"><modelVersion>4.0.0</modelVersion><!-- 定义starter的坐标 --><groupId>jwt-utils</groupId><artifactId>jwt-spring-boot-starter</artifactId><version>1.0.0-SNAPSHOT</version><name>jwt-spring-boot-starter</name></project>

2、Properties实体类

@Data
@ConfigurationProperties(prefix = "auth")
public class ClientProperties {/*** 客户端名称*/private String clientId;/*** 客户端秘钥*/private String secret;/*** 拦截器拦截路径*/private List<String> includeFilterPaths;/*** 拦截器放行路径*/private List<String> excludeFilterPaths;
}

3、Config配置类

@Slf4j
@Configuration
// 只有配置了 auth.clientId 和 auth.secret 属性,当前配置才会生效。
@ConditionalOnProperty(prefix = "auth", name = {"clientId", "secret"})
@EnableConfigurationProperties(ClientProperties.class)
public class AuthAutoConfiguration {// 实现一些starter的逻辑....
}

4、spring.factories

resources/META-INF/spring.factories 目录下编写

org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
com.auth.config.AuthAutoConfiguration

5、使用自动装配

在要使用的模块的pom文件中添加starter依赖

<dependency><groupId>jwt-utils</groupId><artifactId>jwt-spring-boot-starter</artifactId><version>1.0.0-SNAPSHOT</version>
</dependency>

application.yaml文件添加配置

auth:clientId: user-servicesecret: 1234includeFilterPaths:- /path1- /path2

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

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

2024-02-21 学习笔记(DETR)

自动多模态检测验证效果不佳&#xff08;过检太多&#xff09;后&#xff0c;节后开始尝试DETR路线。 基本梳理了下DETR发展和验证的脉络&#xff0c;先进行相应指定场景的效果验证。 关于DETR系列的介绍&#xff0c;B站上比较多&#xff0c;迪哥的都讲的比较细。 推荐大佬的…

AIoT网关 人工智能物联网网关

AIoT(人工智能物联网)作为新一代技术的代表&#xff0c;正以前所未有的速度改变着我们的生活方式。在这个智能时代&#xff0c;AIoT网关的重要性日益凸显。它不仅是连接智能设备和应用的关键&#xff0c;同时也是实现智能化家居、智慧城市和工业自动化的必备技术。      一…

nuxt项目搭建

1.先下载nuxt脚手架 yarn create nuxt-app <项目名>&#xff0c;记得安装完项目&#xff0c;npm i,下载node包 目录介绍 components 存放组件分别是头部&#xff08;包含导航&#xff09;和底部 layouts 页面布局&#xff0c;实现一个页面整体架构规则&#xff0c;头…

matlab|电动汽车充放电V2G模型

目录 1 主要内容 1.1 模型背景 1.2 目标函数 2 部分代码 3 效果图 4 下载链接 1 主要内容 本程序主要建立电动汽车充放电V2G模型&#xff0c;采用粒子群算法&#xff0c;在保证电动汽车用户出行需求的前提下&#xff0c;为了使工作区域电动汽车尽可能多的消纳供给商场基础…

使用redisMQ-spring-boot-starter实现消息队列和延时队列

简介 redisMQ-spring-boot-starter是一个轻量级的、基于Redis实现的消息队列中间件&#xff0c;它有如下优点&#xff1a; 开箱即用&#xff0c;你几乎不用添加额外的配置支持消息队列、延时队列&#xff0c;并提供精细化配置参数提供消息确认机制支持虚拟空间&#xff0c;不…

编写程序,实现shell功能——项目训练——day08

c c今天做了一个实战项目训练&#xff0c;编写一个程序&#xff0c;实现shell功能&#xff0c;我们称之为minishell。 主要是利用Linux中IO接口实现&#xff0c;实现的功能有&#xff1a; 1.ls ls -a ls -l cd cp mv pwd c…

微信小程序 ---- 慕尚花坊 项目初始化

目录 项目介绍 01. 项目概述 02. 项目演示 03. 项目技术栈 04. 接口文档 申请开发权限 项目初始化 01. 创建项目与项目初始化 02. 自定义构建 npm 集成Sass 03. 集成项目页面文件 04. VsCode 开发小程序项目 项目介绍 01. 项目概述 [慕尚花坊] 是一款 同城鲜花订购…

2024.02.22作业

1. 将互斥机制的代码实现重新敲一遍 #include <stdio.h> #include <stdlib.h> #include <string.h> #include <errno.h> #include <time.h> #include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> #include <…

DC与DCT DCG的区别

先进工艺不再wire load model进行静态时序分析&#xff0c;否则综合结果与后端物理电路差距很大&#xff0c;因此DC综合工具也进行了多次迭代&#xff0c;DC工具有两种模式&#xff0c;包括wire load mode和Topographical Mode&#xff0c;也就是对应的DC Expert和DC Ultra。 …

Linux课程三课---Linux开发环境的使用(yum的相关)

作者前言 &#x1f382; ✨✨✨✨✨✨&#x1f367;&#x1f367;&#x1f367;&#x1f367;&#x1f367;&#x1f367;&#x1f367;&#x1f382; ​&#x1f382; 作者介绍&#xff1a; &#x1f382;&#x1f382; &#x1f382; &#x1f389;&#x1f389;&#x1f389…

Python-pdfplumber读取PDF内容

文章目录 前言一、pdfplumber模块1.1 pdfplumber的特点1.2 pdfplumber.PDF类1.3pdfplumber.Page类 二 pdfplumber的使用2.1 加载PDF2.2 pdfplumber.PDF 类2.3 pdfplumber.Page 类2.4 读取PDF2.5 读取PDF文档信息2.6 查看总页数2.7 查看总页数读取第一页的宽度&#xff0c;页高等…

B端系统:工作台页面,如何从平庸走向出众

Hi&#xff0c;大家好&#xff0c;我是贝格前端工场&#xff0c;从事8年前端开发的老司机。大家看过很多平庸的工作台页面&#xff0c;但是仔细分析过平庸的表现吗&#xff0c;仔细思考过如何实现出众的效果吗&#xff1f;这篇文章为你解读。 一、工作台页面是什么&#xff0c;…

【前端素材】推荐优质后台管理系统Xoric平台模板(附源码)

一、需求分析 当我们从多个层次来详细分析后台管理系统时&#xff0c;可以将其功能和定义进一步细分&#xff0c;以便更好地理解其在不同方面的作用和实际运作。 1. 功能层次 a. 用户管理功能&#xff1a; 用户注册和登录&#xff1a;管理用户账户的注册和登录过程。权限管…

【前端】前端三要素之DOM

写在前面&#xff1a;本文仅包含DOM内容&#xff0c;JavaScript传送门在这里&#xff0c;BOM传送门在这里。 本文内容是假期中刷的黑马Pink老师视频&#xff08;十分感谢Pink老师&#xff09;&#xff0c;原文保存在个人的GitLab中&#xff0c;如果需要写的网页内容信息等可以评…

OpenCV人脸检测案例实战

人脸检测是一种计算机视觉技术&#xff0c;旨在识别图像或视频中的人脸。这项技术的基本内容包括使用特定的算法和模型来定位和识别人脸&#xff0c;通常涉及在图像中寻找面部特征&#xff0c;如眼睛、鼻子、嘴巴等&#xff0c;以便准确地确定人脸的位置和边界。人脸检测技术的…

MySQL 窗口函数温故知新

本文用于复习数据库窗口函数&#xff0c;希望能够温故知新&#xff0c;也希望读到这篇文章的有所收获。 本文以&#xff1a;MySQL为例 参考文档&#xff1a; https://www.begtut.com/mysql/mysql-window-functions.html 使用的样例数据&#xff1a;https://www.begtut.com/m…

9.vue学习笔记(组件传递Props校验+组件事件-组件传递数据+组件事件-配合“v-model”使用)

文章目录 1.组件传递Props校验1.1.默认值1.2.必选项1.3.注意事项&#xff1a;props 是只读的 2.组件事件-组件传递数据2.1.温馨提示&#xff1a;组件之间传递数据的方案 3.组件事件-配合“v-model”使用 1.组件传递Props校验 Vue组件可以更细致地声明对传入的 props 的校验要求…

学习鸿蒙一定要搞清楚的几个概念

目录 1、UI框架 2、应用模型 2.1、应用模型介绍 2.2、两种应用模型 2.3、应用模型和UI框架的关系 3、Ability 3.1、Ability介绍 3.2、FA模型的ability 3.3、Stage模型的Ability 1、UI框架 HarmonyOS提供了一套UI(User Interface,用户界面)开发框架&#xff0c;即方舟…

java 课程签到管理系统Myeclipse开发mysql数据库web结构jsp编程servlet计算机网页项目

一、源码特点 java 课程签到管理系统是一套完善的java web信息管理系统 采用serlvetdaobean&#xff0c;对理解JSP java编程开发语言有帮助&#xff0c;系统具有完整的源代码和数据库&#xff0c;系统主要采用B/S模式开发。开发环境为TOMCAT7.0,Myeclipse8.5开发&#xff0…

OpenGauss数据库本地搭建并结合内网穿透实现远程访问

文章目录 前言1. Linux 安装 openGauss2. Linux 安装cpolar3. 创建openGauss主节点端口号公网地址4. 远程连接openGauss5. 固定连接TCP公网地址6. 固定地址连接测试 前言 openGauss是一款开源关系型数据库管理系统&#xff0c;采用木兰宽松许可证v2发行。openGauss内核深度融合…