用Spring构建企业Java应用程序的方法
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
原文链接: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作为不太相关的细节):
@Entity@Table(name = "people")public class PersonEntity {@Id @Column(length = 256)private String email;@Column(nullable = false, length = 256, name = "first_name")private String firstName;@Column(nullable = false, length = 256, name = "last_name")private String lastName;@Versionprivate Long version;}
它只有一个绝对最小的属性集。JPA存储库非常简单,实现了一组典型的CRUD方法。
@ApplicationScoped@EntityManagerConfig(qualifier = PeopleDb.class)public class PeopleJpaRepository implements PeopleRepository {@Inject @PeopleDb private EntityManager em;@Override@Transactional(readOnly = true)public Optional<PersonEntity> findByEmail(String email) {final CriteriaBuilder cb = em.getCriteriaBuilder();final CriteriaQuery<PersonEntity> query = cb.createQuery(PersonEntity.class);final Root<PersonEntity> root = query.from(PersonEntity.class);query.where(cb.equal(root.get(PersonEntity_.email), email));try {final PersonEntity entity = em.createQuery(query).getSingleResult();return Optional.of(entity);} catch (final NoResultException ex) {return Optional.empty();}}@Override@Transactionalpublic PersonEntity saveOrUpdate(String email, String firstName, String lastName) {final PersonEntity entity = new PersonEntity(email, firstName, lastName);em.persist(entity);return entity;}@Override@Transactional(readOnly = true)public Collection<PersonEntity> findAll() {final CriteriaBuilder cb = em.getCriteriaBuilder();final CriteriaQuery<PersonEntity> query = cb.createQuery(PersonEntity.class);query.from(PersonEntity.class);return em.createQuery(query).getResultList();}@Override@Transactionalpublic Optional<PersonEntity> deleteByEmail(String email) {return findByEmail(email).map(entity -> {em.remove(entity);return entity;});}}
事务管理(即@Transactionalannotation)需要一些解释。在典型的Java EE应用程序中,容器运行时负责管理事务。由于我们不想装载应用程序容器,而是保持精简,所以我们可以使用EntityManager来启动/提交/回滚事务。这当然是可行的,但它也会用样板污染代码。可以说,更好的选择是使用Apache DeltaSpikeCDI扩展用于声明性事务管理(这是@Transactional和@EntityManagerConfig注释的来源)。下面的代码片段说明了如何集成它。
@ApplicationScopedpublic class PersistenceConfig {@PersistenceUnit(unitName = "peopledb")private EntityManagerFactory entityManagerFactory;@Produces @PeopleDb @TransactionScopedpublic EntityManager create() {return this.entityManagerFactory.createEntityManager();}public void dispose(@Disposes @PeopleDb EntityManager entityManager) {if (entityManager.isOpen()) {entityManager.close();}}}
太棒了——最难的部分已经过去了!接下来是person数据传输对象和服务层。
public class Person {@NotNull private String email;@NotNull private String firstName;@NotNull private String lastName;}
老实说,为了使示例应用程序尽可能小,我们可以完全跳过服务层,直接进入存储库。但总的来说,这不是一个很好的实践,所以让我们介绍PeopleServiceImpl。
@ApplicationScopedpublic class PeopleServiceImpl implements PeopleService {@Inject private PeopleRepository repository;@Overridepublic Optional<Person> findByEmail(String email) {return repository.findByEmail(email).map(this::toPerson);}@Overridepublic Person add(Person person) {return toPerson(repository.saveOrUpdate(person.getEmail(), person.getFirstName(), person.getLastName()));}@Overridepublic Collection<Person> getAll() {return repository.findAll().stream().map(this::toPerson).collect(Collectors.toList());}@Overridepublic Optional<Person> remove(String email) {return repository.deleteByEmail(email).map(this::toPerson);}private Person toPerson(PersonEntity entity) {return new Person(entity.getEmail(), entity.getFirstName(), entity.getLastName());}}
剩下的部分是JAX-RS应用程序和资源的定义。
@Dependent@ApplicationPath("api")@OpenAPIDefinition(info = @Info(title = "People Management Web APIs",version = "1.0.0",license = @License(name = "Apache License",url = "https://www.apache.org/licenses/LICENSE-2.0")))public class PeopleApplication extends Application {}
没什么好说的;这是尽可能简单的。但是JAX-RS资源实现更有趣(OpenAPI注释占据了大部分位置)。
@ApplicationScoped@Path( "/people" )@Tag(name = "people")public class PeopleResource {@Inject private PeopleService service;@Produces(MediaType.APPLICATION_JSON)@GET@Operation(description = "List all people",responses = {@ApiResponse(content = @Content(array = @ArraySchema(schema = @Schema(implementation = Person.class))),responseCode = "200")})public Collection<Person> getPeople() {return service.getAll();}@Produces(MediaType.APPLICATION_JSON)@Path("/{email}")@GET@Operation(description = "Find person by e-mail",responses = {@ApiResponse(content = @Content(schema = @Schema(implementation = Person.class)),responseCode = "200"),@ApiResponse(responseCode = "404",description = "Person with such e-mail doesn't exists")})public Person findPerson(@Parameter(description = "E-Mail address to lookup for", required = true) @PathParam("email") final String email) {return service.findByEmail(email).orElseThrow(() -> new NotFoundException("Person with such e-mail doesn't exists"));}@Consumes(MediaType.APPLICATION_JSON)@Produces(MediaType.APPLICATION_JSON)@POST@Operation(description = "Create new person",requestBody = @RequestBody(content = @Content(schema = @Schema(implementation = Person.class)),),responses = {@ApiResponse(content = @Content(schema = @Schema(implementation = Person.class)),headers = @Header(name = "Location"),responseCode = "201"),@ApiResponse(responseCode = "409",description = "Person with such e-mail already exists")})public Response addPerson(@Context final UriInfo uriInfo,@Parameter(description = "Person", required = true) @Valid Person payload) {final Person person = service.add(payload);return Response.created(uriInfo.getRequestUriBuilder().path(person.getEmail()).build()).entity(person).build();}@Path("/{email}")@DELETE@Operation(description = "Delete existing person",responses = {@ApiResponse(responseCode = "204",description = "Person has been deleted"),@ApiResponse(responseCode = "404",description = "Person with such e-mail doesn't exists")})public Response deletePerson(@Parameter(description = "E-Mail address to lookup for", required = true ) @PathParam("email") final String email) {return service.remove(email).map(r -> Response.noContent().build()).orElseThrow(() -> new NotFoundException("Person with such e-mail doesn't exists"));}}
这样,我们就完成了!但是,我们怎样才能把这些零件组装起来,然后用电线把它们连在一起呢?现在是 Microprofile进入舞台的时候了。有许多实现可供选择;我们将在这篇文章中使用的是Project Hammock 。我们要做的唯一一件事就是指定我们想要使用的CDI 2.0、JAX-RS 2.1和JPA 2.0实现,它们分别转换为Weld、Apache CXF和OpenJPA(通过 Project Hammock 依赖关系表示)。让我们来看看Apache Mavenpom.xml文件。
<properties><deltaspike.version>1.8.1</deltaspike.version><hammock.version>2.1</hammock.version></properties><dependencies><dependency><groupId>org.apache.deltaspike.modules</groupId><artifactId>deltaspike-jpa-module-api</artifactId><version>${deltaspike.version}</version><scope>compile</scope></dependency><dependency><groupId>org.apache.deltaspike.modules</groupId><artifactId>deltaspike-jpa-module-impl</artifactId><version>${deltaspike.version}</version><scope>runtime</scope></dependency><dependency><groupId>ws.ament.hammock</groupId><artifactId>dist-microprofile</artifactId><version>${hammock.version}</version></dependency><dependency><groupId>ws.ament.hammock</groupId><artifactId>jpa-openjpa</artifactId><version>${hammock.version}</version></dependency><dependency><groupId>ws.ament.hammock</groupId><artifactId>util-beanvalidation</artifactId><version>${hammock.version}</version></dependency><dependency><groupId>ws.ament.hammock</groupId><artifactId>util-flyway</artifactId><version>${hammock.version}</version></dependency><dependency><groupId>ws.ament.hammock</groupId><artifactId>swagger</artifactId><version>${hammock.version}</version></dependency></dependencies>
在没有进一步的ado的情况下,让我们立即构建和运行应用程序(如果您想知道应用程序使用的是什么关系数据存储,那么它是H2,在内存中配置了数据库)。
mvn clean packagejava -jar target/eclipse-microprofile-hammock-0.0.1-SNAPSHOT-capsule.jar
确保RESTful web api功能完备的最佳方法是向它发送几个请求:
> curl -X POST http://localhost:10900/api/people -H "Content-Type: application\json" \-d '{"email": "a@b.com", "firstName": "John", "lastName": "Smith"}'HTTP/1.1 201 CreatedLocation: http://localhost:10900/api/people/a@b.comContent-Type: application/json{"firstName":"John",""lastName":"Smith","email":"a@b.com"}
如何确保Bean Validation 工作正常?为了触发它,让我们发送部分准备好的请求。
> curl --X POST http://localhost:10900/api/people -H "Content-Type: application\json" \-d '{"firstName": "John", "lastName": "Smith"}'HTTP/1.1 400 Bad RequestContent-Length: 0
OpenAPI规范和预捆绑的Swagger UI发行版也可以通过http://localhost:10900/index.html?url=http://localhost:10900/api/openapi.json获得。到目前为止,一切都很好,但公平地说,我们根本没有谈到测试我们的应用程序。要为添加一个person的场景设计出集成测试有多难呢?事实证明,围绕Java EE应用程序测试的框架已经有了很大的改进。特别是,使用Arquillian测试框架(以及受欢迎的JUnit和REST Assured)非常容易完成。一个真实的例子抵得上千言万语。
@RunWith(Arquillian.class)@EnableRandomWebServerPortpublic class PeopleApiTest {@ArquillianResource private URI uri;@Deploymentpublic static JavaArchive createArchive() {return ShrinkWrap.create(JavaArchive.class).addClasses(PeopleResource.class, PeopleApplication.class).addClasses(PeopleServiceImpl.class, PeopleJpaRepository.class, PersistenceConfig.class).addPackages(true, "org.apache.deltaspike");}@Testpublic void shouldAddNewPerson() throws Exception {final Person person = new Person("a@b.com", "John", "Smith");given().contentType(ContentType.JSON).body(person).post(uri + "/api/people").then().assertThat().statusCode(201).body("email", equalTo("a@b.com")).body("firstName", equalTo("John")).body("lastName", equalTo("Smith"));}}
不神奇吗?实际上,开发现代Java EE应用程序是非常有趣的,有人可能会说,用Spring的方式!事实上,与Spring的相似之处并非巧合,因为它很有启发性,很有启发性,而且无疑将继续激励Java EE生态系统中的创新。未来如何?我认为,无论对于雅加达EE还是Eclipse Microprofile来说,都是光明的。后者刚刚接近2.0版本,提供了大量新的规范,这些规范旨在满足微服务体系结构的需求。目睹这些转变真是太棒了。项目的完整源代码可以在GitHub上找到。
用Spring构建企业Java应用程序的方法的更多相关文章
- Spring MVC + Spring + Mybitis开发Java Web程序基础
Spring MVC + Spring + Mybitis是除了SSH外的另外一种常见的web框架组合. Java web开发和普通的Java应用程序开发是不太一样的,下面是一个Java web开发在 ...
- 【Java】一个简单的Java应用程序
简单记录,Java 核心技术卷I 基础知识(原书第10 版) 一个简单的Java应用程序"Hello, World!" Hello, World! Goodbye,World! 一 ...
- 一个简单的Java应用程序
目录 一个简单的Java应用程序 首次运行结果 程序示例 运行结果 修改大小写之后的运行结果 程序示例 运行结果 关键字public 关键字class 类名及其命名规则 类名必须以字母开头 不能使用J ...
- 面向 Java 开发人员的 Ajax: 构建动态的 Java 应用程序
面向 Java 开发人员的 Ajax: 构建动态的 Java 应用程序 Ajax 为更好的 Web 应用程序铺平了道路 在 Web 应用程序开发中,页面重载循环是最大的一个使用障碍,对于 Java™ ...
- Jenkins 构建运行java程序
我们将在Jenkins建立执行一个简单的 HelloWorld 应用程序,构建和运行Java程序.打开网址:http://localhost:8080/jenkins 第1步- 转到Jenkins 仪 ...
- SSH:Struts + Spring + Hibernate 轻量级Java EE企业框架
Java EE(Java Platform,Enterprise Edition)是sun公司(2009年4月20日甲骨文将其收购)推出的企业级应用程序版本.这个版本以前称为 J2EE.能够帮助我们开 ...
- 使用 Acegi 保护 Java 应用程序
第 1 部分: 架构概览和安全过滤器 Acegi Security System 是一种功能强大并易于使用的替代性方案,使您不必再为 Java 企业应用程序编写大量的安全代码.虽然它专门针对使用 Sp ...
- Java后端程序员都做些什么?
这个问题来自于QQ网友,一句两句说不清楚,索性写个文章. 我刚开始做Web开发的时候,根本没有前端,后端之说. 原因很简单,那个时候服务器端的代码就是一切:接受浏览器的请求,实现业务逻辑,访问数据库, ...
- Java 初中级程序员如何快速成长???
Java 技术学习路线 Java 语言是一门非常流行和重要的语言,目前仍是需求量很大的语言,应用范围很广的语言,在企业级开发.移动开发.大数据云计算.人工智能等领域都有大量的应用. 怎么样学习好 Ja ...
随机推荐
- c/c++ 多线程 参数传递
多线程 参数传递 1,值传递,拷贝一份新的给新的线程.线程1中有个int变量a,在线程1中启动线程2,参数是a的值,这时就会拷贝a,线程1和线程2不共享a. 2,引用传递,不拷贝一份新的给新的线程.线 ...
- Centos7安装搭建NTP服务器和NTP客户端同步时间
NTP简介: NTP是网络时间协议(Network Time Protocol),它是用来同步网络中各个计算机的时间的协议. 在计算机的世界里,时间非常地重要 例如:对于火箭发射这种科研活动,对时间的 ...
- 让 Windows7 - 64bit 支持 VC++ 6.0 的解决方法(无法启动此程序,因为计算机中丢失 MSVCRTD.dll。尝试重新安装该程序以解决此问题)
源地址:https://www.cnblogs.com/poissonnotes/p/4372136.html 无法启动此程序,因为计算机中丢失 MSVCRTD.dll.尝试重新安装该程序以解决此问题 ...
- LeetCode算法题-Design HashSet(Java实现)
这是悦乐书的第298次更新,第317篇原创 01 看题和准备 今天介绍的是LeetCode算法题中Easy级别的第166题(顺位题号是705).不使用任何内建的hash表库设计一个hash集合,应包含 ...
- Python操作MySQL:pymysql模块
连接MySQL有两个模块:mysqldb和pymysql,第一个在Python3.x上不能用,所以我们学pymysql import pymysql # 创建连接 conn = pymysql.con ...
- yuan 老师 之 Django
前端: 1.前端基础之JavaScript https://www.cnblogs.com/yuanchenqi/articles/6893904.html 2.前端基础之jQuery https:/ ...
- C#基础知识之类和结构体
虽然项目中一直在使用类.结构体等类型,仔细琢磨,还真无法系统的说出个所以然.记录一下类.结构体.类和结构体区别 一.类 对于类,大家都特别熟悉.简单的介绍一下类的结构,然后记录一下Class需要注意的 ...
- Spring-扫描注解原理,注解自动扫描原理分析
注解自动扫描原理分析 在spring的配置文件中加入如下代码,spring便开启了自动扫描,那么它的底层到底是如何实现的呢? <context:component-scan base-packa ...
- vue.js sha256加密
sha256: 1.使用cnpm安装 :cnpm install js-sha256 2.然后在组件中methods定义方法,在调用 let sha256 = require("js-sha ...
- 19.java反射入门
一.反射机制是什么反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法:对于任意一个对象,都能够调用它的任意一个方法和属性:这种动态获取的信息以及动态调用对象的方法的功能称为jav ...