(Spring Boot教程二 )关于pom.xml文件和注解的一些深入探索

    紧接着上一个教程博客(Spring Boot教程一 )Spring Boot入门(简介,一个HelloWorld项目构建、内部原理分析)。上一篇博客我们编辑了项目的pom.xml文件,并在编写项目的时候为类、方法添加了一些注解,那么这些内容是如何在Spring Boot当中工作的呢?他们的的作用又是什么?作为一个纯新手,我表示十分好奇。接下来的内容基于网络资料和自己的理解,可能不是很官方,如果有什么地方理解不到位,希望看到的大佬帮我及时指出,谢谢。

一、关于pom.xml文件

    回顾一下我们之前配置的pom.xml文件,完整版如下图。pom文件是maven项目的配置基础,包括版本号、artifactId等多个信息,它让我们的项目自动导入了很多相关的依赖,也就是我们在项目的External Library中所看见的jar包们。

    在之前的HelloWorld的项目中,我们主要填充的部分是父pom、依赖组、项目构建所需依赖,即下图画框的这三个部分。其他内容皆为项目自动生成。这三个部分就足够支撑我们的项目运行起来。我们这里也会分成三部分介绍。

1、父pom:依赖管理。我们现在使用的是org.springframework.boot中的spring-boot-starter-parent作为我们的父pom,我们ctrl点击进去,发现spring-boot-starter-parent的父pom是spring-boot-dependencies。在spring-boot-dependencies的文件中,我们可以看到规定了一些依赖的版本号,也有人因此称这个文件为Spring Boot版本的仲裁中心。

    但注意两点。

(1)不是这里的所有依赖都是会被引入进来的。项目具体会引入哪些依赖还是要看我们dependencies里写了什么,所以这里很多地方会报红,不要担心。

(2)如果我们需要引入的依赖这里没有出现,还是需要写版本号的。

    比如,我们在项目的pom文件中引入的spring-boot-starter-web,在pring-boot-dependencies中可以看到它的完整引入,如下。

2、依赖pring-boot-starter-web。这是spring boot的web场景启动器,我们仍然打开这个文件,看到下图中的内容。如果我们的项目引入了这个依赖,就相当于我们导入了所有能使web模块正常运行的相关组件依赖,依赖版本受父pom仲裁。

    这里我们有必要单独说一下starter——启动器,Spring Boot包含了很多针对于不同场景的启动器。我们在Spring Boot的官网上可以看到不同版本的Spring Boot,我们随便选择一个,查看它的Reference,如下图。

    启动器是一些项目中的一些列依赖。Spring Boot将一些常见的场景(功能)及它所需要的一些依赖进行抽取,做成启动器,那么只需要引入启动器,就可以引入全部的相关依赖。

    在这个文档里也可以看到里面介绍了Spring Boot这个版本的相关信息,包含的不同Starter和它的简要描述以及pom引入方法。

3、maven构建配置:Spring Boot的maven插件。里面是maven的一些相关的依赖。如果没有这个部分,我们还是可以将项目打包成jar包,但是,我们无法使用“java -jar 包名”进行布置,会报错。

二、关于@SpringBootApplication注解

    我们主要起到作用的注解是@SpringBootApplication,这是一个Spring Boot应用注解,用于标注一个类,说明这是程序的主程序类,也是程序的入口,程序应该从这个类的main方法进入。通过这个注解我们完成了依赖的导入和项目的运行的一些配置,使我们的Spring Boot使用起来更加方便。

    我们通过ctrl+鼠标点击进入这个文件。和我们前面分析pom文件类似,因为我们前面需要研究的是配置之间的关系,所以我们点进文件中主要看到是文件的配置信息,这里我们点进去主要关注的是文件的注解。@SpringBootApplication文件的完整注解如下图,大致分为两个部分,四个元注解和其他的注解。四个元注解是我们看Spring Boot注解文件时经常看到的,它们专门用来注解注解文件,我们先来介绍他们,然后介绍真正让@SpringBootApplication起到作用的另外三个注解。

    元注解归属于java.lang.annotation这个包下,我们在IDEA的左侧边栏看一下这个包的情况,如下图,因为java的包太多,所以我们的图片展示是拼接而成,如果你想要直接看到这个包,点任意一个元注解的源文件上面的包信息就可以看到。这个包内一共有六个注解文件,但只有@Target、@Retention、@Documented、@Inherited四个被称为元注解。

