理解的英文_Spring ioc

理解的英文_Spring ioc前言:这是在慕课网上学习SpringBoot2.0深度实践之核心技术篇时所做的笔记,主要供本人复习之用。目录第一章概要1.1基本使用1.1.1直接运行1.1.2自定义1.2用到的基础技术1.3用到的扩展衍生技术第二章SpringApplication的准备阶段2.1配置SpringBean的来源2.2推断Web应用类型2.3推断引…

大家好,又见面了,我是你们的朋友全栈君。如果您正在找激活码,请点击查看最新教程,关注关注公众号 “全栈程序员社区” 获取激活教程,可能之前旧版本教程已经失效.最新Idea2022.1教程亲测有效,一键激活。

Jetbrains全系列IDE使用 1年只要46元 售后保障 童叟无欺

前言:这是在慕课网上学习Spring Boot2.0深度实践之核心技术篇 时所做的笔记,主要供本人复习之用。

目录

第一章 概要

1.1 基本使用

1.1.1 直接运行

1.1.2 自定义

1.2 用到的基础技术

1.3 用到的扩展衍生技术

第二章 SpringApplication的准备阶段

2.1 配置SpringBean的来源

2.2 推断Web应用类型

2.3 推断引导类

2.4 加载应用上下文初始器(ApplicationContextInitializer)

2.4.1 自定义上下文初始器

2.5 加载应用事件监听器

 2.5.1 实现自定义事件监听器

第三章 SpringApplication运行阶段

3.1 加载运行监听器

3.1.1 加载监听器

3.1.2 运行监听器

3.1.3 自定义监听器

3.1.4 SpringBoot的监听机制

3.2 创建Spring应用上下文


第一章 概要

什么是SpringApplication?

官方文档中只是写SpringApplication是一个类,提供一些便利的功能,引导Spring的程序进行启动,在一个main的方法里面。在后续的章节里它写了SpringApplication的很多特性,包括错误分析报告等等。

@SpringBootApplication
public class RepApplication {
	public static void main(String[] args) {
        //要理解的SpringApplication
	SpringApplication.run(RepApplication.class, args);
	}

}

SpringApplication定义:Spring应用引导类,提供便利的自定义行为方法。

SpringApplication应用场景:嵌入式Web应用和非Web应用,嵌入式场景指的是tomcat,jetty中。

SpringApplication运行:运行:SpringApplication.run(String…)

基本使用:

1.1 基本使用

1.1.1 直接运行

通过静态方法进行执行,第二个参数是启动参数,java进程的启动参数可以通过外界进行传输.

理解的英文_Spring ioc

1.1.2 自定义

可以通过SpringApplication API调整:

SpringApplication springApplication = new SpringApplication(DiveInSpringBootApplication.class); 
springApplication.setBannerMode(Banner.Mode.CONSOLE); 
springApplication.setWebApplicationType(WebApplicationType.NONE); 
springApplication.setAdditionalProfiles("prod"); springApplication.setHeadless(true);

可以通过SpringApplicationBuilder调整:

new SpringApplicationBuilder(DiveInSpringBootApplication.class)    
.bannerMode(Banner.Mode.CONSOLE)    
.web(WebApplicationType.NONE)    
.profiles("prod")    
.headless(true)    
.run(args)

我们这里不只是要理解官方网页里提供的特性,还要理解SpringApplication的生命周期,作用域,包括源码的一些分析,并将其进行简单的穿插,其中包括外部化配置,事件这样的机制。

这里将主要分准备阶段与运行阶段来进行讲解。

1.2 用到的基础技术

Spring Framework:

Spring模式注解:@component @Service @Repositroy @Configuration ,模式注解允许我们使用派生的方式自定义,也可以使用SpringBoot中的注解,比如@SpringBootApplication。

Spring应用上下文:Spring应用上下文是Spring一个很核心的组件,用它来装配Bean的生命周期,在SpringBoot中有很多上下文,与一些特性绑定。

Spring工厂加载机制:配置@EnableAutoConfiguration来加载Spring.factories里的key,配置实现类放在其上。

Spring应用上下文初始化器:在SpringMVC中用到,对Spring上下文做了一些修改,也就是说在Spring上下文未启动之前,将其做一些相应的调整与变化。

Spring Environment抽象:Spring3.1中提出的抽象的接口,这个接口叫做Environment,这个接口统一了所有环境,包括里面的配置属性与profile。

Spring应用事件/监听器:扩展java Spring应用监听方式。

1.3 用到的扩展衍生技术

