Spring boot学习笔记


Spring boot学习笔记
官方地址:https://spring.io/projects

1. 从hello world 开始

1.1 maven 依赖

<parent>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-parent</artifactId>
    <version>2.0.2.RELEASE</version>
</parent>
<dependencies>
    <!--spring boot web 包-->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>
</dependencies>

1.2 编写启动程序

1.2.1 方式一
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
    
/**
 * <p>Spring boot "hello word"</p>
 * @author vchar fred
 * @version 1.0
 * @date 2018/6/12 15:42
 */
@RestController//相当于Controller+ResponseBody;即自动是ajax的请求
@EnableAutoConfiguration//spring 会自动装配相关的配置,这个是必须有的
public class SpringBootHello {
    @RequestMapping("/")
    public String home(){
        return "hello word!";
    }
    public static void main(String[] args) throws Exception{
        SpringApplication.run(SpringBootHello.class, args);
    }
}

运行main方法,在浏览器中访问:http://localhost:8080/ 即可看到hello word
打jar包运行的方法:添加如下maven插件依赖

<build>
    <plugins>
        <!--spring boot 打包工具-->
        <plugin>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-maven-plugin</artifactId>
        </plugin>
    </plugins>
</build>

运行maven的 package命令;然后运行 java -jar xxx.jar

1.2.2 方式二
@SpringBootApplication//这个注解包含了EnableAutoConfiguration;更多的配置可以查看源码
@RestController//相当于Controller+ResponseBody;即自动是ajax的请求
public class SpringBootHello {
    @RequestMapping("/")
    public String home(){
        return "hello word!";
    }
    public static void main(String[] args) throws Exception{
        SpringApplication.run(SpringBootHello.class, args);
    }
}

@SpringBootApplication 注解源码如下:

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@SpringBootConfiguration
@EnableAutoConfiguration
@ComponentScan(excludeFilters = @Filter(type = FilterType.CUSTOM, classes = TypeExcludeFilter.class))
public @interface SpringBootApplication {

    //排除自启动项
    Class<?>[] exclude() default {};

    //排除自动启动的beanName
    String[] excludeName() default {};

       //扫描包
    @AliasFor(annotation = ComponentScan.class, attribute = "basePackages")
    String[] scanBasePackages() default {};

    //扫描类
    @AliasFor(annotation = ComponentScan.class, attribute = "basePackageClasses")
    Class<?>[] scanBasePackageClasses() default {};

}

2. log日志

来源:https://blog.csdn.net/king_is_everyone/article/details/53074006

SpringBoot默认是采用logback进行日志处理、Logback是由log4j创始人设计的又一个开源日志组件
logback当前分成三个模块:logback-core,logback- classic和logback-access。logback-core是其它两个模块的基础模块

2.1 logback.xml详情

<?xml version="1.0" encoding="UTF-8"?>
<!--
configuration 根节点配置参数说明:
      scan:当此属性设置为true时,配置文件如果发生改变,将会被重新加载,默认值为true
scanPeriod:设置监测配置文件是否有修改的时间间隔,如果没有给出时间单位,默认单位是毫秒。当scan为true时,此属性生效。默认的时间间隔为1分钟。配置示例: 120 seconds
     debug:当此属性设置为true时,将打印出logback内部日志信息,实时查看logback运行状态。默认值为false