1、元注解@Target({ElementType.TYPE}):表示这个文件所定义的注解文件是用来注解类、接口、枚举、注解类型,事实上,它所定义的注解@SpringBootApplication的确是用来注解Spring Boot主入口类的。

    我们来更详细地说一下@Target这个注解。我们之前说过元注解是专门用来注解注解文件的。这个注解的直译是目标,也就是说,@Target生命这个注解作用的范围是什么,它可以注解什么。后面只允许带一个枚举类型的ElementType参量。这个参量一共有10个类型,我们点进去看看它的源码。下图我做了一些标注,前8个除了ANNOTATION_TYPE之外都是比较常用的,我们在Spring Boot注解文件中通常看到的都是TYPE类型。

2、元注解@Retention(RetentionPolicy.RUNTIME),Retention原译为滞留,这个注解是用来告诉编译器所定义的注解的保留时间,里面仍然只能带一个枚举类型变量,有三个取值。在解释这三个取值时,我们必须要了解java运行的几个步骤,一个是java文件在运行前会被编译器编译成.class文件;一个是java的反射机制,允许java获取自身信息。简单来理解反射机制的一个功能,就是你在编译器中输入@,编译器就会自动帮你联想到所有当前类可引用的注释信息一样。

    下面我们来解释这三个枚举变量的取值,对所定义注解所产生的影响。

3、元注解@Documented:被标注的注解所标注的注解会被javadoc记录,并成为API的一部分。举个例子,注解A被标注@Documented,B被A标注,则用javadoc对B类生成API文档时,在类声明上会出现注解A。

4、元注解@Inherited:说明定义的标注所标注的子类可以继承这个标注,比如注解A被@Inherited标注,类B被注解A标注,类C继承类B,那么即使类C的文件中不出现注解A依然默认继承了这个注解。

5、@SpringBootConfiguration:Spring Boot配置文件,表示所标注的类为一个Spring Boot配置类。我们依次往下点击文件内容,如下图,可以看出来@SpringBootConfiguration的底层是Spring的@Configuration配置标注,用来标注Spring配置类的一个注解。配置类实际上就是配置文件,是一个组件,相当于更加便利地实现配置文件的功能。

6、@EnableAutoConfiguration:启动自动配置注解,使Spring Boot开启自动配置,进行组件扫描等操作。它的文件内部有两个注解,如下图。@AutoConfigurationPackage为自动配置包注解,帮助编译器获得@SpringBootApplication所标记的主配置类所在目录下的所有组件。@Import({AutoConfigurationImportSelector.class})自动配置选择器,为容器导入该场景下的所需要的自动配置组件,并以全类名的方式返回,并配置好这些组件。

    这里先展示一下相关文件的一个调用关系和起到作用的函数:

(1)我们详细来看一下@AutoConfigurationPackage是如何实现功能的。我们点击获得@AutoConfigurationPackage的文件内容,其中含有@Import({Registrar.class})的注解。@Import是Spring的底层注解,表示引入()中的类,那么表示起作用的即为Registrar.class。我们点开Registrar.class,里面有一个函数为registerBeanDefinitions(),这个函数起到的作用就是获得主配置类下全部的组件放入注册器中。下面是registerBeanDefinitions()的完整代码:

static class Registrar implements ImportBeanDefinitionRegistrar, DeterminableImports {
        Registrar() {
        }

        public void registerBeanDefinitions(AnnotationMetadata metadata, BeanDefinitionRegistry registry) {
            AutoConfigurationPackages.register(registry, (new AutoConfigurationPackages.PackageImport(metadata)).getPackageName());
        }

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

