1.容器的历史

容器概念始于 1979 年提出的 UNIX chroot,它是一个 UNIX 操作系统的系统调用,将一个进程及其子进程的根目录改变到文件系统中的一个新位置,让这些进程只能访问到这个新的位置,从而达到了进程隔离的目的。

2000 年的时候 FreeBSD 开发了一个类似于 chroot 的容器技术 Jails,这是最早期,也是功能最多的容器技术。Jails 英译过来是监狱的意思,这个“监狱”(用沙盒更为准确)包含了文件系统、用户、网络、进程等的隔离。

2001 Linux 也发布自己的容器技术 Linux VServer,2004 Solaris 也发布了 Solaris Containers,两者都将资源进行划分,形成一个个 zones,又叫做虚拟服务器。

2005 年推出 OpenVZ,它通过对 Linux 内核进行补丁来提供虚拟化的支持,每个 OpenVZ 容器完整支持了文件系统、用户及用户组、进程、网络、设备和 IPC 对象的隔离。

2007 年 Google 实现了 Control Groups( cgroups ),并加入到 Linux 内核中,这是划时代的,为后期容器的资源配额提供了技术保障。

2008 年基于 cgroups 和 linux namespace 推出了第一个最为完善的 Linux 容器 LXC。

2013 年推出到现在为止最为流行和使用最广泛的容器 Docker,相比其他早期的容器技术,Docker 引入了一整套容器管理的生态系统,包括分层的镜像模型,容器注册库,友好的 Rest API。

2014 年 CoreOS 也推出了一个类似于 Docker 的容器 Rocket,CoreOS 一个更加轻量级的 Linux 操作系统,在安全性上比 Docker 更严格。

2016 年微软也在 Windows 上提供了容器的支持,Docker 可以以原生方式运行在 Windows 上,而不是需要使用 Linux 虚拟机。

基本上到这个时间节点,容器技术就已经很成熟了,再往后就是容器云的发展,由此也衍生出多种容器云的平台管理技术,其中以 kubernetes 最为出众,有了这样一些细粒度的容器集群管理技术,也为微服务的发展奠定了基石。因此,对于未来来说,应用的微服务化是一个较大的趋势。

为什么需要容器

其一,这是技术演进的一种创新结果,其二,这是人们追求高效生产活动的一种工具。

随着软件开发的发展,相比于早期的集中式应用部署方式,现在的应用基本都是采用分布式的部署方式,一个应用可能包含多种服务或多个模块,因此多种服务可能部署在多种环境中,如虚拟服务器、公有云、私有云等,由于多种服务之间存在一些依赖关系,所以可能存在应用在运行过程中的动态迁移问题,那这时如何保证不同服务在不同环境中都能平滑的适配,不需要根据环境的不同而去进行相应的定制,就显得尤为重要。

就像货物的运输问题一样,如何将不同的货物放在不同的运输机器上,减少因货物的不同而频繁进行货物的装载和卸载,浪费大量的人力物力。

为此人们发明了集装箱,将货物根据尺寸形状等的不同,用不同规格的集装箱装载,然后再放到运输机上运输,由于集装箱密封,只有货物到达目的地才需拆封,在运输过程能够再不同运输机上平滑过渡,所以避免了资源的浪费。

Injection

javava EE CDI主要使用@Inject批注,以便将托管bean的依赖注入执行到其他容器托管资源。

构造函数依赖注入

构造函数依赖注入
公共类SomeBean {

  私人最终服务;

  @注入
public SomeBean(服务服务){
this.service = service;
} }

当CDI容器实例化SomeBean类型的bean时,它将查找默认(无参数)构造函数并使用它来创建bean实例。这个规则的例外是当我们有另一个用@Inject注释的构造函数时。如果是这种情况,容器将使用带注释的构造函数,并将注入作为构造函数参数传递的依赖项。

在上面的示例中,它将获取一个Service实例并注入SomeBean带注释的构造函数。

注意:请记住,它可能只存在一个单一的与@Inject注释构造函数

场依赖注入

场依赖注入
公共类SomeBean {

  @注入
私人服务; }

在这种情况下,当容器初始化类型为SomeBean的bean时,它会将正确的Service bean注入到字段中,即使它是私有的,也不需要任何setter方法。

Initializer方法依赖注入

Initializer方法依赖注入
公共类SomeBean {

  私人服务;

  @注入
public void setService(服务服务){
this.service = service;
} }

在这种情况下,当容器初始化SomeBean类型的bean时,它将调用所有使用@Inject注释的方法,并将依赖项注入方法参数。

@Any资格赛

为了提供完全松散耦合的应用程序,我们通常将接口注入托管资源。如果我们为给定的接口提供多个bean实现怎么办?我们可以使用@Any限定符以及CDI 实例接口将它们全部注入到托管bean中:

@Any资格赛
公共类SomeBean {

  @注入
public void listServiceImplementations(
@Any Instance <Service> serviceList){ for(服务服务:serviceList){
的System.out.println(service.getClass()另一方面,getCanonicalName());
} }
}

@Any预选赛指示,该注射点可以通过任何可用的依赖性得到满足的容器,因此容器注入他们。如果我们有多个接口实现并且我们只注入一个 - 没有任何消除歧义 - 容器会抱怨并且无法初始化组件。我们将在其他教程中看到依赖消歧。

注入生产者方法

生产者方法参数也可以由CDI容器注入。请参阅Java EE CDI Producer方法教程

CDI代理

本教程将不完整,我们也没有涵盖CDI代理机制。当我们注入被以不同于一个范围内创建一个托管bean @Dependent -到另一个托管的资源- CDI容器也没有注入直接引用注入豆。

对于CDI bean范围,请参阅Java EE CDI bean范围

为什么CDI使用代理?因为如果注入直接bean引用,就会产生线程安全或对托管bean的并发访问等问题。

想象一下,会话范围的bean被注入到应用程序范围的bean中。由于应用程序作用域bean在所有客户端之间共享,如果多个客户端同时访问应用程序作用域bean,则存在一个客户端访问另一个客户端直接引用的会话作用域bean的高风险。

要解决此问题,CDI会创建代理并将代理注入注入点。然后,代理将处理对注入的bean的调用,并将调用转发给正确的bean实例。

CDI创建的代理扩展了注入bean的类。想象一下以下场景:

应用程序和会话范围的bean
@SessionScoped
公共课堂服务{ public void doWork(){
的System.out.println( “工作......”);
} } @ApplicationScoped
公共类SomeBean { @注入
私人服务; public void test(){
service.doWork();
} }

CDI会将会话作用域bean的代理注入到应用程序作用域bean中。对会话范围bean的每次调用都将通过代理,代理又将调用重定向到正确的会话bean实例:属于当前HTTP请求会话的实例。

CDI通过扩展bean类并覆盖所有非私有方法来创建代理。代理的代表性说明可能如下:

说明性的CDI代理
公共类服务$ Proxy $ _ $$ _ WeldClientProxy
扩展服务{ @覆盖
public void doWork(){
Service instance = // ...解析bean实例
instance.doWork();
} }
 
 
 
 
 
05-02 16:46