




版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领
文档简介
JAXRS.:REST式Web服务API中新增的和值得关注的功能JAX-RS2.0:REST式Web服务API中新增的和值得关注的功能JohnClinganJavaEE和GlassFish产品经理JAX-RS2.0中的新增功能JAX-RS回顾客户端API通用配置异步处理过滤器/拦截器超媒体支持服务器端内容协商JAX-RS—用于REST式服务的JavaAPI基于POJO的资源类以HTTP为中心的编程模型实体格式独立性容器独立性包括在JavaEE中注解驱动的标准API,用于帮助开发人员使用Java构建REST式
Web服务和客户端JAX-RS示例...@POST@Path("/withdrawal")@Consumes("text/plain")@Produces("application/json")publicMoneywithdraw(@PathParam("card")Stringcard,@QueryParam("pin")Stringpin,Stringamount){returngetMoney(card,pin,amount);}}JAX-RS注解方法注解目的GET@GET读取,可能缓存POST@POST在没有已知ID的情况下更新或创建PUT@PUT在有已知ID的情况下更新或创建DELETE@DELETE删除HEAD@HEAD没有响应的GETOPTIONS@Options支持的方法JAX-RS注解(续)注解目的@PathParam绑定来自URI的值,例如
@PathParam(“id”)@QueryParam绑定查询名称的值/查询值,例如
@QueryParam(“name”)@CookieParam绑定cookie的值,例如
@CookieParam(“JSESSIONID”)@HeaderParam绑定HTTP标头的值,例如
@HeaderParam(“Accept”)@FormParam绑定HTML表单的值,例如
@FormParam(“name”)@MatrixParam绑定矩阵参数的值,例如
@MatrixParam(“name”)JAXRS2.0客户端API客户端APIHTTP客户端库太低级利用JAX-RS1.xAPI中的提供商/概念主要实现引入的专用API动机客户端API//GetinstanceofClientClientclient=ClientBuilder.newClient();//GetaccountbalanceStringbal=client.target("://.../atm/{cardId}/balance").resolveTemplate().queryParam("pin","9876").request("text/plain").get(String.class);客户端API//WithdrawsomemoneyMoneymoney=
client.target("://.../atm/{cardId}/withdrawal").resolveTemplate().queryParam("pin","9876").request("application/json").post(text("50.0"),Money.class);客户端APIInvocationinvocation1=client.target("://.../atm/{cardId}/balance")….request(“text/plain”).buildGet();Invocationinvocation2=client.target("://.../atm/{cardId}/withdraw")….request("application/json").buildPost(text("50.0"));客户端APICollection<Invocation>invocations=
Arrays.asList(inv1,inv2);Collection<Response>responses=Collections.transform(invocations,newF<Invocation,Response>(){publicResponseapply(Invocationinvocation){returninvocation.invoke();}});客户端API//CreateclientandregisterMyProvider1Clientclient=ClientBuilder.newClient();client.register(MyProvider1.class);//Createatmtarget;inheritsMyProvider1WebTargetatm=client.target("://.../atm");//RegisterMyProvider2atm.register(MyProvider2.class);//Createbalancetarget;inheritsMyProvider1,MyProvider2WebTargetbalance=atm.path(”{cardId}/balance");//RegisterMyProvider3balance.register(MyProvider3.class);JAXRS2.0
通用配置通用配置—动机客户端客户端.register(JsonMessageBodyReader.class).register(JsonMessageBodyWriter.class).register(JsonpInterceptor.class).property().property()...通用配置—动机服务器端publicSet<Class<?>>getClasses(){Set<Class<?>>classes=newHashSet<…>();...classes.add(JsonMessageBodyReader.class);classes.add(JsonMessageBodyWriter.class);classes.add(JsonpInterceptor.class);...returnclasses;}}通用配置—解决方案客户端客户端.register(JsonMessageBodyReader.class).register(JsonMessageBodyWriter.class).register(JsonpInterceptor.class).property().property()...JsonFeaturejf=newJsonFeature().enableCallbackQueryParam();client.register(jf);通用配置—解决方案服务器端publicSet<Class<?>>getClasses(){...classes.add(JsonMessageBodyReader.class);classes.add(JsonMessageBodyWriter.class);classes.add(JsonpInterceptor.class);...}publicSet<Class<?>>getClasses(){...classes.add(JsonFeature.class);...}通用配置publicinterfaceConfigurable{ConfigurationgetConfiguration();Configurableproperty(Stringname,Objectvalue);Configurableregister(...);}publicinterfaceConfiguration{Set<Class>getClasses();Map<Class,Integer>getContracts(ClasscomponentClass);Set<Object>getInstances();Map<String,Object>getProperties();ObjectgetProperty(Stringname);Collection<String>getPropertyNames();booleanisEnabled(Featurefeature);booleanisRegistered(Objectcomponent);...}通用配置publicinterfaceFeature{booleanconfigure(FeatureContextcontext);}Feature示例publicvoidJsonFeatureimplementsFeature{publicbooleanconfigure(FeatureContextcontext){context.register(JsonMessageBodyReader.class).register(JsonMessageBodyWriter.class).register(JsonpInterceptor.class).property(CALLBACK_NAME,calbackName).property(USE_QUERY_PARAM,useQueryParam);returntrue;}}动态Feature仅服务器端publicinterfaceDynamicFeature{voidconfigure(ResourceInfori,FeatureContextcontext);}publicinterfaceResourceInfo{MethodgetResourceMethod();Class<?>getResourceClass();}JAXRS2.0
异步处理异步处理服务器API分流I/O容器线程高效的异步事件处理利用Servlet3.x异步支持(如果可用)客户端API异步的请求调用API异步处理@Stateless@Path("/async/longRunning")publicclassMyResource{@GET@AsynchronouspublicvoidlongRunningOp(@SuspendedAsyncResponsear){ar.setTimeoutHandler(newMyTimoutHandler());ar.setTimeout(15,SECONDS);finalStringresult=executeLongRunningOperation();ar.resume(result);}}异步处理:服务器端publicinterfaceAsyncResponse{publicvoidresume(Object/Throwableresponse);publicvoidcancel();publicvoidcancel(int/DateretryAfter);publicbooleanisSuspended();publicbooleanisCancelled();publicbooleanisDone();publicvoidsetTimeout(longtime,TimeUnitunit);publicvoidsetTimeoutHandler(TimeoutHandlerhandler);publicCollection<Class<?>>register(Class<?>callback);publicMap<Class<?>,Collection<Class<?>>>register(Class<?>callback,Class<?>...callbacks);publicCollection<Class<?>>register(Objectcallback);publicMap<Class<?>,Collection<Class<?>>>register(Objectcallback,Object...callbacks);}异步处理:服务器端@Target({ElementType.PARAMETER})@Retention(RetentionPolicy.RUNTIME)@Documentedpublic@interfaceSuspended{}publicinterfaceTimeoutHandler{voidhandleTimeout(AsyncResponseasyncResponse);}异步处理:服务器端publicinterfaceCompletionCallback{publicvoidonComplete(Throwablethrowable);}publicinterfaceConnectionCallback{publicvoidonDisconnect(AsyncResponsedisconnected);}异步处理:客户端WebTargettarget=client.target("://.../balance”)…//StartasynccallandregistercallbackFuture<?>handle=target.request().async().get(newInvocationCallback<String>(){voidcomplete(Stringbalance){…}voidfailed(InvocationExceptione){…}});//Afterwaitingfortoolong…if(!handle.isDone())handle.cancel(true);JAXRS2.0
过滤器/拦截器过滤器和拦截器定制JAX-RS请求/响应处理用例:日志记录、压缩、安全性,等等针对客户端和服务器API而引入替换现有的专用支持动机过滤器和拦截器非包装过滤器链过滤器不直接调用链中的下一个过滤器由JAX-RS运行时管理
每个过滤器决定是继续还是中断链过滤每个传入/传出消息请求
请求ContainerRequestFilter,ClientRequestFilter响应
响应ContainerResponseFilter,ClientResponseFilter服务器端特性@PreMatching、DynamicFeature过滤器和拦截器日志记录过滤器示例publicclassRequestLoggingFilterimplementsContainerRequestFilter{@Overridepublicvoidfilter(ContainerRequestContextrequestContext){log(requestContext);//non-wrapping=>returnswithoutinvokingthenextfilter}...}过滤器和拦截器只有当发生实体处理时才会调用性能提升
包装拦截器链每个拦截器都通过ceed()调用链中的下一个拦截器拦截实体提供者MessageBodyReader
拦截器ReaderInterceptor接口MessageBodyWriter拦截器WriterInterceptor接口过滤器和拦截器Gzip读取器拦截器示例publicclassGzipInterceptorimplementsReaderInterceptor{@OverrideObjectaroundReadFrom(ReaderInterceptorContextctx){InputStreamold=ctx.getInputStream();ctx.setInputStream(newGZIPInputStream(old));//wrapping=>invokesthenextinterceptorObjectentity=ceed();ctx.setInputStream(old);returnentity;}}过滤器和拦截器应用程序请求过滤器过滤器网络传输……响应过滤器过滤器写入器拦截器…MBW…MBR写入器拦截器读取器拦截器读取器拦截器过滤器和拦截器响应应用程序过滤器过滤器网络…响应过滤器过滤器…MBW写入器拦截器写入器拦截器过滤器过滤器…请求请求读取器拦截器…MBR读取器拦截器过滤器过滤器资源匹配@PreMatching绑定和优先级绑定将过滤器和拦截器与资源方法相关联服务器端概念优先级声明在执行链中的相对位置@Priority(intpriority)过滤器和拦截器共有的概念有作用域的绑定全局绑定静态@NameBinding默认@PreMatching动态DynamicFeature无绑定@NameBinding@Target({ElementType.TYPE,ElementType.METHOD})@Retention(value=RetentionPolicy.RUNTIME)public@interfaceLogged{}@Provider@Logged@Priority(USER)publicclassLoggingFilterimplementsContainerRequestFilter,
ContainerResponseFilter{…}绑定@Path("/greet/{name}")@Produces("text/plain")publicclassMyResourceClass{@Logged@GETpublicStringhello(@PathParam("name")Stringname){return"Hello"+name;}}DynamicFeature示例仅服务器端publicvoidSecurityFeatureimplementsDynamicFeature{publicbooleanconfigure(ResourceInfori,FeatureContextcontext){String[]roles=getRolesAllowed(ri);if(roles!=null){context.register(newRolesAllowedFilter(roles));}}...}JAXRS2.0
超媒体支持超媒体支持REST原则标识符和链接HATEOAS(超媒体作为应用程序状态的引擎)链接类型:结构性链接过渡性链接超媒体支持Link:<://.../orders/1/ship>;rel=ship,<://.../orders/1/cancel>;rel=cancel...<orderid="1"><customer>://.../customers/11</customer><address>://.../customers/11/address/1</address><items><item><product>://.../products/111</product><quantity>2</quantity></item><items>...</order>超媒体Link
和LinkBuilder
类RFC5988:Web链接支持ResponseBuilder
和过滤器中的链接过渡性链接(标头)支持手动结构性链接通过Link.JaxbAdapter
和Link.JaxbLink从客户端API中的链接创建资源目标超媒体//ProducerAPI(server-side)Linkself=Link.fromMethod(MyResource.class,”handleGet”).build();Linkupdate=Link.fromMethod(MyResource.class,“handlePost”).rel(”update”).build();...Responseres=Response.ok(order).link("://.../orders/1/ship","ship").links(self,update).build();超媒体Responseorder=client.target(…).request("application/xml").get();//ConsumerAPI(client-side)LinkshipmentLink=order.getLink(“ship”);if(shipmentLink!=null){Responseshipment=client.target(shipmentLink).post(null);…}JAXRS2.0
服务器端内容协商服务器端内容协商GET://.../widgets2Accept:text/*;q=1…Path("widgets2")publicclassWidgetsResource2{@GET@Produces("text/plain","text/html")publicWidgetsgetWidget(){...}}服务器端内容协商GET://.../widgets2Accept:text/*;q=1…Path("widgets2")publicclassWidgetsResource2{@GET@Produces("text/plain;qs=0.5","text/html;qs=0.75")publicWidgetsgetWidget(){...}}JAX-RS2.0总结主要新功能客户端API、过滤器和拦截器、异步资源、超媒体许多微小的API改进和扩展Bean验证、请求/响应、URI生成器、字符串转换器、
@BeanParam、MultivaluedHashMap、GenericType……DI集成、JavaEE安全性、MVC、延迟的高级客户端API其他资源阅读JSR343规范:GlassFish4.0OpenMessageQueue5.0JAX-RS2.0:REST式Web服务API中新增的和值得关注的功能JohnClinganJavaEE和GlassFish产品经理JAX-RS2.0中的新增功能JAX-RS回顾客户端API通用配置异步处理过滤器/拦截器超媒体支持服务器端内容协商JAX-RS—用于REST式服务的JavaAPI基于POJO的资源类以HTTP为中心的编程模型实体格式独立性容器独立性包括在JavaEE中注解驱动的标准API,用于帮助开发人员使用Java构建REST式
Web服务和客户端JAX-RS示例...@POST@Path("/withdrawal")@Consumes("text/plain")@Produces("application/json")publicMoneywithdraw(@PathParam("card")Stringcard,@QueryParam("pin")Stringpin,Stringamount){returngetMoney(card,pin,amount);}}JAX-RS注解方法注解目的GET@GET读取,可能缓存POST@POST在没有已知ID的情况下更新或创建PUT@PUT在有已知ID的情况下更新或创建DELETE@DELETE删除HEAD@HEAD没有响应的GETOPTIONS@Options支持的方法JAX-RS注解(续)注解目的@PathParam绑定来自URI的值,例如
@PathParam(“id”)@QueryParam绑定查询名称的值/查询值,例如
@QueryParam(“name”)@CookieParam绑定cookie的值,例如
@CookieParam(“JSESSIONID”)@HeaderParam绑定HTTP标头的值,例如
@HeaderParam(“Accept”)@FormParam绑定HTML表单的值,例如
@FormParam(“name”)@MatrixParam绑定矩阵参数的值,例如
@MatrixParam(“name”)JAXRS2.0客户端API客户端APIHTTP客户端库太低级利用JAX-RS1.xAPI中的提供商/概念主要实现引入的专用API动机客户端API//GetinstanceofClientClientclient=ClientBuilder.newClient();//GetaccountbalanceStringbal=client.target("://.../atm/{cardId}/balance").resolveTemplate().queryParam("pin","9876").request("text/plain").get(String.class);客户端API//WithdrawsomemoneyMoneymoney=
client.target("://.../atm/{cardId}/withdrawal").resolveTemplate().queryParam("pin","9876").request("application/json").post(text("50.0"),Money.class);客户端APIInvocationinvocation1=client.target("://.../atm/{cardId}/balance")….request(“text/plain”).buildGet();Invocationinvocation2=client.target("://.../atm/{cardId}/withdraw")….request("application/json").buildPost(text("50.0"));客户端APICollection<Invocation>invocations=
Arrays.asList(inv1,inv2);Collection<Response>responses=Collections.transform(invocations,newF<Invocation,Response>(){publicResponseapply(Invocationinvocation){returninvocation.invoke();}});客户端API//CreateclientandregisterMyProvider1Clientclient=ClientBuilder.newClient();client.register(MyProvider1.class);//Createatmtarget;inheritsMyProvider1WebTargetatm=client.target("://.../atm");//RegisterMyProvider2atm.register(MyProvider2.class);//Createbalancetarget;inheritsMyProvider1,MyProvider2WebTargetbalance=atm.path(”{cardId}/balance");//RegisterMyProvider3balance.register(MyProvider3.class);JAXRS2.0
通用配置通用配置—动机客户端客户端.register(JsonMessageBodyReader.class).register(JsonMessageBodyWriter.class).register(JsonpInterceptor.class).property(“”,“callback”).property(“jsonp.callback.queryParam”,“true”)...通用配置—动机服务器端publicclassMyAppextendsjavax.ws.rs.core.Application{publicSet<Class<?>>getClasses(){Set<Class<?>>classes=newHashSet<…>();...classes.add(JsonMessageBodyReader.class);classes.add(JsonMessageBodyWriter.class);classes.add(JsonpInterceptor.class);...returnclasses;}}通用配置—解决方案客户端客户端.register(JsonMessageBodyReader.class).register(JsonMessageBodyWriter.class).register(JsonpInterceptor.class).property(“”,“callback”).property(“jsonp.callback.queryParam”,“true”)...JsonFeaturejf=newJsonFeature().enableCallbackQueryParam();client.register(jf);通用配置—解决方案服务器端publicSet<Class<?>>getClasses(){...classes.add(JsonMessageBodyReader.class);classes.add(JsonMessageBodyWriter.class);classes.add(JsonpInterceptor.class);...}publicSet<Class<?>>getClasses(){...classes.add(JsonFeature.class);...}通用配置publicinterfaceConfigurable{ConfigurationgetConfiguration();Configurableproperty(Stringname,Objectvalue);Configurableregister(...);}publicinterfaceConfiguration{Set<Class>getClasses();Map<Class,Integer>getContracts(ClasscomponentClass);Set<Object>getInstances();Map<String,Object>getProperties();ObjectgetProperty(Stringname);Collection<String>getPropertyNames();booleanisEnabled(Featurefeature);booleanisRegistered(Objectcomponent);...}通用配置publicinterfaceFeature{booleanconfigure(FeatureContextcontext);}Feature示例publicvoidJsonFeatureimplementsFeature{publicbooleanconfigure(FeatureContextcontext){context.register(JsonMessageBodyReader.class).register(JsonMessageBodyWriter.class).register(JsonpInterceptor.class).property(CALLBACK_NAME,calbackName).property(USE_QUERY_PARAM,useQueryParam);returntrue;}}动态Feature仅服务器端publicinterfaceDynamicFeature{voidconfigure(ResourceInfori,FeatureContextcontext);}publicinterfaceResourceInfo{MethodgetResourceMethod();Class<?>getResourceClass();}JAXRS2.0
异步处理异步处理服务器API分流I/O容器线程高效的异步事件处理利用Servlet3.x异步支持(如果可用)客户端API异步的请求调用API异步处理@Stateless@Path("/async/longRunning")publicclassMyResource{@GET@AsynchronouspublicvoidlongRunningOp(@SuspendedAsyncResponsear){ar.setTimeoutHandler(newMyTimoutHandler());ar.setTimeout(15,SECONDS);finalStringresult=executeLongRunningOperation();ar.resume(result);}}异步处理:服务器端publicinterfaceAsyncResponse{publicvoidresume(Object/Throwableresponse);publicvoidcancel();publicvoidcancel(int/DateretryAfter);publicbooleanisSuspended();publicbooleanisCancelled();publicbooleanisDone();publicvoidsetTimeout(longtime,TimeUnitunit);publicvoidsetTimeoutHandler(TimeoutHandlerhandler);publicCollection<Class<?>>register(Class<?>callback);publicMap<Class<?>,Collection<Class<?>>>register(Class<?>callback,Class<?>...callbacks);publicCollection<Class<?>>register(Objectcallback);publicMap<Class<?>,Collection<Class<?>>>register(Objectcallback,Object...callbacks);}异步处理:服务器端@Target({ElementType.PARAMETER})@Retention(RetentionPolicy.RUNTIME)@Documentedpublic@interfaceSuspended{}publicinterfaceTimeoutHandler{voidhandleTimeout(AsyncResponseasyncResponse);}异步处理:服务器端publicinterfaceCompletionCallback{publicvoidonComplete(Throwablethrowable);}publicinterfaceConnectionCallback{publicvoidonDisconnect(AsyncResponsedisconnected);}异步处理:客户端WebTargettarget=client.target("://.../balance”)…//StartasynccallandregistercallbackFuture<?>handle=target.request().async().get(newInvocationCallback<String>(){voidcomplete(Stringbalance){…}voidfailed(InvocationExceptione){…}});//Afterwaitingfortoolong…if(!handle.isDone())handle.cancel(true);JAXRS2.0
过滤器/拦截器过滤器和拦截器定制JAX-RS请求/响应处理用例:日志记录、压缩、安全性,等等针对客户端和服务器API而引入替换现有的专用支持动机过滤器和拦截器非包装过滤器链过滤器不直接调用链中的下一个过滤器由JAX-RS运行时管理
每个过滤器决定是继续还是中断链过滤每个传入/传出消息请求
请求ContainerRequestFilter,ClientRequestFilter响应
响应ContainerResponseFilter,ClientResponseFilter服务器端特性@PreMatching、DynamicFeature过滤器和拦截器日志记录过滤器示例publicclassRequestLoggingFilterimplementsContainerRequestFilter{@Overridepublicvoidfilter(ContainerRequestContextrequestContext){log(requestContext);//non-wrapping=>returnswithoutinvokingthenextfilter}...}过滤器和拦截器只有当发生实体处理时才会调用性能提升
包装拦截器链每个拦截器都通过ceed()调用链中的下一个拦截器拦截实体提供者MessageBodyReader
拦截器ReaderInterceptor接口MessageBodyWriter拦截器WriterInterceptor接口过滤器和拦截器Gzip读取器拦截器示例publicclassGzipInterceptorimplementsReaderInterceptor{@OverrideObjectaroundReadFrom(ReaderInterceptorContextctx){InputStreamold=ctx.getInputStream();ctx.setInputStream(newGZIPInputStream(old));//wrapping=>invokesthenextinterceptorObjectentity=ceed();ctx.setInputStream(old);returnentity;}}过滤器和拦截器应用程序请求过滤器过滤器网络传输……响应过滤器过滤器写入器拦截器…MBW…MBR写入器拦截器读取器拦截器读取器拦截器过滤器和拦截器响应应用程序过滤器过滤器网络…响应过滤器过滤器…MBW写入器拦截器写入器拦截器过滤器过滤器…请求请求读取器拦截器…MBR读取器拦截器过滤器过滤器资源匹配@PreMatching绑定和优先级绑定将过滤器和拦截器与资源方法相关联服务器端概念优先级声明在执行链中的相对位置@Priority(intpriority)过滤器和拦截器共有的概念有作用域的绑定全局绑定静态@NameBinding默认@PreMatching动态DynamicFeature无绑定@NameBinding@Target({ElementType.TYPE,ElementType.METHOD})@Retention(value=RetentionPolicy.RUNTIME)public@interfaceLogged{}@Provider@Logged@Priority(USER)publicclassLoggingFilterimplementsContainerRequestFilter,
ContainerResponseFilter{…}绑定@Path("/greet/{name}")@Produces("text/plain")publicclassMyResourceClass{@Logged
温馨提示
- 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
- 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
- 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
- 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
- 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
- 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
- 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。
最新文档
- GB/T 45485-2025船舶和海上技术电伴热钢质小舱口盖设计和试验要求
- 短期劳动合同协议书
- 文具店合作合同协议书模板
- 2025年特许金融分析师考试常见问题试题及答案
- 注册会计师报告撰写规范试题及答案
- 项目管理专业考试环境试题及答案
- 项目实施过程中的关键决策试题及答案
- 宾馆行业保安工作评估计划
- 2025年税务筹划策略试题及答案
- 微生物检验仪器应用考察试题及答案
- 三年级西师大语文下学期期末知识点归纳复习知识点巩固练习
- 河南省驻马店市汝南县2024-2025学年七年级下学期期中生物试题(含答案)
- 2025年医保知识考试题库:医保定点医疗机构管理制度要点试题
- 小学科学综合试题及答案
- 2025届陕西省高考适应性检测(三)物理试题+答案
- 2025-2030中国有源电力滤波器(APF)行业市场发展趋势与前景展望战略研究报告
- 2025工厂职工安全培训考试试题及答案可打印
- 儿童寓言故事-乌鸦喝水
- 2025年河南工业和信息化职业学院高职单招语文2019-2024历年真题考点试卷含答案解析
- 2025年韩语TOPIK中级考试真题卷:阅读理解能力测试题
- 华能庆阳煤电限责任公司2025年上半年校园招聘易考易错模拟试题(共500题)试卷后附参考答案
评论
0/150
提交评论