01-SpringBoot入门

一、SpringBoot概述

1.1、什么是SpringBoot

​ 随着动态语言的流行(Ruby、Groovy、Scala、Node.js),Java的开发显得格外的笨重,繁多的配置、低下的开发效率、复杂的部署流程以及第三方技术集成难度大。

​ 在上述环境下,Spring Boot 应运而生。它使用“习惯优于配置”(项目中存在大量的配置,此外还内置一个习惯性的配置,让你无须手动进行配置)的理念让你的项目快速运行起来。

​ 使用 Spring Boot 很容易创建一个独立运行(运行 jar,内嵌 Servlet容器)、准生产级别的基于 Spring 框架的项目,使用 Spring Boot 你可以不用或者只需要很少的 Spring 配置。

1.2、SpringBoot功能

(1)、独立运行的 Spring 项目
Spring Boot 可以以 jar 包的形式独立运行,运行一个 Spring Boot 项目只需通过 java–jar xx.jar 来运行。

(2)、内嵌 Servlet 容器
Spring Boot 可选择内嵌 Tomcat、Jetty 或者 Undertow,这样我们无须以 war 包形式部署项目。

(3)、提供 starter 简化 Maven配置
Spring 提供了一系列的 starter pom 来简化 Maven 的依赖加载,例如,当你使用了spring-boot-starter-web 时,会自动加入如图 1 所示的依赖包。

(4)、自动配置 Spring
Spring Boot 会根据在类路径中的 jar 包、类,为 jar 包里的类自动配置 Bean,这样会极大地减少我们要使用的配置。当然,Spring Boot 只是考虑了大多数的开发场景,并不是所有的场景,若在实际开发中我们需要自动配置 Bean,而 Spring Boot 没有提供支持,则可以自定义自动配置。

(5)、准生产的应用监控
Spring Boot 提供基于 http、ssh、telnet 对运行时的项目进行监控。

(6)、无代码生成和 xml 配置
Spring Boot 的神奇的不是借助于代码生成来实现的,而是通过条件注解来实现的,这是 Spring 4.x 提供的新特性。Spring 4.x 提倡使用 Java 配置和注解配置组合,而 Spring Boot 不需要任何 xml 配置即可实现 Spring 的所有配置。

1.3、Spring Boot的优缺点

(1)、优点

  • 快速构建项目。
  • 对主流开发框架的无配置集成。
  • 项目可独立运行,无须外部依赖Servlet容器。
  • 提供运行时的应用监控。
  • 极大地提高了开发、部署效率。
  • 与云计算的天然集成。

(2)、缺点

  • 版本迭代速度很快,一些模块改动很大。
  • 由于不用自己做配置,报错时很难定位。
  • 网上现成的解决方案比较少。

二、@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 {
}

​ @SpringBootApplication 是一个“三体”结构,实际上它是一个复合 Annotation。虽然它的定义使用了多个 Annotation 进行元信息标注,但实际上对于 SpringBoot 应用来说,重要的只有三个 Annotation,而“三体”结构实际上指的就是这三个 Annotation:

  • @Configuration(SpringBootConfiguration)
  • @EnableAutoConfiguration
  • @ComponentScan

2.1、@Configuration

​ @Configuration就是JavaConfig 形式的 Spring IoC 容器的配置类使用的那个@Configuration,既然 SpringBoot 应用骨子里就是一个 Spring 应用,那么,自然也需要加载某个 IoC 容器的配置,而 SpringBoot 社区推荐使用基于 JavaConfig 的配置形式,所以,很明显,这里的启动类标注了 @Configuration 之后,本身其实也是一个 IoC 容器的配置类!

​ 将SpringBoot 启动类拆分为两个独立的 Java 类,整个形势就明朗了:

@Configuration
@EnableAutoConfiguration
@ComponentScan
public class DemoConfiguration {
    @Bean
    public Controller controller() {
        return new Controller();
    }
}
public class DemoApplication {
    public static void main(String[] args) {
        SpringApplication.run(DemoConfiguration.class, args);
    }
}

2.2、@EnableAutoConfiguration

