SpringBoot启动和自动装配那点事儿

在这里插入图片描述

SpringBoot启动时做了什么?

基于2.2.2.RELEASE

在引入了SprinBoot依赖后,我们通常只需要如下一个简单的启动类,就可以完成整个SpringBoot项目的启动使用:

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

SpringBoot实际就是基于Spring,只是帮我们进行了一些大量的自动化配置,使我们无须关心太多就可以轻松上手使用,因此我们在阅读源码的时候要关注如下几个问题:

  1. 在如上的启动类中,SpringBoot是如何与Spring集成的?
  2. 又是如何启动内嵌的web服务器的?
  3. 以及大量的类是如何进行收集加载到Spring容器中的?springboot如何完成这些自动化装配?
  4. AOP啊、缓存啊、事务啊等等功能是怎么加到Springboot的?

如果要把类加载到Spring中,有哪些方式呢?其实在前面文章讲解Spring的IOC容器和实例化的源码时也介绍了,无非以下几种:

  1. 使用@Component注解使之被@ComponentScan注解扫描到(当然@Configuration相关注解也可以)
  2. 通过xml配置文件的方式,在xml中配置bean标签;
  3. 基于SPI服务发现的机制,在spring.factories文件中配置接口和要实现类的关系,Spring在启动时会加载文件中的类到容器中管理,该方式通常用于管理第三方包中的bean,因为第三方包的命名和当前工程肯定不一样,不好通过@ComponentScan管理。

一、SpringApplication构造【准备】

从run方法点进去,最终会来到SpringApplication#ConfigurableApplicationContext run(Class<?>[] primarySources, String[] args)位置,大概在1257行。

public static ConfigurableApplicationContext run(Class<?>[] primarySources, String[] args) {
		return new SpringApplication(primarySources).run(args);
	}

这里对SpringApplication进行了构造:

public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) {
		this.resourceLoader = resourceLoader;
		Assert.notNull(primarySources, "PrimarySources must not be null");
  	//就是我们的启动类
		this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources));
    //1.1
		this.webApplicationType = WebApplicationType.deduceFromClasspath();
   //1.2
		setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class));
		setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
		this.mainApplicationClass = deduceMainApplicationClass();
	}

说明:

1.1 服务启动类型判断

webApplicationType:服务的启动类型,共有三个类型:

static WebApplicationType deduceFromClasspath() {
		if (ClassUtils.isPresent(“org.springframework.web.reactive.DispatcherHandler”, null) && !ClassUtils.isPresent(“org.springframework.web.servlet.DispatcherServlet”, null)
				&& !ClassUtils.isPresent(“org.glassfish.jersey.servlet.ServletContainer”, null)) {
			return WebApplicationType.REACTIVE;
		}
		for (String className : ["javax.servlet.Servlet",
			"org.springframework.web.context.ConfigurableWebApplicationContext"]) {
			if (!ClassUtils.isPresent(className, null)) {
				return WebApplicationType.NONE;
			}
		}
		return WebApplicationType.SERVLET;
	}
  • REACTIVE:Reactive web是响应式编程的一种新的编程风格,要使用Reactive的嵌入式web服务器
  • SERVLET:默认的servlet web类型,可以使用内嵌的web服务器
  • NONE:非web应用,不能使用内嵌的web服务器

这里会通过ClassUtils.isPresent判断当前classpath下某些class是否存在来返回具体的web类型,默认都会是ServletWeb

1.2 加载应用上下文初始器 ApplicationContextInitializer

这里会通过SPI的方式从spring.factories文件中加载:

		private <T> Collection<T> getSpringFactoriesInstances(Class<T> type, Class<?>[] parameterTypes, Object... args) {
		ClassLoader classLoader = getClassLoader();
		// 1.2.1Use names and ensure unique to protect against duplicates
		Set<String> names = new LinkedHashSet<>(SpringFactoriesLoader.loadFactoryNames(type, classLoader));
		// 1.2.2 加载上来后反射实例化
		List<T> instances = createSpringFactoriesInstances(type, parameterTypes, classLoader, args, names);
		AnnotationAwareOrderComparator.sort(instances);
		return instances;
	}

1.2.1 SPI服务加载

Spring中的SPI基本都是通过SpringFactoriesLoader.loadFactoryNames方法进行加载的:

public static List<String> loadFactoryNames(Class<?> factoryType, @Nullable ClassLoader classLoader) {
		String factoryTypeName = factoryType.getName();
		return loadSpringFactories(classLoader).getOrDefault(factoryTypeName, Collections.emptyList());
	}

这里的factoryTypeName就是org.springframework.context.ApplicationContextInitializer

继续:

这里就是具体收集文件中所有接口的实现类:

private static Map<String, List<String>> loadSpringFactories(@Nullable ClassLoader classLoader) {
   //从缓存中取,第一次没有,加载完毕后会将结果放到该缓存中
		MultiValueMap<String, String> result = cache.get(classLoader);
		if (result != null) {
			return result;
		}

		try {
      //拿到spring下所有项目的spring.factories文件
			Enumeration<URL> urls = (classLoader != null ?
					classLoader.getResources("META-INF/spring.factories") :
					ClassLoader.getSystemResources("META-INF/spring.factories"));
			result = new LinkedMultiValueMap<>();
			while (urls.hasMoreElements()) {
				URL url = urls.nextElement();
				UrlResource resource = new UrlResource(url);
				Properties properties = PropertiesLoaderUtils.loadProperties(resource);
        //遍历当前配置文件的所有key-value
				for (Map.Entry<?, ?> entry : properties.entrySet()) {
					String factoryTypeName = ((String) entry.getKey()).trim();
          //因为一个key下可以有多个实现类(value),因此要将每个实现类都添加进去
					for (String factoryImplementationName : StringUtils.commaDelimitedListToStringArray((String) entry.getValue())) {
						result.add(factoryTypeName, factoryImplementationName.trim());
					}
				}
			}
			cache.put(classLoader, result);
			return result;
		}
		catch (IOException ex) {
			throw new IllegalArgumentException("Unable to load factories from location [" +
					FACTORIES_RESOURCE_LOCATION + "]", ex);
		}
	}

最后会通过getOrDefault(factoryTypeName, Collections.emptyList());根据当前的factoryTypeName(ApplicationContextInitializer)拿到其下的所有实现类:

在这里插入图片描述

1.2.2 实例化实现类

拿到所有的实现类Set<String>names后,就调用createSpringFactoriesInstances方法,内部遍历names通过反射实例化添加到集合中返回。

最后在构造函数中将集合保存到List<ApplicationContextInitializer<?>> initializers;中。

1.3 加载应用事件监听器ApplicationListener

这里同样的方式从spring.factories加载以ApplicationListener为key的监听器实现类,由于上边已经加载过了,因此这里可直接从缓存中获取。

1.4 获取启动类

