SpringBoot源码(自动装配、内嵌Tomcat)

文章目录

  • 依赖管理
      • pom依赖管理
      • Web依赖
      • 自定义starter
  • 一、WebMvcAutoConfiguration
    • 1.1 Filter
    • 1.2 Interceptor
  • 二、源码解析
    • 2.1 SpringApplication
      • 2.1.1 构造方法
        • 1、填充webApplicationType
        • 2、自动装配Initializers
        • 3、自动装配Listeners
      • 2.1.2 run(args)
    • 2.2 SpringApplicationRunListeners
    • 2.3 prepareEnvironment
      • 加载propertySources
      • 填充activeProfiles属性
      • 读取properties、xml、yaml后缀文件
    • 2.4 createApplicationContext
        • 创建DefaultListableBeanFactory
        • AnnotationBeanDefinitionReader
        • ClassPathBeanDefinitionScanner
    • 2.5 prepareContext
        • applyInitializers
        • 为beanFactory设置属性
        • 创建启动类beanDefinition
        • listeners.contextLoaded
    • 2.6 refreshContext
      • 自动装配
          • getAutoConfigurationMetadata
          • 去重 + 排除 + @conditional
            • 1、去重-getExclusions
            • 2、排除-getExclusions
            • 3、@conditional-filter
          • 自定义自动装配类
    • 2.7 tomcat组件
      • 2.7.1定义
      • 2.7.2 tomcat组件
        • Server
        • Service
        • Connector
          • 3.1 Endpoint
          • 3.2 Processor
          • 3.3 Adapter
        • Container
          • **Engine**
          • **Host 虚拟主机**
          • **Context上下文**
          • **Wrapper包装器**
      • 2.7.3 组件Lifecycle
    • 2.8 内嵌tomcat
      • 2.8.1 getWebServerFactory
      • 2.8.2 getWebServer
        • tomcat-所有组件init
        • tomcat-engine-启动
    • 2.9 finishRefresh
      • 2.9.1 startWebServer-NioEndpoint启动
        • 4、Thread.run:Acceptor监听
        • 5.处理请求:Handler处理请求
        • 6、processor.process
        • 7、CoyoteAdapter.service
        • 8、Container-Engineer
        • 9、Container-Host
        • 10、Container-Context
        • 11、Container-Wrapper
        • 12、后续内容
      • 2.9.2 publishEvent
    • 2.10 stop-tomcat
  • 注解
    • @SpringBootApplication

Springboot中文文档

依赖管理

pom依赖管理

1)在SpringBoot项目中,根pom内容

<parent><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-parent</artifactId><version>2.1.6.RELEASE</version>
</parent>

2)此spring-boot-starter-parent-2.1.6.RELEASE.pom中仍有父pom

<parent><groupId>org.springframework.boot</groupId><artifactId>spring-boot-dependencies</artifactId><version>2.1.6.RELEASE</version><relativePath>../../spring-boot-dependencies</relativePath>
</parent>

3)spring-boot-dependencies-2.1.6.RELEASE.pom

<properties><!-- 指定了我们常用中间件的版本号-->	<spring.version>5.1.8.RELEASE</spring.version><byte-buddy.version>1.9.13</byte-buddy.version><commons-lang3.version>3.8.1</commons-lang3.version><elasticsearch.version>6.4.3</elasticsearch.version><gson.version>2.8.5</gson.version><jedis.version>2.9.3</jedis.version><kafka.version>2.0.1</kafka.version><lombok.version>1.18.8</lombok.version><mysql.version>8.0.16</mysql.version><netty.version>4.1.36.Final</netty.version><rabbit-amqp-client.version>5.4.3</rabbit-amqp-client.version><spring-amqp.version>2.1.7.RELEASE</spring-amqp.version><spring-kafka.version>2.2.7.RELEASE</spring-kafka.version>----
</properties><!-- SpringBoot支持提供的场景启动器starter-->	
<dependencyManagement><dependencies><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot</artifactId><version>2.1.6.RELEASE</version></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-test-autoconfigure</artifactId><version>2.1.6.RELEASE</version></dependency><!-- 指定了我们常用中间件的版本号-->	</dependencies> 
</dependencyManagement>   

这样我们在项目根pom中引入中间件时,就无需再指定version。当然如果指定version,则以我们指定的version版本为主


Web依赖

1、我们在项目根pom中,引入了web依赖

<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId><version>3.0.6</version>
</dependency>

2、spring-boot-starter-web-3.0.6.pom中

<dependencies><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-tomcat</artifactId><version>3.0.6</version><scope>compile</scope></dependency><dependency><groupId>org.springframework</groupId><artifactId>spring-web</artifactId><version>6.0.8</version><scope>compile</scope></dependency><dependency><groupId>org.springframework</groupId><artifactId>spring-webmvc</artifactId><version>6.0.8</version><scope>compile</scope></dependency></dependencies>

又引入了tomcat 和 spring-webmvc

3、依赖传递

  • 因为spring-boot-starter-web-3.0.6.pom中有spring-webmvc的依赖
  • 又因为我们项目根pom中,依赖了spring-boot-starter-web-3.0.6
  • 所以根pom有spring-webmvc的依赖
  • 所以,在我们项目中,就可以使用@Controller注解(org.springframework.web.bind.annotation)是spring-web下的内容

4、其它场景的starter

除了spring-boot-starter-web引入Web,还可以引入其它场景的依赖启动器

地址:SpringBoot提供的常见依赖启动器


自定义starter

1、背景

有些中间件,SpringBoot没有为其提供启动器,如mybatis、Druid。这些中间件自己主动与SpringBoot完成整合,提供了启动器

<dependency><groupId>org.mybatis.spring.boot</groupId><artifactId>mybatis-spring-boot-starter</artifactId><version>2.1.4</version>
</dependency>

所以,从名称上看,mybaits不是spring-boot-starter-mybatis,而是mybatis-spring-boot-starter。

说明了mybaits不是SpringBoot本身就提供的启动器

注意:

  • SpringBoot提供的starter,在引入时,可以不指定版本号,因为parsent的parent:spring-boot-dependencies-2.1.6.RELEASE.pom中指定了默认版本号
  • 但是SpringBoot没有提供的starter就必须自己指定版本号

2、整合redis

1)starter

<!-- https://mvnrepository.com/artifact/org.springframework.boot/spring-boot-starter-data-redis -->
<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-data-redis</artifactId>
</dependency><dependency><groupId>org.springframework.data</groupId><artifactId>spring-data-redis</artifactId><version>2.1.9.RELEASE</version>
</dependency>

2)配置redis

  • applicaiton.properties
spring.redis.host=127.0.0.1
spring.redis.port=6379

3)测试

