SpringBoot其实一点都不难

Posted uting

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了SpringBoot其实一点都不难相关的知识,希望对你有一定的参考价值。

扫码关注公众号,领取更多资源
技术图片

@

Spring Boot

技术图片

        Spring Boot 是由 Pivotal 团队提供用来简化 Spring 的搭建和开发过程的全新框架。随着近些年来微服务技术的流行,Spring Boot 也成了时下炙手可热的热点技术。
        Spring Boot 去除了大量的 xml 配置文件,简化了复杂的依赖管理,配合各种 starter 使用,基本上可以做到自动化配置。Spring 可以做的事情,现在用 Spring boot都可以做。
        过去使用Spring创建一个可运行的项目需要进行大量的配置工作,很是繁琐,SpringBoot使用习惯优于配置的理念,内置一个默认配置,无需手动,可以让项目快速运行起来。

Spring Boot 核心功能

  • 独立运行的Spring项目
            Spring Boot可以以jar包的形式独立运行,运行一个Spring Boot项目只需要通过java -jar xx.jar即可。
  • 内嵌Servlet容器
            Spring Boot可以选择内嵌TomcatJetty或者Undertow,这样就可以无需使用war包形式部署项目。
  • 提供starter,简化Maven配置
            Spring Boot提供一系列的starter pom来简化Maven依赖添加。
  • 自动配置Spring
            Spring Boot可以根据类路径中的jar包,类,自动配置Bean,减少了配置工作量。
  • 准生产的应用监控
            Spring Boot提供httpsshtelnet对项目进行实时监控。
  • 无代码生成和xml配置
            Spring Boot通过注解实现自动配置,可以无需任何xml就能完成所有配置。

Spring Boot优缺点

优点

  • 快速构件项目
  • 无配置快速继承主流框架
  • 可独立运行
  • 提供运行时监控
  • 提高开发部署效率
  • 与云计算天然集成

缺点

  • 迭代速度快,某些模块改动较大
  • 因为不用手动配置,报错难以定位

创建一个简单的Spring Boot项目

        打开http://start.spring.io/ ,选择Java语言开发,填写项目信息,然后下载代码。如下图:

技术图片

        解压后可以得到一个Spring Boot的启动类,代码如下:

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class DemoApplication {
    public static void main(String[] args) {
        SpringApplication.run(DemoApplication.class, args);
    }
}

        直接选择当前启动类选择运行,一个简单的Spring Boot项目就启动成功了。

@SpringBootApplication注解

        @SpringBootApplication是一个复合的注解,他是如下几个注解组合而成:

  • @Configuration
  • @EnableAutoConfiguration
  • @ComponentScan

        在启动类上加上如上三个注解可以达到与@SpringBootApplication一样的效果,显然后者更加方便些。

@Configuration

        这里的就是Spring IOC中使用的那个配置。既然Spring Boot本质上就是一个Spring应用,那么自然也需要加载某个IOC容器配置,所以加上@Configuration,本质上也是IOC容器的配置类。
        所以上面的启动类也可以拆分为如下两个类:

//配置类
@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);
    }
}

        在如上两个类中,DemoApplication其实就是一个普通的启动类,而DemoConfiguration就是一个普通的配置类而已。

@EnableAutoConfiguration注解

        @EnableAutoConfiguration也是一个复合的注解,主要包含如下两个:

  • @AutoConfigurationPackage
  • @Import(EnableAutoConfigurationImportSelector.class)

        其中最关键的是@Import(EnableAutoConfigurationImportSelector.class),他借助EnableAutoConfigurationImportSelector这个类,将当前应用所有符合条件的@Configuration配置都加载到当前Spring Boot应用的IOC容器中去。

技术图片

SpringApplication.run()执行流程

        SpringApplication在没有特殊要求的情况下,默认使用模板化的情动流程,但是SpringApplication也在合适的流程节点开放了不用类型的扩展,我们可以对这些扩展点对SpringBoot的启动和关闭流程进行修改扩展。

