微服务架构Spring Boot运行原理

1、微服务:
        在了解什么是Spring Boot之前,先要理解是什么微服务,一般我们在传统的软件开发过程中所有的功能和业务处理都放在同一个项目中并打包成一个War包,放在JEE容器中(Tomcat,Jboss,WebLogic),如过项目过程出现一点问题就需要再次打包运行。所有的功能处理模块不能相对独立和调用,而微服务架构中所有的功能都有自己独立的服务和进程,每个服务都有自己的业务处理独立开发,并且可以进行分布式管理,下面用两张较图片来比较下:

                                     传统模式                                                             微服务架构
                
 
        优缺点:
            (一)传统模式:开发简单,集中式管理,都在本地没有分布式的开销和调用,缺点在于开发效率低,代码维护成本高,部署不灵活,对于高并发,稳定性并不适用,可扩展性不强。

            (二)微服务架构:每个功能模块都有独立的进程和业务处理,相互之间可以进行调用,并且每个服务可以进行独立的开发,组成一个完成的系统,拓展性强,可进行分部式管理。
           
2、什么是Spring Boot
        Spring Boot是由Pivotal团队提供的全新框架,其设计目的是用来简化新Spring应用的初始搭建以及开发过程。该框架使用了特定的方式来进行配置,从而使开发人员不再需要定义样板化的配置。简单的来说Spring Boot封装了Spring所需要的各种jar进行统一式管理,需要的时候调用即可。
        SpringBoot与传统的SpringMVC区别,在SpringMVC当中,当需要搭建一个SpringMVC框架的时所用的步骤是:    配置web.xml , jdbc,spring事务,加载文件及注解和日志。这些东西在项目开发过程很烦锁遇到错误,查找问题花费时间比较大,而Spring Boot就是对Spring的封装,所有的配置文件和资源都进行统一的管理,不需要再次进行写入,需要某个功能直接从pom.xml文件引用对应的资源库即可,简单高效。

3、如何搭建一个Spring Boot框架
        搭建Spring Boot有两种方式:
            一、从官网直接下载   官网地址为:https://start.spring.io/  下载后导入到eclipse当中
            二、从eclipse中加载sts插件远程下载。

        SpringBoot 2.0以下java1.7可以支持2.0以上需要java1.8以上。

        对于开发软件Eclipse,MyEclipse,intellij idea三种有什么区别:
        1、Eclipse是开发软件用的很多,在于软件本身较小,不需消耗太大的内存,运行效率较快,不好之处在于插件较少需要什么软件手动安装或者远程下载。
        2、MyEclipse是很多集中式的插件,消耗内存比较大。优点在于不需要下载很多插件。
        3、Intellij idea是目前比较流行的,但软件运行消耗太大,每个项目都是独立的窗口,项目搭建过程很慢,个人不太喜欢这种。

 本文以Eclipse为例进行演示和讲解。

首先java(8.5)和maven环境要搭建好,如何搭建可以查看其它文档资料。

新建一个项目:

Service Url 后指的就是官网地址,Name就是你的项目名称

下一步:

这里我们先选择支持web的jar包  搜索Web加载进行即可。点击完成。

打开项目会有如下目录:

src/main/java   源代码

src/main/rescoures 资源文件

src/test/java 测试类

pom.xml 配置文件

我们直接运行DemoApplication.java文件  控制台出现spring的图标就说明项目搭建成功。

接下出来我做个测试用例,新建一个类Control.java   如图所示:


接着我们运行DemoApplication.java文件,然后通过浏览访问:http://localhost:8080/hello 发现能够直接打印出来。

我们发现这并不像我们之前的项目,需要通过tomcat或者Jboss打包再运行出来,Spring Boot直接能够运行,这就是Spring Boot的强大之处。下面我们来深入了解下Spring Boot是如何完成这件事的:

我们先来查看pom.xml文件的配置:

    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.1.1.RELEASE</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>

这里定义一个Spring Boot的起始器,进入节点

<parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-dependencies</artifactId>
        <version>2.1.1.RELEASE</version>
        <relativePath>../../spring-boot-dependencies</relativePath>
    </parent>
    <artifactId>spring-boot-starter-parent</artifactId>
    <packaging>pom</packaging>
    <name>Spring Boot Starter Parent</name>
    <description>Parent pom providing dependency and plugin management for applications built with Maven</description>