@Resource
private StringRedisTemplate stringRedisTemplate;//或者RedisTemplate@Test
public void t() {String name = "mjp";Integer age = 18;String key = stringRedisTemplate.opsForValue().get(name);if (StringUtils.isBlank(key)) {stringRedisTemplate.opsForValue().set(name, String.valueOf(age));}
}
  1. 去redis客户端工具中

redis的安装,参考我另一篇:redis安装

get name

一、WebMvcAutoConfiguration

在SpringBoot中主要作用于Web MVC的自动配置,包括视图解析器、静态资源配置、MVC配置、消息转换器和异常处理等。

1.1 Filter

1、背景

参考我设计模式:设计模式Java实战

2、使用

  • Filter1
public class MyFilter1 implements Filter{@Overridepublic void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {// 先拦截reqSystem.out.println("======================filter-req");HttpServletRequest httpRequest = (HttpServletRequest) request;String uri = httpRequest.getRequestURI();if (StringUtils.isNoneBlank(uri)) {String[] split = uri.split("/");if (split.length > 2) {String firstParam = split[2]; // 第一个参数//return;//终止执行业务逻辑}}chain.doFilter(request, response);//执行目标方法// 再拦截respSystem.out.println("======================filter1-resp");}
}
  • Filter2
public class MyFilter2 implements Filter{@Overridepublic void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {System.out.println("===========================filter2-req");chain.doFilter(request, response);System.out.println("===========================filter2-resp");}
}
  • 注入Spring
@Configuration
public class WebConfig {@Beanpublic FilterRegistrationBean<MyFilter> myFilter1 (){FilterRegistrationBean<MyFilter> bean = new FilterRegistrationBean<>(new MyFilter());bean.setOrder(2);//指定生效顺序bean.setUrlPatterns(Sets.newHashSet("/query/*"));//指定拦截的请求路径return bean;}@Beanpublic FilterRegistrationBean<MyFilter2> myFilter2 (){FilterRegistrationBean<MyFilter2> bean = new FilterRegistrationBean<>(new MyFilter2());bean.setOrder(1);bean.setUrlPatterns(Sets.newHashSet("/query/*"));// 不指定,则默认"/*"return bean;}
}

3、补充

  • 作用域是Tomcat容器级别的。所以默认对所有的Servlet进行Filter

1.2 Interceptor

1、作用域

MVC框架的一部分

2、自定义拦截器

@Component
public class MyHandlerInterceptor implements HandlerInterceptor {public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)throws Exception {// 为false会拦截请求,true会放行// 业务逻辑// eg:根据req内容查询,请求是否合法、用户是否存在等。如果不满足,则请求被拦截掉,return falseSystem.out.println("=====================MyHandlerInterceptor-req符合,放行");return true;}public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler,@Nullable ModelAndView modelAndView) throws Exception {System.out.println("=====================MyHandlerInterceptor-resp");}public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler,@Nullable Exception ex) throws Exception {// 一定会执行,类似finallySystem.out.println("finally==============================");}
}

3、将自定义拦截器添加到拦截器链

@Configuration
public class WebConfig implements WebMvcConfigurer {@Resourceprivate MyHandlerInterceptor myHandlerInterceptor;@Overridepublic void addInterceptors(InterceptorRegistry registry) {// 添加自定义拦截器,并指定要拦截的路径registry.addInterceptor(myHandlerInterceptor)
//                .addPathPatterns("/**") // 拦截所有路径,或者指定具体的路径.addPathPatterns("/query/*") // 拦截所有路径,或者指定具体的路径.excludePathPatterns("/login"); // 排除某些路径}
}

4、Filter + HandlerInterceptor整体执行流程

=====================filter-req
=====================MyHandlerInterceptor-req符合,放行
=====do业务方法=======
=====================MyHandlerInterceptor-resp
=====================MyHandlerInterceptor-finally
=====================filter-resp

1)http请求 -->> Filter1#doFilter

  • 过滤req1
  • chain.doFilter -->> ApplicationFilterChain#doFilter ,找到下一个Filter2,Filter2#doFilter
  • 过滤req2
  • chain.doFilter -->> ApplicationFilterChain#doFilter,找不到下一个Filter了,执行servlet.service

2)servlet.service

->子关系 以及 各自方法HttpServlet(3.service、4.doGet)FrameworkServlet(2.service、5.doGet、6.processRequest 、7.doService)DispatcherServlet(1.service 、8.doService、9.doDispatch)

servlet.service-- >> DispatcherServlet#service -->>父类FrameworkServlet#service -->> super.service即HttpServlet#service

  • 获取方法类型GET(或Post) -->>doGet(或doPost) -->> 子类FrameworkServlet重写#doGet

    –>>processRequest -->> doService

    –>> 子类DispatcherServlet#doService -->> doDispatch

3)doDispatch

// Determine handler for the current request.
// 1.找到我们的Controller
mappedHandler = getHandler(processedRequest);// 2.适配
HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());// 3.是否前置拦截applyPreHandle,ture则放行,false则拦截。不执行后续的业务方法
// 执行拦截器链的一系列applyPreHandle
if (!mappedHandler.applyPreHandle(processedRequest, response)) {return;
}// 4.执行业务方法,即本次请求的方法
mv = ha.handle(processedRequest, response, mappedHandler.getHandler());// 5.后置方法
mappedHandler.applyPostHandle(processedRequest, response, mv);

二、源码解析

2.1 SpringApplication

SpringApplication.run(ApplicationLoader.class, args);

内部代码等价

SpringApplication springApplication = new SpringApplication(ApplicationLoader.class);
springApplication.run(args);

SpringApplication类定义

执行以下步骤来启动Spring应用程序

  • 创建ApplicationContext-Spring上下文
  • 刷新应用上下文,加载所有单例bean

2.1.1 构造方法

1、填充webApplicationType

属性:REACTIVE、NONE、SERVLET(这里是SERVLET)

2、自动装配Initializers

1)读取spring.factories内容

spring-boot-2.1.6.RELEASE.jar!/META-INF/spring.factories,封装为map

补充:springboot中总共有2个spring.factories文件,一个为上述路径,还有一个路径为:

spring-boot-autoconfigure-2.1.6.RELEASE.jar!\META-INF\spring.factories,这个和自动装配@Import(AutoConfigurationImportSelector.class)注解有关

2)根据key,获取val集合

  • key:ApplicationContextInitializer
  • val:spring.factories中key是ApplicationContextInitializer类对应的val

3)将val集合List<String全类名>实例化

  • 通过ClassForName实例化

4)将上述自动装配map,存cache,便于下一此直接从缓存中读取,避免再次加载


3、自动装配Listeners

过程同2,不过map根据key获取val,此map注解从cache中获取。

key为:ApplicationListener

2.1.2 run(args)

springApplication.run(args);

