SpringBoot技术入门及原理剖析

  • A+
所属分类:java

一.简介 :Spring Boot用来简化Spring应用开发,约定大于配置,去繁从简,是由Pivotal团队提供的全新框架。其设计目的是用来简化新Spring应用的初始搭建以及开发过程。该框架使用了特定的方式来进行配置(有特殊需求可以添加自己的配置覆盖默认配置),从而使开发人员不再需要定义样板化的配置。Spring Boot可以看成是J2EE的一站式解决方案。

二、Spring Boot的优点:1)、快速创建独立运行的Spring项目以及与主流框架集成。
2)、使用嵌入式的Servlet容器,应用无需打成war包,可以打成jar包,通过java -jar的方式直接运行。
3)、starters(启动器)自动依赖与版本控制。
4)、大量的自动配置,简化开发,也可以修改默认值。
5)、无需配置XML,无代码生成,开箱即用。
6)、准生产环境的运行时应用监控。
7)、与云计算的天然集成。

三、解决微服务存在的部署和运维难的问题:Spring Boot

四、Spring Boot 入门项目:HelloWorld(也可以参考六,快速创建一个SpringBoot项目)
准备环境:为Maven的settings.xml配置文件的profiles标签添加如下信息:

 <profile>
<id>jdk-1.8</id>
<activation>
  <activeByDefault>true</activeByDefault>
  <jdk>1.8</jdk>
</activation>
<properties>
  <maven.compiler.source>1.8</maven.compiler.source>
  <maven.compiler.target>1.8</maven.compiler.target>
  <maven.compiler.compilerVersion>1.8</maven.compiler.compilerVersion>
</properties>
  </profile>

将IDEA的Maven更换为我们自己本地安装的Maven。(自行百度更换)
(1)、创建一个maven工程;(jar)
(2)、在pom.xml中导入相关依赖:

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

(3)、编写一个主程序,启动SpringBoot应用

@SpringBootApplication
public class Hello {
    public static void main(String[] args) throws Exception {
        //启动spring应用
        SpringApplication.run(Hello.class, args);
    }
}

(4)、编写相关的Controller、Service类

@Controller
public class HelloController {
    @ResponseBody
    @RequestMapping("/hello")
    public String hello(){
        return "hello world!";
    }
}

(5)运行主测试程序。
(6)简化部署应用<可以将应用打包成一个可执行的jar包>:通过Maven Projects中的package(双击)即可。生成jar的位置:默认在项目的target目录下的“项目名称.jar”文件。运行jar:在命令行可以通过“java -jar jar文件名.jar”命令运行项目。

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

五、Hello World探究---POM文件:
(1)、父项目(spring-boot-starter-parent):

<parent>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-parent</artifactId>
    <version>2.0.0.RELEASE</version>
</parent>

进入spring-boot-starter-parent发现它还有一个父项目 :

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

进入spring-boot-dependencies后,发现如下信息,与之前我们创建的分布式项目继承的Maven父项目功能是一样的,用来管理所有jar包依赖的版本。称为Spring Boot的版本仲裁中心,以后我们导入依赖默认是不需要写版本;(没有在dependencies里面管理的依赖,需要声明版本号)

<properties>
    <activemq.version>5.15.3</activemq.version>
    <antlr2.version>2.7.7</antlr2.version>
    <appengine-sdk.version>1.9.62</appengine-sdk.version>
    <artemis.version>2.4.0</artemis.version>
    <aspectj.version>1.8.13</aspectj.version>
    <assertj.version>3.9.1</assertj.version>
    <... 此处省略 .../>
</properties>

(2)、启动器(spring-boot-starter-web)

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

spring-boot-starter-web:spring-boot-starter—>spring-boot场景启动器;进入官网可以到有许多场景启动器,简单点说就是通过功能将相关Jar包给组合在起来,我们使用时只需要引入一个Web Starter就可以轻松搞定。
Spring Boot将所有的功能场景都抽取出来,做成一个个的starters(启动器),只需要在项目里面引入这些starter相关场景,所有依赖都会导入进来。要用什么功能就导入什么场景启动器。

点击web右边的Pom可以看到SpringBoot为我们依赖的其他jar包,帮我们导入了web模块正常运行所依赖的所有组件。如下:

 
  <dependencies>
<dependency>
  <groupId>org.springframework.boot</groupId>
  <artifactId>spring-boot-starter</artifactId>
</dependency>
<dependency>
  <groupId>org.springframework.boot</groupId>
  <artifactId>spring-boot-starter-tomcat</artifactId>
</dependency>
<dependency>
  <groupId>org.hibernate</groupId>
  <artifactId>hibernate-validator</artifactId>
</dependency>
<dependency>
  <groupId>com.fasterxml.jackson.core</groupId>
  <artifactId>jackson-databind</artifactId>
</dependency>
<dependency>
  <groupId>org.springframework</groupId>
  <artifactId>spring-web</artifactId>
</dependency>
<dependency>
  <groupId>org.springframework</groupId>
  <artifactId>spring-webmvc</artifactId>
</dependency>
  </dependencies>

