#源码研读#spring boot 入门

194 阅读6分钟

1、什么是spring boot

需要思考为什么要做spring boot,spring的核心思想是为了解决什么问题?

  • 减少配置

  • 简化开发工作量

  • 使用嵌入式的servlet容器,应用无需打成war包,即:可以生成直接运行的jar包,下面的代码是需要配置在pom文件中的,这样在maven直接打包的时候就可以将嵌入式的tomcat放在里面了

        <build>
            <plugins>
                <plugin>
                    <groupId>org.springframework.boot</groupId>
                    <artifactId>spring-boot-maven-plugin</artifactId>
                </plugin>
            </plugins>
        </build>
    
  • starters自动依赖与版本控制,本质上是因为在spring boot中我们引入了parent

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

    spring-boot-starter-parent:配置了相关依赖信息

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

    spring-boot-dependencies:就是spring-boot管理依赖的配置文件,所有的依赖包以及版本控制信息都是放在这里面进行管理的

    <properties>
        <activemq.version>5.15.11</activemq.version>
        <antlr2.version>2.7.7</antlr2.version>
        <appengine-sdk.version>1.9.77</appengine-sdk.version>
        <artemis.version>2.10.1</artemis.version>
        <aspectj.version>1.9.5</aspectj.version>
        <assertj.version>3.13.2</assertj.version>
        <atomikos.version>4.0.6</atomikos.version>
        <awaitility.version>4.0.1</awaitility.version>
        <bitronix.version>2.1.4</bitronix.version>
        <build-helper-maven-plugin.version>3.0.0</build-helper-maven-plugin.version>
        <byte-buddy.version>1.10.4</byte-buddy.version>
        <caffeine.version>2.8.0</caffeine.version>
        <cassandra-driver.version>3.7.2</cassandra-driver.version>
        <classmate.version>1.5.1</classmate.version>
        <commons-codec.version>1.13</commons-codec.version>
        <commons-dbcp2.version>2.7.0</commons-dbcp2.version>
        <commons-lang3.version>3.9</commons-lang3.version>
        <commons-pool.version>1.6</commons-pool.version>
        <commons-pool2.version>2.7.0</commons-pool2.version>
    ……………………………………
    

2、什么是微服务

  • 独立进程的小服务,每个服务可以独立运行

  • 每个服务可以独立替换和升级

  • 部署和运维带来了非常大的挑战。如何解决?

    这个就是spring cloud要解决的问题,以及后面spring boot也会解决的事情

  • 性能问题如何考虑和解决?

    网络开销带来了性能的降低,需要考虑如何解决网络开销带来性能降低的问题,这个需要在服务设计层面进行考虑,尽量的将服务调用频繁的放在一起,减少这类开销

3、学习spring boot的前置条件

  • spring4、maven、idea

  • 环境约束:

    • jdk1.8
    • maven3.3+
    • spring boot 1.5.9
  • maven设置:

    • 给maven的setting配置文件,添加profile配置
  • idea设置:

    • 将自己安装的maven设置到idea中

4、spring boot第一个程序-HelloWorld

  • 创建maven项目

    • 选择maven基本信息

  • 填写项目信息

  • 填写项目信息

  • 导入spring boot相关依赖

       <parent>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-parent</artifactId>
            <version>2.2.2.RELEASE</version>
            <relativePath/> <!-- lookup parent from repository -->
        </parent>
    
    
    
        <dependencies>
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-web</artifactId>
            </dependency>
        </dependencies>
    
  • 编写一个主程序(启动spring boot应用)

    @SpringBootApplication
    public class HelloWorldMainApplication {
        public static void main(String[] args) {
            SpringApplication.run(HelloWorldMainApplication.class,args);
        }
    }
    
  • 编写相关的controller和service等业务类

    @Controller //表示这是一个controller类
    public class HelloWroldController {
        
        @ResponseBody //表示这个方法需要返回一个报文体给调用端
        @RequestMapping("/hello") //设置请求路径
        public String hello(){
            return "Hello World!";
        }
    }
    
  • 运行主程序测试

  • 部署

        <build>
            <plugins>
                <plugin>
                    <groupId>org.springframework.boot</groupId>
                    <artifactId>spring-boot-maven-plugin</artifactId>
                </plugin>
            </plugins>
        </build>
    

    在pom文件中配置了以上的编译插件,就可以打包生成一个可执行的jar包了。

    运行的时候,直接就使用如下命令就可以

    java -jar XXXXXXX.jar
    

