这是Project Student的一部分。 其他职位包括带有Jersey的Webservice Client , 业务层和带有Spring Data的持久性 。

RESTful Web应用程序洋葱的第二层是Web服务服务器。 它应该是一个薄层,用于包装对业务层的调用,但不对其自身进行大量处理。 这篇文章有很多代码,但主要是测试类。

设计决策

泽西岛 -我将泽西岛用于REST服务器。 我考虑了替代方案– Spring MVC , Netty等,但是出于与客户相同的原因,决定选择Jersey。 它轻巧,不会限制开发人员。

依赖注入 –我需要依赖注入,这意味着我需要确定一个框架:Spring,EJB3,Guice等。我已经知道我将在持久层中使用Spring Data ,因此使用它很容易春天的框架。 我仍然会谨慎地最小化对该框架的任何依赖关系(ha!),以实现最大的灵活性。

局限性

球衣 –我不知道球衣将如何处理高负荷。 这是REST服务器必须是业务层的薄包装的关键原因-如果有必要,更改库将相对容易。

用户权限 –没有尝试将对某些方法的访问限制为特定用户或主机。 这应该由业务层来处理,而安全性异常将由REST服务器转换为FORBIDDEN状态代码。

泽西REST服务器

REST API是我们早期的设计文档之一。 对于服务器,这意味着我们从REST服务器开始而不是从业务层API开始实现该层。 实际上,REST服务器在业务层API中定义了必要的方法。

与标准的REST CRUD API有一个小的偏差:对象是使用POST而不是PUT创建的,因为后者的语义是完全按照提供的方式创建了对象。 我们无法做到这一点–出于安全原因,我们从不公开内部ID,也不得接受用户定义的UUID。 这意味着我们将违反REST API合同,因此我们改用POST。

还有一个小作弊:CRUD合同仅需要具有创建或更新对象的能力。 这意味着我们可以仅通过路径就可以找出所需的操作,而无需添加特定的“操作”字段。 随着我们将实现扩展到不仅仅包括CRUD动作,这可能会改变。

继续执行代码...

@Service
@Path("/course")
public class CourseResource extends AbstractResource {private static final Logger log = Logger.getLogger(CourseResource.class);private static final Course[] EMPTY_COURSE_ARRAY = new Course[0];@ContextUriInfo uriInfo;@ContextRequest request;@Resourceprivate CourseService service;/*** Default constructor.*/public CourseResource() {}/*** Unit test constructor.* * @param service*/CourseResource(CourseService service) {this.service = service;}/*** Get all Courses.* * @return*/@GET@Produces({ MediaType.APPLICATION_JSON, MediaType.TEXT_XML })public Response findAllCourses() {log.debug("CourseResource: findAllCourses()");Response response = null;try {List<Course> courses = service.findAllCourses();List<Course> results = new ArrayList<Course>(courses.size());for (Course course : courses) {results.add(scrubCourse(course));}response = Response.ok(results.toArray(EMPTY_COURSE_ARRAY)).build();} catch (Exception e) {if (!(e instanceof UnitTestException)) {log.info("unhandled exception", e);}response = Response.status(Status.INTERNAL_SERVER_ERROR).build();}return response;}/*** Create a Course.* * @param req* @return*/@POST@Consumes({ MediaType.APPLICATION_JSON, MediaType.TEXT_XML })@Produces({ MediaType.APPLICATION_JSON, MediaType.TEXT_XML })public Response createCourse(Name req) {log.debug("CourseResource: createCourse()");final String name = req.getName();if ((name == null) || name.isEmpty()) {return Response.status(Status.BAD_REQUEST).entity("'name' is required").build();}Response response = null;try {Course course = service.createCourse(name);if (course == null) {response = Response.status(Status.INTERNAL_SERVER_ERROR).build();} else {response = Response.created(URI.create(course.getUuid())).entity(scrubCourse(course)).build();}} catch (Exception e) {if (!(e instanceof UnitTestException)) {log.info("unhandled exception", e);}response = Response.status(Status.INTERNAL_SERVER_ERROR).build();}return response;}/*** Get a specific Course.* * @param uuid* @return*/@Path("/{courseId}")@GET@Produces({ MediaType.APPLICATION_JSON, MediaType.TEXT_XML })public Response getCourse(@PathParam("courseId") String id) {log.debug("CourseResource: getCourse()");Response response = null;try {Course course = service.findCourseByUuid(id);response = Response.ok(scrubCourse(course)).build();} catch (ObjectNotFoundException e) {response = Response.status(Status.NOT_FOUND).build();} catch (Exception e) {if (!e instanceof UnitTestException)) {log.info("unhandled exception", e);}response = Response.status(Status.INTERNAL_SERVER_ERROR).build();}return response;}/*** Update a Course.* * FIXME: what about uniqueness violations?* * @param id* @param req* @return*/@Path("/{courseId}")@POST@Consumes({ MediaType.APPLICATION_JSON, MediaType.TEXT_XML })@Produces({ MediaType.APPLICATION_JSON, MediaType.TEXT_XML })public Response updateCourse(@PathParam("courseId") String id, Name req) {log.debug("CourseResource: updateCourse()");final String name = req.getName();if ((name == null) || name.isEmpty()) {return Response.status(Status.BAD_REQUEST).entity("'name' is required").build();}Response response = null;try {final Course course = service.findCourseByUuid(id);final Course updatedCourse = service.updateCourse(course, name);response = Response.ok(scrubCourse(updatedCourse)).build();} catch (ObjectNotFoundException exception) {response = Response.status(Status.NOT_FOUND).build();} catch (Exception e) {if (!(e instanceof UnitTestException)) {log.info("unhandled exception", e);}response = Response.status(Status.INTERNAL_SERVER_ERROR).build();}return response;}/*** Delete a Course.* * @param id* @return*/@Path("/{courseId}")@DELETEpublic Response deleteCourse(@PathParam("courseId") String id) {log.debug("CourseResource: deleteCourse()");Response response = null;try {service.deleteCourse(id);response = Response.noContent().build();} catch (ObjectNotFoundException exception) {response = Response.noContent().build();} catch (Exception e) {if (!(e instanceof UnitTestException)) {log.info("unhandled exception", e);}response = Response.status(Status.INTERNAL_SERVER_ERROR).build();}return response;}
}

