本文是记录自己学习微服务概念SOA,以及Springboot源码跟踪学习的过程

关于微服务和SOA

这,仅是我学习过程中记录的笔记。确定了一个待研究的主题,对这个主题进行全方面的剖析。笔记是用来方便我回顾与学习的,欢迎大家与我进行交流沟通,共同成长。不止是技术。

官网教程学习https://www.martinfowler.com/microservices/

关于微服务的认识

马丁福乐的论文学习

DDD 领域驱动设计

component 组件

微服务数据治理与去中心化

任何的服务调用都会出现失败的情况

熔断器

微服务的优点和缺点

测试和向后兼容会变的更加的复杂

微服务与传统单体应用项目的区别

微服务宏观把控与深度剖析

ESB(企业服务总线)

SOA(面向服务架构)主要针对于企业级,单用ESB,需要序列化和反序列化,采用XML格式传输。

微服务架构主要用于互联网公司。可以独立运行。HTTP+REST+JSON

单体架构存在的缺点:

复杂性高,无法按需伸缩,阻碍技术创新,部署速度逐渐编码,技术债务逐渐上升。

什么是微服务?

简而言之,微服务架构风格这种开发方法,是以开发一组小型服务的方式来开发一个独立的应用系统。其中每个小型服务都运行在自己的进程中,并经常采用HTTP资源API这种轻量的机制来相互通信。这些服务为夭折业务功能进行构建通过全自动的部署机制来进行独立部署。这些服务可以用不同的语言来编写,并且可以使用不同的数据存数技术。

image-20200127145807261

微服务是一种架构风格

image-20200127145936188

微服务的优点与挑战

image-20200127150022823

image-20200127150652519

image-20200127150607199

微服务具备的特性和设计原则

image-20200127150325956

image-20200127150842427

SOA理论和概念

SOA: Service oriented architecture 面向服务架构风格

SOA和微服务的差别

image-20200127152525733

image-20200127153134837

SOA原则:

image-20200127154825504

SOA的设计模式:

image-20200127163234152

实现方法:

image-20200127164039613

SOA带来的好处:

image-20200127164759048

SOA的缺点:

image-20200127165053612

SOA中微服务的介绍总结:

image-20200127172101785

微服务的缺点介绍:

image-20200127172834338

SOA与微服务的差别

SOA 着眼于企业,应用与应用。最大化的应用服务的可重用性,力度更大,

微服务 着眼于单个的应用,注重于解耦。力度更小。

image-20200127175008659

image-20200127175953647

SOA和微服务的区别、

image-20200127180146354

image-20200127180303318

image-20200127180450791

image-20200127180525821

结论:

image-20200127180659827

SpringBoot

SpringBoot应用起步与配置

创建第一个可以运行的springboot项目

约定大于配置

在官网使用Spring initializr创建一个gradle的springboot项目使用vscode启动。

遇到了问题,连不上vpn 依赖下载不动。经过不懈的努力。启动成功。

image-20200128115450296

image-20200128120842617

Apollo(阿波罗)是携程框架部门研发的分布式配置中心,能够集中化管理应用不同环境、不同集群的配置,配置修改后能够实时推送到应用端,并且具备规范的权限、流程治理等特性,适用于微服务配置管理场景。服务端基于Spring Boot和Spring Cloud开发,打包后可以直接运行,不需要额外安装Tomcat等应用容器。

springBoot 提供了两种配置的文件形式:

  1. Properties 文件形式: server.port=9090

  2. yml文件形式 server:

    ​ prot:9090

springboot打包文件内容与结构分析

bootJar : 将项目打成了一个jar包 , 可以直接运行的jar包

image-20200128121928235

jar 命令: 查看当前的jar命令操作。

unzip : 解压缩到一个目录里,如图

image-20200128122605639

打成的jar包的目录:

├── BOOT-INF
│ ├── classes
│ │ ├── application.properties
│ │ ├── com
│ │ │ └── test
│ │ │ └── demo
│ │ │ └── DemoApplication.class
│ │ ├── static
│ │ └── templates
│ └── lib
│ ├── classmate-1.5.1.jar
│ ├── hibernate-validator-6.0.18.Final.jar
│ ├── jackson-annotations-2.10.2.jar
│ ├── jackson-core-2.10.2.jar
│ ├── jackson-databind-2.10.2.jar
│ ├── jackson-datatype-jdk8-2.10.2.jar
│ ├── jackson-datatype-jsr310-2.10.2.jar
│ ├── jackson-module-parameter-names-2.10.2.jar
│ ├── jakarta.annotation-api-1.3.5.jar
│ ├── jakarta.validation-api-2.0.2.jar
│ ├── jboss-logging-3.4.1.Final.jar
│ ├── jul-to-slf4j-1.7.30.jar
│ ├── log4j-api-2.12.1.jar
│ ├── log4j-to-slf4j-2.12.1.jar
│ ├── logback-classic-1.2.3.jar
│ ├── logback-core-1.2.3.jar
│ ├── slf4j-api-1.7.30.jar
│ ├── snakeyaml-1.25.jar
│ ├── spring-aop-5.2.3.RELEASE.jar
│ ├── spring-beans-5.2.3.RELEASE.jar
│ ├── spring-boot-2.2.4.RELEASE.jar
│ ├── spring-boot-autoconfigure-2.2.4.RELEASE.jar
│ ├── spring-boot-starter-2.2.4.RELEASE.jar
│ ├── spring-boot-starter-json-2.2.4.RELEASE.jar
│ ├── spring-boot-starter-logging-2.2.4.RELEASE.jar
│ ├── spring-boot-starter-tomcat-2.2.4.RELEASE.jar
│ ├── spring-boot-starter-validation-2.2.4.RELEASE.jar
│ ├── spring-boot-starter-web-2.2.4.RELEASE.jar
│ ├── spring-context-5.2.3.RELEASE.jar
│ ├── spring-core-5.2.3.RELEASE.jar
│ ├── spring-expression-5.2.3.RELEASE.jar
│ ├── spring-jcl-5.2.3.RELEASE.jar
│ ├── spring-web-5.2.3.RELEASE.jar
│ ├── spring-webmvc-5.2.3.RELEASE.jar
│ ├── tomcat-embed-core-9.0.30.jar
│ ├── tomcat-embed-el-9.0.30.jar
│ └── tomcat-embed-websocket-9.0.30.jar
├── META-INF
│ └── MANIFEST.MF — 清单文件
└── org
└── springframework
└── boot
└── loader
├── ExecutableArchiveLauncher.class
├── archive
│ ├── Archive$Entry.class
│ └── JarFileArchive.class
├── data
│ ├── RandomAccessData.class
│ ├── RandomAccessDataFile$1.class
│ ├── RandomAccessDataFile$DataInputStream.class
│ ├── RandomAccessDataFile$FileAccess.class
│ └── RandomAccessDataFile.class
├── jar
│ ├── AsciiBytes.class

​ ├── JarLauncher.class

META-INF中的MANIFEST.MF 清单文件

➜ META-INF cat MANIFEST.MF
Manifest-Version: 1.0
Start-Class: com.test.demo.DemoApplication
Spring-Boot-Classes: BOOT-INF/classes/
Spring-Boot-Lib: BOOT-INF/lib/
Spring-Boot-Version: 2.2.4.RELEASE
Main-Class: org.springframework.boot.loader.JarLauncher

使用gradle搭建springboot第二个项目

build.gradle

这个地方遇到了一个小插曲。没有网,gradle构建失败,跟着视频上敲的springboot版本找不到。然后直接使用idea构建了一个springboot的项目。build.gradle内容如下:

plugins {
    id 'org.springframework.boot' version '2.2.4.RELEASE'
    id 'io.spring.dependency-management' version '1.0.9.RELEASE'
    id 'java'
}

group = 'com.erwa'
version = '0.0.1-SNAPSHOT'
sourceCompatibility = '1.8'

repositories {
    mavenCentral()
}

dependencies {
    implementation 'org.springframework.boot:spring-boot-starter-web'
    testImplementation('org.springframework.boot:spring-boot-starter-test') {
        exclude group: 'org.junit.vintage', module: 'junit-vintage-engine'
    }
}

test {
    useJUnitPlatform()
}

springboot项目的执行方式有三种:

  1. 直接运行启动类main方法
  2. 通过gradle, bootRun 启动
  3. 通过命令行的方式

小插曲,在这里,配置了一下Mac环境下的gradle的环境变量

springboot参数自动装配练习与loader机制

MyConfig:
  erwaage: 20
  erwaname: erwa
@Value("${MyConfig.erwaname}")
private String erwaname ;
@Value("${MyConfig.erwaage}")
private String erwaage;

@AutoWired @configration @Bean

@Configuration
public class MyConfig {
    @Bean
    public MyConfig myConfig(){
        return new MyConfig();
    }
}

@Autowired
private MyConfig myConfig;

java -jar xxx.jar 直接运行jar包

打包后的jar包中的org目录来自于 spring-boot-loader包

spring_boot_Loader源码分析及自定义类加载器的作用

gradle bootJar : 将项目打成jar包

java -jar xxx.jar : 运行jar包

能使用命令尽量使用命令。不然浪费了Mac

JarLauncher类

ClassLoader 类加载器,JVM课程中深入理解。

