SpringBoot系統列 5 - 接口版本控制、SpringBoot FreeMarker模板引擎

 

接着上篇博客的代碼繼續寫html

1.接口版本控制java

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

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

import org.springframework.web.bind.annotation.Mapping;

import java.lang.annotation.*;

/**
 * 版本控制
 * @author XIHONGLEI
 * @date 2018-11-15
 */
@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Mapping
public @interface ApiVersion {
    int value();
}
import org.springframework.web.servlet.mvc.condition.RequestCondition;

import javax.servlet.http.HttpServletRequest;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 * @author XIHONGLEI
 * @date 2018-11-15
 */
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;
    }
}
import org.springframework.core.annotation.AnnotationUtils;
import org.springframework.web.servlet.mvc.condition.RequestCondition;
import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping;

import java.lang.reflect.Method;

/**
 * @author XIHONGLEI
 * @date 2018-11-15
 */
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());
    }
}
#而後在WebConfig配置類中注入Bean

import com.hello.config.CustomRequestMappingHandlerMapping;
import com.hello.filter.ApiInterceptor;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.SpringBootConfiguration;
import org.springframework.context.annotation.Bean;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurationSupport;
import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping;

/**
 * 配置類
 * @author XIHONGLEI
 * @date 2018-10-31
 */
@SpringBootConfiguration
public class WebConfig extends WebMvcConfigurationSupport {

   

    @Override
    protected void addInterceptors(InterceptorRegistry registry) {
        super.addInterceptors(registry);
        // 將 ApiInterceptor 攔截器類添加進去
        registry.addInterceptor(new ApiInterceptor());
    }

   

    @Override
    @Bean
    public RequestMappingHandlerMapping requestMappingHandlerMapping() {
        RequestMappingHandlerMapping handlerMapping = new CustomRequestMappingHandlerMapping();
        handlerMapping.setOrder(0);
        handlerMapping.setInterceptors(getInterceptors());
        return handlerMapping;
    }
}
#最後定義一個帶版本控制的接口
import com.hello.WebConfig;
import com.hello.config.ApiVersion;
import com.hello.entity.ContractDetailDto;
import com.hello.service.CheckPositionService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.RestController;

import java.util.List;

@RestController

public class HelloController {

    @Autowired
    private WebConfig webConfig;

    @ApiVersion(1)
    @RequestMapping("{version}/getName")
    public String vGetName() {
        return "Hello World! version 1";
    }
    @ApiVersion(2)
    @RequestMapping("{version}/getName")
    public String getName() {
        return "Hello World! version 2";
    }
}

查看效果:api

 

2.模板引擎架構

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

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

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

首先引入 FreeMarker 依賴:ide

        <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 lang="en">
<head>
    <meta charset="UTF-8">
    <title>Hello ${name}!</title>
</head>
<body>
        Hello ${name}!
</body>
</html>

而後在POM中配置Resource的時候必定要把全部的資源文件都包括:

<resources>
            <resource>
                <directory>src/main/java</directory>
                <includes>
                    <include>**/*.yml</include>
                    <include>**/*.properties</include>
                    <include>**/*.xml</include>
                </includes>
                <filtering>false</filtering>
            </resource>
            <resource>
                <directory>src/main/resources</directory>
                <includes>
                    <include>**/*</include>
                </includes>
                <filtering>false</filtering>
            </resource>
        </resources>

新建Contrlller:

import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.servlet.ModelAndView;

@Controller
public class HomeController {
    @RequestMapping(value = "/index")
    public ModelAndView index() {
        ModelAndView view = new ModelAndView("/index");
        view.addObject("name","Tom");
        return view;
    }
}

看結果:

相關文章
相關標籤/搜索