从它的描述中我们知道 父节点的这个pom文件是为整个应用程序提供依赖和插件管理的,并且都是用Maven控制的。

           <resource>
                <filtering>true</filtering>
                <directory>${basedir}/src/main/resources</directory>
                <includes>
                    <include>**/application*.yml</include>
                    <include>**/application*.yaml</include>
                    <include>**/application*.properties</include>
                </includes>
            </resource>

向下看我可以看到系统为我们提供了三个默认的支持文件yml,yaml,properties三种类型的文件并且都是以application标准命名,且都是在src/main/resources 目录资源文件下,查看项目系统自动给我们生成了application.properties文件,文件下面都是一些打包的本相关配置。

进入以下节点

<parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-dependencies</artifactId>
        <version>2.1.1.RELEASE</version>
        <relativePath>../../spring-boot-dependencies</relativePath>
    </parent>

可以看到Spring Boot为我们指定了明确的版本号,这里就相于版本库,好处在于可以解决版本冲突。

        <activemq.version>5.15.8</activemq.version>
        <antlr2.version>2.7.7</antlr2.version>
        <appengine-sdk.version>1.9.68</appengine-sdk.version>
        <artemis.version>2.6.3</artemis.version> 

回到pom.xml文件查看

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

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
    </dependencies>

看到pom.xml引入了spring-boot-starter-web    jar包  这个jar包就是我们新建项目时选择web 所引入进来的,查看下这个里面包含什么内容

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

看到也是和外面的spring-boot-starter-web配置一样只不过加了个版本号,这里的作用在于为我们的jar包统一版号 也就是之前进入父节点的那个文件版本资源库。再进行spring-boot-starter-web节点

<dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-starter-tomcat</artifactId>
      <version>2.1.1.RELEASE</version>
      <scope>compile</scope>
    </dependency>

<dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-webmvc</artifactId>
      <version>5.1.3.RELEASE</version>
      <scope>compile</scope>
    </dependency>

可以看到spring-boot-starter-web 为我们自动依赖了一个tomcat以及spring-webmvc  我们进入tomcat里面

<groupId>org.springframework.boot</groupId>
  <artifactId>spring-boot-starter-tomcat</artifactId>
  <version>2.1.1.RELEASE</version>
  <name>Spring Boot Tomcat Starter</name>
  <description>Starter for using Tomcat as the embedded servlet container. Default
        servlet container starter used by spring-boot-starter-web</description>

描述中可以知道Tomcat作为嵌入式servlet容器的启动程序,当 spring-boot-starter-web启动的时候,tomcat就默认已经启动了。这也就是为什么我们不需要手动将项目布署到tomcat里面运行的原因。

下面我们查看下程序是如何运行的进入主程序:

@SpringBootApplication
public class DemoApplication {

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

}

可以看到DemoApplication >>> main方法运行了一个run方法加载自身使用了并使用了@SpringBootApplication注解,查看@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 {
......}

这里我们可以看到都使用了一些注解,下面来理解下都是些什么意思:

1、@Target(ElementType.TYPE)  通过描述  /** Class, interface (including annotation type), or enum declaration */  可以知道是用于一个类,接口,枚举类型的声明,这里的SpringBootApplication是一个接口,所以需要用它声明。

2、@Retention(RetentionPolicy.RUNTIME) 查看里面内容
    RUNTIME 通过释义知道该类注释在编译的时候会记录在类文件中虚拟机运行时会保留。就可以通过反映机制得到读取它。简单的说如果使用了RetentionPolicy.RUNTIME这个参数,虚拟机会运行过程中保留该类编译后的class文件,其它类可以进行调用直到虚拟机关闭为止。

3、@Documented   简单的说如果使用了annotations作用注释就相当于和javadoc文档类似,提供一种公共的API文档。

4、@Inherited    是用于子类想拥有父类的一些注解,那么父类需要加上@Inherited注解。

5、@SpringBootConfigurationi注解 查看

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Configuration
public @interface SpringBootConfiguration {

}

当中有一个@Configuration的注解 该注解的释义很多,概括的说如果一个类使用了@Configuration作用为注解那么这个类就被定义成一个配置类,可替换xml配置文件,被注解的类内部包含有一个或多个被@Bean注解的方法,这些方法将会被AnnotationConfigApplicationContext或AnnotationConfigWebApplicationContext类进行扫描,并用于构建bean定义,初始化Spring容器。

6、@EnableAutoConfiguration注解,这个注解表示可以使用起始器依赖之外的一些jar包。也就是我们通常说的第三方jar包

@EnableAutoConfiguration  查看里面内容

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