应用类加载器加载jar包中org中的jar文件。自定义类加载器加载BOOT-INF包中的jar文件

launchedURLClassLoader()

自己跟代码

为什么启动类要用main方法?举例拿代码来说明一下。

用别的方法其实也可以。是因为要在ide中支持直接运行启动方法。换成别的名字的话就不能直接通过IDE启动了。

package com.erwa.boot.testMain;

public class MyMain {
    public static void main1(String[] args) {
        System.out.println("hello 你好");
    }
}
package com.erwa.boot.testMain;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;

public class MyMain2 {
    public static void main(String[] args) throws Exception{
        Class<?> myMainClass = MyMain.class;
        Method main = myMainClass.getDeclaredMethod("main1", String[].class);
        main.invoke(null,new Object[]{null});
    }
}

通过反射的方式运行第一个main1方法。

通过不同方式启动,使用的类加载器是否一样的呢?不一样的,结果如下

public static void main(String[] args) {
    System.out.println(BootApplication.class.getClassLoader()+"123");
    SpringApplication.run(BootApplication.class, args);
}

main方法启动的类加载器:

​ sun.misc.Launcher$AppClassLoader@18b4aac2123

通过jar包的方式启动的类加载器:

​ org.springframework.boot.loder.launchedURLClassLoader@659e0bfd123

JDWP:远程调试详解

不通过IDE进行debug调试的方法。通过远程进行调试。

