来源:SpringForAll社区

通过在本教程中构建一个简单的RESTful web API,了解关于使用Java EE和Spring框架构建企业Java应用程序的更多信息。

我认为可以说Java EE在Java开发人员中获得了相当坏的名声。尽管多年来,它确实在各个方面都有所改善,甚至从Eclipse Foundation变成了JakartaEE,但它的苦味仍然相当强烈。另一方面,我们有Spring Framework(或者更好地反映现实,一个成熟的Spring Platform),这是一个出色的、轻量级的、快速的、创新的、高生产力的Java EE替代品。那么,为什么要为Java EE费心呢?我们将通过展示使用大多数Java EE规范构建现代Java应用程序是多么容易来回答这个问题。在这方面取得成功的关键因素是Eclipse Microprofile:J2EE的微服务时代。我们将要构建的应用程序是用于管理人员的RESTful web API;就这么简单。在Java中构建RESTful web服务的标准方法是使用JAX-RS 2.1 (JSR-370)。因此,CDI 2.0 (JSR-365)将负责依赖注入,而JPA 2.0 (JSR-317)将负责数据访问层。当然,Bean Validation 2.0 (JSR-380)正在帮助我们处理输入验证。我们唯一要依赖的非java EE规范是OpenAPI v3.0,它有助于提供关于RESTful web api的可用描述。那么,让我们从personentity域模型开始(省略getter和setter作为不太相关的细节):

  1. @Entity

  2. @Table(name = "people")

  3. public class PersonEntity {

  4.    @Id @Column(length = 256)

  5.    private String email;

  6.    @Column(nullable = false, length = 256, name = "first_name")

  7.    private String firstName;

  8.    @Column(nullable = false, length = 256, name = "last_name")

  9.    private String lastName;

  10.    @Version

  11.    private Long version;

  12. }

它只有一个绝对最小的属性集。JPA存储库非常简单,实现了一组典型的CRUD方法。

  1. @ApplicationScoped

  2. @EntityManagerConfig(qualifier = PeopleDb.class)

  3. public class PeopleJpaRepository implements PeopleRepository {

  4.    @Inject @PeopleDb private EntityManager em;

  5.    @Override

  6.    @Transactional(readOnly = true)

  7.    public Optional<PersonEntity> findByEmail(String email) {

  8.        final CriteriaBuilder cb = em.getCriteriaBuilder();

  9.        final CriteriaQuery<PersonEntity> query = cb.createQuery(PersonEntity.class);

  10.        final Root<PersonEntity> root = query.from(PersonEntity.class);

  11.        query.where(cb.equal(root.get(PersonEntity_.email), email));

  12.        try {

  13.            final PersonEntity entity = em.createQuery(query).getSingleResult();

  14.            return Optional.of(entity);

  15.        } catch (final NoResultException ex) {

  16.            return Optional.empty();

  17.        }

  18.    }

  19.    @Override

  20.    @Transactional

  21.    public PersonEntity saveOrUpdate(String email, String firstName, String lastName) {

  22.        final PersonEntity entity = new PersonEntity(email, firstName, lastName);

  23.        em.persist(entity);

  24.        return entity;

  25.    }

  26.    @Override

  27.    @Transactional(readOnly = true)

  28.    public Collection<PersonEntity> findAll() {

  29.        final CriteriaBuilder cb = em.getCriteriaBuilder();

  30.        final CriteriaQuery<PersonEntity> query = cb.createQuery(PersonEntity.class);

  31.        query.from(PersonEntity.class);

  32.        return em.createQuery(query).getResultList();

  33.    }

  34.    @Override

  35.    @Transactional

  36.    public Optional<PersonEntity> deleteByEmail(String email) {

  37.        return findByEmail(email)

  38.            .map(entity -> {

  39.                em.remove(entity);

  40.                return entity;

  41.            });

  42.    }

  43. }

