Spring Boot入门(二)开发web应用

Web开发

Spring Boot Web 开发非常的简单,其中包括常用的 json 输出、filters、property、log 等

返回json

在控制器上添加@RestController

Spring Boot默认类中的方法都会以 json 的格式返回

@RestController
public class HelloController {
    @RequestMapping("/getUser")
    public User getUser() {
        User user = new User();
        user.setUser("张三");
        return user;
    }
}

User类

package com.example.demo.model;

import lombok.Data;

@Data
public class User {
    private String User;
}

@Data是lombok的注解用于生成get、set方法

引用如下

<dependency>
    <groupId>org.projectlombok</groupId>
    <artifactId>lombok</artifactId>
</dependency>

访问localhost:8080/getUser

{"user":"张三"}

如果需要使用页面开发只要使用@Controller注解即可

@RestController等同于

@Controller
@ResponseBody
public class HelloController {
}

自定义Filter

我们常常在项目中会使用 filters 用于录调用日志、排除有 XSS 威胁的字符、执行权限验证等等。Spring Boot 自动添加了 OrderedCharacterEncodingFilter 和 HiddenHttpMethodFilter,并且我们可以自定义 Filter。

@Configuration注解拦截

两个步骤:

  1. 实现 Filter 接口,实现 Filter 方法
  2. 添加@Configuration 注解,将自定义Filter加入过滤链
package com.example.demo.filter;
import javax.servlet.*;
import javax.servlet.http.HttpServletRequest;
import java.io.IOException;

public class MyFilter implements Filter {
    @Override
    public void destroy() {
        // TODO Auto-generated method stub
    }

    @Override
    public void doFilter(ServletRequest srequest, ServletResponse sresponse, FilterChain filterChain)
            throws IOException, ServletException {
        // TODO Auto-generated method stub
        HttpServletRequest request = (HttpServletRequest) srequest;
        System.out.println("this is MyFilter,url :"+request.getRequestURI());
        filterChain.doFilter(srequest, sresponse);
    }

    @Override
    public void init(FilterConfig arg0) throws ServletException {
        // TODO Auto-generated method stub
    }
}

package com.example.demo.config;

import com.example.demo.filter.MyFilter;
import org.apache.catalina.filters.RemoteIpFilter;
import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class WebConfiguration {
    @Bean
    public RemoteIpFilter remoteIpFilter() {
        return new RemoteIpFilter();
    }

    @Bean
    public FilterRegistrationBean testFilterRegistration() {

        FilterRegistrationBean registration = new FilterRegistrationBean();
        registration.setFilter(new MyFilter());
        registration.addUrlPatterns("/*");
        registration.addInitParameter("paramName", "paramValue");
        registration.setName("MyFilter");
        registration.setOrder(1);
        return registration;
    }
}

运行后访问localhost:8080/getUser 控制台打印

this is MyFilter,url :/getUser

@WebFilter + @ServletComponentScan注解拦截

在MyFilter类中添加@WebFilter(urlPatterns = "/*", filterName = "reqResFilter")注解

package com.example.demo.filter;
import javax.servlet.*;
import javax.servlet.annotation.WebFilter;
import javax.servlet.http.HttpServletRequest;
import java.io.IOException;

@WebFilter(urlPatterns = "/*", filterName = "reqResFilter")
public class MyFilter implements Filter {
    @Override
    public void destroy() {
        // TODO Auto-generated method stub
    }

    @Override
    public void doFilter(ServletRequest srequest, ServletResponse sresponse, FilterChain filterChain)
            throws IOException, ServletException {
        // TODO Auto-generated method stub
        HttpServletRequest request = (HttpServletRequest) srequest;
        System.out.println("this is MyFilter,url :"+request.getRequestURI());
        filterChain.doFilter(srequest, sresponse);
    }

    @Override
    public void init(FilterConfig arg0) throws ServletException {
        // TODO Auto-generated method stub
    }
}

在Spring Boot启动类添加@ServletComponentScan

@SpringBootApplication
@ServletComponentScan
public class DemoApplication {

	public static void main(String[] args) {
		SpringApplication.run(DemoApplication.class, args);
	}

}

运行后访问localhost:8080/getUser 控制台打印

this is MyFilter,url :/getUser

自定义配置

在 Web 开发的过程中,我经常需要自定义一些配置文件,如何使用呢

配置application.properties

resources-> application.properties 中

config.title="配置标题"
config.description="标题描述"

自定义配置类

package com.example.demo.config;

import lombok.Data;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;

@Component
@Data
public class ConfigProperties {
    @Value("${config.title}")
    private String title;
    @Value("${config.description}")
    private String description;
}

重新修改下HelloWorldController文件

package com.example.demo.controller;

import com.example.demo.config.ConfigProperties;
import com.example.demo.model.User;
import lombok.Data;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
@Data
public class HelloWorldController {

    @Autowired
    private ConfigProperties config;

    @RequestMapping("/getUser")
    public User getUser() {
        User user = new User();
        user.setUser(config.getTitle()+config.getDescription());
        return user;
    }
}

重新启动

访问localhost:8080/getUser

输出

{"user":"\"配置标题\"\"标题描述\""}

配置文件乱码

  1. 设置 File Encodings的Transparent native-to-ascii conversion为true,具体步骤如下:依次点击

  File -> Settings -> Editor -> File Encodings

  将Properties Files (*.properties)下的Default encoding for properties files设置为UTF-8,将Transparent native-to-ascii conversion前的勾选上

2 .配置完成后,一定要 重新重新重新 新建一个application.properties

使用yml

删除application.properties 文件

新建application.yml文件

config:
  title: "配置标题"
  description: "标题描述"

重启

访问localhost:8080/getUser

输出

{"user":"配置标题标题描述"}

Spring Boot Rest API

REST介绍

REST代表Representational State Transfer. 是一种架构风格,设计风格而不是标准,可用于设计Web服务,可以从各种客户端使用.

基于REST的基本设计,其是根据一组动词来控制的操作

  • 创建操作:应使用HTTP POST
  • 查询操作:应使用HTTP GET
  • 更新操作:应使用HTTP PUT
  • 删除操作:应使用HTTP DELETE

作为REST服务开发人员或客户端,您应该遵守上述标准。

新建HomeController

package com.example.demo.controller;

import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class HomeController {

    @RequestMapping(method = RequestMethod.GET)
    public String Get(){
        return "get";
    }
    @RequestMapping(method = RequestMethod.POST)
    public String insert(){
        return "insert";
    }
    @RequestMapping(method = RequestMethod.PUT)
    public String update(){
        return "update";
    }
    @RequestMapping(method = RequestMethod.DELETE)
    public String delete(){
        return "delete";
    }
}

通过POSTMAN访问

localhost:8080/home

也可以通过指定的注解@GetMapping@PostMapping@PutMapping@DeleteMapping指定不同的请求方式与上面的方法相同

package com.example.demo.controller;

import org.springframework.web.bind.annotation.*;

@RestController
public class HomeController {

    @GetMapping("/home")
    public String Get(){
        return "get";
    }
    @PostMapping("/home")
    public String insert(){
        return "insert";
    }
    @PutMapping("/home")
    public String update(){
        return "update";
    }
    @DeleteMapping("/home")
    public String delete(){
        return "delete";
    }
}

Log

使用自带日志Slf4j

log4j2 漏洞解析 - 池的巧克力 - 博客园 (cnblogs.com)

Spring Boot 版本不要低于2.5.8

org.apache.logging.log4j > 2.15

如果你的Spring Boot 版本低于2.5.8

使用的是父 POM,则可以设置以下属性:log4j2.version

<properties>
    <log4j2.version>2.17.0</log4j2.version>
</properties>

如果您没有父级,而是导入 BOM 表,则需要使用一个部分:

<dependencyManagement>
    <dependencies>
        <dependency>
            <groupId>org.apache.logging.log4j</groupId>
            <artifactId>log4j-bom</artifactId>
            <version>2.17.0</version>
            <scope>import</scope>
            <type>pom</type>
        </dependency>
    </dependencies>
</dependencyManagement>

或者参考这里:Log4J2 Vulnerability and Spring Boot

package com.example.demo.controller;

import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.*;

@Slf4j
@RestController
public class HomeController {

    @GetMapping("/home")
    public String Get(){
        log.info("日志记录");
        return "get";
    }
    @PostMapping("/home")
    public String insert(){
        return "insert";
    }
    @PutMapping("/home")
    public String update(){
        return "update";
    }
    @DeleteMapping("/home")
    public String delete(){
        return "delete";
    }
}

如果没有配置文件就只在控制台输出

2021-12-22 15:50:56.745  INFO 34316 --- [nio-8080-exec-1] c.e.demo.controller.HomeController       : 日志记录

默认情况下,Spring Boot 仅记录到控制台,不写入日志文件。如果要写入除控制台输出之外的日志文件,则需要设置 属性

在application.yml文件中

logging:
  file:
    name: my.log

然后在根目录查看生成的文件

查看更多信息核心特性 (spring.io)

数据库操作(MyBatis)

如果你不会使用MyBatis请查看MyBatis(一)MyBatis初识 - 青杉 - 博客园 (cnblogs.com)

引入依赖

<!--引入 mybatis-spring-boot-starter 的依赖-->
<dependency>
    <groupId>org.mybatis.spring.boot</groupId>
    <artifactId>mybatis-spring-boot-starter</artifactId>
    <version>2.1.3</version>
</dependency>
<dependency>
    <groupId>mysql</groupId>
    <artifactId>mysql-connector-java</artifactId>
    <version>8.0.27</version>
</dependency>

配置 MyBatis

在 Spring Boot 的配置文件(application.properties/yml)中对 MyBatis 进行配置,例如指定 mapper.xml 的位置、实体类的位置、是否开启驼峰命名法等等,示例代码如下。

mybatis:
  # 指定 mapper.xml 的位置
  mapper-locations: classpath:mapper/*.xml
  #扫描实体类的位置,在此处指明扫描实体类的包,在 mapper.xml 中就可以不写实体类的全路径名
  type-aliases-package: com.example.demo.mapper
  configuration:
    #默认开启驼峰命名法,可以不用设置该属性
    map-underscore-to-camel-case: true
spring:
  datasource:
    url: jdbc:mysql://localhost:3306/jdbc_test?useUnicode=true&characterEncoding=utf8&useSSL=false
    username: root
    password: 123456
    driver-class-name: "com.mysql.cj.jdbc.Driver"

注意:使用 MyBatis 时,必须配置数据源信息,例如数据库 URL、数据库用户型、数据库密码和数据库驱动等。

Sql语句

CREATE DATABASE IF NOT EXISTS `jdbc_test`;

DROP TABLE IF EXISTS `blog`;
CREATE TABLE `blog` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `blog_title` varchar(20),
  `desc` varchar(300),
  PRIMARY KEY (`id`)
) ENGINE=InnoDB;

创建实体类

package com.example.demo.entity;

import lombok.Data;

@Data
public class Blog {
    private int id;
    private String blogTitle;
    private String desc;
}

创建 Mapper 接口

package com.example.demo.mapper;

import com.example.demo.entity.Blog;
import org.apache.ibatis.annotations.Mapper;

import java.util.List;

@Mapper
public interface BlogMapper {
    int insert(Blog blog);
    int delete(int id);
    int update(Blog blog);
    List<Blog> getAll();

}

创建 Mapper 映射文件

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
        PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.example.demo.mapper.BlogMapper">
    <resultMap id="blogMap" type="com.example.demo.entity.Blog">
        <id property="id" column="id" />
        <result property="blogTitle" column="blog_title"/>
        <result property="desc" column="desc"/>
    </resultMap>
    <select id="getAll" resultMap="blogMap">
        select * from Blog
    </select>
    <insert id="insert">
        insert into Blog(blog_title,`desc`) value (#{blogTitle},#{desc})
    </insert>
    <update id="update">
        update Blog set blog_title=#{blogTitle} where id=#{id}
    </update>
    <update id="delete">
        delete from Blog where id=#{id}
    </update>
</mapper>

当 mapper 接口较多时,我们可以在 Spring Boot 主启动类上使用 @MapperScan 注解扫描指定包下的 mapper 接口,而不再需要在每个 mapper 接口上都标注 @Mapper 注解。

创建BlogController控制器

package com.example.demo.controller;

import com.example.demo.entity.Blog;
import com.example.demo.mapper.BlogMapper;
import lombok.Data;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;

import javax.annotation.Resource;
import java.util.List;

@RestController
@Data
public class BlogController {

    @Resource
    private BlogMapper blogMapper;

    @GetMapping("/blog")
    public List<Blog> GetAll(){
        return blogMapper.getAll();
    }
    @PostMapping("/blog")
    public int insert(@RequestBody Blog blog){
        return blogMapper.insert(blog);
    }
    @PutMapping("/blog")
    public int update(@RequestBody Blog blog){
        return blogMapper.update(blog);
    }
    @DeleteMapping("/blog/{id}")
    public int delete(@PathVariable int id){
        return blogMapper.delete(id);
    }

}

使用postMan测试

事务

在方法上@Transactional

如下

@PostMapping("/blog")
@ApiOperation(value = "添加博客")
@Transactional
public int insert(@RequestBody Blog blog){
    blogMapper.insert(blog);
    int i = 1/0;
    return blogMapper.insert(blog);
}

Spring Boot全局异常处理

package com.example.demo.filter;


import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseBody;

import javax.servlet.http.HttpServletRequest;

@Slf4j
@ControllerAdvice
public class GlobalExceptionHander {
    @ResponseBody
    @ExceptionHandler(value =Exception.class)
    public String handleControllerException(HttpServletRequest request, Exception ex) {
        log.error("发生错误:",ex);
        return ex.getMessage();
    }
}

Swagger

springfox/springfox:使用Spring构建的API的自动JSON API文档 (github.com)

SpringFox by springfox

1.引入依赖

<!--引入 swagger 的依赖-->
<dependency>
   <groupId>io.springfox</groupId>
   <artifactId>springfox-boot-starter</artifactId>
   <version>3.0.0</version>
</dependency>

问题

如果出现以下错误

org.springframework.context.ApplicationContextException: Failed to start bean 'documentationPluginsBootstrapper'; nested exception is java.lang.NullPointerException

这是因为Springfox使用的路径匹配是基于AntPathMatcher的,而Spring Boot 2.6.X使用的是PathPatternMatcher。

需要添加以下配置

spring:
  mvc:
    pathmatch:
      matching-strategy: ant_path_matcher

2. 应用主类增加注解@EnableOpenApi

@EnableOpenApi
@SpringBootApplication
public class DemoApplication {
 
    public static void main(String[] args) {
        SpringApplication.run(DemoApplication.class, args);
    }
}

3.配置一些接口例子

@Api(tags = "博客管理")
@RestController
@Data
public class BlogController {

    @Resource
    private BlogMapper blogMapper;

    @ApiOperation(value = "获取所有博客")
    @GetMapping("/blog")
    public List<Blog> GetAll(){
        return blogMapper.getAll();
    }
    @PostMapping("/blog")
    @ApiOperation(value = "添加博客")
    public int insert(@RequestBody Blog blog){
        return blogMapper.insert(blog);
    }
    @PutMapping("/blog")
    @ApiOperation(value = "修改博客")
    public int update(@RequestBody Blog blog){
        return blogMapper.update(blog);
    }
    @DeleteMapping("/blog/{id}")
    @ApiOperation(value = "删除博客")
    public int delete(@PathVariable int id){
        return blogMapper.delete(id);
    }

}
package com.example.demo.entity;

import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;

@ApiModel(description = "博客")
@Data
public class Blog {
    @ApiModelProperty("id")
    private int id;
    @ApiModelProperty("博客标题")
    private String blogTitle;
    @ApiModelProperty("描述")
    private String desc;
}

4.启动

启动应用!访问swagger页面:http://localhost:8080/swagger-ui/index.html

模型验证

引入

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

使用

import lombok.Data;
import org.hibernate.validator.constraints.Length;

import javax.validation.constraints.NotEmpty;


@Data
public class UserDto {
    @Length(min = 6,max = 18,message = "password长度必须位于6到18之间")
    private String password;
    @NotEmpty(message = "请填写手机号")
    private String mobile;
}
/**
 * 类注解
 */
@RestController
@RequestMapping("test")
public class TestController {
    @RequestMapping(value = "/login",method = RequestMethod.POST)
    public UserDto Test(@Validated UserDto user){
        return u;
    }
}

更多: Spring MVC 模型验证 - 青杉 - 博客园 (cnblogs.com)

原文地址:https://www.cnblogs.com/qs315/p/15726005.html