ramostear.comramostear.com 谭朝红的技术分享博客

格言 编程是一门技术,也是一门艺术 !

优雅的处理Spring Boot异常信息

优雅的处理Spring Boot异常信息

Spring Boot (七)— 异常处理

异常处理是一种识别并响应错误的一致性机制,异常机制可以把程序中的异常处理代码和正常的业务逻辑代码分离,包装程序的可读性和健壮性。在Spring Boot应用程序中,能够捕获并及时的响应客户端的错误操作是一件非常重要的事情。在本章节中,我将展示如何处理Spring Boot中的异常。

1. 相关注解说明

在进行演示之前,我们先了解一下在Spring Boot应用程序中与异常处理相关的几个注解

注解名称 说明
@ControllerAdvice 该标签用于处理全局的异常信息
@ExceptionHadler 用于处理特定异常信息,并返回相关的响应到客户端

首先,我们需要使用@ControllerAdvice注解来定义一个全局的异常信息处理类,其语法如下:

package com.ramostear.exception.handler;

import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;

/**
 * @author : ramostear
 * @date : 2019/3/6 0006-16:33
 */
@ControllerAdvice
public class UserExceptionHandler {
    //TODO ...
}

接下来,我们需要定义一个扩展了RuntimeException类的自定义异常处理类:

package com.ramostear.exception.handler;

/**
 * @author : ramostear
 * @date : 2019/3/6 0006-16:31
 */
public class UserNotFoundException extends RuntimeException{
    private static final long serialVersionUID = 5534028121297403043L;
}

最后,我们使用@ExceptionHandler注解来定义一个处理具体异常信息的方法,其语法如下:

@ExceptionHandler(value = UserNotFoundException.class)
    public ResponseEntity<Object> exception(UserNotFoundException ex){
        return new ResponseEntity<>("user not found.", HttpStatus.NOT_FOUND);
    }

以上工作准备完成之后,我们可以使用如下的方式来处理API中的异常信息:

@GetMapping("/users/{id}")
    public ResponseEntity<Object> getUser(@PathVariable(name = "id") long id){
        if(!userRepo.containsKey ( id )){
            throw new UserNotFoundException ();
        }
        return new ResponseEntity<> (userRepo.get (id), HttpStatus.OK);
    }

在接下来的内容当中,我将给出完整的示例代码,使用HTTP GET方法请求一个用户信息,当用户存储库中没有相应的用户信息时,返回“user not found”提示信息。

2. 自定义异常信息类 — UserNotFoundException.java

package com.ramostear.exception.handler;

/**
 * @author : ramostear
 * @date : 2019/3/6 0006-16:31
 */
public class UserNotFoundException extends RuntimeException{
    private static final long serialVersionUID = 5534028121297403043L;
}

说明:这里只是做了一个简单的扩展

2. 全局异常处理类 —UserExceptionHandler.java

package com.ramostear.exception.handler;

import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;

/**
 * @author : ramostear
 * @date : 2019/3/6 0006-16:33
 */
@ControllerAdvice
public class UserExceptionHandler {


    @ExceptionHandler(value = UserNotFoundException.class)
    public ResponseEntity<Object> exception(UserNotFoundException ex){
        return new ResponseEntity<>("user not found.", HttpStatus.NOT_FOUND);
    }


}

在UserExceptionHandler.java文件中,我们定义了一个处理用户不存在异常的方法,

3. API类 — UserServiceController.java

package com.ramostear.exception.handler.controller;

import com.ramostear.exception.handler.UserNotFoundException;
import com.ramostear.exception.handler.model.User;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RestController;

import javax.annotation.PostConstruct;
import java.util.HashMap;
import java.util.Map;

/**
 * @author : ramostear
 * @date : 2019/3/6 0006-16:26
 */
@RestController
public class UserServiceController {

    private static Map<Long,User> userRepo = new HashMap<>();

    @PostConstruct
    public void initUserRepo(){
        User admin = new User ().setId ( 1 ).setName ( "admin" );
        userRepo.put ( admin.getId (),admin );

        User editor = new User ().setId ( 2 ).setName ( "editor" );
        userRepo.put ( editor.getId (),editor );
    }


    @GetMapping("/users/{id}")
    public ResponseEntity<Object> getUser(@PathVariable(name = "id") long id){
        if(!userRepo.containsKey ( id )){
            throw new UserNotFoundException ();
        }
        return new ResponseEntity<> (userRepo.get (id), HttpStatus.OK);
    }

}

在getUser()方法中,如果用户没有找到,则抛出UserNotFoundException异常。

4. 应用主类 —ExceptionHandlerApplication.java

package com.ramostear.exception.handler;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class ExceptionHandlerApplication {

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

}

5. 用户POJO类 — User.java

package com.ramostear.exception.handler.model;

import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;

/**
 * @author : ramostear
 * @date : 2019/3/6 0006-16:23
 */
@Getter
@Setter
@NoArgsConstructor
public class User {

    private long id;

    private String name;

    public User setId(long id){
        this.id = id;
        return this;
    }

    public User setName(String name){
        this.name = name;
        return this;
    }
}

6. Maven构建文件 — pom.xml

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.1.3.RELEASE</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>
    <groupId>com.ramostear</groupId>
    <artifactId>exception-handler</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>exception-handler</name>
    <description>Demo project for Spring Boot</description>

    <properties>
        <java.version>1.8</java.version>
    </properties>

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

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>

</project>

8. 运行测试

接下来,我们将打包运行我们的程序,本次教程演示将使用IDEA来运行程序,运行结果如下图所示:

然后,启动Postman应用程序,我们先在地址栏输入:http://localhost:8080/users/1 ,观察正常情况下的测试信息:

Postman的测试结果显示,请求状态为200,且返回了用户的详细信息。现在,我们更新URL为:http://localhost:8080/users/3 ,再次观察测试结果:

此时的HTTP Status为404,且返回了“user not found.”的提示信息。

使用Spring Boot构建RESTful 服务

使用Spring Boot构建RESTful 服务

Spring Boot(六) — 构建RESTful Web服务

Spring Boot能够轻松的构建起企业级的RESTful Web服务程序。在本章节中,我将通过一个简单的例子演示Spring Boot的这一能力。
为了构建RESTful Web服务,我们需要将Spring Boot Starter Web依赖添加到pom.xml配置文件中。

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

下面是完整的Maven pom.xml配置文件代码:

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.1.3.RELEASE</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>
    <groupId>com.ramostear</groupId>
    <artifactId>restful-webservice</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>restful-webservice</name>
    <description>Demo project for Spring Boot</description>

    <properties>
        <java.version>1.8</java.version>
    </properties>

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

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>

</project>

说明:Lombok是一个java库,可以自动插入编辑器并构建工具,使用lombok注解可以自动生成getter、setter以及构造函数等方法,你可以到Lombok官网了解更多详细信息。

1. RestController

@RestController注解用于定义RESTful Web服务,它提供JSON、XML以及自定义的相应信息,其使用语法如下:

@RestController
public class UserController{
    //TODO ...
}

2. RequestMapping

@RequestMapping注解用于定义访问REST Endpoint的请求路径。@RequestMapping的使用语法如下:

@RequestMapping("/users")
public ResponseEntity<Object> getUsers(){
    //TODO ...
}

注:@RequestMapping注解默认的请求方法GET.

在接下来的内容当中,我们将使用@RequestMapping的组合注解来定义REST Endpoint方法的映射路径:

3. RequestBody

@RequestBody注解用于定义请求正文内容类型。其语法如下:

public ResponseEntity<Object> createUser(@RequestBody User user){
    //TODO ...
}

4. PathVariable

@PathVariable注解用于定义请求路径中的动态变量,请求路径中的动态变量使用花括号“{}”包裹起来。语法如下:

@PutMapping("/users/{id}")
public ResponseEntity<Obejct> updateUser(@PathVariable("id") long id,@RequestBody User user){
    //TODO ...
}

5. GET API

默认的HTTP请求方法是GET,GET方法不需要任何的Request Body。你可以发送任何的请求参数和路径变量来定义一个动态的URL。接下来,我将演示如何定义一个HTTP GET请求方法,用于获取所有的用户信息。

package com.ramostear.restful.webservice.controller;

import com.ramostear.restful.webservice.model.User;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;

import javax.annotation.PostConstruct;
import java.util.HashMap;
import java.util.Map;

/**
 * @author ramostear
 * @create-time 2019/3/6 0006-3:14
 * @modify by :
 * @since:
 */
@RestController
public class UserServiceController {

    private static Map<Long,User> userRepository = new HashMap<>();

    @PostConstruct
    public void initUserRepository(){
        User admin = new User();
        admin.setId(1).setName("admin");
        userRepository.put(admin.getId(),admin);

        User editor = new User();
        editor.setId(2).setName("editor");
        userRepository.put(editor.getId(),editor);
    }

   @GetMapping("/users")
    public ResponseEntity<Object> getUser(){
        return new ResponseEntity<>(userRepository.values(), HttpStatus.OK);
    }

}

在这里,我使用了一个Hash Map来扮演用户存储库的角色,定义的请求路径是/users

6. POST API

HTTP POST请求用于创建新的资源,此方法包含了Request Body。下面我将演示如何定义HTTP POST方法来创建用户信息,并存储到用户存储库中。

package com.ramostear.restful.webservice.controller;

import com.ramostear.restful.webservice.model.User;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;

import javax.annotation.PostConstruct;
import java.util.HashMap;
import java.util.Map;

/**
 * @author ramostear
 * @create-time 2019/3/6 0006-3:14
 * @modify by :
 * @since:
 */
@RestController
public class UserServiceController {

    private static Map<Long,User> userRepository = new HashMap<>();

    @PostConstruct
    public void initUserRepository(){
        User admin = new User();
        admin.setId(1).setName("admin");
        userRepository.put(admin.getId(),admin);

        User editor = new User();
        editor.setId(2).setName("editor");
        userRepository.put(editor.getId(),editor);
    }


    @PostMapping("/users")
    public ResponseEntity<Object> createUser(@RequestBody User user){
        userRepository.put(user.getId(),user);
        return new ResponseEntity<>("User is created successfully",HttpStatus.CREATED);
    }
}

7. PUT API

HTTP PUT请求用于更新存储库中现有的资源,此方法包含请求正文。接下来我将给出相关示例来演示如何定义一个HTTP PUT请求方法来更新用户信息。

package com.ramostear.restful.webservice.controller;

import com.ramostear.restful.webservice.model.User;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;

import javax.annotation.PostConstruct;
import java.util.HashMap;
import java.util.Map;

/**
 * @author ramostear
 * @create-time 2019/3/6 0006-3:14
 * @modify by :
 * @since:
 */
@RestController
public class UserServiceController {

    private static Map<Long,User> userRepository = new HashMap<>();

    @PostConstruct
    public void initUserRepository(){
        User admin = new User();
        admin.setId(1).setName("admin");
        userRepository.put(admin.getId(),admin);

        User editor = new User();
        editor.setId(2).setName("editor");
        userRepository.put(editor.getId(),editor);
    }

    @PutMapping("/users/{id}")
    public ResponseEntity<Object> updateUser(@PathVariable(name = "id") long id,@RequestBody User user){
        userRepository.remove(id);
        user.setId(id);
        userRepository.put(id,user);
        return new ResponseEntity<>("User is updated successfully",HttpStatus.OK);
    }

}

在此方法中,定义的请求路径是/users/{id} ,其中{id}是动态的路径变量,用来定义需要更新的用户ID。

8. DELETE API

HTTP DELETE请求用于删除存储库中现有的资源,此方法不包含任何请求正文。接下来我将给出相关示例来演示如何定义一个HTTP PUT请求方法来更新用户信息。

package com.ramostear.restful.webservice.controller;

import com.ramostear.restful.webservice.model.User;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;

import javax.annotation.PostConstruct;
import java.util.HashMap;
import java.util.Map;

/**
 * @author ramostear
 * @create-time 2019/3/6 0006-3:14
 * @modify by :
 * @since:
 */
@RestController
public class UserServiceController {

    private static Map<Long,User> userRepository = new HashMap<>();

    @PostConstruct
    public void initUserRepository(){
        User admin = new User();
        admin.setId(1).setName("admin");
        userRepository.put(admin.getId(),admin);

        User editor = new User();
        editor.setId(2).setName("editor");
        userRepository.put(editor.getId(),editor);
    }


    @DeleteMapping("/users/{id}")
    public ResponseEntity<Object> delete(@PathVariable(name = "id") long id){
        userRepository.remove(id);
        return new ResponseEntity<>("User is deleted successfully",HttpStatus.OK);
    }

}

在此方法中,定义的请求路径是/users/{id} ,其中{id}是动态的路径变量,用来定义需要删除的用户ID。

9. 代码清单

9.1 Spring Boot应用程序主类 — RestfulWebserviceApplication.java

package com.ramostear.restful.webservice;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class RestfulWebserviceApplication {

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

}

9.2 POJO类 — User.java

package com.ramostear.restful.webservice.model;

import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;

/**
 * @author ramostear
 * @create-time 2019/3/6 0006-3:12
 * @modify by :
 * @since:
 */
@Getter
@Setter
@NoArgsConstructor
public class User {
    private long id;
    private String name;

    public User setId(long id){
        this.id = id;
        return this;
    }

    public User setName(String name){
        this.name = name;
        return this;
    }
}

9.3 Rest Controller类 — UserServiceController.java

package com.ramostear.restful.webservice.controller;

import com.ramostear.restful.webservice.model.User;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;

import javax.annotation.PostConstruct;
import java.util.HashMap;
import java.util.Map;

/**
 * @author ramostear
 * @create-time 2019/3/6 0006-3:14
 * @modify by :
 * @since:
 */
@RestController
public class UserServiceController {

    private static Map<Long,User> userRepository = new HashMap<>();

    @PostConstruct
    public void initUserRepository(){
        User admin = new User();
        admin.setId(1).setName("admin");
        userRepository.put(admin.getId(),admin);

        User editor = new User();
        editor.setId(2).setName("editor");
        userRepository.put(editor.getId(),editor);
    }

    @GetMapping("/users")
    public ResponseEntity<Object> getUser(){
        return new ResponseEntity<>(userRepository.values(), HttpStatus.OK);
    }

    @PostMapping("/users")
    public ResponseEntity<Object> createUser(@RequestBody User user){
        userRepository.put(user.getId(),user);
        return new ResponseEntity<>("User is created successfully",HttpStatus.CREATED);
    }

    @PutMapping("/users/{id}")
    public ResponseEntity<Object> updateUser(@PathVariable(name = "id") long id,@RequestBody User user){
        userRepository.remove(id);
        user.setId(id);
        userRepository.put(id,user);
        return new ResponseEntity<>("User is updated successfully",HttpStatus.OK);
    }

    @DeleteMapping("/users/{id}")
    public ResponseEntity<Object> delete(@PathVariable(name = "id") long id){
        userRepository.remove(id);
        return new ResponseEntity<>("User is deleted successfully",HttpStatus.OK);
    }

}

10. 测试

你可以选择命令行工具或者IDE来编译、打包并运行项目,这里我使用Intellij IDEA来运行项目,运行结果如下图所示:

接下来,我们将使用Postman应用程序来测试准备好的RESTful Web 服务接口:

  1. GET API : http://localhost:8080/users

  1. POST API : http://locahost:8080/users

  1. PUT API : http://locahost:8080/users/3

  1. DELETE API : http://localhost:8080/users/3

如何配置Spring Boot的日志记录信息?

如何配置Spring Boot的日志记录信息?

Spring Boot (五)—日志记录

Spring Boot 应用程序默认使用Apache Commons Logging来记录所有内部的日志。除此之外,Spring Boot还支持使用Java Util Longging、Log4j2和Logback来记录系统日志。我们可以通过配置相关参数,让其在控制台输出日志或者将日志写入到日志文件中。

通常情况下,如果使用Spring Boot Starter的方式引入依赖,系统将会使用Logback来记录日志,以此同时,Logback还可以和Common Logging、Util Loging、Log4j以及SLF4进行整合。

1. 日志的格式

在Spring Boot 应用程序中,默认的日志格式如下所示:

2019-03-05 02:02:00.013  INFO 7700 ---[main] .r.s.b.p.SpringBootPropertiesApplication : The following profiles are active: dev
2019-03-05 02:02:01.697  INFO 7700 --- [main] o.s.b.w.embedded.tomcat.TomcatWebServer  : Tomcat initialized with port(s): 8081 (http)
2019-03-05 02:02:01.732  INFO 7700 --- [main] o.apache.catalina.core.StandardService   : Starting service [Tomcat]

通常情况下,日志文件为我们提供了如下的几个信息:

  • 日期和时间,该日志记录的日期和时间
  • 日志级别,日志级别包括INFO、ERROR、DEBUG和WARN等
  • 进程编号,日志信息通常会包含当前的进程ID
  • —- ,信息分隔符
  • 线程名称,线程名称将使用方括号“[]”包含起来
  • 记录器名称,显示源文件类的名称
  • 日志消息

2. 控制台日志

Spring Boot应用程序在默认的情况下,“INFO”、“ERROR”和“WARN”级别的日志信息将打印到控制台窗口中,如果需要启用“DEBUG”级别的日志记录,需要在启动应用程序的时候添加日志记录级别标识命令:

java -jar jarfile.jar --debug

除此之外,也可以在applicatioin.properties或者application.yml文件中进行配置,以application.properties文件为例:

debug = true

3. 文件日志

默认情况下,所有的日志信息都将在控制台打印输出,而不会写入到日志文件中。如果需要将日志信息记录到日志文件中,则需要在application.properties或者application.yml配置文件中设置属性logging.file 或者logging.path的值。

我们可以显示的指定日志文件的存储路径,以application.properties为例,指定日志文件存储路径:

logging.path = D:\work\logging\

此时,日志文件默认的文件名称是spring.log

除了上面的配置之外,我们还可以同时指定日志文件存储路径和文件名称:

logging.file = D:\work\logging\mine-logging.log

注:一个日志文件最多能够存储10,000KB的日志信息。

4. 日志级别

Spring Boot应用程序支持所有的日志记录级别,如“TRACE”、“DEBUG”、“INFO”、”WARN”、”ERROR”、

“FATAL”以及”OFF”。你可以在配置文件中指定一个root logger:

logging.level.root = WARN

提示:Logback不支持“FATAL”级别的日志,“FATAL”级别的日志将被映射到“ERROR”级别的日志中。

5. 配置Logback

Logback支持一XML文件格式来配置Spring Boot应用程序日志。一般地、日志配置文件也应该放置在类路径下,下面给出Logback.xml文件的一个配置示例:

<?xml version="1.0" encoding="UTF-8"?>
<configuration>
 <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender"></appender>
 <root level = "INFO">
    <appender-ref ref = "STDOUT"/>
 </root>
</configuration>

接下来,我们将在Spring Boot Application主类中使用slf4j logger来记录日志。首先是Logback.xml文件配置:

<?xml version="1.0" encoding="utf-8"?>
<configuration>
    <appender name="STDOUT" class="cn.qos.logback.core.ConsoleAppender">
        <encoder>
            <pattern>[%d{yyyy-MM-dd'T'HH:mm:ss.sss'z'}][%C][%t][%L][%-5p] %m%n</pattern>
        </encoder>
    </appender>
    <appender name="FILE" class="cn.qos.logback.core.FileAppender">
        <File>D:\work\logging\mine-log.log</File>
        <encoder>
            <pattern>[%d{yyyy-MM-dd'T'HH:mm:ss.sss'z'}][%C][%t][%L][%-5p] %m%n</pattern>
        </encoder>
    </appender>
    <root level="INFO">
        <appender-ref ref="STDOUT"/>
        <appender-ref ref="FILE"/>
    </root>
</configuration>

主类源代码:

package com.ramostear.spring.boot.logger;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class SpringBootLoggerApplication {

    private static final Logger logger = LoggerFactory.getLogger(SpringBootLoggerApplication.class);
    public static void main(String[] args) {
        logger.info("This is a info level logger message.");
        logger.warn("This is a warn level logger message.");
        logger.error("This is a error level logger message.");
        SpringApplication.run(SpringBootLoggerApplication.class, args);
    }

}

现在,我们启动应用,观察控制台和日志文件中的内容。控制台窗口输出内容:

"C:\Program Files\Java\jdk1.8.0_144\bin\java"...
[2019-03-05T19:25:59.059Z] [com.ramostear.spring.boot.logger.SpringBootLoggerApplication] [main] [13] [INFO ] This is a info level logger message.
[2019-03-05T19:25:59.059Z] [com.ramostear.spring.boot.logger.SpringBootLoggerApplication] [main] [14] [WARN ] This is a warn level logger message.
[2019-03-05T19:25:59.059Z] [com.ramostear.spring.boot.logger.SpringBootLoggerApplication] [main] [15] [ERROR] This is a error level logger message.

接下来,我们再看看日志文件中记录的内容:

在Spring Boot程序中上传和下载文件

在Spring Boot程序中上传和下载文件

Spring Boot(十一)— 文件上传和下载

文件上传和下载是Web应用程序比较常用的功能之一,在本章节中,我将以一个简单的案例来讲解在Spring Boot中如何进行文件的上传与下载。在开始正文之前,我们通过一张思维导图来了解一下文件上传与下载的简单流程:

文件上传与下载思维导图

1. 文件上传

对于文件上传,控制器中对应的上传方法的参数必须是MultipartFile对象,MultipartFile对象可以是一个数组对象,也可以是单个对象,如果是一个数组对象,则可以进行多文件上传;这里我们仅演示单个文件上传,下面的代码展示了文件上传方法的基本结构:

@PostMapping(value = "/upload",consumes = MediaType.MULTIPART_FORM_DATA_VALUE)
@ResponseBody
public String fileUpload(@RequestParam("file") MultipartFile file) throws IOException {
    return null;
}

接下来,我们使用FileOutputStream对象将客户端上传的文件写入到磁盘中,并返回“File is upload successfully”的提示信息,下面是文件上传完整的代码:

package com.ramostear.application.controller;

import com.ramostear.application.model.FileInfo;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;

import javax.annotation.PostConstruct;
import java.io.*;
import java.util.HashMap;
import java.util.Map;

/**
 * @author : ramostear
 * @date : 2019/3/8 0008-15:35
 */
@Controller
public class FileController {


    @Value ( "${file.upload.root.dir}" )
    String fileUploadRootDir;

    private static Map<String,FileInfo> fileRepository = new HashMap<>();

    @PostConstruct
    public void initFileRepository(){
        FileInfo file1 = new FileInfo ().setFileName ( "bg1.jpg" );
        FileInfo file2 = new FileInfo ().setFileName ( "bg2.jpg" );
        FileInfo file3 = new FileInfo ().setFileName ( "bg3.jpg" );
        fileRepository.put ( file1.getName (),file1 );
        fileRepository.put ( file2.getName (),file2 );
        fileRepository.put ( file3.getName (),file3 );
    }

@PostMapping(value = "/upload",consumes = MediaType.MULTIPART_FORM_DATA_VALUE)
@ResponseBody
public String fileUpload(@RequestParam("file") MultipartFile file) throws IOException {

        File convertFile = new File ( fileUploadRootDir+file.getOriginalFilename ());
        FileOutputStream fileOutputStream = new FileOutputStream ( convertFile );
        fileOutputStream.write ( file.getBytes () );
        fileOutputStream.close ();

        FileInfo fileInfo = new FileInfo()
                .setFileName ( file.getOriginalFilename());

        fileRepository.put ( fileInfo.getName (),fileInfo);

        return "File is upload successfully";
    }
}

fileRepository用于存放已上传文件的索引信息。

2. 文件下载

在Spring Boot应用程序中,我们可以使用InputStreamResource对象来下载文件,在下载文件的方法中,我们需要通过Response来设置HttpHeander对象的相关属性,如Content-DispositionCache-ControlPragmaExpires等属性。除此之外,还需要指定Response的响应类型。下面的代码给出了文件下载的详细信息:

@GetMapping("/download/{fileName}")
@ResponseBody
public ResponseEntity<Object> downloadFile(@PathVariable(name = "fileName") String fileName) throws FileNotFoundException {

        File file = new File ( fileUploadRootDir+fileName);
        InputStreamResource resource = new InputStreamResource ( new FileInputStream ( file ) );

        HttpHeaders headers = new HttpHeaders();
        headers.add ( "Content-Disposition",String.format("attachment;filename=\"%s",fileName));
        headers.add ( "Cache-Control","no-cache,no-store,must-revalidate" );
        headers.add ( "Pragma","no-cache" );
        headers.add ( "Expires","0" );

        ResponseEntity<Object> responseEntity = ResponseEntity.ok()
                .headers ( headers )
                .contentLength ( file.length ())
                .contentType(MediaType.parseMediaType ( "application/txt" ))
                .body(resource);

        return responseEntity;
    }

3. 代码清单

3.1 文件上传和下载控制器

下面给出的是完整的文件上传和下载的代码:

package com.ramostear.application.controller;

import com.ramostear.application.model.FileInfo;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.core.io.InputStreamResource;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;

import javax.annotation.PostConstruct;
import java.io.*;
import java.util.Collection;
import java.util.HashMap;
import java.util.Map;

/**
 * @author : ramostear
 * @date : 2019/3/8 0008-15:35
 */
@Controller
public class FileController {


    @Value ( "${file.upload.root.dir}" )
    String fileUploadRootDir;

    private static Map<String,FileInfo> fileRepository = new HashMap<>();

    @PostConstruct
    public void initFileRepository(){
        FileInfo file1 = new FileInfo ().setFileName ( "bg1.jpg" );
        FileInfo file2 = new FileInfo ().setFileName ( "bg2.jpg" );
        FileInfo file3 = new FileInfo ().setFileName ( "bg3.jpg" );
        fileRepository.put ( file1.getName (),file1 );
        fileRepository.put ( file2.getName (),file2 );
        fileRepository.put ( file3.getName (),file3 );
    }

    @GetMapping("/files")
    public String files(Model model){
        Collection<FileInfo> files = fileRepository.values ();
        model.addAttribute ( "data",files );
        return "files";
    }


    @PostMapping(value = "/upload",consumes = MediaType.MULTIPART_FORM_DATA_VALUE)
    @ResponseBody
    public String fileUpload(@RequestParam("file") MultipartFile file) throws IOException {

        File convertFile = new File ( fileUploadRootDir+file.getOriginalFilename ());
        FileOutputStream fileOutputStream = new FileOutputStream ( convertFile );
        fileOutputStream.write ( file.getBytes () );
        fileOutputStream.close ();

        FileInfo fileInfo = new FileInfo()
                .setFileName ( file.getOriginalFilename());

        fileRepository.put ( fileInfo.getName (),fileInfo);

        return "File is upload successfully";
    }

    @GetMapping("/download/{fileName}")
    @ResponseBody
    public ResponseEntity<Object> downloadFile(@PathVariable(name = "fileName") String fileName) throws FileNotFoundException {

        File file = new File ( fileUploadRootDir+fileName);
        InputStreamResource resource = new InputStreamResource ( new FileInputStream ( file ) );

        HttpHeaders headers = new HttpHeaders();
        headers.add ( "Content-Disposition",String.format("attachment;filename=\"%s",fileName));
        headers.add ( "Cache-Control","no-cache,no-store,must-revalidate" );
        headers.add ( "Pragma","no-cache" );
        headers.add ( "Expires","0" );

        ResponseEntity<Object> responseEntity = ResponseEntity.ok()
                .headers ( headers )
                .contentLength ( file.length ())
                .contentType(MediaType.parseMediaType ( "application/txt" ))
                .body(resource);

        return responseEntity;
    }



}

3.2 数据模型

创建一个文件信息数据模型作为上传文件信息的载体,下面是FileInfo.java的代码:

package com.ramostear.application.model;

import lombok.Data;

import java.util.Date;

/**
 * @author : ramostear
 * @date  : 2019/3/8 0008-15:25
 */
@Data
public class FileInfo {

    private String name;
    private Date uploadTime = new Date();

    public FileInfo setFileName(String name){
        this.setName ( name );
        return this;
    }

}

3.3 Maven build 文件

下面是本次demo应用程序的pom.xml文件配置清单:

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.1.3.RELEASE</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>
    <groupId>com.ramostear</groupId>
    <artifactId>file-handling</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>file-handling</name>
    <description>Demo project for Spring Boot</description>

    <properties>
        <java.version>1.8</java.version>
    </properties>

    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-thymeleaf</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-freemarker</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <optional>true</optional>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>

</project>

注:本次案例使用freemarker模板引擎作为视图模板

3.4 配置文件

application.properties文件主要设置了freemarker的相关属性以及自定义的file.upload.root.dir 属性:

spring.freemarker.cache=false
spring.freemarker.prefix=
spring.freemarker.suffix=.html
spring.freemarker.enabled=true
spring.freemarker.charset=UTF-8
spring.freemarker.template-loader-path=classpath:/templates/
file.upload.root.dir = C:/work/upload/

file.upload.root.dir自定义属性设置了文件上传的更目录为:C:/work/upload/

3.5 视图

在视图文件中,创建了一个form表单用于上传文件,另外还创建了一个已上传文件列表,提供文件下载操作。

文件上传表单:

上传文件form表单

文件下载列表:

文件下载列表

说明:文件上使用的是异步上传方式进行上传,没有使用同步提交form表单的方式进行

文件上传异步操作代码如下:

$("#upload").on("click",function () {
           var fileObj = document.getElementById("file").files[0];
           var form = new FormData();
           form.append("file",fileObj);
           var xhr = new XMLHttpRequest();
           xhr.open("post","http://localhost:8080/upload",true);
           xhr.onload = function(event){
               alert(event.currentTarget.responseText);
               window.location.href = window.location.href;
           };
           xhr.send(form);
        });

4. 打包运行

使用Maven命令对应用程序进行打包,下面是maven打包的命令:

mvn clean install

在控制台窗口中运行上述命令,等待maven打包。若控制台中显示“BUILD SUCCESS”信息,你可以在当前工程目录下的target文件夹中找到相应的JAR文件。

现在,你可以使用下面的命令来运行JAR文件:

java -jar YOUR_JARFILE_NAME

JAR文件成功启动后,你可以在控制台窗口中看到如下的信息:

控制台窗口信息

5. 测试

打开浏览器并在地址栏输入:http://localhost:8080/files 。下面是成功请求后的浏览器截图:
文件列表

接下来,点击其中任意一个download按钮,测试文件下载功能是否正常:
下载文件

最后,我们测试一下文件上传功能是否正常。在进行测试之前,我们先看一下文件上传目录中存储的文件信息:

文件上传目录

接下来,我们选择一份需要上传的文件,然后点击upload按钮上传文件:

上传文件

此时,文件以及上传成功,我们再次观察文件上传目录中的文件信息,以验证文件是否成功写入磁盘:

文件上传目录对比

6. 附件

本章节的全部源代码已经上传至Github代码仓库中,你可以访问下面的地址获得全部的源码:https://github.com/ramostear/Spring-Boot-File-Handling-Tutorial

使用RestTemplate对Restful服务进行调用

使用RestTemplate对Restful服务进行调用

Spring Boot(十)— RestTemplate

Spring Boot提供的RestTemplate类可以在应用中调用Restful Web服务,这极大的简化了与Http服务的通信方式,统一了Restful服务调用的标准,其内部对Http进行了封装,我们只需要传递相应的URL、请求方法、主体信息以及返回值类型即可,相比于HttpClient,RestTemplate以一种更优雅的方式对Restful Web服务进行调用。下面是的代码展示了如何使用@Bean注解创建RestTemplate并进行连接。

package com.ramostear.application;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Bean;
import org.springframework.web.client.RestTemplate;

@SpringBootApplication
public class RestTemplateApplication {

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

    @Bean
    public RestTemplate restTemplate(){
        return new RestTemplate();
    }
}

接下来,我将演示不同请求方法下如何使用RestTemplate。在开始正文之前,我们需要创建一个提供Restful Web Service的控制器,下面给出的代码显示了控制器的详细内容:

package com.ramostear.application.controller;

import com.ramostear.application.model.User;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;

import javax.annotation.PostConstruct;
import java.util.HashMap;
import java.util.Map;

/**
 * @author ramostear
 * @create-time 2019/3/6 0006-3:14
 * @modify by :
 * @since:
 */
@RestController
public class UserServiceController {

    private static Map<Long,User> userRepository = new HashMap<>();

    @PostConstruct
    public void initUserRepository(){
        User admin = new User();
        admin.setId(1).setName("admin");
        userRepository.put(admin.getId(),admin);

        User editor = new User();
        editor.setId(2).setName("editor");
        userRepository.put(editor.getId(),editor);
    }

    @GetMapping("/users")
    public ResponseEntity<Object> getUser(){
        return new ResponseEntity<>(userRepository.values(), HttpStatus.OK);
    }

    @PostMapping("/users")
    public ResponseEntity<Object> createUser(@RequestBody User user){
        userRepository.put(user.getId(),user);
        return new ResponseEntity<>("User is created successfully",HttpStatus.CREATED);
    }

    @PutMapping("/users/{id}")
    public ResponseEntity<Object> updateUser(@PathVariable(name = "id") long id,@RequestBody User user){
        userRepository.remove(id);
        user.setId(id);
        userRepository.put(id,user);
        return new ResponseEntity<>("User is updated successfully",HttpStatus.OK);
    }

    @DeleteMapping("/users/{id}")
    public ResponseEntity<Object> delete(@PathVariable(name = "id") long id){
        userRepository.remove(id);
        return new ResponseEntity<>("User is deleted successfully",HttpStatus.OK);
    }

}

除此之外,还需要创建一个数据模型作为用户信息的载体,下面的代码给出了用户数据模型的详细信息:

package com.ramostear.application.model;

import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;

/**
 * @author ramostear
 * @create-time 2019/3/6 0006-3:12
 * @modify by :
 * @since:
 */
@Getter
@Setter
@NoArgsConstructor
public class User {
    private long id;
    private String name;

    public User setId(long id){
        this.id = id;
        return this;
    }

    public User setName(String name){
        this.name = name;
        return this;
    }
}

1. HTTP GET请求

现在,我们可以使用RestTemplate的exchange()方法来请求URL为http://localhost:8080/usersGET API。下面将给出RestTemplate调用Restful Web服务的基本流程:

  • 创建RestTemplate对象,这里使用@AutoWired注解进行自动装配
  • 使用HttpHeaders对象设置请求头参数
  • 使用HttpEntity包装类对请求对象进行包装
  • 调用exchange()方法,并传入URL、HttpMethod以及返回值类型

下面的代码显示了使用RestTemplate的exchange()方法调用Restful Web服务的详细过程:

@RestController
public class UserWebService {

    @Autowired
    RestTemplate restTemplate;

    private static final String ROOT_URI = "http://localhost:8080/users";

    @GetMapping("/rest/template/users")
    public String getUsers(){
        HttpHeaders headers = new HttpHeaders();
        headers.setAccept(Arrays.asList(MediaType.APPLICATION_JSON));
        HttpEntity<String> entity = new HttpEntity<>(headers);
        return restTemplate.exchange(ROOT_URI, HttpMethod.GET,entity,String.class).getBody();
    }
}

2. HTTP POST请求

RestTemplate调用POST API的流程与调用GET API的流程基本相同,现在,我们使用RestTemplate调用POST API创建一个新的用户。相比调用GET API,唯一不同的地方在于需要把用户信息包装到请求主体中,同时需要将请求方法修改为POST。下面的代码展示了如何调用POST API:

@RestController
public class UserWebService {

    @Autowired
    RestTemplate restTemplate;

    private static final String ROOT_URI = "http://localhost:8080/users";

    @PostMapping("/rest/template/users")
    public String createdUser(@RequestBody User user){
        HttpHeaders headers = new HttpHeaders();
        headers.setAccept(Arrays.asList(MediaType.APPLICATION_JSON));
        HttpEntity<User> entity = new HttpEntity<>(user,headers);
        return restTemplate.exchange(ROOT_URI,HttpMethod.POST,entity,String.class).getBody();
    }
}

3. HTTP PUT 请求

接下来,使用RestTemplate调用PUT API对用户信息进行修改,与调用POST API的方法相同,我们需要将待修改的用户信息包装到请求主体中,同时需要修改请求方法为PUT。下面代码展示了如何调用PUT API:

@RestController
public class UserWebService {

    @Autowired
    RestTemplate restTemplate;

    private static final String ROOT_URI = "http://localhost:8080/users";

   @PutMapping("/rest/template/users/{id}")
    public String updateUser(@PathVariable(name = "id")long id,@RequestBody User user){
        HttpHeaders headers = new HttpHeaders();
        headers.setAccept(Arrays.asList(MediaType.APPLICATION_JSON));
        HttpEntity<User> entity = new HttpEntity<>(user,headers);
        return restTemplate.exchange(ROOT_URI+"/"+id,HttpMethod.PUT,entity,String.class).getBody();
    }
}

4. HTTP DELETE请求

RestTemplate调用DELETE API的方法与上述三种请求方法基本一致,需要注意的是HttpMethod的设置,需要设置为DELETE。下面的代码展示了如何调用DELETE API删除用户信息:

@RestController
public class UserWebService {

    @Autowired
    RestTemplate restTemplate;

    private static final String ROOT_URI = "http://localhost:8080/users";

   @DeleteMapping("/rest/template/users/{id}")
    public String deleteUser(@PathVariable(name = "id")long id){
        HttpHeaders headers = new HttpHeaders();
        headers.setAccept(Arrays.asList(MediaType.APPLICATION_JSON));
        HttpEntity<User> entity = new HttpEntity<>(headers);
        return restTemplate.exchange(ROOT_URI+"/"+id,HttpMethod.DELETE,entity,String.class).getBody();
    }
}

5. 代码清单

下面给出的是完整的RestTemplate调用Restful Web服务的代码:

package com.ramostear.application.webservice;

import com.ramostear.application.model.User;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpMethod;
import org.springframework.http.MediaType;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.client.RestTemplate;

import java.util.Arrays;

/**
 * @author ramostear
 * @create-time 2019/3/8 0008-0:36
 * @modify by :
 * @since:
 */
@RestController
public class UserWebService {

    @Autowired
    RestTemplate restTemplate;

    private static final String ROOT_URI = "http://localhost:8080/users";

    @GetMapping("/rest/template/users")
    public String getUsers(){
        HttpHeaders headers = new HttpHeaders();
        headers.setAccept(Arrays.asList(MediaType.APPLICATION_JSON));
        HttpEntity<String> entity = new HttpEntity<>(headers);
        return restTemplate.exchange(ROOT_URI, HttpMethod.GET,entity,String.class).getBody();
    }

    @PostMapping("/rest/template/users")
    public String createdUser(@RequestBody User user){
        HttpHeaders headers = new HttpHeaders();
        headers.setAccept(Arrays.asList(MediaType.APPLICATION_JSON));
        HttpEntity<User> entity = new HttpEntity<>(user,headers);
        return restTemplate.exchange(ROOT_URI,HttpMethod.POST,entity,String.class).getBody();
    }

    @PutMapping("/rest/template/users/{id}")
    public String updateUser(@PathVariable(name = "id")long id,@RequestBody User user){
        HttpHeaders headers = new HttpHeaders();
        headers.setAccept(Arrays.asList(MediaType.APPLICATION_JSON));
        HttpEntity<User> entity = new HttpEntity<>(user,headers);
        return restTemplate.exchange(ROOT_URI+"/"+id,HttpMethod.PUT,entity,String.class).getBody();
    }

    @DeleteMapping("/rest/template/users/{id}")
    public String deleteUser(@PathVariable(name = "id")long id){
        HttpHeaders headers = new HttpHeaders();
        headers.setAccept(Arrays.asList(MediaType.APPLICATION_JSON));
        HttpEntity<User> entity = new HttpEntity<>(headers);
        return restTemplate.exchange(ROOT_URI+"/"+id,HttpMethod.DELETE,entity,String.class).getBody();
    }
}

Maven build 文件 — pom.xml:

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.1.3.RELEASE</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>
    <groupId>com.ramostear</groupId>
    <artifactId>rest-template</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>rest-template</name>
    <description>Demo project for Spring Boot</description>

    <properties>
        <java.version>1.8</java.version>
    </properties>

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

        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <optional>true</optional>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>

</project>

应用程序主文件 — RestTemplateApplication.java:

package com.ramostear.application;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Bean;
import org.springframework.web.client.RestTemplate;

@SpringBootApplication
public class RestTemplateApplication {

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

    @Bean
    public RestTemplate restTemplate(){
        return new RestTemplate();
    }
}

6. 打包运行

现在,可以使用下面的命令创建一个可执行的jar文件,并运行该jar文件。

使用maven命令创建可执行的jar文件:

mvn clean install

执行此命令,当控制台窗口显示“BUILD SUCCESS”信息后,你可以在当前工程目下的target子目录中找到创建的JAR文件。

接下来,使用以下命令运行创建的JAR文件:

java -jar JARFILE

执行命令,并观察控制台窗口输出的日志信息。

7. 测试

启动Postman应用程序,在地址栏中输入以下的URL,并观察测试结果。

7.1 通过RestTemplate获取用户信息

请求URL:http://localhost:8080/rest/template/users

请求方法:GET

参数:无

7.2 通过RestTemplate新增用户

请求URL: http://localhost:8080/rest/template/users

请求方法:POST

参数:{“id”:3,”name”:ramostear}

7.3 通过RestTemplate更新用户

请求URL: http://localhost:8080/rest/template/users/3

请求方法:PUT

参数:{“id”:3,”name”:”reader”}

7.4 通过RestTemplate删除用户

请求URL: http://localhost:8080/rest/template/users/3

请求方法:DELETE

参数:无

在Spring Boot中使用过滤器

在Spring Boot中使用过滤器

Spring Boot (九)— 过滤器

过滤器被用于过滤应用程序的HTTP请求和响应,我们可以在请求发送到控制器之前对请求进行过滤,或者在响应发送到客户端之前进行过滤。在本章节中,通过一个简单的例子来展示在Spring Boot中如何使用过滤器,首先通过一张图来说明过滤器的使用步骤:

![](https://cdn.ramostear.com/2019-03-07-18-25-23-d1af3dce816741c39efe69da9618f3a6.png)![](https://cdn.ramostear.com/2019-03-07-18-25-23-d1af3dce816741c39efe69da9618f3a6.png)

1. 创建过滤器

在Spring Boot中,自定义的过滤器需要实现Filter接口中的方法,并使用@Component注解进行标注,具体代码如下:

package com.ramostear.application.filter;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;

import javax.servlet.*;
import javax.servlet.http.HttpServletRequest;
import java.io.IOException;

/**
 * @author : ramostear
 * @date : 2019/3/7 0007-16:12
 */
@Component
public class UserServiceFilter implements Filter{

    private static final Logger logger = LoggerFactory.getLogger ( UserServiceFilter.class);

    @Override
    public void init(FilterConfig filterConfig) throws ServletException {}

    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse,
                         FilterChain filterChain) throws IOException, ServletException {
        HttpServletRequest request = (HttpServletRequest)servletRequest;
        logger.info ( "Request url : " + request.getRequestURL ());
        logger.info ( "Request method : "+request.getMethod ());
        filterChain.doFilter ( servletRequest,servletResponse );
    }

    @Override
    public void destroy() {}
}

在上述的代码中,当请求发送到控制器之前,过滤器生效,将从ServletRequest对象中读取请求的地址以及请求的方式。

2. 控制器类

接下来,定义一个简单的控制器,用于处理客户端发送的请求,本章节中的控制器我们沿用之前章节中的控制器:

package com.ramostear.application.controller;

import com.ramostear.application.model.User;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

import javax.annotation.PostConstruct;
import java.util.HashMap;
import java.util.Map;

/**
 * @author : ramostear
 * @date : 2019/3/7 0007-16:28
 */
@RestController
public class UserServiceController {

    private static Map<Long,User> userRepo = new HashMap<> ();

    @PostConstruct
    public void initUserRepo(){
        User admin = new User ().setId ( 1 ).setName ( "admin" );
        userRepo.put ( admin.getId (),admin );

        User editor = new User ().setId ( 2 ).setName ( "editor" );
        userRepo.put ( editor.getId (),editor );
    }


    @GetMapping("/users")
    public ResponseEntity<Object> getUser(){
        return new ResponseEntity<> (userRepo.values (), HttpStatus.OK);
    }

}

getUser方法用于返回用户存储库中的所有用户信息,映射地址为:“/users”

3. 运行并测试

接下来,你可以使用Maven命令:mvn clean install名来创建一个jar文件,并使用java -jar jarfile命令来运行程序。下面是程序运行成功后的控制台窗口截图:

可以看到,我们的应用程序在Tomcat服务器上以8080端口启动成功。现在,打开Postman测试应用程序,以GET的方式打开请求地址:http://localhost:8080/users ,并观察返回的信息:

然后,你可以在控制台窗口中看到如下的输出信息:

spring boot中使用拦截器

spring boot中使用拦截器

Spring Boot (八)— 拦截器

在Spring Boot应用程序中,可以使用拦截器对Request请求到达控制器前和Response响应发送到客户端前进行拦截,并做相应的操作,如添加请求头信息或者进行鉴权等操作。在本章节中,我将演示如何在Spring Boot应用程序中使用拦截器。

在开始正文之前,我们先通过一张图来了解Spring Boot应用程序中拦截器的使用步骤:

首先,我们需要定义一个拦截器类,并使用@Component注解进行标注,此外,该拦截器类还需要实现HandlerInterceptor接口的三个方法:

  • preHandle() : 用于在请求发送到控制器前执行操作
  • postHandle : 用于在将响应发送到客户端之前进行操作
  • afterHandle : 用于在完成请求和响应后执行操作

1. 创建拦截器 — UserServiceInterceptor.java

package com.ramostear.application.interceptor;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.lang.Nullable;
import org.springframework.stereotype.Component;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

/**
 * @author ramostear
 * @create-time 2019/3/6 0006-23:55
 * @modify by :
 * @since:
 */
@Component
public class UserServiceInterceptor implements HandlerInterceptor {

    private static final Logger logger = LoggerFactory.getLogger(UserServiceInterceptor.class);

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        logger.info("Pre handle method is calling.");
        logger.info("Request URL is : "+ request.getRequestURL());
        return true;
    }

    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, @Nullable ModelAndView modelAndView) throws Exception {
        logger.info("Post handle method is calling.");
        logger.info("Response Status is : "+response.getStatus());
    }

    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, @Nullable Exception ex) throws Exception {
        logger.info("Request and Response is completed.");
    }
}

在上述的拦截器中,我们在请求发送到控制器之前,记录了当前请求的URL地址,在控制器执行完操作响应客户端之前,记录了当前响应的状态码;最后,在完成请求和响应后,记录请求和响应已经完成的消息。

2. 注册拦截器

接下来,我们需要将定义好的拦截器注册到拦截器注册表中。在Spring Boot 2.0中,我们可以通过扩展WebMvcConfigurationSupport类来注册我们的拦截器:

package com.ramostear.application.config;

import com.ramostear.application.interceptor.UserServiceInterceptor;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurationSupport;

/**
 * @author ramostear
 * @create-time 2019/3/7 0007-2:42
 * @modify by :
 * @since:
 */
@Component
public class UserServiceInterceptorConfig extends WebMvcConfigurationSupport{

    @Autowired
    UserServiceInterceptor userServiceInterceptor;

    @Override
    protected void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(userServiceInterceptor);
    }
}

说明:在Spring Boot 2.0及以后的版本中,WebMvcConfigurerAdapter类已经被废弃掉了,所以我们可以选择直接实现WebMvcConfigurer 接口中的方法来注册自定义拦截器,也可以选择扩展WebMvcConfigurationSupport类来注册自定义的拦截器。这里采用扩展WebMvcConfigurationSupport类的方式注册自定义的拦截器。

以下是Spring 官方的文档信息:

/**
* @author Rossen Stoyanchev
* @since 3.1
* @deprecated as of 5.0 {@link WebMvcConfigurer} has default methods (made
* possible by a Java 8 baseline) and can be implemented directly without the
* need for this adapter
*/
@Deprecated
public abstract class WebMvcConfigurerAdapter implements WebMvcConfigurer {
    ...
}

3. 控制器 — UserServiceController.java

我们将定义一个控制器来处理客户端的请求,客户端通过HTTP GET方法请求用户资源信息:

package com.ramostear.application.controller;

import com.ramostear.application.model.User;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

import javax.annotation.PostConstruct;
import java.util.HashMap;
import java.util.Map;

/**
 * @author ramostear
 * @create-time 2019/3/7 0007-2:51
 * @modify by :
 * @since:
 */
@RestController
public class UserServiceController {

    private static Map<Long,User> userRepo = new HashMap<>();

    @PostConstruct
    public void initUserRepo(){
        User admin = new User().setId(1).setName("admin");
        User editor = new User().setId(2).setName("editor");
        userRepo.put(admin.getId(),admin);
        userRepo.put(editor.getId(),editor);
    }

    @GetMapping("/users")
    public ResponseEntity<Object> getUsers(){
        return new ResponseEntity<>(userRepo.values(), HttpStatus.OK);
    }

}

4. POJO类 — User.java

用户信息模型的POJO类代码如下所示:

package com.ramostear.application.model;

import lombok.Data;

/**
 * @author ramostear
 * @create-time 2019/3/6 0006-23:52
 * @modify by :
 * @since:
 */
@Data
public class User {

    private long id;

    private String name;

    public User setId(long id){
        this.id = id;
        return this;
    }

    public User setName(String name){
        this.name = name;
        return this;
    }
}

5.应用主类 — InterceptorApplication.java

应用程序的主类代码如下所示:

package com.ramostear.application;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class InterceptorApplication {

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

}

6. Maven构建文件 — pom.xml

maven 构建文件代码清单如下所示:

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.1.3.RELEASE</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>
    <groupId>com.ramostear</groupId>
    <artifactId>interceptor</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>interceptor</name>
    <description>Demo project for Spring Boot</description>

    <properties>
        <java.version>1.8</java.version>
    </properties>

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

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>

</project>

7. 运行程序并测试

你可以使用maven的“mvn clean install”命令来进行打包,然后使用java -jar 命令来运行项目,也可以使用集成开发环境IDEA或者Eclipse来打包运行项目。

现在,我们的应用程序在Tomcat服务器上使用8080端口启动成功,控制台输出信息如下所示:

接下来,启动Postman应用程序,在地址栏输入:http://localhost:8080/users ,并以GET的方式发送请求,如下图所示:

测试显示,控制器成功返回了用户信息,最后,让我们观察控制台窗口输出的信息:

通过控制台显示的信息可知,我们的拦截器已经成功的对客户端的请求以及服务端的响应进行了拦截。