在构造的最后,会通过``getStackTrace()`方法获取当前线程的执行栈,再遍历判断方法名是否为"main"来获取main方法所在的类,对其进行实例化返回。

二、SpringApplication运行

在准备工作做好后,就来到了具体的run()方法,大概在298行。

2.1 加载SpringApplicationRunListener

public ConfigurableApplicationContext run(String... args) {
		// 统计时间的计时器
		StopWatch stopWatch = new StopWatch();
		stopWatch.start();
		ConfigurableApplicationContext context = null;
		Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList<>();
		configureHeadlessProperty();
		// 获取实现了SpringApplicationRunListener接口的实现类,通过SPI机制加载
		SpringApplicationRunListeners listeners = getRunListeners(args);

		// 首先调用SpringApplicationRunListener的starting方法
		listeners.starting();
}

在这里同样以SPI的方式获取所有实现了SpringApplicationRunListener接口的类,放到SpringApplicationRunListeners中的SpringApplicationRunListener集合,并通过listeners.starting();遍历所有的listener调用其starting方法。

# Run Listeners
org.springframework.boot.SpringApplicationRunListener=\
org.springframework.boot.context.event.EventPublishingRunListener

这里SpringApplicationRunListener主要负责在springboot的各个阶段广播对应的事件:

SpringApplicationRunListeners:

 private final Log log;

	private final List<SpringApplicationRunListener> listeners;

	SpringApplicationRunListeners(Log log, Collection<? extends SpringApplicationRunListener> listeners) {
		this.log = log;
		this.listeners = new ArrayList<>(listeners);
	}
void starting() {
		for (SpringApplicationRunListener listener : this.listeners) {
			listener.starting();
		}
	}

	void environmentPrepared(ConfigurableEnvironment environment) {
		for (SpringApplicationRunListener listener : this.listeners) {
			listener.environmentPrepared(environment);
		}
	}

	void contextPrepared(ConfigurableApplicationContext context) {
		for (SpringApplicationRunListener listener : this.listeners) {
			listener.contextPrepared(context);
		}
	}

	void contextLoaded(ConfigurableApplicationContext context) {
		for (SpringApplicationRunListener listener : this.listeners) {
			listener.contextLoaded(context);
		}
	}

	void started(ConfigurableApplicationContext context) {
		for (SpringApplicationRunListener listener : this.listeners) {
			listener.started(context);
		}
	}

	void running(ConfigurableApplicationContext context) {
		for (SpringApplicationRunListener listener : this.listeners) {
			listener.running(context);
		}
	}

	void failed(ConfigurableApplicationContext context, Throwable exception) {
		for (SpringApplicationRunListener listener : this.listeners) {
			callFailedListener(listener, context, exception);
		}
	}

根据方法名可以很容易推断出对应的事件阶段,具体会调用SpringApplicationRunListener的实现类EventPublishingRunListener

2.2 封装启动参数

			ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);

args就是启动传入的参数,这里会将其封装成ApplicationArguments。我们可以直接通过@Autowired注入ApplicationArguments调用其方法,获取相应的参数信息。

2.3 加载外部化配置

ConfigurableEnvironment environment = prepareEnvironment(listeners, applicationArguments);
configureIgnoreBeanInfo(environment);
///
private ConfigurableEnvironment prepareEnvironment(SpringApplicationRunListeners listeners,
			ApplicationArguments applicationArguments) {
		// 1.通过前面的webApplicationType返回不同的ConfigurableEnvironment
    //通常是servlet的StandardServletEnvironment
		ConfigurableEnvironment environment = getOrCreateEnvironment();
		configureEnvironment(environment, applicationArguments.getSourceArgs());
		ConfigurationPropertySources.attach(environment);
		//2. 调用SpringApplicationRunListener的environmentPrepared方法
		listeners.environmentPrepared(environment);
		bindToSpringApplication(environment);
		if (!this.isCustomEnvironment) {
			environment = new EnvironmentConverter(getClassLoader()).convertEnvironmentIfNecessary(environment,
					deduceEnvironmentClass());
		}
		ConfigurationPropertySources.attach(environment);
		return environment;
	}

这里会发布ApplicationEnvironmentPreparedEvent事件,涉及的监听器如下:

在这里插入图片描述

会加载哪些外部化配置呢?

如:

  1. properties或yaml配置文件
  2. 环境变量
  3. 一些系统属性和启动参数等

如上面第一个监听器ConfigFileApplicationListeneronApplicationEvent方法中会监听到该事件,监听到后就会进行加载properties或yaml配置文件,这些配置信息包括其他外部化配置都会加载到Environment类中,因此我们可以直接注入该类获取相关信息。

@Override
	public void onApplicationEvent(ApplicationEvent event) {
		if (event instanceof ApplicationEnvironmentPreparedEvent) {
			onApplicationEnvironmentPreparedEvent((ApplicationEnvironmentPreparedEvent) event);
		}
		if (event instanceof ApplicationPreparedEvent) {
			onApplicationPreparedEvent(event);
		}
	}

所有的外部化配置都对应一个PropertySource,并保存到MutabledPropertySources类的如下集合中:

public class MutablePropertySources implements PropertySources {
	private final List<PropertySource<?>> propertySourceList = new CopyOnWriteArrayList<>();
}

PropertySource数据结构如下:

public abstract class PropertySource<T> {
	protected final String name;
	protected final T source;
}

name为配置配置的路径,source为该配置中的所有配置信息。

可以看下执行完prepareEnvironmentConfigurableEnvironmentpropertySources属性中该集合中有哪些配置:

在这里插入图片描述

最后一个就是我们的yaml配置的具体内容。

【接着会打印banner内容,不重要】

2.4 创建上下文对象

ConfigurableApplicationContext content=null;
context = createApplicationContext();
protected ConfigurableApplicationContext createApplicationContext() {
		Class<?> contextClass = this.applicationContextClass;
		if (contextClass == null) {
			try {
				switch (this.webApplicationType) {
				case SERVLET:
					//AnnotationConfigServletWebServerApplicationContext
					contextClass = Class.forName(DEFAULT_SERVLET_WEB_CONTEXT_CLASS);
					break;
				case REACTIVE:
					contextClass = Class.forName(DEFAULT_REACTIVE_WEB_CONTEXT_CLASS);
					break;
				default:
					contextClass = Class.forName(DEFAULT_CONTEXT_CLASS);
				}
			}
			catch (ClassNotFoundException ex) {
				throw new IllegalStateException(
						"Unable create a default ApplicationContext, please specify an ApplicationContextClass", ex);
			}
		}
		return (ConfigurableApplicationContext) BeanUtils.instantiateClass(contextClass);
	}

这里也是根据准备阶段确定的webApplicationType类型返回不同的上下文对象。默认下对返回servlet类型的上下文实例:

org.springframework.boot.web.servlet.context.AnnotationConfigServletWebServerApplicationContext
public AnnotationConfigServletWebServerApplicationContext() {
		this.reader = new AnnotatedBeanDefinitionReader(this);
		this.scanner = new ClassPathBeanDefinitionScanner(this);
	}

在该类中会实例化如上两个对象。

AnnotatedBeanDefinitionReader:会将ConfigurationClassPostProcessorAutowiredAnnotationBeanPostProcessor等变成BeanDefinition对象并加入到BeanDefinitionRegistry中。
ClassPathBeanDefinitionScanner:在前面文章将Spring的时候,讲过在IOC初始化阶段会采用该scanner扫描包下所有类将符合条件的加载到容器中。

会发现该构造和讲Spring的时候使用AnnotationConfigApplicationContext创建上下文对象作为程序入口的构造是一样的:

AnnotationConfigApplicationContext context =
				new AnnotationConfigApplicationContext(Test.class);
///
public AnnotationConfigApplicationContext() {
		this.reader = new AnnotatedBeanDefinitionReader(this);
		this.scanner = new ClassPathBeanDefinitionScanner(this);
	}

2.5 准备上下文

			prepareContext(context, environment, listeners, applicationArguments, printedBanner);
private void prepareContext(ConfigurableApplicationContext context, ConfigurableEnvironment environment,
			SpringApplicationRunListeners listeners, ApplicationArguments applicationArguments, Banner printedBanner) {
		context.setEnvironment(environment);
		postProcessApplicationContext(context);
		applyInitializers(context);
		// 1.调用contextPrepared,上下文准备完成
		listeners.contextPrepared(context);
		if (this.logStartupInfo) {
			logStartupInfo(context.getParent() == null);
			logStartupProfileInfo(context);
		}
		// Add boot specific singleton beans
		ConfigurableListableBeanFactory beanFactory = context.getBeanFactory();
   //2.将封装的参数注册到容器中(单例的),因此我们可以直接注入使用
		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
		Set<Object> sources = getAllSources();
		Assert.notEmpty(sources, "Sources must not be empty");
		load(context, sources.toArray(new Object[0]));
    //3.上下文加载完成,但尚未启动
		listeners.contextLoaded(context);
	}

这里主要进行上下文的准备,并发布SpringApplicationRunListener的两个事件。

2.6 启动Spring容器

refreshContext(context);
/
private void refreshContext(ConfigurableApplicationContext context) {
		refresh(context);
		if (this.registerShutdownHook) {
			try {
				context.registerShutdownHook();
			}
			catch (AccessControlException ex) {
				// Not allowed in some environments.
			}
		}
	}
protected void refresh(ApplicationContext applicationContext) {
		Assert.isInstanceOf(AbstractApplicationContext.class, applicationContext);
		((AbstractApplicationContext) applicationContext).refresh();
	}

OK,到这里是不是很熟悉了,就是调用AbstractApplicationContextrefresh方法:

public void refresh() throws BeansException, IllegalStateException {
		synchronized (this.startupShutdownMonitor) {
			........................

			// Tell the subclass to refresh the internal bean factory.
			ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();

			try {
				.....

				// Initialize other special beans in specific context subclasses.
				onRefresh();

				.....
			}

		..........
		
	}

该方法是Spring启动的核心方法,也就搞清楚了Springboot如何与spring进行对接,这里在前面Spring源码文章讲的很清楚了,不再赘述。

主要关注钩子方法onRefresh

该方法会调用ServletWebServerApplicationContextonRefresh方法:

@Override
	protected void onRefresh() {
		super.onRefresh();
		try {
			//看这里
			createWebServer();
		}
		catch (Throwable ex) {
			throw new ApplicationContextException("Unable to start web server", ex);
		}
	}

createWebServer方法中就会创建web服务器。

2.7 启动内置Tomcat

private void createWebServer() {
		WebServer webServer = this.webServer;
		ServletContext servletContext = getServletContext();
		if (webServer == null && servletContext == null) {
			ServletWebServerFactory factory = getWebServerFactory();
			// 主要看这个方法
			this.webServer = factory.getWebServer(getSelfInitializer());
		}
		else if (servletContext != null) {
			try {
				getSelfInitializer().onStartup(servletContext);
			}
			catch (ServletException ex) {
				throw new ApplicationContextException("Cannot initialize servlet context", ex);
			}
		}
		initPropertySources();
	}

这里通过ServletWebServerFactory获取webServer,工厂类有三个实现类,我们主要看Tomcat的:

在这里插入图片描述

@Override
	public WebServer getWebServer(ServletContextInitializer... initializers) {
		if (this.disableMBeanRegistry) {
			Registry.disableRegistry();
		}
		//创建Tomcat容器
		Tomcat tomcat = new Tomcat();
		File baseDir = (this.baseDirectory != null) ? this.baseDirectory : createTempDir("tomcat");
		tomcat.setBaseDir(baseDir.getAbsolutePath());
		Connector connector = new Connector(this.protocol);
		connector.setThrowOnFailure(true);
		tomcat.getService().addConnector(connector);
		customizeConnector(connector);
		tomcat.setConnector(connector);
		tomcat.getHost().setAutoDeploy(false);
		configureEngine(tomcat.getEngine());
		for (Connector additionalConnector : this.additionalTomcatConnectors) {
			tomcat.getService().addConnector(additionalConnector);
		}
		prepareContext(tomcat.getHost(), initializers);
    
		return getTomcatWebServer(tomcat);
	}
  1. 创建Tomcat对象
  2. 配置Tomcat信息
  3. 创建TomcatWebServer返回
protected TomcatWebServer getTomcatWebServer(Tomcat tomcat) {
		return new TomcatWebServer(tomcat, getPort() >= 0);
	}

TomcatWebServer的构造中会调用initialize方法

public TomcatWebServer(Tomcat tomcat, boolean autoStart) {
		Assert.notNull(tomcat, "Tomcat Server must not be null");
		this.tomcat = tomcat;
		this.autoStart = autoStart;
		initialize();
	}
private void initialize() throws WebServerException {
		logger.info("Tomcat initialized with port(s): " + getPortsDescription(false));
		synchronized (this.monitor) {
			try {
				
				// Start the server to trigger initialization listeners
				this.tomcat.start();

				.......

				// Unlike Jetty, all Tomcat threads are daemon threads. We create a
				// blocking non-daemon to stop immediate shutdown
				startDaemonAwaitThread();
			}
			catch (Exception ex) {
				....
			}
		}
	}

在该方法中主要做了两件事:

  1. 启动Tomcat
  2. 创建一个守护线程防监听请求
private void startDaemonAwaitThread() {
		Thread awaitThread = new Thread("container-" + (containerCounter.get())) {

			@Override
			public void run() {
				TomcatWebServer.this.tomcat.getServer().await();
			}

		};
		awaitThread.setContextClassLoader(getClass().getClassLoader());
		awaitThread.setDaemon(false);
		awaitThread.start();
	}

【TIP】啥时候创建Web容器工厂的?

其实它也是在Springboot自动装配【具体收集、装配时机看第三部分】的时候【在调用spring的refresh()中进行的,而启动web服务器实在refresh()后的createWebServer()开始的】,所有的spring.factories文件,而在/spring-boot-autoconfigure-2.2.2.RELEASE/META-INF/spring.factories的文件中定义了这么个类:

# Auto Configure
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
  
....  org.springframework.boot.autoconfigure.web.servlet.ServletWebServerFactoryAutoConfiguration,\
....

当然还有好多没有列出来,如AOP的、Rabbit的、缓存的,还有我们常见的中间件:ES、Mongo等等的自动化配置类都在这里定义好了。

这里就看ServletWebServerFactoryAutoConfiguration类:

@Configuration(proxyBeanMethods = false)
@AutoConfigureOrder(Ordered.HIGHEST_PRECEDENCE)
@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 {
  ....
}

看到了啥?@Import导入了三个``ServletWebServerFactory`内容类,对应三个类型的,点进去看看:

@Configuration(proxyBeanMethods = false)
class ServletWebServerFactoryConfiguration {

	@Configuration(proxyBeanMethods = false)
	@ConditionalOnClass({ Servlet.class, Tomcat.class, UpgradeProtocol.class })
	@ConditionalOnMissingBean(value = ServletWebServerFactory.class, search = SearchStrategy.CURRENT)
	public static class EmbeddedTomcat {

		@Bean
		public TomcatServletWebServerFactory tomcatServletWebServerFactory(
				ObjectProvider<TomcatConnectorCustomizer> connectorCustomizers,
				ObjectProvider<TomcatContextCustomizer> contextCustomizers,
				ObjectProvider<TomcatProtocolHandlerCustomizer<?>> protocolHandlerCustomizers) {
			TomcatServletWebServerFactory factory = new TomcatServletWebServerFactory();
			factory.getTomcatConnectorCustomizers()
					.addAll(connectorCustomizers.orderedStream().collect(Collectors.toList()));
			factory.getTomcatContextCustomizers()
					.addAll(contextCustomizers.orderedStream().collect(Collectors.toList()));
			factory.getTomcatProtocolHandlerCustomizers()
					.addAll(protocolHandlerCustomizers.orderedStream().collect(Collectors.toList()));
			return factory;
		}

	}