其中,可以定义args:在IDEA中设置方式如图3所示
在这里插入图片描述

这样在启动时,args就为"–debug"

作用:可以人为设置参数,比如"–debug",可以打印自动装配过程日志信息


2.2 SpringApplicationRunListeners

1、内容

1)从spring-boot-2.1.6.RELEASE.jar!/META-INF/spring.factories中

获取key:SpringApplicationRunListener

对应的val:org.springframework.boot.context.event.EventPublishingRunListener

这里可以直接从cache中读取,无需从spring.factories中加载了

2)创建SpringApplicationRunListeners

将val:EventPublishingRunListener作为属性,通过构造方法,填充给SpringApplicationRunListeners

2、启动监听

SpringApplicationRunListeners listeners = getRunListeners(args);
listeners.starting();

–>> EventPublishingRunListener#starting

this.initialMulticaster.multicastEvent(new ApplicationStartingEvent(this.application, this.args)
);

这里发布的第一个事件:1、ApplicationStartingEvent

Spring观察者模式内容参考我另一篇:Spring源码剖析-Spring观察者模式

  • 创建一个事件:应用程序启动事件

  • 获取多播器initialMulticaster

    • 内含监听器listener
    • 这些监听器就是SpringApplication构造方法中,自动装配Listeners
    • 然后执行supportsEvent过滤,留下能够监听ApplicationStartingEvent事件的监听器
  • 进行广播事件:multicastEvent

    • 循环获取监听器
    • 执行invokeListener(listener, event) -->> listener.onApplicationEvent(event)

多播器会循环调用监听器的处理方法,将应用程序启动此事件,进行了广播给监听器进行处理。


2.3 prepareEnvironment

方法内容:加载环境变量、配置文件等

private ConfigurableEnvironment prepareEnvironment(SpringApplicationRunListeners listeners,ApplicationArguments applicationArguments) {//1.创建environment,加载propertySourcesConfigurableEnvironment environment = getOrCreateEnvironment();//2.填充activeProfiles属性:spring.profiles.activeconfigureEnvironment(environment, applicationArguments.getSourceArgs());// 3.读取properties、xml、yaml后缀文件listeners.environmentPrepared(environment);bindToSpringApplication(environment);if (!this.isCustomEnvironment) {environment = new EnvironmentConverter(getClassLoader()).convertEnvironmentIfNecessary(environment,deduceEnvironmentClass());}ConfigurationPropertySources.attach(environment);return environment;
}

加载propertySources

getOrCreateEnvironment

  • 根据webApplicationType类型SERVLET,new StandardServletEnvironment()
  • 调用其爷爷的构造方法,执行customizePropertySources --> 孙子StandardServletEnvironment#customizePropertySources
  • 完成servletContextInitParams、servletConfigInitParams两个ProertySource的加载
  • 再调用父类customizePropertySources ,完成systemEnvironment和systemProperties两个PropertySource的加载

至此environment中propertySources属性中有4和资源配置


填充activeProfiles属性

configureEnvironment

主要内容就是,填充activeProfiles属性。

假如我们在IDEA的 在VM Options中:-Dspring.profiles.active = prod,则此时

environment中activeProfiles属性中有一个值"prod"


读取properties、xml、yaml后缀文件

listeners.environmentPrepared

–>> EventPublishingRunListener#environmentPrepared -->> multicastEvent

  • 获取所有listener,并过滤监留下听ApplicationEnvironmentPreparedEvent事件的监听器

这里发布的第二个事件:2、ApplicationEnvironmentPreparedEvent

  • 其中最重要的监听器ConfigFileApplicationListener

– >>invokeListener(listener, event) -->> doInvokeListener -->> listener.onApplicationEvent(event)–>>onApplicationEnvironmentPreparedEvent

private void onApplicationEnvironmentPreparedEvent(ApplicationEnvironmentPreparedEvent event) {// 从spring.factories中找到key:EnvironmentPostProcessor对应的val集合List<EnvironmentPostProcessor> postProcessors = loadPostProcessors();// 将ConfigFileApplicationListener监听器本身也加入val集合postProcessors.add(this);AnnotationAwareOrderComparator.sort(postProcessors);// 遍历val集合,最重要的是:执行ConfigFileApplicationListener#postProcessEnvironmentfor (EnvironmentPostProcessor postProcessor : postProcessors) {postProcessor.postProcessEnvironment(event.getEnvironment(), event.getSpringApplication());}
}

–>> ConfigFileApplicationListener#postProcessEnvironment -->> addPropertySources

new Loader(environment, resourceLoader).load()

1、new Loader

this.propertySourceLoaders = SpringFactoriesLoader.loadFactories(PropertySourceLoader.class,getClass().getClassLoader()
);

从spring.factories读取key:PropertySourceLoader对应的val集合

# PropertySource Loaders
org.springframework.boot.env.PropertySourceLoader=\
org.springframework.boot.env.PropertiesPropertySourceLoader,\
org.springframework.boot.env.YamlPropertySourceLoader
  • PropertiesPropertySourceLoader:读取.properties文件 和 .xml文件
  • YamlPropertySourceLoader:读取.yaml文件 和 .yml文件

2、laod

–>> addLoadedPropertySources

将application.properties文件中内容加载到propertySources集合中(同理加载yml),至此propertySources中有五个元素

  • servletContextInitParams
  • servletConfigInitParams
  • systemEnvironment
  • systemProperties
  • classpath:/application.properties

2.4 createApplicationContext

1、方法内容

  • 根据webApplicationType类型获取对应的上下文class类:Class.forName(AnnotationConfigServletWebServerApplicationContext)
  • 通过反射ctor.newInstance(),创建上下文对象

2、构造函数

public AnnotationConfigServletWebServerApplicationContext() {this.reader = new AnnotatedBeanDefinitionReader(this);this.scanner = new ClassPathBeanDefinitionScanner(this);
}
创建DefaultListableBeanFactory
父子关系
GenericApplicationContextGenericWebApplicationContextServletWebServerApplicationContextAnnotationConfigServletWebServerApplicationContext
  • 执行AnnotationConfigServletWebServerApplicationContext构造方法之前,会先调用父类的构造方法,一层一层直到GenericApplicationContext
public GenericApplicationContext() {// 创建了beanFactorythis.beanFactory = new DefaultListableBeanFactory();
}

AnnotationBeanDefinitionReader

加载5个internalXxxProcessor(bfpp)到bdMap中,如图1所示
在这里插入图片描述

ClassPathBeanDefinitionScanner

后续invokeBfpp时,会使用此Scanner,对配置类进行解析,生成对应的beanDefinition


2.5 prepareContext

方法内容:为context上下文对象填充属性

applyInitializers
  • 获取模块2.1中Initializers,即从spring.factories中读取到的7个Initializer
  • 依次遍历执行他们的initialize
  • 完成对context上下文的属性填充