​ Spring 框架提供的各种名字为 @Enable 开头的 Annotation 定义。比如 @EnableScheduling、@EnableCaching、@EnableMBeanExport 等,@EnableAutoConfiguration 的理念和“做事方式”其实一脉相承,借助 @Import 的支持,收集和注册特定场景相关的 bean 定义:

  • @EnableScheduling 是通过 @Import 将 Spring 调度框架相关的 bean 定义都加载到 IoC 容器。
  • @EnableMBeanExport 是通过 @Import 将 JMX 相关的 bean 定义加载到 IoC 容器。

​ @EnableAutoConfiguration 作为一个复合 Annotation,也是借助 @Import 的帮助,将所有符合自动配置条件的 bean 定义加载到 IoC 容器,其自身定义关键信息如下:

@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@AutoConfigurationPackage
@Import({AutoConfigurationImportSelector.class})
public @interface EnableAutoConfiguration {}

​ 借助 EnableAutoConfigurationImportSelector,@EnableAutoConfiguration 可以帮助 SpringBoot 应用将所有符合条件的 @Configuration 配置都加载到当前 SpringBoot 创建并使用的 IoC 容器。

2.2.1、SpringFactoriesLoader

​ SpringFactoriesLoader 属于 Spring 框架私有的一种扩展方案(类似于 Java 的 SPI 方案 java.util.ServiceLoader),其主要功能就是从指定的配置文件 META-INF/spring.factories 加载配置,spring.factories 是一个典型的 java properties 文件,配置的格式为 Key=Value 形式,只不过 Key 和 Value 都是 Java 类型的完整类名(Fully qualified name)。

​ 对于 @EnableAutoConfiguration 来说,SpringFactoriesLoader 的用途稍微不同一些,其本意是为了提供 SPI 扩展的场景,而在 @EnableAutoConfiguration 的场景中,它更多是提供了一种配置查找的功能支持,即根据 @EnableAutoConfiguration 的完整类名 org.springframework.boot.autoconfigure.EnableAutoConfiguration 作为查找的 Key,获取对应的一组 @Configuration 类:

# Auto Configure
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
org.springframework.boot.autoconfigure.admin.SpringApplicationAdminJmxAutoConfiguration,\
org.springframework.boot.autoconfigure.aop.AopAutoConfiguration,\
org.springframework.boot.autoconfigure.amqp.RabbitAutoConfiguration,\
org.springframework.boot.autoconfigure.batch.BatchAutoConfiguration,\
org.springframework.boot.autoconfigure.cache.CacheAutoConfiguration,\
org.springframework.boot.autoconfigure.cassandra.CassandraAutoConfiguration,\
org.springframework.boot.autoconfigure.context.ConfigurationPropertiesAutoConfiguration,\
org.springframework.boot.autoconfigure.context.LifecycleAutoConfiguration,\
org.springframework.boot.autoconfigure.context.MessageSourceAutoConfiguration,\
org.springframework.boot.autoconfigure.context.PropertyPlaceholderAutoConfiguration,\
org.springframework.boot.autoconfigure.couchbase.CouchbaseAutoConfiguration,\
\

​ @EnableAutoConfiguration 自动配置就是从 classpath 中搜寻所有 META-INF/spring.factories 配置文件,并将其中 org.spring-framework.boot.autoconfigure.EnableAutoConfiguration 对应的配置项通过反射(Java Reflection)实例化为对应的标注了 @Configuration 的 JavaConfig 形式的 IoC 容器配置类,然后汇总为一个并加载到 IoC 容器。

三、SpringBoot启动配置原理

3.1、启动流程

(1)、main方法启动程序

public static void main(String[] args) {
        pringApplication.run(DemoApplication.class, args);
}

(2)、创建Application对象然后运行

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

创建对象

public SpringApplication(Class<?>... primarySources) {this(null, primarySources);}

