SpringBoot 2.3.x 启动方法源码解析

本文深入探讨了SpringBoot 2.3.x的启动过程,从run方法开始,逐步解析getRunListeners、prepareEnvironment、prepareContext、refreshContext和callRunners等关键步骤,涉及环境配置、监听器、ApplicationContext初始化和Bean加载等内容。

这是最常见的也是使用最多的SpringBoot应用程序启动类,整个SpringBoot应用启动都要靠执行run方法来启动

@SpringBootApplication
public class Hibernate52Application  {

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

}

点进去看看

// 这是SpringApplication的run方法
public static ConfigurableApplicationContext run(Class<?> primarySource, String... args) {
		return run(new Class<?>[] { primarySource }, args);
}
// 上面的run方法实际是调用这个run方法
public static ConfigurableApplicationContext run(Class<?>[] primarySources, String[] args) {
		return new SpringApplication(primarySources).run(args);
}

首先实例化SpringApplication

private ResourceLoader resourceLoader;
private WebApplicationType webApplicationType;
private List<ApplicationContextInitializer<?>> initializers;
private List<ApplicationListener<?>> listeners;
private Class<?> mainApplicationClass;
public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) {
		// 资源加载器
		this.resourceLoader = resourceLoader;
		Assert.notNull(primarySources, "PrimarySources must not be null");
		// JavaConfig类型的类
		this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources));
		// 根据classPath推导出web应用类型
		this.webApplicationType = WebApplicationType.deduceFromClasspath();
		// 初始化initializers属性
		setInitializers((Collection)getSpringFactoriesInstances(ApplicationContextInitializer.class));
		// 设置监听器
		setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
		// 推导出主应用程序类
		this.mainApplicationClass = deduceMainApplicationClass();
	}

SprigBootApplication 中有非常多的属性,先看这个构造方法中使用到的

  • resourceLoader 资源加载器
  • primarySources 在这就是 Hibernate52Application.class
  • webApplicationType web应用程序类型,看代码是根据Classpath推导而来
  • Initializer 元素类型为ApplicationContextInitializer的列表
  • listeners 元素类型为ApplicationListener的列表
  • mainApplicationClass 主应用程序类

上面的方法,先一个一个看

  1. deduceFormClasspath
public enum WebApplicationType {
/**
	 * The application should not run as a web application and should not start an
	 * embedded web server.
	 * 没有内嵌服务器
	 */
	NONE,

	/**
	 * The application should run as a servlet-based web application and should start an
	 * embedded servlet web server.
	 * Servlet类型的应用
	 */
	SERVLET,

	/**
	 * The application should run as a reactive web application and should start an
	 * embedded reactive web server.
	 * 响应式的应用 比如Spring-Webflux
	 */
	REACTIVE;

	private static final String[] SERVLET_INDICATOR_CLASSES = { "javax.servlet.Servlet",
			"org.springframework.web.context.ConfigurableWebApplicationContext" };

	private static final String WEBMVC_INDICATOR_CLASS = "org.springframework.web.servlet.DispatcherServlet";

	private static final String WEBFLUX_INDICATOR_CLASS = "org.springframework.web.reactive.DispatcherHandler";