JDWP: Java Debug wire Protocol, Java调试协议。

 ~ java -agentlib:jdwp=help
               Java Debugger JDWP Agent Library
               --------------------------------

  (see http://java.sun.com/products/jpda for more information)

jdwp usage: java -agentlib:jdwp=[help]|[<option>=<value>, ...]

Option Name and Value            Description                       Default
---------------------            -----------                       -------
suspend=y|n                      wait on startup?                  y
transport=<name>                 transport spec                    none
address=<listen/attach address>  transport spec                    ""
server=y|n                       listen for debugger?              n
launch=<command line>            run debugger on event             none
onthrow=<exception name>         debug on throw                    none
onuncaught=y|n                   debug on any uncaught?            n
timeout=<timeout value>          for listen/attach in milliseconds n
mutf8=y|n                        output modified utf-8             n
quiet=y|n                        control over terminal messages    n

Obsolete Options 废弃的属性:
----------------
strict=y|n
stdalloc=y|n

Examples  举例:
--------
  - Using sockets connect to a debugger at a specific address:
    java -agentlib:jdwp=transport=dt_socket,address=localhost:8000 ...
  - Using sockets listen for a debugger to attach:
    java -agentlib:jdwp=transport=dt_socket,server=y,suspend=y ...

Notes
-----
  - A timeout value of 0 (the default) is no timeout.

Warnings
--------
  - The older -Xrunjdwp interface can still be used, but will be removed in
    a future release, for example:
        java -Xdebug -Xrunjdwp:[help]|[<option>=<value>, ...]

启动jar包时进入debug状态:

 java -agentlib:jdwp=transport=dt_socket,server=y,suspend=y,address=5050 -jar xxx.jar

idea中集成的远程debug的方式:

image-20200128173627029

调试spring-boot-loader的启动与加载的流程

spring-boot-loader通过非常巧妙的方式完成了类的加载。

@SpringBootApplication相关注解

你以为点下springboot运行,就这么简单吗?

注解扮演着举足轻重的角色。项目启动时,通过扫描注解的机制加载各种配置。

@SpringBootApplication注解

小技巧: Mac中查看方法的文档的快捷键: ⌃ + J

@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注解的作用 :
Indicates a configuration class that declares one or more @Bean methods and also triggers auto-configuration and component scanning. This is a convenience annotation that is equivalent to declaring @Configuration, @EnableAutoConfiguration and @ComponentScan.

@SpringBootConfiguration 注解

@Target(ElementType.TYPE) 
@Retention(RetentionPolicy.RUNTIME) 
@Documented 
@Configuration 
public interface SpringBootConfiguration  extends annotation.Annotation
Indicates that a class provides Spring Boot application @Configuration. Can be used as an alternative to the Springs standard @Configuration annotation so that configuration can be found automatically (for example in tests).

@Configuration 注解 -详解

@Target(ElementType.TYPE) 
@Retention(RetentionPolicy.RUNTIME) 
@Documented 
@Component 
public interface Configuration
extends annotation.Annotation
Indicates that a class declares one or more @Bean methods and may be processed by the Spring container to generate bean definitions and service requests for those beans at runtime, for example:
   @Configuration
   public class AppConfig {
  
       @Bean
       public MyBean myBean() {
           // instantiate, configure and return bean ...
       }
   }
Bootstrapping @Configuration classes   "Configuration注解的实现原理: 
  
Via AnnotationConfigApplicationContext
  
@Configuration classes are typically bootstrapped using either AnnotationConfigApplicationContext or its web-capable variant, AnnotationConfigWebApplicationContext. A simple example with the former follows:

   AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext();
   ctx.register(AppConfig.class);
   ctx.refresh();
   MyBean myBean = ctx.getBean(MyBean.class);
   // use myBean ...

详细的文档自行打开源码查看。
  
  composing @Configuration classes  : "可以组合多个configuration; 使用@Import()
    @Configuration classes may be composed using the @Import annotation, similar to the way that <import> works in Spring XML. Because @Configuration objects are managed as Spring beans within the container, imported configurations may be injected — for example, via constructor injection:
   @Configuration
   public class DatabaseConfig {
  
       @Bean
       public DataSource dataSource() {
           // instantiate, configure and return DataSource
       }
   }
  
   @Configuration
   @Import(DatabaseConfig.class)
   public class AppConfig {
  
       private final DatabaseConfig dataConfig;
  
       public AppConfig(DatabaseConfig dataConfig) {
           this.dataConfig = dataConfig;
       }
  
       @Bean
       public MyBean myBean() {
           // reference the dataSource() bean method
           return new MyBean(dataConfig.dataSource());
       }
   }
Now both AppConfig and the imported DatabaseConfig can be bootstrapped by registering only AppConfig against the Spring context:
   new AnnotationConfigApplicationContext(AppConfig.class);

With the @Profile annotation : "Profile注解和Configuration注解的搭配使用:
  
@Configuration classes may be marked with the @Profile annotation to indicate they should be processed only if a given profile or profiles are active:
   @Profile("development")
   @Configuration
   public class EmbeddedDatabaseConfig {
  
       @Bean
       public DataSource dataSource() {
           // instantiate, configure and return embedded DataSource
       }
   }
  
   @Profile("production")
   @Configuration
   public class ProductionDatabaseConfig {
  
       @Bean
       public DataSource dataSource() {
           // instantiate, configure and return production DataSource
       }
   }
Alternatively, you may also declare profile conditions at the @Bean method level — for example, for alternative bean variants within the same configuration class:
   @Configuration
   public class ProfileDatabaseConfig {
  
       @Bean("dataSource")
       @Profile("development")
       public DataSource embeddedDatabase() { ... }
  
       @Bean("dataSource")
       @Profile("production")
       public DataSource productionDatabase() { ... }
   }

With nested @Configuration classes    : "Configuration注解的嵌套使用
  
@Configuration classes may be nested within one another as follows:
   @Configuration
   public class AppConfig {
  
       @Inject DataSource dataSource;
  
       @Bean
       public MyBean myBean() {
           return new MyBean(dataSource);
       }
  
       @Configuration
       static class DatabaseConfig {
           @Bean
           DataSource dataSource() {
               return new EmbeddedDatabaseBuilder().build();
           }
       }
   }
When bootstrapping such an arrangement, only AppConfig need be registered against the application context. By virtue of being a nested @Configuration class, DatabaseConfig will be registered automatically. This avoids the need to use an @Import annotation when the relationship between AppConfig and DatabaseConfig is already implicitly clear.
Note also that nested @Configuration classes can be used to good effect with the @Profile annotation to provide two options of the same bean to the enclosing @Configuration class.
  

@EnableAutoConfiguration 注解

"自动配置注解": 进行自动的查找和类型匹配。
Enable auto-configuration of the Spring Application Context, attempting to guess and configure beans that you are likely to need. Auto-configuration classes are usually applied based on your classpath and what beans you have defined. For example, if you have tomcat-embedded.jar on your classpath you are likely to want a TomcatServletWebServerFactory (unless you have defined your own ServletWebServerFactory bean).
When using @SpringBootApplication, the auto-configuration of the context is automatically enabled and adding this annotation has therefore no additional effect.
Auto-configuration tries to be as intelligent as possible and will back-away as you define more of your own configuration. You can always manually exclude() any configuration that you never want to apply (use excludeName() if you don‘t have access to them). You can also exclude them via the spring.autoconfigure.exclude property. Auto-configuration is always applied after user-defined beans have been registered.
The package of the class that is annotated with @EnableAutoConfiguration, usually via @SpringBootApplication, has specific significance and is often used as a 'default'. For example, it will be used when scanning for @Entity classes. It is generally recommended that you place @EnableAutoConfiguration (if you’re not using @SpringBootApplication) in a root package so that all sub-packages and classes can be searched.
Auto-configuration classes are regular Spring @Configuration beans. They are located using the SpringFactoriesLoader mechanism (keyed against this class). Generally auto-configuration beans are @Conditional beans (most often using @ConditionalOnClass and @ConditionalOnMissingBean annotations).

@ComponentScan 组件扫描注解

Configures component scanning directives for use with @Configuration classes. Provides support parallel with Spring XML's <context:component-scan> element.
Either basePackageClasses or basePackages (or its alias value) may be specified to define specific packages to scan. If specific packages are not defined, scanning will occur from the package of the class that declares this annotation.

Springboot项目启动

SpringApplication 启动类的介绍

org.springframework.boot 
public class SpringApplication extends Object
  
Class that can be used to bootstrap and launch a Spring application from a Java main method. By default class will perform the following steps to bootstrap your application:
- Create an appropriate ApplicationContext instance (depending on your classpath)
- Register a CommandLinePropertySource to expose command line arguments as Spring properties
- Refresh the application context, loading all singleton beans
- Trigger any CommandLineRunner beans
  
In most circumstances the static run(Class, String[]) method can be called directly from your main method to bootstrap your application:
   @Configuration
   @EnableAutoConfiguration
   public class MyApplication  {
  
     // ... Bean definitions
  
     public static void main(String[] args) {
       SpringApplication.run(MyApplication.class, args);
     }
   }
   
For more advanced configuration a SpringApplication instance can be created and customized before being run:
   public static void main(String[] args) {
     SpringApplication application = new SpringApplication(MyApplication.class);
     // ... customize application settings here
     application.run(args)
   }
   
SpringApplications can read beans from a variety of different sources. It is generally recommended that a single @Configuration class is used to bootstrap your application, however, you may also set sources from:
- The fully qualified class name to be loaded by AnnotatedBeanDefinitionReader
- The location of an XML resource to be loaded by XmlBeanDefinitionReader, or a groovy script to be loaded by GroovyBeanDefinitionReader
- The name of a package to be scanned by ClassPathBeanDefinitionScanner
  
""Configuration properties are also bound to the SpringApplication. This makes it possible to set SpringApplication properties dynamically, like additional sources ("spring.main.sources" - a CSV list) the flag to indicate a web environment ("spring.main.web-application-type=none") or the flag to switch off the banner ("spring.main.banner-mode=off").

springBoot启动流程介绍

启动时,调用springApplication类中的run方法:
SpringApplication.run(BootApplication.class, args);

工厂加载机制详解和工厂缓存源码分析

默认的类加载器是应用类加载器

SpringFactoriesLoader类

org.springframework.core.io.support public final class SpringFactoriesLoader
extends Object
General purpose factory loading mechanism for internal use within the framework.
SpringFactoriesLoader loads and instantiates factories of a given type from "META-INF/spring.factories" files which may be present in multiple JAR files in the classpath. The spring.factories file must be in Properties format, where the key is the fully qualified name of the interface or abstract class, and the value is a comma-separated list of implementation class names. For example:
example.MyService=example.MyServiceImpl1,example.MyServiceImpl2
where example.MyService is the name of the interface, and MyServiceImpl1 and MyServiceImpl2 are two implementations.
  
  

spring工厂Bean加载过程

    private static Map<String, List<String>> loadSpringFactories(@Nullable ClassLoader classLoader) {
        MultiValueMap<String, String> result = cache.get(classLoader);
        if (result != null) {
            return result;
        }

        try {
            Enumeration<URL> urls = (classLoader != null ?
                    classLoader.getResources(FACTORIES_RESOURCE_LOCATION) :
                    ClassLoader.getSystemResources(FACTORIES_RESOURCE_LOCATION));
            result = new LinkedMultiValueMap<>();
            while (urls.hasMoreElements()) {
                URL url = urls.nextElement();
                UrlResource resource = new UrlResource(url);
                Properties properties = PropertiesLoaderUtils.loadProperties(resource);
                for (Map.Entry<?, ?> entry : properties.entrySet()) {
                    String factoryTypeName = ((String) entry.getKey()).trim();
                    for (String factoryImplementationName : StringUtils.commaDelimitedListToStringArray((String) entry.getValue())) {
                        result.add(factoryTypeName, factoryImplementationName.trim());
                    }
                }
            }
            cache.put(classLoader, result);
            return result;
        }
        catch (IOException ex) {
            throw new IllegalArgumentException("Unable to load factories from location [" +
                    FACTORIES_RESOURCE_LOCATION + "]", ex);
        }
    }

    加载bean的过程,会加载三个包里的spring.factories文件 ,
  包分别是  spring-boot-autoconfigure  ,spring-boot,Spring-beans,
  最终返回一个结果集。没有所谓的自动一说,都是底层设计好的,帮你把事情给准备好了。

SpringApplication构造过程

SpringApplication的构造方法:
public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) {
  "设定一个资源加载器"
        this.resourceLoader = resourceLoader;
  "给启动源赋值"
        Assert.notNull(primarySources, "PrimarySources must not be null");
        this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources));
        this.webApplicationType = WebApplicationType.deduceFromClasspath();
  "初始化器"
        setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class));
  "监听器"
        setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
  ""
        this.mainApplicationClass = deduceMainApplicationClass();
    }
