【必看】Spring系列面试题

2 篇文章 0 订阅
1 篇文章 0 订阅

Spring

Spring5.x主要模块

Core Container, AOP, Data Access, Web...

基础

1. 简单介绍Spring

一款开源轻量级 Java 开发框架,旨在提高开发人员的开发效率以及系统的可维护性。Spring 支持 IoC(Inversion of Control:控制反转) 和 AOP(Aspect-Oriented Programming:面向切面编程)、可以很方便地对数据库进行访问、可以很方便地集成第三方组件(电子邮件,任务,调度,缓存等等)、对单元测试支持比较好、支持 RESTful Java 应用程序的开发。不重新造轮子,开箱即用,提高开发效率。

2. SpringMVC, Spring Boot 和 Spring的关系?

Spring MVC 是 Spring 中的一个很重要的模块,主要赋予 Spring 快速构建 MVC 架构的 Web 程序的能力。MVC 是模型(Model)、视图(View)、控制器(Controller)的简写,其核心思想是通过将业务逻辑、数据、显示分离来组织代码。

img

使用 Spring 进行开发各种配置过于麻烦比如开启某些 Spring 特性时,需要用 XML 或 Java 进行显式配置。于是,Spring Boot (减少配置文件,开箱即用!)诞生了!

3. Spring 框架中用到了哪些设计模式?

  • 工厂设计模式 : Spring 使用工厂模式通过 BeanFactoryApplicationContext 创建 bean 对象

  • 代理设计模式 : Spring AOP 功能的实现。

  • 单例设计模式 : Spring 中的 Bean 默认都是单例的。

  • 模板方法模式 : Spring 中 jdbcTemplatehibernateTemplate以 Template 结尾的对数据库操作的类,它们就使用到了模板模式。

  • 包装器设计模式 : 我们的项目需要连接多个数据库,而且不同的客户在每次访问中根据需要会去访问不同的数据库。这种模式让我们可以根据客户的需求能够动态切换不同的数据源。

  • 观察者模式: Spring 事件驱动模型就是观察者模式很经典的一个应用。

  • 适配器模式 : Spring AOP 的增强或通知(Advice)使用到了适配器模式、spring MVC 中也是用到了适配器模式适配Controller

IoC

1. 理解

IoC(Inversion of Control:控制反转) 是一种设计思想,而不是一个具体的技术实现。IoC 的思想就是将原本在程序中手动创建对象的控制权,交由 Spring 框架来管理。将对象之间的相互依赖关系交给 IoC 容器(工厂)来管理,并由 IoC 容器完成对象的注入,需要创建一个对象的时候,只需要配置好配置文件/注解即可,完全不用考虑对象是如何被创建出来的。

  • 控制:指的是对象创建(实例化、管理)的权力

  • 反转:控制权交给外部环境(Spring 框架、IoC 容器)

IoC 图解

2. Spring Bean

Bean 代指的就是那些被 IoC 容器所管理的对象

  • @Component通用的注解,可标注任意类为 Spring 组件。如果一个 Bean 不知道属于哪个层,可以使用@Component 注解标注。

  • @Repository / @Mapper : 对应持久层即 Dao 层,主要用于数据库相关操作

  • @Service : 对应服务层,主要涉及一些复杂的逻辑,需要用到 Dao 层。

  • @Controller : 对应 Spring MVC 控制层,主要用于接受用户请求并调用 Service返回数据给前端页面。

3. @Component vs. @Bean

@Component 注解作用于,而@Bean注解作用于方法 (所在类有@Configuration)。

@Component通常是通过类路径扫描自动侦测以及自动装配到 Spring 容器中(@ComponentScan 注解)。 @Bean 注解通常是我们在标有该注解的方法中定义产生这个 bean,@Bean告诉了 Spring 这是某个类的实例,当我需要用它的时候还给我。

@Bean 注解比 @Component 注解的自定义性更强,而且很多地方我们只能通过 @Bean 注解来注册 bean。比如当我们引用第三方库中的类需要装配到 Spring容器时,则只能通过 @Bean来实现。

@Configuration
public class AppConfig {
    @Bean
    public TransferService transferService() {
        return new TransferServiceImpl();
    }
​
}

4. @Autowired 和 @Resource 的区别是什么?

两者都是注入Bean的注解。

  • @Autowired 是 Spring 提供的注解,@ResourceJDK 提供的注解。

  • Autowired 默认的注入方式为byType(根据类型进行匹配),@Resource默认注入方式为 byName(根据名称进行匹配)。

  • 一个接口存在多个实现类的情况下,@Autowired@Resource都需要通过名称才能正确匹配到对应的 Bean。Autowired 可以通过 @Qualifier 注解来显式指定名称,@Resource可以通过 name 属性来显式指定名称。

  • @Autowired 支持在构造函数、方法、字段和参数上使用。@Resource 主要用于字段和方法上的注入,不支持在构造函数或参数上使用。

5. Bean 的作用域

  • singleton : IoC 容器中只有唯一(全局共享)的 bean 实例。Spring 中的 bean 默认都是单例的,是对单例设计模式的应用。

  • prototype : 每次获取都会创建一个的 bean 实例。也就是说,连续 getBean() 两次,得到的是不同的 Bean 实例。

  • 仅 Web 应用可用:request、session、application/global-session、websocket

配置作用域:

搭配@Scope注解

@Bean
@Scope(value = ConfigurableBeanFactory.SCOPE_PROTOTYPE)
public Person personPrototype() {
    return new Person();
}
​

6. Bean 是线程安全的吗?

取决于其作用域状态

prototype 作用域下,每次获取都会创建一个新的 bean 实例,不存在资源竞争问题,所以不存在线程安全问题。singleton 作用域下,IoC 容器中只有唯一的 bean 实例,可能会存在资源竞争问题(取决于 Bean 是否有状态)。如果这个 bean 是有状态的话,那就存在线程安全问题(有状态 Bean 是指包含可变的成员变量的对象)。

不过,大部分 Bean 实际都是无状态(没有定义可变的成员变量)的(比如 Dao、Service),这种情况下, Bean 是线程安全的。

对于有状态单例 Bean 的线程安全问题,常见的有两种解决办法:

  1. 在 Bean 中尽量避免定义可变的成员变量。

  2. 在类中定义一个 ThreadLocal 成员变量,将需要的可变成员变量保存在 ThreadLocal 中(推荐的一种方式)。

7. Bean 的生命周期了解么?

重要:实例化 —> 属性赋值 —> 初始化 —> 销毁。

img

AOP

1. 理解

AOP(Aspect-Oriented Programming:面向切面编程) 能够将那些与业务无关,却为业务模块的公共行为(例如事务处理、日志管理、权限控制等)封装起来,便于减少系统的重复代码,降低模块间的耦合度,并有利于未来的可拓展性和可维护性。

Spring AOP 就是基于动态代理的,如果要代理的对象,实现了某个接口,那么 Spring AOP 会使用 JDK Proxy,去创建代理对象。 而对于没有实现接口的对象,就无法使用 JDK Proxy 去进行代理了,这时候 Spring AOP 会使用 Cglib生成一个被代理对象的子类来作为代理。

SpringAOPProcess

术语含义
目标(Target)通知的对象
代理(Proxy)向目标对象应用通知之后创建的代理对象
连接点(JoinPoint)目标对象的所属类中,定义的所有方法均为连接点
切入点(Pointcut)切面拦截 / 增强的连接点(切入点一定是连接点,连接点不一定是切入点)
通知(Advice)增强的逻辑 / 代码,也即拦截到目标对象的连接点之后要做的事情
切面(Aspect)切入点(Pointcut)+通知(Advice)
Weaving(织入)通知应用到目标对象,进而生成代理对象的过程动作

一个小例子使用AOP

Spring AOP 属于运行时增强,而 AspectJ 是编译时增强。 Spring AOP 基于代理(Proxying),而 AspectJ 基于字节码操作(Bytecode Manipulation)。

2. 多个切面的执行顺序如何控制?

通常使用 @Order 注解直接定义切面顺序

// 值越小优先级越高
@Order(3)
@Component
@Aspect
public class LoggingAspect implements Ordered {
​

Spring MVC

1. 理解

MVC 是一种设计模式,Spring MVC 是一款很优秀的 MVC 框架。Spring MVC 可以帮助我们进行更简洁的 Web 层的开发,并且它天生与 Spring 框架集成。Spring MVC 下我们一般把后端项目分为 Service 层(处理业务)、Dao 层数据库操作)、Entity 层实体类)、Controller 层(控制层,返回数据给前台页面)。

2. 核心组件

  1. DispatcherServlet核心的中央处理器,负责接收请求、分发,并给予客户端响应。

  2. HandlerMapping处理器映射器,根据 URL 去匹配查找能处理的 Handler ,并会将请求涉及到的拦截器和 Handler 一起封装。

  3. HandlerAdapter处理器适配器,根据 HandlerMapping 找到的 Handler ,适配执行对应的 Handler

  4. Handler请求处理器,处理实际请求的处理器。

  5. ViewResolver视图解析器,根据 Handler 返回的逻辑视图 / 视图,解析并渲染真正的视图,并传递给 DispatcherServlet 响应客户端

img

流程说明(重要):

  1. 客户端(浏览器)发送请求, DispatcherServlet拦截请求。

  2. DispatcherServlet 根据请求信息调用 HandlerMappingHandlerMapping 根据 URL 去匹配查找能处理的 Handler(也就是我们平常说的 Controller 控制器) ,并会将请求涉及到的拦截器和 Handler 一起封装。

  3. DispatcherServlet 调用 HandlerAdapter适配器执行 Handler

  4. Handler 完成对用户请求的处理后,会返回一个 ModelAndView 对象给DispatcherServletModelAndView 顾名思义,包含了数据模型以及相应的视图的信息。Model 是返回的数据对象,View 是个逻辑上的 View

  5. ViewResolver 会根据逻辑 View 查找实际的 View

  6. DispaterServlet 把返回的 Model 传给 View视图渲染)。

  7. View 返回给请求者(浏览器)

3. 统一异常处理

@ControllerAdvice + @ExceptionHandler

@ControllerAdvice
@ResponseBody
public class GlobalExceptionHandler {
​
    @ExceptionHandler(BaseException.class)
    public ResponseEntity<?> handleAppException(BaseException ex, HttpServletRequest request) {
      //......
    }
​
    @ExceptionHandler(value = ResourceNotFoundException.class)
    public ResponseEntity<ErrorReponse> handleResourceNotFoundException(ResourceNotFoundException ex, HttpServletRequest request) {
      //......
    }
}

这种异常处理方式下,会给所有或者指定Controller 织入异常处理的逻辑(AOP),当 Controller 中的方法抛出异常的时候,由被@ExceptionHandler 注解修饰的方法进行处理。

循环依赖

1. 理解

Bean 对象循环引用,是两个或多个 Bean 之间相互持有对方的引用,一种设计缺陷。

@Component
public class CircularDependencyA {
    @Autowired
    private CircularDependencyB circB;
}
​
@Component
public class CircularDependencyB {
    @Autowired
    private CircularDependencyA circA;
}

2. 三级缓存

Spring 框架通过使用三级缓存(就是三个Map)来解决这个问题,确保即使在循环依赖的情况下也能正确创建 Bean

  1. 一级缓存(singletonObjects):存放最终形态的 Bean(已经实例化、属性填充、初始化),单例池,为“Spring 的单例属性”⽽⽣。一般情况我们获取 Bean 都是从这里获取的,但是并不是所有的 Bean 都在单例池里面,例如原型 Bean 就不在里面。

  2. 二级缓存(earlySingletonObjects):存放过渡 Bean半成品,只实例化),也就是三级缓存中ObjectFactory产生的对象,与三级缓存配合使用的,可以防止 AOP 的情况下,每次调用ObjectFactory#getObject()都是会产生新的代理对象的。

  3. 三级缓存(singletonFactories):存放ObjectFactoryObjectFactorygetObject()方法(最终调用的是getEarlyBeanReference()方法)可以生成原始 Bean 对象或者代理对象(如果 Bean 被 AOP 切面代理)。三级缓存只会对单例 Bean 生效。

class A {
    // 使用了 B
    private B b;
}
class B {
    // 使用了 A
    private A a;
}
  • 当 Spring 创建 A 之后,发现 A 依赖了 B ,又去创建 B,B 依赖了 A ,又去创建 A;

  • 在 B 创建 A 的时候,那么此时 A 就发生了循环依赖,由于 A 此时还没有初始化完成,因此在 一二级缓存 中肯定没有 A;

  • 那么此时就去三级缓存中调用 getObject() 方法去获取 A 的 前期暴露的对象 ,也就是调用上边加入的getEarlyBeanReference() 方法;

  • 然后就将这个ObjectFactory从三级缓存中移除,并且将前期暴露对象放入到二级缓存中,那么 B 就将这个前期暴露对象注入到依赖,来支持循环依赖,就不会重复初始化了!

3. @Lazy 能解决循环依赖吗?

@Lazy 用来标识类是否需要懒加载/延迟加载,可以作用在类上、方法上、构造器上、方法参数上、成员变量中。如果一个 Bean 被标记为懒加载,那么它不会在 Spring IoC 容器启动时立即实例化,而是在第一次被请求时才创建。这可以帮助减少应用启动时的初始化时间,也可以用来解决循环依赖(部分)问题。

@Lazy
class A {
    // 使用了 B
    private B b;
}
​
class B {
    // 使用了 A
    private A a;
}

A 的构造器上添加 @Lazy 注解之后(延迟 Bean B 的实例化),加载的流程如下:

  • 首先 Spring 会去创建 A 的 Bean,创建时需要注入 B 的属性;

  • 由于在 A 上标注了 @Lazy 注解,因此 Spring 会去创建一个 B 的代理对象,将这个代理对象注入到 A 中的 B 属性;

  • 之后开始执行 B 的实例化、初始化,在注入 B 中的 A 属性时,此时 A 已经创建完毕了,就可以将 A 给注入进去。

对 A 中的属性 B 进行注入时,注入的是 B 的代理对象,因此不会循环依赖。

全局懒加载会让 Bean 第一次使用的时候加载会变慢,并且它会延迟应用程序问题的发现(当 Bean 被初始化时,问题才会出现)。

#默认false
spring.main.lazy-initialization=true
​

事务

1. 理解

事务是逻辑上的一组操作,要么都执行,要么都不执行。(转账)

2. 特性 ACID

  1. 原子性Atomicity):事务是最小的执行单位,不允许分割。事务的原子性确保动作要么全部完成,要么完全不起作用;

  2. 一致性Consistency):执行事务前后数据保持一致,例如转账业务中,无论事务是否成功,转账者和收款人的总额应该是不变的;

  3. 隔离性Isolation):并发访问数据库时,一个用户的事务不被其他事务所干扰,各并发事务之间数据库是独立的;

  4. 持久性Durability):一个事务被提交之后。它对数据库中数据的改变是持久的,即使数据库发生故障也不应该对其有任何影响。

AID->C

3. Spring 对事务的支持

程序是否支持事务首先取决于数据库 ,比如使用 MySQL 的话,如果你选择的是 innodb 引擎。

想要保证事务的原子性,就需要在异常发生时,对已执行操作进行回滚,在 MySQL 中,恢复机制是通过 回滚日志(undo log) 实现的,所有事务进行的修改都会先记录到这个回滚日志中,然后再执行相关的操作。将数据回滚到修改之前的样子即可!

回滚日志会先于数据持久化到磁盘上,即使遇到数据库突然宕机等情况,当用户再次启动数据库的时候,数据库还能够通过查询回滚日志来回滚之前未完成的事务。

4. Spring 支持两种方式的事务管理

  1. 编程式

通过 TransactionTemplate或者TransactionManager手动管理事务。

@Autowired
private TransactionTemplate transactionTemplate;
public void testTransaction() {
​
        transactionTemplate.execute(new TransactionCallbackWithoutResult() {
            @Override
            protected void doInTransactionWithoutResult(TransactionStatus transactionStatus) {
​
                try {
​
                    // ....  业务代码
                } catch (Exception e){
                    //回滚
                    transactionStatus.setRollbackOnly();
                }
​
            }
        });
}
​

  1. 声明式

推荐使用(代码侵入性最小),实际是通过 AOP 实现(基于 @Transactional 的全注解方式使用最多)。

@Transactional(propagation = Propagation.REQUIRED)
public void aMethod {
  //do something
  B b = new B();
  C c = new C();
  b.bMethod();
  c.cMethod();
}
​

5. 事务管理接口介绍

  • PlatformTransactionManager:(平台)事务管理器,Spring 事务策略的核心

  • TransactionDefinition:事务定义信息(事务隔离级别、传播行为、超时、只读、回滚规则)。

  • TransactionStatus:事务运行状态

Spring 并不直接管理事务,而是提供了多种事务管理器

因为要将事务管理行为抽象出来,然后不同的平台去实现它,这样我们可以保证提供给外部的行为不变,方便我们扩展。(SPI机制

接口:接口理解为提供了一系列功能列表约定,接口本身不提供功能,它只定义行为。但是谁要用,就要先实现我,遵守我的约定,然后再自己去实现我定义的要实现的功能。

1. PlatformTransactionManager: 事务管理接口
package org.springframework.transaction;
​
import org.springframework.lang.Nullable;
​
public interface PlatformTransactionManager {
    //获得事务
    TransactionStatus getTransaction(@Nullable TransactionDefinition var1) throws TransactionException;
    //提交事务
    void commit(TransactionStatus var1) throws TransactionException;
    //回滚事务
    void rollback(TransactionStatus var1) throws TransactionException;
}
​

2. TransactionDefinition:事务属性

通过PlatformTransactionManager#getTransaction(TransactionDefinition definition)得到一个事务。

事务属性是事务的一些基本配置,描述了事务策略如何应用到方法上。

  • 隔离级别 getIsolationLevel(), ISOLATION_*

  • 传播行为 getPropagationBehavior()

  • 回滚规则 常量

  • 是否只读 isReadOnly()

  • 事务超时 getTimeout()

package org.springframework.transaction;
​
import org.springframework.lang.Nullable;
​
public interface TransactionDefinition {
    int PROPAGATION_REQUIRED = 0;
    int PROPAGATION_SUPPORTS = 1;
    int PROPAGATION_MANDATORY = 2;
    int PROPAGATION_REQUIRES_NEW = 3;
    int PROPAGATION_NOT_SUPPORTED = 4;
    int PROPAGATION_NEVER = 5;
    int PROPAGATION_NESTED = 6;
    int ISOLATION_DEFAULT = -1;
    int ISOLATION_READ_UNCOMMITTED = 1;
    int ISOLATION_READ_COMMITTED = 2;
    int ISOLATION_REPEATABLE_READ = 4;
    int ISOLATION_SERIALIZABLE = 8;
    int TIMEOUT_DEFAULT = -1;
    // 返回事务的传播行为,默认值为 REQUIRED。
    int getPropagationBehavior();
    //返回事务的隔离级别,默认值是 DEFAULT
    int getIsolationLevel();
    // 返回事务的超时时间,默认值为-1。如果超过该时间限制但事务还没有完成,则自动回滚事务。
    int getTimeout();
    // 返回是否为只读事务,默认值为 false
    boolean isReadOnly();
​
    @Nullable
    String getName();
}

3. TransactionStatus:事务状态

获取或判断事务的相应状态信息。

通过PlatformTransactionManager#commit/rollback(TransactionStatus var1)得到。

public interface TransactionStatus{
    boolean isNewTransaction(); // 是否是新的事务
    boolean hasSavepoint(); // 是否有恢复点
    void setRollbackOnly();  // 设置为只回滚
    boolean isRollbackOnly(); // 是否为只回滚
    boolean isCompleted; // 是否已完成
}

6. @Transactional 注解使用详解

  1. 方法:推荐将注解使用于方法上,不过需要注意的是:该注解只能应用到 public 方法上,否则不生效。

  2. :如果这个注解使用在类上的话,表明该注解对该类中所有的 public 方法都生效。

  3. 接口:不推荐在接口上使用。

@Transactional 的工作机制是基于 AOP 实现的,AOP 又是使用动态代理实现的。如果目标对象实现了接口,默认情况下会采用 JDK 的动态代理,如果目标对象没有实现了接口,会使用 CGLIB 动态代理

propagation事务的传播行为,默认值为 REQUIRED,可选的值在上面介绍过
isolation事务的隔离级别,默认值采用 DEFAULT,可选的值在上面介绍过
timeout事务的超时时间,默认值为-1(不会超时)。如果超过该时间限制但事务还没有完成,则自动回滚事务。
readOnly指定事务是否为只读事务,默认值为 false。
rollbackFor用于指定能够触发事务回滚异常类型,并且可以指定多个异常类型。

Spring Boot

1. 理解

一个开源的、用于简化 Spring 应用初始化开发过程的框架。虽然Spring的组件代码是轻量级的,但它的配置却是重量级的;所以SpringBoot的设计策略是通过开箱即用约定大于配置 来解决配置重的问题的。

  • 核心功能:起步依赖、自动配置。

  • 特点:

  1. Spring Boot 内嵌了 Tomcat、Jetty、Undertow 等容器,不需要在服务器上部署 WAR 包了,直接运行 jar 包就可以启动项目,超级方便。

  2. 开箱即用,没有代码生成,也无需XML配置。同时也可以修改默认值来满足特定的需求。还允许我们通过 yaml 来管理应用的配置,比传统的 properties 文件更加简洁。

  3. Spring Boot 提供了一系列的 Starter,可以快速集成常用的框架,例如 Spring Data JPA、Spring Security、MyBatis 等。starter-web,Spring Boot 会自动配置 Tomcat 和 Spring MVC。 起步依赖,将具备某种功能的坐标打包到一起,并提供一些默认的功能,一个Maven项目对象模型POM)。定义了对其他库的传递依赖,这些东西加在一起即支持某项功能。

  4. Spring Boot 提供了一系列的 Actuator,可以帮助我们监控和管理应用,比如健康检查、审计、统计等。

2. 自动配置原理

容器利用反射技术,根据 Bean 的类型、名称等自动注入所需的依赖

自动配置 是一个运行时(更准确地说,是应用程序启动时)的过程,考虑了众多因素,才决定Spring配置应该用哪个,不该用哪个。该过程是Spring自动完成的。

Spring Boot 通过@EnableAutoConfiguration开启自动装配,通过 SpringFactoriesLoader 最终加载META-INF/spring.factories中的自动配置类实现自动装配。 自动配置类其实就是通过@Conditional**按需加载**的配置类,想要其生效必须引入spring-boot-starter-xxx包实现起步依赖。

三分恶面渣逆袭:SpringBoot自动配置原理

3. 常用注解

1. @SpringBootApplication

加在启动类上,是@Configuration@EnableAutoConfiguration@ComponentScan 注解的集合

  • @EnableAutoConfiguration:启用 SpringBoot 的自动配置机制

  • @ComponentScan:扫描被@Component (@Repository,@Service,@Controller)注解的 bean,注解默认会扫描该类所在的包下所有的类

  • @Configuration:允许在 Spring 上下文中注册额外的 bean 或导入其他配置类

2. Bean 类相关
  • 对象注入: @Autowired, @Resource

  • 自动装配的 bean 的@Component, @Repository, @Service, @Controller

  • 控制器 bean,并且是将函数的返回值直接填入 HTTP 响应体中,是 REST 风格的控制器:@RestController ( @Controller + @ResponseBody )

  • 声明 Spring Bean 的作用域: @Scope ( singleton, prototype, request, session)

  • 声明配置类: @Configuration

  • @Qualifier

3. 处理常见HTTP请求类型

GET, POST, PUT, DELETE 加入 xMapping(""),相当于为`@RequestMapping指定method

@GetMapping("users") 等价于@RequestMapping(value="/users",method=RequestMethod.GET)

4. 前后端传值
  • @PathVariable用于获取路径参数@RequestParam用于获取查询参数

请求的 url/klasses/123456/teachers?type=web

服务获取klassId=123456,type=web

@GetMapping("/klasses/{klassId}/teachers")
public List<Teacher> getKlassRelatedTeachers(
         @PathVariable("klassId") Long klassId,
         @RequestParam(value = "type", required = false) String type ) {
...
}

  • @RequestBody 读取 Request 请求的 body 部分并且Content-Type 为 application/json 格式的数据,接收到数据之后会自动将数据绑定到 Java 对象上去。 系统(可以自定义)会使用HttpMessageConverter将请求的 body 中的 json 字符串转换为 java 对象。 一个请求方法只可以有一个。

    @PostMapping("/sign-up")
    public ResponseEntity signUp(@RequestBody @Valid UserRegisterRequest userRegisterRequest) {
      userService.save(userRegisterRequest);
      return ResponseEntity.ok().build();
    }

5. 读取配置信息

读取application.yml中的数据。

wuhan2020: 2020年初武汉爆发了新型冠状病毒,疫情严重,但是,我相信一切都会过去!武汉加油!中国加油!
​
my-profile:
  name: Guide哥
  email: koushuangbwcx@163.com
​
library:
  location: 湖北武汉加油中国加油
  books:
    - name: 天才基本法
      description: 二十二岁的林朝夕在父亲确诊阿尔茨海默病这天,得知自己暗恋多年的校园男神裴之即将出国深造的消息——对方考取的学校,恰是父亲当年为她放弃的那所。
    - name: 时间的秩序
      description: 为什么我们记得过去,而非未来?时间“流逝”意味着什么?是我们存在于时间之内,还是时间存在于我们之中?卡洛·罗韦利用诗意的文字,邀请我们思考这一亘古难题——时间的本质。
    - name: 了不起的我
      description: 如何养成一个新习惯?如何让心智变得更成熟?如何拥有高质量的关系? 如何走出人生的艰难时刻?
​

  • @Value(${p}) 读取简单的配置

@Value("${wuhan2020}")
String wuhan2020;

  • @ConfigurationProperties(prefix="")读取配置信息并与 bean 绑定

@Component
@ConfigurationProperties(prefix = "library")
class LibraryProperties {
    @NotEmpty
    private String location;
    private List<Book> books;
​
    @Setter
    @Getter
    @ToString
    static class Book {
        String name;
        String description;
    }
  省略getter/setter
  ......
}

6. 参数校验

即使在前端对数据进行校验的情况下,我们还是要对传入后端的数据再进行一遍校验,避免用户绕过浏览器直接通过一些 HTTP 工具直接向后端请求一些违法数据。

JSR(Java Specification Requests) 是一套 JavaBean 参数校验的标准,它定义了很多常用的校验注解,我们可以直接将这些注解加在我们 JavaBean 的属性上面。引入`spring-boot-starter-validation依赖,来自javax.validation.constraints包。

1. 常用字段

@NotEmpty 被注释的字符串的不能为 null 也不能为空

@NotBlank 被注释的字符串非 null,并且必须包含一个非空白字符

@Null 被注释的元素必须为 null

@NotNull 被注释的元素必须不为 null

@AssertTrue 被注释的元素必须为 true

@AssertFalse 被注释的元素必须为 false

@Pattern(regex=,flag=)被注释的元素必须符合指定的正则表达式

@Email 被注释的元素必须是 Email 格式。

@Min(value)被注释的元素必须是一个数字,其值必须大于等于指定的最小值

@Max(value)被注释的元素必须是一个数字,其值必须小于等于指定的最大值

@DecimalMin(value)被注释的元素必须是一个数字,其值必须大于等于指定的最小值

@DecimalMax(value) 被注释的元素必须是一个数字,其值必须小于等于指定的最大值

@Size(max=, min=)被注释的元素的大小必须在指定的范围内

@Digits(integer, fraction)被注释的元素必须是一个数字,其值必须在可接受的范围内

@Past被注释的元素必须是一个过去的日期

@Future 被注释的元素必须是一个将来的日期

2. 验证参数
  • 请求体,参数前加@Valid,验证失败,它将抛出MethodArgumentNotValidException

@RestController
@RequestMapping("/api")
public class PersonController {
​
    @PostMapping("/person")
    public ResponseEntity<Person> getPerson(@RequestBody @Valid Person person) {
        return ResponseEntity.ok().body(person);
    }
}
  • 其他请求参数,还要类上加@Validated,告诉spring去校验。

@RestController
@RequestMapping("/api")
@Validated
public class PersonController {
​
    @GetMapping("/person/{id}")
    public ResponseEntity<Integer> getPersonByID(@Valid @PathVariable("id") @Max(value = 5,message = "超过 id 的范围了") Integer id) {
        return ResponseEntity.ok().body(id);
    }
}
7. 全局异常处理
  • @RestControllerAdvice 全局异常处理 ( @ResponseBody + @ControllerAdvice)

  • @ExceptionHandler 异常处理方法

@RestControllerAdvice
public class GlobalExceptionHandler {
​
    /**
     * 请求参数异常处理
     */
    @ExceptionHandler(MethodArgumentNotValidException.class)
    public ResponseEntity<?> handleMethodArgumentNotValidException(MethodArgumentNotValidException ex, HttpServletRequest request) {
       ......
    }
}

8. JPA
1. 表

@Entity声明一个类对应一个数据库实体。

@Table 表名

@Entity
@Table(name = "role")
public class Role {
   ...
}

2. 主键

@Id 主键

@GeneratedValue直接使用 JPA 内置提供的四种主键生成策略来指定主键生成策略。

@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;

默认 AUTO, 自增长 IDENTITY

3. 字段类型
  • @Column 普通字段

@Column(name = "user_name", nullable = false, length=32)
private String userName;
  • @Transient 不持久化,不映射数据库字段,不存入数据库。

@Transient
private String secrect; // not persistent because of @Transient
  • @Lob 大字段

@Lob
private String content;
  • @Enumerated 枚举类型

 @Enumerated(EnumType.STRING)
 private Gender gender;
​
...
  
public enum Gender {
    MALE("男性"),
    FEMALE("女性");
​
    private String value;
    Gender(String str){
        value=str;
    }
}
​
9. 事务 @Transactional
  • :当把@Transactional 注解放在类上时,表示该类的所有 public 方法都配置相同的事务属性信息。

  • 方法:当类配置了@Transactional,方法也配置了@Transactional方法的事务会覆盖类的事务配置信息。

@Transactional(rollbackFor = Exception.class)
public void save() {
  ......
}

10. json数据处理
  • @JsonIgnoreProperties, @JsonIgnore 过滤特定字段,不返回或不解析

//生成json时将userRoles属性过滤
@JsonIgnoreProperties({"userRoles"})
public class User {
    private String userName;
    private String fullName;
    private String password;
    //生成json时将userRoles属性过滤
    @JsonIgnore
    private List<UserRole> userRoles = new ArrayList<>();
}
  • @JsonFormat 格式化

@JsonFormat(shape=JsonFormat.Shape.STRING, pattern="yyyy-MM-dd'T'HH:mm:ss.SSS'Z'", timezone="GMT")
private Date date;
  • @JsonUnwrapped 扁平化对象

解析出内层json对象到外层。

...
public class Account {
    // @JsonUnwrapped
    private Location location;
    // @JsonUnwrapped
    private PersonInfo personInfo;
​
  ...
  public static class Location {
     private String provinceName;
     private String countyName;
  }
  
  ...
  public static class PersonInfo {
    private String userName;
    private String fullName;
  }
}
{
  "location": {
    "provinceName": "湖北",
    "countyName": "武汉"
  },
  "personInfo": {
    "userName": "coder1234",
    "fullName": "shaungkou"
  }
}
​
...
​
{
  "provinceName": "湖北",
  "countyName": "武汉",
  "userName": "coder1234",
  "fullName": "shaungkou"
}
  • 14
    点赞
  • 29
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值