69pao国产精品视频-久久精品一区二区二三区-精品国产精品亚洲一本大道-99国产综合一区久久

關(guān)于Spring的統(tǒng)一功能處理(攔截器)實(shí)現(xiàn)

關(guān)于spring的統(tǒng)一功能處理(攔截器)實(shí)現(xiàn)

 

spring攔截器

springboot統(tǒng)一功能處理。也就是aop的具體實(shí)現(xiàn)。

1.統(tǒng)一用戶登錄權(quán)限校驗(yàn)

最原始的用戶登錄驗(yàn)證方法,我們通過封裝了一個(gè)方法來(lái)判斷用戶是否登錄,但如果實(shí)現(xiàn)的功能多了,那么每一個(gè)需要登錄的功能都要在對(duì)應(yīng)的接口中來(lái)調(diào)用這個(gè)函數(shù)來(lái)判讀是否登錄。

public class loginstatus {
  public static user getstatus(httpservletrequest request) {
      httpsession session = request.getsession(false);
      if (session == null) {
          //當(dāng)前用戶未登錄
          return null;
      }
      user user = (user) session.getattribute("user");
      if (user == null) {
          //當(dāng)前用戶未登錄
          return null;
      }
      return user;
  }
}

上面的代碼雖然已經(jīng)封裝成了方法,但是如果隨著程序功能的增多,那么每一個(gè)控制器都要調(diào)用這個(gè)接口進(jìn)行判斷,就出現(xiàn)了代碼的冗余,也增加了代碼的維護(hù)成本。

這個(gè)時(shí)候就需要提供一個(gè)公共的方法來(lái)進(jìn)行統(tǒng)一的用戶登錄權(quán)限驗(yàn)證了。

1) springaop 用戶統(tǒng)一驗(yàn)證的問題

統(tǒng)一驗(yàn)證我們可以使用springaop的前置通知或者是環(huán)繞通知來(lái)實(shí)現(xiàn)

@aspect // 說(shuō)明該類為一個(gè)切面
@component
public class useraspect {
  // 定義切點(diǎn),使用 aspectj表達(dá)式語(yǔ)法,攔截usercontroller所有方法
  @pointcut("execution(* com.example.demo.controller.usercontroller.*(..))")
  public void pointcut(){}
  // 前置通知
  @before("pointcut()")
  public void dobefore() {
      system.out.println("執(zhí)行before前置通知");
  }
  // 添加環(huán)繞通知
  @around("pointcut()")
  public object doaround(proceedingjoinpoint joinpoint) {
      object result = null;
      system.out.println("執(zhí)行環(huán)繞通知的前置方法");
      try {
          // 執(zhí)行(攔截的)業(yè)務(wù)方法
          result = joinpoint.proceed();
      } catch (throwable throwable) {
          throwable.printstacktrace();
      }
      system.out.println("執(zhí)行環(huán)繞通知的后置方法");
      return result;
  }
}

我們發(fā)現(xiàn)原生的springaop的切面實(shí)現(xiàn)用戶登錄權(quán)限的校驗(yàn)功能,會(huì)有兩個(gè)問題

我們是獲取不到httpsession對(duì)象的如果我們只對(duì)一部分方法進(jìn)行攔截,像登錄和注冊(cè)這樣的方法就沒有必要攔截,這樣的很難定義排除對(duì)應(yīng)方法的規(guī)則,甚至說(shuō)沒有辦法定義。

那就可以使用spring的攔截器

2) spring攔截器

對(duì)于上面兩個(gè)問題spring中提供了解決方案,提供了具體實(shí)現(xiàn)的攔截器:handlerlnterceptor,攔截器的實(shí)現(xiàn)分為兩個(gè)步驟:

創(chuàng)建自定義攔截器,實(shí)現(xiàn)handlerlnterceptor接口的prehandle(執(zhí)行具體方法之前的預(yù)處理)方法將自定義攔截器加入webmvcconfigurer的addinterceptors

1.自定義攔截器

用戶登錄權(quán)限校驗(yàn),自定義攔截器代碼實(shí)現(xiàn):

/**
* 定義自定義攔截器實(shí)現(xiàn)用戶登錄校驗(yàn)
*/
@configuration
public class logininterceptor implements handlerinterceptor {
  @override
  public boolean prehandle(httpservletrequest request, httpservletresponse response, object handler) throws exception {
      httpsession session = request.getsession(false);
      if (session != null &&  session.getattribute("userinfo") != null) {
          response.setstatus(200);
          return true;
      }
      response.setstatus(403);
      return false;
  }
}

