了解springboot

什么是springboot

springboot是javaweb开发框架的演化,约定大约配置。

maven、spring、springMvc都是约定大于配置。

优点

  1. 约定大于配置;
  2. 内置tomcat;

什么是微服务

优点

  1. 节省调用资源;
  2. 每个功能都是一个可替换,可独立升级的资源

第一个springboot程序

helloWordDemo

@RestController
public class HelloController {
    @RequestMapping("/")
    public String hello() {
        return "hello springboot!";
    }
 
    @GetMapping("bay")
    public String bay() {
        return "bay springboot!";
    }
}
# 应用名称
spring.application.name=boot
# 应用服务 WEB 访问端口
server.port=80

访问路径:http://localhost/

SpringBoot返回json数据

@RestController
public class JsonHelloWorldController {
    @GetMapping("/getJson")
    public Person hello() {
        Person person = new Person();
        person.setAge(11);
        person.setList(Arrays.asList("1", "2", "3"));
        return person;
    }
}
 
//返回
{"name":null,"age":11,"birthday":null,"dog":null,"list":["1","2","3"],"map":null}

其实 Spring Boot 也是引用了 JSON 解析包 Jackson,那么自然我们就可以在 Demo 对象上使用 Jackson 提供的 json 属性的注解,对时间进行格式化,对一些字段进行忽略等等。

注解的区别

@Controller和@RestControll的区别

@RestController注解相当于@ResponseBody + @Controller合在一起的作用。

  1. 如果只是使用@RestController注解Controller,则Controller中的方法无法返回jsp页面,或者html,配置的视图解析器InternalResourceViewResolver不起作用,返回的内容就是Return 里的内容;
  2. 如果需要返回到指定页面,则需要用 @Controller配合视图解析器InternalResourceViewResolver才行。 如果需要返回JSON,XML或自定义mediaType内容到页面,则需要在对应的方法上加上@ResponseBody注解;

例:

  1. 使用@Controller注解
  • 在对应的方法上,视图解析器可以解析return 的jsp,html页面,并且跳转到相应页面;
  • 若返回json等内容到页面,则需要加@ResponseBody注解
  1. @RestController注解
  • 相当于@Controller+@ResponseBody两个注解的结合。
  • 返回json数据不需要在方法前面加@ResponseBody注解了
  • 使用@RestController这个注解,就不能返回jsp,html页面,视图解析器无法解析jsp,html页面

@SpringBootApplication注解作用

标注在某个类上说明这个类时SpringBoot的主配置类,SpringBoot就应该运行这个类的main方法来启动SpringBoot应用;

@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 {
   //xxxx
}

@ComponentScan

在spring中等同于XML的作用,自动扫描并加载符合条件的bean到IOC容器中。

@SpringBootConfiguration

springboot的配置类,标注在某个类上,表示这是一个SpringBoot的配置类;

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

配置类,对应spring的xml配置文件

@Component

spring中作为依赖注入的作用,说明启动类本身也是spring容器中的组件,负责启动应用;

@EnableAutoConfiguration

开启自动配置功能,开启后配置生效;

@AutoConfigurationPackage:自动配置包
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@Import({Registrar.class})
public @interface AutoConfigurationPackage {
    String[] basePackages() default {};
 
    Class<?>[] basePackageClasses() default {};
}
@Import:Spring底层注解,给容器中一个组件;

Registrar.class 作用:将主启动类的所在包及包下面所有子包里面的所有组件扫描到Spring容器 ;

@Import({AutoConfigurationImportSelector.class})

AutoConfigurationImportSelector :自动配置导入选择器

//AutoConfigurationImportSelector类获取候选者的配置
protected List<String> getCandidateConfigurations(AnnotationMetadata metadata, AnnotationAttributes attributes) {
    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;
}
//返回的就是我们最开始看的启动自动导入配置文件的注解类;EnableAutoConfiguration
protected Class<?> getSpringFactoriesLoaderFactoryClass() {
    return EnableAutoConfiguration.class;
}
 
//返回loadSpringFactories方法
public static List<String> loadFactoryNames(Class<?> factoryType, @Nullable ClassLoader classLoader) {
    ClassLoader classLoaderToUse = classLoader;
    if (classLoaderToUse == null) {
        classLoaderToUse = SpringFactoriesLoader.class.getClassLoader();
    }
    String factoryTypeName = factoryType.getName();
    return loadSpringFactories(classLoaderToUse).getOrDefault(factoryTypeName, Collections.emptyList());
}
 
 
public static final String FACTORIES_RESOURCE_LOCATION = "META-INF/spring.factories";
 