主要填充了context的BeanFactoryPostProcessors属性、applicationListeners属性


为beanFactory设置属性

allowBeanDefinitionOverriding = false

Spring 容器中有相同名称的多个 Bean 定义时,是否允许后续的 Bean 定义覆盖先前的定义

  • false:如果尝试注册一个已经存在的 Bean 定义,将会抛出一个异常

创建启动类beanDefinition
// 获取primarySource,即启动类
Set<Object> sources = getAllSources();// 将启动类抽取为BeanDefinition,存入bdMap
load(context, sources.toArray(new Object[0]));

listeners.contextLoaded

这里发布的第三个事件:3、ApplicationPreparedEvent

至此三个事件:

  • ApplicationStartingEvent
  • ApplicationEnvironmentPreparedEvent
  • ApplicationPreparedEven

均被多播器SimpleApplicationEventMulticaster广播了出去,对此三个事件感兴趣的监听器,也完成了监听处理动作


2.6 refreshContext

对应Spring中的refresh方法。即IOC

此部分的源码剖析,参考我另一篇:Spring源码剖析

自动装配

具体流程参考我的另一篇:Spring源码剖析中第二章节IOC的法五中的@Import注解解析

这里只补充下,自动装配后的过滤逻辑:

getAutoConfigurationEntry(getAutoConfigurationMetadata(), annotationMetadata)
getAutoConfigurationMetadata

1、方法内容

从META-INF/spring-autoconfigure-metadata.properties路径下读取配置类的依赖类信息,封装成map

  • 文件内容如图15所示
    在这里插入图片描述

  • 封装的map即autoConfigurationMetadata(自动装配类.条件=值)

2、方法作用

为了后续将自动装配的类,进行Filter操作,只留下符合项目需要的自动装配类

1)autoConfigurationMetadata中内容

k1:org.springframework.boot.autoconfigure.aop.AopAutoConfiguration.ConditionalOnClass

v1:

org.aspectj.lang.annotation.Aspect,
org.aspectj.lang.reflect.Advice,
org.aspectj.weaver.AnnotatedElement,
org.springframework.context.annotation.EnableAspectJAutoProxy

2)我们看下AopAutoConfiguration此自动装配类的内容

@Configuration
@ConditionalOnClass({ EnableAspectJAutoProxy.class, Aspect.class, Advice.class, AnnotatedElement.class })
public class AopAutoConfiguration {
}

@ConditionalOnClass注解中属性:EnableAspectJAutoProxy、Aspect、Advice。


去重 + 排除 + @conditional
protected AutoConfigurationEntry getAutoConfigurationEntry(AutoConfigurationMetadata autoConfigurationMetadata,AnnotationMetadata annotationMetadata) {AnnotationAttributes attributes = getAttributes(annotationMetadata);//1.自动装配-120个List<String> configurations = getCandidateConfigurations(annotationMetadata, attributes);//2.去重后,仍为120个configurations = removeDuplicates(configurations);// 3.排除后,119个Set<String> exclusions = getExclusions(annotationMetadata, attributes);configurations.removeAll(exclusions);//排除// 4.自动装配类@conditional注解修饰后,还剩下configurations = filter(configurations, autoConfigurationMetadata);// 返回最终剩下的return new AutoConfigurationEntry(configurations, exclusions);
}

在自动装配完成后,key:,对应的val集合中有装配类 120个,如图4所示
在这里插入图片描述

1、去重-getExclusions
2、排除-getExclusions
// 这个要被排除的类的全路径org.springframework.boot.autoconfigure.jdbc.类名;
@SpringBootApplication(exclude = DataSourceAutoConfiguration.class)
public class ApplicationLoader {public static void main(String[] args) {SpringApplication springApplication = new SpringApplication(ApplicationLoader.class);springApplication.run(args);}
}
  • 排除前:自动装配类,如图5所示
    在这里插入图片描述

  • 排除后:自动装配,没有此类了,剩下119个自动装配类,如图6所示
    在这里插入图片描述

3、@conditional-filter

3.1 步骤一:从spring.factories中获取key:AutoConfigurationImportFilter对应的val集合:用于filter

g.springframework.boot.autoconfigure.AutoConfigurationImportFilter=\org.springframework.boot.autoconfigure.condition.OnBeanCondition,\
org.springframework.boot.autoconfigure.condition.OnClassCondition,\
org.springframework.boot.autoconfigure.condition.OnWebApplicationCondition

三折共同的父类FilteringSpringBootCondition 实现了Condition接口,重写了match方法,用于匹配

3.2 使用这三个Filter,过滤候选的119个自动装配类

//依次遍历Filter
// 使用Filter,在autoConfigurationMetadata内容下,判断候选装配类在classpath路径下是否有指定的(Condition)的bean、class等
// 需要的,则留下,不需要的则skipped = true
for (AutoConfigurationImportFilter filter : getAutoConfigurationImportFilters()) {invokeAwareMethods(filter);boolean[] match = filter.match(candidates, autoConfigurationMetadata);for (int i = 0; i < match.length; i++) {if (!match[i]) {skip[i] = true;candidates[i] = null;skipped = true;}}
}

filter.match(candidates, autoConfigurationMetadata)

1)candidates:就119个候选装配类

2)autoConfigurationMetadata,即map:自动装配的类.条件 = 值

根据下面的条件注解@ConditionalOnXxx和元数据autoConfigurationMetadata进行筛选,如果其条件满足(即所有条件注解和元数据条件都通过),则将其标记为匹配成功。

3.3这里以2个自动装配类为例,

  • (这里参考2.1.2 run(args),配置–debug参数,便于观察自动装配filter日志信息)

  • @ConditionalOnXxx相关注解含义如图2所示:即系统中满足指定的条件,才会去实例化此对象

    作用:配置类中某些地方会使用到Xxx.方法,这就要求Xxx必须存在,否则npe

在这里插入图片描述

其中

  • 类org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration因符合条件被留下来
matched:- @ConditionalOnClass found required classes 'javax.sql.DataSource', 'org.springframework.jdbc.datasource.embedded.EmbeddedDatabaseType' (OnClassCondition)
  • 类org.springframework.boot.autoconfigure.kafka.KafkaAutoConfiguration因为不符合条件被过滤掉
Did not match:- @ConditionalOnClass did not find required class 	'org.springframework.kafka.core.KafkaTemplate' (OnClassCondition)

3.1)DataSourceAutoConfiguration

