Spring Boot核心知识点小结(下)

简介: Spring Boot核心知识点小结(下)

Spring Boot核心知识点小结(上)

https://developer.aliyun.com/article/1493706?spm=a2c6h.13148508.setting.14.1e9b4f0eQyphEf

通过@ConfigurationProperties读取并与 bean 绑定

这种方式相较于上面那种更加强大,可以与bean绑定,例如我们yml的配置文件内容如下(注意配置名称必须全小写,否则会报一些奇怪的错误)

myobj:
  name: out-side-config
  email: out-side-config@qq.com

那么我们就可以编写一个类,代码如下所示,使用ConfigurationProperties引入前缀为myobj的配置内容即可,该配置就会将myobj前缀下的所有配置和我们的类绑定

/**
 * 注意 yml配置文件不能有大写字母
 */
@ConfigurationProperties(prefix = "myobj")
public class MyObj {
    private String name;
    private String email;
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    public String getEmail() {
        return email;
    }
    public void setEmail(String email) {
        this.email = email;
    }
    @Override
    public String toString() {
        return "MyObj{" +
                "name='" + name + '\'' +
                ", email='" + email + '\'' +
                '}';
    }
}
@PropertySource读取指定的 properties 文件

有时候我们希望指定配置文件和类进行绑定,那么我们就可以使用PropertySource注解,例如我们在resource目录下有个student.properties文件,内容为

name:xiaoming
no:18 

我们只需使用PropertySource执行路径以及配置文件名,再配合value即可完成属性绑定。

@Component
@PropertySource("classpath:student.properties")
public class Student {
    @Value("${name}")
    private String name;
    @Value("${no}")
    private String no;
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    public String getNo() {
        return no;
    }
    public void setNo(String no) {
        this.no = no;
    }
    @Override
    public String toString() {
        return "Student{" +
                "name='" + name + '\'' +
                ", no='" + no + '\'' +
                '}';
    }
}
Spring Boot 加载配置文件的优先级了解么?

答: 以下图为例,读取顺序优先取外层config目录下的yml文件,然后是resource目录下的configyml文件,最后才是resource目录下的yml配置文件。

如下图,以笔者为例,笔者在在不同路径下都配置了yml文件,最外层内容为

myConfig: 这个是helloworld配置的具体内容哦
myobj:
  name: out-side-config
  email: out-side-config@qq.com

然后我们通过测试单元查看结果,读取的配置取的是最外层

@SpringBootTest
class DemoApplicationTests {
  @Resource
  private MyObj myObj;
  
  @Test
  void contextLoads() {
    //输出结果 MyObj{name='out-side-config', email='out-side-config@qq.com'}
    System.out.println(myObj);
    
  }
}
常用的 Bean 映射工具有哪些?能不能给我说说你最常用的是那种?

答: 常见的是:MapStructModelMapperDozerOrikaJMapper 这几种吧。

最常用的还是MapStruct,它的工作原理也很简单,我们声明一个转换接口后,它会在编译期为了我们生成转换实现类的字节码文件。

对此我们不妨距离一下它的使用方式,首先引入版本号、依赖、插件

版本号

<properties>
        <java.version>1.8</java.version>
        <org.mapstruct.version>1.5.3.Final</org.mapstruct.version>
    </properties>

依赖

<!--mapstruct依赖-->
        <dependency>
            <groupId>org.mapstruct</groupId>
            <artifactId>mapstruct</artifactId>
            <version>${org.mapstruct.version}</version>
        </dependency>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <version>1.18.12</version>
        </dependency>

插件

<plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <version>3.8.1</version>
                <configuration>
                    <source>1.8</source>
                    <target>1.8</target>
                    <annotationProcessorPaths>
                        <path>
                            <groupId>org.mapstruct</groupId>
                            <artifactId>mapstruct-processor</artifactId>
                            <version>${org.mapstruct.version}</version>
                        </path>
                    </annotationProcessorPaths>
                </configuration>
            </plugin>

例如我们现在有个Doctor 希望转为DoctorDTO类,代码如下所示

public class Doctor {
    private Integer id;
    private String name;
    private String srcAddr;
    public Integer getId() {
        return id;
    }
    public void setId(Integer id) {
        this.id = id;
    }
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    public String getSrcAddr() {
        return srcAddr;
    }
    public void setSrcAddr(String srcAddr) {
        this.srcAddr = srcAddr;
    }
    @Override
    public String toString() {
        return "Doctor{" +
                "id=" + id +
                ", name='" + name + '\'' +
                ", srcAddr='" + srcAddr + '\'' +
                '}';
    }
}

DoctorDTO 类,可以看出地址的字段名为dstAddr,和上面的srcAddr有区别

public class DoctorDTO {
    private Integer id;
    private String name;
    private String dstAddr;
    public Integer getId() {
        return id;
    }
    public void setId(Integer id) {
        this.id = id;
    }
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    public String getDstAddr() {
        return dstAddr;
    }
    public void setDstAddr(String dstAddr) {
        this.dstAddr = dstAddr;
    }
    @Override
    public String toString() {
        return "DoctorDTO{" +
                "id=" + id +
                ", name='" + name + '\'' +
                ", dstAddr='" + dstAddr + '\'' +
                '}';
    }
}

所以我们编写一个接口,如下所示,对于字段名不一样的,我们使用Mapping手动配置映射关系

import org.mapstruct.Mapper;
import org.mapstruct.Mapping;
import org.mapstruct.factory.Mappers;
@Mapper
public interface DoctorMapper {
    DoctorMapper INSTANCE = Mappers.getMapper(DoctorMapper.class);
    /**
     * 会在编译期生成
     * @param doctor
     * @return
     */
    @Mapping(source = "srcAddr", target = "dstAddr")
    DoctorDTO toDTO(Doctor doctor);
}

测试代码,可以看到bean转换完成

@Test
  public void testToDTO() {
    Integer doctorId = 15;
    String doctorName = "xiaoming";
    Doctor doctor = new Doctor();
    doctor.setId(doctorId);
    doctor.setName(doctorName);
    doctor.setSrcAddr("中国北京");
    DoctorDTO doctorDTO = DoctorMapper.INSTANCE.toDTO(doctor);
    // 输出结果 DoctorDTO{id=15, name='xiaoming', dstAddr='中国北京'}
        System.out.println(doctorDTO);
    assertEquals(doctorId, doctorDTO.getId());
    assertEquals(doctorName, doctorDTO.getName());
  }

通过源码我们可以看到这个接口的实现类会在编译器生成

Spring Boot 如何监控系统实际运行状况?

答: 很简单,引入下面这个依赖

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

然后键入下面的地址即可查看对应端点的信息

http://localhost:8080/actuator

具体可以参考下面这篇文章

集成Spring Boot Actuator很简单,难的是运用场景!

Spring Boot 如何做请求参数校验?能不能给我说说怎么使用。

答: 有两种校验框架,一个是Hibernate Validator,还有一个是JSR(Java Specification Requests)校验,后者比较常用,无需引入特殊的依赖。就例如我们现在有个Person类,希望名字不为空,性别是是数字最大值为2,而email必须为邮箱格式,那么我们就可以基于JSR的注解进行说明。

public class Person {
    @NotNull(message = "姓名不可为空")
    @Size(max = 10, message = "姓名长度不可超过10位")
    private String name;
    @Max(value = 2, message = "性别最大值只能为2")
    private int sex;
    @Email(message = "邮箱格式不正确")
    private String email;
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    public int getSex() {
        return sex;
    }
    public void setSex(int sex) {
        this.sex = sex;
    }
    public String getEmail() {
        return email;
    }
    public void setEmail(String email) {
        this.email = email;
    }
    @Override
    public String toString() {
        return "Person{" +
                "name='" + name + '\'' +
                ", sex='" + sex + '\'' +
                ", email='" + email + '\'' +
                '}';
    }
}

当他作为controllerrequestBody的参数时,用法如下所示

@PostMapping("/test/hello")
    public void hello(@Valid Person person) {
        logger.info("hello {}", person.getName());
    }

假如我们想校验路径参数时,我们只需在Controller上方加一个注解@Validated,然后对于路径参数加入校验注解Valid +校验规则注解即可即可。

@GetMapping("/test/hello2/{id}")
    public void hello2(@Valid @PathVariable("id") @Max(value = 5,message = "最大值为5") Integer  id) {
        logger.info("hello {}", id);
    }

补充一下常见的一些校验注解:

1. @NotEmpty 被注释的字符串的不能为 null 也不能为空
2. @NotBlank 被注释的字符串非 null,并且必须包含一个非空白字符
3. @Null 被注释的元素必须为 null
4. @NotNull 被注释的元素必须不为 null
5. @AssertTrue 被注释的元素必须为 true
6. @AssertFalse 被注释的元素必须为 false
7. @Pattern(regex=,flag=)被注释的元素必须符合指定的正则表达式
8. @Email 被注释的元素必须是 Email 格式。
9. @Min(value)被注释的元素必须是一个数字,其值必须大于等于指定的最小值
10. @Max(value)被注释的元素必须是一个数字,其值必须小于等于指定的最大值
11. @DecimalMin(value)被注释的元素必须是一个数字,其值必须大于等于指定的最小值
12. @DecimalMax(value) 被注释的元素必须是一个数字,其值必须小于等于指定的最大值
13. @Size(max=, min=)被注释的元素的大小必须在指定的范围内
14. @Digits(integer, fraction)被注释的元素必须是一个数字,其值必须在可接受的范围内
15. @Past被注释的元素必须是一个过去的日期
16. @Future 被注释的元素必须是一个将来的日期
如何使用 Spring Boot 实现全局异常处理?

答:通过@ControllerAdvice将控制器声明为增强器,然后通过ExceptionHandler 对自己自己的异常进行处理。

例如我们想处理所有控制器的BindException,代码如下所示

/**
 * 统一异常处理、数据预处理等
 */
@ControllerAdvice
public class ControllerExceptionHandler {
    private static final Logger LOG = LoggerFactory.getLogger(ControllerExceptionHandler.class);
    /**
     * 校验异常统一处理
     * @param e
     * @return
     */
    @ExceptionHandler(value = BindException.class)
    @ResponseBody
    public CommonResp validExceptionHandler(BindException e) {
        CommonResp commonResp = new CommonResp();
        LOG.warn("参数校验失败:{}", e.getBindingResult().getAllErrors().get(0).getDefaultMessage());
        commonResp.setSuccess(false);
        commonResp.setMessage(e.getBindingResult().getAllErrors().get(0).getDefaultMessage());
        return commonResp;
    }
}
Spring Boot 中如何实现定时任务 ?

答: 操作步骤很简单,首先在启动类中添加@EnableScheduling注解,然后编写一个定时任务bean,然后在定时任务的方法上添加@Scheduled注解

@Component
@EnableAsync
//@EnableAsync 和 @Async 使定时任务并行执行
public class AsyncScheduledTasks {
    private static final Logger log = LoggerFactory.getLogger(AsyncScheduledTasks.class);
    private static final SimpleDateFormat dateFormat = new SimpleDateFormat("HH:mm:ss");
    private List<Integer> index = Arrays.asList(6, 6, 2, 3);
    int i = 0;
    @Scheduled(fixedRate = 5000)
    @Async
    public void reportCurrentTimeWithFixedRate() {
        log.info("Current Thread : {}", Thread.currentThread().getName());
        if (i == 0) {
            log.info("Start time is {}", dateFormat.format(new Date()));
        }
        if (i < 4) {
            try {
                TimeUnit.SECONDS.sleep(index.get(i));
                log.info("Fixed Rate Task : The time is now {}", dateFormat.format(new Date()));
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            i++;
        }
    }
}

参考文献


目录
相关文章
|
7月前
|
监控 Java 应用服务中间件
谈谈你对spring boot 3.0的理解
1. Java 版本要求:Spring Boot 3.0 要求使用 Java 17 或更高版本,这可能会对一些仍在使用旧版 Java 的项目造成兼容性问题。需要确保项目使用的 Java 版本符合要求,并考虑是否需要升级 JDK 版本。 2. 底层依赖项迁移:Spring Boot 3.0 将所有底层依赖项从 Java EE 迁移到了 Jakarta EE API,基于 Jakarta EE 9 并尽可能地兼容 Jakarta EE 10。这可能会对一些使用了 Java EE 的应用造成影响,需要进行相应的修改和调整。 3. 插件和库的支持:尽管 Spring Boot 3.0 支持更多的插件和
170 0
|
7月前
|
Java 应用服务中间件 Go
《Spring Boot框架原理》
《Spring Boot框架原理》
61 1
|
8月前
|
消息中间件 Java 数据安全/隐私保护
Spring Boot 中的 AmqpTemplate 是什么,原理,如何使用
Spring Boot 中的 AmqpTemplate 是什么,原理,如何使用
|
9天前
|
Java 应用服务中间件 API
Spring Boot核心知识点小结(上)
Spring Boot核心知识点小结
21 0
|
10天前
|
XML 前端开发 Java
Spring核心面试题小结
Spring核心面试题小结
10 0
|
6月前
|
开发框架 Java 数据安全/隐私保护
Spring Boot插件化开发概念原理及实现
Spring Boot插件化开发概念原理及实现
226 0
|
10月前
|
SQL XML Java
Spring核心知识点(四)
事务是逻辑上的一组操作,要么都执行,要么都不执行。
36 1
|
10月前
|
Java 数据库连接 数据库
Spring核心知识点(三)
Spring对数据库的操作在jdbc上面做了深层次的封装,使用spring的注入功能,可以把DataSource注册到JdbcTemplate之中。
74 1
BXA
|
12月前
|
消息中间件 缓存 NoSQL
深入理解Spring Boot核心原理
Spring Boot是一个用于构建基于Spring框架的独立应用程序的框架。它采用了自动配置的原则,以减少开发人员在搭建应用架构方面的时间和精力,同时提升了系统的可维护性和可扩展性
BXA
1127 0
|
12月前
|
缓存 监控 安全
Spring Boot框架基础介绍
Spring Boot 是一款基于 Spring 框架的开源应用程序开发工具,它旨在简化 Spring 应用程序的配置和开发过程。Spring Boot 提供了一种简单的方式来创建可独立运行的、生产级别的应用程序,并在需要时进行部署。Spring Boot 在微服务架构和云计算环境下得到了广泛应用,本文将介绍 Spring Boot 的特性、优势以及使用方法。
1306 0