        通过调试,我们可以看到metadata和执行后的registry的数据。metadata里面的introspectedClass里面是我们的项目名称,(new AutoConfigurationPackages.PackageImport(metadata)).getPackageName()经过计算后的结果为com.springboot,即我们通过这个程序语句获得了项目所在的包,然后我们将包下的组件注入到registry中,可以看到beanDefinitionMap中已经注入了我们项目中的两个组件。

(2)接下来我们看一下@Import({AutoConfigurationImportSelector.class})中的AutoConfigurationImportSelector.class自动配置引用选择器如何工作。AutoConfigurationImportSelector.class中主要起到作用的getAutoConfigurationEntry函数。因为这个函数在我查阅资料的过程中,发现内容有所出入,所以我这里就不粘函数的代码了,还是以你当前的版本为主。但是基本上的返回值都与一个List<String>有关,我们通过调试之后发现返回参数内容如下,都是一些自动配置的文件。

7、@ComponentScan:依然在这里介绍一篇很全面的博客——Spring注解——使用@ComponentScan自动扫描组件,@ComponentScan主要就是定义扫描的路径从中找出标识了需要装配的类自动装配到spring的bean容器中,我们也可以使用这个注解排除一些不需要装配的类。

    @ComponentScan源文件中展示的参数有很多,但主要用到的参数只有四个:value、includeFilters()、excludeFilters、useDefaultFilters()。value确认扫描范围,includeFilters()和excludeFilters()可用于指定加载和排除哪些装配,useDefaultFilters()的值默认为true,即默认扫描指定路径下的所有Component,像@Controller、@Service、@Repository等注解的底层都是@Component,所以也会被扫描进去。

   单独说一下includeFilters()和excludeFilters(),它所指定的类型都是@Filter的,所以我们一起看,@Filter代码比较简单,如下

public @interface Filter {
        FilterType type() default FilterType.ANNOTATION;

        @AliasFor("classes")
        Class<?>[] value() default {};

        @AliasFor("value")
        Class<?>[] classes() default {};

        String[] pattern() default {};
    }

    classes用来放class文件,里面是关于装配类的限制。type类型为FilterType,源代码如下,通常使用的是ANNOTATION和CUSTOM。

三、关于Controller文件中的注解

    Controller文件中的注解相比主配置类的简单一些。下面的代码是我们之前项目中的Controller代码,有三个注解,@Controller、@ResponseBody、@RequestMapping这三个注解。

package com.springboot.controller;

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;

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

1、@Controller表示这是一个控制类,用于控制类之前使用,底层是Spring的@Component组件。

2、@ResponseBody响应体注解。首先我们要先对REST API表现层状态转移有一定的了解。其实就是一种构建风格,浅层理解上来说,就是从传统的页面跳转,编程直接进行页面请求,然后将数据交给网页进行加载。以我们的项目为例子,我们直接通过网址向网页发出“\hello”界面的请求,然后把“hello world”信息交给网页,让其进行展示。

     那么@ResponseBody是REST API开发的一个必要的注解。它可用于标记在方法上或者是类上,如果加在类上表示这个类的所有方法返回的数据直接写给浏览器。@ResponseBody和@Controller组合使用标记类的时候可以直接使用@RestController替代。

    @ResponseBody的作用有两个,转自@RequestBody, @ResponseBody 注解详解

(1)用于读取Request请求的body部分数据,使用系统默认配置的HttpMessageConverter进行解析,然后把相应的数据绑定到要返回的对象上;

(2)再把HttpMessageConverter返回的对象数据绑定到 controller中方法的参数上。

3、@RequestMapping:用于标记方法,会将 HTTP 请求映射到 MVC 和 REST 控制器的处理方法上,也是REST API开发的重要注解。

猜你喜欢

转载自blog.csdn.net/zhanggonglalala/article/details/89137065