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">-->
<!--<!–<filter class="ch.qos.logback.classic.filter.ThresholdFilter">–>-->
<!--<!–<level>ERROR</level>–>-->
<!--<!–</filter>–>-->
<!--<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)如下:
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è)你需要的那些配置往毡,智能的幫助你去掃描配置 |