https://mp.weixin.qq.com/s?__biz=MzU0MDEwMjgwNA==&mid=2247484965&idx=1&sn=ca6b847c65e5062036413ce203f77e97&chksm=fb3f1fdecc4896c805abffc1d4b294f2e10e7df7be02e20a95e6cfde2b8ae1ba705ced2a7008&scene=0&key=603c4c794d1e6753e5db1f9f14b6cc66b0ab45eaa6b2d94a7d2969275e5f606ac462a992e9841c07d7ddf86920207734839155714f7d9bba080fc36a92deabc821492123bccacd9b08b98cad7814bf5f&ascene=1&uin=MjgwMTEwNDQxNg%3D%3D&devicetype=Windows-QQBrowser&version=6103000b&lang=zh_CN&pass_ticket=9hOd6G19lRsZ4t2MwOpE8LWMk0vBDz2ra6SytTXe2yTgNcE5eevtRvju1kOKRf4r

https://github.com/reta/eclipse-microprofile-hammock

xieed SpringForAll社区 昨天

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

作者:Andriy Redko

译者:xieed

通过在本教程中构建一个简单的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: application\json" \

  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: application\json" \

  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上找到。

用Spring构建企业Java应用程序的方法的更多相关文章

  1. Spring MVC + Spring + Mybitis开发Java Web程序基础

    Spring MVC + Spring + Mybitis是除了SSH外的另外一种常见的web框架组合. Java web开发和普通的Java应用程序开发是不太一样的,下面是一个Java web开发在 ...

  2. 【Java】一个简单的Java应用程序

    简单记录,Java 核心技术卷I 基础知识(原书第10 版) 一个简单的Java应用程序"Hello, World!" Hello, World! Goodbye,World! 一 ...

  3. 一个简单的Java应用程序

    目录 一个简单的Java应用程序 首次运行结果 程序示例 运行结果 修改大小写之后的运行结果 程序示例 运行结果 关键字public 关键字class 类名及其命名规则 类名必须以字母开头 不能使用J ...

  4. 面向 Java 开发人员的 Ajax: 构建动态的 Java 应用程序

    面向 Java 开发人员的 Ajax: 构建动态的 Java 应用程序 Ajax 为更好的 Web 应用程序铺平了道路 在 Web 应用程序开发中,页面重载循环是最大的一个使用障碍,对于 Java™ ...

  5. Jenkins 构建运行java程序

    我们将在Jenkins建立执行一个简单的 HelloWorld 应用程序,构建和运行Java程序.打开网址:http://localhost:8080/jenkins 第1步- 转到Jenkins 仪 ...

  6. SSH:Struts + Spring + Hibernate 轻量级Java EE企业框架

    Java EE(Java Platform,Enterprise Edition)是sun公司(2009年4月20日甲骨文将其收购)推出的企业级应用程序版本.这个版本以前称为 J2EE.能够帮助我们开 ...

  7. 使用 Acegi 保护 Java 应用程序

    第 1 部分: 架构概览和安全过滤器 Acegi Security System 是一种功能强大并易于使用的替代性方案,使您不必再为 Java 企业应用程序编写大量的安全代码.虽然它专门针对使用 Sp ...

  8. Java后端程序员都做些什么?

    这个问题来自于QQ网友,一句两句说不清楚,索性写个文章. 我刚开始做Web开发的时候,根本没有前端,后端之说. 原因很简单,那个时候服务器端的代码就是一切:接受浏览器的请求,实现业务逻辑,访问数据库, ...

  9. Java 初中级程序员如何快速成长???

    Java 技术学习路线 Java 语言是一门非常流行和重要的语言,目前仍是需求量很大的语言,应用范围很广的语言,在企业级开发.移动开发.大数据云计算.人工智能等领域都有大量的应用. 怎么样学习好 Ja ...

随机推荐

  1. Yapi部署说明

    1.环境搭建 确保 node 版本=> 7.6,请运行 node -v 查看版本号 确保 mongodb 版本 => 2.6,请运行 mongo --version 查看版本号 确保安装了 ...

  2. MySQL的自动提交模式

      默认情况下, MySQL启用自动提交模式(变量autocommit为ON).这意味着, 只要你执行DML操作的语句,MySQL会立即隐式提交事务(Implicit Commit).这个跟SQL S ...

  3. Mysql中联合索引的最左匹配原则

    在Mysql建立多列索引(联合索引)有最左前缀的原则,即最左优先. 如果我们建立了一个2列的联合索引(col1,col2),实际上已经建立了两个联合索引(col1).(col1,col2); 如果有一 ...

  4. Left Jion和where区别

    首先,新建两张表A和B,然后插入6条数据到A表,3条数据到B表.语句如下: create table A( ID ,) not null, AName ) null ) create table B( ...

  5. RH2288V3服务器 硬件安装以及更换硬件

    滑道类型:L型滑道.可伸缩滑道.报轨 L型滑道:只适用于华为机柜. 准备工具:防静电带或者防静电手套.螺丝刀.浮动螺母安装条. 安装服务器步骤: 1.安装L型滑道   2.安装浮动螺母  3.安装服务 ...

  6. PHP实现表单提交发送邮件

    只需要三个文件就可以了: 注意: 文件自命名需修改表单提交url,包含的类文件名: HTML表单文件: <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML ...

  7. 【任务】信息检索.MOOC学习

    [博客导航] [信息检索导航] 任务 18年12月4日开始,快速浏览,学习中国大学MOOC平台上黄如花老师的<信息检索>课程. 关键动力 0.搜索是最基础的能力,需要系统学习并应用. 1. ...

  8. go语言之行--golang操作redis、mysql大全

    一.redis 简介 redis(REmote DIctionary Server)是一个由Salvatore Sanfilippo写key-value存储系统,它由C语言编写.遵守BSD协议.支持网 ...

  9. c++入门之函数指针和函数对象

    函数指针可以方便我们调用函数,但采用函数对象,更能体现c++面向对象的程序特性.函数对象的本质:()运算符的重载.我们通过一段代码来感受函数指针和函数对象的使用: int AddFunc(int a, ...

  10. app.use和app.get,app.post区别

    express中,express的实例app: app.use(path,callback)中的callback既可以是router对象又可以是函数 app.get(path,callback)中的c ...