该实现告诉我们,我们需要三件事:

  • 服务API(CourseService)
  • 请求参数类(名称)
  • 洗涤器(scrubCourse)

我没有显示完整的日志记录。 必须清除请求参数,以避免日志污染。 。 作为一个简单的示例,请考虑使用写入SQL数据库的记录器,以便于分析。 该记录器的一个简单的实现-不使用位置参数-将允许通过精心设计的请求参数进行SQL注入!

OWASP ESAPI包含可用于日志清理的方法。 我没有包含在这里,因为设置起来有些麻烦。 (应该很快就会在签入代码中。)

为什么要登录到数据库? 一种好的做法是记录到达服务器层的所有未处理的异常-您永远都不想依靠用户来报告问题,而且写入日志文件的错误很容易被忽略。 相反,使用简单工具可以轻松检查写入数据库的报告。

发生未处理的异常时,高级开发人员甚至可以创建新的错误报告。 在这种情况下,至关重要的是维护一个单独的异常数据库,以避免提交重复的条目和使开发人员不知所措。 (数据库可以包含每个异常的详细信息,但错误报告系统每个异常类+堆栈跟踪仅应有一个错误报告。)

服务API

CRUD操作的服务API很简单。

public interface CourseService {List<Course> findAllCourses();Course findCourseById(Integer id);Course findCourseByUuid(String uuid);Course createCourse(String name);Course updateCourse(Course course, String name);void deleteCourse(String uuid);
}

该API还包含一个ObjectNotFoundException。 (这应该扩展为包括无法找到的对象的类型。)

public class ObjectNotFoundException extends RuntimeException {private static final long serialVersionUID = 1L;private final String uuid;public ObjectNotFoundException(String uuid) {super("object not found: [" + uuid + "]");this.uuid = uuid;}public String getUuid() {return uuid;}
}

如上所述,我们最终还需要一个UnauthorizedOperationException。

请求参数

请求参数是封装了POST负载的简单POJO。

@XmlRootElement
public class Name {private String name;public String getName() {return name;}public void setName(String name) {this.name = name;}
}

