1. JAX-RS 规范

JAX-RS(Java API for RESTful Web Services)是 Java 平台的一项规范,旨在为开发 RESTful Web 服务提供支持。JAX-RS 定义了一组标准的 API 和注解,使得开发者可以通过简单的注解方式来开发基于 REST 架构风格的 Web 服务。

实现 JAX-RS 规范的产品有很多,例如:Oracle 开发的 Jersey、JBoss(Red Hat)开发的 Resteasy、Apache CXF、Restlet 等等。

1.1. 特性

  1. 注解驱动: JAX-RS 提供了一组注解,如 @Path@GET@POST@PUT@DELETE@Produces@Consumes 等,用于简化 RESTful 服务的开发。
  2. HTTP 方法支持: 直接映射 HTTP 方法到 Java 方法,使得开发者能够轻松实现 RESTful 服务的核心操作。
  3. 内容协商: 支持通过 @Produces@Consumes 注解定义支持的媒体类型(如 JSON、XML),从而实现请求和响应的内容协商。
  4. 参数绑定: 提供对 URI 路径、查询参数、表单参数、请求头等的简便绑定方式。
  5. 异常处理: 提供了一种机制来处理 Web 应用中的异常,使得错误处理更为集中和一致。
  6. 过滤器和拦截器: 允许开发者定义过滤器和拦截器来处理请求和响应的通用逻辑,如身份验证、日志记录等。

1.2. Spring MVC 相似

JAX-RS 和 Spring MVC 都是用于构建 Web 服务的框架,它们提供的注解在功能和命名上确实有许多相似之处。这种相似性主要是因为它们都遵循 REST 架构风格,并且都需要处理 HTTP 请求和响应。

1. 相似之处
  1. 注解驱动:

    • 两者都使用注解来简化 Web 服务的开发。
    • 例如,JAX-RS 使用 @Path 来定义资源路径,Spring MVC 使用 @RequestMapping@GetMapping@PostMapping 等来定义请求路径和方法。
  2. HTTP 方法支持:

    • JAX-RS 提供 @GET@POST@PUT@DELETE 等注解,直接映射到 HTTP 方法。
    • Spring MVC 提供 @GetMapping@PostMapping@PutMapping@DeleteMapping 等注解,功能类似。
  3. 参数绑定:

    • JAX-RS 使用 @PathParam@QueryParam@FormParam 等来绑定请求参数。
    • Spring MVC 使用 @PathVariable@RequestParam@RequestBody 等来实现类似功能。
  4. 内容协商:

    • JAX-RS 使用 @Produces@Consumes 注解来指定支持的媒体类型。
    • Spring MVC 使用 @RequestMappingproducesconsumes 属性来实现相同的目的。
2. 不同之处
  1. 规范 vs 框架:

    • JAX-RS 是 Java EE 规范的一部分,定义了构建 RESTful 服务的标准接口。
    • Spring MVC 是 Spring 框架的一部分,提供了完整的 Web 应用开发解决方案,并不仅限于 RESTful 服务。
  2. 生态系统和集成:

    • JAX-RS 实现(如 Jersey、Resteasy)通常与 Java EE 应用服务器(如 WildFly、GlassFish)集成得很好。
    • Spring MVC 是 Spring 框架的一部分,与 Spring 的其他模块(如 Spring Boot、Spring Data、Spring Security)集成紧密,广泛用于 Spring 应用程序。
  3. 灵活性和扩展性:

    • Spring MVC 提供了更为灵活的配置和扩展能力,适合需要复杂业务逻辑和集成需求的应用。
    • JAX-RS 专注于 RESTful 服务,通常用于构建轻量级的 Web 服务。

JAX-RS 和 Spring MVC 之间没有直接的依赖关系或继承关系,它们是独立发展的技术栈。但由于它们都旨在解决类似的问题(即构建 Web 服务),因此在设计上出现了许多相似的理念和实现方式。Spring MVC 在 JAX-RS 发布之前就已经存在,但随着 RESTful 风格的普及,Spring MVC 也逐步演变以更好地支持 RESTful API。

2. Resteasy 开发组件

Resteasy 是一个开源框架,专门用于实现 JAX-RS(Java API for RESTful Web Services)规范。它由 JBoss(现为 Red Hat 的一部分)开发和维护,是 WildFly 应用服务器的默认 JAX-RS 实现。Resteasy 提供了一套完整的工具和功能,用于构建 RESTful Web 服务。