5、hello world 项目探究

1、pom文件

  • 版本管控
父依赖
<parent>
  <groupId>org.springframework.boot</groupId>
  <artifactId>spring-boot-starter-parent</artifactId>
  <version>2.2.2.RELEASE</version>
  <relativePath/> <!-- lookup parent from repository -->
</parent>

spring-boot-starter-parent的父依赖是spring-boot-dependencies
<parent>
  <groupId>org.springframework.boot</groupId>
  <artifactId>spring-boot-dependencies</artifactId>
  <version>2.2.2.RELEASE</version>
  <relativePath>../../spring-boot-dependencies</relativePath>
</parent>

spring-boot-dependencies:版本管控
<properties>
  <activemq.version>5.15.11</activemq.version>
  <antlr2.version>2.7.7</antlr2.version>
  <appengine-sdk.version>1.9.77</appengine-sdk.version>
  <artemis.version>2.10.1</artemis.version>
  <aspectj.version>1.9.5</aspectj.version>
  <assertj.version>3.13.2</assertj.version>
  <atomikos.version>4.0.6</atomikos.version>
  <awaitility.version>4.0.1</awaitility.version>
  <bitronix.version>2.1.4</bitronix.version>
  <build-helper-maven-plugin.version>3.0.0</build-helper-maven-plugin.version>
  <byte-buddy.version>1.10.4</byte-buddy.version>
  <caffeine.version>2.8.0</caffeine.version>
  <cassandra-driver.version>3.7.2</cassandra-driver.version>
  <classmate.version>1.5.1</classmate.version>
  <commons-codec.version>1.13</commons-codec.version>
  <commons-dbcp2.version>2.7.0</commons-dbcp2.version>
  <commons-lang3.version>3.9</commons-lang3.version>
  <commons-pool.version>1.6</commons-pool.version>
  <commons-pool2.version>2.7.0</commons-pool2.version>
  <couchbase-cache-client.version>2.1.0</couchbase-cache-client.version>
  <couchbase-client.version>2.7.11</couchbase-client.version>
  <db2-jdbc.version>11.5.0.0</db2-jdbc.version>
  <dependency-management-plugin.version>1.0.8.RELEASE</dependency-management-plugin.version>
  ……
</properties>
  • 包下载
<dependencies>
  <dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
  </dependency>
</dependencies>

spring-boot-starter-web:管理和下载包
<dependencies>
  <dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter</artifactId>
    <version>2.2.2.RELEASE</version>
    <scope>compile</scope>
  </dependency>
  <dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-json</artifactId>
    <version>2.2.2.RELEASE</version>
    <scope>compile</scope>
  </dependency>
  <dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-tomcat</artifactId>
    <version>2.2.2.RELEASE</version>
    <scope>compile</scope>
  </dependency>
  <dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-validation</artifactId>
    <version>2.2.2.RELEASE</version>
    <scope>compile</scope>
    <exclusions>
      <exclusion>
        <artifactId>tomcat-embed-el</artifactId>
        <groupId>org.apache.tomcat.embed</groupId>
      </exclusion>
    </exclusions>
  </dependency>
  <dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-web</artifactId>
    <version>5.2.2.RELEASE</version>
    <scope>compile</scope>
  </dependency>
  <dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-webmvc</artifactId>
    <version>5.2.2.RELEASE</version>
    <scope>compile</scope>
  </dependency>
</dependencies>

2、主程序

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

@SpringBootApplication

@SpringBootConfiguration
@EnableAutoConfiguration
@ComponentScan(excludeFilters = { @Filter(type = FilterType.CUSTOM, classes = TypeExcludeFilter.class),
      @Filter(type = FilterType.CUSTOM, classes = AutoConfigurationExcludeFilter.class) })
public @interface SpringBootApplication {

​ @SpringBootConfiguration注解,底层是spring的configuration注解,此注解代表该类是一个配置类等效于以前spring中的xml配置文件。

@Configuration
public @interface SpringBootConfiguration {
  
@Configuration下面是组件注解
  
