AbstractApplicationContext.refresh()应用上下文刷新方法

2023-05-16

前情提要: 学习源码光看博客文章基本没有记住的可能.结合源码和博客+实践才能够通过理解记住,看了很多天才断断续续看完,发现它和其他IOC、Bean生命周期都有关联,将之前片段式的认知串联起来了.

这个刷新的代码很长,建议没耐心的时候就先不看,但是每天固定一个时间看一会,熟练了再串联起来看一遍.

上下文刷新方法被调用时机是SpringBoot.run启动中上下文准备方法(prepareContext)执行完毕后进行同步调用的

上下文准备方法(prepareContext)主要执行SpringApplication初始化时加载的上下文初始化器,以及发布上下文准备完毕事件,关闭启动上下文,发布上下文初始化完毕事件
private void refreshContext(ConfigurableApplicationContext context) {
	if (this.registerShutdownHook) {
		try {
		    // 注册停止钩子(停止执行的线程),当JVM准备停止时执行,自定义钩子优先级最高 -->Runtime.getRuntime().addShutdownHook(Thread hook);
		    // 会发布ContextClosedEvent事件
		    // 销毁所有Bean(删除容器中的Bean,执行Bean的destory方法)
			context.registerShutdownHook();
		}
		catch (AccessControlException ex) {
			// Not allowed in some environments.
		}
	}
	// 该方法会一直调用到AbstractApplicationContext.refresh方法.中间大多是参数校验和直接调用
	refresh((ApplicationContext) context);
}

refresh方法本体解析

public void refresh() throws BeansException, IllegalStateException {
	synchronized (this.startupShutdownMonitor) {
	    // 忽略掉不重要!启动阶段标记,初始化时标记位为false,执行refresh完毕后修改为true(最后的end方法).
		StartupStep contextRefresh = this.applicationStartup.start("spring.context.refresh");

		// 1. 准备刷新上下文方法,用于设置准备刷新前的一些参数:
		//  程序启动标志位/上下文拓展资源加载/上下文环境准备情况验证/监听器监听事件容器初始化准备
		prepareRefresh();

		// 2. 获取BeanFactory,内部调用refreshBeanFactory()和getBeanFactory()均由子类实现
		//  告知子类刷新Bean工厂(设置序列号ID--> 参考GenericApplicationContext)
		ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();

		// 3. 初始化Bean工厂,设置基础属性: ClassLoader、SPEL(SPring表达式)解析器、属性编辑器(自定义属性覆盖默认属性)、
		//  添加系统BeanPostProcessor`ResourceEditorRegistrar(初始化前执行一些Aware即invokeAwareInterfaces方法)`、
		//  忽略一些系统级接口装配依赖、注入一些不能自动创建的Bean依赖(Bean工厂,ResourceLoader(加载资源文件),事件发布类,上下文)、
		//  加系统BeanPostProcessor`ApplicationListenerDetector(Bean初始化后执行,判断是否是单例监听器加到上下文中)`、
		//  加入AspectJ静态代理支持、系统环境Bean检查注册
		prepareBeanFactory(beanFactory);

		try {
			// 4. BeanFactory配置好的后置拓展操作.由子类拓展.可在这里提前加入自定义BeanFactoryPostProcess
			postProcessBeanFactory(beanFactory);

            // 忽略掉不重要!启动阶段标记
			StartupStep beanPostProcess = this.applicationStartup.start("spring.context.beans.post-process");
			// 5. 执行BeanFactory后置处理. Spring的SPI机制保障(可看我自动装载文章).
			invokeBeanFactoryPostProcessors(beanFactory);

			// 6. 注册BeanPostProcessors到BeanFacotry中并排序,并未执行.涉及到Bean生命周期执行
			registerBeanPostProcessors(beanFactory);
			beanPostProcess.end();

			// 7. 初始化MessageSource,用于消息国际化处理
			initMessageSource();

			// 8. 初始化上下文事件广播器
			initApplicationEventMulticaster();

			// 9. 子类实现,springbootstarterweb在此创建web容器,并提前生成容器所需的Bean及其对应生命周期
			onRefresh();

			// 10. 给广播器中注册监听器,执行初期事件
			registerListeners();

			// 11. 初始化所有非懒加载单例Bean
			finishBeanFactoryInitialization(beanFactory);

			// 12. 完成刷新,发布上下文刷新完毕事件
			finishRefresh();
		}catch (BeansException ex) {
			if (logger.isWarnEnabled()) {
				logger.warn("Exception encountered during context initialization - " +
						"cancelling refresh attempt: " + ex);
			}
			// 刷新异常,销毁已经创建的Bean
			destroyBeans();
			// 取消刷新,设置active为false
			cancelRefresh(ex);
			// Propagate exception to caller.
			throw ex;
		}finally {
			// Reset common introspection caches in Spring's core, since we
			// might not ever need metadata for singleton beans anymore...
			
			resetCommonCaches();
			// 刷新结束,设置标志位为true
			contextRefresh.end();
		}
	}
}