	private static final String JERSEY_INDICATOR_CLASS = "org.glassfish.jersey.servlet.ServletContainer";
// 这个方法的作用就是判断当前的应用类型,通过使用反射在类路径下寻找是否存在对应的类,从而判断出该应用属于什么类型的应用
	static WebApplicationType deduceFromClasspath() {
			if (ClassUtils.isPresent(WEBFLUX_INDICATOR_CLASS, null) && !ClassUtils.isPresent(WEBMVC_INDICATOR_CLASS, null)
					&& !ClassUtils.isPresent(JERSEY_INDICATOR_CLASS, null)) {
				return WebApplicationType.REACTIVE;
			}
			for (String className : SERVLET_INDICATOR_CLASSES) {
				if (!ClassUtils.isPresent(className, null)) {
					return WebApplicationType.NONE;
				}
			}
			return WebApplicationType.SERVLET;
		}
}
  1. getSpringFactoriesInstances
	private <T> Collection<T> getSpringFactoriesInstances(Class<T> type) {
		return getSpringFactoriesInstances(type, new Class<?>[] {});
	}

	private <T> Collection<T> getSpringFactoriesInstances(Class<T> type, Class<?>[] parameterTypes, Object... args) {
		// 获取类加载器
		ClassLoader classLoader = getClassLoader();
		// Use names and ensure unique to protect against duplicates
		// 加载指定类型在"META-INF/spring.factories"对应的类名数组
		Set<String> names = new LinkedHashSet<>(SpringFactoriesLoader.loadFactoryNames(type, classLoader));
		// 根据类名数组创建实例
		List<T> instances = createSpringFactoriesInstances(type, parameterTypes, classLoader, args, names);
		// 排序实例
		AnnotationAwareOrderComparator.sort(instances);
		return instances;
	}
	// 其中的loadFactoryNames方法并没有点进去看,其实里面的逻辑就是根据type加载在"META-INF/spring.factories"对应的类名数组
	// 在META-INF/spring.factories中,以KEY-VALUE形式存储了各个类对应的实现类们
	@SuppressWarnings("unchecked")
	private <T> List<T> createSpringFactoriesInstances(Class<T> type, Class<?>[] parameterTypes,
			ClassLoader classLoader, Object[] args, Set<String> names) {
		List<T> instances = new ArrayList<>(names.size());
		for (String name : names) {
			try {
				// 获取name对应的类
				Class<?> instanceClass = ClassUtils.forName(name, classLoader);
				// 判断实例是否实现自type类
				Assert.isAssignable(type, instanceClass);
				// 获取构造方法
				Constructor<?> constructor = instanceClass.getDeclaredConstructor(parameterTypes);
				// 创建实例
				T instance = (T) BeanUtils.instantiateClass(constructor, args);
				instances.add(instance);
			}
			catch (Throwable ex) {
				throw new IllegalArgumentException("Cannot instantiate " + type + " : " + name, ex);
			}
		}
		return instances;
	}
  1. deduceMainApplicationClass
	private Class<?> deduceMainApplicationClass() {
		try {
			// 获得堆栈元素数组
			// 这有意思的是通过new一个运行时异常来获得堆栈元素数组来找到main方法的栈帧
			StackTraceElement[] stackTrace = new RuntimeException().getStackTrace();
			// 判断到底是哪个类执行了main方法
			for (StackTraceElement stackTraceElement : stackTrace) {
				if ("main".equals(stackTraceElement.getMethodName())) {
					return Class.forName(stackTraceElement.getClassName());
				}
			}
		}
		catch (ClassNotFoundException ex) {
			// Swallow and continue
		}
		return null;
	}
	// 这个方法的作用主要是用来获得启动类,用来打印启动时的日志,说明应用是通过这个类启动的。

构造方法看完了,现在再看看run方法

public ConfigurableApplicationContext run(String... args) {
		// 统计启动时长
		StopWatch stopWatch = new StopWatch();
		stopWatch.start();
		//
		ConfigurableApplicationContext context = null;
		Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList<>();
		// 配置 headless属性 和awt相关
		configureHeadlessProperty();
		// 1. 创建SpirngApplicationRunListeners数组
		SpringApplicationRunListeners listeners = getRunListeners(args);
		// 启动监听
		listeners.starting();
		try {
			// 创建applicationArguments 应用程序参数
			ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
			// 2. 加载属性配置
			ConfigurableEnvironment environment = prepareEnvironment(listeners, applicationArguments);
			configureIgnoreBeanInfo(environment);
			// 启动打印banner
			Banner printedBanner = printBanner(environment);
			// 3. 创建Spring容器
			context = createApplicationContext();
			// 获得异常报告器
			exceptionReporters = getSpringFactoriesInstances(SpringBootExceptionReporter.class,
					new Class<?>[] { ConfigurableApplicationContext.class }, context);
			// 4.准备环境
			prepareContext(context, environment, listeners, applicationArguments, printedBanner);
			// 5.刷新环境 (初始化容器)
			refreshContext(context);
			// 后置刷新 逻辑为空
			afterRefresh(context, applicationArguments);
			// 停止统计时长
			stopWatch.stop();
			// 打印启动的时长的日志
			if (this.logStartupInfo) {
				new StartupInfoLogger(this.mainApplicationClass).logStarted(getApplicationLog(), stopWatch);
			}
			// 启动Spring容器
			listeners.started(context);
			// 6.调用runners
			callRunners(context, applicationArguments);
		}
		catch (Throwable ex) {
			handleRunFailure(context, ex, exceptionReporters, listeners);
			throw new IllegalStateException(ex);
		}

		try {
			listeners.running(context);
		}
		catch (Throwable ex) {
			handleRunFailure(context, ex, exceptionReporters, null);
			throw new IllegalStateException(ex);
		}
		return context;
	}