Spring Framework的衍生技术到了SpringBoot中来

SpringApplication:这是一个类,一般都是由它运行run方法来进行应用的启动。

SpringApplication Builder API:用build方式可以很方便的构建出相应的控制行为。

SpringApplication的运行监听器:与前面的事件监听有所区别,运行监听器允许我们扩展,默认情况下SpringBoot提供了一种运行监听器的实现。

SpringApplication参数:参数可以应用到程序中做一些配置。

SpringApplication故障分析:由于自动化装配或者starter搞完之后可能出现一些情况,比如端口被占,资源被锁定,可以提供一些报告,帮助人员进行排查分析。

Spring Boot应用事件/监听器:都是构建在Spring基础上的。

 

第二章 SpringApplication的准备阶段

new的初始化阶段即为准备阶段。

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

在准备阶段会

配置SpringBean的来源

推断web应用类型

加载应用上下文初始器

加载应用事件监听器

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 = WebApplicationType.deduceFromClasspath();
		setInitializers((Collection) getSpringFactoriesInstances(
				ApplicationContextInitializer.class));
		setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
		this.mainApplicationClass = deduceMainApplicationClass();
	}

2.1 配置SpringBean的来源

在Spring容器或应用启动时,里面的许多功能组件是以Bean方式来承载的,来源一般有两种一种是java配置,一种是xml文件.由BeanDefinitionLoader来读取。

BeanDefinitionLoader:

BeanDefinitionLoader(BeanDefinitionRegistry registry, Object... sources) {
		Assert.notNull(registry, "Registry must not be null");
		Assert.notEmpty(sources, "Sources must not be empty");
		this.sources = sources;
                //注解读取
		this.annotatedReader = new AnnotatedBeanDefinitionReader(registry);
                //xml读取
		this.xmlReader = new XmlBeanDefinitionReader(registry);
		if (isGroovyPresent()) {
			this.groovyReader = new GroovyBeanDefinitionReader(registry);
		}
		this.scanner = new ClassPathBeanDefinitionScanner(registry);
		this.scanner.addExcludeFilter(new ClassExcludeFilter(sources));
	}

为什么会做xml的配置源呢? 遗留系统有一些组件已经封装的非常好了,不需要再重复开发,可以把Spring应用配置文件放到源中进行配置。

下面就是传入的RepApplication.class就是一个java配置

@SpringBootApplication
//java配置
public class RepApplication {
	public static void main(String[] args) {
		SpringApplication.run(RepApplication.class, args);
	}
}

也可以自定义java配置,可以看出,传入的class,不一定是运行类的class,也可以是自己定义的class,只要有SpringBootApplication注解即可。

public class SpringApplicationBootstrap {

    public static void main(String[] args) {

        Set<String> sources = new HashSet();
        // 配置Class 名称
        sources.add(ApplicationConfiguration.class.getName());
        SpringApplication springApplication = new SpringApplication();
        springApplication.setSources(sources);
        springApplication.run(args);

    }

    @SpringBootApplication
    public static class ApplicationConfiguration {

    }

}

 

2.2 推断Web应用类型

根据当前应用ClassPath中是否存在相关的实现类来推断Web应用的类型,包括:

Web Reactive: WebApplicationType.REACTIVE

Web Servlet: WebApplicationType.SERVLET

非 Web: WebApplicationType.NONE

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;
	}

 

2.3 推断引导类

引导类RepApplication,通常我们的引导类是放在SpringApplication.run的方法参数中的,为什么还要去推导呢?

@SpringBootApplication
public class RepApplication {

	public static void main(String[] args) {
//run的参数RepApplication.class
		SpringApplication.run(RepApplication.class, args);
	}

}

这个在2.1中的代码已经可以说明了,我们传入class的不一定是我们的引导类(main存在的类)。

查看源码发现,其是根据堆栈来判断的,当我们代码执行时,堆栈中会存储我们的执行方法,与执行的类,当检测到某个方法为main的时候,其所属的类就是我们的引导类。

private Class<?> deduceMainApplicationClass() {
		try {
			StackTraceElement[] stackTrace = new RuntimeException().getStackTrace();
			for (StackTraceElement stackTraceElement : stackTrace) {
				if ("main".equals(stackTraceElement.getMethodName())) {
					return Class.forName(stackTraceElement.getClassName());
				}
			}
		}
		catch (ClassNotFoundException ex) {
			// Swallow and continue
		}
		return null;
	}

2.4 加载应用上下文初始器(ApplicationContextInitializer)

setInitializers((Collection) getSpringFactoriesInstances(
				ApplicationContextInitializer.class));