private static Map<String, List<String>> loadSpringFactories(ClassLoader classLoader) {
    //获得classLoader , 我们返回可以看到这里得到的就是EnableAutoConfiguration标注的类本身
    Map<String, List<String>> result = cache.get(classLoader);
    if (result != null) {
        return result;
    }
 
    result = new HashMap<>();
    try {
        //去获取一个资源 "META-INF/spring.factories"
        Enumeration<URL> urls = classLoader.getResources(FACTORIES_RESOURCE_LOCATION);
        //将读取到的资源遍历,封装成为一个Properties
        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();
                String[] factoryImplementationNames =
                    StringUtils.commaDelimitedListToStringArray((String) entry.getValue());
                for (String factoryImplementationName : factoryImplementationNames) {
                    result.computeIfAbsent(factoryTypeName, key -> new ArrayList<>())
                        .add(factoryImplementationName.trim());
                }
            }
        }
 
        // Replace all lists with unmodifiable lists containing unique elements
        result.replaceAll((factoryType, implementations) -> implementations.stream().distinct()
                          .collect(Collectors.collectingAndThen(Collectors.toList(), Collections::unmodifiableList)));
        cache.put(classLoader, result);
    }
    catch (IOException ex) {
        throw new IllegalArgumentException("Unable to load factories from location [" +
                                           FACTORIES_RESOURCE_LOCATION + "]", ex);
    }
    return result;
}

spring.factories文件是自动配置的根源,里面的类都注入到IOC容器中。

路径:org.springframework.boot.autoconfigure包下的META-INF/spring.factories;

结论:

  1. SpringBoot在启动的时候从类路径下的META-INF/spring.factories中获取EnableAutoConfiguration指定的值;
  2. 将这些值作为自动装配类导入到容器中,自动装配类就生效,进行自动装配工作;
  3. 整个J2EE的整体解决方案和自动配置都在org.springframework.boot.autoconfigure的jar包中;
  4. 会给容器中导入非常多的自动配置类(XXXAutoConfiguration),就是给容器中导入这个场景需要的组件,并配置好这些组件;
  5. 有了自动配置类,免去了手动编写配置注入功能组件的工作。

SpringApplication作用

  1. 推断应用的类型是普通的项目还是web项目;
  2. 查找并加载所有可用初始化器,设置到initiaizers属性中;
  3. 找出所有的应用程序监听器,设置到listeners属性中;
  4. 推断并设置main方法的定义类,找到运行的主类。

run方法流程

依赖

<!-- 父项目,springboot的版本的父类引用,里面引入了所有需要的依赖已经版本 -->
<parent>
  <groupId>org.springframework.boot</groupId>
  <artifactId>spring-boot-starter-parent</artifactId>
  <version>2.4.2</version>
  <relativePath/> <!-- lookup parent from repository -->
</parent>
<!--springboot启动器 -->
<dependency>
  <groupId>org.mybatis.spring.boot</groupId>
  <artifactId>mybatis-spring-boot-starter</artifactId>
</dependency>
<!-- springboot web服务器 -->
<dependency>
  <groupId>org.springframework.boot</groupId>
  <artifactId>spring-boot-starter-web</artifactId>
</dependency>

父项目:在spring中父项目中引用了许多常用的依赖以及中间件启动器,并绑定了版本,在使用时不需要绑定版本,若需要使用的版本在父项目中没有约定再手动绑定。

spring-boot-starter-web:导入web模块正常运行所依赖的组件;

Yaml语法

在SpringBoot中使用全局配置文件,配置文件名称是固定的:

  1. application.properties
    1. 语法结构:key=value
  2. application.yaml
    1. 语法结构:key:空格value

作用:修改SpringBoot自动配置的默认值,修改boot的自动配置已经配置好了端口等信息。

# 应用服务 WEB 访问端口
server.port=80
server:
  port: 80

yaml概述

语法要求严格,空格不可省略,以缩进进行控制层级关系,只要是左对齐的一列数据都是同一个层级,属性和值的而大小写都是敏感的。

server:
  port: 80
person:
  name: 张三
  age: 13
  birthday: 1997/12/13
  dog:
    name: 旺财
    age: 12
  list:
    - 1
    - 2
    - 3
  map: {1: 1,2: 2}
@Data
@Component
@ConfigurationProperties(prefix = "dog")
public class Dog {
    private String name;
    private int age;
}
 
