Spring Boot 進階

上一篇帶領你們初步瞭解瞭如何使用 Spring Boot 搭建框架,經過 Spring Boot 和傳統的 SpringMVC 架構的對比,咱們清晰地發現 Spring Boot 的好處,它使咱們的代碼更加簡單,結構更加清晰。html

從這一篇開始,我將帶領你們更加深刻的認識 Spring Boot,將 Spring Boot 涉及到東西進行拆解,從而瞭解 Spring Boot 的方方面面。學完本文後,讀者能夠基於 Spring Boot 搭建更加複雜的系統框架。前端

咱們知道,Spring Boot 是一個大容器,它將不少第三方框架都進行了集成,咱們在實際項目中用到哪一個模塊,再引入哪一個模塊。好比咱們項目中的持久化框架用 MyBatis,則在 pom.xml 添加以下依賴:java

<dependency>
            <groupId>org.mybatis.spring.boot</groupId>
            <artifactId>mybatis-spring-boot-starter</artifactId>
            <version>1.1.1</version>
        </dependency>
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>5.1.40</version>
        </dependency>

yaml/properties 文件mysql

咱們知道整個 Spring Boot 項目只有一個配置文件,那就是 application.yml,Spring Boot 在啓動時,就會從 application.yml 中讀取配置信息,並加載到內存中。上一篇咱們只是粗略的列舉了幾個配置項,其實 Spring Boot 的配置項是不少的,本文咱們將學習在實際項目中經常使用的配置項(注:爲了方便說明,配置項均以 properties 文件的格式寫出,後續的實際配置都會寫成 yaml 格式)。
圖片描述web

下面是我參與的某個項目的 application.yml 配置文件內容:spring

server:
  port: 8080
  context-path: /api
  tomcat:
    max-threads: 1000
    min-spare-threads: 50
  connection-timeout: 5000
spring:
  profiles:
    active: dev
  http:
    multipart:
      maxFileSize: -1
  datasource:
    url: jdbc:mysql://localhost:3306/database?useUnicode=true&characterEncoding=UTF-8&useSSL=true
    username: root
    password: root
    driverClassName: com.mysql.jdbc.Driver
  jpa:
    database: MYSQL
    showSql: true
    hibernate:
      namingStrategy: org.hibernate.cfg.ImprovedNamingStrategy
    properties:
      hibernate:
        dialect: org.hibernate.dialect.MySQL5Dialect
mybatis:
  configuration:
     #配置項:開啓下劃線到駝峯的自動轉換. 做用:將數據庫字段根據駝峯規則自動注入到對象屬性。
     map-underscore-to-camel-case: true

以上列舉了經常使用的配置項,全部配置項信息均可以在官網中找到,本課程就不一一列舉了。sql

多環境配置數據庫

在一個企業級系統中,咱們可能會遇到這樣一個問題:開發時使用開發環境,測試時使用測試環境,上線時使用生產環境。每一個環境的配置均可能不同,好比開發環境的數據庫是本地地址,而測試環境的數據庫是測試地址。那咱們在打包的時候如何生成不一樣環境的包呢?json

這裏的解決方案有不少:api

1.每次編譯以前手動把全部配置信息修改爲當前運行的環境信息。這種方式致使每次都須要修改,至關麻煩,也容易出錯。
2.利用 Maven,在 pom.xml 裏配置多個環境,每次編譯以前將 settings.xml 裏面修改爲當前要編譯的環境 ID。這種方式會事先設置好全部環境,缺點就是每次也須要手動指定環境,若是環境指定錯誤,發佈時是不知道的。
3.第三種方案就是本文重點介紹的,也是做者強烈推薦的方式。

首先,建立 application.yml 文件,在裏面添加以下內容:

spring:
  profiles:
    active: dev

含義是指定當前項目的默認環境爲 dev,即項目啓動時若是不指定任何環境,Spring Boot 會自動從 dev 環境文件中讀取配置信息。咱們能夠將不一樣環境都共同的配置信息寫到這個文件中。

而後建立多環境配置文件,文件名的格式爲:application-{profile}.yml,其中,{profile} 替換爲環境名字,如 application-dev.yml,咱們能夠在其中添加當前環境的配置信息,如添加數據源:

spring:
  datasource:
    url: jdbc:mysql://localhost:3306/database?useUnicode=true&characterEncoding=UTF-8&useSSL=true
    username: root
    password: root
    driverClassName: com.mysql.jdbc.Driver