1. 准备刷新上下文方法

	protected void prepareRefresh() {
		// 设置基础属性,设置当前状态为活跃/启动
		this.startupDate = System.currentTimeMillis();
		this.closed.set(false);
		this.active.set(true);
        // log打印

		// 交由子类实现,用于加载初始化资源属性.
		initPropertySources();

		// 校验所需要的环境变量是否已经加载进来
		getEnvironment().validateRequiredProperties();

		// 保留未生成Context时加载的Linsteners监听器
		if (this.earlyApplicationListeners == null) {
			this.earlyApplicationListeners = new LinkedHashSet<>(this.applicationListeners);
		}
		else {
			// Reset local application listeners to pre-refresh state.
			this.applicationListeners.clear();
			this.applicationListeners.addAll(this.earlyApplicationListeners);
		}

		// 创建ApplicationEvent监听事件容器
		this.earlyApplicationEvents = new LinkedHashSet<>();
	}

2. 获取BeanFactory

这两个方法均交由子类实现,大致是设置BeanFactory序列化ID号.参考AnnotationConfigServletWebServerApplicationContext类的父类GenericApplicationContext类

protected ConfigurableListableBeanFactory obtainFreshBeanFactory() {
	refreshBeanFactory();
	return getBeanFactory();
}

3. 初始化Bean工厂各种属性和容器内容

protected void prepareBeanFactory(ConfigurableListableBeanFactory beanFactory) {
	// 设置BeanFactory的ClassLoader
	beanFactory.setBeanClassLoader(getClassLoader());
	// 判断是否需要加载Spring-Spel表达式解析器.默认加载
	if (!shouldIgnoreSpel) {
		beanFactory.setBeanExpressionResolver(new StandardBeanExpressionResolver(beanFactory.getBeanClassLoader()));
	}
	// 添加属性编辑器用于自定义属性设置覆盖
	beanFactory.addPropertyEditorRegistrar(new ResourceEditorRegistrar(this, getEnvironment()));

	// 添加默认注册Bean前加入BeanPostProcessor用于Bean初始化前进行操作: 如果Bean实现了某个Aware则调用对应方法.可查看ApplicationContextAwareProcessor.ApplicationContextAwareProcessor方法,自定义Bean可进行拓展
	beanFactory.addBeanPostProcessor(new ApplicationContextAwareProcessor(this));
	// 忽略一些系统级接口依赖,这些方法在上面的Processor中检查回调中会执行.
	beanFactory.ignoreDependencyInterface(EnvironmentAware.class);
	beanFactory.ignoreDependencyInterface(EmbeddedValueResolverAware.class);
	beanFactory.ignoreDependencyInterface(ResourceLoaderAware.class);
	beanFactory.ignoreDependencyInterface(ApplicationEventPublisherAware.class);
	beanFactory.ignoreDependencyInterface(MessageSourceAware.class);
	beanFactory.ignoreDependencyInterface(ApplicationContextAware.class);
	beanFactory.ignoreDependencyInterface(ApplicationStartup.class);

	// 设置注册不能自动创建的Bean依赖
	beanFactory.registerResolvableDependency(BeanFactory.class, beanFactory);
	beanFactory.registerResolvableDependency(ResourceLoader.class, this);
	beanFactory.registerResolvableDependency(ApplicationEventPublisher.class, this);
	beanFactory.registerResolvableDependency(ApplicationContext.class, this);

	// 添加Bean初始化后操作,如果有该单例监听器Bean就加入到上下文监听器容器中
	beanFactory.addBeanPostProcessor(new ApplicationListenerDetector(this));

	// 加入AspectJ支持(静态代理,启动时会改变被代理类字节码进行增强https://blog.csdn.net/crazyzhb2012/article/details/53827540 | https://developer.aliyun.com/article/574571)
	if (!NativeDetector.inNativeImage() && beanFactory.containsBean(LOAD_TIME_WEAVER_BEAN_NAME)) {
		beanFactory.addBeanPostProcessor(new LoadTimeWeaverAwareProcessor(beanFactory));
		// Set a temporary ClassLoader for type matching.
		beanFactory.setTempClassLoader(new ContextTypeMatchClassLoader(beanFactory.getBeanClassLoader()));
	}

	// 检查并加入一些默认的初始化环境Bean
	if (!beanFactory.containsLocalBean(ENVIRONMENT_BEAN_NAME)) {
		beanFactory.registerSingleton(ENVIRONMENT_BEAN_NAME, getEnvironment());
	}
	if (!beanFactory.containsLocalBean(SYSTEM_PROPERTIES_BEAN_NAME)) {
		beanFactory.registerSingleton(SYSTEM_PROPERTIES_BEAN_NAME, getEnvironment().getSystemProperties());
	}
	if (!beanFactory.containsLocalBean(SYSTEM_ENVIRONMENT_BEAN_NAME)) {
		beanFactory.registerSingleton(SYSTEM_ENVIRONMENT_BEAN_NAME, getEnvironment().getSystemEnvironment());
	}
	if (!beanFactory.containsLocalBean(APPLICATION_STARTUP_BEAN_NAME)) {
		beanFactory.registerSingleton(APPLICATION_STARTUP_BEAN_NAME, getApplicationStartup());
	}
}