2.將自定義攔截器加入到系統(tǒng)配置中

將上一步自定義的攔截器加入到系統(tǒng)配置信息中,代碼實(shí)現(xiàn):

@configuration
public class appconfig implements webmvcconfigurer {
  //添加攔截器
  @override
  public void addinterceptors(interceptorregistry registry) {
      registry.addinterceptor(new logininterceptor()) // 添加自定義攔截器
              .addpathpatterns("/**") //攔截所有接口
              .excludepathpatterns("/**/login")//排除的接口
  }
}
  • addpathpatterns:表示需要攔截的 url,*”表示攔截任意?法(也就是所有?法)
  • excludepathpatterns:表示需要排除的 url。
  • 以上的攔截規(guī)則可以攔截程序中使用的url、靜態(tài)文件(圖片、前端文件等)

排除所有靜態(tài)的資源

@configuration
public class appconfig implements webmvcconfigurer {
  //添加攔截器
  @override
  public void addinterceptors(interceptorregistry registry) {
      registry.addinterceptor(new logininterceptor()) // 添加自定義攔截器
              .addpathpatterns("/**")
              .excludepathpatterns("/**/*.html")//排除所有靜態(tài)資源
              .excludepathpatterns("/**/*.css")
              .excludepathpatterns("/**/*.js")
              .excludepathpatterns("/**/img/*");
  }
}

3) 攔截器實(shí)現(xiàn)原理

原本正常的調(diào)用流程是這樣的:

在這里插入圖片描述

但是添加了攔截器后,在調(diào)用controller之前會(huì)進(jìn)行相對(duì)應(yīng)業(yè)務(wù)處理

在這里插入圖片描述

在這里插入圖片描述

?所有?法都會(huì)執(zhí)? dispatcherservlet 中的 dodispatch 調(diào)度?法,dodispatch 部分源碼如下

protected void dodispatch(httpservletrequest request, httpservletresponse response) throws exception {
      httpservletrequest processedrequest = request;
      handlerexecutionchain mappedhandler = null;
      boolean multipartrequestparsed = false;
      webasyncmanager asyncmanager = webasyncutils.getasyncmanager(request);
     //此處省略上面代碼
  // 調(diào)用預(yù)處理
  if (!mappedhandler.applyprehandle(processedrequest, response)) {
      return;
  }
  // 執(zhí)行controller中的業(yè)務(wù)
  mv = ha.handle(processedrequest, response, mappedhandler.gethandler());
  if (asyncmanager.isconcurrenthandlingstarted()) {
      return;
  }
  // ......后面代碼省略
}

從上述源碼可以看出在開始執(zhí)? controller 之前,會(huì)先調(diào)? 預(yù)處理?法 applyprehandle,? applyprehandle ?法的實(shí)現(xiàn)源碼如下

boolean applyprehandle(httpservletrequest request, httpservletresponse response) throws exception {
      for(int i = 0; i < this.interceptorlist.size(); this.interceptorindex = i++) {
          // 獲取項(xiàng)?中使?的攔截器 handlerintercepto
          handlerinterceptor interceptor = (handlerinterceptor)this.interceptorlist.get(i);
          if (!interceptor.prehandle(request, response, this.handler)) {
              this.triggeraftercompletion(request, response, (exception)null);
              return false;
          }
      }
      return true;
  }

從上述源碼可以看出,在 applyprehandle 中會(huì)獲取所有的攔截器 handlerinterceptor 并執(zhí)?攔截器中 的 prehandle ?法,這樣就會(huì)咱們前?定義的攔截器對(duì)應(yīng)上了,如下圖所示 :

在這里插入圖片描述

只有當(dāng)我們重寫的方法放回true的時(shí)候才會(huì)繼續(xù)走調(diào)用controller的業(yè)務(wù)代碼,否則就是直接放回給前端

在這里插入圖片描述

攔截器的實(shí)現(xiàn)原理:

  • 從宏觀上來(lái)講的話,它是根據(jù)aop的思想來(lái)去執(zhí)行的,把統(tǒng)一的方法放到前置處理器來(lái)進(jìn)行處理
  • 在spring中的攔截器實(shí)現(xiàn),就是在調(diào)度器類里的調(diào)度方法,調(diào)度方法在真正調(diào)用controller之前,它會(huì)有一個(gè)方法先去掃描當(dāng)前spring中所有攔截器的一個(gè)列表,然后去執(zhí)行這些攔截器,只有當(dāng)攔截器執(zhí)行通過的時(shí)候,它才會(huì)繼續(xù)走后面的流程,才會(huì)去走controller然后返回結(jié)果給前端。