学生和教师也需要电子邮件地址。

@XmlRootElement
public class NameAndEmailAddress {private String name;private String emailAddress;public String getName() {return name;}public void setName(String name) {this.name = name;}public String getEmailAddress() {return emailAddress;}public void setEmailAddress(String emailAddress) {this.emailAddress = emailAddress;}
}

最终的应用程序将具有大量的请求参数类。

洗涤塔

洗涤塔具有三个目的。 首先,它删除了不应提供给客户端的敏感信息,例如内部数据库标识符。

其次,它可以防止由于引入集合而导致大量的数据库转储。 例如,一个学生应该包括当前部分的列表,但是每个部分都有一个已注册学生和教师的列表。 这些学生和教师中的每一个都有自己的当前部分列表。 进行起泡,冲洗,重复,最终您将转储整个数据库以响应单个查询。

解决方案是仅包含有关每个可以独立查询的对象的浅层信息。 例如,一个学生将拥有当前部分的列表,但是这些部分将仅包含UUID和名称。 一个很好的经验法则是,清理后的集合应完全包含将在下拉列表和表示表中使用的信息,仅此而已。 演示列表可以包括链接(或AJAX操作),以根据需要提取其他信息。

最后,这是执行HTML编码和清理的好地方。 应该清除返回的值,以防止跨站点脚本(CSS)攻击