4. BeanFactory配置好的后置拓展操作

作为BeanFactory设置好后Bean加载初始化前拓展扫描/的后置操作(Web服务查看AnnotationConfigServletWebServerApplicationContext,加入了一个BeanPostProcess,不过实际没有作用)

// ServletWebServerApplicationContext中的方法
protected void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) {
	beanFactory.addBeanPostProcessor(new WebApplicationContextServletContextAwareProcessor(this));
	beanFactory.ignoreDependencyInterface(ServletContextAware.class);
	// 最主要是最后这步,为BeanFactory注册了request、session的web域
	// 并注册了ServletRequest、ServletResponse、HttpSession、WebRequest到容器中
	registerWebApplicationScopes();
}

5. 执行BeanFactory后置处理.

invokeBeanFactoryPostProcessors方法比较长,但是很容易懂,放到这个博客中了.在此步Spring扫描加载完毕所有的BeanDefinition(扫描出来的各种注解和config配置的Bean定义).然后执行所有postProcessBeanDefinitionRegistry方法和postProcessBeanFactory方法

protected void invokeBeanFactoryPostProcessors(ConfigurableListableBeanFactory beanFactory) {
    // 调用执行默认的和自定义的BeanDefinitionRegistryPostProcessor和BeanFactoryPostProcessor类方法
	PostProcessorRegistrationDelegate.invokeBeanFactoryPostProcessors(beanFactory, getBeanFactoryPostProcessors());
	//···其他
}

6. 注册BeanPostProcessors到BeanFacotry中并排序,并未执行.涉及到.涉及到Bean生命周期执行

registerBeanPostProcessors方法比较长,但是很容易懂,放到这个博客中了.
涉及到的Bean生命周期可查看Spring控制反转IOC和依赖注入DI结构化简述、流程分明的Spring的Bean生命周期

protected void registerBeanPostProcessors(ConfigurableListableBeanFactory beanFactory) {
	PostProcessorRegistrationDelegate.registerBeanPostProcessors(beanFactory, this);
}

7. 初始化一个MessageSource.用于消息国际化、参数化

Spring 两个开箱即用的实现 ResourceBundleMessageSource 和 ReloadableResourceBundleMessageSource

