SpringBoot自动配置底层源码分析

文章目录

    • 1. 什么是SpringBoot的自动装配?
    • 2. SpringBoot自动装配的底层原理

1. 什么是SpringBoot的自动装配?

Spring Boot的自动配置是一种机制,它使得开发者能够快速地开始构建Spring应用,而不需要手动编写大量的样板代码。Spring Boot的自动配置是通过@EnableAutoConfiguration注解触发的,它会自动根据类路径下的jar包依赖来启用相应的Spring应用上下文配置。要自定义Spring Boot的自动配置,你可以通过以下方式:

  • 创建自己的@Configuration类,并通过@Configuration注解标记它。
  • 使用@Conditional注解来限制配置类的条件激活,例如@ConditionalOnClass,@ConditionalOnMissingBean等。
  • 使用@EnableAutoConfiguration注解中的exclude或excludeName属性来排除不需要的自动配置类。
  • 在META-INF/spring.factories文件中配置自定义的自动配置类。
    下面是一个简单的自定义自动配置的例子:
// 自定义配置类
@Configuration
public class MyCustomAutoConfiguration {@Bean@ConditionalOnMissingBeanpublic MyService myService() {return new MyServiceImpl();}
}// 在spring.factories中添加自动配置的声明
// 在src/main/resources/META-INF/spring.factories文件中
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\com.example.MyCustomAutoConfiguration

在这个例子中,MyCustomAutoConfiguration是一个自定义的配置类,它提供了一个名为myService的bean,类型为MyService。@ConditionalOnMissingBean注解确保只有在应用上下文中没有提供MyService类型的bean时,才会创建这个bean。spring.factories文件中的条目告诉Spring Boot在启动时自动配置这个类。

2. SpringBoot自动装配的底层原理

我们先从SpringBoot的启动类开始:

@SpringBootApplication
public class MyApplication {public static void main(String[] args) {SpringApplication.run(MyApplication.class, args);}}

run方法将当前类和一些系统参数作为参数调用,我们进入run方法

public static ConfigurableApplicationContext run(Class<?> primarySource, String... args) {return run(new Class<?>[] { primarySource }, args);}

该方法是SpringApplication的一个静态方法,然后 new SpringApplication(primarySources).run(args);创建了一个新的SpringApplication对象调用run方法。