這樣,咱們就實現了多環境的配置,每次編譯打包咱們無需修改任何東西,編譯爲 jar 文件後,運行命令:

java -jar api.jar --spring.profiles.active=dev

其中 --spring.profiles.active 就是咱們要指定的環境。

經常使用註解

咱們知道,Spring Boot 主要採用註解的方式,在上一篇的入門實例中,咱們也用到了一些註解。

本文,我將詳細介紹在實際項目中經常使用的註解。

@SpringBootApplication

咱們能夠注意到 Spring Boot 支持 main 方法啓動,在咱們須要啓動的主類中加入此註解,告訴 Spring Boot,這個類是程序的入口。如:

@SpringBootApplication
public class Application {

    public static void main(String[] args) {
        SpringApplication.run(Application.class, args);
    }
}

若是不加這個註解,程序是沒法啓動的。

咱們查看下 SpringBootApplication 的源碼,源碼以下:

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@SpringBootConfiguration
@EnableAutoConfiguration
@ComponentScan(excludeFilters = {
        @Filter(type = FilterType.CUSTOM, classes = TypeExcludeFilter.class),
        @Filter(type = FilterType.CUSTOM, classes = AutoConfigurationExcludeFilter.class) })
public @interface SpringBootApplication {

    /**
     * Exclude specific auto-configuration classes such that they will never be applied.
     * @return the classes to exclude
     */
    @AliasFor(annotation = EnableAutoConfiguration.class, attribute = "exclude")
    Class<?>[] exclude() default {};

    /**
     * Exclude specific auto-configuration class names such that they will never be
     * applied.
     * @return the class names to exclude
     * @since 1.3.0
     */
    @AliasFor(annotation = EnableAutoConfiguration.class, attribute = "excludeName")
    String[] excludeName() default {};

    /**
     * Base packages to scan for annotated components. Use {@link #scanBasePackageClasses}
     * for a type-safe alternative to String-based package names.
     * @return base packages to scan
     * @since 1.3.0
     */
    @AliasFor(annotation = ComponentScan.class, attribute = "basePackages")
    String[] scanBasePackages() default {};

    /**
     * Type-safe alternative to {@link #scanBasePackages} for specifying the packages to
     * scan for annotated components. The package of each class specified will be scanned.
     * <p>
     * Consider creating a special no-op marker class or interface in each package that
     * serves no purpose other than being referenced by this attribute.
     * @return base packages to scan
     * @since 1.3.0
     */
    @AliasFor(annotation = ComponentScan.class, attribute = "basePackageClasses")
    Class<?>[] scanBasePackageClasses() default {};

}

在這個註解類上有3個註解,以下:

@SpringBootConfiguration
@EnableAutoConfiguration
@ComponentScan(excludeFilters = {
        @Filter(type = FilterType.CUSTOM, classes = TypeExcludeFilter.class),
        @Filter(type = FilterType.CUSTOM, classes = AutoConfigurationExcludeFilter.class) })

所以,咱們能夠用這三個註解代替 SpringBootApplication,如:

@SpringBootConfiguration
@EnableAutoConfiguration
@ComponentScan
public class Application {

    public static void main(String[] args) {
        SpringApplication.run(Application.class, args);
    }
}

其中,SpringBootConfiguration 表示 Spring Boot 的配置註解,EnableAutoConfiguration 表示自動配置,ComponentScan 表示 Spring Boot 掃描 Bean 的規則,好比掃描哪些包。

@Configuration
加入了這個註解的類被認爲是 Spring Boot 的配置類,咱們知道能夠在 application.yml 設置一些配置,也能夠經過代碼設置配置。

若是咱們要經過代碼設置配置,就必須在這個類上標註 Configuration 註解。以下代碼:

@Configuration
public class WebConfig extends WebMvcConfigurationSupport{

    @Override
    protected void addInterceptors(InterceptorRegistry registry) {
        super.addInterceptors(registry);
        registry.addInterceptor(new ApiInterceptor());
    }
}

不過 Spring Boot 官方推薦 Spring Boot 項目用 SpringBootConfiguration 來代替 Configuration。

@Bean

這個註解是方法級別上的註解,主要添加在 @Configuration 或 @SpringBootConfiguration 註解的類,有時也能夠添加在 @Component 註解的類。它的做用是定義一個Bean。

請看下面代碼:

@Bean
    public ApiInterceptor interceptor(){
        return new ApiInterceptor();
    }

那麼,咱們能夠在 ApiInterceptor 裏面注入其餘 Bean,也能夠在其餘 Bean 注入這個類。

@Value

一般狀況下,咱們須要定義一些全局變量,都會想到的方法是定義一個 public static 變量,在須要時調用,是否有其餘更好的方案呢?答案是確定的。下面請看代碼:

@Value("${server.port}")
    String port;
    @RequestMapping("/hello")
    public String home(String name) {
        return "hi "+name+",i am from port:" +port;
    }

其中,server.port 就是咱們在 application.yml 裏面定義的屬性,咱們能夠自定義任意屬性名,經過 @Value 註解就能夠將其取出來。

它的好處不言而喻:

1.定義在配置文件裏,變量發生變化,無需修改代碼。
2.變量交給Spring來管理,性能更好。

注: 本課程默認針對於對 SpringMVC 有所瞭解的讀者,Spring Boot 自己基於 Spring 開發的,所以,本文再也不講解其餘 Spring 的註解。

注入任何類

本節經過一個實際的例子來說解如何注入一個普通類,而且說明這樣作的好處。

假設一個需求是這樣的:項目要求使用阿里雲的 OSS 進行文件上傳。

咱們知道,一個項目通常會分爲開發環境、測試環境和生產環境。OSS 文件上傳通常有以下幾個參數:appKey、appSecret、bucket、endpoint 等。不一樣環境的參數均可能不同,這樣便於區分。按照傳統的作法,咱們在代碼裏設置這些參數,這樣作的話,每次發佈不一樣的環境包都須要手動修改代碼。

這個時候,咱們就能夠考慮將這些參數定義到配置文件裏面,經過前面提到的 @Value 註解取出來,再經過 @Bean 將其定義爲一個 Bean,這時咱們只須要在須要使用的地方注入該 Bean 便可。

首先在 application.yml 加入以下內容:

appKey: 1
appSecret: 1
bucket: lynn
endPoint: https://www.aliyun.com

其次建立一個普通類:

public class Aliyun {

    private String appKey;

    private String appSecret;

    private String bucket;

    private String endPoint;

    public static class Builder{

        private String appKey;

        private String appSecret;

        private String bucket;

        private String endPoint;

        public Builder setAppKey(String appKey){
            this.appKey = appKey;
            return this;
        }

        public Builder setAppSecret(String appSecret){
            this.appSecret = appSecret;
            return this;
        }

        public Builder setBucket(String bucket){
            this.bucket = bucket;
            return this;
        }

        public Builder setEndPoint(String endPoint){
            this.endPoint = endPoint;
            return this;
        }

        public Aliyun build(){
            return new Aliyun(this);
        }
    }

    public static Builder options(){
        return new Aliyun.Builder();
    }

    private Aliyun(Builder builder){
        this.appKey = builder.appKey;
        this.appSecret = builder.appSecret;
        this.bucket = builder.bucket;
        this.endPoint = builder.endPoint;
    }

    public String getAppKey() {
        return appKey;
    }

    public String getAppSecret() {
        return appSecret;
    }

    public String getBucket() {
        return bucket;
    }

    public String getEndPoint() {
        return endPoint;
    }
}

而後在 @SpringBootConfiguration 註解的類添加以下代碼:

@Value("${appKey}")
    private String appKey;
    @Value("${appSecret}")
    private String appSecret;
    @Value("${bucket}")
    private String bucket;
    @Value("${endPoint}")
    private String endPoint;

    @Bean
    public Aliyun aliyun(){
        return Aliyun.options()
                .setAppKey(appKey)
                .setAppSecret(appSecret)
                .setBucket(bucket)
                .setEndPoint(endPoint)
                .build();
    }

最後在須要的地方注入這個 Bean 便可:

@Autowired
    private Aliyun aliyun;

攔截器

咱們在提供 API 的時候,常常須要對 API 進行統一的攔截,好比進行接口的安全性校驗。

本節,我會講解 Spring Boot 是如何進行攔截器設置的,請看接下來的代碼。

建立一個攔截器類:ApiInterceptor,並實現 HandlerInterceptor 接口:

public class ApiInterceptor implements HandlerInterceptor {
    //請求以前
    @Override
    public boolean preHandle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o) throws Exception {
        System.out.println("進入攔截器");
        return true;
    }
    //請求時
    @Override
    public void postHandle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o, ModelAndView modelAndView) throws Exception {

    }
    //請求完成
    @Override
    public void afterCompletion(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o, Exception e) throws Exception {

    }
}

