SpringBoot

创建一个简单的SpringBoot项目:

@SpringBootApplication//启动类上的启动注解
public class Springboot1Application {
    public static void main(String[] args) {
        SpringApplication.run(Springboot1Application.class, args);
    }

做个控制层的类

@RestController//控制层注解    表明以@Restful  以rest规范(请求方式:get,put,delete,json)发请求和响应
public class HelloWorld {
    @GetMapping("/hello")//请求方式为get   ,请求路径为/hello
    public String hello(@RequestParam(value = "name",defaultValue = "World")String name){
//        /hello?name=ycinfo  ->  映射到 @RestController  注解的控制层类中找   /hello 配置的方法
        return String.format("Hello %s!",name);
    }
}

这样就可以在浏览器上输入localhost:8080/hello来看到helloWorld了

下面我们来研究一下源码
@SpringBootApplication首先来看这个注解,点进去研究一下

@Target(ElementType.TYPE)//可以在类上加此注解
@Retention(RetentionPolicy.RUNTIME)//保留到运行时
@Documented//生成上下文
@Inherited//表示可以被继承
@SpringBootConfiguration//表示这是一个SpringBoot的配置类
@EnableAutoConfiguration//启用自动装配
@ComponentScan(excludeFilters = { @Filter(type = FilterType.CUSTOM, classes = TypeExcludeFilter.class),@Filter(type = FilterType.CUSTOM, classes = AutoConfigurationExcludeFilter.class) })//包扫描
public @interface SpringBootApplication {}

这里关键要看的是1.@SpringBootConfiguration2.@EnableAutoConfiguration3.@ComponentScan注解

首先看@SpringBootConfiguration

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Configuration
public @interface SpringBootConfiguration {}

可以看到有@Configuration注解所以可以得出@SpringBootApplication是一个容器配置类也就是SpringBoot配置类

再来看@EnableAutoConfiguration

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@AutoConfigurationPackage//自动配置的包
@Import(AutoConfigurationImportSelector.class)//导入
public @interface EnableAutoConfiguration {}

@AutoConfigurationPackage:作用是将添加该注解的类所在的包作为自动配置的包进行管理(默认情况下扫描所在的包下的所有类及其子包)

重点

@Import(AutoConfigurationImportSelector.class)导入了AutoConfigurationImportSelector自动配置的导入选择器
AutoConfigurationImportSelector中主要得看

@Override
//这个方法作用就是 选择导入器  把一个参数是类上的注解元信息
	public String[] selectImports(AnnotationMetadata annotationMetadata) {
	//看看是否启用了注解如果没有则返回
		if (!isEnabled(annotationMetadata)) {
			return NO_IMPORTS;
		}
		AutoConfigurationEntry autoConfigurationEntry = getAutoConfigurationEntry(annotationMetadata);//获取这个注解的
		return StringUtils.toStringArray(autoConfigurationEntry.getConfigurations());
	}

然后我们继续追踪getAutoConfigurationEntry(annotationMetadata)这个方法

protected AutoConfigurationEntry getAutoConfigurationEntry(AnnotationMetadata annotationMetadata) {
		if (!isEnabled(annotationMetadata)) {
			return EMPTY_ENTRY;
		}
		//那一个注解的元信息来获取其中的属性
		AnnotationAttributes attributes = getAttributes(annotationMetadata);
		List<String> configurations = getCandidateConfigurations(annotationMetadata, attributes);
		//去除重复配置信息
		configurations = removeDuplicates(configurations);
		//得到要排除的配置信息
		Set<String> exclusions = getExclusions(annotationMetadata, attributes);
		//检查一下
		checkExcludedClasses(configurations, exclusions);
		//去除要排除的配置信息
		configurations.removeAll(exclusions);
		//再来使用过滤器来过滤一下
		configurations = getConfigurationClassFilter().filter(configurations);
		//激活自动配置的导入事件
		fireAutoConfigurationImportEvents(configurations, exclusions);
		return new AutoConfigurationEntry(configurations, exclusions);
	}

其中最重要的一句话就是

用注解的元信息和属性getCandidateConfigurations方法来获取配置信息
List configurations = getCandidateConfigurations(annotationMetadata, attributes);
继续追踪getCandidateConfigurations(annotationMetadata, attributes);

protected List<String> getCandidateConfigurations(AnnotationMetadata metadata, AnnotationAttributes attributes) {
                                     //spring的工厂加载器加载工厂bean          获取spring工厂加载器的类    获取类加载器                   
		List<String> configurations = SpringFactoriesLoader.loadFactoryNames(getSpringFactoriesLoaderFactoryClass(),
				getBeanClassLoader());
		Assert.notEmpty(configurations, "No auto configuration classes found in META-INF/spring.factories. If you "
				+ "are using a custom packaging, make sure that file is correct.");
		return configurations;
	}

这个方法就是来加载配置信息的,上面不太好看,得看下面的断言,就是断言这个配置信息不为空,如果为空则就会返回"No auto configuration classes…"
继续追踪loadFactoryNames(getSpringFactoriesLoaderFactoryClass(),getBeanClassLoader())

public static List<String> loadFactoryNames(Class<?> factoryType, @Nullable ClassLoader classLoader) {
		ClassLoader classLoaderToUse = classLoader;
		//容错判断
		if (classLoaderToUse == null) {
			classLoaderToUse = SpringFactoriesLoader.class.getClassLoader();
		}
		//获取到工厂名
		String factoryTypeName = factoryType.getName();
		//根据类加载器来获取spring的工厂加载器然后通过工厂名来获取配置信息以为可能有多个所以是数组
		return loadSpringFactories(classLoaderToUse).getOrDefault(factoryTypeName, Collections.emptyList());
	}

看他是如何通过加载器来获取spring的工厂
loadSpringFactories(classLoaderToUse)

private static Map<String, List<String>> loadSpringFactories(ClassLoader classLoader) {
//这里先用类加载器来创建一个map如果不为空则说明这个配置已经存在了返回,为空则创建一个map
		Map<String, List<String>> result = cache.get(classLoader);
		if (result != null) {
			return result;
		}

		result = new HashMap<>();
		try {
		    //通过来加载器来获取所有的META-INF/spring.factories下的信息
			Enumeration<URL> urls = classLoader.getResources(FACTORIES_RESOURCE_LOCATION);
			//下面就是循环的把META-INF/spring.factories以键值对的形式放入result中
			while (urls.hasMoreElements()) {
				URL url = urls.nextElement();
				UrlResource resource = new UrlResource(url);
				Properties properties = PropertiesLoaderUtils.loadProperties(resource);
				for (Map.Entry<?, ?> entry : properties.entrySet()) {
					String factoryTypeName = ((String) entry.getKey()).trim();
					String[] factoryImplementationNames =
							StringUtils.commaDelimitedListToStringArray((String) entry.getValue());
					for (String factoryImplementationName : factoryImplementationNames) {
						result.computeIfAbsent(factoryTypeName, key -> new ArrayList<>())
								.add(factoryImplementationName.trim());
					}
				}
			}

			// Replace all lists with unmodifiable lists containing unique elements
			//将所有列表替换为包含唯一元素的不可修改列表
			//也就是把这些配置信息存到缓存中
			result.replaceAll((factoryType, implementations) -> implementations.stream().distinct()
					.collect(Collectors.collectingAndThen(Collectors.toList(), Collections::unmodifiableList)));
			cache.put(classLoader, result);
		}
		catch (IOException ex) {
			throw new IllegalArgumentException("Unable to load factories from location [" +
					FACTORIES_RESOURCE_LOCATION + "]", ex);
		}
		return result;
	}

讲了这么多还有一个问题没有解决,selectImports()方法谁调的?
下面我们再来看看主方法里的run()

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

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

public ConfigurableApplicationContext run(String... args) {
        //创建计时器
		StopWatch stopWatch = new StopWatch();
		stopWatch.start();
		DefaultBootstrapContext bootstrapContext = createBootstrapContext();
		ConfigurableApplicationContext context = null;
		configureHeadlessProperty();
		//创建spring的监听器
		SpringApplicationRunListeners listeners = getRunListeners(args);
		listeners.starting(bootstrapContext, this.mainApplicationClass);
		try {
		//把参数包装成DefaultApplicationArguments
			ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
			//这个很重要准备环境了,并且把环境跟spring上下文绑定好,并且执行environmentPrepared()方法
			ConfigurableEnvironment environment = prepareEnvironment(listeners, bootstrapContext, applicationArguments);
			configureIgnoreBeanInfo(environment);
			Banner printedBanner = printBanner(environment);
			context = createApplicationContext();
			context.setApplicationStartup(this.applicationStartup);
			prepareContext(bootstrapContext, context, environment, listeners, applicationArguments, printedBanner);
			//这个是spring启动的代码了,这里就回去里面就回去扫描并且初始化单实列bean了
			refreshContext(context);
			afterRefresh(context, applicationArguments);
			stopWatch.stop();
			if (this.logStartupInfo) {
				new StartupInfoLogger(this.mainApplicationClass).logStarted(getApplicationLog(), stopWatch);
			}
			listeners.started(context);
			callRunners(context, applicationArguments);
		}
		catch (Throwable ex) {
			handleRunFailure(context, ex, listeners);
			throw new IllegalStateException(ex);
		}

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

中间最重要的就是refreshContext(context);方法

private void refreshContext(ConfigurableApplicationContext context) {
		if (this.registerShutdownHook) {
			try {
				context.registerShutdownHook();
			}
			catch (AccessControlException ex) {
				// Not allowed in some environments.
			}
		}
		refresh((ApplicationContext) context);
	}

继续追踪refresh((ApplicationContext) context);一直到底会发现这是一个接口

public interface ConfigurableApplicationContext extends ApplicationContext, Lifecycle, Closeable {
void refresh() throws BeansException, IllegalStateException;
}

然后我们来看看这个接口的实现类

public class ServletWebServerApplicationContext extends GenericWebApplicationContext
		implements ConfigurableWebServerApplicationContext {
		public final void refresh() throws BeansException, IllegalStateException {
		try {
			super.refresh();
		}
		catch (RuntimeException ex) {
			WebServer webServer = this.webServer;
			if (webServer != null) {
				webServer.stop();
			}
			throw ex;
		}
	}
	}

继续追踪super.refresh();调用的是他父类AbstractApplicationContext类中的方法

到这里的时候就会发现现在其实就不是springBoot的东西了而是spring了

public abstract class AbstractApplicationContext extends DefaultResourceLoader
		implements ConfigurableApplicationContext {
		@Override
	public void refresh() throws BeansException, IllegalStateException {
		synchronized (this.startupShutdownMonitor) {
			StartupStep contextRefresh = this.applicationStartup.start("spring.context.refresh");

			// Prepare this context for refreshing.
			prepareRefresh();

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

			// Prepare the bean factory for use in this context.
			prepareBeanFactory(beanFactory);

			try {
				// Allows post-processing of the bean factory in context subclasses.
				postProcessBeanFactory(beanFactory);

				StartupStep beanPostProcess = this.applicationStartup.start("spring.context.beans.post-process");
				// Invoke factory processors registered as beans in the context.
				//这个方法启动工厂处理器以注册bean到容器
				invokeBeanFactoryPostProcessors(beanFactory);
				}
				...
	}

主要看invokeBeanFactoryPostProcessors(beanFactory);方法

protected void invokeBeanFactoryPostProcessors(ConfigurableListableBeanFactory beanFactory) {
		PostProcessorRegistrationDelegate.invokeBeanFactoryPostProcessors(beanFactory, getBeanFactoryPostProcessors());
		...
	}

然后他调用了PostProcessorRegistrationDelegate类中的 invokeBeanDefinitionRegistryPostProcessors方法

private static void invokeBeanDefinitionRegistryPostProcessors(
			Collection<? extends BeanDefinitionRegistryPostProcessor> postProcessors, BeanDefinitionRegistry registry, ApplicationStartup applicationStartup) {

		for (BeanDefinitionRegistryPostProcessor postProcessor : postProcessors) {
			StartupStep postProcessBeanDefRegistry = applicationStartup.start("spring.context.beandef-registry.post-process")
					.tag("postProcessor", postProcessor::toString);
			postProcessor.postProcessBeanDefinitionRegistry(registry);
			postProcessBeanDefRegistry.end();
		}
	}

然后追踪postProcessBeanDefinitionRegistry会发现是一个接口

public interface BeanDefinitionRegistryPostProcessor extends BeanFactoryPostProcessor {
	void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException;

}

追踪他的实现类ConfigurationClassPostProcessor

@Override
	public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) {
		...
		processConfigBeanDefinitions(registry);
	}

调用自身的processConfigBeanDefinitions(registry);

public void processConfigBeanDefinitions(BeanDefinitionRegistry registry) {
        ...
		// Parse each @Configuration class
		ConfigurationClassParser parser = new ConfigurationClassParser(
				this.metadataReaderFactory, this.problemReporter, this.environment,
				this.resourceLoader, this.componentScanBeanNameGenerator, registry);

		Set<BeanDefinitionHolder> candidates = new LinkedHashSet<>(configCandidates);
		Set<ConfigurationClass> alreadyParsed = new HashSet<>(configCandidates.size());
		do {
			StartupStep processConfig = this.applicationStartup.start("spring.context.config-classes.parse");
			parser.parse(candidates);
			}
			...
		}

这里调用了ConfigurationClassParser中的parse()方法然后他会继续调用自身的processConfigurationClass()

protected final void parse(AnnotationMetadata metadata, String beanName) throws IOException {
		processConfigurationClass(new ConfigurationClass(metadata, beanName), DEFAULT_EXCLUSION_FILTER);
	}


protected void processConfigurationClass(ConfigurationClass configClass, Predicate<String> filter) throws IOException {
		...
		// Recursively process the configuration class and its superclass hierarchy.
		SourceClass sourceClass = asSourceClass(configClass, filter);
		do {
			sourceClass = doProcessConfigurationClass(configClass, sourceClass, filter);
		}
	}

调用自身的doProcessConfigurationClass(configClass, sourceClass, filter);

protected final SourceClass doProcessConfigurationClass(
			ConfigurationClass configClass, SourceClass sourceClass, Predicate<String> filter)
			throws IOException {
			...
			// Process any @Import annotations
			processImports(configClass, sourceClass, getImports(sourceClass), filter, true);
		...
		}

接下来就可以看到我们之前的问题了他是如何调用的selectImports

private void processImports(ConfigurationClass configClass, SourceClass currentSourceClass,
			Collection<SourceClass> importCandidates, Predicate<String> exclusionFilter,
			boolean checkForCircularImports) {
			...
						else {
							String[] importClassNames = selector.selectImports(currentSourceClass.getMetadata());
							Collection<SourceClass> importSourceClasses = asSourceClasses(importClassNames, exclusionFilter);
							processImports(configClass, currentSourceClass, importSourceClasses, exclusionFilter, false);
						}
					}
					...
	}

String[] importClassNames = selector.selectImports(currentSourceClass.getMetadata());
就是这里调用了SpringBoot注解中的selectImports()方法来获取配置信息,他会自动去META-INF/spring.factories中找配置信息

中间有些源代码删减掉了用…代替了避免太复杂

  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值