	/**
	 * Nested configuration if Jetty is being used.
	 */
	@Configuration(proxyBeanMethods = false)
	@ConditionalOnClass({ Servlet.class, Server.class, Loader.class, WebAppContext.class })
	@ConditionalOnMissingBean(value = ServletWebServerFactory.class, search = SearchStrategy.CURRENT)
	public static class EmbeddedJetty {

		@Bean
		public JettyServletWebServerFactory JettyServletWebServerFactory(
				ObjectProvider<JettyServerCustomizer> serverCustomizers) {
			JettyServletWebServerFactory factory = new JettyServletWebServerFactory();
			factory.getServerCustomizers().addAll(serverCustomizers.orderedStream().collect(Collectors.toList()));
			return factory;
		}

	}

	/**
	 * Nested configuration if Undertow is being used.
	 */
	@Configuration(proxyBeanMethods = false)
	@ConditionalOnClass({ Servlet.class, Undertow.class, SslClientAuthMode.class })
	@ConditionalOnMissingBean(value = ServletWebServerFactory.class, search = SearchStrategy.CURRENT)
	public static class EmbeddedUndertow {

		@Bean
		public UndertowServletWebServerFactory undertowServletWebServerFactory(
				ObjectProvider<UndertowDeploymentInfoCustomizer> deploymentInfoCustomizers,
				ObjectProvider<UndertowBuilderCustomizer> builderCustomizers) {
			UndertowServletWebServerFactory factory = new UndertowServletWebServerFactory();
			factory.getDeploymentInfoCustomizers()
					.addAll(deploymentInfoCustomizers.orderedStream().collect(Collectors.toList()));
			factory.getBuilderCustomizers().addAll(builderCustomizers.orderedStream().collect(Collectors.toList()));
			return factory;
		}

	}

}

