该负载均衡策略基于最少活跃调用数算法,某个服务活跃调用数越小,表明该服务提供者效率越高,也就表明单位时间内能够处理的请求更多。此时应该选择该类服务器。实现很简单,就是每一个服务都有一个活跃数 active 来记录该服务的活跃值,每收到一个请求,该 active 就会加 1,每完成一个请求,active 就减 1。在服务运行一段时间后,性能好的服务提供者处理请求的速度更快,因此活跃数下降的也越快,此时这样的服务提供者能够优先获取到新的服务请求。除了最小活跃数,还引入了权重值,也就是当活跃数一样的时候,选择利用权重法来进行选择,如果权重也一样,那么随机选择一个。
```java
```java
/**
/**
* LeastActiveLoadBalance
* LeastActiveLoadBalance
@ -290,26 +301,28 @@ public class LeastActiveLoadBalance extends AbstractLoadBalance {
dubbo 抽象出了一个端的概念,也就是Endpoint接口,这个端就是一个点,而点与点之间可以双向传输。在端的基础上再衍生出通道、客户端以及服务端的概念,也就是下面要介绍的 Channel、Client、Server 三个接口。在传输层,Client 和 Server 的区别只是语义上的区别,并不区分请求和应答职责,而在交换层,Client 和 Server 是有方向的端点,所以区分了明确的请求和应答职责。两者都具备发送的能力,只是客户端和服务端所关注的事情不一样,而Endpoint接口抽象的方法就是它们共同拥有的方法。这也就是它们都能被抽象成端的原因。
dubbo 抽象出了一个端的概念,也就是 Endpoint 接口,这个端就是一个点,而点与点之间可以双向传输。在端的基础上再衍生出通道、客户端以及服务端的概念,也就是下面要介绍的 Channel、Client、Server 三个接口。在传输层,Client 和 Server 的区别只是语义上的区别,并不区分请求和应答职责,而在交换层,Client 和 Server 是有方向的端点,所以区分了明确的请求和应答职责。两者都具备发送的能力,只是客户端和服务端所关注的事情不一样,而 Endpoint 接口抽象的方法就是它们共同拥有的方法。这也就是它们都能被抽象成端的原因。
```java
```java
/**
/**
* Endpoint. (API/SPI, Prototype, ThreadSafe)
* Endpoint. (API/SPI, Prototype, ThreadSafe)
@ -92,8 +97,11 @@ public interface Endpoint {
boolean isClosed();
boolean isClosed();
}
}
```
```
### Channel 接口
### Channel 接口
该接口是通道接口,通道是信息传输的载体。Channel 可读可写,并且可以异步读写。Channel 是 client 和 server 的数据传输桥梁。Channel 和 client 是一对一的,也就是一个 client 对应一个 Channel,而 Channel 和 server 则是多对一,也就是一个 server 可以对应多个 Channel。
该接口是通道接口,通道是信息传输的载体。Channel 可读可写,并且可以异步读写。Channel 是 client 和 server 的数据传输桥梁。Channel 和 client 是一对一的,也就是一个 client 对应一个 Channel,而 Channel 和 server 则是多对一,也就是一个 server 可以对应多个 Channel。
```java
```java
/**
/**
* Channel. (API/SPI, Prototype, ThreadSafe)
* Channel. (API/SPI, Prototype, ThreadSafe)
@ -122,7 +130,9 @@ public interface Channel extends Endpoint {
void removeAttribute(String key);
void removeAttribute(String key);
}
}
```
```
### ChannelHandler 接口
### ChannelHandler 接口
```java
```java
/**
/**
* ChannelHandler. (API, Prototype, ThreadSafe)
* ChannelHandler. (API, Prototype, ThreadSafe)
@ -150,6 +160,7 @@ public interface ChannelHandler {
Spring 上很多接口和抽象类,其注解甚至比代码还多,我也经常尝试着去阅读理解这些注释,看看自己的理解与书上的差异,用这种方式来提升英文技术文档的阅读能力,往往更实在一些。
Spring 上很多接口和抽象类,其注解甚至比代码还多,我也经常尝试着去阅读理解这些注释,看看自己的理解与书上的差异,用这种方式来提升英文技术文档的阅读能力,往往更实在一些。
### 二、学习方面(学习模式的构建、学以致用)
### 二、学习方面(学习模式的构建、学以致用)
虽然是做技术的,但我也是一个很爱出去耍的人。构建好自己的学习模式能够让你更从容地面对工作和生活。不加班的情况下(所幸部门加班并不太多),我一般会在晚饭之后以及周日时间充电。不管是学技术还是其它什么东西,我认为 以视频为入口,以业界公认的名书继续深入理解,以社交圈的同行或网上社区为输出交流管道,最后持久化到思维导图及学习文档中。Spring 源码学习是我工作之后对自己学习模式构建的一个尝试,构建起这种学习模式之后,个人的工作和生活也变得更加协调平衡,不至于在繁杂忙碌的工作中渐渐丧失学习能力。另外一个比较重要的就是,看 Spring 源码时经常能看到一些与公司框架有异曲同工之妙的编码技巧及实现,比如异常的批量抛出,ConcurrentHashMap 初始化其容量,ThreadLocal 的使用等等,这些都是在读 Spring 源码之前很少会注意或使用的。
虽然是做技术的,但我也是一个很爱出去耍的人。构建好自己的学习模式能够让你更从容地面对工作和生活。不加班的情况下(所幸部门加班并不太多),我一般会在晚饭之后以及周日时间充电。不管是学技术还是其它什么东西,我认为 以视频为入口,以业界公认的名书继续深入理解,以社交圈的同行或网上社区为输出交流管道,最后持久化到思维导图及学习文档中。Spring 源码学习是我工作之后对自己学习模式构建的一个尝试,构建起这种学习模式之后,个人的工作和生活也变得更加协调平衡,不至于在繁杂忙碌的工作中渐渐丧失学习能力。另外一个比较重要的就是,看 Spring 源码时经常能看到一些与公司框架有异曲同工之妙的编码技巧及实现,比如异常的批量抛出,ConcurrentHashMap 初始化其容量,ThreadLocal 的使用等等,这些都是在读 Spring 源码之前很少会注意或使用的。
对于初级开发者学习 Spring 源码来说,我建议配合阿里的《Java 开发手册》一起看,因为编码能力和框架设计能力是需要很长时间的经验积累才能得到大幅提升的,而编码规范则是我们最开始就能做到并做好的事情,也是很多成熟公司越来越重视的东西。另外,阿里的《Java 开发手册》中不少规范都是参考了 Spring 框架的,这也从侧面体现了 Spring 作为业界知名框架,其编码的规范性是深受认可的。
对于初级开发者学习 Spring 源码来说,我建议配合阿里的《Java 开发手册》一起看,因为编码能力和框架设计能力是需要很长时间的经验积累才能得到大幅提升的,而编码规范则是我们最开始就能做到并做好的事情,也是很多成熟公司越来越重视的东西。另外,阿里的《Java 开发手册》中不少规范都是参考了 Spring 框架的,这也从侧面体现了 Spring 作为业界知名框架,其编码的规范性是深受认可的。
public abstract class InputStream implements Closeable {
public abstract class InputStream implements Closeable {
@ -45,15 +51,17 @@ public abstract class InputStream implements Closeable {
public abstract int read() throws IOException;
public abstract int read() throws IOException;
}
}
```
```
注意其中的一句话 **“This method blocks until input data is available, the end of the stream is detected, or an exception is thrown”**,当对 Socket 的输入流进行读取操作的时候,它会一直阻塞下去,直到发生如下三种事件。
注意其中的一句话 **“This method blocks until input data is available, the end of the stream is detected, or an exception is thrown”**,当对 Socket 的输入流进行读取操作的时候,它会一直阻塞下去,直到发生如下三种事件。
前面我们主要分析了 FileSystemXmlApplicationContext 这个具体的 IoC 容器实现类 的初始化源码,在 IoC 容器 中建立了 beanName 到 BeanDefinition 的数据映射,通过一个 ConcurrentHashMap。现在我们来看一下 Spring 是如何将 IoC 容器中存在依赖关系的 bean 根据配置联系在一起的。
@ -66,8 +80,10 @@ public class RMIServerSourceCode {
</beans>
</beans>
```
```
**注意:rmi使用小写**
**注意:rmi 使用小写**
大写报错信息:
大写报错信息:
```text
```text
Exception in thread "main" org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'rmiClient' defined in class path resource [rmi/RMIClientSourceCode.xml]: Invocation of init method failed; nested exception is org.springframework.remoting.RemoteLookupFailureException: Service URL [RMI://localhost:9999/springRmi] is invalid; nested exception is java.net.MalformedURLException: invalid URL scheme: RMI://localhost:9999/springRmi
Exception in thread "main" org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'rmiClient' defined in class path resource [rmi/RMIClientSourceCode.xml]: Invocation of init method failed; nested exception is org.springframework.remoting.RemoteLookupFailureException: Service URL [RMI://localhost:9999/springRmi] is invalid; nested exception is java.net.MalformedURLException: invalid URL scheme: RMI://localhost:9999/springRmi
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean(AbstractAutowireCapableBeanFactory.java:1795)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean(AbstractAutowireCapableBeanFactory.java:1795)
Spring 的 IoC 是一个独立模块,它并不直接在 Web容器 中发挥作用,如果要在 Web环境 中使用 IoC容器,需要 Spring 为 IoC 设计一个启动过程,把 IoC容器 导入,并在 Web容器 中建立起来。具体说来,这个启动过程是和 Web容器 的启动过程集成在一起的。在这个过程中,一方面处理 Web容器 的启动,另一方面通过设计特定的 Web容器拦截器,将 IoC容器 载人到 Web环境 中来,并将其初始化。在这个过程建立完成以后,IoC容器 才能正常工作,而 SpringMVC 是建立在 IoC容器 的基础上的,这样才能建立起 MVC框架 的运行机制,从而响应从 Web容器 传递的 HTTP请求。
在 Web 环境 中,SpringMVC 是建立在 IoC 容器 基础上的。了解 SpringMVC,首先要了解 Spring 的 IoC 容器 是如何在 Web 环境 中被载入并起作用的。
Spring 的 IoC 是一个独立模块,它并不直接在 Web 容器 中发挥作用,如果要在 Web 环境 中使用 IoC 容器,需要 Spring 为 IoC 设计一个启动过程,把 IoC 容器 导入,并在 Web 容器 中建立起来。具体说来,这个启动过程是和 Web 容器 的启动过程集成在一起的。在这个过程中,一方面处理 Web 容器 的启动,另一方面通过设计特定的 Web 容器拦截器,将 IoC 容器 载人到 Web 环境 中来,并将其初始化。在这个过程建立完成以后,IoC 容器 才能正常工作,而 SpringMVC 是建立在 IoC 容器 的基础上的,这样才能建立起 MVC 框架 的运行机制,从而响应从 Web 容器 传递的 HTTP 请求。
下面以 Tomcat 作为 Web 容器 的例子进行分析。在 Tomcat 中,web.xml 是应用的部署描述文件。在 web.xml 中常常经常能看到与 Spring 相关的部署描述。
DispatchServlet 和 ContextLoaderListener 提供了在 Web 容器 中对 Spring 的接口,也就是说,这些接口与 Web 容器 耦合是通过 ServletContext 来实现的(ServletContext 是容器和应用沟通的桥梁,从一定程度上讲 ServletContext 就是 servlet 规范 的体现)。这个 ServletContext 为 Spring 的 IoC 容器 提供了一个宿主环境,在宿主环境中,Spring MVC 建立起一个 IoC 容器 的体系。这个 IoC 容器体系 是通过 ContextLoaderListener 的初始化来建立的,在建立 IoC 容器体系 后,把 DispatchServlet 作为 Spring MVC 处理 Web 请求 的转发器建立起来,从而完成响应 HTTP 请求 的准备。有了这些基本配置,建立在 IoC 容器 基础上的 SpringMVC 就可以正常地发挥作用了。下面我们看一下 IoC 容器 在 Web 容器 中的启动代码实现。
IoC 容器 的启动过程就是建立上下文的过程,该上下文是与 ServletContext 相伴而生的,同时也是 IoC 容器 在 Web 应用环境 中的具体表现之一。由 ContextLoaderListener 启动的上下文为根上下文。在根上下文的基础上,还有一个与 Web MVC 相关的上下文用来保存控制器(DispatcherServlet)需要的 MVC 对象,作为根上下文的子上下文,构成一个层次化的上下文体系。在 Web 容器 中启动 Spring 应用程序 时,首先建立根上下文,然后建立这个上下文体系,这个上下文体系的建立是由 ContextLoder 来完成的,其 UML 时序图 如下图所示。
对于 Spring 承载的 Web 应用 而言,可以指定在 Web 应用程序 启动时载入 IoC 容器(或者称为 WebApplicationContext)。这个功能是由 ContextLoaderListener 来完成的,它是在 Web 容器 中配置的监听器,会监听 Web 容器 的启动,然后载入 IoC 容器。这个 ContextLoaderListener 通过使用 ContextLoader 来完成实际的 WebApplicationContext,也就是 IoC 容器 的初始化工作。这个 ContextLoader 就像 Spring 应用程序 在 Web 容器 中的启动器。这个启动过程是在 Web 容器 中发生的,所以需要根据 Web 容器 部署的要求来定义 ContextLoader,相关的配置在概述中已经看到了,这里就不重复了。
为了了解 IoC 容器 在 Web 容器 中的启动原理,这里对 启动器 ContextLoaderListener 的实现进行分析。**这个监听器是启动 根 IoC 容器 并把它载入到 Web 容器 的主要功能模块,也是整个 Spring Web 应用 加载 IoC 的第一个地方**。从加载过程可以看到,首先从 Servlet 事件 中得到 ServletContext,然后可以读取配置在 web.xml 中的各个相关的属性值,接着 ContextLoader 会实例化 WebApplicationContext,并完成其载人和初始化过程。这个被初始化的第一个上下文,作为根上下文而存在,这个根上下文载入后,被绑定到 Web 应用程序 的 ServletContext 上。任何需要访问根上下文的应用程序代码都可以从 WebApplicationContextUtils 类 的静态方法中得到。
下面分析具体的根上下文的载人过程。在 ContextLoaderListener 中,实现的是 **ServletContextListener 接口,这个接口里的函数会结合 Web 容器 的生命周期被调用**。因为 ServletContextListener 是 ServletContext 的监听者,如果 ServletContext 发生变化,会触发出相应的事件,而监听器一直在对这些事件进行监听,如果接收到了监听的事件,就会做出预先设计好的响应动作。由于 ServletContext 的变化而触发的监听器的响应具体包括:在服务器启动时,ServletContext 被创建的时候,服务器关闭时,ServletContext 将被销毁的时候等。对应这些事件及 Web 容器状态 的变化,在监听器中定义了对应的事件响应的回调方法。比如,在服务器启动时,ServletContextListener 的 contextInitialized()方法 被调用,服务器将要关闭时,ServletContextListener 的 contextDestroyed()方法 被调用。了解了 Web 容器 中监听器的工作原理,下面看看服务器启动时 ContextLoaderListener 的调用完成了什么。在这个初始化回调中,创建了 ContextLoader,同时会利用创建出来的 ContextLoader 来完成 IoC 容器 的初始化。
在事务处理中,事务处理单元的设计与相应的业务逻辑设计有很紧密的联系。在很多情况下,一个业务逻辑处理不会只有一个单独的数据库操作,而是有一组数据库操作。在这个处理过程中,首先涉及的是事务处理单元划分的问题,Spring 借助 IoC 容器 的强大配置能力,为应用提供了声明式的事务划分方式,这种声明式的事务处理,为 Spring 应用 使用事务管理提供了统一的方式。有了 Spring 事务管理 的支持,只需要通过一些简单的配置,应用就能完成复杂的事务处理工作,从而为用户使用事务处理提供很大的方便。
与编程式使用事务管理不同,在使用声明式事务处理的时候,因为涉及 Spring 框架 对事务处理的统一管理,以及对并发事务和事务属性的处理,所以采用的是一个比较复杂的处理过程,但复杂归复杂,这个过程对使用声明式事务处理的应用来说,基本上是不可见的,而是由 Spring 框架 来完成的。有了这些背景铺垫和前面对 AOP 封装事务处理 的了解,下面来看看 Spring 是如何提供声明式事务处理的,Spring 在这个相对较为复杂的过程中封装了什么。这层封装包括在事务处理中事务的创建、提交和回滚等比较核心的操作。
在 Spring 中,也可以通过编程式的方法来使用事务处理器,以帮助我们处理事务。在编程式的事务处理使用中, TransactionDefinition 是定义事务处理属性的类。对于事务处理属性,Spring 还提供了一个默认的事务属性 DefaultTransactionDefinition 来供用户使用。这种事务处理方式在实现上看起来比声明式事务处理要简单,但编程式实现事务处理却会造成事务处理与业务代码的紧密耦合,因而不经常被使用。在这里,我们之所以举编程式使用事务处理的例子,是因为通过了解编程式事务处理的使用,可以清楚地了解 Spring 统一实现事务处理的大致过程。
在 Spring 中,也可以通过编程式的方法来使用事务处理器,以帮助我们处理事务。在编程式的事务处理使用中, TransactionDefinition 是定义事务处理属性的类。对于事务处理属性,Spring 还提供了一个默认的事务属性 DefaultTransactionDefinition 来供用户使用。这种事务处理方式在实现上看起来比声明式事务处理要简单,但编程式实现事务处理却会造成事务处理与业务代码的紧密耦合,因而不经常被使用。在这里,我们之所以举编程式使用事务处理的例子,是因为通过了解编程式事务处理的使用,可以清楚地了解 Spring 统一实现事务处理的大致过程。
在使用 Spring 声明式事务处理 的时候,一种常用的方法是结合 IoC 容器 和 Spring 已有的 TransactionProxyFactoryBean 对事务管理进行配置,比如,可以在这个 TransactionProxyFactoryBean 中为事务方法配置传播行为、并发事务隔离级别等事务处理属性,从而对声明式事务的处理提供指导。具体来说,在对声明式事务处理的原理分析中,声明式事务处理的实现大致可以分为以下几个部分:
在这个 invoke()方法 的实现中,可以看到整个事务处理在 AOP拦截器 中实现的全过程。同时,它也是 Spring 采用 AOP 封装事务处理和实现声明式事务处理的核心部分。这部分实现是一个桥梁,它胶合了具体的事务处理和 Spring AOP框架,可以看成是一个 Spring AOP应用,在这个桥梁搭建完成以后,Spring事务处理 的实现就开始了。
在这个 invoke()方法 的实现中,可以看到整个事务处理在 AOP拦截器 中实现的全过程。同时,它也是 Spring 采用 AOP 封装事务处理和实现声明式事务处理的核心部分。这部分实现是一个桥梁,它胶合了具体的事务处理和 Spring AOP框架,可以看成是一个 Spring AOP应用,在这个桥梁搭建完成以后,Spring事务处理 的实现就开始了。