  @Component
  public @interface Configuration {
      @AliasFor(
          annotation = Component.class
      )
      String value() default "";
      boolean proxyBeanMethods() default true;
  }

​ @EnableAutoConfiguration注解是自动配置包注解(@AutoConfigurationPackage)和导入注解(@Import(AutoConfigurationImportSelector.class)

@AutoConfigurationPackage
@Import(AutoConfigurationImportSelector.class)
public @interface EnableAutoConfiguration {

​ @AutoConfigurationPackage注解导入了**@Import(AutoConfigurationPackages.Registrar.class)**这个类,其中Registrar方法如下

/**
	 * {@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());
    //将主启动类的所在包的路径获取到并在该包下面,将所有的controller进行注册。所以如果某个controller没有放到主主启动类的子包下面,将不会被扫描到,从而不能被注册
  }

  @Override
  public Set<Object> determineImports(AnnotationMetadata metadata) {
    return Collections.singleton(new PackageImport(metadata));
  }

}

​ @Import(AutoConfigurationImportSelector.class)注解导入了,自动配置导入选择器类

public String[] selectImports(AnnotationMetadata annotationMetadata) {
  if (!isEnabled(annotationMetadata)) {
    return NO_IMPORTS;
  }
  AutoConfigurationMetadata autoConfigurationMetadata = AutoConfigurationMetadataLoader
    .loadMetadata(this.beanClassLoader);//自动配置元数据加载器,其中loadMetadata调用的方法如下
  AutoConfigurationEntry autoConfigurationEntry = getAutoConfigurationEntry(autoConfigurationMetadata,
                                                                            annotationMetadata);
  return StringUtils.toStringArray(autoConfigurationEntry.getConfigurations());
}

​ loadMetadata方法归属的类AutoConfigurationMetadataloader

/**
 * Internal utility used to load {@link AutoConfigurationMetadata}.
 *
 * @author Phillip Webb
 */
final class AutoConfigurationMetadataLoader {

  protected static final String PATH = "META-INF/spring-autoconfigure-metadata.properties";

  private AutoConfigurationMetadataLoader() {
  }

  static AutoConfigurationMetadata loadMetadata(ClassLoader classLoader) {
    return loadMetadata(classLoader, PATH);
  }

​ 从PATH路径描述,可以看出来,配置文件加载路径是在**"META-INF/spring-autoconfigure-metadata.properties"**文件中进行配置的。

通过自动加载的方式,将所需要的配置文件,通过类的方式自动加载到容器中,所以就省略了ApplicationContext.xml配置文件的配置工作。举例如ElasticsearchAutoConfiguration的配置类

/**
 * {@link org.springframework.boot.autoconfigure.EnableAutoConfiguration
 * Auto-configuration} for Elasticsearch.
 *
 * @author Artur Konczak
 * @author Mohsin Husen
 * @author Andy Wilkinson
 * @since 1.1.0
 */
@Configuration(proxyBeanMethods = false)
@ConditionalOnClass({ Client.class, TransportClientFactoryBean.class })
@ConditionalOnProperty(prefix = "spring.data.elasticsearch", name = "cluster-nodes", matchIfMissing = false)
@EnableConfigurationProperties(ElasticsearchProperties.class)
public class ElasticsearchAutoConfiguration {

	private final ElasticsearchProperties properties;

	public ElasticsearchAutoConfiguration(ElasticsearchProperties properties) {
		this.properties = properties;
	}

	@Bean
	@ConditionalOnMissingBean
	public TransportClient elasticsearchClient() throws Exception {
		TransportClientFactoryBean factory = new TransportClientFactoryBean();
		factory.setClusterNodes(this.properties.getClusterNodes());
		factory.setProperties(createProperties());
		factory.afterPropertiesSet();
		return factory.getObject();
	}

	private Properties createProperties() {
		Properties properties = new Properties();
		properties.put("cluster.name", this.properties.getClusterName());
		properties.putAll(this.properties.getProperties());
		return properties;
	}
}

elasticsearchClient作为bean注入到了容器中,从而完成配置

系列文章可以关注公众号 《正涣》 将给带来框架源码解读和算法分析方面的知识,帮助你进阶程序员之路