Resteasy 提供了一组灵活且强大的组件和模块,使开发者能够高效地构建和扩展 RESTful 服务应用。通过理解和正确使用这些组件,开发者可以定制 JAX-RS 应用的行为,以满足各种业务需求。无论是处理复杂的请求和响应格式,还是集成异步处理和客户端功能,Resteasy 都能提供全面的支持。

1. ResteasyDeployment
  • 功能作用:

    • ResteasyDeployment 是 Resteasy 的核心配置类,负责管理和配置 JAX-RS 应用程序的各个方面。
    • 它用于定义和注册资源类、提供者(providers)、异步执行器和其他 JAX-RS 组件。
    • 在应用启动时,ResteasyDeployment 会被初始化,并用于构建 RESTful 服务的运行时环境。
  • 典型用法:

    • 在配置 Resteasy 时,通常会创建一个 ResteasyDeployment 实例,并通过它注册资源和提供者。
    • 可以通过程序化方式或通过 XML 配置文件进行配置。
2. Resource
  • 功能作用:

    • Resource 代表 JAX-RS 中的资源类。资源类是包含业务逻辑的 Java 类,用于处理 HTTP 请求。
    • 每个资源类通过 @Path 注解定义一个 URI 路径,并通过方法级别的注解(如 @GET@POST 等)定义具体的操作。
  • 典型用法:

    • 开发者创建资源类来实现具体的 RESTful API 端点。
    • 在资源类中使用注解来绑定 URI 路径、HTTP 方法和请求参数。
3. Provider
  • 功能作用:

    • Provider 是用于扩展和定制 JAX-RS 运行时行为的组件。
    • 它可以用于消息体读写(如 JSON、XML 的序列化和反序列化)、异常映射、上下文解析等。
    • 通过实现特定接口并使用 @Provider 注解注册,开发者可以创建自定义的提供者。
  • 典型用法:

    • 自定义 MessageBodyReaderMessageBodyWriter 用于处理特定格式的请求和响应。
    • 实现 ExceptionMapper 接口用于统一处理应用程序中的异常。
    • 使用 ContextResolver 提供自定义的配置或对象给 JAX-RS 运行时。
4. Interceptors and Filters
  • 功能作用:

    • 拦截器和过滤器用于在请求处理的不同阶段插入自定义逻辑。
    • 拦截器可以围绕具体的方法调用进行操作,而过滤器通常用于请求和响应的全局处理。
  • 典型用法:

    • 实现 ContainerRequestFilterContainerResponseFilter 来对请求和响应进行预处理和后处理,如身份验证、日志记录。
    • 使用 @PreMatching 注解在资源匹配之前执行过滤器逻辑。
5. Async and Client Modules
  • 功能作用:

    • 异步模块支持非阻塞的请求处理,提高应用的响应性能和可扩展性。
    • 客户端模块提供功能强大的 API,用于从 Java 应用程序中调用外部 RESTful 服务。
  • 典型用法:

    • 使用 @SuspendedAsyncResponse 处理异步请求。
    • 使用 ResteasyClient 类创建和配置 RESTful 客户端。
6. ResourceFactory
  • 功能作用:

    • ResourceFactory 是一个接口,负责创建和管理资源类的实例。
    • 它允许开发者控制资源类的生命周期,特别是在需要自定义实例创建逻辑或支持不同的作用域(如请求作用域、会话作用域)时。
  • 典型用法:

    • 自定义 ResourceFactory 可以用于在实例化资源类之前进行依赖注入或其他初始化操作。
    • 在配置 Resteasy 时,可以将自定义的 ResourceFactory 注册到 ResteasyDeployment 中。
7. ServletConfig
  • 功能作用:

    • ServletConfig 是 Servlet 规范中的一个接口,提供对 Servlet 的配置信息的访问。
    • 在 Resteasy 中,ServletConfig 可以用于获取初始化参数和 Servlet 上下文,从而在配置 RESTful 服务时使用。
  • 典型用法:

    • 在 Resteasy 的 HttpServletDispatcher 中,ServletConfig 用于获取应用的配置参数。
    • 开发者可以通过 web.xml 文件配置初始化参数,并在应用启动时读取这些参数。