@SpringBootConfiguration 註解的類繼承 WebMvcConfigurationSupport 類,並重寫 addInterceptors 方法,將 ApiInterceptor 攔截器類添加進去,代碼以下:

@SpringBootConfiguration
public class WebConfig extends WebMvcConfigurationSupport{

    @Override
    protected void addInterceptors(InterceptorRegistry registry) {
        super.addInterceptors(registry);
        registry.addInterceptor(new ApiInterceptor());
    }
}

異常處理

咱們在 Controller 裏提供接口,一般須要捕捉異常,並進行友好提示,不然一旦出錯,界面上就會顯示報錯信息,給用戶一種很差的體驗。最簡單的作法就是每一個方法都使用 try catch 進行捕捉,報錯後,則在 catch 裏面設置友好的報錯提示。若是方法不少,每一個都須要 try catch,代碼會顯得臃腫,寫起來也比較麻煩。

咱們可不能夠提供一個公共的入口進行統一的異常處理呢?固然能夠。方法不少,這裏咱們經過 Spring 的 AOP 特性就能夠很方便的實現異常的統一處理。

@Aspect
@Component
public class WebExceptionAspect {

    private static final Logger logger = LoggerFactory.getLogger(WebExceptionAspect.class);

//凡是註解了RequestMapping的方法都被攔截   @Pointcut("@annotation(org.springframework.web.bind.annotation.RequestMapping)")
    private void webPointcut() {
    }

    /**
     * 攔截web層異常,記錄異常日誌,並返回友好信息到前端 目前只攔截Exception,是否要攔截Error需再作考慮
     *
     * @param e
     *            異常對象
     */
    @AfterThrowing(pointcut = "webPointcut()", throwing = "e")
    public void handleThrowing(Exception e) {
        e.printStackTrace();
        logger.error("發現異常!" + e.getMessage());
        logger.error(JSON.toJSONString(e.getStackTrace()));
        //這裏輸入友好性信息
        writeContent("出現異常");
    }

    /**
     * 將內容輸出到瀏覽器
     *
     * @param content
     *            輸出內容
     */
    private void writeContent(String content) {
        HttpServletResponse response = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes())
                .getResponse();
        response.reset();
        response.setCharacterEncoding("UTF-8");
        response.setHeader("Content-Type", "text/plain;charset=UTF-8");
        response.setHeader("icop-content-type", "exception");
        PrintWriter writer = null;
        try {
            writer = response.getWriter();
        } catch (IOException e) {
            e.printStackTrace();
        }
        writer.print(content);
        writer.flush();
        writer.close();
    }
}

這樣,咱們無需每一個方法都添加 try catch,一旦報錯,則會執行 handleThrowing 方法。

優雅的輸入合法性校驗

爲了接口的健壯性,咱們一般除了客戶端進行輸入合法性校驗外,在 Controller 的方法裏,咱們也須要對參數進行合法性校驗,傳統的作法是每一個方法的參數都作一遍判斷,這種方式和上一節講的異常處理一個道理,不太優雅,也不易維護。

其實,SpringMVC 提供了驗證接口,下面請看代碼:

@GetMapping("authorize")//GetMapping是RequestMapping(method=method.GET)的組合
public void authorize(@Valid AuthorizeIn authorize, BindingResult ret){
    if(result.hasFieldErrors()){
            List<FieldError> errorList = result.getFieldErrors();
            //經過斷言拋出參數不合法的異常
            errorList.stream().forEach(item -> Assert.isTrue(false,item.getDefaultMessage()));
        }
}
public class AuthorizeIn extends BaseModel{

    @NotBlank(message = "缺乏response_type參數")
    private String responseType;
    @NotBlank(message = "缺乏client_id參數")
    private String ClientId;

    private String state;

    @NotBlank(message = "缺乏redirect_uri參數")
    private String redirectUri;

    public String getResponseType() {
        return responseType;
    }

    public void setResponseType(String responseType) {
        this.responseType = responseType;
    }

    public String getClientId() {
        return ClientId;
    }

    public void setClientId(String clientId) {
        ClientId = clientId;
    }

    public String getState() {
        return state;
    }

    public void setState(String state) {
        this.state = state;
    }

    public String getRedirectUri() {
        return redirectUri;
    }

