编程框架-spring boot之自动装配(spring-boot-autoconfigure)

背景

spring boot可以简化配置,主要就是由自动装配实现,现在总结一篇详细的,之后在封装框架的时候用到。
本博客重点:

  1. 自动装配流程
  2. spring boot注解对启动流程的作用
    • @SpringBootApplication
    • @SpringBootConfiguration
    • @EnableAutoConfiguration
  3. xxxx

过程分析

springboot工程的main函数中会添加一个SpringApplication.run,那么就从这里开始

SpringApplication构造过程

简易流程图

在这里插入图片描述

  1. 推测现在的环境,主要看是不是web环境(在springboot2.0之后,这里编程了枚举,因为web环境包括react模式了,也就是netty的模式)
  2. getSpringFactoriesInstances,获取META-INF/spring.factories里面的ApplicationContextInitializer类型的所有配置类,设置给initializers(等到构造完毕以后进行启动)
  3. getSpringFactoriesInstances,获取所有ApplicationListener类型的所有配置类,保存在listeners中
  4. deduceMainApplicationClass,推断main的类,进行保存

核心代码

以下为spring boot 2的代码

/**
	 * Create a new {@link SpringApplication} instance. The application context will load
	 * beans from the specified primary sources (see {@link SpringApplication class-level}
	 * documentation for details. The instance can be customized before calling
	 * {@link #run(String...)}.
	 * @param resourceLoader the resource loader to use
	 * @param primarySources the primary bean sources
	 * @see #run(Class, String[])
	 * @see #setSources(Set)
	 */
	@SuppressWarnings({ "unchecked", "rawtypes" })
	public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) {
		// 这里的resourceLoader是null
		this.resourceLoader = resourceLoader;
		// primarySources是入口类DemoApplication.class
		Assert.notNull(primarySources, "PrimarySources must not be null");
		// 制作成有序集合(为了去重和排序)
		this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources));
		// 使用WebApplication去探测application的类型
		this.webApplicationType = WebApplicationType.deduceFromClasspath();
		// 设置需要初始化的内容(这些内容都是通过扫描某个基类实现,下文将对getSpringFactoriesInstances进行阐述)
		setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class));
		// 设置监听内容
		setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
		// 检测入口class是哪个(这里当然就是DemoApplication了)
		this.mainApplicationClass = deduceMainApplicationClass();
	}
  • application类型判断,主要看是不是web类型的,如果是web的话,那么到底是servlet还是reactive的
  • 设置启动内容
  • 设置监听内容

大家要问了,设置启动内容和监听内容显然是构造的重点,下文又是怎么利用的呢?我们来继续看下,这里可以直接看getSpringFactoriesInstances方法。

private <T> Collection<T> getSpringFactoriesInstances(Class<T> type, Class<?>[] parameterTypes, Object... args) {
		// 获取classLoader,从debug来看,这里是AppClassLoader(最终是ClassUtils.getDefaultClassLoader中获取)
		ClassLoader classLoader = getClassLoader();
		// Use names and ensure unique to protect against duplicates
		Set<String> names = new LinkedHashSet<>(SpringFactoriesLoader.loadFactoryNames(type, classLoader));
		// 根据类名创建实例(也就是说,谁实现或者继承了这个基类或者接口,都会被实例化出来,最终返回)
		List<T> instances = createSpringFactoriesInstances(type, parameterTypes, classLoader, args, names);
		AnnotationAwareOrderComparator.sort(instances);
		return instances;
	}

SpringApplication的run过程

总体流程图

在这里插入图片描述

  1. getRunListeners,获取工厂配置中所有SpringApplicationRunListener类型的配置类,使用SpringApplicationRunListener的配置类来构造SpringApplicationRunListeners(这个时候一并传入了logger的引用,推测这个时候logger也创建的差不多了。。)
  2. starting上面的Listeners(这个过程以下另行分析,这里包含了异步消息和线程池)
  3. 创建DefaultApplicationArguments,不关键
  4. prepareEnvironment,准备运行环境,分为web和非web的(本工程以下将另行分析)
  5. configureIgnoreBeanInfo,在环境变量中设置spring.beaninfo.ignore为true,即默认忽略beanInfo
  6. printBanner,打印springboot的广告。。。(暂不进行分析)
  7. createApplicationContext,创建运行环境,例如web运行环境,其实就是通过反射来找到Class,然后通过BeanUtils进行实例化(重点是BeanUtils实例化的对象将进入spring容器)
  8. getSpringFactoriesInstances获取类型为SpringBootExceptionReporter的配置类,同时传入context运行环境
  9. prepareContext,配置上下文,这个上下文和运行环境不是一个概念(以下另行分析)
  10. refreshContext,运行上下文启动的全过程(以下另行分析)
  11. afterRefresh,空函数,这之后进行了一次计时,也标志着springboot启动完毕
  12. StartupInfoLogger的logStarted,打印一下启动时间,这个信息经常在启动过程中看到
  13. listeners.started,遍历所有的listener,逐个发送开始启动消息,注意,这里是异步消息
  14. callRunners,也就是执行ApplicationRunner的run方法(通过context.getBeansOfType直接活得这样的bean,然后执行),值得注意的是,这里可以调整执行的优先级,通过实现PriorityOrdered接口即可,代码中有提现
  15. handleRunFailure,异常处理,暂不过多分析
  16. listeners.running,遍历所有的listener,逐个发送运行中消息,注意,这里是异步消息
  17. handleRunFailure

