【Spring Security技術棧開發企業級認證與授權】----使用Spring MVC開發RESTful API (二)

前言

分享以下騷操作:使用切片攔截REST服務、使用Filter和Interceptor攔截REST服務、使用REST方式處理文件服務等demo實現,O(∩_∩)O哈哈~


一、使用Filter和Interceptor攔截REST服務

Filter

  • RESTful API 攔截處理時間:
package com.zcw.filter;


import org.springframework.stereotype.Component;

import javax.servlet.*;
import java.io.IOException;
import java.util.Date;

/**
 * @ClassName : TimeFilter
 * @Description :
 * @Author : Zhaocunwei
 * @Date: 2020-06-17 13:17
 */
@Component
public class TimeFilter implements Filter {
    @Override
    public void init(FilterConfig filterConfig) throws ServletException {
        System.out.println("time filter init");
    }

    @Override
    public void doFilter(ServletRequest servletRequest,
                         ServletResponse servletResponse,
                         FilterChain filterChain) throws IOException, ServletException {
        System.out.println("time filter start");
        long start =new Date().getTime();
        filterChain.doFilter(servletRequest,servletResponse);
        System.out.println("time filter 耗時:"+(new Date().getTime()-start));
        System.out.println("time filter finish");
    }

    @Override
    public void destroy() {
        System.out.println("time filter destroy");
    }
}


在這裏插入圖片描述

  • 測試:
    在這裏插入圖片描述
    在這裏插入圖片描述
  • 模擬調用第三方過濾器,添加到我們項目中的過濾器鏈上
    在這裏插入圖片描述
package com.zcw.config;

import com.zcw.filter.TimeFilter;
import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import java.util.ArrayList;
import java.util.List;

/**
 * @ClassName : WebConfig
 * @Description :配置類
 * @Author : Zhaocunwei
 * @Date: 2020-06-17 13:35
 */
@Configuration
public class WebConfig {
    //注入進來一個filter的bean
    @Bean
    public FilterRegistrationBean timeFilter(){
        FilterRegistrationBean registrationBean = new FilterRegistrationBean();
        TimeFilter timeFilter = new TimeFilter();
        registrationBean.setFilter(timeFilter);
        //配置filter在哪些url下面起作用
        List<String> urls = new ArrayList<>();
        urls.add("/*");
        registrationBean.setUrlPatterns(urls);
        return registrationBean;
    }
}


  • 測試:
    在這裏插入圖片描述
    在這裏插入圖片描述
    通過上面的演示,如果細心的小夥伴,會發現,上面的演示只能拿到HTTP請求與響應

Interceptor – 攔截器

package com.zcw.interceptor;

import org.springframework.stereotype.Component;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.Date;

/**
 * @ClassName : TimeInterceptor
 * @Description :
 * @Author : Zhaocunwei
 * @Date: 2020-06-17 13:47
 */
@Component
public class TimeInterceptor implements HandlerInterceptor {
    @Override
    public boolean preHandle(HttpServletRequest httpServletRequest,
                             HttpServletResponse httpServletResponse, Object o)
            throws Exception {
        System.out.println("preHandle");
        System.out.println(((HandlerMethod)o).getBean()
                            .getClass()
                            .getName());
        System.out.println(((HandlerMethod)o).getMethod().getName());
        httpServletRequest.setAttribute("startTime",new Date().getTime());
        return false;
    }

    @Override
    public void postHandle(HttpServletRequest httpServletRequest,
                           HttpServletResponse httpServletResponse,
                           Object o, ModelAndView modelAndView) throws Exception {
        System.out.println("postHandle");
        long start=(long) httpServletRequest.getAttribute("startTime");
        System.out.println("time interceptor 耗時:"+(new Date().getTime()-start));

    }

    @Override
    public void afterCompletion(HttpServletRequest httpServletRequest,
                                HttpServletResponse httpServletResponse,
                                Object o, Exception e) throws Exception {
        System.out.println("afterCompletion");
        long start = (long)httpServletRequest.getAttribute("startTime");
        System.out.println("time interceptor 耗時:"+(new Date().getTime()-start));
        System.out.println("Exception==="+e);
    }
}


