1. 程式人生 > >Jersey實現對方法進行過濾攔截

Jersey實現對方法進行過濾攔截

    在web程式開發過程中,通常我們會需要對我們的介面進行訪問控制,例如控制使用者的訪問許可權、記錄使用者的訪問日誌等,在我們使用Jersey進行Restful服務開發中,同樣會有類似需求,下面我們介紹下,使用Jersey框架,實現介面訪問的三種方式。
一、使用註解名稱繫結過濾器 1.1 建立名稱繫結註解     使用@NameBinding註解,可以定義一個執行時的自定義註解,該註解可以用於定義類級別名稱和淚的方法。例如,我們定義一個使用者訪問的註解。
    
  1. @NameBinding            //標識名稱繫結的註解
  2. @Target({ElementType.TYPE,ElementType.METHOD}) //表示該註解可以使用在類和方法上。
  3. @Retention(value =RetentionPolicy.RUNTIME)
  4. public@interfaceUserLogger{
  5. }
    上面程式碼我們定義了一個名稱繫結註解UserLogger。 1.2 註解繫結過濾器     建立了註解之後,我們需要將註解和Jersey中的Provider 元件繫結,示例中我們使用的是過濾器。     
  1. @Provider
  2. @UserLogger
  3. @Priority(Priorities.USER)
  4. publicclassLoggerFilterimplementsContainerRequestFilter,ContainerResponseFilter{
  5. @Override
  6. publicvoid filter(ContainerRequestContext requestContext)throwsIOException{
  7. System.out.println("訪問請求日誌過濾器執行了>>>>>>>>>>>>");
  8. }
  9. @Override
  10. public
    void filter(ContainerRequestContext requestContext,ContainerResponseContext responseContext)throwsIOException{
  11. System.out.println("訪問響應日誌過濾器執行了>>>>>>>>>>>");
  12. }
  13. }
    @Provider 註解Jersey註解,Jersey掃描到該註解,就會建立對應的元件物件。
    @UserLogger 就是我們自定義的名稱繫結註解
    @Priority 是用於表示該過濾器的執行順序,其中引數為long型別,對於請求過濾器,該數值越小越先執行,響應過濾器則相反。
    ContainerRequestFilter 為請求過濾器 
    ContainerResponseFilter 為響應過濾器
       在請求和響應的過濾方法中,我們簡單的列印輸出。     需要注意的是,我們建立了過濾器之後需要在Jersey中進行宣告,我們在Jerysey的ResourceConfig 子類中,註冊該過濾器,註冊有兩種方式,一種是掃描包的形式,這時需要在過濾器上加上@Provider註解,另一種是直接註冊該過濾器。
    
  1. packages
    ("com.xxxx.xxxx.xxxx.filter"); //掃描包的形式 過濾器所在的包
  2. register(LoggerFilter.class); //直接註冊過濾器

1.3 註解繫結介面     上面我們建立好註解和過濾器之後,需要將在我們需要使用過濾器的介面方法上使用註解。
    
  1. @Path("/test")
  2. publicclassTestResource{
  3. @GET
  4. @Path("/1")
  5. publicString test1(){
  6. return"不帶過濾器";
  7. }
  8. @GET
  9. @Path("/2")
  10. @UserLogger
  11. publicString test2(){
  12. return"帶過濾器";
  13. }
  14. }
    我們建立了兩個介面,一個路徑是/test/1 沒有使用過濾器,一個路徑是/test/2使用註解,按照我們的設計,當訪問./test/1時,日誌過濾器不起作用,訪問/test/2時日誌過濾器起作用。 二、使用動態繫結的形式註冊過濾器     上面介紹的名稱保定的形式需要通過自定義註解的形式來實現過濾器繫結,而動態繫結則不需要新增註解,而是需要編碼的形式,實現動態繫結。動態繫結需要實現動態特徵介面javax.ws.rs.container,DynamiFeature,定義擴充套件點方法,請求方法型別等匹配資訊,在執行期,一旦Provider匹配到當前處理類或方法,面向切面的Provider方法就是觸發。
2.1 實現動態繫結特徵    
  1. publicclassLoggerDynaimcFeatureimplementsDynamicFeature{
  2. @Override
  3. publicvoid configure(ResourceInfo resourceInfo,FeatureContext context){
  4. String name = resourceInfo.getResourceMethod().getName();
  5. if("test2".equals(name)){
  6. context.register(LoggerFilter.class);
  7. }
  8. }
  9. }
    上面的程式碼實現的是當我們訪問的是test2方法時,就會註冊LoggerFilter,實現該過濾器的方法。
    我們需要在同樣我們需要註冊該動態特徵來
  1. register(LoggerDynaimcFeature.class);