8. Dispatcher
  • 功能作用:

    • Dispatcher 是 Resteasy 的核心组件之一,负责将 HTTP 请求分派到相应的 JAX-RS 资源类和方法。
    • 它处理请求的路由、参数解析、异常处理等。
  • 典型用法:

    • Resteasy 在内部使用 Dispatcher 来管理请求的整个生命周期。
    • 开发者通常不需要直接与 Dispatcher 交互,但可以通过扩展其行为来定制请求处理过程。
9. Registry
  • 功能作用:

    • Registry 是一个接口,用于管理和注册 JAX-RS 资源和提供者。
    • 它允许动态添加或移除资源类和提供者,支持应用的热部署和配置更新。
  • 典型用法:

    • 在应用启动时,通过 ResteasyDeployment 注册资源和提供者。
    • 在应用运行时,可以通过 Registry 接口动态更新配置。
10. ResteasyProviderFactory
  • 功能作用:

    • ResteasyProviderFactory 是一个工厂类,负责创建和管理 JAX-RS 提供者(如 MessageBodyReaderMessageBodyWriter)。
    • 它维护一个提供者的注册表,并提供方法来查找和获取合适的提供者实例。
  • 典型用法:

    • 开发者可以通过 ResteasyProviderFactory 注册自定义的提供者。
    • 在请求处理过程中,Resteasy 使用 ResteasyProviderFactory 来选择合适的提供者进行请求和响应的序列化和反序列化。
11. HttpServletDispatcher
  • 功能作用:

    • HttpServletDispatcher 是 Resteasy 提供的一个 Servlet,负责将 HTTP 请求分派到 JAX-RS 资源。
    • 它集成了 Servlet API 和 JAX-RS API,使得 Resteasy 可以在任何 Servlet 容器中运行。
  • 典型用法:

    • web.xml 中配置 HttpServletDispatcher,以便将请求转发到 Resteasy 管理的资源。
    • 开发者可以通过 web.xml 配置初始化参数和 URL 映射。

下面通过一些示例看看如何使用 Resteasy

3. 示例

3.1. Tomcat 示例1

1. maven
            <dependency>
                <groupId>org.jboss.resteasy</groupId>
                <artifactId>resteasy-servlet-initializer</artifactId>
                <version>3.0.7.Final</version>
            </dependency>
            <dependency>
                <groupId>javax.servlet</groupId>
                <artifactId>javax.servlet-api</artifactId>
                <version>3.1.0</version>
                <scope>provided</scope>
            </dependency>
            <dependency>
                <groupId>org.apache.tomcat.embed</groupId>
                <artifactId>tomcat-embed-core</artifactId>
                <version>8.5.95</version>
            </dependency>
2. POJO
@NoArgsConstructor
@AllArgsConstructor
@Data
public class CustomVO {
    private String name;
    private Integer value;
}
3. Resource

定义一个 GET 方法,映射路径是 /example,返回对象是 CustomVO。

@Path("/example")
public class MyResource {

    @GET
    @Produces(MediaType.APPLICATION_JSON)
    public CustomVO getCustomObject() {
        return new CustomVO("SampleName", 42);
    }
}
4. Provider

针对 CustomVO 对象,定义序列化内容。
其实如果是常见的 JSON 转换,可以不用针对每个 POJO 定义 Provider,可以通过引入 resteasy-jackson2-provider 依赖默认实现。

@Provider
@Produces(MediaType.APPLICATION_JSON)
public class MyProvider implements MessageBodyWriter<CustomVO> {

    @Override
    public boolean isWriteable(Class<?> type, Type genericType, Annotation[] annotations, MediaType mediaType) {
        return type == CustomVO.class;
    }

    @Override
    public long getSize(CustomVO myCustomObject, Class<?> type, Type genericType, Annotation[] annotations, MediaType mediaType) {
        return -1;
    }

    @Override
    public void writeTo(CustomVO myCustomObject, Class<?> type, Type genericType, Annotation[] annotations, MediaType mediaType,
                        MultivaluedMap<String, Object> httpHeaders, OutputStream entityStream) throws IOException {
        String json = "{\"name\":\"" + myCustomObject.getName() + "\", \"value\":" + myCustomObject.getValue() + "}";
        entityStream.write(json.getBytes());
    }
}
5. 启动类
public class App {
    public static void main(String[] args) throws Exception {
        ResteasyDeployment deployment = new ResteasyDeployment();

        Tomcat tomcat = new Tomcat();
        tomcat.setPort(8080);
        Context ctx = tomcat.addContext("", null);
        Tomcat.addServlet(ctx, "ResteasyServlet", new HttpServletDispatcher());
        ctx.addServletMappingDecoded("/*", "ResteasyServlet");
        ctx.getServletContext().setAttribute(ResteasyDeployment.class.getName(), deployment);
        tomcat.start();
        
        deployment.setResourceClasses(Arrays.asList(MyResource.class.getName()));
        deployment.setProviderClasses(Arrays.asList(MyProvider.class.getName()));
        
        tomcat.getServer().await();
    }
}
6. 运行结果