在這裏插入圖片描述

  • 測試:
    在這裏插入圖片描述
    在這裏插入圖片描述

二、使用切片攔截REST服務

在這裏插入圖片描述


package com.zcw.aspect;

import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;

import java.util.Date;

/**
 * @ClassName : TimeAspect
 * @Description :
 * @Author : Zhaocunwei
 * @Date: 2020-06-17 14:43
 */
@Aspect
public class TimeAspect {

    @Around("execution(* com.zcw.web.controller.UserController.*(..))")
    public Object handleControllerMethod(ProceedingJoinPoint proceedingJoinPoint)
            throws Throwable {

        System.out.println("time aspect start");
        long start = new Date().getTime();
        Object[] args = proceedingJoinPoint.getArgs();
        for(Object arg : args){
            System.out.println("arg is"+arg);
        }
        Object proceed = proceedingJoinPoint.proceed();
        System.out.println("time aspect end"+(new Date().getTime() -start));
        return null;
    }
}

  • 測試:
    在這裏插入圖片描述
    在這裏插入圖片描述

三、使用REST方式處理文件服務

文件的上傳和下載

  • Test
 @Test
   public void whenUploadSuccess() throws Exception {
        mockMvc.perform(MockMvcRequestBuilders.fileUpload("/file")
                .file(new MockMultipartFile(
                        "file",
                        "test.txt",
                        "multiparrt/form-data",
                        "hello upload".getBytes("UTF-8"))))
                .andExpect(MockMvcResultMatchers.status().isOk());
   }

  • 服務

package com.zcw.dto;

import lombok.Data;

/**
 * @ClassName : FileInfo
 * @Description :
 * @Author : Zhaocunwei
 * @Date: 2020-06-17 15:33
 */

public class FileInfo {
    public FileInfo(String path){
        this.path=path;
    }
    private String path;

    public String getPath() {
        return path;
    }

    public void setPath(String path) {
        this.path = path;
    }
}

在這裏插入圖片描述


package com.zcw.web.controller;

import com.zcw.dto.FileInfo;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.multipart.MultipartFile;

import java.io.File;
import java.io.IOException;
import java.util.Date;

/**
 * @ClassName : FileController
 * @Description :
 * @Author : Zhaocunwei
 * @Date: 2020-06-17 15:32
 */
@RestController
@RequestMapping("/file")
public class FileController {
    @PostMapping
    public FileInfo upload(MultipartFile file) throws IOException {
        System.out.println(file.getName());
        System.out.println(file.getOriginalFilename());
        System.out.println(file.getSize());
        String folder="F:\\";
        File localFile = new File(folder,new Date().getTime()+".txt");
        file.transferTo(localFile);
        return new FileInfo(localFile.getAbsolutePath());
    }
}

  • 測試:
    在這裏插入圖片描述
    在這裏插入圖片描述
    在這裏插入圖片描述
  • 業務類
@GetMapping("/{id}")
    public void download(@PathVariable String id ,HttpServletRequest request,
                         HttpServletResponse response) throws IOException {
        //jdk 1.7特性,jdk幫我們關閉流
        try (
                InputStream inputStream = new FileInputStream(new File(folder,id+".txt"));
                OutputStream outputStream = response.getOutputStream();
        ){
            response.setContentType("application/x-download");
            response.addHeader("Content-Disposition","attachment;filename=test.txt");
            IOUtils.copy(inputStream,outputStream);
            outputStream.flush();
        }
    }

  • 測試:
  • 在這裏插入圖片描述
    在這裏插入圖片描述

四、使用多線程提高REST服務性能

在這裏插入圖片描述

package com.zcw.web.async;

import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import java.util.concurrent.Callable;

/**
 * @ClassName : AsyncController
 * @Description :
 * @Author : Zhaocunwei
 * @Date: 2020-06-18 10:47
 */