(3)、主程序类(Java类):@SpringBootApplication:此注解声明的类,是SpringBoot的主配置类,SpringBoot就应该运行这个类的main方法来启动SpringBoot。

//@ImportResource(locations={"classpath:bean.xml"})
//@SpringBootApplication 来标注一个主程序类,说明这是一个SpringBoot应用
@SpringBootApplication
public class HellowordQuickStartApplication {
 
    public static void main(String[] args) {
        /*SpringBoot应用启动项
          HellowordQuickStartApplication.class 参数必须是用@SpringBootApplication注解修饰的类
         */
        SpringApplication.run(HellowordQuickStartApplication.class, args);
    }
}

@SpringBootApplication(主要由:@SpringBootConfiguration/@EnableAutoConfiguration/@ComponentScan 组成)

@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)、@SpringBootConfiguration:标注在某个类上,表示此类是一个Spring Boot的配置类。由以下注解组合形成:

@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Configuration //表示此类是一个配置类  是spring的一个组件
public @interface SpringBootConfiguration {
}

配置类 == 配置文件,配置类也是容器的一个组件,底层由@Component等等组成。
2)、@EnableAutoConfiguration:开启自动配置功能。也是一个组合注解,由以下注解组成(部分重要注解):

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

2.1)、@AutoConfigurationPackage:自动依赖相关的配置包,也是一个组合注解,主要由@import等注解组合

@Import({Registrar.class})//给容器中导入一个组件;导入的组件由此组建决定。
public @interface AutoConfigurationPackage {

进入@Import(Registrar.class)中的Registrar类中,通过断点,可以查看到我注释的一些信息。

static class Registrar implements ImportBeanDefinitionRegistrar, DeterminableImports {
    Registrar() {
    }
    //registerBeanDefinitions方法中的metadata可以查看到我们启动类使用的注解 @SpringBootApplication
    public void registerBeanDefinitions(AnnotationMetadata metadata, BeanDefinitionRegistry registry) {
        AutoConfigurationPackages.register(registry, new String[]{(new AutoConfigurationPackages.PackageImport(metadata)).getPackageName()});
    }
    //new AutoConfigurationPackages.PackageImport(metadata) 可以解析出我们当前主启动所在的package包
    public Set<Object> determineImports(AnnotationMetadata metadata) {
        return Collections.singleton(new AutoConfigurationPackages.PackageImport(metadata));
    }
}

结论:@Import(Registrar.class)作用:将主配置类的所在包以及下边所有子包里面的所有组件扫描到Spring容器中。这也就能理解为什么会自动扫描我们写的@Controller类了。
2.2)、@Import(AutoConfigurationImportSelector.class):进入AutoConfigurationImportSelector.class类中,查看如下方法:

public String[] selectImports(AnnotationMetadata annotationMetadata) {
        if(!this.isEnabled(annotationMetadata)) {
            return NO_IMPORTS;
        } else {
            try {
                AutoConfigurationMetadata autoConfigurationMetadata = AutoConfigurationMetadataLoader.loadMetadata(this.beanClassLoader);
                AnnotationAttributes attributes = this.getAttributes(annotationMetadata);
                // 主要用到的是 这个 configurations 后面会有重点说明
                List<String> configurations = this.getCandidateConfigurations(annotationMetadata, attributes);
                configurations = this.removeDuplicates(configurations);
                configurations = this.sort(configurations, autoConfigurationMetadata);
                Set<String> exclusions = this.getExclusions(annotationMetadata, attributes);
                this.checkExcludedClasses(configurations, exclusions);
                configurations.removeAll(exclusions);
                configurations = this.filter(configurations, autoConfigurationMetadata);
                this.fireAutoConfigurationImportEvents(configurations, exclusions);
                return StringUtils.toStringArray(configurations);
            } catch (IOException var6) {
                throw new IllegalStateException(var6);
            }
        }
    }

这是导入组件的选择器方法,将所有需要导入的组件以全类名的方式返回,这些组件最终被添加到容器中。其中List<String> configurations会给容器中导入非常多的自动配置类(xxxAutoConfiguration);就是给容器中导入这个场景需要的所有组件,并配置好这些组件。有了自动配置类,免去了我们手动编写配置注入功能组件等的工作;自动配置类共109个,如下部分所示:
☹ 那么我们就有疑问,这些自动配置类都是从哪里来的?
  ▶ 进入这个方法:this.getCandidateConfigurations(annotationMetadata, attributes)

protected List<String> getCandidateConfigurations(AnnotationMetadata metadata, AnnotationAttributes attributes) {
      // *** 后边需要了解的方法 ***
      //SpringFactoriesLoader.loadFactoryNames(EnableAutoConfiguration.class,classLoader);
      List<String> configurations = SpringFactoriesLoader.loadFactoryNames(this.getSpringFactoriesLoaderFactoryClass(), this.getBeanClassLoader());
      Assert.notEmpty(configurations, "No auto configuration classes found in META-INF/spring.factories. If you are using a custom packaging, make sure that file is correct.");
      return configurations;
  }