4)同一訪問前綴添加

該方法可以給所有接口添加一個(gè)訪問前綴,讓前端訪問接口時(shí)都要加上blog,比如原來(lái)是/add,添加前綴后就是/blog/add

@configuration
public class appconfig implements webmvcconfigurer {
  @override
  public void configurepathmatch(pathmatchconfigurer configurer) {
      configurer.addpathprefix("blog",pre->true);
  }
}

其中第?個(gè)參數(shù)是?個(gè)表達(dá)式,設(shè)置為 true 表示啟動(dòng)前綴

2. 統(tǒng)一異常處理

同一異常處理是通過@controlleradvice+@exceptionhandler兩個(gè)注解結(jié)合實(shí)現(xiàn)的,@controlleradvice表示控制器通知類,@exceptionhandler是異常處理,兩個(gè)結(jié)合起來(lái)就表示出現(xiàn)異常的時(shí)候執(zhí)行某一個(gè)通知,也就是執(zhí)行某個(gè)方法,代碼實(shí)現(xiàn):

@controlleradvice
public class erroradvice  {
  @exceptionhandler(exception.class)
  @responsebody
  public object handler(exception e) {
      map<string,object> map = new hashmap<>();
      map.put("success",1);
      map.put("status",-1);
      map.put("message","服務(wù)器接口異常");
      return map;
  }
}

注意:方法名和返回值可以任意,重要的是注解。

這里的代碼表示的是發(fā)生任何異常都給前端返回一個(gè)hashmap,也可以指定異常進(jìn)行處理代碼如下

@controlleradvice
public class erroradvice  {
  @exceptionhandler(exception.class)
  @responsebody
  public object exceptionadvice(exception e) {
      map<string,object> map = new hashmap<>();
      map.put("success",1);
      map.put("status",-1);
      map.put("message","服務(wù)器接口異常");
      return map;
  }
  @exceptionhandler(nullpointerexception.class)
  @responsebody
  public object nullpointerexceptionadvice(nullpointerexception exception) {
      map<string,object> map = new hashmap<>();
      map.put("success",1);
      map.put("status",-1);
      map.put("message",exception.tostring());
      return map;
  }
}

當(dāng)有多個(gè)異常通知時(shí),匹配順序?yàn)楫?dāng)前類及其?類向上依次匹配

3. 統(tǒng)一數(shù)據(jù)返回格式

1)統(tǒng)一數(shù)據(jù)返回的好處

統(tǒng)一數(shù)據(jù)返回格式有很多好處

  • 方便前端程序員接收和解析后端接口返回的數(shù)據(jù)
  • 降低前端程序員和后端程序員的溝通成本,只需要按照指定格式實(shí)現(xiàn)功能,所有接口放回值都是固定的
  • 有利于項(xiàng)目整體的維護(hù)和修改,有利于后端統(tǒng)一標(biāo)準(zhǔn)規(guī)范。
  • 2)統(tǒng)一數(shù)據(jù)返回實(shí)現(xiàn)

    統(tǒng)一的數(shù)據(jù)返回格式可以使用@controlleradvice+responsebodyadvice實(shí)現(xiàn)

    • supports方法返回true表示對(duì)返回內(nèi)容進(jìn)行重寫,就會(huì)執(zhí)行beforebodywrite方法
    • beforebodywrite方法中body就是controller返回的內(nèi)容
    @controlleradvice
    public class responseadvice implements responsebodyadvice {
      /**
       * 內(nèi)容是否需要重寫,此方法可以選擇部分控制器和方法進(jìn)行重寫
       * 返回 true表示重寫
       */
      @override
      public boolean supports(methodparameter returntype, class convertertype) {
          return true;
      }
      /**
       *控制器方法返回之前會(huì)調(diào)用此方法
       */
      @override
      public object beforebodywrite(object body, methodparameter returntype, mediatype selectedcontenttype,
                                    class selectedconvertertype, serverhttprequest request, serverhttpresponse response) {
          map<string,object> result = new hashmap<>();
          result.put("success",200);
          result.put("status",1);
          result.put("data",body);
          return result;
      }
    }

    關(guān)于關(guān)于spring的統(tǒng)一功能處理(攔截器)實(shí)現(xiàn)的文章就介紹至此,更多相關(guān)spring的統(tǒng)一功能處理內(nèi)容請(qǐng)搜索碩編程以前的文章,希望以后支持碩編程

    下一節(jié):hadoop?mapreduce實(shí)現(xiàn)單詞計(jì)數(shù)(word?count)

    java編程技術(shù)

    相關(guān)文章