protected void initMessageSource() {
	ConfigurableListableBeanFactory beanFactory = getBeanFactory();
	if (beanFactory.containsLocalBean(MESSAGE_SOURCE_BEAN_NAME)) {
		this.messageSource = beanFactory.getBean(MESSAGE_SOURCE_BEAN_NAME, MessageSource.class);
		// Make MessageSource aware of parent MessageSource.
		if (this.parent != null && this.messageSource instanceof HierarchicalMessageSource) {
			HierarchicalMessageSource hms = (HierarchicalMessageSource) this.messageSource;
			if (hms.getParentMessageSource() == null) {
				// Only set parent context as parent MessageSource if no parent MessageSource
				// registered already.
				hms.setParentMessageSource(getInternalParentMessageSource());
			}
		}
		if (logger.isTraceEnabled()) {
			logger.trace("Using MessageSource [" + this.messageSource + "]");
		}
	}
	else {
		// Use empty MessageSource to be able to accept getMessage calls.
		DelegatingMessageSource dms = new DelegatingMessageSource();
		dms.setParentMessageSource(getInternalParentMessageSource());
		this.messageSource = dms;
		beanFactory.registerSingleton(MESSAGE_SOURCE_BEAN_NAME, this.messageSource);
		if (logger.isTraceEnabled()) {
			logger.trace("No '" + MESSAGE_SOURCE_BEAN_NAME + "' bean, using [" + this.messageSource + "]");
		}
	}
}

8. 初始化上下文事件广播器

事件广播器用于Spring事件通知机制,监听器通过指定监听事件,当广播器广播该事件时会执行对应监听器方法.
该方法表示如果有自定义广播器则使用自定义广播器没有则创建一个SimpleApplicationEventMulticaster.可自定义拓展让广播器监听事件异步执行
保存事件和对应监听器列表映射,发布事件后会找到该事件的所有监听器.如果由线程池则异步执行.没有则同步执行

protected void initApplicationEventMulticaster() {
    ConfigurableListableBeanFactory beanFactory = getBeanFactory();
    // 如果用户手动新建了一个名为applicationEventMulticaster类型为ApplicationEventMulticaster的bean,则将这个bean作为事件广播器
    if (beanFactory.containsLocalBean(APPLICATION_EVENT_MULTICASTER_BEAN_NAME)) {
        this.applicationEventMulticaster =
                beanFactory.getBean(APPLICATION_EVENT_MULTICASTER_BEAN_NAME, ApplicationEventMulticaster.class);
    }
    else {
        // 否则新建一个SimpleApplicationEventMulticaster作为默认的事件广播器
        this.applicationEventMulticaster = new SimpleApplicationEventMulticaster(beanFactory);
        beanFactory.registerSingleton(APPLICATION_EVENT_MULTICASTER_BEAN_NAME, this.applicationEventMulticaster);
    }
}

9. springbootstarterweb在此创建web容器

查看AnnotationConfigServletWebServerApplicationContext的父类ServletWebServerApplicationContext.在这一步创建了Tomcate服务器,初始化各种web组件

private void createWebServer() {
	WebServer webServer = this.webServer;
	ServletContext servletContext = getServletContext();
	if (webServer == null && servletContext == null) {
		StartupStep createWebServer = this.getApplicationStartup().start("spring.boot.webserver.create");
		ServletWebServerFactory factory = getWebServerFactory();
		createWebServer.tag("factory", factory.getClass().toString());
		// 创建web服务器并初始化各种MVC组件
		this.webServer = factory.getWebServer(getSelfInitializer());
		createWebServer.end();
		getBeanFactory().registerSingleton("webServerGracefulShutdown",
				new WebServerGracefulShutdownLifecycle(this.webServer));
		getBeanFactory().registerSingleton("webServerStartStop",
				new WebServerStartStopLifecycle(this, this.webServer));
	}
	else if (servletContext != null) {
		try {
			getSelfInitializer().onStartup(servletContext);
		}
		catch (ServletException ex) {
			throw new ApplicationContextException("Cannot initialize servlet context", ex);
		}
	}
	initPropertySources();
}

10. 给广播器中注册监听器,发布广播器未初始化时加载的事件

将早期加入的系统监听器加入广播器中.常规的监听器Bean则先加入名字等到后面和其他Bean一起创建.从BeanFactory中获取即可.并发布早期Application事件

protected void registerListeners() {
	// 将Application启动时加载的Linsteners加入广播器中
	for (ApplicationListener<?> listener : getApplicationListeners()) {
		getApplicationEventMulticaster().addApplicationListener(listener);
	}

	// 先将常规的监听器Bean名称放入,后续再创建
	String[] listenerBeanNames = getBeanNamesForType(ApplicationListener.class, true, false);
	for (String listenerBeanName : listenerBeanNames) {
		getApplicationEventMulticaster().addApplicationListenerBean(listenerBeanName);
	}

	// 早期Application事件发布
	Set<ApplicationEvent> earlyEventsToProcess = this.earlyApplicationEvents;
	this.earlyApplicationEvents = null;
	if (!CollectionUtils.isEmpty(earlyEventsToProcess)) {
		for (ApplicationEvent earlyEvent : earlyEventsToProcess) {
			getApplicationEventMulticaster().multicastEvent(earlyEvent);
		}
	}
}

