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会自动的猜测你需要的那些配置,智能的帮助你去扫描配置
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 202,723评论 5 476
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,080评论 2 379
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 149,604评论 0 335
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,440评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,431评论 5 364
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,499评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,893评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,541评论 0 256
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,751评论 1 296
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,547评论 2 319
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,619评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,320评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,890评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,896评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,137评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,796评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,335评论 2 342

推荐阅读更多精彩内容

  • 摘要 看完本文你将掌握如下知识点: 如何搭建一个SpringBoot项目 SpringBoot自动配置原理 Spr...
    飘逸峰阅读 7,118评论 1 41
  • Spring Boot 参考指南 介绍 转载自:https://www.gitbook.com/book/qbgb...
    毛宇鹏阅读 46,717评论 6 342
  • Spring Cloud为开发人员提供了快速构建分布式系统中一些常见模式的工具(例如配置管理,服务发现,断路器,智...
    卡卡罗2017阅读 134,579评论 18 139
  • 使用SpringBoot搭建项目的理由 1.简化配置,避免了在搭建项目环境时写一堆没有技术含量却又不可缺少的xml...
    wch853阅读 1,011评论 0 1
  • 摘要 看完本文你将掌握如下知识点: Spring Boot对JDBC的支持 Spring Boot项目多数据源的配...
    飘逸峰阅读 2,430评论 0 12