创建实例:初始化器获取到了工厂的名字后,进行下一步操作,生成实例返回。
private <T> List<T> createSpringFactoriesInstances(Class<T> type, Class<?>[] parameterTypes,
            ClassLoader classLoader, Object[] args, Set<String> names) {
        List<T> instances = new ArrayList<>(names.size());
        for (String name : names) {
            try {
                Class<?> instanceClass = ClassUtils.forName(name, classLoader);
                Assert.isAssignable(type, instanceClass);
                Constructor<?> constructor = instanceClass.getDeclaredConstructor(parameterTypes);
                T instance = (T) BeanUtils.instantiateClass(constructor, args);
                instances.add(instance);
            }
            catch (Throwable ex) {
                throw new IllegalArgumentException("Cannot instantiate " + type + " : " + name, ex);
            }
        }
        return instances;
    }

计算机领域的两大难题: 变量命名和缓存一致性

springApplication构造过程使用了观察者模式的监听机制。

使用运行时异常,获取到堆栈信息,然后获取对象。妙啊。  
private Class<?> deduceMainApplicationClass() {
        try {
            StackTraceElement[] stackTrace = new RuntimeException().getStackTrace();
            for (StackTraceElement stackTraceElement : stackTrace) {
                if ("main".equals(stackTraceElement.getMethodName())) {
                    return Class.forName(stackTraceElement.getClassName());
                }
            }
        }
        catch (ClassNotFoundException ex) {
            // Swallow and continue
        }
        return null;
    }