11. 初始化所有非懒加载单例Bean

该步骤如果有类型转换Bean则添加自定义的类型转换Bean
在加载单例Bean前提前加入了为AOP切入点织入通知的Bean
关闭临时类加载器,设置冻结BeanDefinition标志为true,不允许Bean生成前再修改Bean了
最后进行Bean加载.比较长放到finishBeanFactoryInitialization讲解文章和Bean创建文章中

12. 完成刷新,发布上下文刷新完毕事件

  1. 加载LifecycleProcessor.执行其刷新方法.会将实现了SmartLifecycle接口的Bean加入Application生命周期,启动时执行start方法,关闭时执行stop方法,可以启动一些监控组件之类的东西.
  2. 发布上下文刷新事件
protected void finishRefresh() {
	// 清除为上下文创建初始化准备的资源文件数据信息缓存.比如ASM的元数据信息
	clearResourceCaches();

	// 一般是加载DefaultLifecycleProcessor
	initLifecycleProcessor();

	// 执行生命周期处理器的onRefresh方法.会调用实现了SmartLifecycle接口的start方法启动对应Bean生命周期(随着Application启动启动,关闭而关闭)
	getLifecycleProcessor().onRefresh();

	// 发布上下文刷新完毕事件
	publishEvent(new ContextRefreshedEvent(this));

	// 设置JMX则执行
	if (!NativeDetector.inNativeImage()) {
		LiveBeansView.registerApplicationContext(this);
	}
}

protected void initLifecycleProcessor() {
	ConfigurableListableBeanFactory beanFactory = getBeanFactory();
	// 我们自己没定义就一定是走下面默认的LifecycleProcssor上.我们自己定义的话最好也是继承下面那个默认的来定义,下面那个默认的是让Bean生命周期随上下文一致的保证.
	if (beanFactory.containsLocalBean(LIFECYCLE_PROCESSOR_BEAN_NAME)) {
		this.lifecycleProcessor =
				beanFactory.getBean(LIFECYCLE_PROCESSOR_BEAN_NAME, LifecycleProcessor.class);
		if (logger.isTraceEnabled()) {
			logger.trace("Using LifecycleProcessor [" + this.lifecycleProcessor + "]");
		}
	}
	else {
		DefaultLifecycleProcessor defaultProcessor = new DefaultLifecycleProcessor();
		defaultProcessor.setBeanFactory(beanFactory);
		this.lifecycleProcessor = defaultProcessor;
		beanFactory.registerSingleton(LIFECYCLE_PROCESSOR_BEAN_NAME, this.lifecycleProcessor);
		if (logger.isTraceEnabled()) {
			logger.trace("No '" + LIFECYCLE_PROCESSOR_BEAN_NAME + "' bean, using " +
					"[" + this.lifecycleProcessor.getClass().getSimpleName() + "]");
		}
	}
}

DefaultLifecycleProcessor的刷新启动Bean周期方法
关闭就是执行SmartLifecycle的stop方法就不展示了

public void onRefresh() {
	startBeans(true);
	this.running = true;
}
// 找出所有实现SmartLifecycle并且声明为自动启动的Bean执行启动方法
private void startBeans(boolean autoStartupOnly) {
	Map<String, Lifecycle> lifecycleBeans = getLifecycleBeans();
	Map<Integer, LifecycleGroup> phases = new TreeMap<>();

	lifecycleBeans.forEach((beanName, bean) -> {
		if (!autoStartupOnly || (bean instanceof SmartLifecycle && ((SmartLifecycle) bean).isAutoStartup())) {
			int phase = getPhase(bean);
			phases.computeIfAbsent(
					phase,
					p -> new LifecycleGroup(phase, this.timeoutPerShutdownPhase, lifecycleBeans, autoStartupOnly)
			).add(beanName, bean);
		}
	});
	if (!phases.isEmpty()) {
		phases.values().forEach(LifecycleGroup::start);
	}
}

最后还有几个Spring上下文的内置事件ContextRefreshedEvent(ApplicationContext 被初始化或刷新时)、ContextStartedEvent、ContextStoppedEvent、ContextClosedEvent、RequestHandledEvent可以进行拓展

参考

spring学习之源码分析–AbstractApplicationContext之refresh
Spring Event事件通知机制
Spring Boot 中 Tomcat 是怎么启动的
Spring IOC容器启动流程 AbstractApplicationContext#refresh()方法源码分析
SpringMVC之类型转换Converter
Spring支持的转换器 示例 使用ConversionService转换数据

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

AbstractApplicationContext.refresh()应用上下文刷新方法 的相关文章

  • 迁移Linode服务器

    迁移Linode服务器 从美国将Linode的一个服务器迁移到日本的机房 xff1a 1 首先为了保证数据的完整性 xff0c 把两台VPS主机都关机 2 到新的VPS主机控制面板那样把Disk Image和Swap Image给删除了 x
  • JAVA 正则表达式 (超详细)

    新网站上线 欢迎大家 网站交易中心 在这里你可以购买或者出售你的网站 网站信息发布中心 在这里有各种交易信息的发布 同时提供 一些软件的免费使用 xff08 附有源码 xff09 网站博客系统 这里你可以注册自己的博客 一个账户无限量博客
  • mysql查看binlog日志内容

    mysql的binlog日志位置可通过show variables like 39 datadir 39 查看 xff0c 直接打开无法查看 xff0c 要看其内容2个办法 xff1a 1 登录到mysql查看binlog 只查看第一个bi
  • 常用模拟器及ROM下载地址

    Nintendo Nintendo Entertainment System Super Nintendo Entertainment System Nintendo 64 Project64 https www pj64 emu com
  • Linux下安装jdk配置报错:-bash: java: command not found

    可能是jdk解压有问题 xff0c 重新解压然后source etc profile xff1a 使配置生效 再 java version来查看配置成功没
  • IP地址段范围写法

    A类IP段 0 0 0 0 到127 255 255 255 B类IP段 128 0 0 0 到191 255 255 255 C类IP段 192 0 0 0 到223 255 255 255 XP默认分配的子网掩码每段只有255或0 xf
  • 有时间细读这些书

    1 Windows程序设计 第5版 珍藏版 xff1a 这是很经典的一本介绍Win32 API编程的书了 xff0c 基本介绍到了大多数关于Windows程序设计的基本内容 2 Windows程序设计 王艳平版 xff1a 这本和上一本的区
  • linux中systemctl详细理解及常用命令

    一 systemctl理解 Linux 服务管理两种方式service和systemctl systemd是Linux系统最新的初始化系统 init 作用是提高系统的启动速度 xff0c 尽可能启动较少的进程 xff0c 尽可能更多进程并发
  • Linux查看网速命令

    watch 34 ifconfig eth0 grep byte 34
  • 软件正在改变世界,程序员应该得到足够尊重

    软件无处不在 xff0c 越来越多的人离不开软件 xff0c 你打开电脑 xff0c 你使用手机 xff0c 你购物娱乐 软件一直在帮你 xff0c 软件已经渗透到我们的工作 生活 娱乐的方方面面 xff0c 软件每一天都在改变着这个世界
  • apache2.4 配置多个版本的 php7,php8)

    不多说 xff0c 直接上配置修改 httpd conf lt IfDefine php7 gt Listen 82 LoadFile 34 D php 7 2 34 libssh2 dll 34 LoadModule php7 modul
  • 叉乘怎么记忆,计算

    以一个例子直观记忆叉乘 xff1a 引用自 向量积 百度百科 baidu com 在这个式子中 xff0c 我们可以清楚地看到三项分别是i xff0c j xff0c k 前面则是他们的系数 我们可以直接把i xff0c j xff0c k
  • 接口防重方案设计

    幂等性原理 xff1a 前台的多次请求 xff0c 对于后台 xff0c 也是同一次请求 xff1b 通常接口设计方式 xff1a 1 前端的页面提交按钮置灰 xff0c 防止用户重复点击 xff1b 2 对前端提交的token进行校验 x
  • Spark Streaming 与 Kafka 集成分析

    前言 Spark Streaming 诞生于2013年 xff0c 成为Spark平台上流式处理的解决方案 xff0c 同时也给大家提供除Storm 以外的另一个选择 这篇内容主要介绍Spark Streaming 数据接收流程模块中与Ka
  • 微信小程序-轮播图实现

    好久不见 xff0c 今天小h来分享一下如何实现一个微信小程序的轮播图实现方式 xff1a 前提条件是具有微信开发者工具 xff0c 还有对应的开发者ID xff0c 这些基础条件我这边就直接跳过了哈 xff0c 直接进入正题 xff1a
  • 所以,到底什么是微服务?

    1 微服务是一种软件架构 xff0c 是聚焦在单一的职责和业务功能 xff0c 具有独立的进程 xff0c 能够单独运行的服务 xff0c 并且与外部服务是通过HTTP进行交互通信的服务 2 微服务比较常见的特性是 xff0c 具有单一职责
  • 关于云服务Bmob的使用方法(上)——上传数据

    关于第三方云服务平台Bmob是怎样使用的 xff1f 我们从两个方面来写 xff0c 一个是传输数据 xff0c 一个是传输文件 第一个是关于bmob传输数据的 xff0c 首先我们在官网http www bmob cn 上面注册我们自己的
  • 关于云服务Bmob的使用方法(下)——上传文件

    上一篇我们说了如何传输数据 xff0c 那么这一篇我们进阶一下 xff0c 来谈谈如何传输文件 xff0c 比如图片 关于如何在bmob上注册和申请 xff0c 上一篇已经有说明 xff0c 不懂的读者可以去看看 xff0c 然后我们直接进
  • 使用栈模拟递归的算法

    这一篇笔者要讲的是如何用栈来模拟递归 xff0c 或者说替代递归的算法 xff0c 现在我们假如要算从三角形数的叠加 xff0c 比如输入10 xff0c 输出是55 xff0c 输入是100 xff0c 输出是5050 xff0c 等等
  • java集合篇(一)——ArrayList扩容原理

    相信大家都对ArrayList相当熟悉了 xff0c 今天笔者就对ArrayList的源码进行解读 xff0c 讲解一下对ArrayList扩容的基本原理 虽然大家都有用过 xff0c 但还是简单介绍一下吧 xff0c ArrayList实