public abstract class AbstractResource {/*** Scrub 'course' object.** FIXME add HTML scrubbing and encoding for string values!*/    public Course scrubCourse(final Course dirty) {final Course clean = new Course();clean.setUuid(dirty.getUuid());clean.setName(dirty.getName());// clean.setSelf("resource/" + dirty.getUuid());return clean;}
}

配置类

我们有两个配置类。 第一个始终由服务器使用,第二个仅在集成测试期间由服务器使用。 后者的配置(和引用的类)位于集成测试源树中。

我更喜欢使用配置类(在Spring 3.0中引入),因为它们提供了最大的灵活性-例如,我可以根据运行应用程序的用户或环境变量有条件地定义bean-并允许我仍然包括标准配置文件。

@Configuration
@ComponentScan(basePackages = { "com.invariantproperties.sandbox.student.webservice.server.rest" })
@ImportResource({ "classpath:applicationContext-rest.xml" })
// @PropertySource("classpath:application.properties")
public class RestApplicationContext {@Resourceprivate Environment environment;
}

Spring 3.1引入了配置文件。 它们可以工作-但我正在使用的具有弹簧意识的jersey servlet似乎无法正确设置活动概要文件。

@Configuration
//@Profile("test")
public class RestApplicationContextTest {@BeanStudentService studentService() {return new DummyStudentService();}
}

web.xml

现在,我们有足够的资源来实现我们的Web服务器。 所使用的servlet是启用了spring的Jersey servlet,它使用contextClass参数中给出的配置类。 (也可以使用配置文件,但不能同时使用配置类和文件。)

该Servlet还包含spring.profiles.active的定义。 目的是通过spring 3.1 @Profile注释有条件地在RestApplicationContextTest中包含定义,但我无法使其正常工作。 我把它留给以后参考。

<?xml version="1.0" encoding="UTF-8"?>
<web-app version="2.5" xmlns="http://java.sun.com/xml/ns/javaee"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"><display-name>Project Student Webservice</display-name><context-param><param-name>contextClass</param-name><param-value>org.springframework.web.context.support.AnnotationConfigWebApplicationContext</param-value></context-param><context-param><param-name>contextConfigLocation</param-name><param-value>com.invariantproperties.sandbox.student.webservice.server.config.RestApplicationContextcom.invariantproperties.sandbox.student.webservice.server.config.RestApplicationContextTest</param-value></context-param><listener><listener-class>org.springframework.web.context.ContextLoaderListener</listener-class></listener><servlet><servlet-name>REST dispatcher</servlet-name><servlet-class>com.sun.jersey.spi.spring.container.servlet.SpringServlet</servlet-class><init-param><param-name>spring.profiles.active</param-name><param-value>test</param-value></init-param></servlet><servlet-mapping><servlet-name>REST dispatcher</servlet-name><url-pattern>/rest/*</url-pattern></servlet-mapping>
</web-app>

单元测试

单元测试很简单。

public class CourseResourceTest {private Course physics = new Course();private Course mechanics = new Course();@Beforepublic void init() {physics.setId(1);physics.setName("physics");physics.setUuid(UUID.randomUUID().toString());mechanics.setId(1);mechanics.setName("mechanics");mechanics.setUuid(UUID.randomUUID().toString());}@Testpublic void testFindAllCourses() {final List<Course> expected = Arrays.asList(physics);final CourseService service = Mockito.mock(CourseService.class);when(service.findAllCourses()).thenReturn(expected);final CourseResource resource = new CourseResource(service);final Response response = resource.findAllCourses();assertEquals(200, response.getStatus());final Course[] actual = (Course[]) response.getEntity();assertEquals(expected.size(), actual.length);assertNull(actual[0].getId());assertEquals(expected.get(0).getName(), actual[0].getName());assertEquals(expected.get(0).getUuid(), actual[0].getUuid());}@Testpublic void testFindAllCoursesEmpty() {final List<Course> expected = new ArrayList<>();final CourseService service = Mockito.mock(CourseService.class);when(service.findAllCourses()).thenReturn(expected);final CourseResource resource = new CourseResource(service);final Response response = resource.findAllCourses();assertEquals(200, response.getStatus());final Course[] actual = (Course[]) response.getEntity();assertEquals(0, actual.length);}@Testpublic void testFindAllCoursesFailure() {final CourseService service = Mockito.mock(CourseService.class);when(service.findAllCourses()).thenThrow(new UnitTestException();final CourseResource resource = new CourseResource(service);final Response response = resource.findAllCourses();assertEquals(500, response.getStatus());}@Testpublic void testGetCourse() {final Course expected = physics;final CourseService service = Mockito.mock(CourseService.class);when(service.findCourseByUuid(expected.getUuid())).thenReturn(expected);final CourseResource resource = new CourseResource(service);final Response response = resource.getCourse(expected.getUuid());assertEquals(200, response.getStatus());final Course actual = (Course) response.getEntity();assertNull(actual.getId());assertEquals(expected.getName(), actual.getName());assertEquals(expected.getUuid(), actual.getUuid());}@Testpublic void testGetCourseMissing() {final CourseService service = Mockito.mock(CourseService.class);when(service.findCourseByUuid(physics.getUuid())).thenThrow(new ObjectNotFoundException(physics.getUuid()));final CourseResource resource = new CourseResource(service);final Response response = resource.getCourse(physics.getUuid());assertEquals(404, response.getStatus());}@Testpublic void testGetCourseFailure() {final CourseService service = Mockito.mock(CourseService.class);when(service.findCourseByUuid(physics.getUuid())).thenThrow(new UnitTestException();final CourseResource resource = new CourseResource(service);final Response response = resource.getCourse(physics.getUuid());assertEquals(500, response.getStatus());}@Testpublic void testCreateCourse() {final Course expected = physics;final Name name = new Name();name.setName(expected.getName());final CourseService service = Mockito.mock(CourseService.class);when(service.createCourse(name.getName())).thenReturn(expected);final CourseResource resource = new CourseResource(service);final Response response = resource.createCourse(name);assertEquals(201, response.getStatus());final Course actual = (Course) response.getEntity();assertNull(actual.getId());assertEquals(expected.getName(), actual.getName());}@Testpublic void testCreateCourseBlankName() {final Course expected = physics;final Name name = new Name();final CourseService service = Mockito.mock(CourseService.class);final CourseResource resource = new CourseResource(service);final Response response = resource.createCourse(name);assertEquals(400, response.getStatus());}/*** Test handling when the course can't be created for some reason. For now* the service layer just returns a null value - it should throw an* appropriate exception.*/@Testpublic void testCreateCourseProblem() {final Course expected = physics;final Name name = new Name();name.setName(expected.getName());final CourseService service = Mockito.mock(CourseService.class);when(service.createCourse(name.getName())).thenReturn(null);final CourseResource resource = new CourseResource(service);final Response response = resource.createCourse(name);assertEquals(500, response.getStatus());}@Testpublic void testCreateCourseFailure() {final Course expected = physics;final Name name = new Name();name.setName(expected.getName());final CourseService service = Mockito.mock(CourseService.class);when(service.createCourse(name.getName())).thenThrow(new UnitTestException();final CourseResource resource = new CourseResource(service);final Response response = resource.createCourse(name);assertEquals(500, response.getStatus());}@Testpublic void testUpdateCourse() {final Course expected = physics;final Name name = new Name();name.setName(mechanics.getName());final Course updated = new Course();updated.setId(expected.getId());updated.setName(mechanics.getName());updated.setUuid(expected.getUuid());final CourseService service = Mockito.mock(CourseService.class);when(service.findCourseByUuid(expected.getUuid())).thenReturn(expected);when(service.updateCourse(expected, name.getName())).thenReturn(updated);final CourseResource resource = new CourseResource(service);final Response response = resource.updateCourse(expected.getUuid(),name);assertEquals(200, response.getStatus());final Course actual = (Course) response.getEntity();assertNull(actual.getId());assertEquals(mechanics.getName(), actual.getName());assertEquals(expected.getUuid(), actual.getUuid());}/*** Test handling when the course can't be updated for some reason. For now* the service layer just returns a null value - it should throw an* appropriate exception.*/@Testpublic void testUpdateCourseProblem() {final Course expected = physics;final Name name = new Name();name.setName(expected.getName());final CourseService service = Mockito.mock(CourseService.class);when(service.updateCourse(expected, name.getName())).thenReturn(null);final CourseResource resource = new CourseResource(service);final Response response = resource.createCourse(name);assertEquals(500, response.getStatus());}@Testpublic void testUpdateCourseFailure() {final Course expected = physics;final Name name = new Name();name.setName(expected.getName());final CourseService service = Mockito.mock(CourseService.class);when(service.updateCourse(expected, name.getName())).thenThrow(new UnitTestException();final CourseResource resource = new CourseResource(service);final Response response = resource.createCourse(name);assertEquals(500, response.getStatus());}@Testpublic void testDeleteCourse() {final Course expected = physics;final CourseService service = Mockito.mock(CourseService.class);doNothing().when(service).deleteCourse(expected.getUuid());final CourseResource resource = new CourseResource(service);final Response response = resource.deleteCourse(expected.getUuid());assertEquals(204, response.getStatus());}@Testpublic void testDeleteCourseMissing() {final Course expected = physics;final Name name = new Name();name.setName(expected.getName());final CourseService service = Mockito.mock(CourseService.class);doThrow(new ObjectNotFoundException(expected.getUuid())).when(service).deleteCourse(expected.getUuid());final CourseResource resource = new CourseResource(service);final Response response = resource.deleteCourse(expected.getUuid());assertEquals(204, response.getStatus());}@Testpublic void testDeleteCourseFailure() {final Course expected = physics;final CourseService service = Mockito.mock(CourseService.class);doThrow(new UnitTestException()).when(service).deleteCourse(expected.getUuid());final CourseResource resource = new CourseResource(service);final Response response = resource.deleteCourse(expected.getUuid());assertEquals(500, response.getStatus());}
}

整合测试

问题: REST服务器集成测试是否应该使用实时数据库?

答:这是一个技巧问题。 我们都需要。

总体架构包含三个Maven模块。 我们之前介绍了student-ws-client,今天介绍了Student-ws-server。 每个都创建一个.jar文件。 第三个模块-student-ws-webapp-创建实际的.war文件。 学生-ws-服务器模块的集成测试应使用虚拟服务层,而学生-ws-webapp模块的集成测试应使用完整堆栈。

我们从集成测试开始,该集成测试反映了客户端模块中的单元测试。

public class CourseRestServerIntegrationTest {CourseRestClient client = new CourseRestClientImpl("http://localhost:8080/rest/course/");@Testpublic void testGetAll() throws IOException {Course[] courses = client.getAllCourses();assertNotNull(courses);}@Test(expected = ObjectNotFoundException.class)public void testUnknownCourse() throws IOException {client.getCourse("missing");}@Testpublic void testLifecycle() throws IOException {final String physicsName = "Physics 201";final Course expected = client.createCourse(physicsName);assertEquals(physicsName, expected.getName());final Course actual1 = client.getCourse(expected.getUuid());assertEquals(physicsName, actual1.getName());final Course[] courses = client.getAllCourses();assertTrue(courses.length > 0);final String mechanicsName = "Newtonian Mechanics 201";final Course actual2 = client.updateCourse(actual1.getUuid(),mechanicsName);assertEquals(mechanicsName, actual2.getName());client.deleteCourse(actual1.getUuid());try {client.getCourse(expected.getUuid());fail("should have thrown exception");} catch (ObjectNotFoundException e) {// do nothing}}
}

我们还需要一个虚拟服务类,该类可以实现足以支持我们的集成测试的功能。

public class DummyCourseService implements CourseService {private Map cache = Collections.synchronizedMap(new HashMap<String, Course>());public List<Course> findAllCourses() {return new ArrayList(cache.values());}public Course findCourseById(Integer id) {throw new ObjectNotFoundException(null);        }public Course findCourseByUuid(String uuid) {if (!cache.containsKey(uuid)) {throw new ObjectNotFoundException(uuid);       }return cache.get(uuid);}public Course createCourse(String name) {Course course = new Course();course.setUuid(UUID.randomUUID().toString());course.setName(name);cache.put(course.getUuid(), course);return course;}public Course updateCourse(Course oldCourse, String name) {if (!cache.containsKey(oldCourse.getUuid())) {throw new ObjectNotFoundException(oldCourse.getUuid());                   }Course course = cache.get(oldCourse.getUuid());course.setUuid(UUID.randomUUID().toString());course.setName(name);return course;       }public void deleteCourse(String uuid) {if (cache.containsKey(uuid)) {cache.remove(uuid);}}
}

pom.xml

pom.xml文件应包含一个用于运行嵌入式码头或tomcat服务器的插件。 作为集成测试的一部分,高级用户可以旋转和拆除嵌入式服务器-请参阅更新。

<build><plugins><!-- Run the application using "mvn jetty:run" --><plugin><groupId>org.mortbay.jetty</groupId><artifactId>maven-jetty-plugin</artifactId><version>6.1.16</version> <!-- ancient! --><configuration><!-- Log to the console. --><requestLog implementation="org.mortbay.jetty.NCSARequestLog"><!-- This doesn't do anything for Jetty, but is a workaround for a Maven bug that prevents the requestLog from being set. --><append>true</append></requestLog><webAppConfig><contextPath>/</contextPath><extraClasspath>${basedir}/target/test-classes/</extraClasspath></webAppConfig></configuration></plugin></plugins>
</build>

更新资料

经过更多研究后,我进行了配置以在集成测试期间设置和拆除码头服务器。 此配置使用非标准端口,因此我们可以运行它而不必关闭同时运行的另一个码头或tomcat实例。

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"><build><plugins><!-- Run the application using "mvn jetty:run" --><plugin><groupId>org.eclipse.jetty</groupId><artifactId>jetty-maven-plugin</artifactId><version>9.1.0.v20131115</version><configuration><webApp><extraClasspath>${basedir}/target/test-classes/</extraClasspath></webApp><scanIntervalSeconds>10</scanIntervalSeconds><stopPort>18005</stopPort><stopKey>STOP</stopKey><systemProperties><systemProperty><name>jetty.port</name><value>18080</value></systemProperty></systemProperties></configuration><executions><execution><id>start-jetty</id><phase>pre-integration-test</phase><goals><goal>run</goal></goals><configuration><scanIntervalSeconds>0</scanIntervalSeconds><daemon>true</daemon></configuration></execution><execution><id>stop-jetty</id><phase>post-integration-test</phase><goals><goal>stop</goal></goals></execution></executions></plugin></plugins></build>
</project>

源代码