事务管理(即@Transactionalannotation)需要一些解释。在典型的Java EE应用程序中,容器运行时负责管理事务。由于我们不想装载应用程序容器,而是保持精简,所以我们可以使用EntityManager来启动/提交/回滚事务。这当然是可行的,但它也会用样板污染代码。可以说,更好的选择是使用Apache DeltaSpikeCDI扩展用于声明性事务管理(这是@Transactional和@EntityManagerConfig注释的来源)。下面的代码片段说明了如何集成它。

  1. @ApplicationScoped

  2. public class PersistenceConfig {

  3.    @PersistenceUnit(unitName = "peopledb")

  4.    private EntityManagerFactory entityManagerFactory;

  5.    @Produces @PeopleDb @TransactionScoped

  6.    public EntityManager create() {

  7.        return this.entityManagerFactory.createEntityManager();

  8.    }

  9.    public void dispose(@Disposes @PeopleDb EntityManager entityManager) {

  10.        if (entityManager.isOpen()) {

  11.            entityManager.close();

  12.        }

  13.    }

  14. }

太棒了——最难的部分已经过去了!接下来是person数据传输对象和服务层。

  1. public class Person {

  2.    @NotNull private String email;

  3.    @NotNull private String firstName;

  4.    @NotNull private String lastName;

  5. }

老实说,为了使示例应用程序尽可能小,我们可以完全跳过服务层,直接进入存储库。但总的来说,这不是一个很好的实践,所以让我们介绍PeopleServiceImpl。

  1. @ApplicationScoped

  2. public class PeopleServiceImpl implements PeopleService {

  3.    @Inject private PeopleRepository repository;

  4.    @Override

  5.    public Optional<Person> findByEmail(String email) {

  6.        return repository

  7.            .findByEmail(email)

  8.            .map(this::toPerson);

  9.    }

  10.    @Override

  11.    public Person add(Person person) {

  12.        return toPerson(repository.saveOrUpdate(person.getEmail(), person.getFirstName(), person.getLastName()));

  13.    }

  14.    @Override

  15.    public Collection<Person> getAll() {

  16.        return repository

  17.            .findAll()

  18.            .stream()

  19.            .map(this::toPerson)

  20.            .collect(Collectors.toList());

  21.    }

  22.    @Override

  23.    public Optional<Person> remove(String email) {

  24.        return repository

  25.            .deleteByEmail(email)

  26.            .map(this::toPerson);

  27.    }

  28.    private Person toPerson(PersonEntity entity) {

  29.        return new Person(entity.getEmail(), entity.getFirstName(), entity.getLastName());

  30.    }

  31. }

剩下的部分是JAX-RS应用程序和资源的定义。

  1. @Dependent

  2. @ApplicationPath("api")

  3. @OpenAPIDefinition(

  4.    info = @Info(

  5.        title = "People Management Web APIs",

  6.        version = "1.0.0",

  7.        license = @License(

  8.            name = "Apache License",

  9.            url = "https://www.apache.org/licenses/LICENSE-2.0"

  10.        )

  11.    )

  12. )

  13. public class PeopleApplication extends Application {

  14. }