一个好玩的扩展

        在之前的启动类的main方法中只有这么简单的依据话:

SpringApplication.run(DemoApplication.class,args);

        正常情况下,一个SpringBoot项目启动后会在打印台或者日志中打印一个字符画。

技术图片

        但是我们可以在SpringApplication中来修改打印的内容,对启动流程进行扩展:

public class DemoApplication {
    public static void main(String[] args) {        //SpringApplication.run(DemoConfiguration.class, args);
        SpringApplication bootstrap = new SpringApplication(Demo - Configuration.class);
        bootstrap.setBanner(new Banner() {
            @Override
            public void printBanner(Environment environment, Class<?> aClass, PrintStream printStream) {
                // 比如打印一个我们喜欢的ASCII Arts字符画
            }
        });
        bootstrap.setBannerMode(Banner.Mode.CONSOLE);
        // 其他定制设置...
        bootstrap.run(args);
    }
}

        修改banner还有一个简单的方式,就是把需要打印的字符放到一个资源文件中,然后通过ResourceBanner加载:

bootstrap.setBanner(new ResourceBanner(new ClassPathResource("banner.txt")));

        正常情况下我们不需要对这个设置进行定制,因为真正的启动逻辑才是我们需要关注的,这只是为了好玩。

SpringApplication完整执行流程

        SpringApplication中的run()方法主要的流程大体可以概括为如下几个步骤:

  • 如果我们直接使用SpringApplication中的静态run(),那么,在这个方法中会自动创建一个SpringApplication的实例对象,然后调用这个实例对象的run()

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-fsJsNZkq-1604679639272)(Boot.assets/屏幕快照 2020-11-03 13.22.30.png)]

在实例化SpringApplication的时候,会提前做好如下几件事情

  • 根据classpath中是否存在ConfigurableWebApplicationContext来决定创建的ApplicationContext类型
  • SpringFactoriesLoaderclasspath中查找并加载所有可用ApplicationContextInitializer
  • SpringFactoriesLoaderclasspath中查找并加载所有可用ApplicationListener
  • 推断并设置main方法的定义
  • SpringApplication初始化并完成设置后就开始执行run方法的逻辑了,在执行之前,需要通过SpringFactoriesLoader加载所有的SpringApplicationRunListener,调用他们的started(),通知这些类开始执行当前应用。
  • 创建并设置当前SpringBoot应用需要使用的Environment,包括配种的PropertySourceProfile
  • 调用所有SpringApplicationRunListenerenvironmentPrepared()方法
  • 根据用户设置,创建对应类型的ApplicationContext,并将之前准备好的Environment设置给创建号的ApplicationContext使用。
  • 借助SpringFactoriesLoader,加载之前的ApplicationContextInitializer,然后调用initialize(applocationContext)方法对Application进一步处理。
  • 调用所有SpringApplicationRunListenercontextPrepared()方法
  • 将通过@EnableAutoConfiguration获取的所有配置以及其他形式的IOC容器配置加载到已经准备完毕的ApplicationContext中。
  • 遍历调用所有SpringApplicationRunListenercontextLoaded()方法,告诉所有的SpringApplicationRunListenerApplicationContext准备完毕。
  • 调用ApplicationContextrefresh()方法。
  • 执行ApplicationContext中注册的CommandLineRunner
  • 遍历执行SpringApplicationRunListenerfinish()方法。

        至此,一个完整的SpringBoot应用启动完毕。启动的过程很多都是一些通知事件的扩展点,如果忽略这些过程,可以将启动的逻辑精简到如下几步:

技术图片

        对比可以发现,SpringApplication提供的扩展点占了启动逻辑的很大一部分,除了初始化并准备好ApplicationContext之外,剩下的大部分工作都是通过扩展点完成的,下面对这些扩展点进行详细的讲解。