这里使用ConditionalOnClass对不同条件的情况创建不同的容器工厂类,默认会使用内嵌的Tomcat容器,而如果我们在依赖中移除内嵌的Tomcat,引入Jetty,这里就会创建一个jetty工厂JettyServletWebServerFactory.

运行阶段总结

可以看到,整个springboot的run的核心,就做了如下几件大事:

  1. 加载SpringApplicationRunListener
  2. 加载外部配置
  3. 创建上下文对象
  4. 启动Spring容器
  5. 创建和部署内置Tomcat服务器

三、自动化配置源码

在这里我们就需要关注:

  1. @EnableAutoConfiguration何时被加载?
  2. 基于SPI加载了spring.factories文件中的类后,在哪里,又是如何被装配到容器中的?

回到启动类:

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

也就剩了个注解,点进去看看:

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@SpringBootConfiguration
@EnableAutoConfiguration
@ComponentScan(excludeFilters = { @Filter(type = FilterType.CUSTOM, classes = TypeExcludeFilter.class),
		@Filter(type = FilterType.CUSTOM, classes = AutoConfigurationExcludeFilter.class) })
public @interface SpringBootApplication {
}

看到了么?

EnableAutoConfiguration:开启自动化配置,是不是很熟悉?前面讲 什么时候创建web容器工厂时在自动化配置项目的spring.factories文件中定义了该接口,并且提供了约125个自动配置类。

在该注解中,又导入了AutoConfigurationImportSelector

...........
@Import(AutoConfigurationImportSelector.class)
public @interface EnableAutoConfiguration {
}

该类很长,我们先看其中的process方法:

3.1 收集自动化配置类

@Override
		public void process(AnnotationMetadata annotationMetadata, DeferredImportSelector deferredImportSelector) {
			Assert.state(deferredImportSelector instanceof AutoConfigurationImportSelector,
					() -> String.format("Only %s implementations are supported, got %s",
							AutoConfigurationImportSelector.class.getSimpleName(),
							deferredImportSelector.getClass().getName()));
      
			AutoConfigurationEntry autoConfigurationEntry = ((AutoConfigurationImportSelector) deferredImportSelector)
        //1.主要看该方法
					.getAutoConfigurationEntry(getAutoConfigurationMetadata(), annotationMetadata);
			this.autoConfigurationEntries.add(autoConfigurationEntry);
			for (String importClassName : autoConfigurationEntry.getConfigurations()) {
				this.entries.putIfAbsent(importClassName, annotationMetadata);
			}
		}

这里主要看getAutoConfigurationEntry方法:

protected AutoConfigurationEntry getAutoConfigurationEntry(AutoConfigurationMetadata autoConfigurationMetadata,
			AnnotationMetadata annotationMetadata) {
		if (!isEnabled(annotationMetadata)) {
			return EMPTY_ENTRY;
		}
   //1.getAttributes
		AnnotationAttributes attributes = getAttributes(annotationMetadata);
		// 2.SPI获取EnableAutoConfiguration为key的所有实现类
		List<String> configurations = getCandidateConfigurations(annotationMetadata, attributes);
   //去重
		configurations = removeDuplicates(configurations);
		Set<String> exclusions = getExclusions(annotationMetadata, attributes);
		checkExcludedClasses(configurations, exclusions);
    //3.
		configurations.removeAll(exclusions);
		// 4.把某些自动配置类过滤掉
		configurations = filter(configurations, autoConfigurationMetadata);
   //5.
		fireAutoConfigurationImportEvents(configurations, exclusions);
		// 包装成自动配置实体类
		return new AutoConfigurationEntry(configurations, exclusions);
	}

  1. getAttributes方法:
protected AnnotationAttributes getAttributes(AnnotationMetadata metadata) {
		String name = getAnnotationClass().getName();
		AnnotationAttributes attributes = AnnotationAttributes.fromMap(metadata.getAnnotationAttributes(name, true));
		return attributes;
	}
protected Class<?> getAnnotationClass() {
		return EnableAutoConfiguration.class;
	}

从该方法可以看到就是获取了EnableAutoConfiguration注解的属性

  1. getCandidateConfigurations

    该类就是基于SPI获取所有以EnableAutoConfiguration为key的实现类名称

  2. 移除使用exclude定义的要排除的类;

  3. 过滤掉不需要的的自动配置类

  4. 发布AutoConfigurationImportEvents事件,基于SPI获取对应的监听类调用对应的事件。【不重要】

    private void fireAutoConfigurationImportEvents(List<String> configurations, Set<String> exclusions) {
    		// SPI获取所有AutoConfigurationImportListener接口实现类
    		List<AutoConfigurationImportListener> listeners = getAutoConfigurationImportListeners();
    		if (!listeners.isEmpty()) {
    			AutoConfigurationImportEvent event = new AutoConfigurationImportEvent(this, configurations, exclusions);
    			for (AutoConfigurationImportListener listener : listeners) {
    				invokeAwareMethods(listener);
    				listener.onAutoConfigurationImportEvent(event);
    			}
    		}
    	}
    
  5. 最后将收集好的要配置的类和要排除的类包装成AutoConfigurationEntry返回

有个疑问:该方法是在何时被调用的呢?

在前面运行阶段讲创建上下文对象的构造中提到:AnnotatedBeanDefinitionReader:会将ConfigurationClassPostProcessorAutowiredAnnotationBeanPostProcessor等变成BeanDefinition对象并加入到BeanDefinitionRegistry中。

该方法就是在调用ConfigurationClassPostProcessor时进来的,(之前讲Spring容器初始化已经讲过了,这里简单过一下),而ConfigurationClassPostProcessor又是在spring的核心方法refresh方法【即springboot调用refreshContext()刷新上下文的时候回调到】中的invokeBeanFactoryPostProcessors(beanFactory);调用的,该方法会遍历所有的BeanDefinitionRegistryPostProcessor接口进行处理。

ConfigurationClassPostProcessor是用于处理配置类的,其实现了BeanDefinitionRegistryPostProcessor,因此就会调用到该接口的postProcessBeanDefinitionRegistry方法;

接着会调用该方法中的processConfigBeanDefinitions()进行具体的处理,在processConfigBeanDefinitions中,会调用配置文件解析器进行解析所有被@Configuration注解标注的类:

20200829131820948

一直往下跟,会来到ConfigurationClassParser类的以个processImports方法:

在这里插入图片描述

注意这里,如果是DeferredImportSelector类型的selector就会进入下面的handle方法,回去看下EnableAutoConfiguration上面导入的AutoConfigurationImportSelector类:

public class AutoConfigurationImportSelector implements DeferredImportSelector, BeanClassLoaderAware,
		ResourceLoaderAware, BeanFactoryAware, EnvironmentAware, Ordered {
    }

也实现了DeferredImportSelector接口,因此就会从该handle方法处理这个类:

点进入会来到一个内部类DeferredImportSelectorGroupingHandlerprocessGroupImports方法:

在这里插入图片描述

进入getImports方法:

在这里插入图片描述

这里就会调用process()方法,其中第一个实现类就是3.1讲的那个类,这个AutoConfigurationGroupAutoConfigurationImportSelector的内部类,也实现了DeferredImportSelector接口。

3.2 加入到Spring容器

该收集的收集好了,下一步就是要将这些类加载到spring容器中,上图中调用完process后,紧接着就调用了selectImports方法,该方法也是AutoConfigurationImportSelector中的:

@Override
		public Iterable<Entry> selectImports() {
			if (this.autoConfigurationEntries.isEmpty()) {
				return Collections.emptyList();
			}
			Set<String> allExclusions = this.autoConfigurationEntries.stream()
					.map(AutoConfigurationEntry::getExclusions).flatMap(Collection::stream).collect(Collectors.toSet());
			Set<String> processedConfigurations = this.autoConfigurationEntries.stream()
					.map(AutoConfigurationEntry::getConfigurations).flatMap(Collection::stream)
					.collect(Collectors.toCollection(LinkedHashSet::new));
			processedConfigurations.removeAll(allExclusions);

			return sortAutoConfigurations(processedConfigurations, getAutoConfigurationMetadata()).stream()
					.map((importClassName) -> new Entry(this.entries.get(importClassName), importClassName))
					.collect(Collectors.toList());
		}

该类对收集好的要自动配置的类做简单的筛选排序等操作后返回,接着就会回到上面的processGroupImports方法的foreach中,剩下的就交给ConfigurationClassPostProcessor按照Spring的流程将其添加到Spring容器中。

到这我们就搞懂了Springboot加载@EnableAutoConfiguration、收集自动化配置类、和将配置类加载到Spring容器的各个时机。

接下来我们来看下一开始提到的第4个问题:AOP、事务这些事怎么加入到springboot中的?

这里以AOP功能为例,其他类似。

四、AOP的自动配置类

这些自动配置类肯定是在前面自动化配置时通过SPI加载到的。

在普通的Spring工程中,如果我们要使用AOP,就需要使用 @EnableAspectJAutoProxy注解开启,接着就会将AOP相关配置注册进来,详情请看:Spring的AOP调用流程源码分析~

OK,我们再来看看SpringBoot中怎么做的:

从自动配置的包的spring.factories中找到AOP的配置类:AopAutoConfiguration

@Configuration(proxyBeanMethods = false)
@ConditionalOnProperty(prefix = "spring.aop", name = "auto", havingValue = "true", matchIfMissing = true)
public class AopAutoConfiguration {

	@Configuration(proxyBeanMethods = false)
	@ConditionalOnClass(Advice.class)
	static class AspectJAutoProxyingConfiguration {

		@Configuration(proxyBeanMethods = false)
		@EnableAspectJAutoProxy(proxyTargetClass = false)
		@ConditionalOnProperty(prefix = "spring.aop", name = "proxy-target-class", havingValue = "false",
				matchIfMissing = false)
		static class JdkDynamicAutoProxyConfiguration {

		}

		@Configuration(proxyBeanMethods = false)
		@EnableAspectJAutoProxy(proxyTargetClass = true)
		@ConditionalOnProperty(prefix = "spring.aop", name = "proxy-target-class", havingValue = "true",
				matchIfMissing = true)
		static class CglibAutoProxyConfiguration {

		}

	}

	@Configuration(proxyBeanMethods = false)
	@ConditionalOnMissingClass("org.aspectj.weaver.Advice")
	@ConditionalOnProperty(prefix = "spring.aop", name = "proxy-target-class", havingValue = "true",
			matchIfMissing = true)
	static class ClassProxyingConfiguration {

		ClassProxyingConfiguration(BeanFactory beanFactory) {
			if (beanFactory instanceof BeanDefinitionRegistry) {
				BeanDefinitionRegistry registry = (BeanDefinitionRegistry) beanFactory;
				AopConfigUtils.registerAutoProxyCreatorIfNecessary(registry);
				AopConfigUtils.forceAutoProxyCreatorToUseClassProxying(registry);
			}
		}

	}
}

1.@ConditionalOnProperty(prefix = "spring.aop", name = "auto", havingValue = "true", matchIfMissing = true)

先加载配置文件,看看我们的配置文件中是否配置了spring.aop.auto=true这个选项,如果没有,则默认配置为true.

  1. 在内部类AspectJAutoProxyingConfiguration中有个@ConditionalOnClass(Advice.class),即当有Advice类时当前类才生效,因此当我们引入AOP的依赖后,当前类就会自动生效;

  2. 在内部又有两个类,对应使用JDK动态代理还是CGLIB代理,两个类上都有@EnableAspectJAutoProxy注解,OK到这就知道了,原来在这开启了AOP功能;

  3. 两个代理配置类上都有个@ConditionalOnProperty(prefix = "spring.aop", name = "proxy-target-class", havingValue = "false", matchIfMissing = false)注解,即如果我们配置了:

    spring:
      aop:
        proxy-target-class: false
    

    则启用JDK代理,如果没有配置该属性,则给默认值false,即默认不使用JDK代理;

    对应下面的,如果该属性值为true则开启CGLIG代理,如果没有的话就给true,即默认开启CGLIB代理。

就这些了,代码写的很明显。

总结。。也没啥好总结的,看一下文章目录就挺清晰:

在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值