......}

多了两个注释@AutoConfigurationPackage和@Import(AutoConfigurationImportSelector.class)  

先查看@AutoConfigurationPackage注解:

/**
 * Indicates that the package containing the annotated class should be registered with
 * {@link AutoConfigurationPackages}.
 *
 * @author Phillip Webb
 * @since 1.3.0
 * @see AutoConfigurationPackages
 */
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@Import(AutoConfigurationPackages.Registrar.class)
public @interface AutoConfigurationPackage {

}

通过释义可看出是对指示应向包含带注释类的包注册 也就是{@link AutoConfigurationPackages}这个包,进入@Import(AutoConfigurationPackages.Registrar.class)里看到

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 类有一个registerBeanDefinitions方法相当注册一个bean,我们在里设置一个断点如图

可以看到Metadata数据传入的是当前主程序的包名加上自己的类,参数new PackageImport(metadata).getPackageName()该函数返回的是包的名称com.example.demo 再进入看到

PackageImport(AnnotationMetadata metadata) {
            this.packageName = ClassUtils.getPackageName(metadata.getClassName());
 }  

进入getPackageName方法

    public static String getPackageName(String fqClassName) {
        Assert.notNull(fqClassName, "Class name must not be null");
        int lastDotIndex = fqClassName.lastIndexOf(PACKAGE_SEPARATOR);
        return (lastDotIndex != -1 ? fqClassName.substring(0, lastDotIndex) : "");
    }

该方法在这里做了个Assert,如果传入包的名称没有包含主程序包名就不进行注册,也是com.example.demo这个包名,不注册那么spring容器将会扫描不到其它类。 可以把刚刚项目里新寻的Control修改个包名,不包含com.example.demo再调试下,结果肯定扫描不到。这里就不做演示了

回到@EnableAutoConfiguration文件里

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

@AutoConfigurationPackage注解也就相当于根据主程序的包名扫描该系统中所有包含主程序名的所以类,并且注册放入到 spring 容器当中

@Import(AutoConfigurationImportSelector.class)注解 进入节点

找到loadFactoryNames

    protected Collection<String> loadFactoryNames(Class<?> source) {
        return SpringFactoriesLoader.loadFactoryNames(source,
                getClass().getClassLoader());
    }

进入loadFactoryNames----------->loadSpringFactories  可以看到系统加载了一意资源文件

public static final String FACTORIES_RESOURCE_LOCATION = "META-INF/spring.factories";

classLoader.getResources(FACTORIES_RESOURCE_LOCATION) :
ClassLoader.getSystemResources(FACTORIES_RESOURCE_LOCATION));

也就是位于org\springframework\boot\spring-boot-autoconfigure\2.1.1.RELEASE\spring-boot-autoconfigure-2.1.1.RELEASE.jar------->META-INF/spring.factories下

打开可以看到

# Initializers
org.springframework.context.ApplicationContextInitializer=\
org.springframework.boot.autoconfigure.SharedMetadataReaderFactoryContextInitializer,\
org.springframework.boot.autoconfigure.logging.ConditionEvaluationReportLoggingListener

# Application Listeners
org.springframework.context.ApplicationListener=\
org.springframework.boot.autoconfigure.BackgroundPreinitializer

# Auto Configuration Import Listeners
org.springframework.boot.autoconfigure.AutoConfigurationImportListener=\
org.springframework.boot.autoconfigure.condition.ConditionEvaluationReportAutoConfigurationImportListener
 

Spring Boot 自动帮我们加载了一些第三方jar包,包括一些初始化,监听对象,过滤器以及Configuration的jar包

7、@ComponentScan注释 定义扫描的路径从中找出标识了需要装配的类自动装配到spring的bean容器中 excludeFilters参数用于过滤出不用加入spring容器的类

以上就是对

@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 {

.....}注解的一些释义    更详细的需要自己去查看相关文档。

通过对以上的注解,pom配置文件应该对Spring Boot运行的原理有个大概的了解。

这只是一个简单的框架没有任何集成,比如开发过程用到的Jsp,Mybatis,Bootstrap,DataSoucre,还有整合框架spring cold等等。集成方式和资源路径这里就不做介绍了,后续文章会弄个简单的Spring Boot +Spring Clod集成框架进行实例介绍。  

本文主要讲解下Spring Boot的基本运行原理。

以上知识不足之处可以在下方留言板讨论。

猜你喜欢

转载自blog.csdn.net/looplook21/article/details/86158772