SpringApplication.run() 方法介绍

/**
        运行这个spring应用,创建并且刷新ApplicationContext(整个spring中核心的接口)
     * Run the Spring application, creating and refreshing a new
     * {@link ApplicationContext}.
     * @param args the application arguments (usually passed from a Java main method)
     * @return a running {@link ApplicationContext}
     */
    public ConfigurableApplicationContext run(String... args) {
    //秒表计时器“统计应用的启动时间
        StopWatch stopWatch = new StopWatch();
        stopWatch.start(); 
    //ConfigurableApplicationContext:可配置的应用上下文对象
        ConfigurableApplicationContext context = null;
    // springBoot异常报告器
        Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList<>();
        configureHeadlessProperty();
    // SpringApplicationRunListeners   针对于run方法的运行监听器
        SpringApplicationRunListeners listeners = getRunListeners(args); //获取鉴定器的实例
    // run方法首次启动,启动所有的监听器
        listeners.starting(); // 涉及到 观察者模式的经典应用
    // try 中完成了 应用启动之前的所有启动准备工作。
        try {
      // 构建一个应用参数对象
            ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
      // 配置环境准备    
            ConfigurableEnvironment environment = prepareEnvironment(listeners, applicationArguments);
      // 配置忽略的bean信息
            configureIgnoreBeanInfo(environment);
      // Banner信息打印
            Banner printedBanner = printBanner(environment);
      // 给context对象赋值
            context = createApplicationContext();
      //获取spring工厂实例
            exceptionReporters = getSpringFactoriesInstances(SpringBootExceptionReporter.class,
                    new Class[] { ConfigurableApplicationContext.class }, context);
      //准备上下文对象
            prepareContext(context, environment, listeners, applicationArguments, printedBanner);
            refreshContext(context);
            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);
        }
        return context;
    }

ApplicationContext 接口的作用介绍

为spring提供所有的配置信息。所拥有的能力

org.springframework.context public interface ApplicationContext
extends EnvironmentCapable, ListableBeanFactory, HierarchicalBeanFactory, MessageSource, ApplicationEventPublisher, ResourcePatternResolver
  
Central interface to provide configuration for an application. This is read-only while the application is running, but may be reloaded if the implementation supports this.
  
An ApplicationContext provides:
- Bean factory methods for accessing application components. Inherited from ListableBeanFactory.
  //用于访问公共组件的bean工厂的方法
- The ability to load file resources in a generic fashion. Inherited from the org.springframework.core.io.ResourceLoader interface.
  //加载资源的能力
- The ability to publish events to registered listeners. Inherited from the ApplicationEventPublisher interface.
  //向注册的监听器发送消息
