一,springboot的啟動原理
@SpringBootApplication
public class Application {
public static void main(String[] args) {
//從主程式的入口進入
SpringApplication.run(Application.class,args);
}
}
public static ConfigurableApplicationContext run(Class<?>[] primarySources, String[] args) {
//這里的流程實際上分為了兩步:
//第一步:創建SpringApplication
//第二步:執行run()
return new SpringApplication(primarySources).run(args);
}
1.創建SpringApplication
進入new 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));
//判斷當前是否是一個web應用
this.webApplicationType = WebApplicationType.deduceFromClasspath();
//從類路徑下找到META-INF/spring.factory配置的所有ApplicationContextInitializer,然后保存起來 TODO
setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class));
//從類路徑下找到META-INF/spring.factory配置的所有ApplicationListener,然后保存起來
setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
//從多個配置類找到main方法的主配置類
this.mainApplicationClass = deduceMainApplicationClass();
}
再來看getSpringFactoriesInstances()
private <T> Collection<T> getSpringFactoriesInstances(Class<T> type, Class<?>[] parameterTypes, Object... args) {
ClassLoader classLoader = getClassLoader();
//SpringFactoriesLoader.loadFactoryNames(type, classLoader)
//往工廠里面加載bean
Set<String> names = new LinkedHashSet<>(SpringFactoriesLoader.loadFactoryNames(type, classLoader));
List<T> instances = createSpringFactoriesInstances(type, parameterTypes, classLoader, args, names);
AnnotationAwareOrderComparator.sort(instances);
return instances;
}
接著看loadFactoryNames()
public static List<String> loadFactoryNames(Class<?> factoryType, @Nullable ClassLoader classLoader) {
String factoryTypeName = factoryType.getName();
//呼叫了loadSpringFactories
return loadSpringFactories(classLoader).getOrDefault(factoryTypeName, 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下加載組態檔
//FACTORIES_RESOURCE_LOCATION代表的哪里?
//public static final String 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()) {
String factoryTypeName = ((String) entry.getKey()).trim();
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);
}
}
再回到SpringApplication()看setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
private <T> Collection<T> getSpringFactoriesInstances(Class<T> type) {
//實際呼叫的是getSpringFactoriesInstances
return getSpringFactoriesInstances(type, new Class<?>[] {});
}
private <T> Collection<T> getSpringFactoriesInstances(Class<T> type, Class<?>[] parameterTypes, Object... args) {
ClassLoader classLoader = getClassLoader();
//呼叫了 loadFactoryNames
Set<String> names = new LinkedHashSet<>(SpringFactoriesLoader.loadFactoryNames(type, classLoader));
List<T> instances = createSpringFactoriesInstances(type, parameterTypes, classLoader, args, names);
AnnotationAwareOrderComparator.sort(instances);
return instances;
}
這里就和上一步其實是一樣的,從META-INF/spring.factories加載bean,
2.執行run()
public static ConfigurableApplicationContext run(Class<?>[] primarySources, String[] args) {
return new SpringApplication(primarySources).run(args);
}
點擊進入run()
public ConfigurableApplicationContext run(String... args) {
//監聽springboot應用的創建
StopWatch stopWatch = new StopWatch();
stopWatch.start();
ConfigurableApplicationContext context = null;
Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList<>();
configureHeadlessProperty();
//獲取SpringApplicationRunListeners;從類路徑下META‐INF/spring.factories
SpringApplicationRunListeners listeners = getRunListeners(args);
//回呼所有的獲取SpringApplicationRunListener.starting()方法
listeners.starting();
try {
//封裝命令列引數
ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
//準備環境
ConfigurableEnvironment environment = prepareEnvironment(listeners, applicationArguments);
configureIgnoreBeanInfo(environment);
//創建環境完成后回呼SpringApplicationRunListener.environmentPrepared();表示環境準備完成
Banner printedBanner = printBanner(environment);
//創建ApplicationContext;決定創建web的ioc還是普通的ioc 利用反射創建
context = createApplicationContext();
//做例外列印報告的,沒啥用
exceptionReporters = getSpringFactoriesInstances(SpringBootExceptionReporter.class,
new Class[] { ConfigurableApplicationContext.class }, context);
//準備背景關系環境;將environment保存到ioc中;而且applyInitializers();
prepareContext(context, environment, listeners, applicationArguments, printedBanner);
//容器重繪,ioc容器初始化(如果是web應用還會創建嵌入式的Tomcat)
refreshContext(context);
//從ioc容器中獲取所有的ApplicationRunner和CommandLineRunner進行回呼
//ApplicationRunner先回呼,CommandLineRunner再回呼
afterRefresh(context, applicationArguments);
//停止監聽
stopWatch.stop();
if (this.logStartupInfo) {
new StartupInfoLogger(this.mainApplicationClass).logStarted(getApplicationLog(), stopWatch);
}
//所有的SpringApplicationRunListener回呼finished方法
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;
}
prepareContext()
private void prepareContext(ConfigurableApplicationContext context, ConfigurableEnvironment environment,
SpringApplicationRunListeners listeners, ApplicationArguments applicationArguments, Banner printedBanner) {
//給背景關系設定環境
context.setEnvironment(environment);
//執行背景關系的后置處理器,給容器中加載一些組件
postProcessApplicationContext(context);
//回呼之前保存的所有的ApplicationContextInitializer的initialize方法
applyInitializers(context);
//回呼所有的SpringApplicationRunListener的contextPrepared();
listeners.contextPrepared(context);
if (this.logStartupInfo) {
logStartupInfo(context.getParent() == null);
logStartupProfileInfo(context);
}
// Add boot specific singleton beans
ConfigurableListableBeanFactory beanFactory = context.getBeanFactory();
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]));
listeners.contextLoaded(context);
}
refresh()
public void refresh() throws BeansException, IllegalStateException {
synchronized (this.startupShutdownMonitor) {
// 清空快取
prepareRefresh();
// cas的方式初始化bean工廠
ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
// 創建bean工廠,加載xxxAware 注冊單實體bean
prepareBeanFactory(beanFactory);
try {
// 加載bean工廠的后置處理器
postProcessBeanFactory(beanFactory);
// 執行bean工廠的后置處理器
invokeBeanFactoryPostProcessors(beanFactory);
// 將bean的后置處理器注冊到容器中
registerBeanPostProcessors(beanFactory);
// 初始化容器資訊
initMessageSource();
// 初始化事件派發器
initApplicationEventMulticaster();
// 加載其他的單實體bean,并創建web容器
onRefresh();
// 將監聽器注冊到容器
registerListeners();
// 完成bean工廠的初始化,看看有沒有自定義的類加載器和xxxAware介面,都加載到容器中
finishBeanFactoryInitialization(beanFactory);
//清理快取,為此背景關系初始化生命周期處理器,將容器重繪派發到生命周期處理器,事件派發器發布事件,啟動web容器,并發布事件
finishRefresh();
}
catch (BeansException ex) {
if (logger.isWarnEnabled()) {
logger.warn("Exception encountered during context initialization - " +
"cancelling refresh attempt: " + ex);
}
// Destroy already created singletons to avoid dangling resources.
destroyBeans();
// Reset 'active' flag.
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...
resetCommonCaches();
}
}
}
二,自定義starter
1.原理分析
1、這個場景需要使用到的依賴是什么?
2、如何撰寫自動配置
@Configuration //指定這個類是一個配置類
@ConditionalOnXXX //在指定條件成立的情況下自動配置類生效
@AutoConfigureAfter //指定自動配置類的順序
@Bean //給容器中添加組件
@ConfigurationPropertie結合相關xxxProperties類來系結相關的配置
@EnableConfigurationProperties //讓xxxProperties生效加入到容器中
自動配置類要能加載
將需要啟動就加載的自動配置類,配置在META‐INF/spring.factories
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
org.springframework.boot.autoconfigure.admin.SpringApplicationAdminJmxAutoConfiguration,\
org.springframework.boot.autoconfigure.aop.AopAutoConfiguration,\
3、模式:
啟動器只用來做依賴匯入;
專門來寫一個自動配置模塊;
啟動器依賴自動配置;別人只需要引入啟動器(starter)
mybatis-spring-boot-starter;自定義啟動器名-spring-boot-starter
2.代碼
1.首先創建兩個maven工程
yhd-spring-boot-starter
yhd-spring-boot-starter-autoconfigurer
2.引入依賴
在yhd-spring-boot-starter-autoconfigurer里面引入
<!-- 引入springboot核心啟動器 -->
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
<version>2.2.6.RELEASE</version>
</dependency>
</dependencies>
在yhd-spring-boot-starter里面引入
<!-- 引入自動配置模塊 -->
<dependencies>
<dependency>
<groupId>com.yhd</groupId>
<artifactId>yhd-spring-boot-starter-autoconfigurer</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
</dependencies>
3.撰寫yhd-spring-boot-starter-autoconfigurer
1.創建一個service
/**
* @author yhd
* @createtime 2020/10/6 1:28
*/
public class HelloService {
@Autowired
private HelloServiceProperties helloServiceProperties;
public String sayHello(String name){
return helloServiceProperties.getPrefix()+" "+name+" " +helloServiceProperties.getSuffix();
}
}
2.創建一個HelloServiceProperties
/**
* @author yhd
* @createtime 2020/10/6 1:30
* Not registered via @EnableConfigurationProperties, marked as Spring component, or scanned via @ConfigurationPropertiesScan
*/
@Component
@ConfigurationProperties(prefix = "yhd.hello")
public class HelloServiceProperties {
private String prefix;
private String suffix;
public String getPrefix() {
return prefix;
}
public void setPrefix(String prefix) {
this.prefix = prefix;
}
public String getSuffix() {
return suffix;
}
public void setSuffix(String suffix) {
this.suffix = suffix;
}
}
3.創建一個HelloServiceAutoConfiguration
/**
* @author yhd
* @createtime 2020/10/6 1:35
*/
@SpringBootConfiguration
@ConditionalOnWebApplication
@EnableConfigurationProperties(HelloServiceProperties.class)
public class HelloServiceAutoConfiguration {
@Autowired
private HelloServiceProperties helloServiceProperties;
@Bean
public HelloService helloService(){
return new HelloService();
}
}
4.在resources目錄下創建META-INF目錄
5.在META-INF下創建spring.factories
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
com.yhd.config.HelloServiceAutoConfiguration
4.將這兩個專案安裝到倉庫
5.別的專案引入yhd-spring-boot-starter依賴
/**
* @author yhd
* @createtime 2020/10/6 1:45
*/
@RestController
public class HelloController {
@Autowired
private HelloService helloService;
@GetMapping("/hello")
public String hello(){
return helloService.sayHello("尹會東");
}
}
組態檔:
yhd.hello.prefix=Hello
yhd.hello.suffix=!
訪問localhost:8080,輸出:Hello 尹會東 !,
至此,starter創建成功,
轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/163732.html
標籤:python
上一篇:Java日常學習之集合
下一篇:Java例外真的看這一篇就夠了
