走向REST:在Spring和JAX-RS(Apache CXF)中嵌入Jetty

对于服务器核心Java开发人员而言,向世界“展示”的唯一方法是使用API​​。 今天的帖子都是关于JAX-RS的 :使用Java编写和公开RESTful服务。

但是,我们不会使用涉及应用程序服务器,WAR打包以及诸如此类的传统的重量级方法来做到这一点。 相反,我们将使用很棒的Apache CXF框架,并且一如既往地依靠Spring将所有组件连接在一起。 当然,我们也不会停止,因为我们需要一个Web服务器来运行我们的服务。 使用胖子或一个罐子的概念,我们将Jetty服务器嵌入到我们的应用程序中,并使最终的JAR可重新分发(包括所有依赖项)并且可运行。

这是很多工作,所以让我们开始吧。 如前所述,我们将使用Apache CXFSpringJetty作为构建块,因此让我们在POM文件中对其进行描述。 值得一提的另一个依赖项是出色的用于JSON处理的Jackson库。

<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">
    <modelversion>4.0.0</modelversion>

    <groupid>com.example</groupid>
    <artifactid>spring-one-jar</artifactid>
    <version>0.0.1-SNAPSHOT</version>
    <packaging>jar</packaging>

    <properties>
        <project.build.sourceencoding>UTF-8</project.build.sourceencoding>
        <org.apache.cxf.version>2.7.2</org.apache.cxf.version>
        <org.springframework.version>3.2.0.RELEASE</org.springframework.version>
        <org.eclipse.jetty.version>8.1.8.v20121106</org.eclipse.jetty.version>
    </properties>

    <dependencies>   
        <dependency>
            <groupid>org.apache.cxf</groupid>
            <artifactid>cxf-rt-frontend-jaxrs</artifactid>
            <version>${org.apache.cxf.version}</version>
        </dependency>

        <dependency>
            <groupid>javax.inject</groupid>
            <artifactid>javax.inject</artifactid>
            <version>1</version>
        </dependency>

        <dependency>
            <groupid>org.codehaus.jackson</groupid>
            <artifactid>jackson-jaxrs</artifactid>
            <version>1.9.11</version>
        </dependency>
     
        <dependency>
            <groupid>org.codehaus.jackson</groupid>
            <artifactid>jackson-mapper-asl</artifactid>
            <version>1.9.11</version>
        </dependency>
     
        <dependency>
            <groupid>cglib</groupid>
            <artifactid>cglib-nodep</artifactid>
            <version>2.2</version>
        </dependency>

        <dependency>
            <groupid>org.springframework</groupid>
            <artifactid>spring-core</artifactid>
            <version>${org.springframework.version}</version>
        </dependency>

        <dependency>
            <groupid>org.springframework</groupid>
            <artifactid>spring-context</artifactid>
            <version>${org.springframework.version}</version>
        </dependency>

        <dependency>
            <groupid>org.springframework</groupid>
            <artifactid>spring-web</artifactid>
            <version>${org.springframework.version}</version>
        </dependency>
      
        <dependency>
            <groupid>org.eclipse.jetty</groupid>
            <artifactid>jetty-server</artifactid>
            <version>${org.eclipse.jetty.version}</version>
        </dependency>
     
        <dependency>
            <groupid>org.eclipse.jetty</groupid>
            <artifactid>jetty-webapp</artifactid>
            <version>${org.eclipse.jetty.version</version>
        </dependency>  
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupid>org.apache.maven.plugins</groupid>
                <artifactid>maven-compiler-plugin</artifactid>
                <version>3.0</version>
                <configuration>
                    <source>1.6</source>
                    <target>1.6</target>
                </configuration>
            </plugin>  
            <plugin>
                <groupid>org.apache.maven.plugins</groupid>
                <artifactid>maven-jar-plugin</artifactid>
                <configuration>
                    <archive>
                        <manifest>
                            <mainclass>com.example.Starter</mainclass>
                        </manifest>
                    </archive>
                </configuration>
            </plugin>
            <plugin>
                <groupid>org.dstovall</groupid>
                <artifactid>onejar-maven-plugin</artifactid>
                <version>1.4.4</version>
                <executions>
                    <execution>
                        <configuration>
                            <onejarversion>0.97</onejarversion>
                            <classifier>onejar</classifier>
                        </configuration>
                        <goals>
                            <goal>one-jar</goal>
                        </goals>
                    </execution>
                </executions>
            </plugin>
        </plugins>
    </build>
    
    <pluginrepositories>
        <pluginrepository>
            <id>onejar-maven-plugin.googlecode.com</id>
            <url>http://onejar-maven-plugin.googlecode.com/svn/mavenrepo</url>
        </pluginrepository>
    </pluginrepositories>
 
    <repositories>
        <repository>
            <id>maven2-repository.dev.java.net</id>
            <url>http://download.java.net/maven/2/</url>
        </repository>
    </repositories>
</project>

它有很多东西,但是应该很清楚。 现在,我们准备从简单的JAX-RS应用程序开始开发第一个JAX-RS服务。

package com.example.rs;

import javax.ws.rs.ApplicationPath;
import javax.ws.rs.core.Application;

@ApplicationPath( 'api' )
public class JaxRsApiApplication extends Application {
}

看起来很简单,我们的应用程序将/ api定义为JAX-RS服务的入口路径。 样本服务将管理由Person类代表的人员

package com.example.model;

public class Person {
    private String email;
    private String firstName;
    private String lastName;

    public Person() {
    }

    public Person( final String email ) {
        this.email = email;
    }

    public String getEmail() {
        return email;
    }

    public void setEmail( final String email ) {
        this.email = email;
    }

    public String getFirstName() {
        return firstName;
    }

    public String getLastName() {
        return lastName;
    }

    public void setFirstName( final String firstName ) {
        this.firstName = firstName;
    }

    public void setLastName( final String lastName ) {
        this.lastName = lastName;
    } 
}

并遵循基本的业务服务(为简单起见,不包括数据库或任何其他存储)。

package com.example.services;

import java.util.ArrayList;
import java.util.Collection;

import org.springframework.stereotype.Service;

import com.example.model.Person;

@Service
public class PeopleService {
    public Collection< Person > getPeople( int page, int pageSize ) {
        Collection< Person > persons = new ArrayList< Person >( pageSize );

        for( int index = 0; index < pageSize; ++index ) {
            persons.add( new Person( String.format( 'person+%d@at.com', ( pageSize * ( page - 1 ) + index + 1 ) ) ) );
        }

        return persons;
    }

    public Person addPerson( String email ) {
        return new Person( email );
    }
}

如您所见,我们将根据请求的页面动态生成人员列表。 标准Spring注释@Service将此类标记为服务bean。 我们的JAX-RS服务PeopleRestService将使用它来检索人员,如以下代码所示。

package com.example.rs;

import java.util.Collection;

import javax.inject.Inject;
import javax.ws.rs.DefaultValue;
import javax.ws.rs.FormParam;
import javax.ws.rs.GET;
import javax.ws.rs.PUT;
import javax.ws.rs.Path;
import javax.ws.rs.Produces;
import javax.ws.rs.QueryParam;

import com.example.model.Person;
import com.example.services.PeopleService;

@Path( '/people' ) 
public class PeopleRestService {
    @Inject private PeopleService peopleService;

    @Produces( { 'application/json' } )
    @GET
    public Collection< Person > getPeople( @QueryParam( 'page') @DefaultValue( '1' ) final int page ) {
        return peopleService.getPeople( page, 5 );
    }

    @Produces( { 'application/json' } )
    @PUT
    public Person addPerson( @FormParam( 'email' ) final String email ) {
        return peopleService.addPerson( email );
    }
}

尽管很简单,但该类需要更多说明。 首先,我们要向/ people端点公开我们的RESTful服务。 将其与/ api (我们的JAX-RS应用程序所在的位置)组合,则以/ api / people作为限定路径给出。

现在,只要有人向该路径发出HTTP GET ,就应该调用方法getPeople 。 此方法接受可选的参数页面 (默认值为1),并以JSON形式返回人员列表。 反过来,如果有人向同一路径发出HTTP PUT ,则应调用addPerson方法(使用必需的参数email ),并以JSON形式返回new person。

现在,让我们看一下应用程序的核心Spring配置。

package com.example.config;

import java.util.Arrays;

import javax.ws.rs.ext.RuntimeDelegate;

import org.apache.cxf.bus.spring.SpringBus;
import org.apache.cxf.endpoint.Server;
import org.apache.cxf.jaxrs.JAXRSServerFactoryBean;
import org.codehaus.jackson.jaxrs.JacksonJsonProvider;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import com.example.rs.JaxRsApiApplication;
import com.example.rs.PeopleRestService;
import com.example.services.PeopleService;

@Configuration
public class AppConfig { 
    @Bean( destroyMethod = 'shutdown' )
    public SpringBus cxf() {
        return new SpringBus();
    }

    @Bean
    public Server jaxRsServer() {
        JAXRSServerFactoryBean factory = RuntimeDelegate.getInstance().createEndpoint( jaxRsApiApplication(), JAXRSServerFactoryBean.class );
        factory.setServiceBeans( Arrays.< Object >asList( peopleRestService() ) );
        factory.setAddress( '/' + factory.getAddress() );
        factory.setProviders( Arrays.< Object >asList( jsonProvider() ) );
        return factory.create();
    }

    @Bean 
    public JaxRsApiApplication jaxRsApiApplication() {
        return new JaxRsApiApplication();
    }

    @Bean 
    public PeopleRestService peopleRestService() {
        return new PeopleRestService();
    }

    @Bean 
    public PeopleService peopleService() {
        return new PeopleService();
    }

    @Bean
    public JacksonJsonProvider jsonProvider() {
        return new JacksonJsonProvider();
    }
}

它看起来并不复杂,但是在后台却发生了很多事情。 让我们将其分解为香料。 这里的两个关键组件是工厂JAXRSServerFactoryBean ,它为配置我们的JAX-RS服务器实例提供了所有繁重的工作,而SpringBus实例则将 SpringApache CXF无缝地粘合在一起。 所有其他组件代表常规的Spring Bean。

图片上还没有嵌入Jetty Web服务器实例。 我们的主要应用程序类Starter正是这样做的。

package com.example;

import org.apache.cxf.transport.servlet.CXFServlet;
import org.eclipse.jetty.server.Server;
import org.eclipse.jetty.servlet.ServletContextHandler;
import org.eclipse.jetty.servlet.ServletHolder;
import org.springframework.web.context.ContextLoaderListener;
import org.springframework.web.context.support.AnnotationConfigWebApplicationContext;

import com.example.config.AppConfig;

public class Starter {
    public static void main( final String[] args ) throws Exception {
        Server server = new Server( 8080 );

        // Register and map the dispatcher servlet
        final ServletHolder servletHolder = new ServletHolder( new CXFServlet() );
        final ServletContextHandler context = new ServletContextHandler();   
        context.setContextPath( '/' );
        context.addServlet( servletHolder, '/rest/*' );  
        context.addEventListener( new ContextLoaderListener() );

        context.setInitParameter( 'contextClass', AnnotationConfigWebApplicationContext.class.getName() );
        context.setInitParameter( 'contextConfigLocation', AppConfig.class.getName() );

        server.setHandler( context );
        server.start();
        server.join(); 
    }
}

查看此代码后,我们发现我们正在端口8080上运行Jetty服务器实例,我们正在配置Apache CXF servlet来处理/ rest / *路径上的所有请求(连同我们的JAX-RS应用程序和服务一起为我们提供了/ rest / api / people ),我们使用上面定义的配置添加了参数化的Spring上下文侦听器,最后启动了服务器。 此时,我们拥有的是托管我们的JAX-RS服务的功能完善的Web服务器。 让我们来看看它的作用。 首先,让我们将其包装为单个,可运行和可再分配的脂肪或一个罐子

mvn clean package

让我们从目标文件夹中拾取位并运行它们:

java -jar target/spring-one-jar-0.0.1-SNAPSHOT.one-jar.jar

我们应该看到这样的输出:

2013-01-19 11:43:08.636:INFO:oejs.Server:jetty-8.1.8.v20121106
2013-01-19 11:43:08.698:INFO:/:Initializing Spring root WebApplicationContext
Jan 19, 2013 11:43:08 AM org.springframework.web.context.ContextLoader initWebApplicationContext
INFO: Root WebApplicationContext: initialization started
Jan 19, 2013 11:43:08 AM org.springframework.context.support.AbstractApplicationContext prepareRefresh
INFO: Refreshing Root WebApplicationContext: startup date [Sat Jan 19 11:43:08 EST 2013]; root of context hierarchy
Jan 19, 2013 11:43:08 AM org.springframework.context.annotation.ClassPathScanningCandidateComponentProvider registerDefaultFilters
INFO: JSR-330 'javax.inject.Named' annotation found and supported for component scanning
Jan 19, 2013 11:43:08 AM org.springframework.web.context.support.AnnotationConfigWebApplicationContext loadBeanDefinitions
INFO: Successfully resolved class for [com.example.config.AppConfig]
Jan 19, 2013 11:43:09 AM org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor 
INFO: JSR-330 'javax.inject.Inject' annotation found and supported for autowiring
Jan 19, 2013 11:43:09 AM org.springframework.beans.factory.support.DefaultListableBeanFactory preInstantiateSingletons
INFO: Pre-instantiating singletons in org.springframework.beans.factory.support.DefaultListableBeanFactory@1f8166e5: 
defining beans [org.springframework.context.annotation.internal
ConfigurationAnnotationProcessor,
org.springframework.context.annotation.internalAutowiredAnnotationProcessor,
org.springframework.context.annotation.internalRequiredAnnotationProces
sor,org.springframework.context.annotation.internalCommonAnnotationProcessor,appConfig,
org.springframework.context.annotation.ConfigurationClassPostProcessor.importAwareProcessor,c
xf,jaxRsServer,jaxRsApiApplication,peopleRestService,peopleService,jsonProvider]; root of factory hierarchy
Jan 19, 2013 11:43:10 AM org.apache.cxf.endpoint.ServerImpl initDestination
INFO: Setting the server's publish address to be /api
Jan 19, 2013 11:43:10 AM org.springframework.web.context.ContextLoader initWebApplicationContext
INFO: Root WebApplicationContext: initialization completed in 2227 ms
2013-01-19 11:43:10.957:INFO:oejsh.ContextHandler:started o.e.j.s.ServletContextHandler{/,null}
2013-01-19 11:43:11.019:INFO:oejs.AbstractConnector:Started SelectChannelConnector@0.0.0.0:8080

让我们的服务器启动并运行,让我们向它发出一些HTTP请求,以确保一切都按预期运行:

> curl http://localhost:8080/rest/api/people?page=2
[
  {'email':'person+6@at.com','firstName':null,'lastName':null},
  {'email':'person+7@at.com','firstName':null,'lastName':null},
  {'email':'person+8@at.com','firstName':null,'lastName':null}, 
  {'email':'person+9@at.com','firstName':null,'lastName':null}, 
  {'email':'person+10@at.com','firstName':null,'lastName':null}
]

> curl http://localhost:8080/rest/api/people -X PUT -d 'email=a@b.com'
{'email':'a@b.com','firstName':null,'lastName':null}

太棒了! 并且请注意,我们完全不使用XML ! 源代码: https : //github.com/reta/spring-one-jar/tree/jetty-embedded

在结束这篇文章之前,我想提到一个很棒的项目Dropwizard ,它使用了非常相似的概念,但是由于Yammer的支持,所以将其推向了出色的,精心设计的框架。

参考: 走向REST:Andriy Redko {devmind}博客上,从我们的JCG合作伙伴 Andrey Redko 嵌入Spring和JAX-RS(Apache CXF)的Jetty

翻译自: https://www.javacodegeeks.com/2013/01/going-rest-embedding-jetty-with-spring-and-jax-rs-apache-cxf.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值