▶ 进入SpringFactoriesLoader.loadFactoryNames(EnableAutoConfiguration.class,classLoader)方法,具体注释说明:

public static List<String> loadFactoryNames(Class<?> factoryClass, @Nullable ClassLoader classLoader) {
       //org.springframework.context.ApplicationContextInitializer
       String factoryClassName = factoryClass.getName();
       return (List)loadSpringFactories(classLoader).getOrDefault(factoryClassName, Collections.emptyList());
   }    
private static Map<String, List<String>> loadSpringFactories(@Nullable ClassLoader classLoader) {
       MultiValueMap<String, String> result = (MultiValueMap)cache.get(classLoader);
       if(result != null) {
           return result;
       } else {
           try {
               //通过类加载器(classLoader获取)META-INF/spring.factories(也就是配置了109个自动配置类的文件) 资源
               Enumeration<URL> urls = classLoader != null?classLoader.getResources("META-INF/spring.factories"):ClassLoader.getSystemResources("META-INF/spring.factories");
               LinkedMultiValueMap result = new LinkedMultiValueMap();

               while(urls.hasMoreElements()) {
                   URL url = (URL)urls.nextElement();
                   UrlResource resource = new UrlResource(url);
                   //将urls 当做一个properties配置文件
                   Properties properties = PropertiesLoaderUtils.loadProperties(resource);
                   Iterator var6 = properties.entrySet().iterator();

                   while(var6.hasNext()) {
                       Entry<?, ?> entry = (Entry)var6.next();
                       //将META-INF/spring.factories文件中的EnableAutoConfiguration下的配置进行加载   如下图所示
                       List<String> factoryClassNames = Arrays.asList(StringUtils.commaDelimitedListToStringArray((String)entry.getValue()));
                       result.addAll((String)entry.getKey(), factoryClassNames);
                   }
               }

               cache.put(classLoader, result);
               return result;
           } catch (IOException var9) {
               throw new IllegalArgumentException("Unable to load factories from location [META-INF/spring.factories]", var9);
           }
       }
   }

▶ 我们进入其中一个自动配置类中看看SpringBoot是不是真的帮我们已经配置好了一些属性(WebMvcAutoConfiguration)

//这里我就摘出一些重要的配置,来帮我我们观察即可。
@Configuration
public class WebMvcAutoConfiguration {
        @Bean
        @ConditionalOnMissingBean
        /*视图解析器 , SpringBoot中的所有配置文件都是.java形式,方法的名字,就是以前xml中的id。
        等等都是用注解表示的,这个我们后面会重点说明,这里就先了解一下*/
        //我们可以看到SpringBoot已经帮我们配置好了视图解析器 等等一些功能 我们直接使用就好
        public InternalResourceViewResolver defaultViewResolver() {
            InternalResourceViewResolver resolver = new InternalResourceViewResolver();
            resolver.setPrefix(this.mvcProperties.getView().getPrefix());
            resolver.setSuffix(this.mvcProperties.getView().getSuffix());
            return resolver;
        }
}

总结:SpringBoot在启动的时候从类路径下的META-INF/spring.factories中获取EnableAutoConfiguration指定的值,将这些值作为自动配置类导入到容器中,自动配置类就生效,帮我们进行自动配置工作。如此一来,就具有我们在SSM等环境下写了一大堆配置文件后才具有的功能。而这些所有配置文件都在spring-boot-autoconfigure-2.0.0.RELEASE.jar中。

六、使用Spring Initializer快速创建Spring Boot项目
▶ 注意:Artifact中不能大小写混合使用。

▶ 通过需求选择starts,例如选择Web。

▶ 我们就会发现pom.xml文件中,就会自动配置了我们在 三 种引入的starts。

<!-- 摘取一部分 -->    
<parent>
     <groupId>org.springframework.boot</groupId>
     <artifactId>spring-boot-starter-parent</artifactId>
     <version>2.0.0.RELEASE</version>
     <relativePath/> <!-- lookup parent from repository -->
 </parent>
 
 <properties>
     <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
     <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
     <java.version>1.8</java.version>
 </properties>
 <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>
 
     <dependency>
         <groupId>org.springframework.boot</groupId>
         <artifactId>spring-boot-configuration-processor</artifactId>
         <optional>true</optional>
     </dependency>
 </dependencies>

▶ 添加controller层:新注解@RestController == @ResponseBody与@Controller的合体

//这个类的所有方法返回的数据直接写给浏览器(如果是对象转为JSON)
/*@ResponseBody
@Controller*/
@RestController
public class HelloWordController {
    @RequestMapping("/hello")
    public String hello(){
        return "hell";
    }
}

▶ 优点:默认生成的SpringBoot项目,我们只需要编写自己的逻辑
▶ 默认生成的Resources配置文件的目录结构:
●  static:保存所有的静态资源。 (js/css/image)
●  templates:保存所有的模板页面(SpringBoot默认jar包使用嵌入式的Tomcat,默认不支持JSP页面)但可以使用模板引擎。(freemarker、thymeleaf)
●  application.properties:SpringBoot应用的配置文件。默认的配置都在此文件可以修改。

 

avatar

发表评论

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen: