Spring
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)的简写,其核心思想是通过将业务逻辑、数据、显示分离来组织代码。
使用 Spring 进行开发各种配置过于麻烦比如开启某些 Spring 特性时,需要用 XML 或 Java 进行显式配置。于是,Spring Boot (减少配置文件,开箱即用!)诞生了!
3. Spring 框架中用到了哪些设计模式?
-
工厂设计模式 : Spring 使用工厂模式通过
BeanFactory
、ApplicationContext
创建 bean 对象。 -
代理设计模式 : Spring AOP 功能的实现。
-
单例设计模式 : Spring 中的 Bean 默认都是单例的。
-
模板方法模式 : Spring 中
jdbcTemplate
、hibernateTemplate
等以 Template 结尾的对数据库操作的类,它们就使用到了模板模式。
-
包装器设计模式 : 我们的项目需要连接多个数据库,而且不同的客户在每次访问中根据需要会去访问不同的数据库。这种模式让我们可以根据客户的需求能够动态切换不同的数据源。
-
观察者模式: Spring 事件驱动模型就是观察者模式很经典的一个应用。
-
适配器模式 : Spring AOP 的增强或通知(Advice)使用到了适配器模式、spring MVC 中也是用到了适配器模式适配
Controller
。
IoC
1. 理解
IoC(Inversion of Control:控制反转) 是一种设计思想,而不是一个具体的技术实现。IoC 的思想就是将原本在程序中手动创建对象的控制权,交由 Spring 框架来管理。将对象之间的相互依赖关系交给 IoC 容器(工厂)来管理,并由 IoC 容器完成对象的注入,需要创建一个对象的时候,只需要配置好配置文件/注解即可,完全不用考虑对象是如何被创建出来的。
-
控制:指的是对象创建(实例化、管理)的权力
-
反转:控制权交给外部环境(Spring 框架、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 提供的注解,@Resource
是 JDK 提供的注解。 -
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 的线程安全问题,常见的有两种解决办法:
-
在 Bean 中尽量避免定义可变的成员变量。
-
在类中定义一个 ThreadLocal 成员变量,将需要的可变成员变量保存在
ThreadLocal
中(推荐的一种方式)。
7. Bean 的生命周期了解么?
重要:实例化 —> 属性赋值 —> 初始化 —> 销毁。
AOP
1. 理解
AOP(Aspect-Oriented Programming:面向切面编程) 能够将那些与业务无关,却为业务模块的公共行为(例如事务处理、日志管理、权限控制等)封装起来,便于减少系统的重复代码,降低模块间的耦合度,并有利于未来的可拓展性和可维护性。
Spring AOP 就是基于动态代理的,如果要代理的对象,实现了某个接口,那么 Spring AOP 会使用 JDK Proxy,去创建代理对象。 而对于没有实现接口的对象,就无法使用 JDK Proxy 去进行代理了,这时候 Spring AOP 会使用 Cglib 去生成一个被代理对象的子类来作为代理。
术语 | 含义 |
---|---|
目标(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. 核心组件
-
DispatcherServlet
:核心的中央处理器,负责接收请求、分发,并给予客户端响应。 -
HandlerMapping
:处理器映射器,根据 URL 去匹配查找能处理的Handler
,并会将请求涉及到的拦截器和Handler
一起封装。 -
HandlerAdapter
:处理器适配器,根据HandlerMapping
找到的Handler
,适配执行对应的Handler
; -
Handler
:请求处理器,处理实际请求的处理器。 -
ViewResolver
:视图解析器,根据Handler
返回的逻辑视图 / 视图,解析并渲染真正的视图,并传递给DispatcherServlet
响应客户端
流程说明(重要):
-
客户端(浏览器)发送请求,
DispatcherServlet
拦截请求。 -
DispatcherServlet
根据请求信息调用HandlerMapping
。HandlerMapping
根据 URL 去匹配查找能处理的Handler
(也就是我们平常说的Controller
控制器) ,并会将请求涉及到的拦截器和Handler
一起封装。 -
DispatcherServlet
调用HandlerAdapter
适配器执行Handler
。 -
Handler
完成对用户请求的处理后,会返回一个ModelAndView
对象给DispatcherServlet
,ModelAndView
顾名思义,包含了数据模型以及相应的视图的信息。Model
是返回的数据对象,View
是个逻辑上的View
。 -
ViewResolver
会根据逻辑View
查找实际的View
。 -
DispaterServlet
把返回的Model
传给View
(视图渲染)。 -
把
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。
-
一级缓存(singletonObjects):存放最终形态的 Bean(已经实例化、属性填充、初始化),单例池,为“Spring 的单例属性”⽽⽣。一般情况我们获取 Bean 都是从这里获取的,但是并不是所有的 Bean 都在单例池里面,例如原型 Bean 就不在里面。
-
二级缓存(earlySingletonObjects):存放过渡 Bean(半成品,只实例化),也就是三级缓存中
ObjectFactory
产生的对象,与三级缓存配合使用的,可以防止 AOP 的情况下,每次调用ObjectFactory#getObject()都是会产生新的代理对象的。 -
三级缓存(singletonFactories):存放
ObjectFactory
,ObjectFactory
的getObject()
方法(最终调用的是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
-
原子性(
Atomicity
):事务是最小的执行单位,不允许分割。事务的原子性确保动作要么全部完成,要么完全不起作用; -
一致性(
Consistency
):执行事务前后,数据保持一致,例如转账业务中,无论事务是否成功,转账者和收款人的总额应该是不变的; -
隔离性(
Isolation
):并发访问数据库时,一个用户的事务不被其他事务所干扰,各并发事务之间数据库是独立的; -
持久性(
Durability
):一个事务被提交之后。它对数据库中数据的改变是持久的,即使数据库发生故障也不应该对其有任何影响。
3. Spring 对事务的支持
程序是否支持事务首先取决于数据库 ,比如使用 MySQL 的话,如果你选择的是 innodb 引擎。
想要保证事务的原子性,就需要在异常发生时,对已执行操作进行回滚,在 MySQL 中,恢复机制是通过 回滚日志(undo log) 实现的,所有事务进行的修改都会先记录到这个回滚日志中,然后再执行相关的操作。将数据回滚到修改之前的样子即可!
回滚日志会先于数据持久化到磁盘上,即使遇到数据库突然宕机等情况,当用户再次启动数据库的时候,数据库还能够通过查询回滚日志来回滚之前未完成的事务。
4. Spring 支持两种方式的事务管理
-
编程式
通过 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(); } } }); }
-
声明式
推荐使用(代码侵入性最小),实际是通过 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 注解使用详解
-
方法:推荐将注解使用于方法上,不过需要注意的是:该注解只能应用到 public 方法上,否则不生效。
-
类:如果这个注解使用在类上的话,表明该注解对该类中所有的 public 方法都生效。
-
接口:不推荐在接口上使用。
@Transactional
的工作机制是基于 AOP 实现的,AOP 又是使用动态代理实现的。如果目标对象实现了接口,默认情况下会采用 JDK 的动态代理,如果目标对象没有实现了接口,会使用 CGLIB 动态代理。
propagation | 事务的传播行为,默认值为 REQUIRED,可选的值在上面介绍过 |
---|---|
isolation | 事务的隔离级别,默认值采用 DEFAULT,可选的值在上面介绍过 |
timeout | 事务的超时时间,默认值为-1(不会超时)。如果超过该时间限制但事务还没有完成,则自动回滚事务。 |
readOnly | 指定事务是否为只读事务,默认值为 false。 |
rollbackFor | 用于指定能够触发事务回滚的异常类型,并且可以指定多个异常类型。 |
Spring Boot
1. 理解
一个开源的、用于简化 Spring 应用初始化和开发过程的框架。虽然Spring的组件代码是轻量级的,但它的配置却是重量级的;所以SpringBoot的设计策略是通过开箱即用和约定大于配置 来解决配置重的问题的。
-
核心功能:起步依赖、自动配置。
-
特点:
-
Spring Boot 内嵌了 Tomcat、Jetty、Undertow 等容器,不需要在服务器上部署 WAR 包了,直接运行 jar 包就可以启动项目,超级方便。
-
开箱即用,没有代码生成,也无需XML配置。同时也可以修改默认值来满足特定的需求。还允许我们通过 yaml 来管理应用的配置,比传统的 properties 文件更加简洁。
-
Spring Boot 提供了一系列的 Starter,可以快速集成常用的框架,例如 Spring Data JPA、Spring Security、MyBatis 等。starter-web,Spring Boot 会自动配置 Tomcat 和 Spring MVC。 起步依赖,将具备某种功能的坐标打包到一起,并提供一些默认的功能,一个Maven项目对象模型(POM)。定义了对其他库的传递依赖,这些东西加在一起即支持某项功能。
-
Spring Boot 提供了一系列的 Actuator,可以帮助我们监控和管理应用,比如健康检查、审计、统计等。
2. 自动配置原理
容器利用反射技术,根据 Bean 的类型、名称等自动注入所需的依赖。
自动配置 是一个运行时(更准确地说,是应用程序启动时)的过程,考虑了众多因素,才决定Spring配置应该用哪个,不该用哪个。该过程是Spring自动完成的。
Spring Boot 通过@EnableAutoConfiguration
开启自动装配,通过 SpringFactoriesLoader 最终加载META-INF/spring.factories
中的自动配置类实现自动装配。 自动配置类其实就是通过@Conditional
**按需加载**的配置类,想要其生效必须引入spring-boot-starter-xxx
包实现起步依赖。
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" }