    public void setRedirectUri(String redirectUri) {
        this.redirectUri = redirectUri;
    }
}

在 controller 的方法須要校驗的參數後面必須跟 BindingResult,不然沒法進行校驗。可是這樣會拋出異常,對用戶而言不太友好!

那怎麼辦呢?

很簡單,咱們能夠利用上一節講的異常處理,對報錯進行攔截:

@Component
@Aspect
public class WebExceptionAspect implements ThrowsAdvice{

    public static final Logger logger = LoggerFactory.getLogger(WebExceptionAspect.class);

//攔截被GetMapping註解的方法    @Pointcut("@annotation(org.springframework.web.bind.annotation.GetMapping)")
    private void webPointcut() {
    }

    @AfterThrowing(pointcut = "webPointcut()",throwing = "e")
    public void afterThrowing(Exception e) throws Throwable {
        logger.debug("exception 來了!");
        if(StringUtils.isNotBlank(e.getMessage())){
                           writeContent(e.getMessage());
        }else{
            writeContent("參數錯誤!");
        }

    }

    /**
     * 將內容輸出到瀏覽器
     *
     * @param content
     *            輸出內容
     */
    private void writeContent(String content) {
        HttpServletResponse response = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes())
                .getResponse();
        response.reset();
        response.setCharacterEncoding("UTF-8");
        response.setHeader("Content-Type", "text/plain;charset=UTF-8");
        response.setHeader("icop-content-type", "exception");
        PrintWriter writer = null;
        try {
            writer = response.getWriter();

            writer.print((content == null) ? "" : content);
            writer.flush();
            writer.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

這樣當咱們傳入不合法的參數時就會進入 WebExceptionAspect 類,從而輸出友好參數。

咱們再把驗證的代碼單獨封裝成方法:

protected void validate(BindingResult result){
        if(result.hasFieldErrors()){
            List<FieldError> errorList = result.getFieldErrors();
            errorList.stream().forEach(item -> Assert.isTrue(false,item.getDefaultMessage()));
        }
    }

這樣每次參數校驗只須要調用 validate 方法就好了,咱們能夠看到代碼的可讀性也大大的提升了。

接口版本控制

一個系統上線後會不斷迭代更新,需求也會不斷變化,有可能接口的參數也會發生變化,若是在原有的參數上直接修改,可能會影響線上系統的正常運行,這時咱們就須要設置不一樣的版本,這樣即便參數發生變化,因爲老版本沒有變化,所以不會影響上線系統的運行。

通常咱們能夠在地址上帶上版本號,也能夠在參數上帶上版本號,還能夠再 header 裏帶上版本號,這裏咱們在地址上帶上版本號,大體的地址如:http://api.example.com/v1/test,其中,v1 即表明的是版本號。具體作法請看代碼:

@Target({ElementType.METHOD,ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Mapping
public @interface ApiVersion {

    /**
     * 標識版本號
     * @return
     */
    int value();
}
public class ApiVersionCondition implements RequestCondition<ApiVersionCondition> {

    // 路徑中版本的前綴, 這裏用 /v[1-9]/的形式
    private final static Pattern VERSION_PREFIX_PATTERN = Pattern.compile("v(\\d+)/");

    private int apiVersion;

    public ApiVersionCondition(int apiVersion){
        this.apiVersion = apiVersion;
    }

    @Override
    public ApiVersionCondition combine(ApiVersionCondition other) {
        // 採用最後定義優先原則,則方法上的定義覆蓋類上面的定義
        return new ApiVersionCondition(other.getApiVersion());
    }

    @Override
    public ApiVersionCondition getMatchingCondition(HttpServletRequest request) {
        Matcher m = VERSION_PREFIX_PATTERN.matcher(request.getRequestURI());
        if(m.find()){
            Integer version = Integer.valueOf(m.group(1));
            if(version >= this.apiVersion)
            {
                return this;
            }
        }
        return null;
    }

    @Override
    public int compareTo(ApiVersionCondition other, HttpServletRequest request) {
        // 優先匹配最新的版本號
        return other.getApiVersion() - this.apiVersion;
    }

    public int getApiVersion() {
        return apiVersion;
    }
}
public class CustomRequestMappingHandlerMapping extends
        RequestMappingHandlerMapping {

    @Override
    protected RequestCondition<ApiVersionCondition> getCustomTypeCondition(Class<?> handlerType) {
        ApiVersion apiVersion = AnnotationUtils.findAnnotation(handlerType, ApiVersion.class);
        return createCondition(apiVersion);
    }

    @Override
    protected RequestCondition<ApiVersionCondition> getCustomMethodCondition(Method method) {
        ApiVersion apiVersion = AnnotationUtils.findAnnotation(method, ApiVersion.class);
        return createCondition(apiVersion);
    }

    private RequestCondition<ApiVersionCondition> createCondition(ApiVersion apiVersion) {
        return apiVersion == null ? null : new ApiVersionCondition(apiVersion.value());
    }
}
@SpringBootConfiguration
public class WebConfig extends WebMvcConfigurationSupport {

    @Bean
    public AuthInterceptor interceptor(){
        return new AuthInterceptor();
    }

    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(new AuthInterceptor());
    }

    @Override
    @Bean
    public RequestMappingHandlerMapping requestMappingHandlerMapping() {
        RequestMappingHandlerMapping handlerMapping = new CustomRequestMappingHandlerMapping();
        handlerMapping.setOrder(0);
        handlerMapping.setInterceptors(getInterceptors());
        return handlerMapping;
    }
}

Controller 類的接口定義以下:

@ApiVersion(1)
@RequestMapping("{version}/dd")
public class HelloController{}

這樣咱們就實現了版本控制,若是增長了一個版本,則建立一個新的 Controller,方法名一致,ApiVersion 設置爲2,則地址中 v1 會找到 ApiVersion 爲1的方法,v2 會找到 ApiVersion 爲2的方法。

自定義 JSON 解析

Spring Boot 中 RestController 返回的字符串默認使用 Jackson 引擎,它也提供了工廠類,咱們能夠自定義 JSON 引擎,本節實例咱們將 JSON 引擎替換爲 fastJSON,首先須要引入 fastJSON:

<dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>fastjson</artifactId>
            <version>${fastjson.version}</version>
        </dependency>

其次,在 WebConfig 類重寫 configureMessageConverters 方法:

@Override
    public void configureMessageConverters(List<HttpMessageConverter<?>> converters) {
        super.configureMessageConverters(converters);
        /*
        1.須要先定義一個convert轉換消息的對象;
        2.添加fastjson的配置信息,好比是否要格式化返回的json數據
        3.在convert中添加配置信息
        4.將convert添加到converters中
         */
        //1.定義一個convert轉換消息對象
        FastJsonHttpMessageConverter fastConverter=new FastJsonHttpMessageConverter();
        //2.添加fastjson的配置信息,好比:是否要格式化返回json數據
        FastJsonConfig fastJsonConfig=new FastJsonConfig();
        fastJsonConfig.setSerializerFeatures(
                SerializerFeature.PrettyFormat
        );
        fastConverter.setFastJsonConfig(fastJsonConfig);
        converters.add(fastConverter);
    }

單元測試

Spring Boot 的單元測試很簡單,直接看代碼:

@SpringBootTest(classes = Application.class)
@RunWith(SpringJUnit4ClassRunner.class)
public class TestDB {

    @Test
    public void test(){
    }
}

模板引擎

在傳統的 SpringMVC 架構中,咱們通常將 JSP、HTML 頁面放到 webapps 目錄下面,可是 Spring Boot 沒有 webapps,更沒有 web.xml,若是咱們要寫界面的話,該如何作呢?

Spring Boot 官方提供了幾種模板引擎:FreeMarker、Velocity、Thymeleaf、Groovy、mustache、JSP。

這裏以 FreeMarker 爲例講解 Spring Boot 的使用。

首先引入 FreeMarker 依賴:

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

在 resources 下面創建兩個目錄:static 和 templates,如圖所示:

圖片描述

其中 static 目錄用於存放靜態資源,譬如:CSS、JS、HTML 等,templates 目錄存放模板引擎文件,咱們能夠在 templates 下面建立一個文件:index.ftl(freemarker 默認後綴爲 .ftl),並添加內容:

<!DOCTYPE html>
<html>
    <head>

    </head>
    <body>
        <h1>Hello World!</h1>
    </body>
</html>

而後建立 PageController 並添加內容:

@Controller
public class PageController {

    @RequestMapping("index.html")
    public String index(){
        return "index";
    }
}

啓動 Application.java,訪問:http://localhost:8080/index.html,就能夠看到如圖所示:

圖片描述

相關文章
相關標籤/搜索