执行 main 方法,tomcat 启动之后,然后在 tomcat 服务器上部署了 HttpServletDispatcher 的 Servlet,Resteasy 中配置了一个 Resource。

GET方法请求 http://localhost:8080/example 可以获得 Provider 序列化后的结果。

ResteasyDeployment 类中的 setResourceClassessetProviderClasses 方法用于配置不同类型的组件:

  • setResourceClasses:用于注册 JAX-RS 资源类。资源类定义了 RESTful API 的端点和业务逻辑。
  • setProviderClasses:用于注册 JAX-RS 提供者类。提供者类用于扩展和定制 JAX-RS 的行为,例如消息体的读写、异常映射、上下文解析等。

3.2. Tomcat 示例2

这个示例要稍微复杂一点,但实际可以扩展的功能要更丰富些。为什么要单独拿出来这个示例,因为在看 Dubbo 框架 RestProtocol协议的实现方法时,看到就是这么使用的。

具体细节可以看看com.alibaba.dubbo.rpc.protocol.rest.DubboHttpServer 中有关 Resteasy 的部署使用,示例中就保留对 Resteasy 组件的使用,通过简单的 Demo 来执行。

1. maven
            <dependency>
                <groupId>org.jboss.resteasy</groupId>
                <artifactId>resteasy-servlet-initializer</artifactId>
                <version>3.0.7.Final</version>
            </dependency>
            <dependency>
                <groupId>javax.servlet</groupId>
                <artifactId>javax.servlet-api</artifactId>
                <version>3.1.0</version>
                <scope>provided</scope>
            </dependency>
            <dependency>
                <groupId>org.apache.tomcat.embed</groupId>
                <artifactId>tomcat-embed-core</artifactId>
                <version>8.5.95</version>
            </dependency>
2. POJO
@NoArgsConstructor
@AllArgsConstructor
@Data
public class UserVO {
    private String name;
    private Integer age;
}
3. Provider
@Provider
@Produces(MediaType.TEXT_PLAIN)
public class UserProvider implements MessageBodyWriter<UserVO> {

    @Override
    public boolean isWriteable(Class<?> type, Type genericType, Annotation[] annotations, MediaType mediaType) {
        return type == UserVO.class;
    }

    @Override
    public long getSize(UserVO myCustomObject, Class<?> type, Type genericType, Annotation[] annotations, MediaType mediaType) {
        return -1;
    }

    @Override
    public void writeTo(UserVO userVO, Class<?> type, Type genericType, Annotation[] annotations, MediaType mediaType,
                        MultivaluedMap<String, Object> httpHeaders, OutputStream entityStream) throws IOException {
        String json = userVO.getName()+"'s age is "+userVO.getAge() ;
        entityStream.write(json.getBytes());
    }

}
4. Resource 1
@Path("/say")
public class SayResource {

    @GET
    @Path("/hello")
    @Produces(MediaType.TEXT_PLAIN)
    public String hello() {
        return "Hello!";
    }

    @GET
    @Path("/world")
    @Produces(MediaType.TEXT_PLAIN)
    public String world() {
        return "World!";
    }

    @GET
    @Path("/user")
    public UserVO user(){
        return new UserVO("Tom", 12);
    }
}
5. Resource 2
@Path("/action")
public class ActionResource {
    @GET
    @Path("/run")
    @Produces(MediaType.TEXT_PLAIN)
    public String run() {
        return "Run!";
    }

    @GET
    @Path("/fly")
    @Produces(MediaType.TEXT_PLAIN)
    public String fly() {
        return "Fly!";
    }
}
6. ServletConfig
public class CustomServletConfig implements ServletConfig {
    private final Map<String, String> initParameters;
    private final ServletContext servletContext;

    public CustomServletConfig(ServletContext servletContext) {
        this.servletContext = servletContext;
        this.initParameters = new HashMap<>();
    }
    @Override
    public String getServletName() {
        return "ResteasyServlet";
    }