没什么好说的;这是尽可能简单的。但是JAX-RS资源实现更有趣(OpenAPI注释占据了大部分位置)。

  1. @ApplicationScoped

  2. @Path( "/people" )

  3. @Tag(name = "people")

  4. public class PeopleResource {

  5.    @Inject private PeopleService service;

  6.    @Produces(MediaType.APPLICATION_JSON)

  7.    @GET

  8.    @Operation(

  9.        description = "List all people",

  10.        responses = {

  11.            @ApiResponse(

  12.                content = @Content(array = @ArraySchema(schema = @Schema(implementation = Person.class))),

  13.                responseCode = "200"

  14.            )

  15.        }

  16.    )

  17.    public Collection<Person> getPeople() {

  18.        return service.getAll();

  19.    }

  20.    @Produces(MediaType.APPLICATION_JSON)

  21.    @Path("/{email}")

  22.    @GET

  23.    @Operation(

  24.        description = "Find person by e-mail",

  25.        responses = {

  26.            @ApiResponse(

  27.                content = @Content(schema = @Schema(implementation = Person.class)),

  28.                responseCode = "200"

  29.            ),

  30.            @ApiResponse(

  31.                responseCode = "404",

  32.                description = "Person with such e-mail doesn't exists"

  33.            )

  34.        }

  35.    )

  36.    public Person findPerson(@Parameter(description = "E-Mail address to lookup for", required = true) @PathParam("email") final String email) {

  37.        return service

  38.            .findByEmail(email)

  39.            .orElseThrow(() -> new NotFoundException("Person with such e-mail doesn't exists"));

  40.    }

  41.    @Consumes(MediaType.APPLICATION_JSON)

  42.    @Produces(MediaType.APPLICATION_JSON)

  43.    @POST

  44.    @Operation(

  45.        description = "Create new person",

  46.        requestBody = @RequestBody(

  47.            content = @Content(schema = @Schema(implementation = Person.class)),

  48.        ),

  49.        responses = {

  50.            @ApiResponse(

  51.                 content = @Content(schema = @Schema(implementation = Person.class)),

  52.                 headers = @Header(name = "Location"),

  53.                 responseCode = "201"

  54.            ),

  55.            @ApiResponse(

  56.                responseCode = "409",

  57.                description = "Person with such e-mail already exists"

  58.            )

  59.        }

  60.    )

  61.    public Response addPerson(@Context final UriInfo uriInfo,

  62.            @Parameter(description = "Person", required = true) @Valid Person payload) {

  63.        final Person person = service.add(payload);

  64.        return Response

  65.             .created(uriInfo.getRequestUriBuilder().path(person.getEmail()).build())

  66.             .entity(person)

  67.             .build();

  68.    }

  69.    @Path("/{email}")

  70.    @DELETE

  71.    @Operation(

  72.        description = "Delete existing person",

  73.        responses = {

  74.            @ApiResponse(

  75.                responseCode = "204",

  76.                description = "Person has been deleted"

  77.            ),

  78.            @ApiResponse(

  79.                responseCode = "404",

  80.                description = "Person with such e-mail doesn't exists"

  81.            )

  82.        }

  83.    )

  84.    public Response deletePerson(@Parameter(description = "E-Mail address to lookup for", required = true ) @PathParam("email") final String email) {

  85.        return service

  86.            .remove(email)

  87.            .map(r -> Response.noContent().build())

  88.            .orElseThrow(() -> new NotFoundException("Person with such e-mail doesn't exists"));

  89.    }

  90. }

这样,我们就完成了!但是,我们怎样才能把这些零件组装起来,然后用电线把它们连在一起呢?现在是 Microprofile进入舞台的时候了。有许多实现可供选择;我们将在这篇文章中使用的是Project Hammock 。我们要做的唯一一件事就是指定我们想要使用的CDI 2.0、JAX-RS 2.1和JPA 2.0实现,它们分别转换为Weld、Apache CXF和OpenJPA(通过 Project Hammock 依赖关系表示)。让我们来看看Apache Mavenpom.xml文件。

  1. <properties>

  2.    <deltaspike.version>1.8.1</deltaspike.version>

  3.    <hammock.version>2.1</hammock.version>

  4. </properties>

  5. <dependencies>

  6.    <dependency>

  7.        <groupId>org.apache.deltaspike.modules</groupId>

  8.        <artifactId>deltaspike-jpa-module-api</artifactId>

  9.        <version>${deltaspike.version}</version>

  10.        <scope>compile</scope>

  11.    </dependency>

  12.    <dependency>

  13.        <groupId>org.apache.deltaspike.modules</groupId>

  14.        <artifactId>deltaspike-jpa-module-impl</artifactId>

  15.        <version>${deltaspike.version}</version>

  16.        <scope>runtime</scope>

  17.    </dependency>

  18.    <dependency>

  19.        <groupId>ws.ament.hammock</groupId>

  20.        <artifactId>dist-microprofile</artifactId>

  21.        <version>${hammock.version}</version>

  22.    </dependency>

  23.    <dependency>

  24.        <groupId>ws.ament.hammock</groupId>

  25.        <artifactId>jpa-openjpa</artifactId>

  26.        <version>${hammock.version}</version>

  27.    </dependency>

  28.    <dependency>

  29.        <groupId>ws.ament.hammock</groupId>

  30.        <artifactId>util-beanvalidation</artifactId>

  31.        <version>${hammock.version}</version>

  32.    </dependency>

  33.    <dependency>

  34.        <groupId>ws.ament.hammock</groupId>

  35.        <artifactId>util-flyway</artifactId>

  36.        <version>${hammock.version}</version>

  37.    </dependency>

  38.    <dependency>

  39.        <groupId>ws.ament.hammock</groupId>

  40.        <artifactId>swagger</artifactId>

  41.        <version>${hammock.version}</version>

  42.    </dependency>

  43. </dependencies>