利用Spring工厂加载机制,实例化ApplicationContextInitializer,并排序对象集合。

public static <T> List<T> loadFactories(Class<T> factoryClass, @Nullable ClassLoader classLoader) {
		Assert.notNull(factoryClass, "'factoryClass' must not be null");
		ClassLoader classLoaderToUse = classLoader;
		if (classLoaderToUse == null) {
			classLoaderToUse = SpringFactoriesLoader.class.getClassLoader();
		}
		List<String> factoryNames = loadFactoryNames(factoryClass, classLoaderToUse);
		if (logger.isTraceEnabled()) {
			logger.trace("Loaded [" + factoryClass.getName() + "] names: " + factoryNames);
		}
		List<T> result = new ArrayList<>(factoryNames.size());
		for (String factoryName : factoryNames) {
			result.add(instantiateFactory(factoryName, factoryClass, classLoaderToUse));
		}
		AnnotationAwareOrderComparator.sort(result);
		return result;
	}

关于类加载的顺序:

可以实现Ordered接口来定义顺序,数字越小优先级越高,

也可以使用Order注解来定义顺序。

不定义也没关系,会有内置的顺序。

在autoConfigure的jar包中的spring.factories能找到其配置项

# Initializers
org.springframework.context.ApplicationContextInitializer=\
org.springframework.boot.autoconfigure.SharedMetadataReaderFactoryContextInitializer,\
org.springframework.boot.autoconfigure.logging.ConditionEvaluationReportLoggingListener

 

2.4.1 自定义上下文初始器

我们可以模仿其配置,自定义一个实现自己的上下文初始化器。

在resource中新建META-INF,在其中新建spring.factories文件,书写一下配置。

org.springframework.context.ApplicationContextInitializer=\
com.imooc.diveinspringboot.context.HelloWorldApplicationContextInitializer

新的上下文类的书写可以参org.springframework.context.ApplicationContextInitializer接口的实现类。

下面的类也是仿照其实现类书写的。

@Order(Ordered.HIGHEST_PRECEDENCE)
public class HelloWorldApplicationContextInitializer<C extends ConfigurableApplicationContext>
        implements ApplicationContextInitializer<C> {
    @Override
    public void initialize(C applicationContext) {
        System.out.println("ConfigurableApplicationContext.id = "+ applicationContext.getId());
    }
}

 

2.5 加载应用事件监听器

setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));

事件监听器的加载与上面上下文的加载方式差不多,在autoConfigure的jar包中的spring.factories能找到其配置项

# Application Listeners
org.springframework.context.ApplicationListener=\
org.springframework.boot.autoconfigure.BackgroundPreinitializer

打开org.springframework.boot.autoconfigure.BackgroundPreinitializer,发现其监听了另外一个SpringBoot事件SpringApplicationEvent

@Order(LoggingApplicationListener.DEFAULT_ORDER + 1)
public class BackgroundPreinitializer
		implements ApplicationListener<SpringApplicationEvent> {
    ...
}

Spring的事件与一个ApplicationEvent相关,它继承了一个标记接口EventObject,它是所有事件的源,在Application中除了继承过来的源(比如鼠标点击)之外还有一个当时发生的时间,

public abstract class ApplicationEvent extends EventObject {

	/** use serialVersionUID from Spring 1.2 for interoperability. */
	private static final long serialVersionUID = 7099057708183571937L;

	/** System time when the event happened. */
	private final long timestamp;
}

Spring中有ApplicationContextEvent,这个事件是关于Spring上下文的事件。

public abstract class ApplicationContextEvent extends ApplicationEvent {

	public ApplicationContextEvent(ApplicationContext source) {
		super(source);
	}
	public final ApplicationContext getApplicationContext() {
		return (ApplicationContext) getSource();
	}

}

ApplicationContextEvent有一个实现类,ContextRefreshEvent,当上下文刷新或者启动完成的时候,

public class ContextRefreshedEvent extends ApplicationContextEvent {

	public ContextRefreshedEvent(ApplicationContext source) {
		super(source);
	}
}

 2.5.1 实现自定义事件监听器

监听ContextRefreshEvent

@Order(Ordered.HIGHEST_PRECEDENCE)
public class HelloWorldApplicationListener implements ApplicationListener<ContextRefreshedEvent> {
    @Override
    public void onApplicationEvent(ContextRefreshedEvent event) {
        System.out.println("HelloWorld : " + event.getApplicationContext().getId()
                + " , timestamp : " + event.getTimestamp());
    }
}