@Data
@Component
@ConfigurationProperties(prefix = "person")
public class Person {
    private String name;
    private int age;
    private Date birthday;
    private Dog dog;
    private List<String> list;
    private Map<String, String> map;
}
 
//测试
@SpringBootTest
class BootApplicationTests {
 
    @Resource
    private Person person;
 
    @Test
    void contextLoads() {
        System.out.println(person);
    }
 
}
 
//输出
Person(name=张三, age=13, birthday=Sat Dec 13 00:00:00 CST 1997, dog=Dog(name=旺财, age=12), list=[1, 2, 3], map={1=1, 2=2})

松散绑定:yaml支持松散绑定,即若实体类中参数名为lastName,在yaml中可以为last-name表示。

@ConfigurationProperties(prefix = “person”)

将配置文件中配置的每一个属性的值,映射到这个组件(类)中,告诉springboot将本类中的虽有属性和配置文件中相关的配置进行绑定,参数prefix将配置文件中的person下面的属性一一对应。

使用该注解需要添加依赖:

<!-- 导入配置文件处理器,配置文件进行绑定就会有提示,需要重启 -->
<dependency>
  <groupId>org.springframework.boot</groupId>
  <artifactId>spring-boot-configuration-processor</artifactId>
  <optional>true</optional>
</dependency>

@PropertySource(value = “classpath:dog.properties”)

指定文件读取信息

@Value(”${name}”)

  1. @PropertySource注解使用指定文件下的属性值作为默认值;
@Data
@Component
@PropertySource(value = "classpath:dog.properties")
public class Dog {
    @Value("${dog.name}")
    private String name;
    private int age;
}
dog.name=xxx

注意:在编写properties文件时有可能保存会乱码,需要对idea配置进行修改settingsFileEncodeings

  1. 单独使用时,读取application.yaml文件下的属性值作为默认值。
@Data
@Component
public class Dog {
    @Value("${dog.name}")
    private String name;
    private int age;
}
dog:
  age: 12
  name: xxx

@ConfigurationProperties和@PropertySource/@Value区别

  1. @ConfigurationProperties只需要写一次即可 , @Value则需要每个字段都添加
  2. 松散绑定:如我的yml中写的last-name,这个和lastName是一样的, - 后面跟着的字母默认是大写的。这就是松散绑定。可以测试一下
  3. JSR303数据校验 , 这个就是我们可以在字段是增加一层过滤器验证 , 可以保证数据的合法性
  4. 复杂类型封装,yml中可以封装对象 , 使用value就不支持

yaml多模块

在yaml中若存在多套环境配置,不需要创建多个文件,可用过配置切换环境。

server:
  port: 80
 
#选择需要激活的模块
spring:
  profiles:
    active: dev
 
---
server:
  port: 81
spring:
  profiles: dev
 
---
server:
  port: 8080
spring:
  profiles: prod

注意:若yaml和properties配置了相同的内容,且没有激活其他环境,则默认使用properties配置文件内容。

JSR303数据校验及多环境切换

在springboot中可以用@validated进行数据校验,如果数据异常则则会统一抛出异常,方便异常中心统一处理。

<dependency>
  <groupId>org.hibernate</groupId>
  <artifactId>hibernate-validator</artifactId>
  <version>5.3.6.Final</version>
</dependency>
chenkdata:
  name: 123
@Data
@Component
@Validated
@ConfigurationProperties(prefix = "chenkdata")
public class CheckData {
    @Email(message = "不是合法邮件")
    private String name;
}
 
//输出
HV000030: No validator could be found for constraint 'javax.validation.constraints.Email' validating type 'java.lang.String'. Check configuration for 'name'

常见校验

@NotNull(message="名字不能为空")
private String userName;
@Max(value=120,message="年龄最大不能查过120")
private int age;
@Email(message="邮箱格式错误")
private String email;
 
空检查
@Null       验证对象是否为null
@NotNull    验证对象是否不为null, 无法查检长度为0的字符串
@NotBlank   检查约束字符串是不是Null还有被Trim的长度是否大于0,只对字符串,且会去掉前后空格.
@NotEmpty   检查约束元素是否为NULL或者是EMPTY.
    
Booelan检查
@AssertTrue     验证 Boolean 对象是否为 true  
@AssertFalse    验证 Boolean 对象是否为 false  
    