這時我們重新啟動專案時,分別訪問test/1和test/2,就會看到只有test2時,日誌過濾器才會起作用。 2.2 名稱繫結和動態繫結對比     動態繫結相比於名稱繫結,不需要自定義註解,使用純編碼的形式實現。
    名稱繫結相比於動態繫結使用範圍更廣,因為我們使用註解的方式,可以對任意資源,任意方法進行控制。而使用動態繫結的形式,我們需要在動態特徵類進行對應的匹配,適用範圍較窄。 三、使用註解繫結攔截器     前面的兩種形式是通過過濾器的形式,對介面控制,一般我們做日誌訪問記錄等方式,採用該方式較好。而如果我們需要控制使用者對介面的訪問,例如登陸控制,許可權控制等,就需要使用方法攔截器。
    由於Jersey中的AOP是基於HK2框架實現的,所以對應的攔截器的功能也是由HK2框架實現。
    現在我們模擬實現一個登陸攔截器的功能。
3.1 登入器註解     
  1. @Documented
  2. @Target({ElementType.METHOD,ElementType.TYPE})
  3. @Retention(RetentionPolicy.RUNTIME)
  4. public@interfaceLoginTest{
  5. }
3.2 方法攔截器     
  1. @LoginTest
  2. publicclassLoginTestMethodInterceptorimplementsMethodInterceptor{
  3. @Override
  4. publicObject invoke(MethodInvocation methodInvocation)throwsThrowable{
  5. return"沒有許可權訪問";
  6. }
  7. }

3.3 方法攔截器的繫結     Jersey在呼叫方法攔截器的時候,需要InterceptionService的實現。
   該介面中有三個方法,在執行對應的介面方法之前會呼叫getMethodInteceptors()方法,獲取對應的攔截器,並執行攔截器。
  1. publicclassJerseyInterceptorimplementsInterceptionService{
  2. privatestaticMap<Annotation,MethodInterceptor> map =newHashMap<>();
  3. static{
  4. Annotation[] annotations =LoginTestMethodInterceptor.class.getAnnotations();
  5. for(Annotation annotation : annotations){
  6. map.put(annotation,newLoginTestMethodInterceptor());
  7. }
  8. }
  9. @Override
  10. publicFilter getDescriptorFilter(){
  11. returnnewFilter(){
  12. publicboolean matches(Descriptor descriptor){
  13. returntrue;
  14. }
  15. };
  16. }
  17. @Override
  18. publicList<MethodInterceptor> getMethodInterceptors(Method method){
  19. Annotation[] annotations = method.getAnnotations();
  20. List<MethodInterceptor> list =newArrayList<>();
  21. for(Annotation annotation :annotations){
  22. if(map.get(annotation)!=null){
  23. list.add(map.get(annotation));
  24. }
  25. }
  26. return list;
  27. }
  28. @Override
  29. publicList<ConstructorInterceptor> getConstructorInterceptors(Constructor<?> constructor){
  30. returnnull;
  31. }
  32. }
    上面程式碼可以看到,我們是將註解與攔截器繫結,通過方法上的註解,獲取方法對應的攔截器,並執行攔截器。
我們建立的攔截服務實現,需要與攔截服務進行繫結,這時需要AbstracctBinding物件的實現‘。
  1. publicclassJerseyBindingextendsAbstractBinder{
  2. @Override
  3. protectedvoid configure(){
  4. this.bind(JerseyInterceptor.class).to(InterceptionService.class).in(Singleton.class);
  5. }
  6. }
為了使繫結生效,我們需要定義特徵類,用於註冊該繫結
  1. publicclassJerseyFeatureimplementsFeature{
  2. @Override
  3. publicboolean configure(FeatureContext context){
  4. context.register(newJerseyBinding());
  5. returntrue;
  6. }
  7. }
同樣我們需要在ResourceConfig中註冊該特徵類
  1. register(JerseyFeature.class);
3,4 測試     測試介面如下
  1. @Path("/test")
  2. publicclassTestResource{
  3. @Path("/1")
  4. @GET
  5. publicString test1(){
  6. return"不帶攔截器";
  7. }
  8. @Path("/2")
  9. @GET
  10. @LoginTest
  11. publicString test2(){
  12. return"不帶攔截器";