- The ability to resolve messages, supporting internationalization. Inherited from the MessageSource interface.
  //解析消息的能力
- Inheritance from a parent context. Definitions in a descendant context will always take priority. This means, for example, that a single parent context can be used by an entire web application, while each servlet has its own child context that is independent of that of any other servlet.
  //从父的上下文中继承消息的能力
  
In addition to standard org.springframework.beans.factory.BeanFactory lifecycle capabilities, ApplicationContext implementations detect and invoke ApplicationContextAware beans as well as ResourceLoaderAware, ApplicationEventPublisherAware and MessageSourceAware beans.

SpringApplication.Run的运行生命周期

读源码的时候,多问几个为什么

  • 不同的生命周期对应着应用不同的事件
Listener for the SpringApplication run method. SpringApplicationRunListeners are loaded via the SpringFactoriesLoader and should declare a public constructor that accepts a SpringApplication instance and a String[] of arguments. A new SpringApplicationRunListener instance will be created for each run.
  
public interface SpringApplicationRunListener {

    /**
     * Called immediately when the run method has first started. Can be used for very
     * early initialization.
     */
    default void starting() {
    }

    /**
     * Called once the environment has been prepared, but before the
     * {@link ApplicationContext} has been created.
     * @param environment the environment
     */
    default void environmentPrepared(ConfigurableEnvironment environment) {
    }

    /**
     * Called once the {@link ApplicationContext} has been created and prepared, but
     * before sources have been loaded.
     * @param context the application context
     */
    default void contextPrepared(ConfigurableApplicationContext context) {
    }

    /**
     * Called once the application context has been loaded but before it has been
     * refreshed.
     * @param context the application context
     */
    default void contextLoaded(ConfigurableApplicationContext context) {
    }

    /**
     * The context has been refreshed and the application has started but
     * {@link CommandLineRunner CommandLineRunners} and {@link ApplicationRunner
     * ApplicationRunners} have not been called.
     * @param context the application context.
     * @since 2.0.0
     */
    default void started(ConfigurableApplicationContext context) {
    }

    /**
     * Called immediately before the run method finishes, when the application context has
     * been refreshed and all {@link CommandLineRunner CommandLineRunners} and
     * {@link ApplicationRunner ApplicationRunners} have been called.
     * @param context the application context.
     * @since 2.0.0
     */
    default void running(ConfigurableApplicationContext context) {
    }

    /**
     * Called when a failure occurs when running the application.
     * @param context the application context or {@code null} if a failure occurred before
     * the context was created
     * @param exception the failure
     * @since 2.0.0
     */
    default void failed(ConfigurableApplicationContext context, Throwable exception) {
    }

}

SimpleApplicationEventMulticaster 简单的应用事件广播器 — 观察者模式的经典应用

观察者模式: 广播器广播,订阅了事件的监听器监听,决定事件是否执行操作。

应用定义了很多事件。来自于SpringApplicationEvent 

org.springframework.context.event public class SimpleApplicationEventMulticaster
extends AbstractApplicationEventMulticaster
  
Simple implementation of the ApplicationEventMulticaster interface.
Multicasts all events to all registered listeners, leaving it up to the listeners to ignore events that they are not interested in. Listeners will usually perform corresponding instanceof checks on the passed-in event object.
  
By default, all listeners are invoked in the calling thread. This allows the danger of a rogue listener blocking the entire application, but adds minimal overhead. Specify an alternative task executor to have listeners executed in different threads, for example from a thread pool.

Environment 环境 组件

代表当前应用的环境

image-20200130122637656

ConfigurabeEnvironment 配置环境组件的doc

image-20200130122511903

#### Banner信息输出与定制的原理

            Banner printedBanner = printBanner(environment);

private Banner printBanner(ConfigurableEnvironment environment) {
        if (this.bannerMode == Banner.Mode.OFF) {
            return null;
        }
        ResourceLoader resourceLoader = (this.resourceLoader != null) ? this.resourceLoader
                : new DefaultResourceLoader(getClassLoader());
        SpringApplicationBannerPrinter bannerPrinter = new SpringApplicationBannerPrinter(resourceLoader, this.banner);
        if (this.bannerMode == Mode.LOG) {
            return bannerPrinter.print(environment, this.mainApplicationClass, logger);
        }
        return bannerPrinter.print(environment, this.mainApplicationClass, System.out);
    }

SpringBoot启动流程结束

SpringBoot Modules

springboot的重要模块。

There are a number of modules in Spring Boot, here is a quick overview:

spring-boot