在没有进一步的ado的情况下,让我们立即构建和运行应用程序(如果您想知道应用程序使用的是什么关系数据存储,那么它是H2,在内存中配置了数据库)。

  1. mvn clean package

  2. java -jar target/eclipse-microprofile-hammock-0.0.1-SNAPSHOT-capsule.jar

确保RESTful web api功能完备的最佳方法是向它发送几个请求:

  1. >  curl -X POST http://localhost:10900/api/people -H "Content-Type: applicationjson"

  2.     -d '{"email": "a@b.com", "firstName": "John", "lastName": "Smith"}'

  3. HTTP/1.1 201 Created

  4. Location: http://localhost:10900/api/people/a@b.com

  5. Content-Type: application/json

  6. {

  7.    "firstName":"John","

  8.    "lastName":"Smith",

  9.    "email":"a@b.com"

  10. }

如何确保Bean Validation 工作正常?为了触发它,让我们发送部分准备好的请求。

  1. >  curl  --X POST http://localhost:10900/api/people -H "Content-Type: applicationjson"

  2.     -d '{"firstName": "John", "lastName": "Smith"}'

  3. HTTP/1.1 400 Bad Request

  4. Content-Length: 0

OpenAPI规范和预捆绑的Swagger UI发行版也可以通过http://localhost:10900/index.html?url=http://localhost:10900/api/openapi.json获得。到目前为止,一切都很好,但公平地说,我们根本没有谈到测试我们的应用程序。要为添加一个person的场景设计出集成测试有多难呢?事实证明,围绕Java EE应用程序测试的框架已经有了很大的改进。特别是,使用Arquillian测试框架(以及受欢迎的JUnit和REST Assured)非常容易完成。一个真实的例子抵得上千言万语。

  1. @RunWith(Arquillian.class)

  2. @EnableRandomWebServerPort

  3. public class PeopleApiTest {

  4.    @ArquillianResource private URI uri;

  5.    @Deployment

  6.    public static JavaArchive createArchive() {

  7.        return ShrinkWrap

  8.            .create(JavaArchive.class)

  9.            .addClasses(PeopleResource.class, PeopleApplication.class)

  10.            .addClasses(PeopleServiceImpl.class, PeopleJpaRepository.class, PersistenceConfig.class)

  11.            .addPackages(true, "org.apache.deltaspike");

  12.    }

  13.    @Test

  14.    public void shouldAddNewPerson() throws Exception {

  15.        final Person person = new Person("a@b.com", "John", "Smith");

  16.        given()

  17.            .contentType(ContentType.JSON)

  18.            .body(person)

  19.            .post(uri + "/api/people")

  20.            .then()

  21.            .assertThat()

  22.            .statusCode(201)

  23.            .body("email", equalTo("a@b.com"))

  24.            .body("firstName", equalTo("John"))

  25.            .body("lastName", equalTo("Smith"));

  26.    }

  27. }

不神奇吗?实际上,开发现代Java EE应用程序是非常有趣的,有人可能会说,用Spring的方式!事实上,与Spring的相似之处并非巧合,因为它很有启发性,很有启发性,而且无疑将继续激励Java EE生态系统中的创新。未来如何?我认为,无论对于雅加达EE还是Eclipse Microprofile来说,都是光明的。后者刚刚接近2.0版本,提供了大量新的规范,这些规范旨在满足微服务体系结构的需求。目睹这些转变真是太棒了。项目的完整源代码可以在GitHub上找到。

原文链接:https://dzone.com/articles/building-enterprise-java-applications-the-spring-w

作者:Andriy Redko

译者:xieed

·END·

 近期热文:

  • 深入聊一聊 Spring AOP 实现机制

  • Spring Cloud Stream 学习小清单

  • 在一台Mac上不同平台同时使用多个Git账号

  • Git 版本控制之 GitFlow

  • 彻底搞懂 Git-Rebase

  • 我说分布式事务之最大努力通知型事务

  • 我说分布式事务之TCC

  • 不可错过的CMS学习笔记

  • 在生产中使用Java 11:需要了解的重要事项

  • 可能是最全面的G1学习笔记

看完,赶紧点个“好看”鸭

点鸭点鸭

↓↓↓↓