  • 可从http://code.google.com/p/invariant-properties-blog/source/browse/student/student-webservices/student-ws-server获取源代码。
参考: 项目学生:来自Invariant Properties博客的JCG合作伙伴 Bear Giles提供的带有Jersey的Webservice Server 。

翻译自: https://www.javacodegeeks.com/2014/01/project-student-webservice-server-with-jersey.html

项目学生:带有Jersey的Web服务服务器相关推荐

  1. jersey put 服务_项目学生:带有Jersey的Web服务服务器

    jersey put 服务 这是Project Student的一部分. 其他职位包括带有Jersey的Webservice Client , 业务层和带有Spring Data的持久性 . REST ...

  2. 项目学生:带有Jersey的Web服务客户端

    这是Project Student的一部分. 其他职位包括带有Jersey的Webservice Client , 业务层和带有Spring Data的持久性 . RESTful Web应用程序洋葱的 ...

  3. jersey客户端_项目学生:带有Jersey的Web服务客户端

    jersey客户端 这是Project Student的一部分. 其他职位包括带有Jersey的Webservice Client, 带有Spring Data的 业务层和持久性 . RESTful ...

  4. web 项目集成福昕_项目学生:Web服务集成

    web 项目集成福昕 这是Project Student的一部分. 其他帖子包括带有Jersey的 Web服务 客户端,带有Jersey的 Web服务服务器 , 业务层 , 具有Spring数据的持久 ...