-->
<configuration>
    <!--contextName 设置日志上下文名称,可以通过%contextName来打印日志上下文名称
           每个logger都关联到logger上下文,默认上下文名称为“default”。
           但可以使用<contextName>设置成其他名字,用于区分不同应用程序的记录。一旦设置,不能修改
   -->
    <contextName>spring-boot-demo-log</contextName>

    <!--property 可以用来设置变量,可以通过${name}来访问,有以下的属性
            name:用于${name}访问的key;
            value:用于${name}访问的value
                例:<property name="log.path" value="c:\\logback.log" />
            file: 用于指定配置文件的路径,他的作用在于,如果你有多个配置信息的话,可以直接写在配置文件中,然后通过file引入
                例:<property file="src/main/java/resources/conf/log.properties" />
            resource: 作用和file一样,但是,它是可以直接从classpath路径下引入配置文件
                例:<property resource="/conf/log.properties" />
    -->
    <property name="APP_ID" value="top.vchar.learn.springboot"/>
    <property name="LOG_PATH" value="log"></property>

    <!--appender 格式化日志输出节点
        1. 有2个属性name和class,class用来指定哪种输出策略,常用就是控制台输出策略[STDOUT/console]和文件输出策略[file].
        2. appender有以下子节点:
            2.1 filter: 日志输出拦截器,可以自定义拦截器也可以用系统一些定义好的拦截器
                /**
                * 自定义日志输出拦截器
                * @author vf
                * @date 2016-04-28 3:36
                */
                public class MyFilter extends Filter<ILoggingEvent> {

                    @Override
                    public FilterReply decide(ILoggingEvent event) {

                        if (event.getMessage().contains("sample")) {
                            return FilterReply.ACCEPT; //允许输入串
                        } else {
                            return FilterReply.DENY; //不允许输出
                        }
                    }
                }
                系统自带的日志拦截器
                例:用ThresholdFilter来过滤掉ERROR级别以下的日志不输出
                    <filter class="ch.qos.logback.classic.filter.ThresholdFilter">
                        <level>ERROR</level>
                    </filter>
            2.2 encoder和pattern节点组合用于具体输出的日志格式
            2.3 file节点用来指明日志文件的输出位置,可以是绝对路径也可以是相对路径
            2.4 rollingPolicy日志回滚策略.
                2.4.1 TimeBasedRollingPolicy: 基于时间的回滚策略,有以下子节点
                    fileNamePattern:必要节点,可以用来设置指定时间的日志归档,例如每天将日志归档成一个zip包
                    maxHistory:     可选节点,控制保留的归档文件的最大数量,超出数量就删除旧文件,,例如设置为30的话,则30天之后,旧的日志就会被删除
                    totalSizeCap:   可选节点,用来指定日志文件的上限大小,例如设置为1GB的话,那么到了这个值,就会删除旧的日志

                    例:
                    <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
                        <fileNamePattern>${log.path}.%d{yyyy-MM-dd}.zip</fileNamePattern>
                        <maxHistory>30</maxHistory>
                        <totalSizeCap>1GB</totalSizeCap>
                    </rollingPolicy>

                2.4.2 SizeAndTimeBasedRollingPolicy 基于日志文件大小的回滚策略。
                    例:
                    <appender name="ROLLING" class="ch.qos.logback.core.rolling.RollingFileAppender">
                        <file>mylog.txt</file>
                        <rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy">
                            <fileNamePattern>mylog-%d{yyyy-MM-dd}.%i.txt</fileNamePattern>
                            <maxFileSize>100MB</maxFileSize>
                            <maxHistory>60</maxHistory>
                            <totalSizeCap>20GB</totalSizeCap>
                        </rollingPolicy>
                        <encoder>
                            <pattern>%msg%n</pattern>
                        </encoder>
                    </appender>

    -->
    <!--每个级别的日志配置-->
    <!-- 控制台输出 -->
    <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
        <encoder>
            <pattern>%d{yyyy-MM-dd HH:mm:ss} %-4relative [%thread] %-5level %logger{35} - %msg %n</pattern>
        </encoder>
    </appender>
    <!--<appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender">-->
    <!--&lt;!&ndash;<filter class="ch.qos.logback.classic.filter.ThresholdFilter">&ndash;&gt;-->
    <!--&lt;!&ndash;<level>ERROR</level>&ndash;&gt;-->
    <!--&lt;!&ndash;</filter>&ndash;&gt;-->
    <!--<encoder>-->
    <!--<pattern>%d{HH:mm:ss.SSS} %contextName [%thread] %-5level %logger{36} - %msg%n</pattern>-->
    <!--</encoder>-->
    <!--</appender>-->

    <!--maxHistory配置了日志在服务器上面只存留几个备份-->
    <appender name="FILE_LOG"
              class="ch.qos.logback.core.rolling.RollingFileAppender">
        <filter class="ch.qos.logback.classic.filter.LevelFilter">
            <level>DEBUG</level>
        </filter>
        <file>${LOG_PATH}/${APP_ID}/access.log</file>
        <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
            <fileNamePattern>${LOG_PATH}/${APP_ID}/access.log.%d{yyyy-MM-dd}.zip</fileNamePattern>
            <maxHistory>10</maxHistory>
        </rollingPolicy>
        <encoder>
            <pattern>%d{yyyy-MM-dd HH:mm:ss} %-4relative [%thread] %-5level %logger{35} - %msg%n</pattern>
        </encoder>
    </appender>
    <appender name="FILE_DEBUG"
              class="ch.qos.logback.core.rolling.RollingFileAppender">
        <filter class="ch.qos.logback.classic.filter.LevelFilter">
            <level>DEBUG</level>
            <onMatch>ACCEPT</onMatch>
            <onMismatch>DENY</onMismatch>
        </filter>
        <file>${LOG_PATH}/${APP_ID}/access_debug.log</file>
        <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
            <fileNamePattern>${LOG_PATH}/${APP_ID}/access_debug.log.%d{yyyy-MM-dd}.zip</fileNamePattern>
            <maxHistory>10</maxHistory>
        </rollingPolicy>
        <encoder>
            <pattern>%d{yyyy-MM-dd HH:mm:ss} %-4relative [%thread] %-5level %logger{35} - %msg%n</pattern>
        </encoder>
    </appender>
    <appender name="FILE_INFO"
              class="ch.qos.logback.core.rolling.RollingFileAppender">
        <filter class="ch.qos.logback.classic.filter.LevelFilter">
            <level>INFO</level>
            <onMatch>ACCEPT</onMatch>
            <onMismatch>DENY</onMismatch>
        </filter>
        <file>${LOG_PATH}/${APP_ID}/access_info.log</file>
        <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
            <fileNamePattern>${LOG_PATH}/${APP_ID}/access_info.log.%d{yyyy-MM-dd}.zip</fileNamePattern>
            <maxHistory>10</maxHistory>
        </rollingPolicy>
        <encoder>
            <pattern>%d{yyyy-MM-dd HH:mm:ss} %-4relative [%thread] %-5level %logger{35} - %msg%n</pattern>
        </encoder>
    </appender>
    <appender name="FILE_WARN"
              class="ch.qos.logback.core.rolling.RollingFileAppender">
        <filter class="ch.qos.logback.classic.filter.LevelFilter">
            <level>WARN</level>
            <onMatch>ACCEPT</onMatch>
            <onMismatch>DENY</onMismatch>
        </filter>
        <file>${LOG_PATH}/${APP_ID}/access_warn.log</file>
        <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
            <fileNamePattern>${LOG_PATH}/${APP_ID}/access_warn.log.%d{yyyy-MM-dd}.zip</fileNamePattern>
            <maxHistory>10</maxHistory>
        </rollingPolicy>
        <encoder>
            <pattern>%d{yyyy-MM-dd HH:mm:ss} %-4relative [%thread] %-5level %logger{35} - %msg%n</pattern>
        </encoder>
    </appender>
    <appender name="FILE_ERROR"
              class="ch.qos.logback.core.rolling.RollingFileAppender">
        <filter class="ch.qos.logback.classic.filter.LevelFilter">
            <level>ERROR</level>
            <onMatch>ACCEPT</onMatch>
            <onMismatch>DENY</onMismatch>
        </filter>
        <file>${LOG_PATH}/${APP_ID}/access_error.log</file>
        <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
            <fileNamePattern>${LOG_PATH}/${APP_ID}/access_error.log.%d{yyyy-MM-dd}.zip</fileNamePattern>
            <maxHistory>10</maxHistory>
        </rollingPolicy>
        <encoder>
            <pattern>%d{yyyy-MM-dd HH:mm:ss} %-4relative [%thread] %-5level %logger{35} - %msg%n</pattern>
        </encoder>
    </appender>

    <!--在生产中往往会因为大量的日志导致io过高,所以通过AsyncAppender进行异步的日志记录。-->
    <appender name="ASYNC_LOG" class="ch.qos.logback.classic.AsyncAppender">
        <!-- 不丢失日志.默认的,如果队列的80%已满,则会丢弃TRACT、DEBUG、INFO级别的日志 -->
        <discardingThreshold>0</discardingThreshold>
        <!-- 更改默认的队列的深度,该值会影响性能.默认值为256 -->
        <queueSize>64</queueSize>
        <appender-ref ref="FILE_LOG"/>
    </appender>
    <appender name="ASYNC_LOG" class="ch.qos.logback.classic.AsyncAppender">
        <!-- 不丢失日志.默认的,如果队列的80%已满,则会丢弃TRACT、DEBUG、INFO级别的日志 -->
        <discardingThreshold>0</discardingThreshold>
        <!-- 更改默认的队列的深度,该值会影响性能.默认值为256 -->
        <queueSize>64</queueSize>
        <appender-ref ref="FILE_LOG"/>
    </appender>
    <appender name="ASYNC_LOG_DEBUG" class="ch.qos.logback.classic.AsyncAppender">
        <!-- 不丢失日志.默认的,如果队列的80%已满,则会丢弃TRACT、DEBUG、INFO级别的日志 -->
        <discardingThreshold>0</discardingThreshold>
        <!-- 更改默认的队列的深度,该值会影响性能.默认值为256 -->
        <queueSize>64</queueSize>
        <appender-ref ref="FILE_DEBUG"/>
    </appender>
    <appender name="ASYNC_LOG_INFO" class="ch.qos.logback.classic.AsyncAppender">
        <!-- 不丢失日志.默认的,如果队列的80%已满,则会丢弃TRACT、DEBUG、INFO级别的日志 -->
        <discardingThreshold>0</discardingThreshold>
        <!-- 更改默认的队列的深度,该值会影响性能.默认值为256 -->
        <queueSize>64</queueSize>
        <appender-ref ref="FILE_INFO"/>
    </appender>
    <appender name="ASYNC_LOG_WARN" class="ch.qos.logback.classic.AsyncAppender">
        <!-- 不丢失日志.默认的,如果队列的80%已满,则会丢弃TRACT、DEBUG、INFO级别的日志 -->
        <discardingThreshold>0</discardingThreshold>
        <!-- 更改默认的队列的深度,该值会影响性能.默认值为256 -->
        <queueSize>64</queueSize>
        <appender-ref ref="FILE_WARN"/>
    </appender>
    <appender name="ASYNC_LOG_ERROR" class="ch.qos.logback.classic.AsyncAppender">
        <!-- 不丢失日志.默认的,如果队列的80%已满,则会丢弃TRACT、DEBUG、INFO级别的日志 -->
        <discardingThreshold>0</discardingThreshold>
        <!-- 更改默认的队列的深度,该值会影响性能.默认值为256 -->
        <queueSize>64</queueSize>
        <appender-ref ref="FILE_ERROR"/>
    </appender>

    <!--指定root的日志级别,一般来说都会指定到info级别,
        因为SpringBoot运行的时候会产生大量的debug日志-->
    <root level="INFO">
        <!-- appender referenced after it is defined -->
        <appender-ref ref="STDOUT"/>
        <appender-ref ref="ASYNC_LOG"/>
        <appender-ref ref="ASYNC_LOG_DEBUG"/>
        <appender-ref ref="ASYNC_LOG_INFO"/>
        <appender-ref ref="ASYNC_LOG_WARN"/>
        <appender-ref ref="ASYNC_LOG_ERROR"/>
    </root>
    <!--定义org.springframework这个包里面输出debug日志、-->
    <!--一般来说如果使用Mybatis或者hibernate,需要输出SQL都需要通过这里进行配置,输出debug级别的日志-->
    <logger name="org.springframework" level="INFO"/>
