zoukankan      html  css  js  c++  java
  • 《深入实践Spring Boot》阅读笔记之三:核心技术源代码分析

    刚关注的朋友,可以回顾前两篇文章:

    上篇文章总结了《深入实践Spring Boot》的第二部分,本篇文章总结第三部分,也是最后一部分。这部分主要讲解核心技术的源代码分析,因为篇幅和能力原因,分析的不会太详细,后续深入研究后再专门写文章。希望大家能从「阅读笔记」3篇文章中,对Spring Boot提供的功能有所了解,在项目中进行实践,不断从繁琐重复的开发中解放出来。

    我也是最近刚开始了解Spring Boot,计划今年在项目中实践,到时会总结实践过程中的一些问题和经验,分享给大家。想一起学习、实践、交流的朋友,可以扫描文章下方的二维码,关注我的个人公众号,感谢大家。

    本篇主要从以下几个方面总结:

    • Spring Boot自动配置实现原理;
    • Spring Boot数据访问实现原理;
    • 微服务核心技术实现原理;

    题外话

    春节假期很快过去了,明天就要上班了,相信大家还是意犹未尽,没吃够、没玩够、没和家人待够。今年因为个人原因,没有回家过年,心理最牵挂的还是爷爷奶奶,他们都80多了,希望她们身体健康,开开心心地度过生命最后的旅程。

    不管怎样,大家要切换频道了,回归到正常的工作中,好好努力,一起期待明年和家人更好的团聚。

    Spring Boot自动配置实现原理

    使用Spring Boot创建一个简单的Web项目很简洁,不需要太多配置,编写一个简单的主程序就行:

    @SpringBootApplication
    public class ConfigApplication{
        public static void main(String[] args) {
            SpringApplication.run(ConfigApplication.class, args);
    }
    
    主程序分析

    首先分析下run方法(省略不关键的部分代码):

    public ConfigurableApplicationContext run(String... args) {
    
            ConfigurableApplicationContext context = null;
            configureHeadlessProperty();
            SpringApplicationRunListeners listeners = getRunListeners(args);
            listeners.started();
            try {
                ApplicationArguments applicationArguments = new DefaultApplicationArguments(
                        args);
                ConfigurableEnvironment environment = prepareEnvironment(listeners,
                        applicationArguments);
                Banner printedBanner = printBanner(environment);
                context = createApplicationContext();
                analyzers = new FailureAnalyzers(context);
                prepareContext(context, environment, listeners, applicationArguments,
                        printedBanner);
                listeners.finished(context, null);
                return context;
            }
            catch (Throwable ex) {
                handleRunFailure(context, listeners, analyzers, ex);
                throw new IllegalStateException(ex);
            }
        }
    

    它首先开启一个SpringApplicationRunListeners监听器,然后创建一个应用上下文ConfigurableApplicationContext,通过这个上下文加载应用所需的类和各种环境配置等。

    一个应用能够正常运行起来,需要一些环境变量、各种资源和一些相关配置等,下面看下createApplicationContext方法会加载应用定义的和需要的类及各种资源。

    自动配置

    所有的自动配置都是从注解@SpringBootApplication引入的,它其实又包含了@Configuration、@EnableAutoConfiguration和@ComponentScan,其中,@EnableAutoConfiguration就是启用自动配置的,并将导入一些自动配置的类定义。

    @Target({ElementType.TYPE})
    @Retention(RetentionPolicy.RUNTIME)
    @Documented
    @Inherited
    @SpringBootConfiguration
    @EnableAutoConfiguration
    @ComponentScan(
        excludeFilters = {@Filter(
        type = FilterType.CUSTOM,
        classes = {TypeExcludeFilter.class}
    )}
    )
    public @interface SpringBootApplication {
        Class<?>[] exclude() default {};
        String[] excludeName() default {};
        @AliasFor(
            annotation = ComponentScan.class,
            attribute = "basePackages"
        )
        String[] scanBasePackages() default {};
        @AliasFor(
            annotation = ComponentScan.class,
            attribute = "basePackageClasses"
        )
        Class<?>[] scanBasePackageClasses() default {};
    }
    

    EnableAutoConfiguration最终会导入一个自动配置的类列表,列表中的自动配置类很多,这些配置类中大都将被导入,并处于备用状态,当项目中引入了相关的包时,相关的功能将被启用。

    例如在项目的maven配置中配置了Redis的引用,Redis的默认配置项将被启用,首先会读取项目中的配置,只有项目中没有相关配置才启用配置的默认值,下面代码是Redis的自动配置,如果配置文件中没设置,会使用下面默认设置。

    @ConfigurationProperties(
        prefix = "spring.redis"
    )
    public class RedisProperties {
        private int database = 0;
        private String host = "localhost";
        private String password;
        private int port = 6379;
        private int timeout;
        private RedisProperties.Pool pool;
        private RedisProperties.Sentinel sentinel;
        private RedisProperties.Cluster cluster;
    
        public RedisProperties() {
        }
    

    通过自动配置,就不用重复定义配置项名称了,覆盖约定的配置项即可。可通过查看各个Properties类,查看有哪些配置项。

    Spring Boot数据访问实现原理

    要使用数据库,首先必须与数据库服务器建立连接。对于关系型数据库,Spring Boot 连接数据源一般都采用JDBC的方式来实现。其他类型的数据库使用各自独立的方式来建立连接。

    数据源类型和驱动

    JDBC连接数据源必须指定数据源类型和数据库驱动程序,数据源主要有4中:

    • 使用java.sql.DriverManager类;
    • 使用实现了javax.sql.DataSource接口的子类,DataSource接口由驱动程序供应商实现,主要有3类实现、基本实现、连接池实现,分布式事务实现 ;
    • DBCP连接池,Apache 软件基金组织下的开源连接池实现,Tomcat 的连接池正是采用该连接池来实现的;
    • C3P0连接池;

    Spring Boot 默认使用org.apache.tomcat.jdbc.pool.DataSource,它使用第2种方式,实现了javax.sql.DataSource接口。数据源的类型可以通过配置更改。

    另外,Spring Boot 默认几乎支持现有的所有数据库。

    数据存取功能实现

    与数据库建立连接后,就可以对数据库执行一些存取操作,对数据库实现管理的功能。数据存取操作大体上包含两方面的内容,即实体建模和持久化。

    不管是关系型数据库,还是NoSQL数据库,都遵循这一设计规范。实体建模即将Java的普通对象和关系映射为数据库表机器相关的关系,在Spring Boot中,主要是通过注解实现。

    关系型数据库都使用了JPA的一套标准,它结合使用Hibernate实现了实体的持久化。后续的数据库管理设计都遵循了JPA这一个标准规范,提供相同的访问数据库的API。

    微服务核心技术实现原理

    Spring Cloud是基于对Netfix开源组件进一步封装的一套云应用开发工具,可以用来开发各种微服务应用。

    配置服务实现

    前一篇文章说到,配置管理的在线更新功能使用事件总线,即spring-cloud-bus来发布状态变化,并使用分布式消息来发布更新事件,分布式消息最终使用RabbitMQ来实现消息收发。

    再来回顾下在线更新流程:

    • 更新Git仓库的配置文件;
    • 以POST指令出发更新请求;
    • 配置管理服务器从Git仓库中读取配置文件,并将配置文件分发给各个客户端,同时在RabbitMQ中发布一个更新消息;
    • 客户端订阅RabbitMQ消息,收到消息后执行更新;

    配置管理服务器中的消息分发是从spring-cloud-bus中调用spring-cloud-stream组件实现的,而spring-cloud-stream使用RabbitMQ实现了分布式消息分发。具体实现就不说了,使用过RabbitMQ的很好理解。

    发现服务和负载均衡

    客户端执行注册使用计划任务的方式来实现,而客户端从发现服务器中更新其他在线的客户端列表,也使用了一个定时任务来管理。

    当一个应用启用发现服务的功能之后,会默认启用Ribbon的负载均衡服务。Ribbon通过发现服务获取在线的客户端,为具有多个实例的客户端建立起负载均衡管理机制。

    分布式消息实现

    使用spirng-cloud-stream可以非常简单地使用RabbitMQ的异步消息,Spring Cloud的配置管理中的分布式消息分发也是通过调用spring-cloud-stream组件来实现的。

    下面以消息生产者和消费者的实现说明分布式消息实现

    消息生产者:

    @EnableBinding(Source.class)
    @RestController 
    @SpringBootApplication 
    public class SenderApplication { 
        @Autowired 
        @Output(Source.OUTPUT) 
        private MessageChannel channel;
    
        @RequestMapping(method = RequestMethod.POST, path = "/send") 
        public void write (@RequestBody Map<String, Object> msg){
            channel.send(MessageBuilder.withPayload(msg).build());
        } 
    
        public static void main(String[] args) {
            SpringApplication.run(SenderApplication.class, args);
        } 
    }
    

    消息消费者:

    @EnableBinding(Sink.class)
    @IntegrationComponentScan
    @MessageEndpoint
    @SpringBootApplication
    public class ReceiverApplication {
        @ServiceActivator(inputChannel=Sink.INPUT)
        public void accept(Map<String, Object> msg){
            System.out.println(msg.get("msg").toString() + ":" + msg.get("name"));
        }
    
        public static void main(String[] args) {
            SpringApplication.run(ReceiverApplication.class, args);
        }
    }
    

    从上面的分析可以看到,Spring Boot及其一些相关组件,已经尽量把一些可以实现和做到的功能,都帮我们实现了。 虽然使用Spring Boot及其相关组件看起来非常简单,但实际上可以实现无比强大的功能,这就是Spring Boot 及其组件的神奇所在。

     


    情情说

     

  • 相关阅读:
    春色人间
    如是
    Go -- FileManage 自建云盘
    JavaScript -- 定义二维数组
    mysql 碎片清理
    vue2.0项目中使用Ueditor富文本编辑器示例
    浅谈css中一个元素如何在其父元素居中显示
    CSS -- 文字竖直居中
    memcached与redis区别
    mac -- 安装OpenCV
  • 原文地址:https://www.cnblogs.com/qqtalk/p/8762298.html
Copyright © 2011-2022 走看看