run方法的逻辑大致如此,这里面有几个比较重要的方法还是点进去看一下,分别是getRunListeners,prepareEnvironment,prepareContext,refreshContext,callRunners,

1. getRunListeners

	// 该方法获取了SpringApplicationRunListener在META-INF/spring.factories中对应的实现类并且返回了他们的实例
	private SpringApplicationRunListeners getRunListeners(String[] args) {
		Class<?>[] types = new Class<?>[] { SpringApplication.class, String[].class };
		return new SpringApplicationRunListeners(logger,
				getSpringFactoriesInstances(SpringApplicationRunListener.class, types, this, args));
	}
	//这里实际上也是用了上面的getSpringFactoriesInstances方法

此时的Listeners变量
在这里插入图片描述

2. prepareEnvironment

	private ConfigurableEnvironment prepareEnvironment(SpringApplicationRunListeners listeners,
			ApplicationArguments applicationArguments) {
		// Create and configure the environment
		// 2.1创建环境
		ConfigurableEnvironment environment = getOrCreateEnvironment();
		// 2.2配置环境
		configureEnvironment(environment, applicationArguments.getSourceArgs());
		// 2.3属性绑定环境
		ConfigurationPropertySources.attach(environment);
		// 2.4通知SpringApplicationRunListeners,环境已经准备好
		listeners.environmentPrepared(environment);
		// 2.5将DefaultProperties移动到environment的最后
		DefaultPropertiesPropertySource.moveToEnd(environment);
		// 2.6配置其他额外的profiles
		configureAdditionalProfiles(environment);
		// 2.7把环境绑定到SpringApplication
		bindToSpringApplication(environment);
		// 2.8如果不是自定义环境,根据条件转换
		if (!this.isCustomEnvironment) {
			environment = new EnvironmentConverter(getClassLoader()).convertEnvironmentIfNecessary(environment,
					deduceEnvironmentClass());
		}
		// 如果有attach到environment的MutablePropertySources,就添加到environment的PropertySources中
		ConfigurationPropertySources.attach(environment);
		return environment;
	}

2.1 getOrCreateEnvironment

	// 创建环境
	private ConfigurableEnvironment getOrCreateEnvironment() {
		// 存在就返回
		if (this.environment != null) {
			return this.environment;
		}
		// 不存在就根据构造方法中推断出的web应用类型来进行创建
		switch (this.webApplicationType) {
		case SERVLET:
			return new StandardServletEnvironment();
		case REACTIVE:
			return new StandardReactiveWebEnvironment();
		default:
			return new StandardEnvironment();
		}
	}

2.2 configureEnvironment

protected void configureEnvironment(ConfigurableEnvironment environment, String[] args) {
		// 设置environment的conversionService属性
		if (this.addConversionService) { //true
			ConversionService conversionService = ApplicationConversionService.getSharedInstance();
			environment.setConversionService((ConfigurableConversionService) conversionService);
		}
		// 2.2.1配置environment的propertySources属性
		configurePropertySources(environment, args);
		// 2.2.2配置environment的profiles属性
		configureProfiles(environment, args);
	}

这涉及到了很多名词比如environment,profile,propertySource等,先挖个坑,知道大体意思就好,具体的细节以后再看

2.2.1 configurePropertySources

	protected void configurePropertySources(ConfigurableEnvironment environment, String[] args) {
		MutablePropertySources sources = environment.getPropertySources();
		DefaultPropertiesPropertySource.ifNotEmpty(this.defaultProperties, sources::addLast);
		if (this.addCommandLineProperties && args.length > 0) {
			// 如果存在就替换
			String name = CommandLinePropertySource.COMMAND_LINE_PROPERTY_SOURCE_NAME;
			if (sources.contains(name)) {
				PropertySource<?> source = sources.get(name);
				CompositePropertySource composite = new CompositePropertySource(name);
				composite.addPropertySource(
						new SimpleCommandLinePropertySource("springApplicationCommandLineArgs", args));
				composite.addPropertySource(source);
				sources.replace(name, composite);
			}
			else {
				// 不存在就添加	
				sources.addFirst(new SimpleCommandLinePropertySource(args));
			}
		}
	}