在resource中新建META-INF,在其中新建spring.factories文件,书写一下配置。

org.springframework.context.ApplicationListener=\
com.imooc.diveinspringboot.listener.HelloWorldApplicationListener

配置完成,启动应用,即可监听上下文刷新事件。

 

第三章 SpringApplication运行阶段

加载:SpringApplication运行监听器

运行:SpringApplication运行监听器

监听:SpringBoot事件、Spring事件

创建:应用上下文、Enviroment、其它(不重要),应用上下文创建后会被应用上下文初始化器初始化,Enviroment是抽象的环境对象。

失败:故障分析报告。

回调:CommandLineRunner、ApplicationRunner

整隔运行代码如下:

public ConfigurableApplicationContext run(String... args) {
		StopWatch stopWatch = new StopWatch();
		stopWatch.start();
		ConfigurableApplicationContext context = null;
		Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList<>();
		configureHeadlessProperty();
		SpringApplicationRunListeners listeners = getRunListeners(args);
		listeners.starting();
		try {
			ApplicationArguments applicationArguments = new DefaultApplicationArguments(
					args);
			ConfigurableEnvironment environment = prepareEnvironment(listeners,
					applicationArguments);
			configureIgnoreBeanInfo(environment);
			Banner printedBanner = printBanner(environment);
			context = createApplicationContext();
			exceptionReporters = getSpringFactoriesInstances(
					SpringBootExceptionReporter.class,
					new Class[] { ConfigurableApplicationContext.class }, context);
			prepareContext(context, environment, listeners, applicationArguments,
					printedBanner);
			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, exceptionReporters, listeners);
			throw new IllegalStateException(ex);
		}

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

 

3.1 加载运行监听器

3.1.1 加载监听器

加载SpringApplication运行监听器(SpringApplicationRunListeners)

利用Spring工厂加载机制,读取SpringApplicationRunListener对象集合,并且封装到组合类SpringApplicationRunListeners。

SpringApplicationRunListeners listeners = getRunListeners(args);
listeners.starting();

 

private SpringApplicationRunListeners getRunListeners(String[] args) {
		Class<?>[] types = new Class<?>[] { SpringApplication.class, String[].class };
		return new SpringApplicationRunListeners(logger, getSpringFactoriesInstances(
				SpringApplicationRunListener.class, types, this, args));
	}

SpringApplicationRunListeners称为组合对象,当我们在实现某个接口的时候,可以通过foreach的方式迭代执行,是一种很常见的设计模式。

class 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);
	}

	public void starting() {
		for (SpringApplicationRunListener listener : this.listeners) {
			listener.starting();
		}
	}
    
    ...
}

3.1.2 运行监听器

监听器加载后在对应的阶段方法会执行对应的方法。

理解的英文_Spring ioc

同时因为EventPublishingRunListener实现了SpringApplicationRunListener,而EventPublishingRunListener会发出与阶段对应的事件,所以在对应的阶段也会有对应的事件出现。

比如在Starting阶段。

public class EventPublishingRunListener implements SpringApplicationRunListener, Ordered {

    //...
    public void starting() {
		this.initialMulticaster.multicastEvent(
				new ApplicationStartingEvent(this.application, this.args));
	}

    //...
}

3.1.3 自定义监听器

在resource中新建META-INF,在其中新建spring.factories文件,书写一下配置。

org.springframework.boot.SpringApplicationRunListener=\
com.imooc.diveinspringboot.run.HelloWorldRunListener

然后实现SpringApplicationRunListener接口即可自定义监听器,但是要注意这里必须要有如下的构造方法。

public class HelloWorldRunListener implements SpringApplicationRunListener {

    public HelloWorldRunListener(SpringApplication application, String[] args) {

    }

    @Override
    public void starting() {
        System.out.println("HelloWorldRunListener.starting()...");
    }

    //省略其余实现
}

我们会发现在打印banner的时候,会先打印HelloWorldRunListener.starting()…。

3.1.4 SpringBoot的监听机制

声明:这个是看了源码后的个人理解,不保证一定正确,只提供一定的参考.

在准备阶段加载实现了ApplicationListener的监听器,

理解的英文_Spring ioc

在运行阶段会加载所有实现了SpringApplicationRunListener的类,然后根据阶段调用所有SpringApplicationRunListener类的方法,比如在Spring应用刚启动时调用所有实现类的starting的方法,可以看到下面就是主动调用了starting方法,在Spring应用正在运行时调用所有实现类的running方法.

理解的英文_Spring ioc

具体阶段会调用的方法可以见下图