</configuration>

3.统一异常处理

来源:https://blog.csdn.net/king_is_everyone/article/details/53080851

3.1 spring boot 自带的统一异常处理,重新配置异常地址和页面

SpringBoot在页面发生异常的时候会自动把请求转到/error; SpringBoot内置了一个BasicErrorController对异常进行统一的处理,
这个错误的地址是可以重新配置的。

resources目录下创建一个application.yaml配置文件;写入如下配置

server:
    #访问端口号
    port:  8082
    error:
        #设置错误路径
        path:  /test/error

开始编写测试程序

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
/**
 * <p>测试异常</p>
 *
 * @author vchar fred
 * @version 1.0
 * @date 2018/6/13 11:17
 */
@SpringBootApplication
@Controller
public class TestExceptionController {

    @RequestMapping(value = "/ajax")
    @ResponseBody
    public String ajaxRequestExceptionTest(int nu){
        //传入参数为0时抛出异常
        int num = 1/nu;
        return "this ok "+ num;
    }
    
    @RequestMapping(value = "/htmls")
    public String htmlRequestExceptionTest(int nu){
        //传入参数为0时抛出异常
        int num = 1/nu;
        return "index";
    }

    //启动
    public static void main(String[] args) throws Exception{
        SpringApplication.run(TestExceptionController.class);
    }
}

