技術是需要積累的。
一、日志
spring boot內部使用Commons Logging來記錄日志,但也保留外部接口可以讓一些日志框架來進行實現,例如Java Util Logging,Log4J2還有Logback。如果你想用某一種日志框架來進行實現的話,就必須先配置,默認情況下,spring boot使用Logback作為日志實現的框架。
1.顯示debug級別的日志
debug是打印信息最冗余的級別,其次是info,warn,error。在開發階段,可能需要debug級別的日志,這可以通過如下兩種方式實現:
- 通過application.properites配置debug=true
- 既然是更改的application.properties,那么肯定也能通過命令行來配置:
java -jar C:\Users\Administrator\Desktop\xx\demo.jar --debug
2.一份完美的配置
logback.xml
<!-- Logback configuration. See http://logback.qos.ch/manual/index.html -->
<configuration scan="true" scanPeriod="10 seconds">
<include resource="org/springframework/boot/logging/logback/base.xml"/>
<appender name="INFO_FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
<File>${LOG_PATH}/info.log</File>
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<fileNamePattern>${LOG_PATH}/info-%d{yyyyMMdd}.log.%i</fileNamePattern>
<timeBasedFileNamingAndTriggeringPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP">
<maxFileSize>10MB</maxFileSize>
</timeBasedFileNamingAndTriggeringPolicy>
<maxHistory>2</maxHistory>
</rollingPolicy>
<layout class="ch.qos.logback.classic.PatternLayout">
<Pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{36} -%msg%n
</Pattern>
</layout>
</appender>
<appender name="ERROR_FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
<filter class="ch.qos.logback.classic.filter.ThresholdFilter">
<level>ERROR</level>
</filter>
<File>${LOG_PATH}/error.log</File>
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<fileNamePattern>${LOG_PATH}/error-%d{yyyyMMdd}.log.%i
</fileNamePattern>
<timeBasedFileNamingAndTriggeringPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP">
<maxFileSize>10MB</maxFileSize>
</timeBasedFileNamingAndTriggeringPolicy>
<maxHistory>2</maxHistory>
</rollingPolicy>
<layout class="ch.qos.logback.classic.PatternLayout">
<Pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{36} -%msg%n
</Pattern>
</layout>
</appender>
<root level="INFO">
<appender-ref ref="INFO_FILE"/>
<appender-ref ref="ERROR_FILE"/>
</root>
</configuration>
在application.properties中,進行如下配置
#log
logging.config=classpath:logback.xml
logging.path=${user.home}/poem-log
3.最佳實踐
- 日志目錄最好不要放在webapp中,而要放在其它文件夾中。
- 日志配置最好單獨一個文件進行配置,這樣擴展性好、清晰。
二、視圖
spring boot 在springmvc的視圖解析器方面就默認集成了ContentNegotiatingViewResolver和BeanNameViewResolver,在視圖引擎上就已經集成自動配置的模版引擎,如下:
- FreeMarker
- Groovy
- Thymeleaf
- Velocity (deprecated in 1.4)
- Mustache
JSP技術spring boot 官方是不推薦的,原因有三:
- 在tomcat上,jsp不能在嵌套的tomcat容器解析即不能在打包成可執行的jar的情況下解析
- Jetty 嵌套的容器不支持jsp
- Undertow
而其他的模版引擎spring boot 都支持,並默認會到classpath的templates里面查找模版引擎,這里假如我們使用freemarker模版引擎
三、靜態資源
Spring Boot 默認配置的/**
映射到/static
(或/public
,/resources
,/META-INF/resources
),/webjars/**
會映射到classpath:/META-INF/resources/webjars/
。
注意:上面的/static
等目錄都是在classpath:
下面。
靜態資源映射還有一個配置選項,為了簡單這里用.properties方式書寫:
spring.mvc.static-path-pattern=/** # Path pattern used for static resources.
這個配置會影響默認的/**
,例如修改為/static/**
后,只能映射如/static/js/sample.js
這樣的請求(修改前是/js/sample.js
)。這個配置只能寫一個值,不像大多數可以配置多個用逗號隔開的。
四、異常處理
SpringBoot提供了健全的異常機制。異常處理分為三種:
- 按照異常分類處理
- 按照Controller分類處理
- 全局異常處理
1、使用@ResponseStatus
定義異常的類型
眾所周知,Java中可以通過繼承Exception自定義異常類型。在JavaWeb中還可以更進一步,異常可以分為很多種:
- 404:頁面不見了
- 500:內部錯誤(這是一切異常的默認statusCode)
......
使用SpringBoot可以通過注解來定義異常的種類,如下所以定義了一個“訂單未找到”異常,這個異常的狀態碼是404
@ResponseStatus(value=HttpStatus.NOT_FOUND, reason="No such Order") // 404
public class OrderNotFoundException extends RuntimeException {
// ...
}
使用這個異常時,如下寫法:直接拋出這個異常就行了。
@RequestMapping(value="/orders/{id}", method=GET)
public String showOrder(@PathVariable("id") long id, Model model) {
Order order = orderRepository.findOrderById(id);
if (order == null) throw new OrderNotFoundException(id);
model.addAttribute(order);
return "orderDetail";
}
2、使用@ExceptionHandler
處理某個Controller內的異常
在一個Controller中,使用@RequestMapping
注解某個函數,表示這個函數用來處理請求。使用@ExceptionHandler
注解某個函數,表示這個函數用來處理@RequestMapping
函數所拋出的異常。
如下代碼,在Controller中定義了三個ExceptionHandler,體會一下用法。
@Controller
public class ExceptionHandlingController {
// @RequestHandler methods
...
// Exception handling methods
// Convert a predefined exception to an HTTP Status code
@ResponseStatus(value=HttpStatus.CONFLICT,
reason="Data integrity violation") // 409
@ExceptionHandler(DataIntegrityViolationException.class)
public void conflict() {
// Nothing to do
}
// Specify name of a specific view that will be used to display the error:
@ExceptionHandler({SQLException.class,DataAccessException.class})
public String databaseError() {
// Nothing to do. Returns the logical view name of an error page, passed
// to the view-resolver(s) in usual way.
// Note that the exception is NOT available to this view (it is not added
// to the model) but see "Extending ExceptionHandlerExceptionResolver"
// below.
return "databaseError";
}
// Total control - setup a model and return the view name yourself. Or
// consider subclassing ExceptionHandlerExceptionResolver (see below).
@ExceptionHandler(Exception.class)
public ModelAndView handleError(HttpServletRequest req, Exception ex) {
logger.error("Request: " + req.getRequestURL() + " raised " + ex);
ModelAndView mav = new ModelAndView();
mav.addObject("exception", ex);
mav.addObject("url", req.getRequestURL());
mav.setViewName("error");
return mav;
}
}
3、異常顯示頁面
千萬不要讓用戶看見異常的stacktrace,那樣顯得很不專業。但是調試的時候,可以直接顯示異常棧。
例如使用JSP:
<h1>Error Page</h1>
<p>Application has encountered an error. Please contact support on ...</p>
<!--
Failed URL: ${url}
Exception: ${exception.message}
<c:forEach items="${exception.stackTrace}" var="ste"> ${ste}
</c:forEach>
-->
4、全局異常控制@ControllerAdvice
使用@ControllerAdvice
注解了的類相當於攔截器,把Controller的請求處理前、請求處理后、請求有異常的時候分別進行處理。
使用@ControllerAdvice
注解的類功能可以包含@ModelAttribute
,@ExceptionHandler
,@InitBinder
。但是只需要了解@ExceptionHandler
注解即可,別的都用不上。
@ControllerAdvice
class GlobalControllerExceptionHandler {
@ResponseStatus(HttpStatus.CONFLICT) // 409
@ExceptionHandler(DataIntegrityViolationException.class)
public void handleConflict() {
// Nothing to do
}
}
可以定義一個全局的處理一切異常的函數:
@ControllerAdvice
class GlobalDefaultExceptionHandler {
public static final String DEFAULT_ERROR_VIEW = "error";
@ExceptionHandler(value = Exception.class)
public ModelAndView
defaultErrorHandler(HttpServletRequest req, Exception e) throws Exception {
// If the exception is annotated with @ResponseStatus rethrow it and let
// the framework handle it - like the OrderNotFoundException example
// at the start of this post.
// AnnotationUtils is a Spring Framework utility class.
if (AnnotationUtils.findAnnotation
(e.getClass(), ResponseStatus.class) != null)
throw e;
// Otherwise setup and send the user to a default error-view.
ModelAndView mav = new ModelAndView();
mav.addObject("exception", e);
mav.addObject("url", req.getRequestURL());
mav.setViewName(DEFAULT_ERROR_VIEW);
return mav;
}
}
5、優先級
- 同一個異常被局部范圍異常處理器和全局范圍異常處理器同時覆蓋,會選擇小范圍的局部范圍處理器
- 同一個異常被小范圍的異常類和大范圍的異常處理器同時覆蓋,會選擇小范圍的異常處理器
五、熱部署
使用devtools
運行springBoot的兩種方式:mvn run,springboot:run
六、SpringBoot測試
過去,我以為每個類都寫一個main函數測試一下這個類就可以了。這種方式在使用Spring的情況下不好使,因為很多注解都沒有發揮作用。
使用Spring的代碼,必須寫測試,否則
- application.properties文件不會正常加載。
- 使用的
@Autowired
的成員變量都不會自動注入。
寫測試很簡單,只需要用到三個注解:
- 用下面兩個注解來注解測試類
@SpringBootTest
@RunWith(SpringJUnit4ClassRunner.class)
- 用
@Test
注解來注解測試方法
下面看一個具體的例子,這個例子演示了多例的用法。
在Spring中,使用Component
注解的類相當於一個“Bean”,像Controller本身也是Component。使用Component注解的類默認都是單例,即@Scope("singleton")
,如果改成多例,可以通過@Scope("prototype")
注解來實現。
下面定義了一個類Config,這個類有一個成員變量token。如果Config是單例,會發現config2跟config指向同一個對象;如果Config是多例,會發現config和config2互不影響。
@SpringBootTest
@RunWith(SpringJUnit4ClassRunner.class)
public class SingletonTest {
@Autowired Config config;
@Autowired Config config2;
@Test
public void go(){
System.out.println(config.getToken());
System.out.println(config2.getToken());
config2.setToken("haha");
System.out.println(config.getToken());
}
}
注意一個知識點,在SpringBoot中,Controller默認是單例。
對於只包含靜態方法的類,完全可以用單例來替代。
即便不使用Web,也可以使用Spring的單例、多例、注入等機制。
參考資料
http://jinnianshilongnian.iteye.com/blog/1866350 開濤的@ControllerAdvice(三個作用)
http://www.tuicool.com/articles/fA7nuii springboot約定的異常處理體系
https://spring.io/blog/2013/11/01/exception-handling-in-spring-mvc springMVC異常處理體系
這篇博客提供了一個github代碼,用到thymleaf,是挺好的SpringMVC入門資料。
http://www.baeldung.com/2013/01/31/exception-handling-for-rest-with-spring-3-2/ springMVC異常處理體系