SpringBoot總結

技術是須要積累的。html

1、日誌

spring boot內部使用Commons Logging來記錄日誌,但也保留外部接口可讓一些日誌框架來進行實現,例如Java Util Logging,Log4J2還有Logback。若是你想用某一種日誌框架來進行實現的話,就必須先配置,默認狀況下,spring boot使用Logback做爲日誌實現的框架。java

1.顯示debug級別的日誌

debug是打印信息最冗餘的級別,其次是info,warn,error。在開發階段,可能須要debug級別的日誌,這能夠經過以下兩種方式實現:git

  • 經過application.properites配置debug=true
  • 既然是更改的application.properties,那麼確定也能經過命令行來配置:
    java -jar C:\Users\Administrator\Desktop\xx\demo.jar --debug

2.一份完美的配置

logback.xmlgithub

<!-- 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中,進行以下配置web

#log
logging.config=classpath:logback.xml
logging.path=${user.home}/poem-log

3.最佳實踐

  • 日誌目錄最好不要放在webapp中,而要放在其它文件夾中。
  • 日誌配置最好單獨一個文件進行配置,這樣擴展性好、清晰。

2、視圖

spring boot 在springmvc的視圖解析器方面就默認集成了ContentNegotiatingViewResolver和BeanNameViewResolver,在視圖引擎上就已經集成自動配置的模版引擎,以下:spring

  1. FreeMarker
  2. Groovy
  3. Thymeleaf
  4. Velocity (deprecated in 1.4)
  5. Mustache

JSP技術spring boot 官方是不推薦的,緣由有三:spring-mvc

  1. 在tomcat上,jsp不能在嵌套的tomcat容器解析即不能在打包成可執行的jar的狀況下解析
  2. Jetty 嵌套的容器不支持jsp
  3. Undertow

而其餘的模版引擎spring boot 都支持,並默認會到classpath的templates裏面查找模版引擎,這裏假如咱們使用freemarker模版引擎tomcat

3、靜態資源

Spring Boot 默認配置的/**映射到/static(或/public/resources/META-INF/resources),/webjars/**會映射到classpath:/META-INF/resources/webjars/springboot

注意:上面的/static等目錄都是在classpath:下面。mvc

靜態資源映射還有一個配置選項,爲了簡單這裏用.properties方式書寫:
spring.mvc.static-path-pattern=/** # Path pattern used for static resources.
這個配置會影響默認的/**,例如修改成/static/**後,只能映射如/static/js/sample.js這樣的請求(修改前是/js/sample.js)。這個配置只能寫一個值,不像大多數能夠配置多個用逗號隔開的。

4、異常處理

SpringBoot提供了健全的異常機制。異常處理分爲三種:

  • 按照異常分類處理
  • 按照Controller分類處理
  • 全局異常處理

一、使用@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";
 }

二、使用@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;
  }
}

三、異常顯示頁面

千萬不要讓用戶看見異常的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>
  -->

四、全局異常控制@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

6、SpringBoot測試

過去,我覺得每一個類都寫一個main函數測試一下這個類就能夠了。這種方式在使用Spring的狀況下很差使,由於不少註解都沒有發揮做用。

使用Spring的代碼,必須寫測試,不然

  • application.properties文件不會正常加載。
  • 使用的@Autowired的成員變量都不會自動注入。

寫測試很簡單,只須要用到三個註解:

  1. 用下面兩個註解來註解測試類
@SpringBootTest
@RunWith(SpringJUnit4ClassRunner.class)
  1. @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的單例、多例、注入等機制。

參考資料

打不死的小強
深刻學習微框架SpringBoot

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異常處理體系

相關文章
相關標籤/搜索