resources目录下创建templates目录(这个是默认放置模版文件的目录),并分别创建error.ftl和index.ftl 文件。error.ftl文件用于替换spring boot原有的错误页面,index.ftl用于测试页面

由于用到了freemarker,需要添加maven依赖

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

启动后开始测试

#测试是否可正常访问
http://127.0.0.1:8082/htmls?nu=1    
#测试异常页面
http://127.0.0.1:8082/htmls?nu=0     
#ajax请求返回的错误提示
{
    "timestamp": "2018-06-13T03:55:51.587+0000",
    "status": 500,
    "error": "Internal Server Error",
    "message": "/ by zero",
    "path": "/ajax"
}

3.2 通用Exception处理

通过使用@ControllerAdvice来进行统一异常处理,@ExceptionHandler(value = Exception.class)来指定捕获的异常
下面针对两种异常进行了特殊处理分别返回页面和json数据,使用这种方式有个局限,无法根据不同的头部返回不同的数据格式,而且无法针对404、403等多种状态进行处理

//异常处理类
@ControllerAdvice
public class GlobalExceptionHandler {

    public static final String DEFAULT_ERROR_VIEW = "error";
    @ExceptionHandler(value = CustomException.class)
    @ResponseBody
    public ResponseEntity defaultErrorHandler(HttpServletRequest req, CustomException e) throws Exception {
        return ResponseEntity.ok("ok");
    }
    @ExceptionHandler(value = Exception.class)
    public ModelAndView defaultErrorHandler(HttpServletRequest req, Exception e) throws Exception {
        ModelAndView mav = new ModelAndView();
        mav.addObject("exception", e);
        mav.addObject("url", req.getRequestURL());
        mav.setViewName(DEFAULT_ERROR_VIEW);
        return mav;
    }
}
//自定义异常
public class CustomException extends Exception {
    CustomException(String errorMsg){
        super(errorMsg);
    }
}