// 和@Conditional相关注解如下
@Configuration
@ConditionalOnClass({ DataSource.class, EmbeddedDatabaseType.class })
public class DataSourceAutoConfiguration {}
  • 自动装配类DataSourceAutoConfiguration被@ConditionalOnClass注解修饰

  • 此注解的作用:系统中有指定的类时,自动装配生效

  • 获取@ConditionalOnClass注解内容:DataSource和EmbeddedDatabaseType

  • 我pom文件中,引入了mysql-connector-java相关依赖,所以beanFactory中有DataSource类

3.2)KafkaAutoConfiguration

// 和@Conditional相关注解如下
@Configuration
@ConditionalOnClass(KafkaTemplate.class)
public class KafkaAutoConfiguration {}
  • 自动装配类KafkaAutoConfiguration被@ConditionalOnClass注解修饰

  • 此注解的作用:系统中有指定的类时,自动装配生效

  • 我的pom文件中,未引入任何与kafak相关的依赖

  • 所以filter.match(candidates, autoConfigurationMetadata) -->>

    filter.match(KafkaAutoConfiguration, autoConfigurationMetadata)不符合条件,会过滤掉


自定义自动装配类
@Configuration
@ConditionalOnBean(UserController.class)
public class MyAutoConfig {
}
  • @Configuration
  • @ConditionalXxx相关注解
MyAutoConfig matched:- @ConditionalOnBean (types: com.mjp.tx.UserController; SearchStrategy: all) found bean 'userController' (OnBeanCondition)

自定义的装配类被加载了


2.7 tomcat组件

因为Springboot内嵌了tomcat。这里前置先分析下tomcat的源码,为后续getWebServer方法剖析作铺垫

2.7.1定义

  • tomcat是web服务器,本质是Servlet容器

  • Servlet容器是一个接口规则,定义了Java类被Tomcat服务器识别的规则


2.7.2 tomcat组件

整体组件架构图如图7所示
在这里插入图片描述

Server

1、定义

表示一个Tomcat实例

2、结构

包括多个Service服务

Service

1、定义

提供服务来处理请求的组件

2、结构

包括多个Connector和一个Container

Connector

1、定义

  • 是客户端连接到Tomcat容器的服务点 ,使得客户端可以通过Connector与服务器建立连接、发送请求并接收响应

  • 每个连接器监控一个指定的IP及端口,并通过指定的协议做出响应

<Service name="Catalina"><Connector port="8080" protocol="HTTP/1.1"connectionTimeout="20000"redirectPort="8443"/><Connector port="8009" protocol="AJP/1.3" redirectPort="8443"/>...
<Service/>

2、具体实现

Coyote

3、结构

如图8所示

在这里插入图片描述

3.1 Endpoint

1)定义

  • 通信端点即通信监听的接口,是具体的Socket接收处理类

2)具体实现

  • Tomcat并没Endpoint接口,而是一个抽象类AbstractEndpoint其默认实现类NioEndpoint

3)AbstractEndpoint(具体实现Nio2Endpoint)结构

  • 属性Acceptor(默认实现):用于监听请求
  • 内部接口Handler(默认实现ConnectionHandler): 用于处理接收到的Socket,在内部调用Processor进行处理。
3.2 Processor

1)定义

  • Processor是协议处理接口

2)具体实现类

Http11Processor

3)功能

  • 将tcp/IP请求转为http1.1协议请求
  • 将http请求封装成request对象
3.3 Adapter

1)定义

适配器,request对象转换为ServletRequest交给Container进行处理

4、总结

http://127.0.0.1:8080/xxx

  • Nio2Endpoint中Acceptor监听请求
  • ConnectionHandler处理接收到的Socket,交由Processor
  • Processor将tcp/ip协议转为http1.1请求,并封装request
  • Adapter将request换为ServletRequest,交由Container
Container

Container用于封装和管理Servlet,以及具体处理Request请求;

整体结构如图9所示
在这里插入图片描述

Container-Engine:

  • host1:127.0.0.1
    • context1:/login
      • Wrapper
    • context2:/query
      • Wrapper
  • host2:123.456.78.90
Engine

将传入请求委托给适当的虚拟主机处理

Host 虚拟主机
  • 一个虚拟主机下都可以部署一个或者多个Web App,每个Web App对应于一个Context
  • 当Host获得一个请求时,将把该请求匹配到某个Context上
Context上下文

表示Web应用程序本身 ,即Servlet上下文

Wrapper包装器
  • 代表一个 Servle。负责管理一个 Servlet的装载、初始化、执行以及资源回收 :如init()、service()和destroy()方法 。

总结

来自客户的请求为:http://localhost:8080/user/login 请求被发送到本机端口8080

1、Connection

  • Nio2Endpoint中Acceptor监听请求
  • ConnectionHandler处理接收到的Socket,交由Processor
  • Processor将tcp/ip协议转为http1.1请求,并封装request
  • Adapter将request换为ServletRequest,交由Container

2、Container

Engine来处理,并等待Engine的回应

  • Engine获得请求localhost:8080/user/login,匹配到名为localhost的Host

  • localhost Host获得请求/user/login,匹配它所拥有的所有Context

  • Context获得请求,寻找对应的servlet

    匹配到HttpServlet类,构造HttpServletRequest对象和HttpServletResponse对象

  • 调用HttpServlet的doGet或doPost方法


2.7.3 组件Lifecycle

1、内容

将所有组件的生命周期抽象为一个接口

2、生命周期 以及 结构

父子关系以及方法图
Lifecycle(全抽象方法init、start、stop、destroy、getState)LifecycleBase(init、抽象initInternal、start、抽象startInternal)LifecycleMBeanBase(initInternal、destroyInternal)StandardServer(操作Server相关方法、initInternal、startInternal)StandardService(操作ContainerConnector相关方法、initInternal、	startInternal、destroyInternal)

2.8 内嵌tomcat

onFeresh

2.8.1 getWebServerFactory

1、方法作用

根据ServletWebServerFactory.class找到对应的WebServerFactory:tomcatServletWebServerFactory

2、方法调用流程

onRefresh -->> createWebServer -->> getWebServerFactory -->> getBeanNamesForType -->> DefaultListableBeanFactory#getBeanNamesForType -->> doGetBeanNamesForType -->>

3、bdMap中为什么会存在tomcatServletWebServerFactory

1)SpringBoot自动装配,将ServletWebServerFactoryAutoConfiguration类从spring.factories文件中读取出来。

类结构如下

@Configuration
@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 {@Bean@ConditionalOnClass(name = "org.apache.catalina.startup.Tomcat")public TomcatServletWebServerFactoryCustomizer tomcatServletWebServerFactoryCustomizer(ServerProperties serverProperties) {return new TomcatServletWebServerFactoryCustomizer(serverProperties);}
}

内含TomcatServletWebServerFactoryCustomizer此bean

2)pom中引入了starter-web依赖

<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId><version>3.0.6</version>
</dependency>

所以,在自动装配过滤的时候,不会将ServletWebServerFactoryAutoConfiguration此装配类过滤掉。