启动监听器listeners.starting()过程

在这里插入图片描述
通过上述流程最终调用到以下代码。
SimpleApplicationEventMulticaster主要依赖两个:

  • executor,可选,这里是执行invokeListener的载体,如果没有的话,就在当前线程执行
  • Listener,通过getApplicationListeners获取所有实现过ApplicationListener的类(在spring boot工程里面,只要实现ApplicationListener,再添加@Component注解,就可以进行消息的打印了。不过呢,这个消息是每个springcontext一套,spring cloud的context同样也会打印)。其实这些Listener就是之前构造函数里面设置的。

注:需要了解springframework:spring-context的包

	@Override
	public void multicastEvent(final ApplicationEvent event, @Nullable ResolvableType eventType) {
		ResolvableType type = (eventType != null ? eventType : resolveDefaultEventType(event));
		Executor executor = getTaskExecutor();
		for (ApplicationListener<?> listener : getApplicationListeners(event, type)) {
			if (executor != null) {
				executor.execute(() -> invokeListener(listener, event));
			}
			else {
				invokeListener(listener, event);
			}
		}
	}

doInvoke过程其实就是回调过程。
在这里插入图片描述

@SuppressWarnings({"rawtypes", "unchecked"})
	private void doInvokeListener(ApplicationListener listener, ApplicationEvent event) {
		try {
			listener.onApplicationEvent(event);
		}
		catch (ClassCastException ex) {
			String msg = ex.getMessage();
			if (msg == null || matchesClassCastMessage(msg, event.getClass())) {
				// Possibly a lambda-defined listener which we could not resolve the generic event type for
				// -> let's suppress the exception and just log a debug message.
				Log logger = LogFactory.getLog(getClass());
				if (logger.isTraceEnabled()) {
					logger.trace("Non-matching event type for listener: " + listener, ex);
				}
			}
			else {
				throw ex;
			}
		}
	}

最终调用到doInvokeListener,回调给上层的onApplicationEvent

prepareEnvironment准备运行环境过程

  • configureEnvironment,设置环境,包括设置servlet的启动参数和profile的active环境参数等。这里涉及到了命令行传入args的解析、环境变量的解析(启动的时候也可以通过环境变量来设置部分参数,具体没有细看。。)
  • environmentPrepared,用于event通知,在通知过程中,进行配置文件的读取
    在这里插入图片描述
  • bindToSpringApplication,把运行环境和当前application应用进行绑定
  • convertEnvironmentIfNecessary,环境转化(客户化定制化的环境对象需要进行一次转化,这个定制化环境的功能是可能在某些极端情况下用到)
  • ConfigurationPropertySources.attach,配置绑定

注:准备环境的过程主要是加载配置(自定义配置、系统配置)

prepareContext准备上下文过程

待续。。。。

根据提供的引用内容,spring-boot-starter-data-redis是Spring Boot中用于自动装配Redis的starter包。它包含了自动装配所需的类和注解等。当我们在项目的pom.xml文件中引入spring-boot-starter-data-redis包时,Spring Boot会自动根据配置文件中的相关配置信息来完成Redis的自动装配。 具体来说,spring-boot-starter-data-redis使用了RedisAutoConfiguration类来实现自动装配。该类通过读取配置文件中的相关配置信息,例如主机名、端口号、密码等,来创建Redis连接工厂和RedisTemplate等实例。这些实例可以在应用程序中直接使用,而无需手动配置和初始化。 下面是一个示例代码,展示了如何使用spring-boot-starter-data-redis进行自动装配: ```java import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.data.redis.core.RedisTemplate; @SpringBootApplication public class RedisApplication { private final RedisTemplate<String, String> redisTemplate; public RedisApplication(RedisTemplate<String, String> redisTemplate) { this.redisTemplate = redisTemplate; } public static void main(String[] args) { SpringApplication.run(RedisApplication.class, args); } // 在需要使用Redis的地方,可以直接注入RedisTemplate实例,并进行操作 // 例如: // redisTemplate.opsForValue().set("key", "value"); // String value = redisTemplate.opsForValue().get("key"); } ``` 通过上述代码,我们可以看到,在Spring Boot应用程序中,我们只需要在需要使用Redis的地方注入RedisTemplate实例,就可以直接使用Redis的相关操作方法。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值