  5. 项目学生:Web服务集成

    这是Project Student的一部分. 其他职位包括带有Jersey的 Web服务 客户端,带有Jersey的 Web服务服务器 , 业务层 , 具有Spring数据的持久性和分片集成测试数据 ...

  6. 项目学生:分片集成测试数据

    这是Project Student的一部分. 其他职位包括带有Jersey的 Web服务 客户端,带有Jersey的 Web服务服务器 , 业务层和带有Spring Data的持久性 . 到目前为止, ...

  7. orcad自上而下_开发自上而下的Web服务项目

    orcad自上而下 这是从Alessio Soldano编辑的Advanced JAX-WS Web Services手册中摘录的示例章节. 第一章介绍了自底向上创建Web服务端点的方法. 它允许非常 ...

  8. 开发自上而下的Web服务项目

    这是从Alessio Soldano编辑的Advanced JAX-WS Web Services手册中摘录的示例章节. 第一章介绍了自底向上创建Web服务端点的方法. 它允许非常快地将现有bean作 ...

  9. 集成测试还原数据库_项目学生:分片集成测试数据

    集成测试还原数据库 这是Project Student的一部分. 其他职位包括带有Jersey的Webservice Client,带有Jersey的 Webservice Server , 业务层和 ...

最新文章

  1. CF1385E Directing Edges (拓扑排序判断环)
  2. Rest Framework
  3. python大气校正_Python 处理遥感图像:光谱辐射定标、大气校正和计算反射率
  4. python中goto如何使用,基于python goto的正确用法说明
  5. Httpwatch抓包
  6. c语言间接寻址运算符,间接寻址运算符:*
  7. 艾司博讯:拼多多商家sku编码是什么意思
  8. 慢就是快的人生哲理_快和慢人生感悟
  9. unity Debug.DrawLine画线
  10. 从帝王之术中窥探天机
  11. nuxt实现服务端渲染查看源代码显示动态接口数据
  12. efishell无法开机shell_efi shell 添加启动项 修复启动
  13. Objective-C之父Brad J. Cox去世,他推动了今天苹果的软件生态
  14. 微控制器编程技术c语言,1.单片机C语言编程技术分析.pptx
  15. 2017-2018 ACM-ICPC, Asia Daejeon Regional Contest H题
  16. windows压缩包 无法在Linux上打开
  17. 5G改变的不仅是网速,还可以改变这些......
  18. 秒表工具类StopWatch
  19. 【UG NX MCD 机电一体化概念设计】UG NX MCD+PLCSIM Advanced联合仿真实例(三) 联合调试
  20. Rhinoceros 建模简介1

热门文章

  1. java出代码1601_LeetCode 1601. 最多可达成的换楼请求数目
  2. C语言的运算符的优先级与结合性+ASCII表
  3. Spring boot(七):Spring boot+ mybatis 多数据源最简解决方案
  4. jdk 1.8 内存可见性_JDK 14中的常规,安全和确定性外部内存访问
  5. 字节流和字符流哪个不刷新_不喜欢节流吗?
  6. netflix_学习Netflix管理员–第2部分
  7. wildfly管理控制台_WildFly 9 –别希望您的控制台像这样!
  8. junit测试线程_一个在自己的线程中运行测试的JUnit规则
  9. spring基于注释的配置_基于注释的Spring MVC Web应用程序入门
  10. 在Spring MVC中处理域对象