一、前言
本文仅代表作者的个人观点;
本文在书写过程中,得到了同事kylin和shuli的指导,在此表示感谢;
本文的内容仅限于技术探讨,不能作为指导生产环境的素材;
本文素材是红帽公司产品技术和手册;
本文分为系列文章,将会有多篇,初步预计将会有9篇。
一.上篇回顾
在上一篇中,我们介绍了Java的基础,并通过maven编译和运行一个Java应用。具体而言:
三.应用服务器到底是个啥?
应用程序服务器是一个软件组件,提供必要的运行时环境和基础结构来托管和管理Java EE企业应用程序。 应用程序服务器提供诸如并发性、分布式组件架构、多平台可移植性、事务管理、Web服务、数据库对象关系映射(ORM)、异步消息传递以及企业应用程序安全性等功能。
在Java SE应用程序中,这些功能必须由开发人员手动实现,这很耗时且难以正确实现。
下图展示的就是JavaSE和Java EE应用部署的区别。简单而言,JavaSE应用,我们可以通过java -jar直接运行;而Java EE应用,需要部署到app server上去运行。
JBoss企业应用平台7,JBoss EAP 7或简称EAP,是一个用于托管和管理Java EE应用程序的应用程序服务器。EAP 7建立在基于Wildfly开源软件的开放标准上,并提供以下功能:
EAP体系结构的一个重要概念是模块的概念。模块提供了由EAP服务或应用程序使用的代码(Java Classes)。
其实这点也好理解,我们写个java程序,很多时候需要import类来做一些事情。举个简单的代码。
代码要实现的效果:
A:键盘录入月份的值,所以我们要使用Scanner。
B:我们应该判断这个月份在那个季节
我们看一下源码:
我们可以看到,第一行是import java.util.Scanner,这其实就是导入键盘录入功能的包。这个包是在SDK中。
成功编译:
运行应用,我们看到,import的包起作用了:
在EAP中,模块被加载到独立的类加载器中,并且只有在明确请求时才能看到来自其他模块的类。这意味着可以实现一个模块,而不用担心与其他模块的实施可能产生冲突。在EAP中运行的所有代码(包括由核心提供的代码)都在模块内部运行。这包括应用程序代码,这意味着应用程序彼此隔离并且来自EAP服务。
这种模块化体系结构允许对代码可视性进行非常细致的控制。应用程序可以看到一个暴露特定版本的API的模块,而另一个应用程序可能会看到另一个暴露不同版本的相同API的模块。
应用程序开发人员可以手动控制此可见性,并且在某些情况下它可能非常有用。但是对于大多数常见情况,EAP 7会根据其对Java EE API的使用情况自动决定向应用程序公开哪些模块。
四. 两种容器
我们在中间件中说的容器,指的不是时下很火的lxc、Docker等。这里的容器是指:应用程序服务器中的逻辑组件,为应用程序服务器上部署的应用程序提供运行时上下文。容器充当应用程序组件与应用程序服务器提供的低级基础架构服务之间的接口。
应用程序中的不同类型的组件有不同的容器。应用程序组件部署到容器并可用于其他部署。部署基于部署描述符(与代码一起打包的XML配置文件)或代码级别注释,指示应该如何部署和配置组件。
Java EE应用程序服务器中有两种主要的容器类型(我们可以简单地把容器理解成线程池):
容器负责安全性、事务处理、JNDI查找和远程连接等。容器还可以管理运行时服务,例如EJB和Web组件生命周期,数据源池,数据持久性和JMS消息传递。例如,Java EE规范允许您声明性地配置安全性,以便只有授权用户才能调用应用程序组件提供的功能。此限制使用XML部署描述符或代码中的注释进行配置。此元数据在部署时由容器读取,并相应地配置组件。
五、Java EE 7配置文件
Java EE应用程序服务器上下文中的配置文件,是一组针对特定应用程序类型的组件API。配置文件是Java EE 6中引入的一个新概念。目前在Java EE 7中定义了两个配置文件,而JBoss EAP应用程序服务器完全支持这两个配置文件:
有超过30种不同的技术构成了Java EE的完整配置文件。每种技术都有自己的JSR规范和版本号。通常是结合使用:它们允许Java EE应用程序连接到数据库、发布和使用Web服务、提供Web应用程序、执行事务、实施安全策略以及连接到大量外部资源,以执行诸如消息传递,命名,发送电子邮件以及与非Java应用程序通信。
Web配置文件包含Web开发人员常用的Java EE的基于Web的技术,如Servlet、Java Server Pages、Java Server Faces、CDI、JPA、JAX-RS、WebSockets和Enterprise Java Beans的限制版本(EJB),称为EJB Lite。
六、识别JNDI资源
在跨多个服务器、运行不同组件的分布式多层应用程序中,组件需要相互通信。例如,Java客户端可能调用部署在单独机器上的EJB上的方法,并且EJB组件与数据库通信以检索数据。 Java命名和目录接口(JNDI)是目录服务(用于查找资源)的Java API,允许组件通过逻辑名称发现和查找对象。
资源是一个逻辑对象,可以被Java EE应用程序中的组件查找和使用。每个资源都由唯一名称标识,称为JNDI名称或JNDI资源绑定。例如,JBoss EAP默认提供的Java Database Connectivity(JDBC)数据源的JNDI名称(指向嵌入式H2数据库)为java:jboss /datasources / ExampleDS。
JNDI资源不仅限于JDBC数据源。可以配置多种类型的资源,例如JMS ConnectionFactory对象,消息传递队列和主题,电子邮件服务器,线程池等。
每个不同的JNDI绑定都是在逻辑名称空间下组织的,通常称为JNDI树。以下是JBoss EAP应用程序服务器中最常见的一些命名空间:
JDBC数据源在java:jboss/datasources / *名称空间下注册。
JMS相关资源在java:jboss / jms / *命名空间下注册(在java下的JMS队列:jboss / jms / queue / *和java下的主题:jboss / jms / topic / *)。
与电子邮件相关的资源在java:jboss / mail / *命名空间下注册。
线程和并发相关资源在java:jboss / ee / concurrency / *名称空间下注册。
七、使用CDI进行资源注入
Java EE 7提供上下文和依赖注入(CDI),使组件无需手动实例化服务器资源或组件对象,即可获取对其他组件对象的引用以及应用程序服务器资源。 这使得松散耦合的架构成为可能,客户端不需要知道被调用对象的所有底层实现细节。
在应用程序服务器级别配置所需的JNDI资源绑定后,可以使用@Resource注释将资源注入到需要资源的应用程序中。 应用程序服务器在运行时实例化资源并提供对资源的引用。
例如,我们已在EAP配置文件中配置了如下的JDBC数据源绑定:
<subsystem xmlns="urn:jboss:domain:datasources:4.0">
<datasources>
<datasource jndi-name="java:jboss/datasources/ExampleDS" pool-name="ExampleDS" enabled="true" use-java-context="true">
<connection-url>jdbc:h2:mem:test;DB_CLOSE_DELAY=-1;DB_CLOSE_ON_EXIT=FALSE</connection-url>
<driver>h2</driver>
<security>
<user-name>sa</user-name>
<password>sa</password>
</security>
</datasource>
</datasources>
...
我们可以将java:jboss / datasources / ExampleDS数据源注入到应用程序中,如下所示:
public class TestDS {
@Resource(name="java:jboss/datasources/ExampleDS")
private javax.sql.DataSource ds;
// Use the DataSource reference to create a Connection etc..
如果我们在EAP中设置了类似以下的JMS队列资源:
<jms-queue name="helloWorldQueue" entries="java:jboss/jms/queue/helloWorldQueue"/>
我们可以通过将资源注入到JMS客户端类来将消息发送到此队列:
@Resource(mappedName = "java:jboss/jms/queue/helloWorldQueue")private Queue helloWorldQueue;
@Inject
JMSContext context;
// Use the Queue object to send messages...
try {
context.createProducer().send(helloWorldQueue, "Hello World!");
...
}
接下来,我们实验验证JNDI
在实验环境启动JBoss EAP:
EAP启动成功以后,我们接下来看JNDI。
应用程序服务器维护一个JNDI资源绑定列表。 应用程序需要的资源(例如邮件,JDBC数据源和JMS连接工厂和队列)绑定到各自名称空间下的唯一可识别名称。
JDBC数据源绑定到java:jboss / datasources / *名称空间。 在/opt/jboss-eap-7.0/standalone/log/server.log文件中,确认您可以看到以下两个数据源绑定:
ExampleDS绑定指向EAP附带的嵌入式H2数据库。MySQLDS绑定指向一个MySQL数据库,该数据库将将会被下面一小节的JavaEE应用使用。。
八、打包并部署一个Java EE应用
Java EE应用程序可以以不同的方式打包,以部署到兼容的应用程序服务器。 根据应用程序类型及其包含的组件,可以将应用程序打包到不同的部署类型(包含类,应用程序资产和XML部署描述符的压缩存档文件)中。 三种最常见的部署类型是:
JAR文件:JAR文件可以包含Plain Old Java Object(POJO)类,JPA Entity Beans、实用程序Java类、EJB和MDB。 部署到应用程序服务器时,根据JAR文件内部组件的类型,应用程序服务器会查找XML部署描述符或代码级别注释,并相应地部署每个组件。
WAR文件:WAR文件用于打包Web应用程序。 它可以包含一个或多个JAR文件,以及WEB-INF或WEB-INF / classes / META-INF文件夹下的XML部署描述符文件。
EAR文件:EAR文件包含多个JAR和WAR文件,以及META-INF文件夹中的XML部署描述符。
XML部署描述符(如果存在)会覆盖代码级别注释。 对于给定的组件,避免在两个地方重复配置。
Maven提供了几个有用的插件来简化在开发生命周期中对EAP的打包和部署(这些插件都是widfly的,就觉定了war包默认后续会部署到EAP上)。
如果遵循Maven标准源代码布局,maven-war-plugin会从应用程序创建WAR文件。 maven-war-plugin可以在Maven pom.xml文件的<build>部分中声明:
<build>
<finalName>todo</finalName>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-war-plugin</artifactId>
<version>${version.war.plugin}</version>
<extensions>false</extensions>
<configuration>
<failOnMissingWebXml>false</failOnMissingWebXml>
</configuration>
</plugin>
</plugins>
</build>
同样,maven-ear-plugin从应用程序源代码创建EAR文件。 它在Maven pom.xml文件的<build>部分中声明。 您需要使用<webModule>标记指示应该打包在EAR文件内的WAR文件:
<build>
<finalName>todo</finalName>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-ear-plugin</artifactId>
<version>${version.ear.plugin}</version>
<configuration>
<version>6</version>
<defaultLibBundleDir>lib</defaultLibBundleDir>
<modules>
<webModule>
<groupId>com.redhat.training</groupId>
<artifactId>todojee-web</artifactId>
<contextRoot>/todo-ear</contextRoot>
</webModule>
</modules>
<fileNameMapping>no-version</fileNameMapping>
</configuration>
</plugin>
</plugins>
</build>
可以使用Maven通过wildfly-maven插件将应用程序部署到JBoss EAP,该插件提供了部署和取消部署应用程序到EAP的功能。 它支持部署所有三种部署格式:JAR,WAR和EAR。 您可以在项目的Maven pom.xml文件中声明插件:
<plugin>
<groupId>org.wildfly.plugins</groupId>
<artifactId>wildfly-maven-plugin</artifactId>
<version>${version.wildfly.maven.plugin}</version>
</plugin>
要将应用程序构建,打包并部署到EAP,请从项目根文件夹运行以下命令:
$ mvn clean package wildfly:deploy
要从EAP取消部署应用程序,请从项目根文件夹运行以下命令:
$ mvn wildfly:undeploy
九、实验验证:打包和部署一个J2EE应用
通过JBDS,导入一个已经存在的maven项目,并加载:
接下来,增加应用服务器:
选择启动EAP:
接下来,通过maven编译应用,并直接部署到EAP上:
应用部署完毕以后,通过浏览器可以进行访问:
部署成功!
我们在web上增加一条任务:Earn lots of money:
然后查看EAP的日志,可以看到增加任务的操作,并且把增加的内容通过调用JNDI写入到了后端数据库。
魏新宇