SpringBoot基础使用
1.SpringBoot简介
Spring是Java企业版(Java Enterprise Edition,JEE,也称J2EE)的轻量级代替品。无需开发重量级的EnterpriseJavaBean(EJB),Spring为企业级Java开发提供了一种相对简单的方法,通过依赖注入和面向切面编程,用简单的Java对象(Plain Old Java Object,POJO)实现了EJB的功能。
虽然Spring的组件代码是轻量级的,但它的配置却是重量级的。一开始,Spring用XML配置,而且是很多XML配置。Spring 2.5引入了基于注解的组件扫描,这消除了大量针对应用程序自身组件的显式XML配置。Spring 3.0引入了基于Java的配置,这是一种类型安全的可重构配置方式,可以代替XML。
所有这些配置都代表了开发时的损耗。因为在思考Spring特性配置和解决业务问题之间需要进行思维切换,所以编写配置挤占了编写应用程序逻辑的时间。和所有框架一样,Spring实用,但与此同时它要求的回报也不少。
除此之外,项目的依赖管理也是一件耗时耗力的事情。在环境搭建时,需要分析要导入哪些库的坐标,而且还需要分析导入与之有依赖关系的其他库的坐标,一旦选错了依赖的版本,随之而来的不兼容问题就会严重阻碍项目的开发进度。
Spring Boot简化了Spring应用开发,不需要配置就能运行Spring应用,Spring Boot管理Spring 容器、第三方插件并提供很多默认系统级的服务。大部 Spring应用无论是简单的系统还是构建复杂系统,都只需要少量配置和代码就能完成。
2.SpringBoot项目创建
- 访问SpringBoot官方网站
https://start.spring.io/
输入信息后生成下载maven项目,导入到IDE工具中即可创建SpringBoot项目 - 直接利用Idea提供的方式
3.RESTful Web 服务
RESTful Web 服务与传统的 MVC 开发一个关键区别是返回给客户端的内容的创建方式:传统的 MVC 模式开发会直接返回给客户端一个视图,但是 RESTful Web 服务一般会将返回的数据以 JSON 的形式返回,这也就是现在所推崇的前后端分离开发。
假如我们有一个书架,上面放了很多书。为此,我们需要新建一个 Book
实体类。
@Datapublic class Book {
private String name;
private String description;
}
我们还需要一个控制器对书架上进行添加、查找以及查看。为此,我们需要新建一个BookController
@RestController
@RequestMapping("/api")
public class BookController {
private List<Book> books = new ArrayList<>();
@PostMapping("/book")
public ResponseEntity<List<Book>> addBook(@RequestBody Book book) {
books.add(book);
return ResponseEntity.ok(books);
}
@DeleteMapping("/book/{id}")
public ResponseEntity deleteBookById(@PathVariable("id") int id) {
books.remove(id);
return ResponseEntity.ok(books);
}
@GetMapping("/book")
public ResponseEntity getBookByName(@RequestParam("name") String name) {
List<Book> results = books.stream().filter(book -> book.getName().equals(name)).collect(Collectors.toList());
return ResponseEntity.ok(results);
}
}
- @RestController 将返回的对象数据直接以 JSON 或 XML 形式写入 HTTP 响应(Response)中。绝大部分情况下都是直接以 JSON 形式返回给客户端,很少的情况下才会以 XML 形式返回。转换成 XML 形式还需要额为的工作,上面代码中演示的直接就是将对象数据直接以 JSON 形式写入 HTTP 响应(Response)中。
- @RequestMapping :上面的示例中没有指定 GET 与 PUT、POST 等,因为@RequestMapping默认映射所有HTTP Action,你可以使用@RequestMapping(method=ActionType)来缩小这个映射。
- @PostMapping实际上就等价于 @RequestMapping(method = RequestMethod.POST),同样的 @DeleteMapping ,@GetMapping也都一样,常用的 HTTP Action 都有一个这种形式的注解所对应。
- @PathVariable :取url地址中的参数。@RequestParam url的查询参数值。
- @RequestBody:可以将 HttpRequest body 中的 JSON 类型数据反序列化为合适的 Java 类型。
- ResponseEntity: 表示整个HTTP Response:状态码,标头和正文内容。我们可以使用它来自定义HTTP Response 的内容。
4.SpringBoot 实现WebMvcConfigurer拦截器
4.1 WebMvcConfigurer介绍
WebMvcConfigurer
配置类,其实是Spring内部的一种配置方式,采用JavaBean的形式来代替传统的xml配置文件形式,进行针对框架个性化定制。我们可以在这里自定义一些Handler,Interceptor,ViewResolver,MessageConverter。基于java-based方式的Spring MVC配置,需要创建一个配置类并实现WebMvcConfigurer
接口。
4.2 WebMvcConfigurer接口常用方法
/* 拦截器配置 */
void addInterceptors(InterceptorRegistry var1);
/* 视图跳转控制器 */
void addViewControllers(ViewControllerRegistry registry);
/** 静态资源处理 **/
void addResourceHandlers(ResourceHandlerRegistry registry);
/* 默认静态资源处理器 */
void configureDefaultServletHandling(DefaultServletHandlerConfigurer configurer);
/** 这里配置视图解析器 */
void configureViewResolvers(ViewResolverRegistry registry);
/* 配置内容裁决的一些选项 */
void configureContentNegotiation(ContentNegotiationConfigurer configurer);
/** 解决跨域问题 **/
public void addCorsMappings(CorsRegistry registry) ;
4.3 WebMvcConfigurer拦截器
/**
* 登录认证统一拦截器
*
*/
@Configuration
public class AuthConfig implements WebMvcConfigurer {
@Resource
private TokenInterceptor tokenInterceptor;
@Override
public void addInterceptors(InterceptorRegistry registry) {
// 自定义拦截器,添加拦截路径和排除拦截路径
registry.addInterceptor(tokenInterceptor).addPathPatterns("/api/**")
.excludePathPatterns(
"/swagger-resources/**",
"/webjars/**",
"/v2/**",
"/swagger-ui.html/**",
"/dayu/**",
"/actuator/health",
"/info"
)
.order(2);
}
}
这里.addInterceptor()
方法需要HandlerInterceptor
类型参数,一般我们自定义一个类实现HandlerInterceptor
接口重写preHandle、postHandle、afterCompletion
方法
public boolean preHandle(HttpServletRequest request,
HttpServletResponse response,
Object handler)
public void postHandle(HttpServletRequest request,
HttpServletResponse response,
Object handler,
ModelAndView modelAndView)
public void afterCompletion(HttpServletRequest request,
HttpServletResponse response,
Object handler,
Exception ex)
- 三个方法的运行顺序为: preHandle -> postHandle -> afterCompletion;
- 如果preHandle返回值为false,三个方法仅运行preHandle;
- 如果运行拦截放行后的代码出错,则不会执行postHandle;
- 自定义拦截器实例需要实现HandleInterceptor接口;
5.SpringBoot常用注解总结
- @SpringBootApplication
这个注解是 Spring Boot 项目的基石,创建 SpringBoot 项目之后会默认在主类加上。
@SpringBootApplication
public class SpringSecurityJwtGuideApplication {
public static void main(java.lang.String[] args) {
SpringApplication.run(SpringSecurityJwtGuideApplication.class, args);
}
}
以把 @SpringBootApplication
看作是 @Configuration、@EnableAutoConfiguration、@ComponentScan
注解的集合。
@EnableAutoConfiguration
:启用 SpringBoot 的自动配置机制
@ComponentScan
: 扫描被@Component (@Repository,@Service,@Controller)
注解的 bean,注解默认会扫描该类所在的包下所有的类。
@Configuration
:允许在 Spring 上下文中注册额外的 bean 或导入其他配置类
- @Autowired
自动导入对象到类中,被注入进的类同样要被 Spring 容器管理比如:Service 类注入到 Controller 类中。
@Service
public class UserService {
......
}
@RestController
@RequestMapping("/users")
public class UserController {
@Autowired
private UserService userService;
......
}
- @Component,@Repository,@Service, @Controller
我们一般使用 @Autowired 注解让 Spring 容器帮我们自动装配 bean。要想把类标识成可用于 @Autowired 注解自动装配的 bean 的类,可以采用以下注解实现:
@Component
:通用的注解,可标注任意类为 Spring 组件。如果一个 Bean 不知道属于哪个层,可以使用@Component 注解标注。@Repository
: 对应持久层即 Dao 层,主要用于数据库相关操作。@Service
: 对应服务层,主要涉及一些复杂的逻辑,需要用到 Dao 层。@Controller
: 对应 Spring MVC 控制层,主要用于接受用户请求并调用 Service 层返回数据给前端页面。
- @RestController
@RestController
注解是@Controller和@ResponseBody
的合集,表示这是个控制器 bean,并且是将函数的返回值直接填入 HTTP 响应体中,是 REST 风格的控制器。 - @Scope
声明 Spring Bean 的作用域,使用方法:
@Bean
@Scope("singleton")
public Person personSingleton() {
return new Person();
}
四种常见的 Spring Bean 的作用域:
- singleton : 唯一 bean 实例,Spring 中的 bean 默认都是单例的。
- prototype : 每次请求都会创建一个新的 bean 实例。
- request : 每一次 HTTP 请求都会产生一个新的 bean,该 bean 仅在当前 HTTP request 内有效。
- session : 每一个 HTTP Session 会产生一个新的 bean,该 bean 仅在当前 HTTP session 内有效。
- @Configuration
一般用来声明配置类,可以使用@Component
注解替代,不过使用@Configuration
注解声明配置类更加语义化。
@Configuration
public class AppConfig {
@Bean
public TransferService transferService() {
return new TransferServiceImpl();
}
}
- 常见的HTTP请求
5 种常见的请求类型:
- GET :请求从服务器获取特定资源。举个例子:GET /users(获取所有学生)
- POST :在服务器上创建一个新的资源。举个例子:POST /users(创建学生)
- PUT :更新服务器上的资源(客户端提供更新后的整个资源)。举个例子:PUT /users/12(更新编号为 12 的学生)
- DELETE :从服务器删除特定的资源。举个例子:DELETE /users/12(删除编号为 12 的学生)
- PATCH :更新服务器上的资源(客户端提供更改的属性,可以看做作是部分更新),使用的比较少。
GET 请求:
@GetMapping("users") 等价于@RequestMapping(value="/users",method=RequestMethod.GET)
@GetMapping("/users")
public ResponseEntity<List<User>> getAllUsers() {
return userRepository.findAll();
}
POST 请求
@PostMapping("users") 等价于@RequestMapping(value="/users",method=RequestMethod.POST)
@PostMapping("/users")
public ResponseEntity<User> createUser(@Valid @RequestBody UserCreateRequest userCreateRequest) {
return userRespository.save(userCreateRequest);
}
PUT 请求
@PutMapping("/users/{userId}") 等价于@RequestMapping(value="/users/{userId}",method=RequestMethod.PUT)
@PutMapping("/users/{userId}")
public ResponseEntity<User> updateUser(@PathVariable(value = "userId") Long userId,
@Valid @RequestBody UserUpdateRequest userUpdateRequest) {
......
}
DELETE 请求
@DeleteMapping("/users/{userId}")等价于@RequestMapping(value="/users/{userId}",method=RequestMethod.DELETE)
@DeleteMapping("/users/{userId}")
public ResponseEntity deleteUser(@PathVariable(value = "userId") Long userId){
......
}
PATCH 请求
@PatchMapping("/profile")
public ResponseEntity updateStudent(@RequestBody StudentUpdateRequest studentUpdateRequest) {
studentRepository.updateDetail(studentUpdateRequest);
return ResponseEntity.ok().build();
}
- @PathVariable 和 @RequestParam
@PathVariable
用于获取路径参数,@RequestParam
用于获取查询参数。
@GetMapping("/klasses/{klassId}/teachers")
public List<Teacher> getKlassRelatedTeachers(
@PathVariable("klassId") Long klassId,
@RequestParam(value = "type", required = false) String type ) {
...
}
如果我们请求的 url 是:/klasses/123456/teachers?type=web
那么我们服务获取到的数据就是:klassId=123456,type=web
- @RequestBody
用于读取 Request 请求(可能是 POST,PUT,DELETE,GET 请求)的 body 部分并且Content-Type 为 application/json 格式的数据,接收到数据之后会自动将数据绑定到 Java 对象上去。系统会使用HttpMessageConverter
或者自定义的HttpMessageConverter
将请求的 body 中的 json 字符串转换为 java 对象。
@PostMapping("/sign-up")
public ResponseEntity signUp(@RequestBody @Valid UserRegisterRequest userRegisterRequest) {
userService.save(userRegisterRequest);
return ResponseEntity.ok().build();
}
UserRegisterRequest对象:
@Data
@AllArgsConstructor
@NoArgsConstructor
public class UserRegisterRequest {
@NotBlank
private String userName;
@NotBlank
private String password;
@NotBlank
private String fullName;
}
- 读取配置信息的注解
@Value
(常用)
@Value("${beijing}")
String beijing
@ConfigurationProperties(常用)
通过@ConfigurationProperties
读取配置信息并与 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;
}
}
beijing: 冬奥会
library:location: 中国加油
books:- name: 天才基本法
description: 二十二岁的林朝夕在父亲确诊阿尔茨海默病这天,得知自己暗恋多年的校园男神裴之即将出国深造的消息——对方考取的学校,恰是父亲当年为她放弃的那所。
- name: 时间的秩序
description: 为什么我们记得过去,而非未来?时间“流逝”意味着什么?是我们存在于时间之内,还是时间存在于我们之中?卡洛·罗韦利用诗意的文字,邀请我们思考这一亘古难题——时间的本质。
- name: 了不起的我
description: 如何养成一个新习惯?如何让心智变得更成熟?如何拥有高质量的关系? 如何走出人生的艰难时刻?
@PropertySource(不常用)
@Component
@PropertySource("classpath:website.properties")
class WebSite {
@Value("${url}")
private String url;
省略getter/setter
......
}
- 参数校验
一些常用的参数校验
@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
被注释的元素必须是一个将来的日期
例子:
@Data
@AllArgsConstructor
@NoArgsConstructor
public class Person {
@NotNull(message = "classId 不能为空")
private String classId;
@Size(max = 33)
@NotNull(message = "name 不能为空")
private String name;
@NotNull(message = "sex 不能为空")
private String sex;
@Email(message = "email 格式不正确")
@NotNull(message = "email 不能为空")
private String email;
}
我们在需要验证的参数上加上了@Valid注解,如果验证失败,它将抛出MethodArgumentNotValidException
@RestController
@RequestMapping("/api")
public class PersonController {
@PostMapping("/person")
public ResponseEntity<Person> getPerson(@RequestBody @Valid Person person) {
return ResponseEntity.ok().body(person);
}
}
@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);
}
}
- JPA相关
@Entity声明一个类对应一个数据库实体。
@Table 设置表名
@Entity
@Data
@Table(name = "role")
public class Role {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String name;
private String description;
}
@Column
声明字段
@Column(name = "user_name", nullable = false, length=32)
private String userName;
6.SpringBoot异常处理
6.1 使用 @ControllerAdvice和@ExceptionHandler处理全局异常
在JavaWeb系统的开发中,不管是dao层还是service层、controller层都有可能抛出异常。在SpringMvc中,我们将所有类型的异常处理从各个单独的方法中解耦出来,进行异常信息的统一处理和维护。下面举例如何使用@ControllerAdvice将Controller层的异常和数据校验的异常进行统一处理。
6.1.1定义统一异常处理类
首先,定义一个使用@ControllerAdvice
标注的WikiExceptionHandler
类,再声明一个@ExceptionHandler
标注的方法defaultErrorHandler,代码如下:
6.1.2信息展示
对应的请求视图映射在 个继承 WebMvcConfigurationSupport 类的 WebMvcConfig类的addViewControllers方法中代码如下:
7.SpringBoot项目的一般命名规范
7.1 在RESTful下,路径@RequestMapping(“/api/v1/amis/ebook”)
在Controller要分类分层不同的层级在下面分开 如:
/**
* @version 1.0
* @date 2022/09/05
*/
@IgnoreCsrfCheck
@RestController
@RequestMapping("/api/v1/amis/ebook")
@Api(tags = "[AMIS]电子手册模块")
public class AmisEbookController {
@Resource
AmisEbookService amisEbookService;
@ApiOperation("根据关键字查询电子手册信息")
@PostMapping("/query")
public HttpJsonResponse queryEbookList(@Validated @RequestBody EbookQueryParam param) {
return HttpJsonResponse.success().add(amisEbookService.queryEbookList(param));
}
@ApiOperation("分页查询白名单用户信息")
@PostMapping("/user/search")
public HttpJsonResponse pageSearchUserList(@Validated @RequestBody EbookPageSearchUserParam param) {
return HttpJsonResponse.success().add(amisEbookService.pageSearchUserList(param));
}
@ApiOperation("删除白名单用户")
@PostMapping("/user/delete/{id}")
public HttpJsonResponse delUser(@Validated @PathVariable Long id) {
amisEbookService.delUser(id);
return HttpJsonResponse.success().add(new AmisPageVo<>());
}
@ApiOperation("批量增加白名单用户")
@PostMapping("/user/add-white-user")
public HttpJsonResponse batchAddUser(@Validated @RequestBody EbookUserAddParam param) {
amisEbookService.batchAddUser(param);
return HttpJsonResponse.success().add(new AmisPageVo<>());
}
}