从一次编译出发梳理概念: Jetty,Jersey,hk2,glassFish,Javax,Jakarta
0x00 摘要
本文借助一次开源项目的编译过程,梳理了一些java相关概念,与大家分享此文。
0x01 缘由
最近在编译蚂蚁金服的sofa-registry,因为不可名状的原因,无法完全下载依赖的maven包,所以只能手动一个一个下载。事实证明,这是一个痛苦的过程,因为各种java包环环相扣,于是一个个java相关概念跃入眼帘。索性把这些概念一一梳理下,与大家分享。
比如从下面 mvn dependency:tree
的输出结果看,我们遇到的概念或者名词就有:glassfish,javax.ws.rs,jersey,jetty,hk2,javax.inject,javax.annotation ......
[INFO] +- com.alipay.sofa:registry-remoting-http:jar:5.4.2:compile
[INFO] | +- org.glassfish.jersey.core:jersey-server:jar:2.26:compile
[INFO] | | +- org.glassfish.jersey.core:jersey-common:jar:2.26:compile
[INFO] | | | \- org.glassfish.hk2:osgi-resource-locator:jar:1.0.1:compile
[INFO] | | +- javax.ws.rs:javax.ws.rs-api:jar:2.1:compile
[INFO] | | +- org.glassfish.jersey.media:jersey-media-jaxb:jar:2.25.1:compile
[INFO] | | | \- org.glassfish.hk2:hk2-api:jar:2.5.0-b32:compile
[INFO] | | +- javax.annotation:javax.annotation-api:jar:1.2:compile
[INFO] | | \- org.glassfish.hk2.external:javax.inject:jar:2.5.0-b42:compile
[INFO] | +- org.glassfish.jersey.inject:jersey-hk2:jar:2.26:compile
[INFO] | | \- org.glassfish.hk2:hk2-locator:jar:2.5.0-b42:compile
[INFO] | | +- org.glassfish.hk2:hk2-utils:jar:2.5.0-b42:compile
[INFO] | | | \- javax.inject:javax.inject:jar:1:compile
[INFO] | | \- org.javassist:javassist:jar:3.21.0-GA:compile
[INFO] | +- org.eclipse.jetty:jetty-server:jar:9.4.19.v20190610:compile
0x02 概念
2.1 JSR
JSR 是 Java Specification Requests 的缩写,意思是Java 规范提案。是指向 JCP (Java Community Process)提出新增一个标准化技术规范的正式请求。任何人都可以提交JSR,以向Java平台增添新的API和服务。JSR已成为Java界的一个重要标准。
2.2 javax
java 和 javax 都是Java的API(Application Programming Interface)包,java是核心包,javax的x是extension的意思,也就是扩展包。
java类库是java发布之初就确定了的基础库,而javax类库则是在上面增加的一层东西,就是为了保持版本兼容要保存原来的,但有些东西有了更好的解决方案,所以就加上些。典型的就是awt(Abstract Windowing ToolKit) 和swing。
2.3 JSR311
2.3.1 JSR311
JSR311是java中实现Restful Web Service的API规范(JSR311: JAX-RS: The Java API for RESTful Web Services)。
JSR311有一个重要目标:使用注解(annotation)把POJO暴露成Web Service,这样就比较轻量级。
- jsr311-api - 这是JAX-RS 1.x系列的官方规范jar
- javax.ws.rs-api - 这是JAX-RS 2.x系列的官方规范jar
2.3.2 javax.ws.rs
java.ws.rs 是JAX-RS规范中定义的包名。
jax-rs
全称 Java API for RESTful Services,规范目前版本是 2.0。
jax-rs 中定义了:
一组启动方式
(以jee作为http容器,还是配合servlet作为http容器)一组注解
@GET, @POST, @DELETE, @PUT, @Consumes ...
通过 POJO Resource类, 提供Rest服务
就像 JSR 规范中定义了 Servlet 是 以继承 HttpServlet 并重写 doGet, doPost, do... 方法 一样。只要遵循 这套标准的 我们我们都可以称之为 Servlet 程序。你写的 Servlet 程序,可以不经过任何修改,放到任何实现 Servlet 容器中运行。类似,你写的 jax-rs 程序,可以不经任何修改,和任何 jax-rs 框架配合使用。
而 Spring MVC 是以 Servlet 为http容器,并自己构建了一套Api,没有遵循 jax-rs
规范。
2.3.3 框架
目前实现 jax-rs 标准的框架有很多:
- Apache CXF,开源的Web服务框架。
- Jersey, 由Sun提供的JAX-RS的参考实现。
- RESTEasy,JBoss的实现。
- Restlet,由Jerome Louvel和Dave Pawson开发,是最早的REST框架,先于JAX-RS出现。
- Apache Wink,一个Apache软件基金会孵化器中的项目,其服务模块实现JAX-RS规范
2.3.4 Jersey
Jersey 是 JAX-RS(JSR311)开源参考实现。
SpringMVC在开发REST应用时,是不支持 JSR311/JSR339 标准的。如果想要按照标准行事,最常用的实现了这两个标准的框架就是Jersey和CxF了。但因为Jersey是最早的实现,也是JSR311参考的主要对象,可以说Jersey就是事实上的标准(类似Hibernate是JPA的事实上的标准),也是现在使用最为广泛的REST开发框架之一。
Jersey用于构建 RESTful Web service。此外 Jersey 还提供一些额外的 API 和扩展机制,所以开发人员能够按照自己的需要对 Jersey 进行扩展。
sun.Jersey 和 glassfish.Jersey 是Jersey的两个版本,对应1.x和2.x,其中:
- 1.x中Jersey的包是以com.sun开头。
- 2.x是以org.glassfish为前缀。
2.4 JSR-330
2.4.1 JSR-330
JSR-330
是 Java
的依赖注入标准。定义了如下的术语描述依赖注入:
- A 类型依赖 B类型(或者说 B 被 A 依赖),则 A类型 称为”依赖(物) dependency”
- 运行时查找依赖的过程,称为”解析 resolving“依赖
- 如果找不到依赖的实例,称该依赖是”不能满足的 unsatisfied”
- 在”依赖注入 dependency injection”机制中,提供依赖的工具称为 ”依赖注入器 dependency injector”
2.4.2 javax.inject
标准对依赖注入的使用进行了定义, 但是对实现和配置未定义。Java EE
包javax.inject
对应此标准。其中也仅定义了依赖注入的使用(即通过注解),同样也未定义依赖注入的配置方式和实现方式。
javax.inject
提供如下5个注解(Inject、Qualifier、Named、Scope、Singleton)和1个接口(Provider)。
2.4.3 框架
支持JSR-330的框架有很多:
- Android下面的Dagger2就是基于这个规范。在dagger2 中用的JSR-330标准注释有:@Inject @Qualifier @Scope @Named等。
Guice
是一个由Google实现的针对Java 6以上版本的流行的、轻量级的DI框架。- 而其他的注入框架如Spring也支持JSR-330。
当使用JSR-330标准的注解时,了解其和Spring注解的不同点也是十分必要的,参考如下表。
@Autowired | @Inject | @Inject 注解没有required 属性,但是可以通过Java 8的Optional 取代 |
@Component | @Named | JSR_330标准并没有提供复合的模型,只有一种方式来识别组件 |
@Scope(“singleton”) | @Singleton | JSR-330默认的作用域类似Spring的prototype ,然而,为何和Spring的默认保持一致,JSR-330标准中的Bean在Spring中默认也是单例的。如果要使用非单例的作用域,开发者应该使用Spring的@Scope 注解。java.inject 也提供一个@Scope 注解,然而,这个注解仅仅可以用来创建自定义的作用域时才能使用。 |
@Qualifier | @Qualifier/@Named | javax.inject.Qualifier 仅仅是一个元注解,用来构建自定义限定符的。而String的限定符(比如Spring中的@Qualifier )可以通过javax.inject.Named 来实现 |
@Value | - | 不等价 |
@Required | - | 不等价 |
@Lazy | - | 不等价 |
ObjectFactory | Provider | javax.inject.Provider 是SpringObjectFactory 的另一个选择,通过get() 方法来代理,Provider 可以和Spring的@Autowired 组合使用 |
2.4.4 hk2
HK2是一个轻量级动态依赖注入框架,它是JSR-330的实现。
HK2的全称为“Hundred Kilobytes Kernel”,包括Modules Subsytem和Component Model两部分。SUN在其开源的GlassFish J2EE应用服务器项目中将HK2作为其系统内核实现。
在HK2组件模型中,一个组件的功能是通过服务接口-服务实现的模式声明的。一个HK2服务接口 标识并描述了一个构建模块或者应用程序扩展点。HK2服务实现实现了HK2服务接口。
hk2包为 org.glassfish.hk2。
2.5 JSR 250
2.5.1 JSR 250
JSR 250 规范包含用于将资源注入到端点实现类的注释和用于管理应用程序生命周期的注释。
2.5.2 javax.annotation
包含 JST 250 标准中的每一个注释的 Java 类的名称为 javax.annotation.xxx,其中 xxx 是“@”字符后面的注释的名称。 例如,@Resource 注释的 Java 类名为 javax.annotation.resource。
javax.annotation 中主要包含以下几个注解:
- @Generated:生成资源的注解,通过该项标记产生的实例是一个资源。类似于Spring中的@Bean注解,用于生成一向资源。
- @PostConstruct 创造资源之后的回调处理。
- @PreDestroy 销毁资源之前的回调处理。
- @Resource 标记使用资源的位置。功能上有些类似于@Autowired、@Inject,但是两者有不少的差别。
- @Resources 标记使用多项资源的位置,类似于使用@Autowired向一个列表装载数据。
2.5.3 框架
仔细看JSR-250定义的这些注解就会发现,他们都是关于“资源”的构建、销毁、使用的。
Spring实现了@PostConstruct、@PreDestroy和@Resource。
2.6 Jakarta
虽然Oracle 决定把 JavaEE 移交给开源组织 Eclipse 基金会,但是不希望 JavaEE 继续使用 Java 这个名字。于是 Eclipse 做了一项民意调查,最终 JakartaEE 已明显的优势胜出。因此Eclipse 宣布正式将 JavaEE 更名为 JakartaEE。
Eclipse基金会也对 Java EE 标准的每个规范进行了重命名,阐明了每个规范在Jakarta EE平台未来的角色。
新的名称Jakarta EE是Java EE的第二次重命名。2006年5月,“J2EE”一词被弃用,并选择了Java EE这个名称。在YouTube还只是一家独立的公司的时候,数字2就就从名字中消失了,而且当时冥王星仍然被认为是一颗行星。同样,作为Java SE 5(2004)的一部分,数字2也从J2SE中删除了,那时谷歌还没有上市。
因为不能再使用javax名称空间,Jakarta EE提供了非常明显的分界线。
- Jakarta 9(2019及以后)使用jakarta命名空间。
- Java EE 5(2005)到Java EE 8(2017)使用javax命名空间。
- Java EE 4使用javax命名空间。
提到java改名,我想起了javaeye网站更名为iteye之后,影响力急剧下降,令人扼腕。
2.7 GlassFish
Eclipse Foundation不只是发布规范。它还发布了Eclipse GlassFish 5.1,这是一个可立即运行的Jakarta EE 8实现。它还被认证为Jakarta EE 8平台的开源兼容实现。
GlassFish 是用于构建 Java EE 5应用服务器的开源开发项目的名称。它基于 Sun Microsystems 提供的 Sun Java System Application Server PE 9 的源代码以及 Oracle 贡献的 TopLink 持久性代码。该项目提供了开发高质量应用服务器的结构化过程,以前所未有的速度提供新的功能。这是对希望能够获得源代码并为开发 Sun 的下一代应用服务器(基于 GlassFish)作出贡献的 Java 开发者作出的回应。该项目旨在促进 Sun 和 Oracle 工程师与社区之间的交流,它将使得所有开发者都能够参与到应用服务器的开发过程中来。
过去,新EE功能诞生的过程称为Java Community Process。
Java SE今天仍然使用JCP。但是,由于EE已经改变了它的所有权,从Oracle到Eclipse Foundation,我们有一个新的独立流程。它是Eclipse Foundation Specification Process(EFSP),是Eclipse Development Process的扩展。
作为JCP的一部分,JSR需要一个具体的参考实现。这有点像实现接口的类。参考实现必须兼容以往库包或其他组织的开发人员创建自己的规范实现。
对于Java EE功能,JCP使用Glassfish作为其参考实现。
2.8 Jetty
Jetty 是一个开源的servlet容器,它为基于Java的web容器,例如JSP和servlet提供运行环境。Jetty是使用Java语言编写的,它的API以一组JAR包的形式发布。开发人员可以将Jetty容器实例化成一个对象,可以迅速为一些独立运行(stand-alone)的Java应用提供网络和web连接。由于其轻量、灵活的特性,Jetty也被应用于一些知名产品中,例如ActiveMQ、Maven、Spark、GoogleAppEngine、Eclipse、Hadoop等。
为什么使用Jetty?
- 异步的 Servlet,支持更高的并发量
- 模块化的设计,更灵活,更容易定制,也意味着更高的资源利用率
- 在面对大量长连接的业务场景下,Jetty 默认采用的 NIO 模型是更好的选择
- 将jetty嵌入到应用中,使一个普通应用可以快速支持 http 服务
2.9 概念关系
以上涉及概念中,若干关系如下( 只是大致逻辑示意图,不代表继承等关系 ):
+--------+ +--------+ +---------+ Jakarta
| JSR311 | | JSR330 | | JSR 250 |
+----+---+ +----+---+ +----+----+
| | |
v v v
+------+-----+ +------+-----+ +--------+-------+
|javax.ws.rs | |javax.inject| |javax.annotation|
+------+-----+ +------+-----+ +--------+-------+
| | |
| v |
| +-------+---------+ |
| +-----------+ |org.glassfish.hk2| |
| | +-----------------+ |
| | |
v v |
+------+-----+-------+ |
|org.glassfish.jersey| <----------------------------------------+
+-------------------++
|
v
+---+---+
| Jetty |
+-------+
0x03 在SOFARegistry的使用
我们来看看前面提到的概念中,其中几个在SOFARegistry中如何使用。
3.1 javax.ws.rs
javax.ws.rs是JSR311的包名。其重要目标是:使用注解(annotation)把POJO暴露成Web Service。
具体举例如下,可以看到 DecisionModeResource 已经被暴露成 Web Service:
package com.alipay.sofa.registry.server.meta.resource;
import javax.ws.rs.POST;
import javax.ws.rs.Path;
import javax.ws.rs.Produces;
import javax.ws.rs.core.MediaType;
@Path("decisionMode")
public class DecisionModeResource {
@Autowired
private MetaServerConfig metaServerConfig;
@POST
@Produces(MediaType.APPLICATION_JSON)
public Result changeDecisionMode(DecisionMode decisionMode) {
((MetaServerConfigBean) metaServerConfig).setDecisionMode(decisionMode);
Result result = new Result();
result.setSuccess(true);
return result;
}
}
3.2 jersey和jetty
因为jetty轻量级的特点,在SOFARegistry中,使用了 org.eclipse.jetty.server.Server,从而拉开了一场大戏。
com.alipay.sofa.registry.remoting.jersey.JerseyJettyServer
import javax.ws.rs.ProcessingException;
import org.eclipse.jetty.server.Connector;
import org.eclipse.jetty.server.ServerConnector;
import org.eclipse.jetty.util.thread.QueuedThreadPool;
import org.glassfish.jersey.internal.guava.ThreadFactoryBuilder;
import org.glassfish.jersey.jetty.JettyHttpContainer;
import org.glassfish.jersey.jetty.internal.LocalizationMessages;
import org.glassfish.jersey.process.JerseyProcessingUncaughtExceptionHandler;
import org.glassfish.jersey.server.ContainerFactory;
import org.glassfish.jersey.server.ResourceConfig;
import org.glassfish.jersey.server.spi.Container;
public class JerseyJettyServer implements Server {
private org.eclipse.jetty.server.Server server;
public static org.eclipse.jetty.server.Server createServer(final URI uri,
final ResourceConfig resourceConfig,
final boolean start) {
JettyHttpContainer handler =
ContainerFactory.createContainer(JettyHttpContainer.class, resourceConfig);
final org.eclipse.jetty.server.Server server = new org.eclipse.jetty.server.Server(
new JettyConnectorThreadPool());
server.setHandler(handler);
server.start();
}
}
3.3 hk2
HK2是一个轻量级动态依赖注入框架,它是JSR-330的实现。其应用十分广泛且底层,比如在 jersey 中就有各种直接或者间接的使用。
package org.glassfish.jersey.server;
...
import org.glassfish.jersey.internal.inject.Binder;
import org.glassfish.jersey.internal.inject.Bindings;
import org.glassfish.jersey.internal.inject.CompositeBinder;
import org.glassfish.jersey.internal.inject.InjectionManager;
import org.glassfish.jersey.internal.inject.Injections;
import org.glassfish.jersey.internal.inject.InstanceBinding;
import org.glassfish.jersey.internal.inject.Providers;
...
public final class ApplicationHandler implements ContainerLifecycleListener {
...
public ApplicationHandler(final Class<? extends Application> jaxrsApplicationClass) {
initialize(new ApplicationConfigurator(jaxrsApplicationClass), Injections.createInjectionManager(), null);
}
...
}
org.glassfish.jersey.internal.inject.*
是一个对hk2的封装,选取一个堆栈给大家看看,能够看到最终调用到了 hk2。
value:73, NamedImpl (org.glassfish.hk2.utilities)
invoke0:-1, NativeMethodAccessorImpl (sun.reflect)
invoke:62, NativeMethodAccessorImpl (sun.reflect)
invoke:43, DelegatingMethodAccessorImpl (sun.reflect)
invoke:498, Method (java.lang.reflect)
invoke:309, AnnotationLiteral (org.glassfish.hk2.api)
hashCode:242, AnnotationLiteral (org.glassfish.hk2.api)
hash:339, HashMap (java.util)
put:612, HashMap (java.util)
add:220, HashSet (java.util)
getThreeThirtyDescriptor:1282, Utilities (org.jvnet.hk2.internal)
initialize:76, ServiceLocatorGeneratorImpl (org.jvnet.hk2.external.generator)
create:103, ServiceLocatorGeneratorImpl (org.jvnet.hk2.external.generator)
internalCreate:312, ServiceLocatorFactoryImpl (org.glassfish.hk2.internal)
create:268, ServiceLocatorFactoryImpl (org.glassfish.hk2.internal)
createLocator:114, AbstractHk2InjectionManager (org.glassfish.jersey.inject.hk2)
<init>:86, AbstractHk2InjectionManager (org.glassfish.jersey.inject.hk2)
<init>:62, ImmediateHk2InjectionManager (org.glassfish.jersey.inject.hk2)
createInjectionManager:79, Hk2InjectionManagerFactory$Hk2InjectionManagerStrategy$1 (org.glassfish.jersey.inject.hk2)
create:97, Hk2InjectionManagerFactory (org.glassfish.jersey.inject.hk2)
createInjectionManager:93, Injections (org.glassfish.jersey.internal.inject)
<init>:282, ApplicationHandler (org.glassfish.jersey.server)
<init>:469, JettyHttpContainer (org.glassfish.jersey.jetty)
createContainer:61, JettyHttpContainerProvider (org.glassfish.jersey.jetty)
createContainer:82, ContainerFactory (org.glassfish.jersey.server)
createServer:100, JerseyJettyServer (com.alipay.sofa.registry.remoting.jersey)
startServer:83, JerseyJettyServer (com.alipay.sofa.registry.remoting.jersey)
......