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{
    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
        // 先拦截req
		System.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);//执行目标方法
        // 再拦截resp
        System.out.println("======================filter1-resp");
    }
}
  • Filter2
public class MyFilter2 implements Filter{
    @Override
    public 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 {

    @Bean
    public FilterRegistrationBean<MyFilter> myFilter1 (){
        FilterRegistrationBean<MyFilter> bean = new FilterRegistrationBean<>(new MyFilter());
        bean.setOrder(2);//指定生效顺序
        bean.setUrlPatterns(Sets.newHashSet("/query/*"));//指定拦截的请求路径
        return bean;
    }

    @Bean
    public 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 false
        System.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 {
        // 一定会执行,类似finally
        System.out.println("finally==============================");
    }
}

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

@Configuration
public class WebConfig implements WebMvcConfigurer {
    @Resource
    private MyHandlerInterceptor myHandlerInterceptor;

    @Override
    public 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,加载propertySources
	ConfigurableEnvironment environment = getOrCreateEnvironment();
	
	//2.填充activeProfiles属性:spring.profiles.active
	configureEnvironment(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#postProcessEnvironment
	for (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
父子关系
GenericApplicationContext
	GenericWebApplicationContext
		ServletWebServerApplicationContext
			AnnotationConfigServletWebServerApplicationContext
  • 执行AnnotationConfigServletWebServerApplicationContext构造方法之前,会先调用父类的构造方法,一层一层直到GenericApplicationContext
public GenericApplicationContext() {
    // 创建了beanFactory
	this.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.创建tomcat
	Tomcat tomcat = new Tomcat();
	File baseDir = (this.baseDirectory != null) ? this.baseDirectory : createTempDir("tomcat");
	tomcat.setBaseDir(baseDir.getAbsolutePath());
    
    // 2.创建connector
	Connector connector = new Connector(this.protocol);//Http11NioProtocol
	tomcat.getService().addConnector(connector);
	customizeConnector(connector);
	tomcat.setConnector(connector);
    
    // 3.创建Engine、Host、Service
	tomcat.getHost().setAutoDeploy(false);
	configureEngine(tomcat.getEngine());
    
    // 4.创建Wrapper、Servlet
	prepareContext(tomcat.getHost(), initializers);
    
    // 5.创建TomcatWebServer:完成组件的init
	return 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
    // 步骤一:init
    init();
}

// 步骤二:启动
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 {
	// 步骤一.创建Socket
    if (bindState == BindState.UNBOUND) {
        bindWithCleanup();
        bindState = BindState.BOUND_ON_START;
    }
    // 步骤二.NioEndpoint#startInternal
    startInternal();
}

步骤一: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 创建acceptor
    acceptor = 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.当前状态设置为Running
        state = 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 connections
            if (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 delay
            errorDelay = 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() {
				@Override
				public 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

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

实用软件分享---超级轻量级的强力卸载软件工具UninstallView_1.51

专栏介绍:本专栏主要分享一些实用的软件(Po Jie版); 声明1:软件不保证时效性;只能保证在写本文时,该软件是可用的;不保证后续时间该软件能一直正常运行;不保证没有bug;如果软件不可用了,我知道后会第一时间在题目上注明(已失效)。介意者请勿订阅。 声明2:本专栏的…

【OrangePi AIpro】从开箱到第一个AI应用开发

第一章 OrangePi AIpro介绍和开发环境搭建 1.1 OrangePi AIpro介绍 OrangePi AIpro(8T)采用昇腾AI技术路线&#xff0c;具体为4核64位处理器AI处理器&#xff0c;集成图形处理器&#xff0c;支持8TOPS AI算力&#xff0c;拥有8GB/16GB LPDDR4X&#xff0c;可以外接32GB/64GB/…

CANOE制造dll文件,以及应用dll文件

1、使用canoe自带的capl dll 2、然后使用Visual Studio 2022 打开项目 3、项目打开后修改下项目属性 4、修改capldll.cpp文件 4.1 添加的内容 void CAPLEXPORT far CAPLPASCAL appSum(long i, long j, long* s){*s i j;} {"sum", (CAPL_FARCALL)appSum, "…

FinalShell无法连接Linux

Linux使用Vmware会创建一个网络&#xff0c;让两个子网处于一个网关&#xff0c;这样就能在windows中连接Linux&#xff0c;只有在这种情况下才能FinalShell才能连接Linux

Redis 和 Mysql 如何保证两者数据一致性

文章目录 概述解决方案消息队列异步重试 基于 RocketMQ 的可靠性消息通信&#xff0c;来实现最终一致Canal 组件&#xff0c;监控 Mysql 中 binlog 的日志&#xff0c;把更新后的数据同步到 Redis 里面延时双删弱一致性和强一致性Canal详解 概述 在分布式系统中&#xff0c;保…

2024中国军民两用智能装备与通信技术产业展览会带你走进轻元素量子材料世界

在科技创新的浪潮中&#xff0c;北京怀柔科学城迎来了一场革命性的突破——世界首个轻元素量子材料平台正式启动运行。这一里程碑事件不仅彰显了中国在量子科学研究上的领先地位&#xff0c;也为全球科技界带来了一股新风潮。由北京大学领衔打造的这一平台&#xff0c;专注于轻…

[论文笔记]MemGPT: Towards LLMs as Operating Systems

引言 今天介绍一篇论文MemGPT: Towards LLMs as Operating Systems。翻过过来就是把LLM看成操作系统。 大语言模型已经在人工智能领域引起了革命性的变革&#xff0c;但受到有限上下文窗口的限制&#xff0c;在扩展对话和文档分析等任务中的效用受到了阻碍。为了能够利用超出…

免费生物蛋白质的类chatgpt工具助手copilot:小分子、蛋白的折叠、对接

参考: https://310.ai/copilot 可以通过自然语言通话晚上蛋白质的相关处理:生成序列、折叠等 应该是agent技术调用不同工具实现 从UniProt数据库中搜索和加载蛋白质。使用ESM Fold方法折叠蛋白质。使用310.ai基础模型设计新蛋白质。使用TM-Align方法比较蛋白质。利用Protei…

谁是镰刀谁是韭菜?程序交易与手动交易的博弈,靠技术还是靠运气

备受争议的话题&#xff0c;很多人认为程序化交易是在破坏市场的平衡&#xff0c;大量的程序交易订单可能会造成市场价格的异常波动&#xff0c;尤其是在高频交易未被监管时&#xff0c;程序化交易者占尽优势&#xff0c;来回收割。 而支持程序交易的人认为&#xff0c;市场是…

Java八股文:程序员的“面试经”还是技术壁垒?

Java八股文&#xff1a;程序员的“面试经”还是技术壁垒&#xff1f; “八股文”&#xff0c;在中国古代科举考试中&#xff0c;指的是一种程式化的文章写作格式&#xff0c;内容空洞&#xff0c;缺乏创新。而如今&#xff0c;这个词语被赋予了新的含义&#xff0c;用来形容技术…

python基础(习题、资料)

免费提取资料&#xff1a; 练习、资料免费提取。持续更新迅雷云盘https://pan.xunlei.com/s/VNz6kH1EXQtK8j-wwwz_c0k8A1?pwdrj2x# 本文为Python的进阶知识合辑&#xff0c;包括列表&#xff08;List&#xff09;、元组&#xff08;Tuple&#xff09;、字典&#xff08;Dic…

微信密码忘记了怎么找回?自助找回2个方法揭晓!

在微信的世界里&#xff0c;密码就像是我们通往个人世界的“钥匙”&#xff0c;一旦丢失&#xff0c;就仿佛被锁在了自己的门外。微信密码忘记了怎么找回&#xff1f;别担心&#xff0c;微信提供了多种自助找回密码的方法&#xff0c;让我们一起来揭秘这些找回密码的秘诀吧&…

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

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

【MySQL访问】

文章目录 一、C远程连接到MySQLmysql_init()函数mysql_real_connect&#xff08;&#xff09;函数实战案例 二、处理查询select的细节mysql_store_result()函数获取结果行和列获取select结果获取行内容获取列属性 三、MySQL图形化界面连接 关于动态链接&#xff0c;请看这篇文章…

【C语言】基于C语言实现的贪吃蛇游戏

【C语言】基于C语言实现的贪吃蛇游戏 &#x1f525;个人主页&#xff1a;大白的编程日记 &#x1f525;专栏&#xff1a;C语言学习之路 文章目录 【C语言】基于C语言实现的贪吃蛇游戏前言一.最终实现效果一.Win32 API介绍1.1Win32 API1.2控制台程序1.3控制台屏幕上的坐标COORD…

类和对象(中)【类的6个默认成员函数】 【零散知识点】 (万字)

类和对象&#xff08;中&#xff09; 1.类的6个默认成员函数 如果一个类中什么成员都没有&#xff0c;简称为空类。 空类中真的什么都没有吗&#xff1f;并不是&#xff0c;任何类在什么都不写时&#xff0c;编译器会自动生成以下6个默认成员函数。 默认成员函数&#xff1…

TiDB-从0到1-体系结构

TiDB从0到1系列 TiDB-从0到1-体系结构TiDB-从0到1-分布式存储TiDB-从0到1-分布式事务TiDB-从0到1-MVCC 一、TiDB体系结构图 TiDB基础的体系架构中有4大组件 TiDB Server&#xff1a;用于处理客户端的请求PD&#xff1a;体系的大脑&#xff0c;存储元数据信息TiKV&#xff1a…

kvm--安装启动

前期 使用vmware workstation 时 安装kvm yum install qemu-kvm qemu-kvm-tools libvirt libvirt-client virt-manager virt-install -y systemctl enable --now libvirtd

【数据结构】二叉树运用及相关例题

文章目录 前言查第K层的节点个数判断该二叉树是否为完全二叉树例题一 - Leetcode - 226反转二叉树例题一 - Leetcode - 110平衡二叉树 前言 在笔者的前几篇篇博客中介绍了二叉树的基本概念及基本实现方法&#xff0c;有兴趣的朋友自己移步看看。 这篇文章主要介绍一下二叉树的…