目前我們的電影服務只提供了對電影信息的訪問服務,現在我們要再增加兩項級服務,分別用來訪問導演和演員信息。加上原先的電信信息服務,我們把 URI 統一放到 /ms/rest/service/
的子路徑下。最先想到的方法就是為這三個 URI 分別寫 JAX-RS 服務:
@Singleton @Path("service/movie") public class MovieService { // 此處省略若干行 } @Singleton @Path("service/director") public class DirectorService { // 此處省略若干行 } @Singleton @Path("service/director") public class ActorService { // 此處省略若干行 }
這種寫法的缺點就是讓三個本來有點關系(父級 URI 相同)的服務被放到了毫不相干的三個類里面,不一個個類地查看注解難以看出這點關系。為此,JAX-RS 提供了動態資源綁定的功能,讓我們能夠對這種情況做一些整理。
首先,我們引入一個服務定位器來處理集中管理這三個子級服務:
@Singleton @Path("service") public class ServiceLocator { @Inject private MovieService movieService; @Inject private DirectorService directorService; @Inject private ActorService actorService; private Map<String, Object> serviceMap; @PostConstruct private initServiceMap() { serviceMap = new HashMap<>(); serviceMap.put("movie", movieService); serviceMap.put("director", directorService); serviceMap.put("actor", actorService); } @Path("{name}") public Object locateService(@PathParam("name") String name) { Object service = serviceMap.get(name); if (service == null) { throw new WebApplicationException(Status.SERVICE_UNAVAILABLE); } return service; } }
該類中的 locateService
方法根據服務的名稱返回相應的服務實例,注意該方法只有一個 @Path
注解,因為它并不清楚請求的具體內容;返回對象的類型為 Object
,表明動態資源定位不要求服務類實現相同的接口,只需要它們的方法帶有相應的 JAX-RS 注解,就能夠被 JAX-RS 自動發現和處理(專業術語稱為 introspect,內省),以 MovieService
為例:
@Singleton public class MovieService { @GET @Path("{id}") @Produces(MediaType.APPLICATION_JSON) public Movie find(@PathParam("id") int id) { Movie movie = movieDao.get(id); if (movie != null) { return movie; } else { throw new WebApplicationException(Status.NOT_FOUND); } } // 此處省略若干行 }
這樣,每個請求實際上都由兩個類先后處理。例如,處理請求 GET /ms/rest/service/movie/1
的時候,先由 ServiceLocator
返回相配的服務實例 movieService
,然后再由該實例的 find
方法返回結果。比起最開始那三個簡單的類,雖然多了一層調用,但換來了更加清晰的結構。
動態資源定位是一個非常靈活強大的功能,用好的話,完全可以把 URI 層次整理成一個類似于文件目錄結構的抽象文件系統。