//测试路由
@RequestMapping(value = "/coustom")
public String coustomExceptionTest() throws CustomException {
    customExce();
    return "index";
}

private void customExce() throws CustomException {
    throw new CustomException("自定义异常");
}

3.3 自定义BasicErrorController 错误处理

在初始介绍哪里提到了BasicErrorController,这个是SpringBoot的默认错误处理,也是一种全局处理方式。咱们可以模仿这种处理方式自定义自己的全局错误处理
下面定义了一个自己的BasicErrorController,可以根据自己的需求自定义对应的错误处理。

@ResponseStatus 注解的异常类会被ResponseStatusExceptionResolver 解析

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.autoconfigure.web.servlet.error.AbstractErrorController;
import org.springframework.boot.web.servlet.error.ErrorAttributes;
import org.springframework.core.Ordered;
import org.springframework.core.annotation.Order;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.NoHandlerFoundException;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;


/**
 * <p>自定义异常controller</p>
 *
 * @author vchar fred
 * @version 1.0
 * @date 2018/6/13 14:25
 */
@Order(Ordered.HIGHEST_PRECEDENCE)
@ControllerAdvice
@Controller
@RequestMapping("${server.error.path:${error.path:/error}}")
public class BasicErrorController extends AbstractErrorController {

    public BasicErrorController(ErrorAttributes errorAttributes) {
        super(errorAttributes);
    }

    private static final Logger log = LoggerFactory.getLogger(BasicErrorController.class);
    @Value("${server.error.path:${error.path:/error}}")
    private static String errorPath = "/error";

    /**
     * 500 错误
     * @param request
     * @param response
     * @param ex
     * @return
     * @throws Exception
     */
    @ResponseStatus(code = HttpStatus.INTERNAL_SERVER_ERROR)
    @ExceptionHandler(Exception.class)
    public ModelAndView serverError(HttpServletRequest request, HttpServletResponse response, Exception ex) throws Exception {
        return handleViewError(ex.getMessage(), "500", "error/500");
    }

    /**
     * 404错误
     * @param request
     * @param response
     * @param ex
     * @return
     * @throws Exception
     */
    @ResponseStatus(code = HttpStatus.NOT_FOUND)
    @ExceptionHandler(NoHandlerFoundException.class)
    public ResponseEntity<Map<String, Object>> notFound(HttpServletRequest request, HttpServletResponse response, Exception ex) throws Exception {
        Map<String, Object> map = new HashMap<>();
        map.put("status", 404);
        map.put("msg", "the resource is not found");
        map.put("exception", ex.getMessage());
        return ResponseEntity.ok(map);
    }

    /**
     * 参数不完整错误.
     * @param req
     * @param rsp
     * @param ex
     * @return
     * @throws Exception
     */
    @ResponseStatus(code = HttpStatus.BAD_REQUEST)
    @ExceptionHandler(MethodArgumentNotValidException.class)
    public ModelAndView methodArgumentNotValidException(HttpServletRequest req, HttpServletResponse rsp, MethodArgumentNotValidException ex) throws Exception {
       return handleViewError(ex.getMessage(), "404", "error/404");
    }


    private ModelAndView handleViewError(String errorStack, String errorMessage, String viewName) {
        ModelAndView mav = new ModelAndView();
        mav.addObject("exception", errorStack);
        mav.addObject("msg", errorMessage);
        mav.addObject("timestamp", new Date());
        mav.setViewName(viewName);
        return mav;
    }

    @Override
    public String getErrorPath() {
        return errorPath;
    }
}

定义文件结构如下:

文件结构

4. spring boot中使用redis

application.yaml配置文件中添加redis如下配置

spring:
    rdeis:
        host: 127.0.0.1
        port: 6379
        timeout: 3000
        pool:
            # 连接池最大连接数(使用负值表示没有限制)
            max-total: 8
            # 连接池最大阻塞等待时间(使用负值表示没有限制)
            max-wait-millis: -1
            # 连接池中的最大空闲连接
            max-idle: 8
            # 连接池中的最小空闲连接
            min-idle: 0

加入需要的依赖包

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

注:网上其他有很多教程说引入的是下面这个包

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

但是我在引入时发现当前这个版本中这个包已经无法下载了。

4.1 redis 配置方式1,使用默认的

因为上面依赖了spring-boot-starter-data-redis,可以使用默认的 org.springframework.boot.autoconfigure.data.redis.RedisAutoConfiguration 类加载properties文件的配置。它的源码如下:

@Configuration
protected static class RedisConfiguration {

    @Bean
    @ConditionalOnMissingBean(name = "redisTemplate")
    public RedisTemplate<Object, Object> redisTemplate(
            RedisConnectionFactory redisConnectionFactory)
                    throws UnknownHostException {
        RedisTemplate<Object, Object> template = new RedisTemplate<Object, Object>();
        template.setConnectionFactory(redisConnectionFactory);
        return template;
    }

    @Bean
    @ConditionalOnMissingBean(StringRedisTemplate.class)
    public StringRedisTemplate stringRedisTemplate(
            RedisConnectionFactory redisConnectionFactory)
                    throws UnknownHostException {
        StringRedisTemplate template = new StringRedisTemplate();
        template.setConnectionFactory(redisConnectionFactory);
        return template;
    }
}

使用方法

@RestController
public class StartServe {
    @Autowired
    private StringRedisTemplate template;
    @Autowired
    private RedisTemplate<Object, Object> redisTemplate;

    @RequestMapping("/redis")
    public String redisTest(){
        //这两个的是不一样的。因为RedisTemplate<Object,Object> 默认会将对象使用JdkSerializationRedisSerializer进行序列化
        
        redisTemplate.opsForValue().set("test", "123456");
        template.opsForValue().set("test", "abcdef");
        return template.opsForValue().get("test")+"---|||---"+redisTemplate.opsForValue().get("test");
    }
}

4.2 redis 配置方式2,自己手动配置

4.2.1 redis的单机版连接池配置

需要加入额外的jar包:

<dependency>
    <groupId>redis.clients</groupId>
    <artifactId>jedis</artifactId>
    <version>2.9.0</version>
</dependency>

编写相关代码:

  • 单机版
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.connection.jedis.JedisConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.data.redis.serializer.RedisSerializer;
import org.springframework.data.redis.serializer.StringRedisSerializer;
import redis.clients.jedis.JedisPoolConfig;

/**
 * <p>redis 配置</p>
 *
 * @author vchar fred
 * @version 1.0
 * @date 2018/6/13 18:05
 */
@Configuration
public class RedisConfig {
    @Value("${spring.redis.host}")
    private String host;
    @Value("${spring.redis.port}")
    private int port;
    @Value("${spring.redis.timeout}")
    private int timeout;
    //@Value("${spring.redis.password}")
    //private String password;
    @Value("${spring.redis.pool.max-idle}")
    private int maxIdle;
    @Value("${spring.redis.pool.min-idle}")
    private int minIdle;
    @Value("${spring.redis.pool.max-total}")
    private int maxTotal;
    @Value("${spring.redis.pool.max-wait-millis}")
    private int maxWaitMillis;

    /**
     * 连接设置
     * @return 返回连接工厂
     */
    @Bean(name = "jedisConnectionFactory")
    public JedisConnectionFactory getJedisConnectionFactory(JedisPoolConfig jedisPoolConfig){
        JedisConnectionFactory factory = new JedisConnectionFactory();
        factory.setHostName(host);
        factory.setPort(port);
        //factory.setPassword("");//设置认证密码
        //factory.setDatabase();//设置库
        factory.setTimeout(timeout);
        factory.setUsePool(true);
        factory.setPoolConfig(jedisPoolConfig);
        return factory;
    }

    /**
     * 连接池设置
     * @return 返回连接池配置
     */
    @Bean(name = "jedisPoolConfig")
    public JedisPoolConfig getJedisPoolConfig(){
        JedisPoolConfig jedisPoolConfig = new JedisPoolConfig();
        jedisPoolConfig.setMaxTotal(maxTotal);
        jedisPoolConfig.setMaxIdle(maxIdle);
        jedisPoolConfig.setMaxWaitMillis(maxWaitMillis);
        jedisPoolConfig.setMinIdle(minIdle);
        return jedisPoolConfig;
    }

    /**
     * RedisTemplate<?,?>
     * @param jedisConnectionFactory JedisConnectionFactory
     * @return
     */
    @Bean
    public RedisTemplate<?,?> redisTemplate(JedisConnectionFactory jedisConnectionFactory){
        RedisTemplate<?, ?> redisTemplate = new RedisTemplate<>();
        redisTemplate.setConnectionFactory(jedisConnectionFactory);
        //key序列化方式;但是如果方法上有Long等非String类型的话,会报类型转换错误;
        RedisSerializer<String> redisSerializer = new StringRedisSerializer();//Long类型不可以会出现异常信息;
        redisTemplate.setKeySerializer(redisSerializer);
        redisTemplate.setHashKeySerializer(redisSerializer);
        //key序列化方式;但是如果方法上有Long等非String类型的话,会报类型转换错误;
        redisTemplate.setValueSerializer(redisSerializer);
        redisTemplate.setHashValueSerializer(redisSerializer);
        redisTemplate.afterPropertiesSet();
        //查看 StringRedisTemplate 的初始化源码,你会发现其实它和上面一样做了的key和value的序列化设置
        return redisTemplate;
    }

    /**
     * StringRedisTemplate
     * @param jedisConnectionFactory JedisConnectionFactory
     * @return  StringRedisTemplate
     */
    @Bean
    public StringRedisTemplate stringRedisTemplate(JedisConnectionFactory jedisConnectionFactory){
        StringRedisTemplate stringRedisTemplate = new StringRedisTemplate();
        stringRedisTemplate.setConnectionFactory(jedisConnectionFactory);
        return stringRedisTemplate;
    }
}
  • 集群版
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import redis.clients.jedis.HostAndPort;
import redis.clients.jedis.JedisCluster;

import java.util.HashSet;
import java.util.Set;

/**
 * redis集群配置
 */
@Configuration //相当于以前的配置文件
public class RedisConfig {

    @Value("${spring.redis.cluster.nodes}")
    private static String clusterNodes;

    @Bean("jedisCluster")
    public JedisCluster getJedisCluster(){

        String[] cNodes = clusterNodes.split(",");
        Set<HostAndPort> nodes = new HashSet<HostAndPort>();
        for (String node: cNodes) {
            String[] hp = node.split(":");
            nodes.add(new HostAndPort(hp[0],Integer.parseInt(hp[1])));
        }
        //创建redis的集群对象
        return new JedisCluster(nodes);
    }
}

使用方式同上面方式一的,这次你会发现他们的key是一样(因为序列化的方式是一样的了)。

另附一个redis配置比较完整的配置文件
来源:https://www.cnblogs.com/EasonJim/p/7805665.html

  • 单机版

# REDIS(RedisProperties)
# (普通集群,不使用则不用开启)在群集中执行命令时要遵循的最大重定向数目。
# spring.redis.cluster.max-redirects=
# (普通集群,不使用则不用开启)以逗号分隔的“主机:端口”对列表进行引导。
# spring.redis.cluster.nodes=
# 连接工厂使用的数据库索引。
spring.redis.database=0
# 连接URL,将覆盖主机,端口和密码(用户将被忽略),例如:redis://user:password@example.com:6379
spring.redis.url=
# Redis服务器主机。
spring.redis.host=localhost
# 登录redis服务器的密码。
spring.redis.password=
# 启用SSL支持。
spring.redis.ssl=false
# 池在给定时间可以分配的最大连接数。使用负值无限制。
spring.redis.pool.max-total=8
# 池中“空闲”连接的最大数量。使用负值表示无限数量的空闲连接。
spring.redis.pool.max-idle=8
# 连接分配在池被耗尽时抛出异常之前应该阻塞的最长时间量(以毫秒为单位)。使用负值可以无限期地阻止。
spring.redis.pool.max-wait-millis=-1
# 目标为保持在池中的最小空闲连接数。这个设置只有在正面的情况下才有效果。
spring.redis.pool.min-idle=0
# Redis服务器端口。
spring.redis.port=6379
# (哨兵模式,不使用则不用开启)Redis服务器的名称。
# spring.redis.sentinel.master=
# (哨兵模式,不使用则不用开启)主机:端口对的逗号分隔列表。 
# spring.redis.sentinel.nodes=
# 以毫秒为单位的连接超时。
spring.redis.timeout=0
  • 集群版,将下面这2项打开即可。
    # (普通集群,不使用则不用开启)在群集中执行命令时要遵循的最大重定向数目。
    spring.redis.cluster.max-redirects=
    # (普通集群,不使用则不用开启)以逗号分隔的“主机:端口”对列表进行引导。
    spring.redis.cluster.nodes=127.0.0.1:1001,127.0.0.1:1002

注意:一旦开启了集群模式,那么基于单机的配置就会覆盖。

使用到的注解说明

注解 说明
@EnableAutoConfiguration spring会自动的猜测你需要的那些配置,智能的帮助你去扫描配置

特别提醒:扫码关注微信订阅号'起岸星辰',实时掌握IT业界技术资讯! 转载请保留原文中的链接!
  目录