Spring Boot 學(xué)習(xí)筆記

spring boot學(xué)習(xí)筆記

官方地址: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 編寫啟動(dòng)程序

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//相當(dāng)于Controller+ResponseBody;即自動(dòng)是ajax的請(qǐng)求
@EnableAutoConfiguration//spring 會(huì)自動(dòng)裝配相關(guān)的配置,這個(gè)是必須有的
public class SpringBootHello {
    @RequestMapping("/")
    public String home(){
        return "hello word!";
    }
    public static void main(String[] args) throws Exception{
        SpringApplication.run(SpringBootHello.class, args);
    }
}

運(yùn)行main方法慨飘,在瀏覽器中訪問:http://localhost:8080/ 即可看到hello word
打jar包運(yùn)行的方法:添加如下maven插件依賴

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

運(yùn)行maven的 package命令柿扣;然后運(yùn)行 java -jar xxx.jar

1.2.2 方式二
@SpringBootApplication//這個(gè)注解包含了EnableAutoConfiguration贾节;更多的配置可以查看源碼
@RestController//相當(dāng)于Controller+ResponseBody;即自動(dòng)是ajax的請(qǐng)求
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 {

    //排除自啟動(dòng)項(xiàng)
    Class<?>[] exclude() default {};

    //排除自動(dòng)啟動(dòng)的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默認(rèn)是采用logback進(jìn)行日志處理、Logback是由log4j創(chuàng)始人設(shè)計(jì)的又一個(gè)開源日志組件
logback當(dāng)前分成三個(gè)模塊:logback-core,logback- classic和logback-access颓影。logback-core是其它兩個(gè)模塊的基礎(chǔ)模塊

2.1 logback.xml詳情

<?xml version="1.0" encoding="UTF-8"?>
<!--
configuration 根節(jié)點(diǎn)配置參數(shù)說明:
      scan:當(dāng)此屬性設(shè)置為true時(shí),配置文件如果發(fā)生改變,將會(huì)被重新加載熏迹,默認(rèn)值為true
scanPeriod:設(shè)置監(jiān)測(cè)配置文件是否有修改的時(shí)間間隔辑甜,如果沒有給出時(shí)間單位衰絮,默認(rèn)單位是毫秒。當(dāng)scan為true時(shí)磷醋,此屬性生效猫牡。默認(rèn)的時(shí)間間隔為1分鐘。配置示例: 120 seconds
     debug:當(dāng)此屬性設(shè)置為true時(shí)邓线,將打印出logback內(nèi)部日志信息淌友,實(shí)時(shí)查看logback運(yùn)行狀態(tài)。默認(rèn)值為false
-->
<configuration>
    <!--contextName 設(shè)置日志上下文名稱骇陈,可以通過%contextName來打印日志上下文名稱
           每個(gè)logger都關(guān)聯(lián)到logger上下文震庭,默認(rèn)上下文名稱為“default”。
           但可以使用<contextName>設(shè)置成其他名字你雌,用于區(qū)分不同應(yīng)用程序的記錄。一旦設(shè)置超凳,不能修改
   -->
    <contextName>spring-boot-demo-log</contextName>

    <!--property 可以用來設(shè)置變量狭姨,可以通過${name}來訪問,有以下的屬性
            name:用于${name}訪問的key;
            value:用于${name}訪問的value
                例:<property name="log.path" value="c:\\logback.log" />
            file: 用于指定配置文件的路徑肴颊,他的作用在于,如果你有多個(gè)配置信息的話渣磷,可以直接寫在配置文件中苫昌,然后通過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 格式化日志輸出節(jié)點(diǎn)
        1. 有2個(gè)屬性name和class祟身,class用來指定哪種輸出策略,常用就是控制臺(tái)輸出策略[STDOUT/console]和文件輸出策略[file].
        2. appender有以下子節(jié)點(diǎn):
            2.1 filter: 日志輸出攔截器物独,可以自定義攔截器也可以用系統(tǒng)一些定義好的攔截器
                /**
                * 自定義日志輸出攔截器
                * @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; //不允許輸出
                        }
                    }
                }
                系統(tǒng)自帶的日志攔截器
                例:用ThresholdFilter來過濾掉ERROR級(jí)別以下的日志不輸出
                    <filter class="ch.qos.logback.classic.filter.ThresholdFilter">
                        <level>ERROR</level>
                    </filter>
            2.2 encoder和pattern節(jié)點(diǎn)組合用于具體輸出的日志格式
            2.3 file節(jié)點(diǎn)用來指明日志文件的輸出位置袜硫,可以是絕對(duì)路徑也可以是相對(duì)路徑
            2.4 rollingPolicy日志回滾策略.
                2.4.1 TimeBasedRollingPolicy: 基于時(shí)間的回滾策略,有以下子節(jié)點(diǎn)
                    fileNamePattern:必要節(jié)點(diǎn),可以用來設(shè)置指定時(shí)間的日志歸檔挡篓,例如每天將日志歸檔成一個(gè)zip包
                    maxHistory:     可選節(jié)點(diǎn)婉陷,控制保留的歸檔文件的最大數(shù)量,超出數(shù)量就刪除舊文件,官研,例如設(shè)置為30的話秽澳,則30天之后,舊的日志就會(huì)被刪除
                    totalSizeCap:   可選節(jié)點(diǎn)戏羽,用來指定日志文件的上限大小担神,例如設(shè)置為1GB的話,那么到了這個(gè)值始花,就會(huì)刪除舊的日志

                    例:
                    <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>

    -->
    <!--每個(gè)級(jí)別的日志配置-->
    <!-- 控制臺(tái)輸出 -->
    <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配置了日志在服務(wù)器上面只存留幾個(gè)備份-->
    <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>

    <!--在生產(chǎn)中往往會(huì)因?yàn)榇罅康娜罩緦?dǎo)致io過高,所以通過AsyncAppender進(jìn)行異步的日志記錄酷宵。-->
    <appender name="ASYNC_LOG" class="ch.qos.logback.classic.AsyncAppender">
        <!-- 不丟失日志.默認(rèn)的,如果隊(duì)列的80%已滿,則會(huì)丟棄TRACT亥贸、DEBUG、INFO級(jí)別的日志 -->
        <discardingThreshold>0</discardingThreshold>
        <!-- 更改默認(rèn)的隊(duì)列的深度,該值會(huì)影響性能.默認(rèn)值為256 -->
        <queueSize>64</queueSize>
        <appender-ref ref="FILE_LOG"/>
    </appender>
    <appender name="ASYNC_LOG" class="ch.qos.logback.classic.AsyncAppender">
        <!-- 不丟失日志.默認(rèn)的,如果隊(duì)列的80%已滿,則會(huì)丟棄TRACT浇垦、DEBUG炕置、INFO級(jí)別的日志 -->
        <discardingThreshold>0</discardingThreshold>
        <!-- 更改默認(rèn)的隊(duì)列的深度,該值會(huì)影響性能.默認(rèn)值為256 -->
        <queueSize>64</queueSize>
        <appender-ref ref="FILE_LOG"/>
    </appender>
    <appender name="ASYNC_LOG_DEBUG" class="ch.qos.logback.classic.AsyncAppender">
        <!-- 不丟失日志.默認(rèn)的,如果隊(duì)列的80%已滿,則會(huì)丟棄TRACT、DEBUG男韧、INFO級(jí)別的日志 -->
        <discardingThreshold>0</discardingThreshold>
        <!-- 更改默認(rèn)的隊(duì)列的深度,該值會(huì)影響性能.默認(rèn)值為256 -->
        <queueSize>64</queueSize>
        <appender-ref ref="FILE_DEBUG"/>
    </appender>
    <appender name="ASYNC_LOG_INFO" class="ch.qos.logback.classic.AsyncAppender">
        <!-- 不丟失日志.默認(rèn)的,如果隊(duì)列的80%已滿,則會(huì)丟棄TRACT朴摊、DEBUG、INFO級(jí)別的日志 -->
        <discardingThreshold>0</discardingThreshold>
        <!-- 更改默認(rèn)的隊(duì)列的深度,該值會(huì)影響性能.默認(rèn)值為256 -->
        <queueSize>64</queueSize>
        <appender-ref ref="FILE_INFO"/>
    </appender>
    <appender name="ASYNC_LOG_WARN" class="ch.qos.logback.classic.AsyncAppender">
        <!-- 不丟失日志.默認(rèn)的,如果隊(duì)列的80%已滿,則會(huì)丟棄TRACT煌抒、DEBUG仍劈、INFO級(jí)別的日志 -->
        <discardingThreshold>0</discardingThreshold>
        <!-- 更改默認(rèn)的隊(duì)列的深度,該值會(huì)影響性能.默認(rèn)值為256 -->
        <queueSize>64</queueSize>
        <appender-ref ref="FILE_WARN"/>
    </appender>
    <appender name="ASYNC_LOG_ERROR" class="ch.qos.logback.classic.AsyncAppender">
        <!-- 不丟失日志.默認(rèn)的,如果隊(duì)列的80%已滿,則會(huì)丟棄TRACT、DEBUG寡壮、INFO級(jí)別的日志 -->
        <discardingThreshold>0</discardingThreshold>
        <!-- 更改默認(rèn)的隊(duì)列的深度,該值會(huì)影響性能.默認(rèn)值為256 -->
        <queueSize>64</queueSize>
        <appender-ref ref="FILE_ERROR"/>
    </appender>

    <!--指定root的日志級(jí)別贩疙,一般來說都會(huì)指定到info級(jí)別讹弯,
        因?yàn)镾pringBoot運(yùn)行的時(shí)候會(huì)產(chǎn)生大量的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這個(gè)包里面輸出debug日志、-->
    <!--一般來說如果使用Mybatis或者h(yuǎn)ibernate这溅,需要輸出SQL都需要通過這里進(jìn)行配置组民,輸出debug級(jí)別的日志-->
    <logger name="org.springframework" level="INFO"/>
</configuration>

3.統(tǒng)一異常處理

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

3.1 spring boot 自帶的統(tǒng)一異常處理,重新配置異常地址和頁(yè)面

SpringBoot在頁(yè)面發(fā)生異常的時(shí)候會(huì)自動(dòng)把請(qǐng)求轉(zhuǎn)到/error; SpringBoot內(nèi)置了一個(gè)BasicErrorController對(duì)異常進(jìn)行統(tǒng)一的處理悲靴,
這個(gè)錯(cuò)誤的地址是可以重新配置的臭胜。

resources目錄下創(chuàng)建一個(gè)application.yaml配置文件;寫入如下配置

server:
    #訪問端口號(hào)
    port:  8082
    error:
        #設(shè)置錯(cuò)誤路徑
        path:  /test/error

開始編寫測(cè)試程序

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>測(cè)試異常</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){
        //傳入?yún)?shù)為0時(shí)拋出異常
        int num = 1/nu;
        return "this ok "+ num;
    }
    
    @RequestMapping(value = "/htmls")
    public String htmlRequestExceptionTest(int nu){
        //傳入?yún)?shù)為0時(shí)拋出異常
        int num = 1/nu;
        return "index";
    }

    //啟動(dòng)
    public static void main(String[] args) throws Exception{
        SpringApplication.run(TestExceptionController.class);
    }
}

resources目錄下創(chuàng)建templates目錄(這個(gè)是默認(rèn)放置模版文件的目錄)癞尚,并分別創(chuàng)建error.ftl和index.ftl 文件耸三。error.ftl文件用于替換spring boot原有的錯(cuò)誤頁(yè)面,index.ftl用于測(cè)試頁(yè)面

由于用到了freemarker浇揩,需要添加maven依賴

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

啟動(dòng)后開始測(cè)試

#測(cè)試是否可正常訪問
http://127.0.0.1:8082/htmls?nu=1    
#測(cè)試異常頁(yè)面
http://127.0.0.1:8082/htmls?nu=0     
#ajax請(qǐng)求返回的錯(cuò)誤提示
{
    "timestamp": "2018-06-13T03:55:51.587+0000",
    "status": 500,
    "error": "Internal Server Error",
    "message": "/ by zero",
    "path": "/ajax"
}

3.2 通用Exception處理

通過使用@ControllerAdvice來進(jìn)行統(tǒng)一異常處理仪壮,@ExceptionHandler(value = Exception.class)來指定捕獲的異常
下面針對(duì)兩種異常進(jìn)行了特殊處理分別返回頁(yè)面和json數(shù)據(jù),使用這種方式有個(gè)局限胳徽,無法根據(jù)不同的頭部返回不同的數(shù)據(jù)格式积锅,而且無法針對(duì)404、403等多種狀態(tài)進(jìn)行處理

//異常處理類
@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);
    }
}

//測(cè)試路由
@RequestMapping(value = "/coustom")
public String coustomExceptionTest() throws CustomException {
    customExce();
    return "index";
}

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

3.3 自定義BasicErrorController 錯(cuò)誤處理

在初始介紹哪里提到了BasicErrorController养盗,這個(gè)是SpringBoot的默認(rèn)錯(cuò)誤處理缚陷,也是一種全局處理方式。咱們可以模仿這種處理方式自定義自己的全局錯(cuò)誤處理
下面定義了一個(gè)自己的BasicErrorController往核,可以根據(jù)自己的需求自定義對(duì)應(yīng)的錯(cuò)誤處理箫爷。

@ResponseStatus 注解的異常類會(huì)被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 錯(cuò)誤
     * @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錯(cuò)誤
     * @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);
    }

    /**
     * 參數(shù)不完整錯(cuò)誤.
     * @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;
    }
}

定義文件結(jié)構(gòu)如下:

文件結(jié)構(gòu)

4. spring boot中使用redis

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

spring:
    rdeis:
        host: 127.0.0.1
        port: 6379
        timeout: 3000
        pool:
            # 連接池最大連接數(shù)(使用負(fù)值表示沒有限制)
            max-total: 8
            # 連接池最大阻塞等待時(shí)間(使用負(fù)值表示沒有限制)
            max-wait-millis: -1
            # 連接池中的最大空閑連接
            max-idle: 8
            # 連接池中的最小空閑連接
            min-idle: 0

加入需要的依賴包

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

注:網(wǎng)上其他有很多教程說引入的是下面這個(gè)包

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

但是我在引入時(shí)發(fā)現(xiàn)當(dāng)前這個(gè)版本中這個(gè)包已經(jīng)無法下載了。

4.1 redis 配置方式1铆铆,使用默認(rèn)的

因?yàn)樯厦嬉蕾嚵藄pring-boot-starter-data-redis,可以使用默認(rèn)的 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(){
        //這兩個(gè)的是不一樣的。因?yàn)镽edisTemplate<Object,Object> 默認(rèn)會(huì)將對(duì)象使用JdkSerializationRedisSerializer進(jìn)行序列化
        
        redisTemplate.opsForValue().set("test", "123456");
        template.opsForValue().set("test", "abcdef");
        return template.opsForValue().get("test")+"---|||---"+redisTemplate.opsForValue().get("test");
    }
}

4.2 redis 配置方式2薄货,自己手動(dòng)配置

4.2.1 redis的單機(jī)版連接池配置

需要加入額外的jar包:

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

編寫相關(guān)代碼:

  • 單機(jī)版

      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;
    
          /**
           * 連接設(shè)置
           * @return 返回連接工廠
           */
          @Bean(name = "jedisConnectionFactory")
          public JedisConnectionFactory getJedisConnectionFactory(JedisPoolConfig jedisPoolConfig){
              JedisConnectionFactory factory = new JedisConnectionFactory();
              factory.setHostName(host);
              factory.setPort(port);
              //factory.setPassword("");//設(shè)置認(rèn)證密碼
              //factory.setDatabase();//設(shè)置庫(kù)
              factory.setTimeout(timeout);
              factory.setUsePool(true);
              factory.setPoolConfig(jedisPoolConfig);
              return factory;
          }
      
          /**
           * 連接池設(shè)置
           * @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類型的話,會(huì)報(bào)類型轉(zhuǎn)換錯(cuò)誤碍论;
              RedisSerializer<String> redisSerializer = new StringRedisSerializer();//Long類型不可以會(huì)出現(xiàn)異常信息;
              redisTemplate.setKeySerializer(redisSerializer);
              redisTemplate.setHashKeySerializer(redisSerializer);
              //key序列化方式;但是如果方法上有Long等非String類型的話谅猾,會(huì)報(bào)類型轉(zhuǎn)換錯(cuò)誤;
              redisTemplate.setValueSerializer(redisSerializer);
              redisTemplate.setHashValueSerializer(redisSerializer);
              redisTemplate.afterPropertiesSet();
              //查看 StringRedisTemplate 的初始化源碼鳍悠,你會(huì)發(fā)現(xiàn)其實(shí)它和上面一樣做了的key和value的序列化設(shè)置
              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 //相當(dāng)于以前的配置文件
      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])));
              }
              //創(chuàng)建redis的集群對(duì)象
              return new JedisCluster(nodes);
          }
      }
    

使用方式同上面方式一的税娜,這次你會(huì)發(fā)現(xiàn)他們的key是一樣(因?yàn)樾蛄谢姆绞绞且粯拥牧耍?/p>

另附一個(gè)redis配置比較完整的配置文件
來源:https://www.cnblogs.com/EasonJim/p/7805665.html

  • 單機(jī)版

      # REDIS(RedisProperties)
      # (普通集群,不使用則不用開啟)在群集中執(zhí)行命令時(shí)要遵循的最大重定向數(shù)目藏研。
      # spring.redis.cluster.max-redirects=
      # (普通集群敬矩,不使用則不用開啟)以逗號(hào)分隔的“主機(jī):端口”對(duì)列表進(jìn)行引導(dǎo)。
      # spring.redis.cluster.nodes=
      # 連接工廠使用的數(shù)據(jù)庫(kù)索引蠢挡。
      spring.redis.database=0
      # 連接URL弧岳,將覆蓋主機(jī)凳忙,端口和密碼(用戶將被忽略),例如:redis://user:password@example.com:6379
      spring.redis.url=
      # Redis服務(wù)器主機(jī)禽炬。
      spring.redis.host=localhost
      # 登錄redis服務(wù)器的密碼涧卵。
      spring.redis.password=
      # 啟用SSL支持。
      spring.redis.ssl=false
      # 池在給定時(shí)間可以分配的最大連接數(shù)腹尖。使用負(fù)值無限制柳恐。
      spring.redis.pool.max-total=8
      # 池中“空閑”連接的最大數(shù)量。使用負(fù)值表示無限數(shù)量的空閑連接热幔。
      spring.redis.pool.max-idle=8
      # 連接分配在池被耗盡時(shí)拋出異常之前應(yīng)該阻塞的最長(zhǎng)時(shí)間量(以毫秒為單位)乐设。使用負(fù)值可以無限期地阻止。
      spring.redis.pool.max-wait-millis=-1
      # 目標(biāo)為保持在池中的最小空閑連接數(shù)绎巨。這個(gè)設(shè)置只有在正面的情況下才有效果伤提。
      spring.redis.pool.min-idle=0
      # Redis服務(wù)器端口。
      spring.redis.port=6379
      # (哨兵模式认烁,不使用則不用開啟)Redis服務(wù)器的名稱肿男。
      # spring.redis.sentinel.master=
      # (哨兵模式,不使用則不用開啟)主機(jī):端口對(duì)的逗號(hào)分隔列表却嗡。 
      # spring.redis.sentinel.nodes=
      # 以毫秒為單位的連接超時(shí)舶沛。
      spring.redis.timeout=0
    
  • 集群版,將下面這2項(xiàng)打開即可窗价。

      # (普通集群如庭,不使用則不用開啟)在群集中執(zhí)行命令時(shí)要遵循的最大重定向數(shù)目。
      spring.redis.cluster.max-redirects=
      # (普通集群撼港,不使用則不用開啟)以逗號(hào)分隔的“主機(jī):端口”對(duì)列表進(jìn)行引導(dǎo)坪它。
      spring.redis.cluster.nodes=127.0.0.1:1001,127.0.0.1:1002
    

注意:一旦開啟了集群模式,那么基于單機(jī)的配置就會(huì)覆蓋帝牡。

使用到的注解說明

注解 說明
@EnableAutoConfiguration spring會(huì)自動(dòng)的猜測(cè)你需要的那些配置往毡,智能的幫助你去掃描配置
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市靶溜,隨后出現(xiàn)的幾起案子开瞭,更是在濱河造成了極大的恐慌,老刑警劉巖罩息,帶你破解...
    沈念sama閱讀 218,755評(píng)論 6 507
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件嗤详,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡瓷炮,警方通過查閱死者的電腦和手機(jī)葱色,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,305評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來娘香,“玉大人苍狰,你說我怎么就攤上這事办龄。” “怎么了舞痰?”我有些...
    開封第一講書人閱讀 165,138評(píng)論 0 355
  • 文/不壞的土叔 我叫張陵土榴,是天一觀的道長(zhǎng)。 經(jīng)常有香客問我响牛,道長(zhǎng)玷禽,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,791評(píng)論 1 295
  • 正文 為了忘掉前任呀打,我火速辦了婚禮矢赁,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘贬丛。我一直安慰自己撩银,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,794評(píng)論 6 392
  • 文/花漫 我一把揭開白布豺憔。 她就那樣靜靜地躺著额获,像睡著了一般。 火紅的嫁衣襯著肌膚如雪恭应。 梳的紋絲不亂的頭發(fā)上抄邀,一...
    開封第一講書人閱讀 51,631評(píng)論 1 305
  • 那天,我揣著相機(jī)與錄音昼榛,去河邊找鬼境肾。 笑死,一個(gè)胖子當(dāng)著我的面吹牛胆屿,可吹牛的內(nèi)容都是我干的奥喻。 我是一名探鬼主播,決...
    沈念sama閱讀 40,362評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼非迹,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼环鲤!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起彻秆,我...
    開封第一講書人閱讀 39,264評(píng)論 0 276
  • 序言:老撾萬榮一對(duì)情侶失蹤楔绞,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后唇兑,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,724評(píng)論 1 315
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡桦锄,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,900評(píng)論 3 336
  • 正文 我和宋清朗相戀三年扎附,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片结耀。...
    茶點(diǎn)故事閱讀 40,040評(píng)論 1 350
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡留夜,死狀恐怖匙铡,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情碍粥,我是刑警寧澤鳖眼,帶...
    沈念sama閱讀 35,742評(píng)論 5 346
  • 正文 年R本政府宣布,位于F島的核電站嚼摩,受9級(jí)特大地震影響钦讳,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜枕面,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,364評(píng)論 3 330
  • 文/蒙蒙 一愿卒、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧潮秘,春花似錦琼开、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,944評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至躏精,卻和暖如春渣刷,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背玉控。 一陣腳步聲響...
    開封第一講書人閱讀 33,060評(píng)論 1 270
  • 我被黑心中介騙來泰國(guó)打工飞主, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人高诺。 一個(gè)月前我還...
    沈念sama閱讀 48,247評(píng)論 3 371
  • 正文 我出身青樓碌识,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親虱而。 傳聞我的和親對(duì)象是個(gè)殘疾皇子筏餐,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,979評(píng)論 2 355

推薦閱讀更多精彩內(nèi)容

  • 摘要 看完本文你將掌握如下知識(shí)點(diǎn): 如何搭建一個(gè)SpringBoot項(xiàng)目 SpringBoot自動(dòng)配置原理 Spr...
    飄逸峰閱讀 7,146評(píng)論 1 41
  • Spring Boot 參考指南 介紹 轉(zhuǎn)載自:https://www.gitbook.com/book/qbgb...
    毛宇鵬閱讀 46,822評(píng)論 6 342
  • Spring Cloud為開發(fā)人員提供了快速構(gòu)建分布式系統(tǒng)中一些常見模式的工具(例如配置管理,服務(wù)發(fā)現(xiàn)牡拇,斷路器魁瞪,智...
    卡卡羅2017閱讀 134,659評(píng)論 18 139
  • 使用SpringBoot搭建項(xiàng)目的理由 1.簡(jiǎn)化配置,避免了在搭建項(xiàng)目環(huán)境時(shí)寫一堆沒有技術(shù)含量卻又不可缺少的xml...
    wch853閱讀 1,011評(píng)論 0 1
  • 摘要 看完本文你將掌握如下知識(shí)點(diǎn): Spring Boot對(duì)JDBC的支持 Spring Boot項(xiàng)目多數(shù)據(jù)源的配...
    飄逸峰閱讀 2,436評(píng)論 0 12