The main library providing features that support the other parts of Spring Boot, these include:

  • The SpringApplication class, providing static convenience methods that make it easy to write a stand-alone Spring Application. Its sole job is to create and refresh an appropriate Spring ApplicationContext
  • Embedded web applications with a choice of container (Tomcat, Jetty or Undertow)
  • First class externalized configuration support
  • Convenience ApplicationContext initializers, including support for sensible logging defaults

spring-boot-autoconfigure

Spring Boot can configure large parts of common applications based on the content of their classpath. A single @EnableAutoConfiguration annotation triggers auto-configuration of the Spring context.

Auto-configuration attempts to deduce which beans a user might need. For example, if HSQLDB is on the classpath, and the user has not configured any database connections, then they probably want an in-memory database to be defined. Auto-configuration will always back away as the user starts to define their own beans.

spring-boot-starters

Starters are a set of convenient dependency descriptors that you can include in your application. You get a one-stop-shop for all the Spring and related technology that you need without having to hunt through sample code and copy paste loads of dependency descriptors. For example, if you want to get started using Spring and JPA for database access just include the spring-boot-starter-data-jpa dependency in your project, and you are good to go.

spring-boot-cli

命令行应用

The Spring command line application compiles and runs Groovy source, making it super easy to write the absolute minimum of code to get an application running. Spring CLI can also watch files, automatically recompiling and restarting when they change.

spring-boot-actuator

Actuator endpoints let you monitor and interact with your application. Spring Boot Actuator provides the infrastructure required for actuator endpoints. It contains annotation support for actuator endpoints. Out of the box, this module provides a number of endpoints including the HealthEndpoint, EnvironmentEndpoint, BeansEndpoint and many more.

spring-boot-actuator-autoconfigure

This provides auto-configuration for actuator endpoints based on the content of the classpath and a set of properties. For instance, if Micrometer is on the classpath, it will auto-configure the MetricsEndpoint. It contains configuration to expose endpoints over HTTP or JMX. Just like Spring Boot AutoConfigure, this will back away as the user starts to define their own beans.

spring-boot-test

This module contains core items and annotations that can be helpful when testing your application.

spring-boot-test-autoconfigure

Like other Spring Boot auto-configuration modules, spring-boot-test-autoconfigure, provides auto-configuration for tests based on the classpath. It includes a number of annotations that can be used to automatically configure a slice of your application that needs to be tested.

spring-boot-loader

Spring Boot Loader provides the secret sauce that allows you to build a single jar file that can be launched using java -jar. Generally you will not need to use spring-boot-loader directly, but instead work with the Gradle or Maven plugin.

spring-boot-devtools

提供了额外的开发工具。

The spring-boot-devtools module provides additional development-time features such as automatic restarts, for a smoother application development experience. Developer tools are automatically disabled when running a fully packaged application.

springboot整合组件

springboot日志处理

springboot中logback日志的使用

@PostConstruct

在yml中 : logging.level.root : debug 设置日志级别

日志级别: Trance , debug , info,warn ,error

springboot整合JSP

Springboot 项目 推荐使用模板引擎Thymeleaf

注意事项:使用JSP的时候,需要先引入jsp的依赖, 使用普通的@controller 注解

javax.servlet:jstl 
org.apache.tomcat.embed:tomcat-embed-jasper

springboot整合webSocket

需要添加依赖:

org.springframework.boot:spring-boot-starter-websocket

org.springframework.boot:spring-boot-starter-json

需要添加注解:

@EnableWebSocket

静态资源的存放位置:

resource/static/xxx.html

以前没有接触过 websocket开发,听的时候有点蒙蔽,不知道在说的是什么。

但是举的例子让我有了一点儿大概的认识。根据TCP协议来进行客户端和服务器端的通信。

设计模式:适配器模式

每次引用新的依赖时,需要在启动类上添加相应的注解来引用。

springboot使用开发者工具和单元测试

spring-boot-devtools

The spring-boot-devtools module provides additional development-time features such as automatic restarts, for a smoother application development experience. Developer tools are automatically disabled when running a fully packaged application.

需要先添加依赖:

org.springframework.boot:spring-boot-devtools

提供了热部署的功能。IDEA要配合修改一下IDE的配置才能使用热部署的功能。

模块小还好,都一样。模块大的话,特别的浪费开发时间。

自动重启和手动重启的区别: 使用的类加载器不一样。自动重启重新加载的类会少很多,自己手动重启时会重新加载所有的类,所以自动重启的速度会快一点。

image-20200130220642748

image-20200130220349458

test 单元测试

需要引入依赖:

org.springframework.boot:spring-boot-starter-test

使用到的注解:

@RunWith(SpringRunner.class)

@SpringBootTest

public class MyControllerTest{

@Before

@Test

}

版权声明:本文为bigbaby原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://www.cnblogs.com/bigbaby/p/12501627.html