public static ConfigurableApplicationContext run(Class<?> primarySource, String... args) {return run(new Class<?>[] { primarySource }, args);}
public static ConfigurableApplicationContext run(Class<?>[] primarySources, String[] args) {// 构造SpringApplication对象return new SpringApplication(primarySources).run(args);}

我们继续进入run方法:

public ConfigurableApplicationContext run(String... args) {long startTime = System.nanoTime();// 1、创建引导启动器,类似一个ApplicationContext,可以往里面添加一些对象DefaultBootstrapContext bootstrapContext = createBootstrapContext();ConfigurableApplicationContext context = null;configureHeadlessProperty();// 2、从spring.factories中获取SpringApplicationRunListener对象// 默认会拿到一个EventPublishingRunListener,它会启动过程的各个阶段发布对应的ApplicationEvent事件SpringApplicationRunListeners listeners = getRunListeners(args);// 3、发布ApplicationStartingEventlisteners.starting(bootstrapContext, this.mainApplicationClass);try {// 4、将run()的参数封装为DefaultApplicationArguments对象ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);// 5、准备Environment// 包括操作系统,JVM、ServletContext、properties、yaml等等配置// 会发布一个ApplicationEnvironmentPreparedEventConfigurableEnvironment environment = prepareEnvironment(listeners, bootstrapContext, applicationArguments);// 默认spring.beaninfo.ignore=true,表示不需要jdk缓存beanInfo信息,Spring自己会缓存configureIgnoreBeanInfo(environment);Banner printedBanner = printBanner(environment);// 6、根据应用类型创建Spring容器context = createApplicationContext();context.setApplicationStartup(this.applicationStartup);// 7、利用ApplicationContextInitializer初始化Spring容器// 8、发布ApplicationContextInitializedEvent// 9、关闭DefaultBootstrapContext// 10、注册primarySources类,就是run方法存入进来的配置类// 11、发布ApplicationPreparedEvent事件prepareContext(bootstrapContext, context, environment, listeners, applicationArguments, printedBanner);// 12、刷新Spring容器,会解析配置类、扫描、启动WebServerrefreshContext(context);// 空方法afterRefresh(context, applicationArguments);// 启动时间Duration timeTakenToStartup = Duration.ofNanos(System.nanoTime() - startTime);if (this.logStartupInfo) {new StartupInfoLogger(this.mainApplicationClass).logStarted(getApplicationLog(), timeTakenToStartup);}// 13、发布ApplicationStartedEvent事件,表示Spring容器已经启动listeners.started(context, timeTakenToStartup);// 14、从Spring容器中获取ApplicationRunner和CommandLineRunner,并执行其run()callRunners(context, applicationArguments);}catch (Throwable ex) {// 15、发布ApplicationFailedEvent事件handleRunFailure(context, ex, listeners);throw new IllegalStateException(ex);}try {Duration timeTakenToReady = Duration.ofNanos(System.nanoTime() - startTime);// 16、发布ApplicationReadyEvent事件,表示Spring容器已经准备好了listeners.ready(context, timeTakenToReady);}catch (Throwable ex) {handleRunFailure(context, ex, null);throw new IllegalStateException(ex);}return context;}

上面就是SpringBoot的启动流程的核心源码,这一部分我们先不详细分析,后面再分析,我只需要知道这个函数会创建一个Spring的容器,然后调用refreshContext(context);方法,我们进入该方法:

private void refreshContext(ConfigurableApplicationContext context) {if (this.registerShutdownHook) {shutdownHook.registerApplicationContext(context);}refresh(context);}@Overridepublic final void refresh() throws BeansException, IllegalStateException {try {super.refresh();}catch (RuntimeException ex) {WebServer webServer = this.webServer;if (webServer != null) {webServer.stop();}throw ex;}}

然后在super.refresh底层会调用一个onrefresh方法,我们进入方法:

@Overrideprotected void onRefresh() {super.onRefresh();try {// 启动Tomcat 非web应用不用部署在Tomcat容器里吗createWebServer();}catch (Throwable ex) {throw new ApplicationContextException("Unable to start web server", ex);}}

在调用onRefresh方法之前我们只需要知道一件事,springBoot此时将我们的Spring容器创建好了,并启动起来了。然后onRefresh方法中调用了一个createWebServer();方法,这个就是用来创建一个Web容器的,例如Tomcat和Jetty等。我们进入该方法:

private void createWebServer() {WebServer webServer = this.webServer;ServletContext servletContext = getServletContext();//如果web容器为空,且servelet容器为空,此时我们就需要去创建if (webServer == null && servletContext == null) {//ServletWebServerFactory就是一个创建Web服务器的工厂ServletWebServerFactory factory = getWebServerFactory();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();}

ServletWebServerFactory factory = getWebServerFactory();上面代码就是用来创建Tomcat、Jetty等Web服务器的。我们进入这个工厂:

@FunctionalInterface
public interface ServletWebServerFactory {WebServer getWebServer(ServletContextInitializer... initializers);
}

这个工厂是一个函数式接口,然后就只有一个方法,就是getWebServer意思就是获取我们的web服务器。然后我们看看这个工厂的实现类:
在这里插入图片描述

在这么多实现类中我们可以看到我们熟悉的两个实现类JettyServletWebServerFactoryTomcatServletWebServerFactory。我们看看Tomcat实现类:

public class TomcatServletWebServerFactory extends AbstractServletWebServerFactoryimplements ConfigurableTomcatWebServerFactory, ResourceLoaderAware {....@Overridepublic WebServer getWebServer(ServletContextInitializer... initializers) {if (this.disableMBeanRegistry) {Registry.disableRegistry();}//创建一个Tomcat服务器Tomcat tomcat = new Tomcat();File baseDir = (this.baseDirectory != null) ? this.baseDirectory : createTempDir("tomcat");tomcat.setBaseDir(baseDir.getAbsolutePath());//注册Tomcat生命周期的监听器for (LifecycleListener listener : this.serverLifecycleListeners) {tomcat.getServer().addLifecycleListener(listener);}//创建Tomcat的连接器组件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);}
}

执行上面代码(内嵌Tomcat的实现方法)我们就可以获取一个Tomcat的实例,并启动起来。

那么我们现在思考一个核心的问题:Springboot怎么判断我们什么时候使用Tomcat什么时候使用Jetty呢?

下面我们分析一下上面的问题,首先我们回到createWebServer方法:

private void createWebServer() {WebServer webServer = this.webServer;ServletContext servletContext = getServletContext();//如果web容器为空,且servelet容器为空,此时我们就需要去创建if (webServer == null && servletContext == null) {//ServletWebServerFactory就是一个创建Web服务器的工厂ServletWebServerFactory factory = getWebServerFactory();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();}

前面说到ServletWebServerFactory factory = getWebServerFactory();真正会返回一个ServletWebServerFactory具体的实现类的一个对象,然后调用具体工厂对象的getWebServer方法来获取真正的Web服务器实例。我们进入getWebServerFactory方法。

protected ServletWebServerFactory getWebServerFactory() {//从工厂中获取所有的bean,这里是按照类型获取bean,类型就是ServletWebServerFactory,也就是获得所有的ServletWebServerFactory接口实现类的beanString[] beanNames = getBeanFactory().getBeanNamesForType(ServletWebServerFactory.class);//如果当前没有拿到一个web服务器工厂,就抛出异常if (beanNames.length == 0) {throw new ApplicationContextException("Unable to start ServletWebServerApplicationContext due to missing "+ "ServletWebServerFactory bean.");}//如果获得多个web服务器工厂也需要抛出异常if (beanNames.length > 1) {throw new ApplicationContextException("Unable to start ServletWebServerApplicationContext due to multiple "+ "ServletWebServerFactory beans : " + StringUtils.arrayToCommaDelimitedString(beanNames));}//然后就真正获取bean(getBeanFactory()获取bean工厂,调用getBean创建或获取bean,Spring源码分析中有讲) return getBeanFactory().getBean(beanNames[0], ServletWebServerFactory.class);}

上面代码的逻辑就是从spring容器中拿到实现ServletWebServerFactory接口的bean,如果有多个或者一个都没有SpringBoot就会抛出相应的异常。那么新的问题来了,从上面代码我们可以看到,创建Web服务器的工厂都会被注入到Spring容器中,那么这些bean是如何被注入到Spring容器中的?,而这个注入bean的过程就是SpringBoot的自动配置帮我们做的!。在SpringBoot中这些事都是自动配置类完成的,而对于Web服务器的自动配置类就是ServletWebServerFactoryAutoConfiguration,我们进入该类。

@Configuration(proxyBeanMethods = false)
@AutoConfigureOrder(Ordered.HIGHEST_PRECEDENCE)
@ConditionalOnClass(ServletRequest.class)
@ConditionalOnWebApplication(type = Type.SERVLET)
@EnableConfigurationProperties(ServerProperties.class)
@Import({ ServletWebServerFactoryAutoConfiguration.BeanPostProcessorsRegistrar.class,ServletWebServerFactoryConfiguration.EmbeddedTomcat.class,ServletWebServerFactoryConfiguration.EmbeddedJetty.class,ServletWebServerFactoryConfiguration.EmbeddedUndertow.class })
public class ServletWebServerFactoryAutoConfiguration {@Beanpublic ServletWebServerFactoryCustomizer servletWebServerFactoryCustomizer(ServerProperties serverProperties,ObjectProvider<WebListenerRegistrar> webListenerRegistrars,ObjectProvider<CookieSameSiteSupplier> cookieSameSiteSuppliers) {return new ServletWebServerFactoryCustomizer(serverProperties,webListenerRegistrars.orderedStream().collect(Collectors.toList()),cookieSameSiteSuppliers.orderedStream().collect(Collectors.toList()));}@Bean@ConditionalOnClass(name = "org.apache.catalina.startup.Tomcat")public TomcatServletWebServerFactoryCustomizer tomcatServletWebServerFactoryCustomizer(ServerProperties serverProperties) {return new TomcatServletWebServerFactoryCustomizer(serverProperties);}@Configuration(proxyBeanMethods = false)@ConditionalOnProperty(value = "server.forward-headers-strategy", havingValue = "framework")@ConditionalOnMissingFilterBean(ForwardedHeaderFilter.class)static class ForwardedHeaderFilterConfiguration {@Bean@ConditionalOnClass(name = "org.apache.catalina.startup.Tomcat")ForwardedHeaderFilterCustomizer tomcatForwardedHeaderFilterCustomizer(ServerProperties serverProperties) {return (filter) -> filter.setRelativeRedirects(serverProperties.getTomcat().isUseRelativeRedirects());}@BeanFilterRegistrationBean<ForwardedHeaderFilter> forwardedHeaderFilter(ObjectProvider<ForwardedHeaderFilterCustomizer> customizerProvider) {ForwardedHeaderFilter filter = new ForwardedHeaderFilter();customizerProvider.ifAvailable((customizer) -> customizer.customize(filter));FilterRegistrationBean<ForwardedHeaderFilter> registration = new FilterRegistrationBean<>(filter);registration.setDispatcherTypes(DispatcherType.REQUEST, DispatcherType.ASYNC, DispatcherType.ERROR);registration.setOrder(Ordered.HIGHEST_PRECEDENCE);return registration;}}interface ForwardedHeaderFilterCustomizer {void customize(ForwardedHeaderFilter filter);}/*** Registers a {@link WebServerFactoryCustomizerBeanPostProcessor}. Registered via* {@link ImportBeanDefinitionRegistrar} for early registration.*/public static class BeanPostProcessorsRegistrar implements ImportBeanDefinitionRegistrar, BeanFactoryAware {private ConfigurableListableBeanFactory beanFactory;@Overridepublic void setBeanFactory(BeanFactory beanFactory) throws BeansException {if (beanFactory instanceof ConfigurableListableBeanFactory) {this.beanFactory = (ConfigurableListableBeanFactory) beanFactory;}}@Overridepublic void registerBeanDefinitions(AnnotationMetadata importingClassMetadata,BeanDefinitionRegistry registry) {if (this.beanFactory == null) {return;}// 往Spring容器中注册一个WebServerFactoryCustomizerBeanPostProcessorregisterSyntheticBeanIfMissing(registry, "webServerFactoryCustomizerBeanPostProcessor",WebServerFactoryCustomizerBeanPostProcessor.class,WebServerFactoryCustomizerBeanPostProcessor::new);registerSyntheticBeanIfMissing(registry, "errorPageRegistrarBeanPostProcessor",ErrorPageRegistrarBeanPostProcessor.class, ErrorPageRegistrarBeanPostProcessor::new);}private <T> void registerSyntheticBeanIfMissing(BeanDefinitionRegistry registry, String name,Class<T> beanClass, Supplier<T> instanceSupplier) {if (ObjectUtils.isEmpty(this.beanFactory.getBeanNamesForType(beanClass, true, false))) {RootBeanDefinition beanDefinition = new RootBeanDefinition(beanClass, instanceSupplier);beanDefinition.setSynthetic(true);registry.registerBeanDefinition(name, beanDefinition);}}}}

首先是@Configuration(proxyBeanMethods = false)注解,说明在Spring容器的启动过程中,这个类会作为一个配置类被处理。@ConditionalOnClass(ServletRequest.class)只有满足这个条件这个配置类才会被处理。@ConditionalOnWebApplication(type = Type.SERVLET)判断当前是一个web应用且是SERVLET类型这个配置类才会生效。只有满足上面两个条件,这个配置类才会被处理,然后处理@Import注解,这个注解在分析Spring源码的时候详细分析过,它可以帮我们额外导入一些配置类或者Bean。我们看看导入的四个类是什么:

ServletWebServerFactoryAutoConfiguration.BeanPostProcessorsRegistrar.class,ServletWebServerFactoryConfiguration.EmbeddedTomcat.class,ServletWebServerFactoryConfiguration.EmbeddedJetty.class,ServletWebServerFactoryConfiguration.EmbeddedUndertow.class

第一个我们不重点分析,重点是后面是那个,从名字我们也可以看出来,是有关Web服务器的一些类,即内嵌的Tomcat、内嵌的Jetty和内嵌的Undertow。我们进入ServletWebServerFactoryConfiguration

@Configuration(proxyBeanMethods = false)
class ServletWebServerFactoryConfiguration {@Configuration(proxyBeanMethods = false)@ConditionalOnClass({ Servlet.class, Tomcat.class, UpgradeProtocol.class })@ConditionalOnMissingBean(value = ServletWebServerFactory.class, search = SearchStrategy.CURRENT)static class EmbeddedTomcat {@BeanTomcatServletWebServerFactory tomcatServletWebServerFactory(ObjectProvider<TomcatConnectorCustomizer> connectorCustomizers,ObjectProvider<TomcatContextCustomizer> contextCustomizers,ObjectProvider<TomcatProtocolHandlerCustomizer<?>> protocolHandlerCustomizers) {TomcatServletWebServerFactory factory = new TomcatServletWebServerFactory();// orderedStream()调用时会去Spring容器中找到TomcatConnectorCustomizer类型的Bean,默认是没有的,程序员可以自己定义factory.getTomcatConnectorCustomizers().addAll(connectorCustomizers.orderedStream().collect(Collectors.toList()));factory.getTomcatContextCustomizers().addAll(contextCustomizers.orderedStream().collect(Collectors.toList()));factory.getTomcatProtocolHandlerCustomizers().addAll(protocolHandlerCustomizers.orderedStream().collect(Collectors.toList()));return factory;}}/*** Nested configuration if Jetty is being used.*/@Configuration(proxyBeanMethods = false)@ConditionalOnClass({ Servlet.class, Server.class, Loader.class, WebAppContext.class })@ConditionalOnMissingBean(value = ServletWebServerFactory.class, search = SearchStrategy.CURRENT)static class EmbeddedJetty {@BeanJettyServletWebServerFactory JettyServletWebServerFactory(ObjectProvider<JettyServerCustomizer> serverCustomizers) {JettyServletWebServerFactory factory = new JettyServletWebServerFactory();factory.getServerCustomizers().addAll(serverCustomizers.orderedStream().collect(Collectors.toList()));return factory;}}/*** Nested configuration if Undertow is being used.*/@Configuration(proxyBeanMethods = false)@ConditionalOnClass({ Servlet.class, Undertow.class, SslClientAuthMode.class })@ConditionalOnMissingBean(value = ServletWebServerFactory.class, search = SearchStrategy.CURRENT)static class EmbeddedUndertow {@BeanUndertowServletWebServerFactory undertowServletWebServerFactory(ObjectProvider<UndertowDeploymentInfoCustomizer> deploymentInfoCustomizers,ObjectProvider<UndertowBuilderCustomizer> builderCustomizers) {UndertowServletWebServerFactory factory = new UndertowServletWebServerFactory();factory.getDeploymentInfoCustomizers().addAll(deploymentInfoCustomizers.orderedStream().collect(Collectors.toList()));factory.getBuilderCustomizers().addAll(builderCustomizers.orderedStream().collect(Collectors.toList()));return factory;}@BeanUndertowServletWebServerFactoryCustomizer undertowServletWebServerFactoryCustomizer(ServerProperties serverProperties) {return new UndertowServletWebServerFactoryCustomizer(serverProperties);}}}

首先一眼丁真,这是一个配置类,自然也会被Spring处理,然后这个类里面有有三个静态内部类,分别是EmbeddedTomcatEmbeddedJettyEmbeddedUndertow。然后这三个静态内部类也同样是配置类,同样会被Spring处理,然后每个静态内部类都会注入相应web服务器需要的Bean,我们这里拿Tomcat的分析一下:

@Configuration(proxyBeanMethods = false)@ConditionalOnClass({ Servlet.class, Tomcat.class, UpgradeProtocol.class })@ConditionalOnMissingBean(value = ServletWebServerFactory.class, search = SearchStrategy.CURRENT)static class EmbeddedTomcat {@BeanTomcatServletWebServerFactory tomcatServletWebServerFactory(ObjectProvider<TomcatConnectorCustomizer> connectorCustomizers,ObjectProvider<TomcatContextCustomizer> contextCustomizers,ObjectProvider<TomcatProtocolHandlerCustomizer<?>> protocolHandlerCustomizers) {TomcatServletWebServerFactory factory = new TomcatServletWebServerFactory();// orderedStream()调用时会去Spring容器中找到TomcatConnectorCustomizer类型的Bean,默认是没有的,程序员可以自己定义factory.getTomcatConnectorCustomizers().addAll(connectorCustomizers.orderedStream().collect(Collectors.toList()));factory.getTomcatContextCustomizers().addAll(contextCustomizers.orderedStream().collect(Collectors.toList()));factory.getTomcatProtocolHandlerCustomizers().addAll(protocolHandlerCustomizers.orderedStream().collect(Collectors.toList()));return factory;}}

首先看下面的注解:

@ConditionalOnClass({ Servlet.class, Tomcat.class, UpgradeProtocol.class })
@ConditionalOnMissingBean(value = ServletWebServerFactory.class, search = SearchStrategy.CURRENT)

上面的两个注解就是Spring中的两个注解,它在这里的作用就是判断下面的配置类会不会被解析,例如只有满足Servlet.class, Tomcat.class, UpgradeProtocol.class 这三个类都存在,且项目中没有ServletWebServerFactory这个bean才会被解析。如果上面的条件的满足,就会处理静态内部类的@Bean注解,来注入相应web服务器的工厂bean。上面这两个注解是很重要的,它的SpringBoot在判断选择使用哪个Web服务器的核心逻辑。然后我们看一下具体的方法:

	@Bean//返回值是一个TomcatServletWebServerFactory对象TomcatServletWebServerFactory tomcatServletWebServerFactory(//Tomcat需要的一些依赖,加了ObjectProvider就表示可以为空,为空就会使用默认的Tomcat配置,有了这三个bean,我们就可以设置一些tomcat的常用的属性ObjectProvider<TomcatConnectorCustomizer> connectorCustomizers,ObjectProvider<TomcatContextCustomizer> contextCustomizers,ObjectProvider<TomcatProtocolHandlerCustomizer<?>> protocolHandlerCustomizers) {//创建一个TomcatServletWebServerFactory对象TomcatServletWebServerFactory factory = new TomcatServletWebServerFactory();// orderedStream()调用时会去Spring容器中找到TomcatConnectorCustomizer类型的Bean,默认是没有的,程序员可以自己定义factory.getTomcatConnectorCustomizers().addAll(connectorCustomizers.orderedStream().collect(Collectors.toList()));factory.getTomcatContextCustomizers().addAll(contextCustomizers.orderedStream().collect(Collectors.toList()));factory.getTomcatProtocolHandlerCustomizers().addAll(protocolHandlerCustomizers.orderedStream().collect(Collectors.toList()));return factory;}//返回的this.tomcatConnectorCustomizers一个connectorCustomizers的集合
public Collection<TomcatConnectorCustomizer> getTomcatConnectorCustomizers() {return this.tomcatConnectorCustomizers;}

如果注入实例为空 , 则使用ObjectProvider可以避免强依赖导致的依赖不存在问题

上面代码核心就是创建了一个TomcatServletWebServerFactory的实例,然后设置了一些属性,它的参数是三个对象,分别是TomcatConnectorCustomizerTomcatContextCustomizerTomcatProtocolHandlerCustomizer这意味着我们同样可以自己注入这些bean,来完成Tomcat的一些配置,例如:

@Bean
public TomcatConnectorCustomizer tomcatConnectorCustomizer{return new TomcatConnectorCustomizer(){@Overridepublic void customize(Connector connetor){connector.setPort(8888);}}
}

上面代码我们就注入了一个TomcatConnectorCustomizer的bean,修改了Tomcat的默认监听端口8080。那么现在思考新的问题:Tomcat是如何将上面案例我们设置的端口号设置到我们的Tomcat实例中的呢?我们进入TomcatServletWebServerFactory的getServer方法,它里面有一句代码:

customizeConnector(connector);protected void customizeConnector(Connector connector) {//private int port = 8080; getport方法返回值就是8080,也就是Tomcat的默认监听端口int port = Math.max(getPort(), 0);//设置监听的端口connector.setPort(port);if (StringUtils.hasText(getServerHeader())) {connector.setProperty("server", getServerHeader());}if (connector.getProtocolHandler() instanceof AbstractProtocol) {customizeProtocol((AbstractProtocol<?>) connector.getProtocolHandler());}invokeProtocolHandlerCustomizers(connector.getProtocolHandler());if (getUriEncoding() != null) {connector.setURIEncoding(getUriEncoding().name());}// Don't bind to the socket prematurely if ApplicationContext is slow to startconnector.setProperty("bindOnInit", "false");if (getHttp2() != null && getHttp2().isEnabled()) {connector.addUpgradeProtocol(new Http2Protocol());}if (getSsl() != null && getSsl().isEnabled()) {customizeSsl(connector);}TomcatConnectorCustomizer compression = new CompressionConnectorCustomizer(getCompression());compression.customize(connector);for (TomcatConnectorCustomizer customizer : this.tomcatConnectorCustomizers) {customizer.customize(connector);}}

上面代码核心的几十下面这一句:

for (TomcatConnectorCustomizer customizer : this.tomcatConnectorCustomizers) {customizer.customize(connector);}

前面我们知道,在创建TomcatServletWebServerFactory的时候会将TomcatConnectorCustomizer都注入到this.tomcatConnectorCustomizers集合中,然后这里就拿到了所有的TomcatConnectorCustomizer然后遍历处理,也就是在这里前面我们案例设置的端口号就生效了。

使用过SpringBoot都知道我们的端口号同样可以在application.properties中配置,那么这种配置形式又是如何生效的呢?我们接着分析,首先我们回到自动Web服务器的自动配置类:

@Configuration(proxyBeanMethods = false)
@AutoConfigureOrder(Ordered.HIGHEST_PRECEDENCE)
@ConditionalOnClass(ServletRequest.class)
@ConditionalOnWebApplication(type = Type.SERVLET)
@EnableConfigurationProperties(ServerProperties.class)
@Import({ ServletWebServerFactoryAutoConfiguration.BeanPostProcessorsRegistrar.class,ServletWebServerFactoryConfiguration.EmbeddedTomcat.class,ServletWebServerFactoryConfiguration.EmbeddedJetty.class,ServletWebServerFactoryConfiguration.EmbeddedUndertow.class })
public class ServletWebServerFactoryAutoConfiguration {
....
}

我们前面介绍了@Import注解中导入的EmbeddedTomcatEmbeddedJettyEmbeddedUndertow这三个类。然而还没有分析BeanPostProcessorsRegistrar这个类,我们这里分析一下。

public static class BeanPostProcessorsRegistrar implements ImportBeanDefinitionRegistrar, BeanFactoryAware {private ConfigurableListableBeanFactory beanFactory;@Overridepublic void setBeanFactory(BeanFactory beanFactory) throws BeansException {if (beanFactory instanceof ConfigurableListableBeanFactory) {this.beanFactory = (ConfigurableListableBeanFactory) beanFactory;}}@Overridepublic void registerBeanDefinitions(AnnotationMetadata importingClassMetadata,BeanDefinitionRegistry registry) {if (this.beanFactory == null) {return;}// 往Spring容器中注册一个WebServerFactoryCustomizerBeanPostProcessorregisterSyntheticBeanIfMissing(registry, "webServerFactoryCustomizerBeanPostProcessor",WebServerFactoryCustomizerBeanPostProcessor.class,WebServerFactoryCustomizerBeanPostProcessor::new);registerSyntheticBeanIfMissing(registry, "errorPageRegistrarBeanPostProcessor",ErrorPageRegistrarBeanPostProcessor.class, ErrorPageRegistrarBeanPostProcessor::new);}private <T> void registerSyntheticBeanIfMissing(BeanDefinitionRegistry registry, String name,Class<T> beanClass, Supplier<T> instanceSupplier) {if (ObjectUtils.isEmpty(this.beanFactory.getBeanNamesForType(beanClass, true, false))) {RootBeanDefinition beanDefinition = new RootBeanDefinition(beanClass, instanceSupplier);beanDefinition.setSynthetic(true);registry.registerBeanDefinition(name, beanDefinition);}}}

BeanPostProcessorsRegistrarServletWebServerFactoryAutoConfiguration内部定义的一个内部类,首先这个类有个鲜明的特征就是实现了ImportBeanDefinitionRegistrar接口,那么在Spring启动过程中会自动执行实现了这个接口的类的registerBeanDefinitions方法,然后就是一个回调接口BeanFactoryAware,有了这个回调接口我们就可以拿到bean工厂。下面我们看一下registerBeanDefinitions方法:

public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata,BeanDefinitionRegistry registry) {if (this.beanFactory == null) {return;}// 往Spring容器中注册一个WebServerFactoryCustomizerBeanPostProcessorregisterSyntheticBeanIfMissing(registry, "webServerFactoryCustomizerBeanPostProcessor",WebServerFactoryCustomizerBeanPostProcessor.class,WebServerFactoryCustomizerBeanPostProcessor::new);registerSyntheticBeanIfMissing(registry, "errorPageRegistrarBeanPostProcessor",ErrorPageRegistrarBeanPostProcessor.class, ErrorPageRegistrarBeanPostProcessor::new);}

上面代码就是向Spring容器中注入了一个核心的beanWebServerFactoryCustomizerBeanPostProcessor我们进入该类:

public class WebServerFactoryCustomizerBeanPostProcessor implements BeanPostProcessor, BeanFactoryAware {private ListableBeanFactory beanFactory;private List<WebServerFactoryCustomizer<?>> customizers;@Overridepublic void setBeanFactory(BeanFactory beanFactory) {Assert.isInstanceOf(ListableBeanFactory.class, beanFactory,"WebServerCustomizerBeanPostProcessor can only be used with a ListableBeanFactory");this.beanFactory = (ListableBeanFactory) beanFactory;}@Overridepublic Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {if (bean instanceof WebServerFactory) {postProcessBeforeInitialization((WebServerFactory) bean);}return bean;}@Overridepublic Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {return bean;}@SuppressWarnings("unchecked")private void postProcessBeforeInitialization(WebServerFactory webServerFactory) {// 获取WebServerFactoryCustomizer对WebServerFactory进行自定义,默认会拿到5个,其中就包括ServletWebServerFactoryCustomizerLambdaSafe.callbacks(WebServerFactoryCustomizer.class, getCustomizers(), webServerFactory).withLogger(WebServerFactoryCustomizerBeanPostProcessor.class).invoke((customizer) -> customizer.customize(webServerFactory));}private Collection<WebServerFactoryCustomizer<?>> getCustomizers() {if (this.customizers == null) {// Look up does not include the parent contextthis.customizers = new ArrayList<>(getWebServerFactoryCustomizerBeans());this.customizers.sort(AnnotationAwareOrderComparator.INSTANCE);this.customizers = Collections.unmodifiableList(this.customizers);}return this.customizers;}@SuppressWarnings({ "unchecked", "rawtypes" })private Collection<WebServerFactoryCustomizer<?>> getWebServerFactoryCustomizerBeans() {return (Collection) this.beanFactory.getBeansOfType(WebServerFactoryCustomizer.class, false, false).values();}}

首先这个类实现了BeanPostProcessor,这个接口在介绍Spring的时候详细分析过(bean的后置处理器)。我们看看它的一个核心方法:

@Overridepublic Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {//判断bean的类型是否是WebServerFactory类型if (bean instanceof WebServerFactory) {//如果是调用postProcessBeforeInitialization((WebServerFactory) bean);方法postProcessBeforeInitialization((WebServerFactory) bean);}return bean;}

这里重写了BeanPostProcessorpostProcessBeforeInitialization方法,即在bean初始化前调用的方法,上面判断 此时创建的bean是否是WebServerFactory类型,即web服务器类型,如果是就调用postProcessBeforeInitialization方法,我们进入该方法:

private void postProcessBeforeInitialization(WebServerFactory webServerFactory) {// 获取WebServerFactoryCustomizer对WebServerFactory进行自定义,默认会拿到5个,其中就包括ServletWebServerFactoryCustomizerLambdaSafe.callbacks(WebServerFactoryCustomizer.class, getCustomizers(), webServerFactory).withLogger(WebServerFactoryCustomizerBeanPostProcessor.class).invoke((customizer) -> customizer.customize(webServerFactory));}

上面代码核心就是调用了 customizer.customize(webServerFactory),即WebServerFactoryCustomizercustomize方法,那么这个WebServerFactoryCustomizer类型的bean是什么时候创建的?细心的可以发现,这个bean就是在我们Web服务器的自动配置类里面创建了。

	@Beanpublic ServletWebServerFactoryCustomizer servletWebServerFactoryCustomizer(ServerProperties serverProperties,ObjectProvider<WebListenerRegistrar> webListenerRegistrars,ObjectProvider<CookieSameSiteSupplier> cookieSameSiteSuppliers) {return new ServletWebServerFactoryCustomizer(serverProperties,webListenerRegistrars.orderedStream().collect(Collectors.toList()),cookieSameSiteSuppliers.orderedStream().collect(Collectors.toList()));}

我们进入该类的customize方法:

注意这里的customize和前面介绍的customize是不同的,前面是处理Connecter对象的,这里是处理WebFactory对象的,这一点要做区分

@Overridepublic void customize(ConfigurableServletWebServerFactory factory) {PropertyMapper map = PropertyMapper.get().alwaysApplyingWhenNonNull();map.from(this.serverProperties::getPort).to(factory::setPort);map.from(this.serverProperties::getAddress).to(factory::setAddress);map.from(this.serverProperties.getServlet()::getContextPath).to(factory::setContextPath);map.from(this.serverProperties.getServlet()::getApplicationDisplayName).to(factory::setDisplayName);map.from(this.serverProperties.getServlet()::isRegisterDefaultServlet).to(factory::setRegisterDefaultServlet);map.from(this.serverProperties.getServlet()::getSession).to(factory::setSession);map.from(this.serverProperties::getSsl).to(factory::setSsl);map.from(this.serverProperties.getServlet()::getJsp).to(factory::setJsp);map.from(this.serverProperties::getCompression).to(factory::setCompression);map.from(this.serverProperties::getHttp2).to(factory::setHttp2);map.from(this.serverProperties::getServerHeader).to(factory::setServerHeader);map.from(this.serverProperties.getServlet()::getContextParameters).to(factory::setInitParameters);map.from(this.serverProperties.getShutdown()).to(factory::setShutdown);for (WebListenerRegistrar registrar : this.webListenerRegistrars) {registrar.register(factory);}if (!CollectionUtils.isEmpty(this.cookieSameSiteSuppliers)) {factory.setCookieSameSiteSuppliers(this.cookieSameSiteSuppliers);}}

上面方法的参数就是我们正在创建的ConfigurableServletWebServerFactory即web服务器工厂对象。然后下面就是很多map.from来设置web服务器工厂的一些属性,我们拿下面这一句分析:

map.from(this.serverProperties::getPort).to(factory::setPort);

首先this.serverProperties.getPort()就可以拿到我们设置的端口的属性,然后调用setPort方法,来设置端口。我们看一下this.serverProperties这个类:

@ConfigurationProperties(prefix = "server", ignoreUnknownFields = true)
public class ServerProperties {
......
}

@ConfigurationProperties(prefix = "server", ignoreUnknownFields = true)就是取application.proporties文件中server开头的属性,所以我们可以拿到application.proporties中我们配置的server.port属性,然后自动配置类上有这个注解@EnableConfigurationProperties(ServerProperties.class),就会将ServerProperties.class这个类注册为Spring的一个bean,这样就可以拿到application.properties中的相关属性并设置到web服务器工厂中了。

上面就拿SpringBoot对web服务器的自动配置过程,分析了一下Spring的自动配置大致原理。在介绍前面的过程中,我们分析过SpringBoot提供的条件注解,条件注解对于SpringBoot来说是很重要的,它自己实现了很多条件注解,大致有下面这些:

在这里插入图片描述
我们就拿ConditionalOnClass来分析SpringBoot是如何处理这些条件注解的:

@Target({ ElementType.TYPE, ElementType.METHOD })
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Conditional(OnClassCondition.class)
public @interface ConditionalOnClass {Class<?>[] value() default {};String[] name() default {};}@Overridepublic final boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {// 针对每个条件注解进行条件判断// 条件注解写在了哪个类上,或哪个方法上String classOrMethodName = getClassOrMethodName(metadata);try {// 条件的判断结果(ConditionOutcome就是条件判断得结果)ConditionOutcome outcome = getMatchOutcome(context, metadata);// 如果log的日志级别为trace,那就直接记录当前条件的判断结果logOutcome(classOrMethodName, outcome);// 将判断结果记录到ConditionEvaluationReport中//ConditionEvaluationReportLoggingListener会在收到ContextRefreshedEvent事件后把判断结果用日志的方式打印出来recordEvaluation(context, classOrMethodName, outcome);return outcome.isMatch();}catch (NoClassDefFoundError ex) {throw new IllegalStateException("Could not evaluate condition on " + classOrMethodName + " due to "+ ex.getMessage() + " not found. Make sure your own configuration does not rely on "+ "that class. This can also happen if you are "+ "@ComponentScanning a springframework package (e.g. if you "+ "put a @ComponentScan in the default package by mistake)", ex);}catch (RuntimeException ex) {throw new IllegalStateException("Error processing condition on " + getName(metadata), ex);}}

任何SpringBoot自己实现的条件注解内部都有@Conditional(OnClassCondition.class)只是OnClassCondition.class指定的内不同,@Conditional注解会被Spring识别并调用OnClassCondition.classmatches方法,而matches是Conditon接口提供的,意味着OnClassCondition.class底层必定实现了Conditon接口。这也是Springboot条件注解的底层实现原理(就是Spring条件注解底层的实现原理),解析条件注解底层太过细节了这里就不详细分析了。继续回到SpringBoot的启动类。

@SpringBootApplication
public class MyApplication {public static void main(String[] args) {SpringApplication.run(MyApplication.class, args);}}

run方法的参数就是MyApplication,这个类上面有一个@SpringBootApplication注解,我们进入该注解:

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@SpringBootConfiguration
@EnableAutoConfiguration
@ComponentScan(excludeFilters = { @Filter(type = FilterType.CUSTOM, classes = TypeExcludeFilter.class),@Filter(type = FilterType.CUSTOM, classes = AutoConfigurationExcludeFilter.class) })
// AutoConfigurationExcludeFilter的作用是扫描到的配置类名字如果在自动配置类名集合中,就不解析
public @interface SpringBootApplication {
}

首先有一个@ComponentScan(excludeFilters = { @Filter(type = FilterType.CUSTOM, classes = TypeExcludeFilter.class),这个在Spring里面详细介绍过。然后我们看@EnableAutoConfiguration注解:

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@AutoConfigurationPackage
@Import(AutoConfigurationImportSelector.class)
public @interface EnableAutoConfiguration {}

这个注解中又包含一个@Import注解,导入了一个AutoConfigurationImportSelector类,而这个类实现了DeferredImportSelector接口,在Spring解析配置类的时候会自动调用这个类的SelectImports方法:

@Overridepublic String[] selectImports(AnnotationMetadata annotationMetadata) {// 会在所有@Configuration都解析完了之后才执行if (!isEnabled(annotationMetadata)) {return NO_IMPORTS;}// 获取自动配置类(spring.factories中所导入的)AutoConfigurationEntry autoConfigurationEntry = getAutoConfigurationEntry(annotationMetadata);return StringUtils.toStringArray(autoConfigurationEntry.getConfigurations());}

上面方法就会将获取所有的配置类spring.factories中定义的,底层用的SPI机制。 我们进入getAutoConfigurationEntry方法。

protected AutoConfigurationEntry getAutoConfigurationEntry(AnnotationMetadata annotationMetadata) {if (!isEnabled(annotationMetadata)) {return EMPTY_ENTRY;}// 获取@EnableAutoConfiguration的属性AnnotationAttributes attributes = getAttributes(annotationMetadata);// 获取spring.factories中所有的AutoConfigurationList<String> configurations = getCandidateConfigurations(annotationMetadata, attributes);// 去重(也就是按类名去重)configurations = removeDuplicates(configurations);// 获取需要排除的AutoConfiguration,可以通过@EnableAutoConfiguration注解的exclude属性,或者spring.autoconfigure.exclude来配置Set<String> exclusions = getExclusions(annotationMetadata, attributes);// 排除checkExcludedClasses(configurations, exclusions);configurations.removeAll(exclusions);// 获取spring.factories中的AutoConfigurationImportFilter对AutoConfiguration进行过滤// 默认会拿到OnBeanCondition、OnClassCondition、OnWebApplicationCondition// 这三个会去判断上面的AutoConfiguration是否符合它们自身所要求的条件,不符合的会过滤掉,表示不会进行解析了// 会利用spring-autoconfigure-metadata.properties中的配置来进行过滤// spring-autoconfigure-metadata.properties文件中的内容是利用Java中的AbstractProcessor技术在编译时生成出来的configurations = getConfigurationClassFilter().filter(configurations);// configurations表示合格的,exclusions表示被排除的,把它们记录在ConditionEvaluationReportAutoConfigurationImportListener中fireAutoConfigurationImportEvents(configurations, exclusions);// 最后返回的AutoConfiguration都是符合条件的return new AutoConfigurationEntry(configurations, exclusions);}

List<String> configurations = getCandidateConfigurations(annotationMetadata, attributes);就获取了spring.factories中所有的AutoConfiguration所有的AutoConfiguration。我们进入该方法:

protected List<String> getCandidateConfigurations(AnnotationMetadata metadata, AnnotationAttributes attributes) {List<String> configurations = SpringFactoriesLoader.loadFactoryNames(getSpringFactoriesLoaderFactoryClass(),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;}

首先调用了loadFactoryNames方法,这个是SpringFactoriesLoader类中的方法,我们看一下这个类:


public final class SpringFactoriesLoader {//这里就定义了spring.factories文件的路径public static final String FACTORIES_RESOURCE_LOCATION = "META-INF/spring.factories";public static List<String> loadFactoryNames(Class<?> factoryType, @Nullable ClassLoader classLoader) {ClassLoader classLoaderToUse = classLoader;if (classLoaderToUse == null) {classLoaderToUse = SpringFactoriesLoader.class.getClassLoader();}String factoryTypeName = factoryType.getName();return loadSpringFactories(classLoaderToUse).getOrDefault(factoryTypeName, Collections.emptyList());}
}

然后loadFactoryNames又调用了loadSpringFactories方法,然后调用getOrDefault方法,我们首先进入loadSpringFactories方法

private static Map<String, List<String>> loadSpringFactories(ClassLoader classLoader) {//cache就是缓存的spring.factories中的内容Map<String, List<String>> result = cache.get(classLoader);//如果缓存不为空,直接返回if (result != null) {return result;}result = new HashMap<>();try {//解析SPring.factoriesEnumeration<URL> urls = classLoader.getResources(FACTORIES_RESOURCE_LOCATION);while (urls.hasMoreElements()) {URL url = urls.nextElement();UrlResource resource = new UrlResource(url);Properties properties = PropertiesLoaderUtils.loadProperties(resource);for (Map.Entry<?, ?> entry : properties.entrySet()) {String factoryTypeName = ((String) entry.getKey()).trim();String[] factoryImplementationNames =StringUtils.commaDelimitedListToStringArray((String) entry.getValue());for (String factoryImplementationName : factoryImplementationNames) {result.computeIfAbsent(factoryTypeName, key -> new ArrayList<>()).add(factoryImplementationName.trim());}}}// Replace all lists with unmodifiable lists containing unique elementsresult.replaceAll((factoryType, implementations) -> implementations.stream().distinct().collect(Collectors.collectingAndThen(Collectors.toList(), Collections::unmodifiableList)));cache.put(classLoader, result);}catch (IOException ex) {throw new IllegalArgumentException("Unable to load factories from location [" +FACTORIES_RESOURCE_LOCATION + "]", ex);}return result;}

上面代码就是在解析spring.factories文件,并将其放入了一个Map<String, List<String>> 集合中,并返回。然后调用getOrDefault方法,这个是map集合的方法。参数为factoryTypeName,即获得所有的自动配置类。上面的逻辑SpringBoot就获得了所有的自动配置类。回到selectImports,就返回了所有的自动配置类的集合AutoConfigurationEntry,而这些自动配置类就会被注册为Spring的bean。

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

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

相关文章

FRPC+PHP+MYSQL+APACHE2=个人网站

应用背景有公网需求,但是又不想去买又贵又低配置的服务器,然后方案就应运而生 frp/README_zh.md at dev fatedier/frp (github.com) 在这里, FRPC作为内网穿透服务, PHPMYSQLAPACHE2,作为网站搭建,具体细节不细讲, 但是在我的/var/www/html下面 linaroHinlink:/var/www/h…

CSS3新增特性(二)

四、2D 转换 • 属性名&#xff1a;transform &#xff08;可用于制作2D转换&#xff0c;也可用于制作3D转转换&#xff1b;2D转换是平面上的转换&#xff0c;3D转换是在三维立体空间的转换&#xff09; • 作用&#xff1a;对元素进行水平或垂直方向的移动、缩放、旋转、拉长…

二. 搭建Nginx 直播流程服务器

目录 1. 前言 2. 安装 Nginx 依赖 3.下载源码 4. 编译安装 5.配置 rtmp 服务 6.验证配置 1. 前言 服务器由 NGINX+RTMP 构成。 NGINX 是 HTTP 服务器, RTMP 是附加模块。 其中 NGINX 我选择的是用 源码编译方式 进行安装,因为这种方式可以自定义安装指定的…

DevOps(八)Jenkins的Maven和Git插件

一、Maven简介 Maven是一个构建生命周期管理和理解工具&#xff0c;用于Java项目。它提供了标准化的构建流程&#xff0c;并简化了从项目编译到文档生成等各种构建方面的管理。 Maven是由Apache软件基金会开发和维护的一个流行的项目管理工具。它的设计目的是简化Java项目的构…

Linux驱动开发:深入理解I2C时序

目录标题 I2C简介I2C时序关键点Linux内核中的I2C时序处理I2C适配器I2C算法I2C核心 代码示例&#xff1a;I2C设备访问调试I2C时序问题 在Linux驱动开发中&#xff0c;理解和正确处理I2C时序对于确保I2C设备正常工作至关重要。本文将详细介绍I2C通信协议的时序特征&#xff0c;并…

应用在防蓝光显示器中的LED防蓝光灯珠

相比抗蓝光眼镜、防蓝光覆膜、软体降低蓝光强度这些“软”净蓝手段&#xff0c;通过对LED的发光磷粉进行LED背光进行技术革新&#xff0c;可实现硬件“净蓝”。其能够将90%以上的有害蓝光转换为450nm以上的长波低能光线&#xff0c;从硬件的角度解决了蓝光危害眼睛的问题&#…

❤️新版Linux零基础快速入门到精通——第一部分❤️

❤️新版Linux零基础快速入门到精通——第一部分❤️ 非科班的我&#xff01;Ta&#xff01;还是来了~~~1. 来认识一下Linux吧!1.1 操作系统概述1.1.1 操作系统概述1.1.2 操作系统的发展史1.1.2.1 Unix1.1.2.2 Minix1.1.2.3 Linux 1.1.3 操作系统的发展 1.2 Linux初识1.2.1 Lin…

【MySQL】数据库操作指南:数据类型篇

&#x1f331;博客主页&#xff1a;青竹雾色间 &#x1f331;系列专栏&#xff1a;MySQL探险日记 &#x1f618;博客制作不易欢迎各位&#x1f44d;点赞⭐收藏➕关注 ✨人生如寄&#xff0c;多忧何为 ✨ 文章目录 1. 数值类型1.1 tinyint 类型1.2 bit 类型1.3 小数类型1.3.1 f…

nacos配置mysql(windows)

nacos默认是使用的内置数据库derby ,可通过配置修改成mysql,修改成mysql之后&#xff0c;之前配置在derby的数据会丢失 本文使用mysql版本为8.0.22 nacos版本为2.3.1 在mysql里面先创建一个数据库test(名称自定义&#xff0c;和后面配置文件里面的一样就好了) 在上面创建的数据…

Milvus 在哈啰的应用与落地

向量数据库还有哪些可能性&#xff1f; 本期的【User Tech】直播告诉你答案&#xff01;明晚的直播&#xff0c;我们邀请了来自哈啰的资深研发工程师王永辉&#xff0c;他将为我们详细讲解 Milvus 在本地出行及生活服务平台的应用及未来发展的诸多可能性&#xff0c;敬请期待&a…

如何在Windows服务做性能测试(CPU、磁盘、内存)

目录 前言1. 基本知识2. 参数说明 前言 由于需要做一些接口测试&#xff0c;测试是否有真的优化 1. 基本知识 该基本知识主要用来用到Performance Monitor&#xff0c;以下着重介绍下这方面的知识 性能监视器&#xff08;Performance Monitor&#xff09;&#xff1a;Windo…

C++ 核心编程 - 内存分区模型

文章目录 1.1 程序运行前1.2 程序运行后1.3 new 操作符 C 程序在执行时&#xff0c;将内存大致划分为 4个区域&#xff1a; 代码区&#xff1a;存放函数体的二进制代码&#xff0c;由操作系统进行管理&#xff1b;全局区&#xff1a;存放全局变量和静态变量以及常量&#xff1…

MT2041 三角形的个数

思路&#xff1a;找规律&#xff0c;推公式 4等分&#xff1a; 头朝上的三角形&#xff1a; 边长为1&#xff1a;1234s1&#xff1b; 边长为2&#xff1a;123s2&#xff1b; 边长为3&#xff1a;12s3&#xff1b; 边长为4&#xff1a;1s4&#xff1b; 即si12...n-i1(n-i2)*(n-i…

基于高斯混合模型的视频背景提取和人员跟踪算法matlab仿真

目录 1.算法运行效果图预览 2.算法运行软件版本 3.部分核心程序 4.算法理论概述 5.算法完整程序工程 1.算法运行效果图预览 2.算法运行软件版本 MATLAB2013B 3.部分核心程序 .............................................................................. %我们这里…

根据当年节假日和非工作时间计算请假时间-获取每个月的节假日,计算每个月的工作日时间进度,节假日每年更新

根据需求请假时间要排除法定节假日和非工作时间 1.获取当年的节假日 节假日是每年更新的&#xff0c;没有固定接口&#xff0c;需要手动录入 个人根据官方的节假日整理了当年的所有节假日&#xff0c;可以根据个人需求进行修改 // 获取每个月的节假日&#xff0c;如果当月没…

Java | 冒泡排序算法实现

大家可以关注一下专栏&#xff0c;方便大家需要的时候直接查找&#xff0c;专栏将持续更新~ 题目描述 编写一个Java程序&#xff0c;实现冒泡排序算法。程序需要能够接收一个整型数组作为输入&#xff0c;并输出排序后的数组。 冒泡排序是一种简单的排序算法&#xff0c;它…

Laravel 6 - 第十五章 验证器

​ 文章目录 Laravel 6 - 第一章 简介 Laravel 6 - 第二章 项目搭建 Laravel 6 - 第三章 文件夹结构 Laravel 6 - 第四章 生命周期 Laravel 6 - 第五章 控制反转和依赖注入 Laravel 6 - 第六章 服务容器 Laravel 6 - 第七章 服务提供者 Laravel 6 - 第八章 门面 Laravel 6 - …

记录好用的python包

记录好用的python包 PipxCentos 安装pipx确保 Pip 被安装更新 Pip安装 Pipx添加 Pipx 到 PATH临时添加到 PATH:永久添加到 PATH: 验证 Pipx 安装 Poetry安装准备工作创建虚拟环境激活虚拟环境安装包追踪 & 更新包常用配置pycharm 远程连接poetry创建的虚拟环境参考链接 cen…

Java基础入门day36

day36 js dom控制css变化 <!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><meta name"viewport" content"widthdevice-width, initial-scale1.0"><title>Document</title>…

【刷题】代码随想录算法训练营第二十二天|235、二叉搜索树的最近公共祖先,701、二叉搜索树中的插入操作,450、删除二叉搜索树中的节点

目录 235、二叉搜索树的最近公共祖先701、二叉搜索树中的插入操作450、删除 235、二叉搜索树的最近公共祖先 讲解&#xff1a; 回忆一下递归的三部曲&#xff0c;以这题为例。 1、参数&#xff1a; 当前节点和两个节点。 Treenode* cur, Treenode*p, Treenode*q2、递归终止条…