SpringApplicationRunListener

        SpringApplicationRunListener是一个在SpringBootmain方法执行过程中接收事件通知的监听者,主要方法如下:

public interface SpringApplicationRunListener {
    void started();
    void environmentPrepared(ConfigurableEnvironment environment);
    void contextPrepared(ConfigurableApplicationContext context);
    void contextLoaded(ConfigurableApplicationContext context);
    void finished(ConfigurableApplicationContext context, Throwable exception);
}

        正常情况下,我们并不需要自己实现一个SpringApplicationRunListenerSpringBoot也只是实现了一个EventPublishingRunListener,用于处理SpringBoot启动时不同的ApplicationEvent
        假如我们需要自定义一个SpringApplicationRunListener,在实现类DemoSpringApplicationRunListener的构造方法中需要两个参数SpringApplicationargs,然后可以通过SpringFactoriesLoader的规定,在classpath下的META-INF/spring.factories 中添加如下配置:

org.springframework.boot.SpringApplicationRunListener=com.keevol.springboot.demo.DemoSpringApplicationRunListener

        随后SpringApplication就会在运行的时候调用他了。

ApplicationListener

        SpringApplicationjava中监听者模式的一种实现方式。如果需要添加自定的SpringListener,可以有如下两种方式:

  • 通过SpringApplication.addListeners(...)或者SpringApplication.setListeners(...)添加一个或多个自定义的listener
  • 借助SpringFactoriesLoader,在META-INF/spring.factories中添加如下配置:
org.springframework.context.ApplicationListener=
org.springframework.boot.builder.ParentContextCloserApplicationListener,
org.springframework.boot.cloudfoundry.VcapApplicationListener,
org.springframework.boot.context.FileEncodingApplicationListener

ApplicationContextInitializer

       ApplicationContextInitializer的目的就是在ConfigurableApplicationContextApplicationContext执行refresh()之前,对ConfigurableApplicationContext的实例做一些设置或处理。
       实现一个自定义的ApplicationContextInitializer有如下两种方法:

  • SpringApplication.addInitializers(...)
  • 通过添加SpringFactoriesLoader配置
org.springframework.context.ApplicationContextInitializer=
org.springframework.boot.context.ConfigurationWarningsApplication-ContextInitializer,
org.springframework.boot.context.ContextIdApplicationContextInitia-lizer,

CommandLineRunner

       CommandLineRunner的源码很简单,如下:

public interface CommandLineRunner{
	void run(String... args) throws Exception;
}

       需要注意的两点:

  • 所有的CommandLineRunner需要在ApplicationContext完全初始化之后执行。
  • 只要存在于ApplicationContext中的CommandLineRunner都会被执行,无需手动添加。

       以上几个扩展点都建议使用@org.springframework.core.annotation.Order进行注解或者实现org.springframework.core.Ordered 接口,便于对他们的执行顺序进行调整,避免阻塞。

Spring Boot自动配置

       之前在将@EnableAutoConfiguration的时候说了,这个配置可以借助SpringFactoriesLoader的帮助,将注解了@Configuration的Java类汇总并加载到ApplicationContext中去。
       实际上,@EnableAutoConfiguration还能够对自动配置进行正价细化的配置和控制。

基于条件的自动配置

       在Spring框架中,可以使用@Conditional这个注解配合@Configuration或者@Bean来设置一个配置或者实例是否生效,生成一个类似于if-else条件的生成逻辑。

关注公众号回复“Conditional”获取注解相关详解

       如果要基于条件配置,只需要在@Conditional中指定自己的Condition实现类就好了,如下:

@Conditional({DemoConditional1.class,DemoConditional2.class...})

       @Conditional除了可以注解在类和方法上之外,还可以注解在其他Annotation实现类上,组成一个符合的注解,SpringBoot中已经实现了一批,如下:

  • @ConditionalOnBean:当容器里有指定的 Bean 的条件下。
  • @ConditionalOnClass:当类路径下有指定的类的条件下。
  • @ConditionalOnExpression:基于 SpEL 表达式作为判断条件。
  • @ConditionalOnJava:基于 JVM 版本作为判断条件。
  • @ConditionalOnJndi:在 JNDI 存在的条件下查找指定的位置。
  • @ConditionalOnMissingBean:当容器里没有指定 Bean 的情况下。
  • @ConditionalOnMissingClass:当类路径下没有指定的类的条件下。
  • @ConditionalOnNotWebApplication:当前项目不是 Web 项目的条件下。
  • @ConditionalOnProperty:指定的属性是否有指定的值。
  • @ConditionalOnResource:类路径是否有指定的值。
  • @ConditionalOnSingleCandidate:当指定 Bean 在容器中只有一个,或者虽然有多个但是指定首选的 Bean。
  • @ConditionalOnWebApplication:当前项目是 Web 项目的条件下。

调整自动配置的顺序

       除了可以设置基于条件的配置,我们还可以对当前配置或者组件的加载顺序进行调整,以便于在创建加载过程中解决依赖分析和组装的问题。
       @AutoConfigureBefore或者@AutoConfigureAfter可以配置当前组件或者配置的加载在其他的之前或者之后进行。比如需要某个配置的加载在另外一个之后,可以进行如下配置:

@Configuration
@AutoConfigureAfter(JmxAutoConfiguration.class)
public class AfterMBeanServerReadyConfiguration {
    @AutoWired
    MBeanServer mBeanServer;
    //通过 @Bean 添加必要的 bean 定义
}

Spring-Boot-Starter常用依赖

       之前说过,SpringBoot对开发者的影响主要有以下两点:

  • 基于Spring的配置理念,约定优先于配置
  • 提供了各种自动配置依赖的模块,使开发更加高效和快速

       SpringBoot提供了很多以Spring-Boot-Starter开头的依赖模块,这些模块有着默认的配置,正常情况下开箱即用,下面对常用的几个模块做一下介绍:

应用日志spring-boot-starter-logging

       Java中有很多日志工具,比如log4j,log4j2,sel4j等等,在SpringBoot中,我们可以直接引用现成的依赖包,即可达到开箱即用的效果。比如添加如下Maven依赖:

<dependency>
    <groupId> org.springframework.boot </groupId>
    <artifactId> spring-boot-starter-logging </artifactId>
</dependency>

       那么SpringBoot就会自动使用logback作为应用的日志框架,在应用启动的时候,初始化LoggingApplicationListener并使用。
       如果我们要对日志系统做响应的设置,可以通过如下几种方式进行配置:

  • 在classpath下使用自己定制的logback.xml配置文件
  • 在系统中任意位置添加logback.xml配置文件,然后通过application.yml指定文件位置。
logging:
  config: xml_path

       初次之外,还有其他的日志框架,可自行选择,比如: spring-boot-starter-log4j和 spring-boot-starter-log4j2等等,在实际项目中只需要引用一个即可。

web应用开发spring-boot-starter-web

       通常情况下,我们开发的项目都是一个web项目,需要使用相应的容器来部署。在SpringBoot的maven依赖中添加下面的配置,就可以得到一个可直接运行的web应用。

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
</dependency>

       在引入这个依赖之后,启动当前项目,会自动嵌入一个默认的tomcat容器,当前服务就运行在这个容器之中。
       spring-boot-starter-web默认将当前项目打包成jar包,在运行的时候可以直接使用java -jar xxx.jar来运行,然后访问响应的请求地址就可以得到响应的response。

数据访问spring-boot-starter-jdbc

       如果项目中需要使用到数据库,那么添加如下依赖:

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-jdbc</artifactId>
</dependency>

       正常情况下我们在一个服务中只需要使用一个数据库,那么这种情况下,只需要在application.yml中添加如下配置即可:

spring:
  datasource:
    url: jdbc:mysql://host:port/databaseName
    username: username
    password: password

       某些特殊情况下,如果一个服务中需要使用到多个DataSource,那么只需要配置如下实例就好:

@Bean
public DataSource dataSource1() throws Throwable {
    DruidDataSource dataSource = new DruidDataSource();
    dataSource.setUrl(...);
    dataSource.setUsername(...);
    dataSource.setPassword(...);
    // TODO other settings if necessary in the future.
    return dataSource;
}
@Bean
public DataSource dataSource2() throws Throwable {
    DruidDataSource dataSource = new DruidDataSource();
    dataSource.setUrl(...);
    dataSource.setUsername(...);
    dataSource.setPassword(...);
    // TODO other settings if necessary in the future.
    return dataSource;
}

       但是在添加完上面的代码之后,直接启动会报错,我们需要在启动类上排除SpringBoot在启动的时候对默认的DataSource的加载。

@SpringBootApplication(exclude = { DataSourceAutoConfiguration.class,
DataSourceTransactionManagerAutoConfiguration.class })

       或者在上面配置多个的DataSource上加上@Primary,可同时达到默认的DataSource和定制化的多个DataSource同时加载的两全其美的效果。
       除此之外,还有很多适配不同数据库的依赖,spring-boot-starter-data-jpa、spring-boot-starter-data-mongodb 等,在实际项目中可以根据不同的数据库自行选择。

其他的依赖

       SpringBoot还有很多其他的maven依赖,比如:

  • spring-boot-starter-aop
  • spring-boot-starter-security
  • spring-boot-starter-actuator
    ......
           具体的包可以去Maven官网查找,可以根据当前业务查找到我们需要使用的包以及选择对应的版本。

SpringBoot微服务的注册与发现

       SpringBoot微服务部署到响应的环境之后需要对外提供服务,通常情况下,微服务很少单兵作战,而是同时部署多个节点集群部署。
       在这种情况下,如果访问者发出一个请求,这个请求具体访问哪一个节点,如何才能找到对应的节点,这个过程就是服务的发现,而一个服务需要作为一个主题对外提供服务,这个构件主体的过程就成为服务的注册过程。
       服务的注册和发现在结构上可以简单的展现为下图的三角关系:
技术图片

       在这整个三角关系中,提供服务的注册者Service Registry是整个关系的核心,他负责登记和保存哪些服务是可以对外提供服务的。当服务的访问者Service Accessor发出请求访问某个服务节点的时候,需要先访问服务注册者,获取可以访问的服务节点信息,这样服务访问者就可以根据返回的信息访问响应的服务。
       SpringBoot的微服务注册与发现是有SpringBoot微服务提供服务方式来决定的,如果是基于Dubbo框架的微服务发现方式 ,则很可能是基于Redis或者Zookeeper的注册和发现机制。

       上面这种方式,服务的访问者需要了解服务注册机制,获取到服务节点之后再去访问对应的微服务。但是对于用户而言,服务的访问者就是使用者,不应该过多的去了解服务端有多少节点,而且需要多次访问才能获取响应的请求结果。
       一个更好的方案是在服务访问者和注册者之后添加一个服务访问代理,访问者直接对代理发出请求,获取可用节点,对可用节点再次发出请求的工作都交给服务代理去做。优化之后的结构如下:
技术图片

       经过这样的调整之后,服务的访问者只需要与服务代理打交道,具体服务代理如何发现服务并访问的,对于访问者来说都只是一个黑盒,并不需要做过多的关心。


























以上是关于SpringBoot其实一点都不难的主要内容,如果未能解决你的问题,请参考以下文章

秒懂!JVM虚拟机图文详解!一点都不难!

剪视频一点都不难,多款超实用剪辑软件全方位评测!

做减法才是真本事,别以为你很能学,做加法一点都不难

C语言必须写main函数?最简单的 Hello world 你其实一点都不懂!

ssl证书验证失败打不开网页

SpringBoot的一些简单的知识点(一点也不难捏) springboot系列第一期