    @Override
    public ServletContext getServletContext() {
        return servletContext;
    }

    /**
     * org.apache.catalina.servlets.DefaultServlet#init() 等实现类,会调用该方法读取环境属性配置
     *
     * @param name the name of the initialization parameter whose value to
     *             get
     * @return Parameter
     */
    @Override
    public String getInitParameter(String name) {
        return initParameters.get(name);
    }

    @Override
    public Enumeration<String> getInitParameterNames() {
        return new Vector<>(initParameters.keySet()).elements();
    }
}
7. HttpServletDispatcher
public class DispatcherServlet extends HttpServlet {
    private final HttpServletDispatcher dispatcher;

    public DispatcherServlet(HttpServletDispatcher dispatcher) {
        this.dispatcher = dispatcher;
    }

    protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        dispatcher.service(request,response);
    }
}
8. 启动类
public class Main {

    public static void main(String[] args) throws Exception {
        ResteasyDeployment deployment = new ResteasyDeployment();
        HttpServletDispatcher servletDispatcher = new HttpServletDispatcher();
        
        Tomcat tomcat = new Tomcat();
        tomcat.setPort(8080);
        Context context = tomcat.addContext("", null);
        Tomcat.addServlet(context, "servletDispatcher", new DispatcherServlet(servletDispatcher));
        context.addServletMappingDecoded("/*", "servletDispatcher");
        ServletContext servletContext = context.getServletContext();
        tomcat.start();
        
        servletContext.setAttribute(ResteasyDeployment.class.getName(), deployment);
        ServletConfig servletConfig = new CustomServletConfig(servletContext);
        servletDispatcher.init(servletConfig);
        deployment.getRegistry().addResourceFactory(new SingletonResource(new SayResource()));
        deployment.getRegistry().addResourceFactory(new SingletonResource(new ActionResource()));
        deployment.getProviderFactory().registerProviderInstance(new UserProvider());
        tomcat.getServer().await();
    }
}
9. 执行结果

访问 http://localhost:8080 下列路径均可访问:

  • /say/hello
  • /say/world
  • /say/user
  • /action/run
  • /action/fly

4. Resteasy 特点

4.1. 单一Servlet

示例2中,配置了2个 Resource,每个 Resource 又有多个方法映射不同 @Path。但对于 Tomcat 服务器来说,其实就部署了一个 Servlet - HttpServletDispatcher

在使用 Resteasy 和嵌入式 Tomcat 的情况下,无论你添加多少个 JAX-RS 资源,它们通常都部署在同一个 Servlet 上。这是因为 Resteasy 的 HttpServletDispatcher 作为一个单一的 Servlet 来处理所有的 JAX-RS 请求。

