久久精品精选,精品九九视频,www久久只有这里有精品,亚洲熟女乱色综合一区
    分享

    猿學-springboot 實現(xiàn)攔截的五種姿勢

     AnonymousV臉 2019-04-06

    簡介AOP(面向切面編程)常用于解決系統(tǒng)中的一些耦合問題,是一種編程的模式

    通過將一些通用邏輯抽取為公共模塊,由容器來進行調(diào)用,以達到模塊間隔離的效果。

    其還有一個別名,叫面向關(guān)注點編程,把系統(tǒng)中的核心業(yè)務(wù)邏輯稱為核心關(guān)注點,而一些通用的非核心邏輯劃分為橫切關(guān)注點OP常用于...日志記錄你需要為你的Web應(yīng)用程序?qū)崿F(xiàn)訪問日志記錄,卻又不想在所有接口中一個個進行打點。



    安全控制為URL 實現(xiàn)訪問權(quán)限控制,自動攔截一些非法訪問。

    事務(wù)某些業(yè)務(wù)流程需要在一個事務(wù)中串行

    異常處理系統(tǒng)發(fā)生處理異常,根據(jù)不同的異常返回定制的消息體。

    在筆者剛開始接觸編程之時,AOP還是個新事物,當時曾認為AOP會大行其道。

    果不其然,目前流行的Spring 框架中,AOP已經(jīng)成為其關(guān)鍵的核心能力。

    先看看下面的一個Controller方法:

    示例@RestController

    @RequestMapping('/intercept')

    public class InterceptController {

    @PostMapping(value = '/body', consumes = { MediaType.TEXT_PLAIN_VALUE, MediaType.APPLICATION_JSON_UTF8_VALUE })

    public String body(@RequestBody MsgBody msg) {

    return msg == null ? '<EMPTY>' : msg.getContent();

    }

    public static class MsgBody {

    private String content;

    public String getContent() {

    return content;

    }

    public void setContent(String content) {

    this.content = content;

    }

    }

    在上述代碼的 body 方法中,會接受一個MsgBody請求消息體,最終簡單的輸出content字段。

    下面,我們將介紹如何為這個方法實現(xiàn)攔截動作。算起來,共有五種姿勢。

    姿勢一、使用 Filter 接口Filter 接口由 J2EE 定義,在Servlet執(zhí)行之前由容器進行調(diào)用。

    而SpringBoot中聲明 Filter 又有兩種方式:

    1. 注冊 FilterRegistrationBean聲明一個FilterRegistrationBean 實例,對Filter 做一系列定義,如下:

    @Bean

    public FilterRegistrationBean customerFilter() {

    FilterRegistrationBean registration = new FilterRegistrationBean();

    // 設(shè)置過濾器

    registration.setFilter(new CustomerFilter());

    // 攔截路由規(guī)則

    registration.addUrlPatterns('/intercept/*');

    // 設(shè)置初始化參數(shù)

    registration.addInitParameter('name', 'customFilter');

    registration.setName('CustomerFilter');

    registration.setOrder(1);

    return registration;

    }

    其中 CustomerFilter 實現(xiàn)了Filter接口,如下:

    public class CustomerFilter implements Filter {

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

    private String name;

    @Override

    public void init(FilterConfig filterConfig) throws ServletException {

    name = filterConfig.getInitParameter('name');

    }

    @Override

    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)

    throws IOException, ServletException {

    logger.info('Filter {} handle before', name);

    chain.doFilter(request, response);

    logger.info('Filter {} handle after', name);

    }

    }

    2. @WebFilter 注解為Filter的實現(xiàn)類添加 @WebFilter注解,由SpringBoot 框架掃描后注入

    @WebFilter的啟用需要配合@ServletComponentScan才能生效@Component

    @ServletComponentScan

    @WebFilter(urlPatterns = '/intercept/*', filterName = 'annotateFilter')

    public class AnnotateFilter implements Filter {

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

    private final String name = 'annotateFilter';

    @Override

    public void init(FilterConfig filterConfig) throws ServletException {

    }

    @Override

    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)

    throws IOException, ServletException {

    logger.info('Filter {} handle before', name);

    chain.doFilter(request, response);

    logger.info('Filter {} handle after', name);

    }

    }

    使用注解是最簡單的,但其缺點是仍然無法支持 order屬性(用于控制Filter的排序)。

    而通常的@Order注解只能用于定義Bean的加載順序,卻真正無法控制Filter排序。

    這是一個已知問題,參考這里

    推薦指數(shù)3 顆星,F(xiàn)ilter 定義屬于J2EE規(guī)范,由Servlet容器調(diào)度執(zhí)行。

    由于獨立于框架之外,無法使用 Spring 框架的便捷特性,

    目前一些第三方組件集成時會使用該方式。

    姿勢二、HanlderInterceptorHandlerInterceptor 用于攔截 Controller 方法的執(zhí)行,其聲明了幾個方法:

    |方法 | 說明|

    |-----|-----|

    |preHandle | Controller方法執(zhí)行前調(diào)用 |

    |preHandle | Controller方法后,視圖渲染前調(diào)用 |

    |afterCompletion| 整個方法執(zhí)行后(包括異常拋出捕獲) |

    基于 HandlerInterceptor接口 實現(xiàn)的樣例:

    public class CustomHandlerInterceptor implements HandlerInterceptor {

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

    /*

    * Controller方法調(diào)用前,返回true表示繼續(xù)處理

    */

    @Override

    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)

    throws Exception {

    HandlerMethod method = (HandlerMethod) handler;

    logger.info('CustomerHandlerInterceptor preHandle, {}', method.getMethod().getName());

    return true;

    }

    /*

    * Controller方法調(diào)用后,視圖渲染前

    */

    @Override

    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler,

    ModelAndView modelAndView) throws Exception {

    HandlerMethod method = (HandlerMethod) handler;

    logger.info('CustomerHandlerInterceptor postHandle, {}', method.getMethod().getName());

    response.getOutputStream().write('append content'.getBytes());

    }

    /*

    * 整個請求處理完,視圖已渲染。如果存在異常則Exception不為空

    */

    @Override

    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex)

    throws Exception {

    HandlerMethod method = (HandlerMethod) handler;

    logger.info('CustomerHandlerInterceptor afterCompletion, {}', method.getMethod().getName());

    }

    }

    除了上面的代碼實現(xiàn),還不要忘了將 Interceptor 實現(xiàn)進行注冊:

    @Configuration

    public class InterceptConfig extends WebMvcConfigurerAdapter {

    // 注冊攔截器

    @Override

    public void addInterceptors(InterceptorRegistry registry) {

    registry.addInterceptor(new CustomHandlerInterceptor()).addPathPatterns('/intercept/**');

    super.addInterceptors(registry);

    }

    推薦指數(shù)4顆星,HandlerInterceptor 來自SpringMVC框架,基本可代替 Filter 接口使用;

    除了可以方便的進行異常處理之外,通過接口參數(shù)能獲得Controller方法實例,還可以實現(xiàn)更靈活的定制。

    姿勢三、@ExceptionHandler 注解@ExceptionHandler 的用途是捕獲方法執(zhí)行時拋出的異常,

    通常可用于捕獲全局異常,并輸出自定義的結(jié)果。

    如下面的實例:

    @ControllerAdvice(assignableTypes = InterceptController.class)

    public class CustomInterceptAdvice {

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

    /**

    * 攔截異常

    *

    * @param e

    * @param m

    * @return

    */

    @ExceptionHandler(value = { Exception.class })

    @ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)

    @ResponseBody

    public String handle(Exception e, HandlerMethod m) {

    logger.info('CustomInterceptAdvice handle exception {}, method: {}', e.getMessage(), m.getMethod().getName());

    return e.getMessage();

    }

    }

    需要注意的是,@ExceptionHandler 需要與 @ControllerAdvice配合使用

    其中 @ControllerAdvice的 assignableTypes 屬性指定了所攔截類的名稱。

    除此之外,該注解還支持指定包掃描范圍、注解范圍等等。

    推薦指數(shù)5顆星,@ExceptionHandler 使用非常方便,在異常處理的機制上是首選;

    目前也是SpringBoot 框架最為推薦使用的方法。

    姿勢四、RequestBodyAdvice/ResponseBodyAdviceRequestBodyAdvice、ResponseBodyAdvice 相對于讀者可能比較陌生,

    而這倆接口也是 Spring 4.x 才開始出現(xiàn)的。

    RequestBodyAdvice 的用法我們都知道,SpringBoot 中可以利用@RequestBody這樣的注解完成請求內(nèi)容體與對象的轉(zhuǎn)換。

    RequestBodyAdvice 則可用于在請求內(nèi)容對象轉(zhuǎn)換的前后時刻進行攔截處理,其定義了幾個方法:

    方法說明supports判斷是否支持handleEmptyBody當請求體為空時調(diào)用beforeBodyRead在請求體未讀取(轉(zhuǎn)換)時調(diào)用afterBodyRead在請求體完成讀取后調(diào)用

    實現(xiàn)代碼如下:

    @ControllerAdvice(assignableTypes = InterceptController.class)

    public class CustomRequestAdvice extends RequestBodyAdviceAdapter {

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

    @Override

    public boolean supports(MethodParameter methodParameter, Type targetType,

    Class<? extends HttpMessageConverter<?>> converterType) {

    // 返回true,表示啟動攔截

    return MsgBody.class.getTypeName().equals(targetType.getTypeName());

    }

    @Override

    public Object handleEmptyBody(Object body, HttpInputMessage inputMessage, MethodParameter parameter,

    Type targetType, Class<? extends HttpMessageConverter<?>> converterType) {

    logger.info('CustomRequestAdvice handleEmptyBody');

    // 對于空請求體,返回對象

    return body;

    }

    @Override

    public HttpInputMessage beforeBodyRead(HttpInputMessage inputMessage, MethodParameter parameter, Type targetType,

    Class<? extends HttpMessageConverter<?>> converterType) throws IOException {

    logger.info('CustomRequestAdvice beforeBodyRead');

    // 可定制消息序列化

    return new BodyInputMessage(inputMessage);

    }

    @Override

    public Object afterBodyRead(Object body, HttpInputMessage inputMessage, MethodParameter parameter, Type targetType,

    Class<? extends HttpMessageConverter<?>> converterType) {

    logger.info('CustomRequestAdvice afterBodyRead');

    // 可針對讀取后的對象做轉(zhuǎn)換,此處不做處理

    return body;

    }

    上述代碼實現(xiàn)中,針對前面提到的 MsgBody對象類型進行了攔截處理。

    在beforeBodyRead 中,返回一個BodyInputMessage對象,而這個對象便負責源數(shù)據(jù)流解析轉(zhuǎn)換 public static class BodyInputMessage implements HttpInputMessage {

    private HttpHeaders headers;

    private InputStream body;

    public BodyInputMessage(HttpInputMessage inputMessage) throws IOException {

    this.headers = inputMessage.getHeaders();

    // 讀取原字符串

    String content = IOUtils.toString(inputMessage.getBody(), 'UTF-8');

    MsgBody msg = new MsgBody();

    msg.setContent(content);

    this.body = new ByteArrayInputStream(JsonUtil.toJson(msg).getBytes());

    }

    @Override

    public InputStream getBody() throws IOException {

    return body;

    }

    @Override

    public HttpHeaders getHeaders() {

    return headers;

    }

    }

    代碼說明完成數(shù)據(jù)流的轉(zhuǎn)換,包括以下步驟:

    ResponseBodyAdvice 用法ResponseBodyAdvice 的用途在于對返回內(nèi)容做攔截處理,如下面的示例:

    @ControllerAdvice(assignableTypes = InterceptController.class)

    public static class CustomResponseAdvice implements ResponseBodyAdvice<String> {

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

    @Override

    public boolean supports(MethodParameter returnType, Class<? extends HttpMessageConverter<?>> converterType) {

    // 返回true,表示啟動攔截

    return true;

    }

    @Override

    public String beforeBodyWrite(String body, MethodParameter returnType, MediaType selectedContentType,

    Class<? extends HttpMessageConverter<?>> selectedConverterType, ServerHttpRequest request,

    ServerHttpResponse response) {

    logger.info('CustomResponseAdvice beforeBodyWrite');

    // 添加前綴

    String raw = String.valueOf(body);

    return 'PREFIX:' + raw;

    }

    }

    看,還是容易理解的,我們在返回的字符串中添加了一個前綴!

    推薦指數(shù)2 顆星,這是兩個非常冷門的接口,目前的使用場景也相對有限;

    一般在需要對輸入輸出流進行特殊處理(比如加解密)的場景下使用。

    姿勢五、@Aspect 注解這是目前最靈活的做法,直接利用注解可實現(xiàn)任意對象、方法的攔截。

    在某個Bean的類上面** @Aspect** 注解便可以將一個Bean 聲明為具有AOP能力的對象。

    @Aspect

    @Component

    public class InterceptControllerAspect {

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

    @Pointcut('target(org.zales.dmo.boot.controllers.InterceptController)')

    public void interceptController() {

    }

    @Around('interceptController()')

    public Object handle(ProceedingJoinPoint joinPoint) throws Throwable {

    logger.info('aspect before.');

    try {

    return joinPoint.proceed();

    } finally {

    logger.info('aspect after.');

    }

    }

    }

    簡單說明@Pointcut 用于定義切面點,而使用target關(guān)鍵字可以定位到具體的類。

    @Around 定義了一個切面處理方法,通過注入ProceedingJoinPoint對象達到控制的目的。

    一些常用的切面注解:

    注解說明@Before方法執(zhí)行之前@After方法執(zhí)行之后@Around方法執(zhí)行前后@AfterThrowing拋出異常后@AfterReturing正常返回后

    深入一點aop的能力來自于spring-boot-starter-aop,進一步依賴于aspectjweaver組件。

    有興趣可以進一步了解。

    推薦指數(shù)5顆星,aspectj 與 SpringBoot 可以無縫集成,這是一個經(jīng)典的AOP框架,

    可以實現(xiàn)任何你想要的功能,筆者之前曾在多個項目中使用,效果是十分不錯的。

    注解的支持及自動包掃描大大簡化了開發(fā),然而,你仍然需要先對 Pointcut 的定義有充分的了解。

    思考到這里,讀者可能想知道,這些實現(xiàn)攔截器的接口之間有什么關(guān)系呢?

    答案是,沒有什么關(guān)系! 每一種接口都會在不同的時機被調(diào)用,我們基于上面的代碼示例做了日志輸出:

    - Filter customFilter handle before

    - Filter annotateFilter handle before

    - CustomerHandlerInterceptor preHandle, body

    - CustomRequestAdvice beforeBodyRead

    - CustomRequestAdvice afterBodyRead

    - aspect before.

    - aspect after.

    - CustomResponseAdvice beforeBodyWrite

    - CustomerHandlerInterceptor postHandle, body

    - CustomerHandlerInterceptor afterCompletion, body

    - Filter annotateFilter handle after

    - Filter customFilter handle after

    可以看到,各種攔截器接口的執(zhí)行順序如下圖:

    小結(jié)AOP 是實現(xiàn)攔截器的基本思路,本文介紹了SpringBoot 項目中實現(xiàn)攔截功能的五種常用姿勢

    對于每一種方法都給出了真實的代碼樣例,讀者可以根據(jù)需要選擇自己適用的方案。

    聲明:該文觀點僅代表作者本人,搜狐號系信息發(fā)布平臺,搜狐僅提供信息存儲空間服務(wù)。

      本站是提供個人知識管理的網(wǎng)絡(luò)存儲空間,所有內(nèi)容均由用戶發(fā)布,不代表本站觀點。請注意甄別內(nèi)容中的聯(lián)系方式、誘導(dǎo)購買等信息,謹防詐騙。如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,請點擊一鍵舉報。
      轉(zhuǎn)藏 分享 獻花(0

      0條評論

      發(fā)表

      請遵守用戶 評論公約

      類似文章 更多

      主站蜘蛛池模板: 67194熟妇在线直接进入| 午夜免费无码福利视频| 午夜成人无码免费看网站| 国产色视频网站免费| 亚洲爆乳精品无码AAA片| 麻豆国产传媒精品视频| 色综合久久久久综合99| 四虎永久精品免费视频| 精品国偷自产在线视频99| 欧美野外伦姧在线观看| 四虎国产精品免费久久久| 欧美日韩在线视频| 国产日韩久久免费影院| 成人免费A级毛片无码网站入口| 少妇粗大进出白浆嘿嘿视频| 无码人妻蜜肉动漫中文字幕| 日韩在线视频线观看一区| 国产精品久久无码不卡黑寡妇 | 亚洲国产一线二线三线| 欧美极品色午夜在线视频| 无码AV岛国片在线播放| 日韩AV高清在线看片| 国产丰满美女A级毛片| 中文字幕在线亚洲精品| 99热精品毛片全部国产无缓冲| 亚洲AV无码专区亚洲AV| 久久午夜色播影院| 男人扒开女人腿桶到爽免费| 国产欧美VA天堂在线观看视频 | 日韩国产精品无码一区二区三区 | 性欧美牲交在线视频| 久久精品一本到99热免费| 亚洲爆乳少妇无码激情| 老司机免费的精品视频| 日韩精品一区二区三区视频| 久久综合久久美利坚合众国| 国产在线不卡精品网站 | 国产对白熟女受不了了| 精品视频不卡免费观看| 婷婷综合久久狠狠色成人网| 中文字幕无线码中文字幕免费|