@RestController
@Slf4j
public class AsyncController {
    @RequestMapping("/order")
    public Callable<String> order() throws Exception {
        log.info("主線程開始");

        Callable<String> result =new Callable<String>(){
            @Override
            public String call()throws Exception{
                log.info("副線程開始");
                Thread.sleep(1000);
                log.info("副線程返回");
                return "success";
            }
        };
        return result;
    }
}


  • 測試
  • 在這裏插入圖片描述
    在這裏插入圖片描述
  • 異步處理REST服務
    在這裏插入圖片描述
  • 使用Runnable異步處理Rest服務
  • 使用DeferredResult異步處理Rest服務
  • 異步處理配置
package com.zcw.web.async;

import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;

/**
 * @ClassName : MockQueue
 * @Description :
 * @Author : Zhaocunwei
 * @Date: 2020-06-18 12:53
 */
@Component
@Slf4j
public class MockQueue {
    private String placeOrder;
    private String completeOrder;

    public String getPlaceOrder() {
        return placeOrder;
    }

    public void setPlaceOrder(String placeOrder) throws InterruptedException {
        new Thread(()->{
            log.info("接到下單請求");
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            this.completeOrder = placeOrder;
            log.info("下單請求處理完畢,"+completeOrder);
        }).start();

    }

    public String getCompleteOrder() {
        return completeOrder;
    }

    public void setCompleteOrder(String completeOrder) {
        this.completeOrder = completeOrder;
    }
}



package com.zcw.web.async;

import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang.RandomStringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.context.request.async.DeferredResult;

import java.util.concurrent.Callable;

/**
 * @ClassName : AsyncController
 * @Description :
 * @Author : Zhaocunwei
 * @Date: 2020-06-18 10:47
 */
@RestController
@Slf4j
public class AsyncController {
    @Autowired
    private MockQueue mockQueue;
    @Autowired
    private DeferredResultHolder deferredResultHolder;

    @RequestMapping("/order")
    public DeferredResult<String> order() throws Exception {
        log.info("主線程開始");
        String orderNumber = RandomStringUtils.randomNumeric(8);
        mockQueue.setPlaceOrder(orderNumber);

        DeferredResult<String> result = new DeferredResult<>();
        deferredResultHolder.getMap().put(orderNumber,result);

//        Callable<String> result =new Callable<String>(){
//            @Override
//            public String call()throws Exception{
//                log.info("副線程開始");
//                Thread.sleep(1000);
//                log.info("副線程返回");
//                return "success";
//            }
//        };
        return result;
    }
}


package com.zcw.web.async;

import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationListener;
import org.springframework.context.event.ContextRefreshedEvent;
import org.springframework.stereotype.Component;

/**
 * @ClassName : QueueListener
 * @Description : 監聽器
 * @Author : Zhaocunwei
 * @Date: 2020-06-18 13:05
 */
