jersey put 服务

这是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 put 服务

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

  1. IBM Lotus Domino 7 中的实用 Web 服务,第 1 部分: 什么是 Web 服务以及它们为何如此重要

    Julian Robichaux, 开发人员, 独立顾问 Julian Robichaux 是专门研究 IBM Lotus Notes 和 Java 开发的软件开发人员和专业程序员.他擅长于各种与开发 ...

  2. IBM Lotus Domino 7 中的实用 Web 服务,第 1 部分: 什么是 Web 服务以及它们为何如此重要...

    IBM Lotus Domino 7 中的实用 Web 服务,第 1 部分: 什么是 Web 服务以及它们为何如此重要 级别: 初级 Julian Robichaux, 开发人员, 独立顾问 2005 ...

  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. jpa中::::_项目学生:JPA标准查询

    jpa中:::: 这是Project Student的一部分. 其他职位包括带有Jersey的Webservice Client,带有Jersey的 Webservice Server , 业务层 , ...

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

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

  7. 编程填空:学生信息处理程序_项目学生:业务层

    编程填空:学生信息处理程序 这是Project Student的一部分. 其他职位包括带有Jersey的Webservice Client,带有Jersey的 Webservice Server和带有 ...

  8. aop 代码_项目学生:使用AOP简化代码

    aop 代码 这是Project Student的一部分. 许多人坚信方法应适合您的编辑器窗口(例如20行),而有些人认为方法应小于此范围. 这个想法是一种方法应该做一件事,而只能做一件事. 如果它做 ...

  9. spring 事务持久性_项目学生:Spring数据的持久性

    spring 事务持久性 这是Project Student的一部分. 其他职位包括带有Jersey的Webservice Client,带有Jersey的 Webservice Server和业务层 ...

最新文章

  1. Setup best practices for Agile Scrum in your organization
  2. Android怎么设置主活动,如何从另一个活动启动Android AppWidget的配置活动?
  3. Win7下面wubi安装Ubuntu14.04LTS
  4. python3 并行计算_Python-并行计算
  5. Oracle/Mysql查看锁表与解锁表
  6. Python 进阶 —— 装饰器函数的使用
  7. 本地图片预览代码(支持 IE6、IE7)
  8. C++ TBB concurrent_unordered_map find() at() return static_cast<size_t>( t ) * internal::hash_multip
  9. 【POJ 1733】Parity game【带权并查集维护奇偶】
  10. delphi(注入)附部分源代码
  11. 异速联某客户端无法登陆
  12. Direct3D透视教程,教你做出属于自己的透视
  13. edge打开pdf不显示印章_edge打开pdf后,draw功能栏怎么没了?
  14. Kite Compositor制作下雨打雷特效详细教学
  15. 苹果计算机磁盘格式,Mac怎么将ntfs格式的磁盘格式化
  16. Xcode8写代码闪退
  17. IDA*算法实现的数字拼图游戏
  18. Android studio实现个人体重指数计算
  19. IPhone 4(是iPhone系列中的第四代)
  20. 数据库中的Schema(模式)和View(视图)

热门文章

  1. 【区间DP】摆渡线路(2017 特长生 T4)
  2. 子数整数(luogu 1151)
  3. 【高精】Gift(jzoj(gz) 1763)
  4. 【DP】和谐的奶牛(jzoj 1750)
  5. codeforces F.F. Teodor is not a liar! 最长不降子序列
  6. 31、JAVA_WEB开发基础之servlet(2)
  7. 一次频繁Full GC的排查过程,根源居然是它...
  8. 在JavaFX程序中嵌入Swing内容
  9. 漫画:如何用Zookeeper实现分布式锁?
  10. Mysql的安装与远程登录