基于Spring boot - 2.0.1.RELEAS
之前的文章介绍了:BeanFactory 、ApplicationContext、WebApplicationContext
也写了各个部分的实现,总觉得缺点什么,最近整理自己博客的时候,终于发现了。少SpringBoot的啊。
Spring 4.x IOC介绍(一.BeanFactory 、ApplicationContext、WebApplicationContext之BeanFactory)
Spring 4.x IOC介绍(二.BeanFactory 、ApplicationContext、WebApplicationContext之ApplicationContext)
Spring 4.x IOC介绍(三.BeanFactory 、ApplicationContext、WebApplicationContext之WebApplicationContext)
Spring 4.x IOC介绍(四.BeanFactory 、ApplicationContext、WebApplicationContext——总结)
但是现在使用Spring Boot居多,在启动SpringBoot的时候,是如何创建Bean的?这篇文章说一下SpringBoot启动以及创建Bean。
Spring boot 启动代码
@SpringBootApplication
public class StudyApplication {
public static void main(String[] args) {
SpringApplication.run(StudyApplication.class, args);
}
}
两个重点
- @SpringBootApplication 注解
- SpringApplication.run() 方法。
一个一个看
@SpringBootApplication 源码
@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 {
/**
* Exclude specific auto-configuration classes such that they will never be applied.
* @return the classes to exclude
*/
@AliasFor(annotation = EnableAutoConfiguration.class)
Class<?>[] exclude() default {};
/**
* Exclude specific auto-configuration class names such that they will never be
* applied.
* @return the class names to exclude
* @since 1.3.0
*/
@AliasFor(annotation = EnableAutoConfiguration.class)
String[] excludeName() default {};
/**
* Base packages to scan for annotated components. Use {@link #scanBasePackageClasses}
* for a type-safe alternative to String-based package names.
* @return base packages to scan
* @since 1.3.0
*/
@AliasFor(annotation = ComponentScan.class, attribute = "basePackages")
String[] scanBasePackages() default {};
/**
* Type-safe alternative to {@link #scanBasePackages} for specifying the packages to
* scan for annotated components. The package of each class specified will be scanned.
* <p>
* Consider creating a special no-op marker class or interface in each package that
* serves no purpose other than being referenced by this attribute.
* @return base packages to scan
* @since 1.3.0
*/
@AliasFor(annotation = ComponentScan.class, attribute = "basePackageClasses")
Class<?>[] scanBasePackageClasses() default {};
}
SpringBootApplication 注解
比较重要的几个注解:
@SpringBootConfiguration
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Configuration
public @interface SpringBootConfiguration {
}
@SpringBootConfiguration继承自@Configuration,二者功能也一致(可以穿插使用),标注当前类是配置类,
并会将当前类内声明的一个或多个以@Bean注解标记的方法的实例纳入到spring容器中,并且实例名就是方法名。
@EnableAutoConfiguration
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@AutoConfigurationPackage
@Import(AutoConfigurationImportSelector.class)
public @interface EnableAutoConfiguration {
String ENABLED_OVERRIDE_PROPERTY = "spring.boot.enableautoconfiguration";
/**
* Exclude specific auto-configuration classes such that they will never be applied.
* @return the classes to exclude
*/
Class<?>[] exclude() default {};
/**
* Exclude specific auto-configuration class names such that they will never be
* applied.
* @return the class names to exclude
* @since 1.3.0
*/
String[] excludeName() default {};
}
借助@Import的帮助,将所有符合自动配置条件的bean定义加载到IoC容器(建议放在根包路径下,这样可以扫描子包和类)。
可以看到@Import(AutoConfigurationImportSelector.class)
看下 AutoConfigurationImportSelector.Class
AutoConfigurationImportSelector
看下类继承关系:
AutoConfigurationImportSelector
最终实现了ImportSelector#selectImports
,代码如下
@Override
public String[] selectImports(AnnotationMetadata annotationMetadata) {
if (!isEnabled(annotationMetadata)) {
return NO_IMPORTS;
}
// 1.从META-INF/spring-autoconfigure-metadata.properties文件中载入配置属性
AutoConfigurationMetadata autoConfigurationMetadata = AutoConfigurationMetadataLoader
.loadMetadata(this.beanClassLoader);
//2.获取注解属性
AnnotationAttributes attributes = getAttributes(annotationMetadata);
// 3.获取配置信息
List<String> configurations = getCandidateConfigurations(annotationMetadata,
attributes);
// //4.移除重复的
configurations = removeDuplicates(configurations);
// 6.获取需要排除的
Set<String> exclusions = getExclusions(annotationMetadata, attributes);
// 7.校验排除类
checkExcludedClasses(configurations, exclusions);
// 8.删除所有需要排除的
configurations.removeAll(exclusions);
// 9.过滤器OnClassCondition(注解中配置的当存在某类才生效)
configurations = filter(configurations, autoConfigurationMetadata);
// 10.触发自动配置导入监听事件
fireAutoConfigurationImportEvents(configurations, exclusions);
return StringUtils.toStringArray(configurations);
}
@ComponentScan
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@Documented
@Repeatable(ComponentScans.class)
public @interface ComponentScan {
@AliasFor("basePackages")
String[] value() default {};
@AliasFor("value")
String[] basePackages() default {};
Class<?>[] basePackageClasses() default {};
Class<? extends BeanNameGenerator> nameGenerator() default BeanNameGenerator.class;
Class<? extends ScopeMetadataResolver> scopeResolver() default AnnotationScopeMetadataResolver.class;
ScopedProxyMode scopedProxy() default ScopedProxyMode.DEFAULT;
String resourcePattern() default ClassPathScanningCandidateComponentProvider.DEFAULT_RESOURCE_PATTERN;
boolean useDefaultFilters() default true;
Filter[] includeFilters() default {};
Filter[] excludeFilters() default {};
boolean lazyInit() default false;
@Retention(RetentionPolicy.RUNTIME)
@Target({})
@interface Filter {
FilterType type() default FilterType.ANNOTATION;
@AliasFor("classes")
Class<?>[] value() default {};
@AliasFor("value")
Class<?>[] classes() default {};
String[] pattern() default {};
}
}
spring的自动扫描并加载符合条件的组件,可定义扫描范围,加载到IOC容器
我们可以通过basePackages等属性来细粒度的定制@ComponentScan自动扫描的范围,如果不指定,则默认Spring框架实现会从声明@ComponentScan所在类的package进行扫描。
SpringApplication.run() 方法
public static ConfigurableApplicationContext run(Class<?> primarySource,
String... args) {
return run(new Class<?>[] { primarySource }, args);
}
public static ConfigurableApplicationContext run(Class<?>[] primarySources,
String[] args) {
// 创建SpringApplication对象,并调用run方法。
return new SpringApplication(primarySources).run(args);
}
new SpringApplication(primarySources).——创建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));
this.webApplicationType = deduceWebApplicationType();
setInitializers((Collection) getSpringFactoriesInstances(
ApplicationContextInitializer.class));
setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
this.mainApplicationClass = deduceMainApplicationClass();
}
private WebApplicationType deduceWebApplicationType() {
if (ClassUtils.isPresent(REACTIVE_WEB_ENVIRONMENT_CLASS, null)
&& !ClassUtils.isPresent(MVC_WEB_ENVIRONMENT_CLASS, null)) {
return WebApplicationType.REACTIVE;
}
for (String className : WEB_ENVIRONMENT_CLASSES) {
if (!ClassUtils.isPresent(className, null)) {
return WebApplicationType.NONE;
}
}
return WebApplicationType.SERVLET;
}
判断了webApplicationType是不是SERVLET,如果是,则创建Servlet的环境,否则创建基本环境。
spring boot是通过检查当前环境中是否存在
org.springframework.web.servlet.DispatcherServlet
类来判断当前是否是web环境的。
接着往下看,获得了ConfigurableEnvironment
环境以后,通过后面的代码对环境进行“微调”。
通过configureIgnoreBeanInfo(environment)
;如果System
中的spring.beaninfo.ignore
属性为空,就把当前环境中的属性覆盖上去:
run(String[] args) 核心方法。
public ConfigurableApplicationContext run(String... args) {
// 创建秒表
StopWatch stopWatch = new StopWatch();
// 开始计时
stopWatch.start();
ConfigurableApplicationContext context = null;
Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList<>();
configureHeadlessProperty();
// 1.获取监听器
SpringApplicationRunListeners listeners = getRunListeners(args);
// 2.启动
listeners.starting();
try {
// 3.获取ApplicationArguments对象
ApplicationArguments applicationArguments = new DefaultApplicationArguments(
args);
// 4.准备环境,并触发 ApplicationEnvironmentPreparedEvent 事件
ConfigurableEnvironment environment = prepareEnvironment(listeners,
applicationArguments);
// 4.1.如果System中的spring.beaninfo.ignore属性为空,就把当前环境中的属性覆盖上去:
configureIgnoreBeanInfo(environment);
Banner printedBanner = printBanner(environment);
// 5.创建应用上下文
context = createApplicationContext();
exceptionReporters = getSpringFactoriesInstances(
SpringBootExceptionReporter.class,
new Class[] { ConfigurableApplicationContext.class }, context);
// 5.1.准备上下文
prepareContext(context, environment, listeners, applicationArguments,
printedBanner);
// 5.2刷新上下文
refreshContext(context);
// 5.3刷新上下文后
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, exceptionReporters, listeners);
throw new IllegalStateException(ex);
}
try {
listeners.running(context);
}
catch (Throwable ex) {
handleRunFailure(context, ex, exceptionReporters, null);
throw new IllegalStateException(ex);
}
return context;
}
SpringApplicationRunListeners listeners = getRunListeners(args); —— 获取监听器
private SpringApplicationRunListeners getRunListeners(String[] args) {
Class<?>[] types = new Class<?>[] { SpringApplication.class, String[].class };
return new SpringApplicationRunListeners(logger, getSpringFactoriesInstances(
SpringApplicationRunListener.class, types, this, args));
}
SpringApplicationRunListeners(Log log,
Collection<? extends SpringApplicationRunListener> listeners) {
this.log = log;
this.listeners = new ArrayList<>(listeners);
}
private <T> Collection<T> getSpringFactoriesInstances(Class<T> type,
Class<?>[] parameterTypes, Object... args) {
ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
// 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;
}
// 加载工厂名
public static List<String> loadFactoryNames(Class<?> factoryClass, @Nullable ClassLoader classLoader) {
String factoryClassName = factoryClass.getName();
return loadSpringFactories(classLoader).getOrDefault(factoryClassName, Collections.emptyList());
}
// 加载工厂
private static Map<String, List<String>> loadSpringFactories(@Nullable ClassLoader classLoader) {
MultiValueMap<String, String> result = cache.get(classLoader);
if (result != null) {
return result;
}
try {
// 路径:FACTORIES_RESOURCE_LOCATION = META-INF/spring.factories
Enumeration<URL> urls = (classLoader != null ?
classLoader.getResources(FACTORIES_RESOURCE_LOCATION) :
ClassLoader.getSystemResources(FACTORIES_RESOURCE_LOCATION));
result = new LinkedMultiValueMap<>();
while (urls.hasMoreElements()) {
URL url = urls.nextElement();
UrlResource resource = new UrlResource(url);
// 加载属性
Properties properties = PropertiesLoaderUtils.loadProperties(resource);
for (Map.Entry<?, ?> entry : properties.entrySet()) {
List<String> factoryClassNames = Arrays.asList(
StringUtils.commaDelimitedListToStringArray((String) entry.getValue()));
result.addAll((String) entry.getKey(), factoryClassNames);
}
}
cache.put(classLoader, result);
return result;
}
catch (IOException ex) {
throw new IllegalArgumentException("Unable to load factories from location [" +
FACTORIES_RESOURCE_LOCATION + "]", ex);
}
}
public static Properties loadProperties(Resource resource) throws IOException {
Properties props = new Properties();
// 填充属性
fillProperties(props, resource);
return props;
}
// 从给定的资源中填充给定的属性(采用ISO-8859-1编码)
public static void fillProperties(Properties props, Resource resource) throws IOException {
InputStream is = resource.getInputStream();
try {
String filename = resource.getFilename();
if (filename != null && filename.endsWith(XML_FILE_EXTENSION)) {
props.loadFromXML(is);
}
else {
props.load(is);
}
}
finally {
is.close();
}
}
ConfigurableEnvironment environment = prepareEnvironment(listeners, applicationArguments); — 准备环境
private ConfigurableEnvironment prepareEnvironment(
SpringApplicationRunListeners listeners,
ApplicationArguments applicationArguments) {
// Create and configure the environment
// 得到环境对象ConfigurableEnvironment,没有则创建一个StandardServletEnvironment
//
ConfigurableEnvironment environment = getOrCreateEnvironment();
// 配置环境信息(激活环境,通过从系统环境变量里取)
configureEnvironment(environment, applicationArguments.getSourceArgs());
// 发布ApplicationEnvironmentPreparedEvent事件,加载配置文件,具体请看(ConfigFileApplicationListener)。
listeners.environmentPrepared(environment);
bindToSpringApplication(environment);
if (this.webApplicationType == WebApplicationType.NONE) {
environment = new EnvironmentConverter(getClassLoader())
.convertToStandardEnvironmentIfNecessary(environment);
}
ConfigurationPropertySources.attach(environment);
return environment;
}
protected void configureEnvironment(ConfigurableEnvironment environment,String[] args) {
configurePropertySources(environment, args);
// 配置ConfigurableEnvironment中的激活属性
configureProfiles(environment, args);
}
protected void configureProfiles(ConfigurableEnvironment environment, String[] args) {
environment.getActiveProfiles(); // ensure they are initialized
// additionalProfiles是项目启动时在main中SpringApplication.setAdditionalProfiles("")配置的
Set<String> profiles = new LinkedHashSet<>(this.additionalProfiles);
// 获取环境变量中设置的spring.profiles.active属性
profiles.addAll(Arrays.asList(environment.getActiveProfiles()));
// 赋值 activeProfiles
environment.setActiveProfiles(StringUtils.toStringArray(profiles));
}
createApplicationContext() —— 创建应用上下文
protected ConfigurableApplicationContext createApplicationContext() {
Class<?> contextClass = this.applicationContextClass;
if (contextClass == null) {
try {
switch (this.webApplicationType) {
case SERVLET:
contextClass = Class.forName(DEFAULT_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);
}
spring boot是根据不同的webApplicationType的类型,来创建不同的ApplicationContext的。
prepareContext —— 准备上下文
private void prepareContext(ConfigurableApplicationContext context,
ConfigurableEnvironment environment, SpringApplicationRunListeners listeners,
ApplicationArguments applicationArguments, Banner printedBanner) {
// 对ApplicationContext设置环境变量;
context.setEnvironment(environment);
// 配置属性ResourceLoader和ClassLoader属性;
postProcessApplicationContext(context);
// 循环初始化继承ApplicationContextInitializer接口的类
applyInitializers(context);
listeners.contextPrepared(context);
if (this.logStartupInfo) {
logStartupInfo(context.getParent() == null);
logStartupProfileInfo(context);
}
// Add boot specific singleton beans
context.getBeanFactory().registerSingleton("springApplicationArguments",
applicationArguments);
if (printedBanner != null) {
context.getBeanFactory().registerSingleton("springBootBanner", printedBanner);
}
// Load the sources
Set<Object> sources = getAllSources();
Assert.notEmpty(sources, "Sources must not be empty");
load(context, sources.toArray(new Object[0]));
listeners.contextLoaded(context);
}
@Override
public void setEnvironment(ConfigurableEnvironment environment) {
super.setEnvironment(environment);
this.reader.setEnvironment(environment);
this.scanner.setEnvironment(environment);
}
加粗样式
refreshContext —— 刷新上下文
private void refreshContext(ConfigurableApplicationContext context) {
refresh(context);
if (this.registerShutdownHook) {
try {
context.registerShutdownHook();
}
catch (AccessControlException ex) {
// Not allowed in some environments.
}
}
}
@Override
public void refresh() throws BeansException, IllegalStateException {
synchronized (this.startupShutdownMonitor) {
// 准备刷新的上下文环境
prepareRefresh();
// 初始化BeanFactory
ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
// 对BeanFactory进行各种功能填充
prepareBeanFactory(beanFactory);
try {
// 子类覆盖方法做额外的处理
postProcessBeanFactory(beanFactory);
// 激活各种BeanFactory处理器
invokeBeanFactoryPostProcessors(beanFactory);
// 注册拦截Bean创建的Bean处理,这里只是注册,真正调用是再拿去Bean的时候
registerBeanPostProcessors(beanFactory);
// 为上下文初始化Message源,即不同语言的消息体,国际化处理
initMessageSource();
// 初始化应用消息广播器,并放到applicationEventMulticaster bean中
initApplicationEventMulticaster();
// 留给子类来初始化其他bean
onRefresh();
// 在所有注册的bean中查找Listener bean,注册到消息广播中
registerListeners();
// 初始化剩下的单实例(非惰性)
finishBeanFactoryInitialization(beanFactory);
// 完成刷新过程,通知生命周期处理器lifecycleProcessor刷新过程,同时发出ContextRefreshEvent通知别人
finishRefresh();
}
catch (BeansException ex) {
if (logger.isWarnEnabled()) {
logger.warn("Exception encountered during context initialization - " +
"cancelling refresh attempt: " + ex);
}
// 销毁已经创建的单例Bean, 以避免资源占用.
destroyBeans();
// 取消refresh操作, 重置active标志.
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...
// 重置Spring的核心缓存
resetCommonCaches();
}
}
}
afterRefresh —— 刷新上下文后
protected void afterRefresh(ConfigurableApplicationContext context,
ApplicationArguments args) {
}
上面的refresh() 便是构建IoC的最重要的一步
总结
参考博文: https://blog.csdn.net/u010811939/article/details/80592461
概述:
- 构造SpringApplication的实例(时序图步骤1-2)
- 调用SpringApplication.run()方法(时序图步骤3)
- 构造SpringApplicationRunListeners 实例(时序图步骤3.1.1)
- 发布ApplicationStartedEvent事件(时序图步骤3.1.2)
- SpringApplicationRunListeners 实例准备环境信息(时序图步骤3.1.3)
- 创建ApplicationContext对象(时序图步骤3.1.4)
- ApplicationContext实例准备环境信息(时序图步骤3.1.5)
- 刷新的上下文(时序图步骤3.1.6)