原因:

@ConditionalOnClass(ServletRequest.class):web服务
@ConditionalOnWebApplication(type = Type.SERVLET):从2.1.1中可知,类型为SERVLET

4、getBean(“tomcatServletWebServerFactory”, ServletWebServerFactory.class)

创建tomcatServletWebServerFactory此bean对象

2.8.2 getWebServer

tomcat-所有组件init
public WebServer getWebServer(ServletContextInitializer... initializers) {// 1.创建tomcatTomcat tomcat = new Tomcat();File baseDir = (this.baseDirectory != null) ? this.baseDirectory : createTempDir("tomcat");tomcat.setBaseDir(baseDir.getAbsolutePath());// 2.创建connectorConnector connector = new Connector(this.protocol);//Http11NioProtocoltomcat.getService().addConnector(connector);customizeConnector(connector);tomcat.setConnector(connector);// 3.创建Engine、Host、Servicetomcat.getHost().setAutoDeploy(false);configureEngine(tomcat.getEngine());// 4.创建Wrapper、ServletprepareContext(tomcat.getHost(), initializers);// 5.创建TomcatWebServer:完成组件的initreturn getTomcatWebServer(tomcat);
}

1、tomcalt属性

  • hostname:“localhost”
  • port:8080

2、connector属性

protocolHandler:Http11NioProtocol

  • maxHttpHeaderSize:8192
  • endpoint
    • handler:ConnectionHandler
      • proto:Http11NioProtocol
    • acceptor:在最后2.9.1中完成创建
    • selectorPool
      • maxSelector:200
    • nioChannel:
    • maxConnection:10000
    • port:8080
    • maxThreads:200
  • **adapter:**init时会创建CoyoteAdapter

3、Engine属性

  • children:standardHost

    • contextClass:standardContext
    • appBase:webApps
  • defaultHost:“localhost”

  • service:standardService

    • connectors:Connector[HTTP/1.1-8080]
    • engineer:StandardEngine[Tomcat]
  • state:LifeCycleState:new

4、Wrapper属性

new StandardWrapper

  • servletClass:org.apache.catalina.servlets.DefaultServlet

5、创建TomcatWebServer:完成组件的init初始化

new TomcatWebServer -->> initialize -->> this.tomcat.start() -->>

Tomcat#start -->> server.start(StandardServer)-- >> LifecycleBase#start

if (state.equals(LifecycleState.NEW)) {//显然Engine属性state是NEW// 步骤一:initinit();
}// 步骤二:启动
startInternal();

=======================步骤一:init开始-=------------------------------

LifecycleBase#init – initInternal -->> 孙子StandardServer#initInternal -->> services[i].init() -->>LifecycleBase#init

  • engineer#init

  • connector#init

    • adapter完成初始化
    adapter = new CoyoteAdapter(this);
    protocolHandler.setAdapter(adapter);
    
    • protocolHandler#init
    • endpoint#init

=======================步骤一:init结束-=------------------------------

tomcat-engine-启动

=======================步骤二:启动-=------------------------------

父子关系以及方法图
Lifecycle(全抽象方法init、start、stop、destroy、getState)LifecycleBase(init、抽象initInternal、start、抽象startInternal)LifecycleMBeanBase(initInternal、destroyInternal)StandardServer(操作Server相关方法、initInternal、startInternal)StandardService(操作ContainerConnector相关方法、initInternal、	startInternal、destroyInternal)

LifecycleBase#startInternal–>>StandardServer#startInternal -->> services[i].start()

– >> LifecycleBase#start -->> startInternal -->> StandardService#startInternal

engine.start();//child.start,一系列Host、Context等start             

2.9 finishRefresh

2.9.1 startWebServer-NioEndpoint启动

webServer.start -->> TomcatWebServer#start -->> addPreviouslyRemovedConnectors -->>

service.addConnector -->> connector.start -->>LifecycleBase#start -->> startInternal -->>

Connector#startInternal -->>

AbstractProtocol#start -->>

AbstractEndpoint#start -->>

public final void start() throws Exception {// 步骤一.创建Socketif (bindState == BindState.UNBOUND) {bindWithCleanup();bindState = BindState.BOUND_ON_START;}// 步骤二.NioEndpoint#startInternalstartInternal();
}

步骤一:bindWithCleanup

ServerSocketChannel serverSock = ServerSocketChannel.open();
serverSock.socket().bind(addr,getAcceptCount());//绑定8081端口,acceptCount = 100// NioSelectorPool==》》BlockPoller.start阻塞轮询器启动
selectorPool.open(getName());

步骤二:NioEndpoint#startInternal

// 1.创建Executor
createExecutor()// 2.创建Poller
poller = new Poller();Thread pollerThread = new Thread(poller, getName() + "-ClientPoller");
pollerThread.setPriority(threadPriority);
pollerThread.setDaemon(true);
pollerThread.start();// 3.创建Thread并start-Acceptor!!!
startAcceptorThread();

1、createExecutor

  • keepAlive:60000000000ns即60s
  • workers:10个ThreadPoolExecutor
  • coolPoolSize:10
  • maximumPoolSize:200

2、创建poller

  • selector
  • 同步队列

3、startAcceptorThread

protected void startAcceptorThread() {// 3.1 创建acceptoracceptor = new Acceptor<>(this);String threadName = getName() + "-Acceptor";acceptor.setThreadName(threadName);// 3.2 创建线程Thread t = new Thread(acceptor, threadName);t.setPriority(getAcceptorThreadPriority());t.setDaemon(getDaemon());// 3.3启动t.start();
}
  • name:http-nio-8080-Acceptor

  • tid:

  • target:Acceprot

    • endpoint:nioEndpoint
  • group

    • name:“main”
    • nThreads:16

    如图10所示

在这里插入图片描述

至此整个tomcat容器完成启动,控制台打印日志:Tomcat started on port(s): 8080 (http) with context path。SpringBoot启动完成


4、Thread.run:Acceptor监听

Acceptor会一直监听,随时准备accept请求

–>> target.run -->> Acceptor#run -->>

Acceptor implements Runnable{
}

run方法内容

public void run() {int errorDelay = 0;// 一直循环监听while (endpoint.isRunning()) {// 1.当前状态设置为Runningstate = AcceptorState.RUNNING;try {//if we have reached max connections, wait// 2.当到达了最大连接,则等待endpoint.countUpOrAwaitConnection();// Endpoint might have been paused while waiting for latch// If that is the case, don't accept new connectionsif (endpoint.isPaused()) {continue;}U socket = null;try {// Accept the next incoming connection from the server// socket//!!!接收来自客户端的请求socket = endpoint.serverSocketAccept();} catch (Exception ioe) {}// Successful accept, reset the error delayerrorDelay = 0;// 其它closeSocket、destroySocket}
}