4.1.1. 工作机制

  1. 单一 Servlet:

    • Resteasy 使用 HttpServletDispatcher 作为一个统一的前端控制器(Front Controller)。
    • 这个 Servlet 负责拦截所有传入的 HTTP 请求,并将它们路由到适当的 JAX-RS 资源方法。如:示例2中 ctx.addServletMappingDecoded("/*", "ResteasyServlet"),拦截所有 /* 路径下的请求。
  2. 路径解析:

    • 每个 JAX-RS 资源类和方法上的 @Path 注解定义了该资源处理的 URI 路径。
    • HttpServletDispatcher 通过解析请求的 URI 路径,将请求映射到相应的资源类和方法。
  3. 集中管理:

    • 通过这种集中式的请求处理机制,开发者只需要在应用中配置一个 HttpServletDispatcher,而不必为每个资源类单独配置 Servlet。
    • 这种方式简化了配置、应用的部署和管理,使应用更易于扩展和维护。

4.1.2. 好处

通过使用单一的 HttpServletDispatcher,Resteasy 提供了一种高效、简洁和灵活的方式来管理和处理 Web 服务请求。这种设计模式不仅降低了开发和维护的复杂性,还提高了应用的性能和可扩展性。对于开发者来说,这意味着可以更专注于业务逻辑的实现,而不必过多关注底层的请求管理细节。

1. 简化配置
  • 集中管理: 只需要配置一个 HttpServletDispatcher,无需为每个资源类单独配置 Servlet。这减少了配置的复杂性,特别是在大型应用中,管理多个资源类时更加方便。
2. 易于维护和扩展
  • 统一入口: 所有请求都通过同一个入口处理,使得日志记录、错误处理、身份验证等跨切面逻辑可以在一个地方统一管理和应用。
  • 易于扩展: 添加新的资源类和路径只需在代码中进行,无需额外的配置更改。
3. 提高性能
  • 优化资源使用: 通过集中管理,可以更有效地使用服务器资源,因为只需一个 Servlet 实例来处理所有请求。
  • 减少上下文切换: 由于所有请求都通过同一个 Servlet 处理,减少了不同 Servlet 之间的上下文切换开销。
4. 增强灵活性
  • 路径映射灵活性: 通过 @Path 注解,可以灵活地定义 URI 路径,使得应用程序的路由更加直观和可维护。
  • 中间件集成: 由于有一个统一的入口,集成诸如安全、事务管理、监控等中间件变得更加简单和一致。
5. 支持跨切面功能
  • 过滤器和拦截器: 可以在 HttpServletDispatcher 层面应用全局的过滤器和拦截器,处理请求和响应的通用逻辑,如身份验证、日志记录、CORS 处理等。
  • 异常处理: 提供集中化的异常处理机制,通过全局异常映射器可以一致地处理应用中的异常。
6. 与 JAX-RS 标准兼容
  • 标准化: Resteasy 作为 JAX-RS 的实现,使用 HttpServletDispatcher 保持了与 JAX-RS 标准的兼容性,使得应用可以在不同的 JAX-RS 实现之间更容易地移植。

4.2. Spring MVC 相似

之前在内嵌 Tomcat 的文章中讲过,SpringBoot 的实现也是部署一个 Servlet。那 SpringBoot 和 Dubbo 一样,也是用 Resteasy 实现的吗?

Spring MVC 的 DispatcherServlet 和 Resteasy 的 HttpServletDispatcher 都是用于处理 HTTP 请求的核心组件,虽然它们属于不同的框架并服务于不同的架构模式,但它们之间仍然有一些相似之处和不同之处。以下是它们的详细比较:

1. 相似点
  1. HTTP 请求处理:

    • 两者都是基于 Servlet 的实现,用于接收和处理 HTTP 请求。
  2. 请求路由:

    • 都负责将请求路由到合适的处理器(Spring 中是控制器方法,Resteasy 中是 JAX-RS 资源方法)。
  3. 扩展支持:

    • 都支持通过注解配置来简化请求的映射和处理。
    • 都可以通过注册扩展(如提供者或拦截器)来增强功能。
  4. Servlet 机制:

    • 都是作为 Servlet 在应用服务器(如 Tomcat)中运行,利用 Servlet 规范的生命周期和配置机制。
2. 不同点
  1. 框架背景与目标:

    • DispatcherServlet:

      • 属于 Spring MVC 框架的一部分,主要用于构建 Web 应用,尤其是支持 MVC 模式的应用。
      • 提供了视图解析、数据绑定、验证等丰富功能。
    • HttpServletDispatcher:

      • 属于 JAX-RS 的实现之一(Resteasy),专注于构建 RESTful API 服务。
      • 主要处理 RESTful 请求,关注于 HTTP 方法和路径的映射。
  2. 配置与自动化:

    • DispatcherServlet:

      • Spring Boot 提供了自动配置,开发者通常不需要手动配置。
      • 高度可配置,通过 Spring 配置文件或 Java 配置类进行调整。
    • HttpServletDispatcher:

      • 通常需要手动配置,特别是在嵌入式服务器环境中。
      • 配置相对简单,以注解驱动的方式为主。
  3. 功能范围:

    • DispatcherServlet:

      • 提供全面的 Web 应用支持,包括会话管理、模板渲染、国际化等。
      • 支持复杂的 Web 应用场景。
    • HttpServletDispatcher:

      • 专注于 RESTful 风格的服务,处理 JSON/XML 等数据格式。
      • 适合轻量级服务和 API 开发。
  4. 生态系统与集成:

    • DispatcherServlet:

      • 支持 Spring 的整个生态系统,包括 Spring Security、Spring Data 等。
      • 适合构建复杂的企业级应用。
    • HttpServletDispatcher:

      • 集成 JAX-RS 标准,适用于与其他 JAX-RS 实现的互操作。
      • 更专注于服务和 API 的开发。

KerryWu
641 声望159 粉丝

保持饥饿


引用和评论

0 条评论