理解的英文_Spring ioc

 

其中有一个实现了SpringApplicationRunListener的类是EventPublishingRunListener,在这个类的构造函数中,将所有实现了ApplicationListener类的监听器添加到了SimpleApplicationEventMulticaster中.

理解的英文_Spring ioc

当我们把所有监听器都添加到SimpleApplicationEventMulticaster后,我们就可以用它来发布事件,当有事件发布时,会检测事件类型与SimpleApplicationEventMulticaster中的实现了ApplicationListener的监听器的泛型是否符合,若符合则调用.

总之就是所有监听器在一个类里,当有事件发布时,此类会将所有监听器检查一遍,符合就调用.

理解的英文_Spring ioc

同时EventPublishingRunListener也在对应的阶段发布了对应的事件,所以我们在对应的阶段会有对应的事件.

理解的英文_Spring ioc

 

3.2 创建Spring应用上下文

根据准备阶段推断的Web应用类型创建对应的ConfigurableApplicationContext实例.根据类型有三种不同而实例.

由SpringApplication的run方法->createApplicationContext方法

protected ConfigurableApplicationContext createApplicationContext() {
		Class<?> contextClass = this.applicationContextClass;
		if (contextClass == null) {
			try {
				switch (this.webApplicationType) {
				case SERVLET:
					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);
	}

总的来说:

理解的英文_Spring ioc

根据准备阶段推断的Web应用类型创建对应的ConfigurableEnviroment实例.根据类型有三种不同而实例.

由SpringApplication的run方法->prepareEnvironment方法->getOrCreateEnvironment方法

private ConfigurableEnvironment getOrCreateEnvironment() {
		if (this.environment != null) {
			return this.environment;
		}
		switch (this.webApplicationType) {
		case SERVLET:
			return new StandardServletEnvironment();
		case REACTIVE:
			return new StandardReactiveWebEnvironment();
		default:
			return new StandardEnvironment();
		}
	}

总的来说:

理解的英文_Spring ioc

 

 

 

 

 

 

 

 

 

 

 

 

 

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。

发布者:全栈程序员-用户IM,转载请注明出处:https://javaforall.cn/195466.html原文链接:https://javaforall.cn

【正版授权,激活自己账号】: Jetbrains全家桶Ide使用,1年售后保障,每天仅需1毛

【官方授权 正版激活】: 官方授权 正版激活 支持Jetbrains家族下所有IDE 使用个人JB账号...

(0)


相关推荐

  • 回溯法解01背包问题_01背包问题回溯法伪代码

    回溯法解01背包问题_01背包问题回溯法伪代码一、问题n皇后问题的解空间树是一颗排列树,而01背包问题的解空间树应该是一颗子集树。再简述下该问题:有n件物品和一个容量为c的背包。第i件物品的价值是v[i],重量是w[i]。求解将哪些物品装入背包可使价值总和最大。所谓01背包,表示每一个物品看成一个整体,要么全部装入,要么都不装入。这里n=5,c=10,w={2,2,6,5,4},v={6,3,5,4,6}。01背…

  • APK签名机制原理详解

    APK签名机制原理详解众所周知,Android系统在安装Apk的过程中,会对Apk进行签名校验,校验通过后才能安装成功。那你知道签名校验的机制是什么?具体校验的是什么内容吗?申请第三方SDK(如微信支付)时填入的SAH1值是什么?目前众多的快速批量打包方案又是如何绕过签名检验的?我将通过一系列的文章来解开这些疑惑。

  • Assert.assertEquals()方法参数详解

    Assert.assertEquals()方法参数详解junit.framework包下的Assert提供了多个断言方法.主用于比较测试传递进去的两个参数.Assert.assertEquals();及其重载方法:1.如果两者一致,程序继续往下运行.2.如果两者不一致,中断测试方法,抛出异常信息AssertionFailedError.查看源码,以Assert.assertEquals(intexpected,inta…

  • c++语言入门教程–16c++ 中的 String 类

    c++语言入门教程–16c++ 中的 String 类

  • springcloud seata_seata github

    springcloud seata_seata github111

  • 数据结构和算法_数据库原理考试题库

    数据结构和算法_数据库原理考试题库前言2016年又是一个全新的开始,每到一年的这个时候,总是颇有感慨。想对过去的一年做一些总结,但又觉得经历和精力总是不够。俗话说,一年之计在于春,当然,新的一年,也总是计划着N多事情,想做什么事情

发表回复

您的电子邮箱地址不会被公开。

关注全栈程序员社区公众号