– >> endpoint.serverSocketAccept -->> NioEndpoint#serverSocketAccept -->>accept -->> ServerSocketChannelImpl#accept -->> accept -->> accept0

private native int accept0() {
}

5.处理请求:Handler处理请求

1、客户端发起请求:http://localhost:8080/query/18

2、服务器端

Thread#run -->> target.run -->> TaskThread内部类WrappingRunnable#run–>>wrappedRunnable.run

–>> ThreadPoolExecutor#run -->> runWorker -->> task.run -->> SocketProcessorBase#run -->> doRun -->> NioEndpoint#doRun -->> getHandler().process -->> AbstractProtocol(继承ProtocolHandler)#process

// 创建Processor,new Http11Processor
Processor processor = getProtocol().createProcessor();

6、processor.process

– AbstractProcessorLight#process -->> service -->> Http11Processor#service

  • 将tcp/IP请求转为http1.1协议请求
  • 将http请求封装成request对象

Request对象属性如图11所示

在这里插入图片描述

getAdapter().service(request, response)

  • 获取CoyoteAdapter
  • 执行service

7、CoyoteAdapter.service

1、方法作用

适配器,request对象转换为ServletRequest交给Container进行处理

2、内容

connector.getService().getContainer().getPipeline().getFirst().invoke(request, response);
  • getContainer获取container即StandardEngineer

– >> StandardEngineValve#invoke


8、Container-Engineer

StandardEngineer -->> StandardEngineValve#invoke -->>

host.getPipeline().getFirst().invoke(request, response);
9、Container-Host

StandardHost -->> ErrorReportValve#invoke -->> StandardHostValve#invoke

context.getPipeline().getFirst().invoke(request, response);
10、Container-Context

–>> AuthenticatorBase#invoke -->> getNext().invoke(request, response) -->>StandardContextValve#invoke

wrapper.getPipeline().getFirst().invoke(request, response);

pipleLine结构图如图12所示
在这里插入图片描述

11、Container-Wrapper

–>> StandardWrapperValve#invoke -->>

  • 属性requestCount,用于记录请求次数
// 步骤一.获取Servlet
Servlet servlet = wrapper.allocate();

DispatcherServlet属性如图13所示
在这里插入图片描述

//步骤二:为本次请求,创建Filter过滤器链
ApplicationFilterChain filterChain =ApplicationFilterFactory.createFilterChain(request, wrapper, servlet);
  • filterChain = new ApplicationFilterChain()
  • filterChain .add(系统自带的 和 用户定义的)
// 步骤三:执行Filter过滤器链
filterChain.doFilter(request.getRequest(), response.getResponse());

过滤器链如图14所示
在这里插入图片描述

其中MyFilter和MyFilter2是模块一中1.1 我们自定义的Filter


12、后续内容

–>> XxxFilter#doFilter

上接模块一中1.1 Filer,具体执行流程可参考1.2 Interceptor中执行流程


2.9.2 publishEvent

广播第四个事件:ServletWebServerInitializedEvent。ServletWeb容器已经完成实例化了,至此四个事件都已广播出去,并被相关监听器监听处理


2.10 stop-tomcat

private void refreshContext(ConfigurableApplicationContext context) {//1.spring的refresh方法refresh(context);//2.注册钩子函数context.registerShutdownHook();	}

上文执行完成整个Spring的refresh方法后,会注册一个钩子函数

1、钩子函数

refreshContext -->> AbstractApplicationContext#registerShutdownHook

public void registerShutdownHook() {if (this.shutdownHook == null) {this.shutdownHook = new Thread() {@Overridepublic void run() {synchronized (startupShutdownMonitor) {doClose();}}};Runtime.getRuntime().addShutdownHook(this.shutdownHook);}}

1、将shutdownHook此线程注入hooks

2、当上下文AbstractApplicationContext关闭时,则会

执行ApplicationShutdownHooks#静态代码块

class ApplicationShutdownHooks {private static IdentityHashMap<Thread, Thread> hooks;static {try {Shutdown.add(1 /* shutdown hook invocation order */,false /* not registered if shutdown in progress */,new Runnable() {public void run() {runHooks();}});hooks = new IdentityHashMap<>();}}
}

3、runHooks

for (Thread hook : threads) {hook.start();
}

遍历所有的钩子线程,执行start。此时我们AbstractApplicationContext中的shutdownHook线程就会执行run

–>> doClose

// Destroy all cached singletons in the context's BeanFactory.
destroyBeans();// Close the state of this context itself.
closeBeanFactory();// Let subclasses do some final clean-up if they wish...
// stop-tomcat
onClose();

onClose -->>ServletWebServerApplicationContext#onClose -->> stopAndReleaseWebServer -->> webServer.stop -->> TomcatWebServer#stop

stopTomcat();//停止
this.tomcat.destroy();//销毁

注解

@SpringBootApplication

使用@SpringBootApplication注解后,就无需使用@ComponentScan注解。因为@SpringBootApplication默认会扫描同级别下的所有包

如果想单独指定扫描路径,则可以使用@ComponentScan

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

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

相关文章

AI边缘计算盒子在智慧交通的应用

方案背景 随着经济增长&#xff0c;交通出行需求大幅增长&#xff0c;但道路建设增长缓慢&#xff0c;交通供需矛盾日益显著&#xff0c;中心城区主要道路高峰时段交通拥堵严重&#xff0c;道路交通拥堵逐渐常态化&#xff0c;成为制约城市可持续发展的重要因素之一。 痛点问题…

web 前端开发技术---网页的制作

这是一个网页代码 上年包含了电子邮件&#xff0c;选项建 等等 分享给大家 <!-- prj_7_1.html --> <!DOCTYPE html> <html lang"en"><head><meta charset"utf-8"><title>留言板设计</title><style type&…

【C++】入门(一):命名空间、缺省参数、函数重载

目录 一、关键字 二、命名空间 问题引入(问题代码)&#xff1a; 域的问题 1.::域作用限定符 的 用法&#xff1a; 2.域的分类 3.编译器的搜索原则 命名空间的定义 命名空间的使用 举个&#x1f330;栗子&#xff1a; 1.作用域限定符指定命名空间名称 2. using 引入…

K210视觉识别模块学习笔记3:内存卡写入拍摄图片_LED三色灯的操作_按键操作_定时器的配置使用

今日开始学习K210视觉识别模块: LED三色灯的操作_按键操作_定时器的配置使用_内存卡写入拍摄图片 亚博智能的K210视觉识别模块...... 本文最终目的是编写一个按键拍照的例程序&#xff1a; 为以后的专用场景的模型训练做准备&#xff0c;因为训练自己的模型需要大量的图片&a…

编译安装PHP服务(LAMP3)

目录 1.初始化设置&#xff0c;将安装PHP所需软件包传到/opt目录下 &#xff08;1&#xff09;关闭防火墙 &#xff08;2&#xff09;上传软件包到/opt目录 2.安装GD库和GD库关联程序&#xff0c;用来处理和生成图片 3.配置软件模块 4.编译及安装 5.优化把PHP 的可执行程…

nginx的安装001

Nginx是一款高性能的HTTP和反向代理服务器&#xff0c;以及邮件代理服务器&#xff0c;由 Igor Sysoev 开发并公开发布于2004年。Nginx以其高并发处理能力、低内存消耗和稳定性著称&#xff0c;特别适合部署在高流量的网站上。 操作系统&#xff1a; CentOS Stream 9 安装步骤…

企业如何利用社交媒体二维码做宣传?提升品牌形象

和普通的二维码不同&#xff0c;社交媒体二维码可以通过一个二维码链接企业的超过16的社交媒体渠道链接&#xff0c;包括&#xff1a;企业官网、小程序、公众号、淘宝店铺、抖音链接、小红书链接、美团链接、饿了么链接…等等。扫描之后&#xff0c;可以在这个社交媒体二维码界…

校园志愿者|基于SprinBoot+vue的校园志愿者管理系统(源码+数据库+文档)

校园志愿者管理系统 目录 基于SprinBootvue的校园志愿者管理系统 一、前言 二、系统设计 三、系统功能设计 1 系统功能模块 2管理员功能 3志愿者功能 四、数据库设计 五、核心代码 六、论文参考 七、最新计算机毕设选题推荐 八、源码获取&#xff1a; 博主介绍&a…

12.RedHat认证-Linux文件系统(下)

12.RedHat认证-Linux文件系统(下) swap虚拟内存 我加一个硬盘做实验sdc # 创建交换分区&#xff08;不用做成逻辑卷也能灵活分区&#xff09; [rootcentos8 ~]# fdisk /dev/sdc -l Disk /dev/sdc&#xff1a;10 GiB&#xff0c;10737418240 字节&#xff0c;20971520 个扇区 …

深入理解linux文件系统与日志分析

深入理解linux文件系统与日志分析 linux文件系统: 文件是存储在硬盘上的&#xff0c;硬盘上的最小存储单位是扇区&#xff0c;每个扇区的大小是512字节。 inode&#xff1a;元信息&#xff08;文件的属性 权限&#xff0c;创建者&#xff0c;创建日期等等&#xff09; block…

【AVL Design Explorer DOE】

AVL Design Explorer DOE 1、关于DOE的个人理解2、DOE参考资料-知乎2.1 DOE发展及基本类型2.2 DOE应用场景2.3 Mintab 中的 DOE工具3、AVL Design Explorer DOE示例 1、关于DOE的个人理解 仿真和试验一样&#xff0c;就像盲人摸象&#xff0c;在不知道大象的全景之前&#xff…

Java 垃圾回收

一、概述 GC GC(Garbage Collection)&#xff0c;在程序运行过程中内存空间是有限的&#xff0c;为了更好的的使用有限的内存空间&#xff0c;GC会将不再使用的对象清除然后将其所占用的内存释放出来。 java的垃圾回收机制 Java的垃圾收集&#xff08;Garbage Collection, …

嵌入式Linux复制剪切删除指令详解

指令操作 1. cp 复制指令 a. 用法&#xff1a;cp [ 选项 ] [ 源文件或目录 ] [ 目标文件或目录 ]&#xff1b; b. 用途&#xff1a;用于复制文件或目录&#xff1b; c. 通常情况下&#xff0c;复制的都不是空文件夹&#xff0c;所以直接使用 cp 复制空文件会失败&#xff0…

臭氧浓度传感器在食品厂与制药厂中的应用

在食品厂和制药厂的生产过程中&#xff0c;消毒是一个至关重要的环节。有效的消毒可以确保产品免受微生物污染&#xff0c;从而保障消费者的健康。近年来&#xff0c;臭氧作为一种广谱杀菌剂&#xff0c;因其强效的消毒能力和低污染性&#xff0c;在食品厂和制药厂的消毒过程中…

SpringMVC:创建一个简单的SpringMVC框架

目录 一、框架介绍 两个重要的xml文件 SpringMVC执行流程 二、Vscode搭建SpringMVC框架 1、maven创建webapp原型项目 2、pom.xml下添加springmvc的相关依赖 3、在web.xml配置 4、springmvc.xml的配置 5、编写Controller控制器类 6、 编写JSP界面 7、项目结构图 一…

VS2017中使用qt翻译家,除ui界面外其他用tr包裹的字符串在翻译家中显示为乱码

1、ui界面中的中文,可以正常显示 2、其他用tr包裹的字符串,显示为乱码 3、解决 改为utf8保存。 然后更新翻译文件,重新打开发现已经ok了。 参考博客: https://blog.csdn.net/zhou714534957/article/details/124948822 https://blog.csdn.net/weixin_52689816/article/d…

PAT-1004 成绩排名(java实现)

这一关感觉还没第三关难&#xff0c;思路很清晰 题目 1004 成绩排名 读入 n&#xff08;>0&#xff09;名学生的姓名、学号、成绩&#xff0c;分别输出成绩最高和成绩最低学生的姓名和学号。 输入格式&#xff1a; 每个测试输入包含 1 个测试用例&#xff0c;格式为 第 1 行…

【算法】宵暗的妖怪

✨题目链接&#xff1a; 宵暗的妖怪 ✨题目描述 露米娅作为宵暗的妖怪&#xff0c;非常喜欢吞噬黑暗。这天&#xff0c;她来到了一条路上&#xff0c;准备吞噬这条路上的黑暗。这条道路一共被分为n 部分&#xff0c;每个部分上的黑暗数量为ai 。露米娅每次可以任取 连续的 未被…

赚钱其实没有秘密,多琢磨一下不丢人

为什么学了很多知识还是挣不到钱&#xff1f; 挣不到钱&#xff0c;是因为你不够稀缺&#xff1b;挣钱太少&#xff0c;是因为你不懂杠杆&#xff0c;用杠杆撬动稀缺&#xff0c;个人价值自然水涨船高。 学富五车&#xff0c;为何财库依旧空空&#xff1f;怎样才能提高挣钱的…

在全志H616核桃派开发板上配置SSH远程终端方法详解

熟悉指令用户可以对已经联网的核桃派进行局域网SSH远程终端控制&#xff0c;方便使用自己的PC对核桃派远程进行各种指令操作。 普通用户&#xff08;默认&#xff09; 账号&#xff1a;pi ; 密码&#xff1a;pi管理员账户 账号&#xff1a;root ; 密码&#xff1a;root 在这之…