jones's technical blog

  • 首页
  • 文章归档
  • 默认分类
  • 关于页面

  • 搜索
博客系统 linux 酸酸乳

号称开箱即用的 Spring Boot, 到底替我们做了些什么

发表于 2020-05-31 | 分类于 Spring Boot | 0 | 阅读次数 315

一. Spring Boot, Spring MVC, Spring 之间的区别

Spring 是一个轻量级的控制反转(IOC) 和面向切面(AOP)的容器框架. Spring 使你能够编写更干净, 更可管理, 并且更易于测试的代码.

Spring MVC 是 Spring 的一个模块, 一个 web 框架. 通过 Dispatcher Servlet, ModelAndView 和 View Resolver, 开发 web 应用变得很容易. 主要针对的是网站应用程序或者服务开发 — URL 路由, Session, 模板引擎, 静态 web 资源等等.

Spring 配置 复杂, 繁琐, 所以推出了 Spring Boot, 约定大于配置, 简化了 Spring 的配置流程.

Spring VS SpringMVC

Spring 是一个一站式的轻量级的 java 开发框架, 核心是控制反转 (IOC) 和面向切面, 针对于开发的WEB层 (SpringMVC, 业务层 (IOC), 持久层(Repository) 等都提供了多种配置解决方案.

SpringMVC是Spring基础之上的一个MVC框架,主要处理web开发的路径映射和视图渲染,属于Spring框架中WEB层开发的一部分.

1.2 SpringMVC VS SpringBoot

SpringMVC属于一个企业 WEB 开发的 MVC 框架 , 涵盖面包括前端视图, 文件配置, 后台接口逻辑的开发等, XML, config 等配置相对比较繁琐复杂.

SpringBoot 框架相对于 SpringMVC 框架来说, 只专注于做开发 Restful API, 不开发前端视图, 配置也简单很多, 约定大于配置.

  • Spring 是核心, 提供了基础功能.
  • SpringMVC 是基于 Spring 的一个 MVC 框架
  • SpringBoot 是为简化 Spring 配置的快速开发的工具

二. Spring Boot Starter 是什么

在我们工作中, 经常会看到各种各样的 Spring Boot Starter, 比如 spring-boot-starter-jdbc, mybatis-spring-boot-starter, mybatis-plus-boot-starter 等等. 这些 starter 究竟有什么作用呢?

在了解这些 starter 之前, 我们要知道 SpringMVC 和 Spring Boot 的关系, 他们的关系上文已经说过了, 在这里就不再絮叨了.

正如上文所述, Spring Boot 约定大于配置, 使用注解和配置文件替代了 xml 的繁琐配置, 甚至我们不再需要 外部容器, Spring Boot 内嵌了 Tomcat 容器, 我们只需要执行SpringApplication.run(Application.class, args);,然后一切就能正常工作.

这些都少不了 Spring Boot 自动装配的功劳. 而关于自动装配, Spring Boot 官方文档是这么说的:

Spring Boot auto-configuration attempts to automatically configure your Spring application based on the jar dependencies that you have added.

Spring Boot 框架会根据当前运行时的 jar 依赖来自动生成并装配相应的 Bean, 文档上举了个例子: 假如 HSQLDB 在你的 classpath下, 并且你没有手动配置任何的数据库连接 Bean, 那么 Spring Boot 会自动装配一个内存的数据库. 另外, 自动装配还支持根据自定义属性和自定义自动装配条件, 功能很强大.

其实 starter 就是一个写好自动装配逻辑的 jar 依赖, 我们只要引用这个 starter, 那么当程序运行时的依赖库, 配置属性或者其他条件满足 starter 中定义的逻辑是, starter 会自动装配我们的运行环境.

举例说明: 当我们引用 spring-cloud-alibaba-nacos-discovery 这个 starter 时, 并且增加了几个服务注册发现的配置, 那么我们的应用就具备了微服务的能力. 当然, 除此之外, 我们还需要一个注册中心. 这个 starter 并不包含注册中心, 他只是帮助你初始化当前的微服务注册, 发现, 调用, 客户端负载均衡环境, 让你只需要一个 @Autowired 的注解就能使用微服务的能力.

2.1 自动装配原理

它是通过 @Configuration 和 @Conditional注解来工作的. @Configuration 注解是 Spring 中用来声明 Bean 的;而@Conditional代表了一类条件注解,表示当且仅当满足了一定条件之后,使用@Configuration的类才真正创建并装配Bean.

那么我们使用 starter 时只是引用一个 jar 包, Spring Boot 框架是如何知道 jar 包中的自动装配逻辑的呢? 答案是 META-INF/spring.factories文件. Spring Boot 会检查你的 jar 包中是否存在 META-INF/spring.factories 文件,如果存在则会进一步读取其中的EnableAutoConfiguration 配置项,该配置项包含了jar包中的自动装配类,例如 druid 的 jar:

org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
com.alibaba.druid.spring.boot.autoconfigure.DruidDataSourceAutoConfigure

如果我们的自动装配存在上下文的依赖, 我们还可以使用 @AutoConfigureAfter 和 @AutoConfigureBefore 来声明装配顺序.

三. 自定义 Spring Boot Starter

Spring Boot 官方文档推荐一个 starter 应当包含如下几个部分:

  • autoconfigure模块:实现自动装配的逻辑.
  • starter模块:集成autoconfigure模块、依赖库和其他依赖,用户只需要引用这个starter那么就可以使用.

自定义 Spring Boot Starter 就是按照 Spring Boot 的规范来实现上面的两个模块, 如果我们分开这两个模块做成 jar 包比较麻烦, 也可以将它们合成一个大的模块, 也就是最终的 starter 包.

在starer包命名上,Spring Boot官方包一般会以 spring-boot 为前缀,而其他自定义的包则建议不能以 spring-boot 开头,即便使用不同的groupId. 假如我们需要创建一个 ”amqp” 库的 starter,那么 autoconfigure 模块可以命名为 amqp-spring-boot-autoconfigure,而 starter 模块可以命名为 amqp-spring-boot-starter;而如果你选择将这两个模块合成一个模块,那么就采取 amqp-spring-boot-starter 就行.

另外,如果starter使用了一些配置项,那么这些配置项应当放在特定的命名空间(即前缀)下,不能放在 Spring Boot 配置项所在的命名空间下,如 server、management、spring 等等.

四. Spring Boot 的启动流程

用过 Spring Boot 的都知道使用 Spring Boot 和使用 SSM 框架的视觉上明显的差别就是 Spring Boot 有独立的启动类.

@SpringBootApplication
public class Application {
    public static void main(String[] args) {
        SpringApplication.run(Application.class, args);
    }
}

从上面代码可以看出,Annotation定义(@SpringBootApplication)和类定义(SpringApplication.run)最为明显,所以要揭开SpringBoot的神秘面纱,我们要从这两位开始就可以了.

4.1 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 {
}

图片来自网上

在其中比较重要的有三个注解,分别是:

  • @SpringBootConfiguration // 继承了Configuration,表示当前是注解类
  • @EnableAutoConfiguration // 开启springboot的注解功能,springboot的四大神器之一,其借助@import的帮助
  • @ComponentScan(excludeFilters = {} // 扫描路径设置

接下来对这三个注解一一讲解, 增加对 SpringBootApplication 的理解

4.1.1@Configuration

按照原来xml配置文件的形式,在springboot中我们大多用配置类来解决配置问题

  • xml 配置文件的形式来配置 bean
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd"
default-lazy-init="true">
<!--bean定义-->
</beans>
  • java configuration的配置形式配置bean
@Configuration
public class MockConfiguration{
    //bean定义
}

注入bean方式的不同:

  • xml配置文件的形式注入bean

    <bean id="mockService" class="..MockServiceImpl">
    ...
    </bean>
    
  • java configuration的配置形式注入bean

    @Configuration
    public class MockConfiguration{
        @Bean
        public MockService mockService(){
            return new MockServiceImpl();
        }
    }
    

    任何一个标注了@Bean的方法,其返回值将作为一个bean定义注册到Spring的IoC容器,方法名将默认成该bean定义的id.

表达bean之间依赖关系的不同:

  • xml配置文件的形式表达依赖关系

    <bean id="mockService" class="..MockServiceImpl">
      <propery name ="dependencyService" ref="dependencyService" />
    </bean>
    <bean id="dependencyService" class="DependencyServiceImpl"></bean>
    
  • java configuration配置的形式表达依赖关系(重点)

    如果一个bean A的定义依赖其他bean B,则直接调用对应的JavaConfig类中依赖bean B的创建方法就可以了.

    @Configuration
    public class MockConfiguration{
      @Bean
      public MockService mockService(){
          return new MockServiceImpl(dependencyService());
      }
      @Bean
      public DependencyService dependencyService(){
          return new DependencyServiceImpl();
      }
    }
    

    4.1.2 @ComponentScan

  • 对应xml配置中的元素.

  • ComponentScan的功能其实就是自动扫描并加载符合条件的组件(比如 @Component 和 @Repository 等)或者bean定义.

  • 将这些 bean 定义加载到 IoC 容器中.

    我们可以通过basePackages等属性来细粒度的定制@ComponentScan自动扫描的范围,如果不指定,则 默认 Spring 框架实现会从声明 @ComponentScan所在类的package进行扫描.

    4.1.3 @EnableAutoConfiguration

    此注解顾名思义是可以自动配置,所以应该是springboot中最为重要的注解.

    在spring框架中就提供了各种以@Enable开头的注解,例如: @EnableScheduling, @EnableCaching, @EnableMBeanExport等; @EnableAutoConfiguration的理念和做事方式其实一脉相承简单概括一下就是,借助@Import的支持,收集和注册特定场景相关的bean定义.

  • @EnableScheduling是通过@Import将Spring调度框架相关的bean定义都加载到 IoC 容器[定时任务、时间调度任务]

    @EnableAutoConfiguration也是借助@Import的帮助,将所有符合自动配置条件的bean定义加载到IoC容器.

    @EnableAutoConfiguration作为一个复合Annotation,其自身定义关键信息如下:

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

    上面的代码中最重要的注解已经标注: @AutoConfigurationPackage , @Import(AutoConfigurationImportSelector.class)

    @AutoConfigurationPackage:

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

    通过@Import(AutoConfigurationPackages.Registrar.class)

    /**
    	 * {@link ImportBeanDefinitionRegistrar} to store the base package from the importing
    	 * configuration.
    	 */
    	static class Registrar implements ImportBeanDefinitionRegistrar, DeterminableImports {
    
    		@Override
    		public void registerBeanDefinitions(AnnotationMetadata metadata,
    				BeanDefinitionRegistry registry) {
    			register(registry, new PackageImport(metadata).getPackageName());
    		}
    
    		@Override
    		public Set<Object> determineImports(AnnotationMetadata metadata) {
    			return Collections.singleton(new PackageImport(metadata));
    		}
    
    	}
    

    这是 Registrar 类, 他注册了一个 bean 的定义: new PackageImport(metadata).getPackageName() 返回了当前主程序类的同级以及子级的包组件, 那就是注册当前主程序类的同级以及子级的包中的符合条件的 bean 的定义

以上图为例, CodeApplication 和 config 包同级, 但是 demo 包是 CodeApplication 的父级, 也就是说,CodeApplication 启动加载的 Bean 中,并不会加载Demo 类,这也就是为什么,我们要把 CodeApplication 放在项目的最高级中.

4.2 探索SpringApplication执行流程

jones wechat
更多精彩内容请关注微信公众号
  • 本文作者: jones
  • 本文链接: https://www.lushuaiyu.com/archives/号称开箱即用的springboot到底替我们做了些什么
  • 版权声明: 本博客所有文章除特别声明外,均采用CC BY-NC-SA 3.0 许可协议。转载请注明出处!
# 博客系统 # linux # 酸酸乳
创建型模式之工厂模式
@Transactional 失效的几种场景及原理分析
  • 文章目录
  • 站点概览
jones

jones

程序猿

46 日志
16 分类
3 标签
Github E-mail
Creative Commons
0%
© 2021 jones
主题 - NexT.Pisces