这段代码大体意思就是propertySource可以通过命令行参数进行附加

2.2.2 configureProfiles

	// 逻辑为空
	protected void configureProfiles(ConfigurableEnvironment environment, String[] args) {
	}

2.3 attach

	// 这个方法的作用大概就是往environment的MutablePropertySources中添加一个新的配置
	public static void attach(Environment environment) {
		Assert.isInstanceOf(ConfigurableEnvironment.class, environment);
		MutablePropertySources sources = ((ConfigurableEnvironment) environment).getPropertySources();
		PropertySource<?> attached = sources.get(ATTACHED_PROPERTY_SOURCE_NAME);
		if (attached != null && attached.getSource() != sources) {
			sources.remove(ATTACHED_PROPERTY_SOURCE_NAME);
			attached = null;
		}
		if (attached == null) {
			sources.addFirst(new ConfigurationPropertySourcesPropertySource(ATTACHED_PROPERTY_SOURCE_NAME,
					new SpringConfigurationPropertySources(sources)));
		}
	}

2.4 environmentPrepared

    //通知SpringApplicationRunListener,环境已经准备完成
	void environmentPrepared(ConfigurableEnvironment environment) {
		for (SpringApplicationRunListener listener : this.listeners) {
			listener.environmentPrepared(environment);
		}
	}

2.5 moveToEnd

	public static final String NAME = "defaultProperties";
	// 将defaultProperties移动到environment的PropertySources的最后,至于这么做的目的,不太清楚
	public static void moveToEnd(ConfigurableEnvironment environment) {
		moveToEnd(environment.getPropertySources());
	}
	public static void moveToEnd(MutablePropertySources propertySources) {
		PropertySource<?> propertySource = propertySources.remove(NAME);
		if (propertySource != null) {
			propertySources.addLast(propertySource);
		}
	}

2.6 configureAdditionalProfiles

	// 配置额外的配置文件
	private void configureAdditionalProfiles(ConfigurableEnvironment environment) {
		if (!CollectionUtils.isEmpty(this.additionalProfiles)) {
			Set<String> profiles = new LinkedHashSet<>(Arrays.asList(environment.getActiveProfiles()));
			if (!profiles.containsAll(this.additionalProfiles)) {
				profiles.addAll(this.additionalProfiles);
				environment.setActiveProfiles(StringUtils.toStringArray(profiles));
			}
		}
	}

2.7 bindToSpringApplication

	// 绑定springApplication到environment 不知道有啥用
	protected void bindToSpringApplication(ConfigurableEnvironment environment) {
		try {
			Binder.get(environment).bind("spring.main", Bindable.ofInstance(this));
		}
		catch (Exception ex) {
			throw new IllegalStateException("Cannot bind to SpringApplication", ex);
		}
	}

2.8 如果不是自定义条件 则转换

默认情况下 isCustomEnvironment 为false,所以这的代码一般就会执行,不过返回的还是StandardEnvironment类型的environment 代码不放了

3.createApplicationContext

	private ApplicationContextFactory applicationContextFactory = ApplicationContextFactory.DEFAULT;
	// 根据webApplicationType创建spring容器
	protected ConfigurableApplicationContext createApplicationContext() {
		return this.applicationContextFactory.create(this.webApplicationType);
	}
	//根据webApplicationType创建不同的context
	ApplicationContextFactory DEFAULT = (webApplicationType) -> {
		try {
			switch (webApplicationType) {
			case SERVLET:
				return new AnnotationConfigServletWebServerApplicationContext();
			case REACTIVE:
				return new AnnotationConfigReactiveWebServerApplicationContext();
			default:
				return new AnnotationConfigApplicationContext();
			}
		}
		catch (Exception ex) {
			throw new IllegalStateException("Unable create a default ApplicationContext instance, "
					+ "you may need a custom ApplicationContextFactory", ex);
		}
	};

4.prepareContext

	private void prepareContext(ConfigurableApplicationContext context, ConfigurableEnvironment environment,
			SpringApplicationRunListeners listeners, ApplicationArguments applicationArguments, Banner printedBanner) {
		// 上下文设置环境
		context.setEnvironment(environment);
		// 1.后置处理上下文
		postProcessApplicationContext(context);
		// 2.初始化Initilizers
		applyInitializers(context);
		// 3.通知listeners上下文已经准备好
		listeners.contextPrepared(context);
		// 打印日志
		if (this.logStartupInfo) {
			logStartupInfo(context.getParent() == null);
			logStartupProfileInfo(context);
		}
		// Add boot specific singleton beans
		// 设置beanFactory的属性
		ConfigurableListableBeanFactory beanFactory = context.getBeanFactory();
		beanFactory.registerSingleton("springApplicationArguments", applicationArguments);
		if (printedBanner != null) {
			beanFactory.registerSingleton("springBootBanner", printedBanner);
		}
		if (beanFactory instanceof DefaultListableBeanFactory) {
			((DefaultListableBeanFactory) beanFactory)
					.setAllowBeanDefinitionOverriding(this.allowBeanDefinitionOverriding);
		}
		if (this.lazyInitialization) {
			context.addBeanFactoryPostProcessor(new LazyInitializationBeanFactoryPostProcessor());
		}
		// Load the sources
		// 加载BeanDefinition们
		Set<Object> sources = getAllSources();
		Assert.notEmpty(sources, "Sources must not be empty");
		// 4.加载
		load(context, sources.toArray(new Object[0]));
		// 通知 SpringApplicationRunListener 的数组,Spring 容器加载完成。
		listeners.contextLoaded(context);
	}

4.1 postProcessApplicationContext

	//后置处理applicationContext
	protected void postProcessApplicationContext(ConfigurableApplicationContext context) {
		if (this.beanNameGenerator != null) {
			context.getBeanFactory().registerSingleton(AnnotationConfigUtils.CONFIGURATION_BEAN_NAME_GENERATOR,
					this.beanNameGenerator);
		}
		if (this.resourceLoader != null) {
			if (context instanceof GenericApplicationContext) {
				((GenericApplicationContext) context).setResourceLoader(this.resourceLoader);
			}
			if (context instanceof DefaultResourceLoader) {
				((DefaultResourceLoader) context).setClassLoader(this.resourceLoader.getClassLoader());
			}
		}
		// 只在SpringMVC环境下,前两个都为空,也就是只会进入这个分支,context的工厂会set一个conversionService
		if (this.addConversionService) {
			context.getBeanFactory().setConversionService(ApplicationConversionService.getSharedInstance());
		}
	}

4.2 applyInitializers

	// 大概意思就是遍历之后判断非空然后初始化initializer
	protected void applyInitializers(ConfigurableApplicationContext context) {
		for (ApplicationContextInitializer initializer : getInitializers()) {
			Class<?> requiredType = GenericTypeResolver.resolveTypeArgument(initializer.getClass(),
					ApplicationContextInitializer.class);
			Assert.isInstanceOf(requiredType, context, "Unable to call initializer.");
			initializer.initialize(context);
		}
	}

4.3 contextPrepared

	// 通知SpringApplicationRunListeners:容器已经准备好了
	void contextPrepared(ConfigurableApplicationContext context) {
		doWithListeners("spring.boot.application.context-prepared", (listener) -> listener.contextPrepared(context));
	}
	private void doWithListeners(String stepName, Consumer<SpringApplicationRunListener> listenerAction) {
		doWithListeners(stepName, listenerAction, StartupStep::end);
	}

	private void doWithListeners(String stepName, Consumer<SpringApplicationRunListener> listenerAction,
			Consumer<StartupStep> stepAction) {
		StartupStep step = this.applicationStartup.start(stepName);
		this.listeners.forEach(listenerAction);
		stepAction.accept(step);
		step.end();
	}

4.4 load

	protected void load(ApplicationContext context, Object[] sources) {
		if (logger.isDebugEnabled()) {
			logger.debug("Loading source " + StringUtils.arrayToCommaDelimitedString(sources));
		}
		BeanDefinitionLoader loader = createBeanDefinitionLoader(getBeanDefinitionRegistry(context), sources);
		if (this.beanNameGenerator != null) {
			loader.setBeanNameGenerator(this.beanNameGenerator);
		}
		if (this.resourceLoader != null) {
			loader.setResourceLoader(this.resourceLoader);
		}
		if (this.environment != null) {
			loader.setEnvironment(this.environment);
		}
		loader.load();
	}
	void load() {
		for (Object source : this.sources) {
			// BeanDefinitionLoader根据sources加载beandefinition
			load(source);
		}
	}