随机推荐

  • 怎样快速开发一个 Dubbo 应用?

    背景 本文将以 Dubbo 为例 xff0c 介绍如何快速开发一个 Dubbo 应用 为了便于读者理解 xff1a 首先会介绍一下传统的 RMI 的基本概念 然后比较下现代的 RPC 框架与 RMI 的区别 再基于 Dubbo 提供的 AP
  • 百度历届笔试题(1)

    题目描述 牛牛和妞妞正在玩一个猜数游戏 xff0c 妞妞心里想两个不相等的正数 xff0c 把这两个正数的和y告诉牛牛 妞妞声称这两个数都不超过x xff0c 让牛牛猜这两个数是多少 牛牛每猜一次 xff0c 妞妞会告诉他猜对了还是猜错了
  • systemd service 配置自启动,配置多个环境变量,最大打开文件数

    一 创建service文件 样例 vim usr lib systemd system nacos service Unit Description 61 nacos After 61 network target Service Type
  • IntelliJ IDEA 架构概述(面向插件开发者)

    原文未定稿 2014 11 这篇文章的目的是从插件开发者的视角描述IntelliJ IDEA的结构 文章将以一种任务驱动的方式组织 xff1a 相比列出所有你可以对各组件对象进行的操作并描述这些操作它们的实现方式 xff0c 这篇文章将尽可
  • 用大数据思维做运维监控

    今天一大早就看到了一篇文章 xff0c 叫 大数据对于运维的意义 该文章基本上是从三个层面阐述的 xff1a 工程数据 xff0c 譬如工单数量 xff0c SLA可用性 xff0c 基础资源 xff0c 故障率 xff0c 报警统计业务数
  • 仿滴滴抢单倒计时的Demo

    滴滴里面有一个下单完成之后等待界面的倒计时转圈的视图 就是这个 原理 xff1a 通过CAShapeLayer层添加到自己自定义的视图layer上 设置ShapeLayer的path 他的路劲绘制一般通过UIBezierPath配合设置 通
  • iOS开发之自己封装的提示框(警告框)样式BHAlertView

    最近需要使用到提示框 xff08 警告框 xff09 进行信息的展示和提醒 xff0c 所以进行了一个类的封装 xff0c 想用Swift调用此OC文件 xff0c 但是发现有些困难 xff0c 所以暂时先把OC代码进行展示 xff0c 随
  • 使用ActiveMQ进行C++与C#的通信

    该系列文章介绍如何通过ActiveMQ在C 43 43 和C 控制台项目之间通信 关于ActiveMQ的介绍可以参考 深入浅出ActiveMQ xff0c 平台搭建可以参考 Activemq 平台搭建与C 示列 xff0c 两者之间有交叉内
  • Centos8防火墙设置

    进程与状态相关 systemctl start firewalld service 启动防火墙 systemctl stop firewalld service 停止防火墙 systemctl status firewalld 查看防火墙状
  • c/c++笔试

    1 xff0e 进程和线程的差别 线程是指进程内的一个执行单元 也是进程内的可调度实体 与进程的区别 1 调度 xff1a 线程作为调度和分配的基本单位 xff0c 进程作为拥有资源的基本单位 2 并发性 xff1a 不仅进程之间可以并发执
  • android onNewIntent 调用时机

    当前Activity已经在Activity堆栈当中时 xff0c 主要取决于LaunchMode对应的设置 LaunchMode为SingleTop时 xff0c 如果ActivityA在栈顶 且现在要再启动ActivityA xff0c
  • 不知道怎么开发VR游戏?Unity5.3官方VR教程重磅登场-系列3 VR中的交互方式

    不知道怎么开发VR游戏 xff1f Unity5 3官方VR教程重磅登场 系列3 VR中的交互方式 王寒 4 个月前 https zhuanlan zhihu com p 20505470 概览 xff1a 在VR项目中 xff0c 我们需
  • Java多线程通信-利用传统的线程通信wait(),notify()方法实现“生产者消费者模式”

    想利用传统的线程通信wait notify xff0c notifyAll 方法 xff0c 必须依赖于同步监听器的存在 xff0c 也就是说 xff0c 对于synchronized修饰的同步方法 xff0c 因为该类的默认实例 xff0
  • java TCP/IP实现简单的多人聊天功能

    TCP IP是可靠的网络协议 xff0c 数据的传输需要服务端和客户端之间三次 握手 xff0c 比较适合文本等一些可靠性要求高的数据传输 xff0c 但是它的效率较UDP低 下面通过一张图来简要说明使用 ServerSocket 创建 T
  • JavaScript开发工具WebStorm使用教程:从命令行运行代码检查

    从命令行运行代码检查 WebStorm运行代码检查可以发现并突出显示语法错误 死代码 可能的错误 错误的编码风格和其他问题 还可以从命令行为特定项目运行所有已配置的检查 xff0c 并将结果存储为 XML JSON 或带有报告的纯文本文件
  • 基于Spark的机器学习经验

    这篇内容基于我去年的一些感悟写的 xff0c 但是今年才在Stuq 的微信群做的分享 从技术角度而言 xff0c 对Spark的掌握和使用还是显得很手生的 但是今天一位做数据分析相关的朋友说 xff0c 受这篇内容影响 xff0c 他接受了
  • Linux(manjaro)微信web开发者工具

    安装 wine和winetricks span class hljs built in sudo span pacman S wine winetricks 打开winetricks 安装需要的组件 安装linux版本运行程序 github
  • C++类与对象

    C 43 43 面向对象 一 面向对象程序设计方法概述 1 凡是以类对象为基本构成单位的程序称为基于对象的程序 2 面向对象和面向过程的区别 xff1a 在笔者看来 xff0c 通俗地讲 xff0c 面向对象 就是在描述一个对象 xff0c
  • SVN无法提交修改问题

    分享一下我老师大神的人工智能教程 xff01 零基础 xff0c 通俗易懂 xff01 http blog csdn net jiangjunshow 也欢迎大家转载本篇文章 分享知识 xff0c 造福人民 xff0c 实现我们中华民族伟大
  • AbstractApplicationContext.refresh()应用上下文刷新方法

    前情提要 学习源码光看博客文章基本没有记住的可能 结合源码和博客 43 实践才能够通过理解记住 看了很多天才断断续续看完 发现它和其他IOC Bean生命周期都有关联 将之前片段式的认知串联起来了 这个刷新的代码很长 建议没耐心的时候就先不