用Spring构建企业Java应用程序相关推荐

  1. idea 构建spring_以Spring方式构建企业Java应用程序

    idea 构建spring 我认为可以说Java EE在Java开发人员中享有很高的声誉. 尽管多年来确实在各个方面都有所改善,甚至将其改名为Eclipse Foundation成为Jakarta E ...

  2. 以Spring方式构建企业Java应用程序

    我认为可以肯定地说, Java EE在Java开发人员中享有很高的声誉. 尽管多年来确实在各个方面都有所改善,甚至将其改名为Eclipse Foundation成为Jakarta EE ,但其苦味仍然 ...

  3. 使用Java和Spring构建现代Web应用程序

    使用Spring Framework创建Java Web应用程序从未如此简单. 如果您已经熟悉Java并且几乎没有创建Web应用程序的经验,或者如果您担心所有很酷的孩子都放弃Java取而代之的是Rub ...

  4. Spring Boot –现代Java应用程序的基础

    Spring Boot是Spring.io中一个相对较新的项目. 其目的是简化创建新的基于Spring Framework的项目,并通过应用一些约定来统一其配置. 这种关于配置的方法约定已经成功地应用 ...

  5. 在独立Java应用程序中使用Tomcat JDBC连接池

    这是从我们的客人文章W4G伙伴克拉伦斯豪的作者临春3从A按. 您可能会在文章结尾找到本书的折扣券代码,仅适用于Java Code Geeks的读者! 请享用! 在需要数据访问权限的独立Java应用程序 ...

  6. 构建服务器集群感知的 Java 应用程序

    Mukul Gupta, 高级技术架构师, DGI Paresh Paladiya, 高级技术架构师, CGI 简介: 服务器集群对于高度可扩展的 Java 企业级应用程序开发已司空见惯,但是应用程序 ...

  7. 为什么我会在2012年的新企业Java项目中使用Java EE而不是Spring

    这个问题经常出现. 我的新项目也在2011年11月发布. 在这个新的Enterprise Java项目中,我将使用Java EE(JEE)代替Spring框架. 我知道:关于此主题的文章,博客和论坛讨 ...

  8. Java:现实世界中最流行的10个Java应用程序示例

    Java 是 DevOps.AI.机器学习和微服务的第一大编程语言.今天,Java 广泛用于企业应用程序和构建动态数字产品.它也是增强和虚拟现实.大数据和持续集成的有用技术.Java 生态系统是使用先 ...

  9. asp编程工具_asp.net core 成为构建企业首选

    微软开发了跨平台和开放源代码框架ASP.NET Core,用于构建现代的,基于云的和Internet连接的应用程序.它旨在使运行时组件,API,编译器和语言能够快速发展,并在.NET Core或.NE ...

最新文章

  1. ACM寒假训练第二周总结
  2. xampp python linux,Ubuntu的XAMPP着运行python脚本
  3. Paxos 实现日志复制同步(Multi-Paxos)
  4. CSP认证202012-2 期末预测之最佳阈值[C++题解]:遍历、前缀和
  5. wxWidgets:wxScrollBar类用法
  6. 设计延迟加载的“单例设计模式”
  7. 搭建服务器Apache+PHP+MySql需要注意的问题
  8. 【Flink】Flink写入es报错failed to get node info for request_id time out out after
  9. r矢量球坐标系旋度_三个常用坐标系的认识及矢量旋度表达式的证明
  10. OCR 工具tesseract初体验
  11. AWVS 13 Docker版本(破解后)
  12. Linux串口驱动分析及移植
  13. B端、C端产品的区别|产品经理面试题第1篇|||答题思路
  14. tomacat 上传图片崩溃问题
  15. 李开复写给女孩子们的
  16. 这三个文件在 C:\Program Files (x86)\Microsoft Office\root\Office16 下怎么会是符号链接?
  17. 为什么《大长今》是湖南卫视引入的?
  18. 博客园有一段时间登不上
  19. 中国雅虎推出雅虎统计服务
  20. 4N25光耦合器:简单的应用电路

热门文章

  1. 什么是 ClickHouse(实时数据分析数据库)
  2. new IntersectionObserver 使用笔记
  3. 只需 5 秒钟,你就能取到 WPF 程序的超高分辨率超高清截图
  4. 算法与数据结构 判断选择程序填空 绪论
  5. 虹软人脸识别中人脸追踪框框运动方向跟实际相反
  6. 2022年华为OD亲测招聘流程
  7. 通过Shell实现小火车效果
  8. python cv2实现视频抽帧
  9. 剑指offer 68. 骰子的点数
  10. html+css实战91-pxcook使用