关于IOC加载Bean,可以看【死磕 Spring】—– IOC 之 加载 Bean

5.refreshcontext

	private void refreshContext(ConfigurableApplicationContext context) {
		// 1.刷新容器
		// 这里可以触发springboot的自动配置功能,挖个坑 以后研究
		refresh((ApplicationContext) context);
		// 2.注册关闭挂钩,主要用来在容器关闭时销毁bean信息
		if (this.registerShutdownHook) {
			try {
				context.registerShutdownHook();
			}
			catch (AccessControlException ex) {
				// Not allowed in some environments.
			}
		}
	}

6.callRunners

	private void callRunners(ApplicationContext context, ApplicationArguments args) {
		// runners列表
		List<Object> runners = new ArrayList<>();
		// 获得所有ApplicationRunner实例
		runners.addAll(context.getBeansOfType(ApplicationRunner.class).values());
		// 获得所有CommandLineRunner实例
		runners.addAll(context.getBeansOfType(CommandLineRunner.class).values());
		// 排序runners
		AnnotationAwareOrderComparator.sort(runners);
		// 遍历并执行
		for (Object runner : new LinkedHashSet<>(runners)) {
			if (runner instanceof ApplicationRunner) {
				callRunner((ApplicationRunner) runner, args);
			}
			if (runner instanceof CommandLineRunner) {
				callRunner((CommandLineRunner) runner, args);
			}
		}
	}

总结

  • Spring Boot的启动分为实例化SpringApplication以及run方法。
    实例化方法主要做两件事:
    1.初始化Initializers
    2.初始化Listeners
    这两种初始化方法都是通过加载’META-INF/spring.factories’中配置的实现类来实现的。
  • Spring Boot的启动方法代码就到此结束了,只是简单的过了一遍,还有好多概念不明白,也有几个坑要填,比如environment,profile到底是啥,还有Spring Boot的自动配置功能到底怎么实现的,以及tomcat是怎么嵌入Spring Boot中的。