public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) {
	// 1、资源初始化资源加载器为 null	
    this.resourceLoader = resourceLoader;
    // 2、断言主要加载资源类不能为 null,否则报错
	Assert.notNull(primarySources, "PrimarySources must not be null");
    // 3、初始化主要加载资源类集合并去重
	this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources));
    // 4、推断当前 WEB 应用类型
	this.webApplicationType = WebApplicationType.deduceFromClasspath();
    // 5、设置初始化器(类路径下META-INF/spring.factories配置的所有ApplicationContextInitializer)
	setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class));
    // 6、设置监听器(类路径下ETA-INF/spring.factories配置的所有ApplicationListener)
	setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
    // 7、推断主入口应用类(有main方法)
	this.mainApplicationClass = deduceMainApplicationClass();
}

运行run方法

public ConfigurableApplicationContext run(String... args) {
	StopWatch stopWatch = new StopWatch();
	stopWatch.start();
	ConfigurableApplicationContext context = null;
	Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList<>();
	configureHeadlessProperty();
    //类路径下META-INF/spring.factories 获取SpringApplicationRunListeners
	SpringApplicationRunListeners listeners = getRunListeners(args);
     //回调所有的获取SpringApplicationRunListener.starting()方法
	listeners.starting();
	try {
        //封装命令行参数
		ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
		//准备环境:完成后回调SpringApplicationRunListener.environmentPrepared()
        ConfigurableEnvironment environment = prepareEnvironment(listeners, applicationArguments);
		configureIgnoreBeanInfo(environment);
		Banner printedBanner = printBanner(environment);
        //创建ApplicationContext(web的ioc还是普通的ioc)
		context = createApplicationContext();
		exceptionReporters = getSpringFactoriesInstances(SpringBootExceptionReporter.class,
				new Class[] { ConfigurableApplicationContext.class }, context);
        //准备上下文环境;将environment保存到ioc中;而且applyInitializers();
        //applyInitializers():回调之前保存的所有的ApplicationContextInitializer的initialize方法
        //回调所有的SpringApplicationRunListener的contextPrepared();
        //prepareContext运行完成以后回调所有的SpringApplicationRunListener的contextLoaded();
		prepareContext(context, environment, listeners, applicationArguments, printedBanner);
		//刷新容器;ioc容器初始化(如果是web应用还会创建嵌入式的Tomcat);Spring注解版
         //扫描,创建,加载所有组件的地方;(配置类,组件,自动配置)
         refreshContext(context);
         //从ioc容器中获取所有的ApplicationRunner和CommandLineRunner进行回调
         //ApplicationRunner先回调,CommandLineRunner再回调
		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);
	}
    //整个SpringBoot应用启动完成以后返回启动的ioc容器;
	return context;
}

3.2、事件监听机制

​ ApplicationContextInitializer和SpringApplicationRunListener需要配置在配置在META-INF/spring.factories

org.springframework.context.ApplicationContextInitializer=\
com.atguigu.springboot.listener.HelloApplicationContextInitializer

org.springframework.boot.SpringApplicationRunListener=\
com.atguigu.springboot.listener.HelloSpringApplicationRunListener

​ ApplicationRunner和CommandLineRunner只需添加到IOC容器

(1)、ApplicationContextInitializer

public class HelloApplicationContextInitializer implements ApplicationContextInitializer<ConfigurableApplicationContext> {
    @Override
    public void initialize(ConfigurableApplicationContext applicationContext) {
        System.out.println("ApplicationContextInitializer...initialize..."+applicationContext);
    }
}

(2)、SpringApplicationRunListener

public class HelloSpringApplicationRunListener implements SpringApplicationRunListener {
    //必须有的构造器
    public HelloSpringApplicationRunListener(SpringApplication application, String[] args){

    }
	//...
}

(3)、ApplicationRunner

@Component
public class HelloApplicationRunner implements ApplicationRunner {
    @Override
    public void run(ApplicationArguments args) throws Exception {
        System.out.println("ApplicationRunner...run....");
    }
}

(4)、CommandLineRunner

@Component
public class HelloCommandLineRunner implements CommandLineRunner {
    @Override
    public void run(String... args) throws Exception {
        System.out.println("CommandLineRunner...run..."+ Arrays.asList(args));
    }
}

四、自定义starter

posted @ 2020-06-16 17:27  ciyelc  阅读(91)  评论(0)    收藏  举报