@Component
@Slf4j
public class QueueListener implements ApplicationListener<ContextRefreshedEvent> {
    @Autowired
    private MockQueue mockQueue;
    @Autowired
    private DeferredResultHolder deferredResultHolder;
    @Override
    public void onApplicationEvent(ContextRefreshedEvent event) {
     //ContextRefreshedEvent 整個spring容器初始化完畢的事件
        new Thread(() ->{
            while(true){
                if(StringUtils.isNotBlank(mockQueue.getCompleteOrder())){
                    String orderNumber = mockQueue.getCompleteOrder();
                    log.info("返回訂單處理結果:"+orderNumber);
                    deferredResultHolder.getMap().get(orderNumber).setResult("place order success");
                    mockQueue.setCompleteOrder(null);
                }else{
                    try {
                        Thread.sleep(100);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }
        }).start();

    }
}



package com.zcw.web.async;

import org.springframework.stereotype.Component;
import org.springframework.web.context.request.async.DeferredResult;

import java.util.HashMap;
import java.util.Map;

/**
 * @ClassName : DeferredResultHolder
 * @Description :
 * @Author : Zhaocunwei
 * @Date: 2020-06-18 12:57
 */
@Component
public class DeferredResultHolder {
    private Map<String, DeferredResult<String>> map = new HashMap<String,DeferredResult<String>>();

    public Map<String, DeferredResult<String>> getMap() {
        return map;
    }

    public void setMap(Map<String, DeferredResult<String>> map) {
        this.map = map;
    }
}


  • 測試:
    在這裏插入圖片描述
    在這裏插入圖片描述

五、使用Swagger自動生成文檔

        <dependency>
            <groupId>io.springfox</groupId>
            <artifactId>springfox-swagger2</artifactId>
            <version>2.7.0</version>
        </dependency>
        <dependency>
            <groupId>io.springfox</groupId>
            <artifactId>springfox-swagger-ui</artifactId>
            <version>2.7.0</version>
        </dependency>

六、使用WireMock僞造REST服務

在這裏插入圖片描述
下載地址:
http://wiremock.org/docs/running-standalone/
在這裏插入圖片描述
在這裏插入圖片描述

  • 啓動
    在這裏插入圖片描述
    在這裏插入圖片描述
  • 修改我們項目中pom文件:
<dependency>
            <groupId>com.github.tomakehurst</groupId>
            <artifactId>wiremock</artifactId>
        </dependency>

  • demo
package com.zcw.wiremock;

import com.github.tomakehurst.wiremock.client.WireMock;

/**
 * @ClassName : MockServer
 * @Description :
 * @Author : Zhaocunwei
 * @Date: 2020-06-18 13:52
 */
public class MockServer {
    public static void main(String[] args) {
        WireMock.configureFor(8080);
        WireMock.removeAllMappings();
        WireMock.stubFor(WireMock.get(WireMock.urlPathEqualTo("/order/1"))
        .willReturn(WireMock.aResponse().withBody("{\"id\":1}").withStatus(200)));
    }
}


  • 把我們的項目啓動,然後運行main方法:
    在這裏插入圖片描述
  • 運行main方法時報錯,我把main方法改成如下:
    在這裏插入圖片描述
Exception in thread "main" java.lang.NoClassDefFoundError: org/apache/http/client/methods/HttpUriRequest
	at com.github.tomakehurst.wiremock.client.WireMock.<init>(WireMock.java:70)
	at com.github.tomakehurst.wiremock.client.WireMock.configureFor(WireMock.java:122)
	at com.zcw.wiremock.MockServer.main(MockServer.java:13)
Caused by: java.lang.ClassNotFoundException: org.apache.http.client.methods.HttpUriRequest
	at java.net.URLClassLoader.findClass(URLClassLoader.java:381)
	at java.lang.ClassLoader.loadClass(ClassLoader.java:424)
	at sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:335)
	at java.lang.ClassLoader.loadClass(ClassLoader.java:357)
	... 3 more

  • 通過網上解決搜索資料進行解決問題:
    在這裏插入圖片描述
 <dependency>
            <groupId>com.github.tomakehurst</groupId>
            <artifactId>wiremock-jre8-standalone</artifactId>
            <version>2.23.2</version>
            <scope>compile</scope>
        </dependency>

在這裏插入圖片描述
在這裏插入圖片描述
在這裏插入圖片描述

<dependency>
            <groupId>org.apache.httpcomponents</groupId>
            <artifactId>httpclient</artifactId>
        </dependency>

package com.zcw.wiremock;


import static com.github.tomakehurst.wiremock.client.WireMock.*;

/**
 * @ClassName : MockServer
 * @Description :
 * @Author : Zhaocunwei
 * @Date: 2020-06-18 13:52
 */
public class MockServer {
    public static void main(String[] args) {
        // 01 連接配置
        configureFor(8062); // 配置連接信息(PS:這個端口必須和啓動WireMock的端口保持一致)

        // 02 清空發佈信息
        removeAllMappings(); // 清空上一次的發佈信息

        // 03 發佈新信息
        stubFor(
                get(urlPathEqualTo("/wiremock/test")) // 設置請求路徑
                        .willReturn(
                                aResponse() // 設置響應信息
                                        .withBody("{\"id\":12,\"name\":null,\"password\":null}") // 響應數據
                                        .withStatus(200) // 響應狀態碼
                        )
        );

    }
}


在這裏插入圖片描述
在這裏插入圖片描述
在這裏插入圖片描述

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章