<think>我们正在处理用户关于SpringBoot2.3.x升级到2.7.x的问题。根据用户提供的引用信息,我们可以整合一些关键点,特别是引用[1]和引用[3]中提到的关于版本更新和依赖管理的部分。注意:引用[2]提到了Kafka客户端版本兼容性问题,引用[4]是关于NetBackup升级的,与SpringBoot升级关系不大,但我们可以忽略不相关引用。升级SpringBoot版本是一个多步骤的过程,需要谨慎操作。根据SpringBoot官方升级指南,我们可以总结以下步骤:1.**更新SpringBoot版本号**:在Maven或Gradle中修改父项目或插件的版本号。2.**处理依赖库的兼容性**:检查并更新相关依赖库的版本,确保与SpringBoot2.7.x兼容。3.**配置更新**:根据版本变更日志调整配置文件(如application.properties/yml)。4.**代码更新**:修改因API变化而受影响的代码。5.**测试**:全面测试应用程序。我们参考引用[1]中提到的在pom.xml中更新版本号的方式,以及引用[3]中关于Logback升级的注意事项(因为SpringBoot2.7.x可能使用更新的Logback版本)。具体步骤:**步骤1:更新SpringBoot版本**对于Maven项目,在pom.xml中修改父项目的版本:```xml<parent><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-parent</artifactId><version>2.7.18</version><!--使用2.7.x的最新版本,这里以2.7.18为例--></parent>```对于Gradle项目,在build.gradle中修改插件版本:```groovyplugins{id&#39;org.springframework.boot&#39;version&#39;2.7.18&#39;}```**步骤2:更新依赖库**检查所有依赖库是否与SpringBoot2.7.x兼容。特别是注意:-数据库驱动-各种SpringBootStarters-第三方库(如Kafka、Logback等)引用[3]提到Logback从1.2.x升级到1.3.x的注意事项,因为SpringBoot2.7.x可能使用Logback1.3.x。需要检查日志配置文件(logback.xml)是否符合新版本要求。引用[2]提醒我们,如果使用Kafka,需要注意客户端版本兼容性。SpringBoot2.7.x默认使用的Kafka客户端版本是2.8.x,如果需要使用2.4.0客户端,可能需要覆盖版本(但建议使用SpringBoot管理的版本)。**步骤3:处理配置变更**查阅SpringBoot2.4到2.7的官方迁移指南,特别注意配置属性的变化。例如:-在2.4版本中,配置文件处理机制有重大变化(多文档配置文件支持,默认激活的配置文件改变等)。-一些过时的属性被移除,需要替换为新的属性。官方迁移指南链接:https://github.com/spring-projects/spring-boot/wiki/Spring-Boot-2.4-Release-Notes以及后续版本的ReleaseNotes。**步骤4:代码更新**检查并更新代码中过时的API。例如:-在SpringBoot2.3中,`server.max-http-header-size`被替换为`server.max-http-request-header-size`和`server.max-http-response-header-size`。-SpringBoot2.4开始,`spring.main.allow-bean-definition-overriding`的默认值从true变为false,如果项目中有同名bean覆盖,需要显式设置为true或解决冲突。**步骤5:测试**进行全面的测试,包括单元测试、集成测试和功能测试。另外,建议逐步升级:先从2.3.x升级到2.4.x,再升级到2.5.x,然后2.6.x,最后2.7.x。每一步都解决兼容性问题,这样更容易定位问题。根据引用[3]的提示,在升级过程中要特别注意日志框架的兼容性。Logback1.3.x移除了`StaticLoggerBinder`,因此如果项目中有直接引用该类的代码,需要移除。生成相关问题:1.升级SpringBoot时如何处理依赖库的兼容性问题?2.SpringBoot2.4版本在配置文件处理上有哪些重大变化?3.如何解决SpringBoot升级后出现的日志配置问题?</think>### Spring Boot2.3.x 升级到 2.7.x 指南 #### 1. **版本更新** 首先修改构建工具的版本声明: - **Maven** (参考引用[1]) ```xml <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>2.7.18</version> <!-- 2.7.x最新稳定版 --> </parent> ``` - **Gradle** ```gradle plugins { id &#39;org.springframework.boot&#39; version &#39;2.7.18&#39; } ``` #### 2. **依赖兼容性检查** - **关键依赖升级**: ```xml <!-- 示例:Spring Cloud 需匹配 2.7.x --> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-dependencies</artifactId> <version>2021.0.8</version> <!-- 对应 Boot 2.7.x --> </dependency> ``` - **第三方库适配** (参考引用[2]): - 如使用 Kafka,需确认 `kafka-clients` 版本与 Spring Kafka 兼容 - 使用 `mvn dependency:tree` 检查冲突依赖 #### 3. **配置迁移** - **废弃属性处理**: - 替换 `management.metrics.export.*` 为 `management.*.metrics.export.*` - 移除 `spring.security.oauth2.client.registration.*.client-authentication-method` - **配置文件调整**: ```yaml # 新版本多文档配置语法 (YAML) spring: config: activate: on-profile: prod --- spring: profiles: dev ``` #### 4. **日志框架升级** (参考引用[3]) Logback 1.3.x 需注意: - 移除 `StaticLoggerBinder` 引用 - 配置文件更新: ```xml <!-- logback.xml 示例 --> <configuration> <appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender"> <encoder> <pattern>%d{ISO8601} [%thread] %-5level %logger{36} - %msg%n</pattern> </encoder> </appender> <root level="INFO"> <appender-ref ref="CONSOLE" /> </root> </configuration> ``` #### 5. **代码变更** - 过时 API 替换: - `SpringApplication.run()` 无需显式调用 `setAllowBeanDefinitionOverriding` - 迁移 `JUnit 4` 到 `JUnit 5` 测试 - 安全配置更新: ```java // 新版本安全配置示例 @Configuration public class SecurityConfig extends WebSecurityConfigurerAdapter { @Override protected void configure(HttpSecurity http) throws Exception { http.authorizeRequests(auth -> auth.anyRequest().authenticated()) .oauth2Login(withDefaults()); } } ``` #### 6. **升级后验证** 1. 运行测试套件:`mvn test` 或 `gradle test` 2. 检查启动日志中的弃用警告 3. 关键功能回归测试 4. 监控内存和性能指标 > **重要提示**:建议通过中间版本逐步升级(如 2.32.4 → 2.5 → 2.6 → 2.7),每个版本参考官方迁移指南: > - [Spring Boot 2.4 Release Notes](https://github.com/spring-projects/spring-boot/wiki/Spring-Boot-2.4-Release-Notes) > - [Spring Boot 2.7 Migration Guide](https://github.com/spring-projects/spring-boot/wiki/Spring-Boot-2.7-Release-Notes)
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值