长度检查
@Size(min=, max=) 验证对象(Array,Collection,Map,String)长度是否在给定的范围之内  
@Length(min=, max=) string is between min and max included.
 
 
日期检查
@Past       验证 Date 和 Calendar 对象是否在当前时间之前  
@Future     验证 Date 和 Calendar 对象是否在当前时间之后  
@Pattern    验证 String 对象是否符合正则表达式的规则

多配置文件

在编写配置文件时,可以做多个环境文件配置,作为多环境切换,如:开发,测试,生产等。

appilcation-test.properties、application-dev.properties等。

springboot中启动默认使用application.properties,可在该文件中修改配置,指定配置文件的使用:

#激活application-dev.properties文件作为环境配置的唯一选项
spring.profiles.active=dev

配置文件加载位置

springboot启动会扫以下位置的application.properties会yaml文件作为默认配置

:::info 优先级1:项目路径下的config文件夹配置文件

优先级2:项目路径下配置文件

优先级3:资源路径下的config文件夹配置文件

优先级4:资源路径下配置文件

:::

优先级由高到低,高优先级覆盖低优先级的配置。

自动配置原理

springboot中存在大量的配置,在spring加载时会根据不同的注解判断配置类是否生效。

  • 一旦配置类生效,这个配置类就会给容器中添加各种组件;
  • 这些组件的数据从对应的properties类中获取,这些类里的每一个属性有和配置文件绑定的;
  • 所有在配置文件中能配置的属性都是在xxxProperties类中封存着;
  • 配置文件能配置什么就可以参照某功能对应的属性类来配置

xxxAutoConfiguration:自动配置类,给容器中添加组件;

xxxProperties:封装配置文件中相关属性;

@Conditional

自动配置类必须在一定条件下才能生效。

@Conditional派生注解,必须在指定条件成立,才会给容器中添加组件,配置里的所有内容才生效。

满足条件才会生效,则也会存在不满足条件但加载了的配置类,可以通过debug=true属性,控制台打印自动配置报告。

Positive matches:(自动配置类启用的:正匹配)

Negative matches:(没有启动,没有匹配成功的自动配置类:负匹配)

Unconditional classes: (没有条件的类)

构建Restful Api

  • @Controller:修饰class,用来创建处理http请求的对象;
  • @RestController:Spring4之后加入的注解,原来在@Controller中返回json需要@ResponseBody来配合,如果直接用@RestController替代@Controller就不需要再配置@ResponseBody,默认返回json格式;
  • @RequestMapping:配置url映射。现在更多的也会直接用以Http Method直接关联的映射注解来定义,比如:GetMappingPostMappingDeleteMappingPutMapping等。
@RestController
@RequestMapping("/user")
public class RestFulController {
    private static final Map<String, User> userMap = getData();
 
    public static Map<String, User> getData() {
        Map<String, User> userMap = new HashMap<>();
        User user1 = new User();
        user1.setUserName("张三");
        user1.setUid("1");
        user1.setPassword("张三123");
        userMap.put(user1.getUserName(), user1);
        user1 = new User();
        user1.setUserName("李四");
        user1.setUid("2");
        user1.setPassword("李四123");
        userMap.put(user1.getUserName(), user1);
        user1 = new User();
        user1.setUserName("王五");
        user1.setUid("3");
        user1.setPassword("王五123");
        userMap.put(user1.getUserName(), user1);
        return userMap;
    }
 
    @GetMapping("/")
    public List<User> getUsers() {
        return new ArrayList<>(userMap.values());
    }
 
    @PostMapping("/addOrUpd")
    public int addOrUpd(User user) {
        User userOld = userMap.get(user.getUserName());
        if (Objects.nonNull(userOld)) {
            userOld.setUid(user.getUid());
            userOld.setPassword(user.getPassword());
            return 2;
        } else {
            userMap.put(user.getUserName(), user);
            return 1;
        }
    }
 
    @DeleteMapping("/delete")
    public int delete(String userName) {
        User user = userMap.get(userName);
        if (Objects.nonNull(user)) {
            userMap.remove(userName);
            return 1;
        } else {
            return 0;
        }
    }
}

构建Swagger2接口文档

<dependency>
  <groupId>io.springfox</groupId>
  <artifactId>springfox-swagger-ui</artifactId>
  <version>2.9.2</version>
</dependency>
<!--doc.html模式        -->
<dependency>
  <groupId>com.github.xiaoymin</groupId>
  <artifactId>swagger-bootstrap-ui</artifactId>
  <version>1.9.2</version>
</dependency>