修缮之前的博文

pull/28/head
AmyliaY 6 years ago
parent bf5bf3603e
commit 289fa63ab4

@ -1,17 +1,16 @@
理论性的文字,我觉得就没必要再扯一遍咯,大道理讲这么多,越听越迷糊。不如直接看源码+注解来的明白痛快。所以话不多说,直接上源码。
理论性的文字,我觉得就没必要再扯一遍咯,大道理讲这么多,越听越迷糊。不如直接看源码加注释来的明白痛快。所以话不多说,直接上源码。
## 1主要的接口
### 1.1Advice 通知
定义了切面的增强方式,如:前置增强 BeforeAdvice后置增强 AfterAdvice异常增强 ThrowsAdvice 等。下面看两个主要的子接口的源码。
## 1 主要的接口
### 1.1 Advice 通知
本接口定义了切面的增强方式,如:前置增强 BeforeAdvice后置增强 AfterAdvice异常增强 ThrowsAdvice 等。下面看两个主要的子接口的源码。
```java
public interface MethodBeforeAdvice extends BeforeAdvice {
/**
* 目标方法 method 要开始执行时AOP 会回调此方法
* 目标方法 method 开始执行前AOP 会回调此方法
*/
void before(Method method, Object[] args, Object target) throws Throwable;
}
public interface AfterReturningAdvice extends AfterAdvice {
@ -20,14 +19,13 @@ public interface AfterReturningAdvice extends AfterAdvice {
* 目标方法 method 执行后AOP 会回调此方法,注意,它还传入了 method 的返回值
*/
void afterReturning(Object returnValue, Method method, Object[] args, Object target) throws Throwable;
}
```
### 1.2Pointcut 方法的横切面
用来定义需要增强的目标方法的集合一般使用正则表达式去匹配筛选指定范围内的所有满足条件的目标方法。Pointcut 接口有很多实现,我们主要看一下 JdkRegexpMethodPointcut 和 NameMatchMethodPointcut 的实现原理,前者主要通过正则表达式对方法名进行匹配,后者则通过匹配方法名进行匹配。
### 1.2 Pointcut 方法的横切面
本接口用来定义需要增强的目标方法的集合一般使用正则表达式去匹配筛选指定范围内的所有满足条件的目标方法。Pointcut 接口有很多实现,我们主要看一下 JdkRegexpMethodPointcut 和 NameMatchMethodPointcut 的实现原理,前者主要通过正则表达式对方法名进行匹配,后者则通过匹配方法名进行匹配。
```java
//JdkRegexpMethodPointcut 的实现源码
// JdkRegexpMethodPointcut 的实现源码
private Pattern[] compiledPatterns = new Pattern[0];
protected boolean matches(String pattern, int patternIndex) {
@ -35,7 +33,7 @@ public interface AfterReturningAdvice extends AfterAdvice {
return matcher.matches();
}
//NameMatchMethodPointcut 的实现源码
// NameMatchMethodPointcut 的实现源码
private List<String> mappedNames = new LinkedList<String>();
public boolean matches(Method method, Class targetClass) {
@ -47,7 +45,7 @@ public interface AfterReturningAdvice extends AfterAdvice {
return false;
}
```
### 1.3Advisor 通知器
### 1.3 Advisor 通知器
将 Pointcut 和 Advice 有效地结合在一起。它定义了在哪些方法Pointcut上执行哪些动作Advice。下面看一下 DefaultPointcutAdvisor 的源码实现,它通过持有 Pointcut 和 Advice 属性来将两者有效地结合在一起。
```java
@ -63,7 +61,7 @@ public class DefaultPointcutAdvisor extends AbstractGenericPointcutAdvisor imple
}
/**
* 自己定义了 PointcutAdvice 则使用父类中的定义
* 自己定义了 Pointcut属性而 Advice属性 则使用父类中的定义
*/
public DefaultPointcutAdvisor(Pointcut pointcut, Advice advice) {
this.pointcut = pointcut;
@ -88,16 +86,13 @@ public abstract class AbstractGenericPointcutAdvisor extends AbstractPointcutAdv
public String toString() {
return getClass().getName() + ": advice [" + getAdvice() + "]";
}
}
```
## 2、spring AOP 的设计与实现
AOP 的实现代码中,主要使用了 JDK 动态代理,在特定场景下(被代理对象 implements 的接口)也用到了 CGLIB 生成代理对象。通过 AOP 的源码设计可以看到,其先为目标对象建立了代理对象,这个代理对象的生成可以使用 JDK 动态代理或 CGLIB 完成。然后启动为代理对象配置的拦截器,对横切面(目标方法集合)进行相应的增强,将 AOP 的横切面设计和 Proxy 模式有机地结合起来,实现了在 AOP 中定义好的各种织入方式。
## 2 Spring AOP 的设计与实现
AOP 的实现代码中,主要使用了 JDK 动态代理,在特定场景下(被代理对象没有 implements 的接口)也用到了 CGLIB 生成代理对象。通过 AOP 的源码设计可以看到,其先为目标对象建立了代理对象,这个代理对象的生成可以使用 JDK 动态代理或 CGLIB 完成。然后启动为代理对象配置的拦截器,对横切面(目标方法集合)进行相应的增强,将 AOP 的横切面设计和 Proxy 模式有机地结合起来,实现了在 AOP 中定义好的各种织入方式。
### 2.1、ProxyFactoryBean
这里我们主要以 ProxyFactoryBean 的实现为例,对 AOP 的实现原理进行分析。ProxyFactoryBean 主要持有目标对象 target 的代理对象 aopProxy和 Advisor 通知器,而 Advisor 持有 Advice 和 Pointcut这样就可以判断 aopProxy 中的方法 是否是某个指定的切面 Pointcut然后根据其配置的织入方向前置增强/后置增强),通过反射为其织入相应的增强行为 Advice。
先看一下 ProxyFactoryBean 的配置和使用。
### 2.1 ProxyFactoryBean
这里我们主要以 ProxyFactoryBean 的实现为例,对 AOP 的实现原理进行分析。ProxyFactoryBean 主要持有目标对象 target 的代理对象 aopProxy和 Advisor 通知器,而 Advisor 持有 Advice 和 Pointcut这样就可以判断 aopProxy 中的方法 是否是某个指定的切面 Pointcut然后根据其配置的织入方向前置增强/后置增强),通过反射为其织入相应的增强行为 Advice。先看一下 ProxyFactoryBean 的配置和使用。
```xml
<!-- 定义自己的 Advisor 实现,其中包含了 Pointcut 和 Advice -->
@ -116,7 +111,7 @@ AOP 的实现代码中,主要使用了 JDK 动态代理,在特定场景下
</property>
</bean>
```
### 2.2、ProxyFactoryBean 为配置的 target 生成 AopProxy 代理对象
### 2.2 为配置的 target 生成 AopProxy 代理对象
ProxyFactoryBean 的 getObject() 方法先对通知器链进行了初始化,然后根据被代理对象类型的不同,生成代理对象。
```java
@ -125,9 +120,9 @@ ProxyFactoryBean 的 getObject() 方法先对通知器链进行了初始化,
* 创建此工厂要返回的 AOP 代理的实例,该实例将作为一个单例被缓存
*/
public Object getObject() throws BeansException {
//初始化通知器链
// 初始化通知器链
initializeAdvisorChain();
//这里对 Singleton 和 prototype 的类型进行区分,生成对应的 proxy
// 这里对 Singleton 和 prototype 的类型进行区分,生成对应的 proxy
if (isSingleton()) {
return getSingletonInstance();
}
@ -140,14 +135,14 @@ ProxyFactoryBean 的 getObject() 方法先对通知器链进行了初始化,
}
}
```
### 2.3、initializeAdvisorChain() 初始化 Advisor 链
### 2.3 初始化 Advisor 链
```java
/**
* 初始化 Advisor 链,可以发现,其中有通过对 IoC 容器的 getBean() 方法的调用来获取配置好的 advisor 通知器
*/
private synchronized void initializeAdvisorChain() throws AopConfigException, BeansException {
//如果通知器链已经完成初始化,则直接返回
// 如果通知器链已经完成初始化,则直接返回
if (this.advisorChainInitialized) {
return;
}
@ -163,15 +158,14 @@ ProxyFactoryBean 的 getObject() 方法先对通知器链进行了初始化,
throw new AopConfigException("Target required after globals");
}
//这里添加了 Advisor 链的调用,下面的 interceptorNames 是在配置文件中
//通过 interceptorNames 进行配置的。由于每一个 Advisor 都是被配置为 bean 的,
//所以通过遍历 interceptorNames 得到的 name其实就是 bean 的 id通过这个 nameid
//我们就可以从 IoC 容器中获取对应的实例化 bean
// 这里添加了 Advisor 链的调用,下面的 interceptorNames 是在配置文件中
// 通过 interceptorNames 进行配置的。由于每一个 Advisor 都是被配置为 bean 的,
// 所以通过遍历 interceptorNames 得到的 name其实就是 bean 的 id通过这个 nameid
// 我们就可以从 IoC 容器中获取对应的实例化 bean
for (String name : this.interceptorNames) {
if (logger.isTraceEnabled()) {
logger.trace("Configuring advisor or advice '" + name + "'");
}
if (name.endsWith(GLOBAL_SUFFIX)) {
if (!(this.beanFactory instanceof ListableBeanFactory)) {
throw new AopConfigException(
@ -180,55 +174,53 @@ ProxyFactoryBean 的 getObject() 方法先对通知器链进行了初始化,
addGlobalAdvisor((ListableBeanFactory) this.beanFactory,
name.substring(0, name.length() - GLOBAL_SUFFIX.length()));
}
else {
//对当前的 factoryBean 进行类型判断,是属于单例 bean 还是原型 bean
// 对当前的 factoryBean 进行类型判断,是属于单例 bean 还是原型 bean
Object advice;
if (this.singleton || this.beanFactory.isSingleton(name)) {
//通过 beanFactory 的 getBean() 方法获取 advisor
//这个 name 是从 interceptorNames 中获取的
// 通过 beanFactory 的 getBean() 方法获取 advisor
// 这个 name 是从 interceptorNames 中获取的
advice = this.beanFactory.getBean(name);
}
else {
//如果是原型 bean
// 如果是原型 bean
advice = new PrototypePlaceholderAdvisor(name);
}
addAdvisorOnChainCreation(advice, name);
}
}
}
this.advisorChainInitialized = true;
}
```
生成 singleton 的代理对象在 getSingletonInstance 方法中完成,这是 ProxyFactoryBean 生成 AopProxy 代理对象的调用入口。代理对象会封装对 target 对象的调用,针对 target 对象的方法调用会被这里生成的代理对象所拦截。
### 2.4、getSingletonInstance() 生成单例代理对象
### 2.4 生成单例代理对象
```java
/**
* 返回此类代理对象的单例实例,如果尚未创建该实例,则使用单例模式的懒汉式 创建它
* 返回此类代理对象的单例实例,如果尚未创建该实例,则单例地创建它
*/
private synchronized Object getSingletonInstance() {
if (this.singletonInstance == null) {
this.targetSource = freshTargetSource();
if (this.autodetectInterfaces && getProxiedInterfaces().length == 0 && !isProxyTargetClass()) {
//根据 AOP 框架来判断需要代理的接口
// 根据 AOP 框架来判断需要代理的接口
Class targetClass = getTargetClass();
if (targetClass == null) {
throw new FactoryBeanNotInitializedException("Cannot determine target class for proxy");
}
//设置代理对象的接口
// 设置代理对象的接口
setInterfaces(ClassUtils.getAllInterfacesForClass(targetClass, this.proxyClassLoader));
}
super.setFrozen(this.freezeProxy);
//这里会通过 AopProxy 来得到代理对象
// 这里会通过 AopProxy 来得到代理对象
this.singletonInstance = getProxy(createAopProxy());
}
return this.singletonInstance;
}
/**
* 通过 createAopProxy() 方法返回的 aopProxy 获取代理对象
* 通过 createAopProxy()方法 返回的 aopProxy 获取代理对象
*/
protected Object getProxy(AopProxy aopProxy) {
return aopProxy.getProxy(this.proxyClassLoader);
@ -243,8 +235,8 @@ public class ProxyCreatorSupport extends AdvisedSupport {
private AopProxyFactory aopProxyFactory;
public ProxyCreatorSupport() {
//注意这里实例化的是一个 DefaultAopProxyFactory所以下面的 createAopProxy() 方法
//中调用的也是 DefaultAopProxyFactory 的实现
// 注意这里实例化的是一个 DefaultAopProxyFactory所以下面的 createAopProxy() 方法
// 中调用的也是 DefaultAopProxyFactory 的实现
this.aopProxyFactory = new DefaultAopProxyFactory();
}
@ -263,17 +255,16 @@ public class ProxyCreatorSupport extends AdvisedSupport {
public AopProxyFactory getAopProxyFactory() {
return this.aopProxyFactory;
}
}
```
下面看一下 AopProxyFactory 接口的实现类 DefaultAopProxyFactory 的代码
下面看一下 AopProxyFactory 接口的实现类 DefaultAopProxyFactory 的 createAopProxy(AdvisedSupport config)方法
```java
public AopProxy createAopProxy(AdvisedSupport config) throws AopConfigException {
//AopProxy 代理对象的生成过程:
//首先从 AdvisedSupport 对象中获取配置的 target 目标对象的类型 targetClass
//然后根据 targetClass 是否为接口采取不同的生成代理对象的策略
// AopProxy 代理对象的生成过程:
// 首先从 AdvisedSupport 对象中获取配置的 target 目标对象的类型 targetClass
// 然后根据 targetClass 是否为接口采取不同的生成代理对象的策略
if (config.isOptimize() || config.isProxyTargetClass() || hasNoUserSuppliedProxyInterfaces(config)) {
Class targetClass = config.getTargetClass();
if (targetClass == null) {
@ -297,19 +288,26 @@ public class ProxyCreatorSupport extends AdvisedSupport {
}
```
可以看到其根据目标对象是否实现了接口,而决定是使用 JDK 动态代理还是 CGLIB 去生成代理对象,而 AopProxy 接口的实现类也只有 JdkDynamicAopProxy 和 CglibAopProxy 这两个。
可以看到其根据目标对象是否实现了接口,而决定是使用 JDK动态代理 还是 CGLIB 去生成代理对象,而 AopProxy 接口的实现类也只有 JdkDynamicAopProxy 和 CglibAopProxy 这两个。
### 2.5、JDK 生成 AopProxy 代理对象
### 2.5 JDK动态代理 生成 AopProxy代理对象
```java
/**
* 可以看到,其实现了 InvocationHandler 接口,所以肯定也定义了一个 使用 java.lang.reflect.Proxy
* 动态生成代理对象的方法,并在实现的 invoke() 方法中为代理对象织入增强方法
*/
final class JdkDynamicAopProxy implements AopProxy, InvocationHandler, Serializable {
/** AdvisedSupport 持有一个 List<Advisor>属性 */
private final AdvisedSupport advised;
public JdkDynamicAopProxy(AdvisedSupport config) throws AopConfigException {
Assert.notNull(config, "AdvisedSupport must not be null");
if (config.getAdvisors().length == 0 && config.getTargetSource() == AdvisedSupport.EMPTY_TARGET_SOURCE) {
throw new AopConfigException("No advisors and no TargetSource specified");
}
//这个 advised 是一个 AdvisedSupport 对象,可以通过它获取被代理对象 target
//这样,当 invoke() 方法被代理对象 aopProxy 调用时,就可以调用 target 的目标方法了
// 这个 advised 是一个 AdvisedSupport 对象,可以通过它获取被代理对象 target
// 这样,当 invoke()方法 被 代理对象aopProxy 调用时,就可以调用 target 的目标方法了
this.advised = config;
}
@ -322,20 +320,25 @@ final class JdkDynamicAopProxy implements AopProxy, InvocationHandler, Serializa
logger.debug("Creating JDK dynamic proxy: target source is " + this.advised.getTargetSource());
}
//获取代理类要实现的接口
// 获取代理类要实现的接口
Class[] proxiedInterfaces = AopProxyUtils.completeProxiedInterfaces(this.advised);
findDefinedEqualsAndHashCodeMethods(proxiedInterfaces);
//通过 Proxy 生成代理对象并返回
// 通过 java.lang.reflect.Proxy 生成代理对象并返回
return Proxy.newProxyInstance(classLoader, proxiedInterfaces, this);
}
}
```
通过 JdkDynamicAopProxy 的源码可以非常清楚地看到,其使用了 JDK 动态代理的方式生成了代理对象。JdkDynamicAopProxy 实现了 InvocationHandler 接口,并通过 Proxy.newProxyInstance() 方法生成代理对象并返回。
通过 JdkDynamicAopProxy 的源码可以非常清楚地看到,其使用了 JDK动态代理 的方式生成了 代理对象。JdkDynamicAopProxy 实现了 InvocationHandler 接口,并通过 java.lang.reflect.Proxy 的 newProxyInstance()静态方法 生成代理对象并返回。
### 2.6、CGLIB 生成 AopProxy 代理对象CglibAopProxy
### 2.6 CGLIB 生成 AopProxy代理对象
```java
final class CglibAopProxy implements AopProxy, Serializable {
/** AdvisedSupport 持有一个 List<Advisor>属性 */
protected final AdvisedSupport advised;
public Object getProxy(ClassLoader classLoader) {
if (logger.isDebugEnabled()) {
logger.debug("Creating CGLIB proxy: target source is " + this.advised.getTargetSource());
@ -356,7 +359,7 @@ final class JdkDynamicAopProxy implements AopProxy, InvocationHandler, Serializa
validateClassIfNecessary(proxySuperClass);
//创建并配置 Enhancer 对象Enhancer 是 CGLIB 中主要的操作类
// 创建并配置 Enhancer对象Enhancer 是 CGLIB 中主要的操作类
Enhancer enhancer = createEnhancer();
if (classLoader != null) {
enhancer.setClassLoader(classLoader);
@ -381,7 +384,7 @@ final class JdkDynamicAopProxy implements AopProxy, InvocationHandler, Serializa
}
enhancer.setCallbackTypes(types);
//通过 enhancer 生成代理对象
// 通过 enhancer 生成代理对象
Object proxy;
if (this.constructorArgs != null) {
proxy = enhancer.create(this.constructorArgTypes, this.constructorArgs);
@ -410,14 +413,16 @@ final class JdkDynamicAopProxy implements AopProxy, InvocationHandler, Serializa
}
}
```
为目标对象 target 生成代理对象之后,在调用代理对象的目标方法时,目标方法会进行 invoke() 回调或 callbacks() 回调,然后就可以在回调方法中对目标对象的目标方法进行拦截和增强处理了。
目标对象target 生成 代理对象 之后,在调用 代理对象 的目标方法时,目标方法会进行 invoke()回调JDK动态代理 或 callbacks()回调CGLIB,然后就可以在回调方法中对目标对象的目标方法进行拦截和增强处理了。
## 3、spring AOP 拦截器调用的实现
spring AOP 通过 JDK 的 Proxy 类生成代理对象时,相关的拦截器已经配置到了代理对象持有的 InvocationHandler(即ProxyBeanFactory) 的 invoke() 方法中,拦截器最后起作用,是通过调用代理对象的目标方法时,在代理类中触发了 InvocationHandler 的 invoke() 回调。通过 CGLIB 实现的 AOP原理与此相似。
## 3 Spring AOP 拦截器调用的实现
Spring AOP 通过 JDK 的 Proxy类 生成代理对象时,相关的拦截器已经配置到了代理对象持有的 InvocationHandler(即ProxyBeanFactory) 的 invoke() 方法中,拦截器最后起作用,是通过调用代理对象的目标方法时,在代理类中触发了 InvocationHandler 的 invoke() 回调。通过 CGLIB 实现的 AOP原理与此相似。
### 3.1JdkDynamicAopProxy 的 invoke() 拦截
前面已经通过两种不同的方式生成了 AopProxy 代理对象,下面我们先看一下 JdkDynamicAopProxy 中的 invoke() 回调方法中对拦截器调用的实现。
### 3.1 JdkDynamicAopProxy 的 invoke() 拦截
前面已经通过两种不同的方式生成了 AopProxy 代理对象,下面我们先看一下 JdkDynamicAopProxy 中的 invoke()回调方法 中对拦截器调用的实现。
```java
final class JdkDynamicAopProxy implements AopProxy, InvocationHandler, Serializable {
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
MethodInvocation invocation;
Object oldProxy = null;
@ -429,56 +434,56 @@ final class JdkDynamicAopProxy implements AopProxy, InvocationHandler, Serializa
Object target = null;
try {
//如果目标对象调用的是 Obejct 类中的基本方法equals、hashCode 则进行相应的处理
// 如果目标对象调用的是 Obejct类 中的基本方法equals()、hashCode() 则进行相应的处理
if (!this.equalsDefined && AopUtils.isEqualsMethod(method)) {
//如果目标对象没有重写 Object 类的基本方法equals(Object other)
// 如果目标对象没有重写 Object类 的基本方法equals(Object other)
return equals(args[0]);
}
if (!this.hashCodeDefined && AopUtils.isHashCodeMethod(method)) {
//如果目标对象没有重写 Object 类的基本方法hashCode()
// 如果目标对象没有重写 Object类 的基本方法hashCode()
return hashCode();
}
if (!this.advised.opaque && method.getDeclaringClass().isInterface() &&
method.getDeclaringClass().isAssignableFrom(Advised.class)) {
//使用代理配置对 ProxyConfig 进行服务调用
// 使用代理配置对 ProxyConfig 进行服务调用
return AopUtils.invokeJoinpointUsingReflection(this.advised, method, args);
}
Object retVal;
if (this.advised.exposeProxy) {
//如果有必要,可以援引
// 如果有必要,可以援引
oldProxy = AopContext.setCurrentProxy(proxy);
setProxyContext = true;
}
//获取目标对象,为目标方法的调用做准备
// 获取目标对象,为目标方法的调用做准备
target = targetSource.getTarget();
if (target != null) {
targetClass = target.getClass();
}
//获取定义好的拦截器链
// 获取定义好的拦截器链,即 Advisor列表
List<Object> chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(method, targetClass);
//如果没有配置拦截器,就直接调用目标对象 target 的 method 方法,并获取返回值
// 如果没有配置拦截器,就直接通过反射调用目标对象 target 的 method对象,并获取返回值
if (chain.isEmpty()) {
retVal = AopUtils.invokeJoinpointUsingReflection(target, method, args);
}
else {
//如果有拦截器链,则需要先调用拦截器链中的拦截器,再调用目标的对应方法
//这里通过构造 ReflectiveMethodInvocation 来实现
// 如果有拦截器链,则需要先调用拦截器链中的拦截器,再调用目标的对应方法
// 这里通过构造 ReflectiveMethodInvocation 来实现
invocation = new ReflectiveMethodInvocation(proxy, target, method, args, targetClass, chain);
//沿着拦截器链继续向下处理
// 沿着拦截器链继续向下处理
retVal = invocation.proceed();
}
//获取 method 返回值的类型
// 获取 method 返回值的类型
Class<?> returnType = method.getReturnType();
if (retVal != null && retVal == target && returnType.isInstance(proxy) &&
!RawTargetAccess.class.isAssignableFrom(method.getDeclaringClass())) {
//特殊提醒它返回“this”方法的返回类型与类型兼容。
//注意,如果 target 在另一个返回的对象中设置了对自身的引用spring 将无法处理
// 特殊提醒它返回“this”方法的返回类型与类型兼容。
// 注意,如果 target 在另一个返回的对象中设置了对自身的引用Spring 将无法处理
retVal = proxy;
} else if (retVal == null && returnType != Void.TYPE && returnType.isPrimitive()) {
throw new AopInvocationException("Null return value from advice does not match primitive return type for: " + method);
@ -487,20 +492,23 @@ final class JdkDynamicAopProxy implements AopProxy, InvocationHandler, Serializa
}
finally {
if (target != null && !targetSource.isStatic()) {
//必须来自 TargetSource.
// 必须来自 TargetSource.
targetSource.releaseTarget(target);
}
if (setProxyContext) {
//存储旧的 proxy.
// 存储旧的 proxy.
AopContext.setCurrentProxy(oldProxy);
}
}
}
}
```
### 3.2CglibAopProxy 的 intercept() 拦截
### 3.2 CglibAopProxy 的 intercept() 拦截
CglibAopProxy 的 intercept() 回调方法实现和 JdkDynamicAopProxy 的 invoke() 非常相似,只是在 CglibAopProxy 中构造 CglibMethodInvocation 对象来完成拦截器链的调用,而在 JdkDynamicAopProxy 中则是通过构造 ReflectiveMethodInvocation 对象来完成的。
```java
final class CglibAopProxy implements AopProxy, Serializable {
public Object intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
Object oldProxy = null;
boolean setProxyContext = false;
@ -515,18 +523,18 @@ CglibAopProxy 的 intercept() 回调方法实现和 JdkDynamicAopProxy 的 invok
if (target != null) {
targetClass = target.getClass();
}
//从 adviced 对象中获取配置好的拦截器链advised 是一个 AdvisedSupport 对象,
//而 AdvisedSupport 也是 ProxyFactoryBean 的父类之一。
// 从 adviced 对象中获取配置好的拦截器链advised 是一个 AdvisedSupport对象
// 而 AdvisedSupport 也是 ProxyFactoryBean 的父类之一。
List<Object> chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(method, targetClass);
Object retVal;
//如果没有配置 AOP 通知,那么直接使用 CGLIB 的 MethodProxy 对象完成对目标方法的调用
// 如果没有配置 AOP 通知,那么直接使用 CGLIB 的 MethodProxy 对象完成对目标方法的调用
if (chain.isEmpty() && Modifier.isPublic(method.getModifiers())) {
retVal = methodProxy.invoke(target, args);
}
else {
//通过 CglibMethodInvocation 来启动 advice 通知,
//CglibMethodInvocation 是 ReflectiveMethodInvocation 的子类
//最终还是调用的 ReflectiveMethodInvocation 对象的 proceed() 方法
// 通过 CglibMethodInvocation 来启动 advice 通知,
// CglibMethodInvocation 是 ReflectiveMethodInvocation 的子类
// 最终还是调用的 ReflectiveMethodInvocation 对象的 proceed()方法
retVal = new CglibMethodInvocation(proxy, target, method, args, targetClass, chain, methodProxy).proceed();
}
retVal = processReturnType(proxy, target, method, retVal);
@ -542,9 +550,10 @@ CglibAopProxy 的 intercept() 回调方法实现和 JdkDynamicAopProxy 的 invok
}
}
}
}
```
### 3.3目标对象中目标方法的调用
对目标对象中目标方法的调用,是在 AopUtils 工具类中利用反射机制完成的。具体代码如下:
### 3.3 目标对象中目标方法的调用
对目标对象中目标方法的调用,是在 AopUtils 工具类中利用反射机制完成的,具体代码如下。
```java
public abstract class AopUtils {
@ -556,9 +565,9 @@ public abstract class AopUtils {
throws Throwable {
try {
//如果 method 是 private 等不可访问状态,则设置为 public 公开可访问
// 如果该 method 是 private的则将其访问权限设为 public的
ReflectionUtils.makeAccessible(method);
//最后利用反射完成调用
// 最后利用反射完成调用
return method.invoke(target, args);
}
catch (InvocationTargetException ex) {
@ -574,64 +583,106 @@ public abstract class AopUtils {
}
}
```
### 3.4AOP 拦截器链的调用
### 3.4 AOP 拦截器链的调用
JdkDynamicAopProxy 和 CglibAopProxy 虽然使用了不同的代理对象,但对 AOP 拦截的处理却是相同的,都是通过 ReflectiveMethodInvocation 的 proceed() 方法实现的。
```java
public class ReflectiveMethodInvocation implements ProxyMethodInvocation, Cloneable {
protected final Object proxy;
protected final Object target;
protected final Method method;
protected Object[] arguments;
private final Class targetClass;
/** MethodInterceptor和InterceptorAndDynamicMethodMatcher的集合 */
protected final List interceptorsAndDynamicMethodMatchers;
private int currentInterceptorIndex = -1;
protected ReflectiveMethodInvocation(Object proxy, Object target, Method method,
Object[] arguments, Class targetClass,
List<Object> interceptorsAndDynamicMethodMatchers) {
this.proxy = proxy;
this.target = target;
this.targetClass = targetClass;
this.method = BridgeMethodResolver.findBridgedMethod(method);
this.arguments = arguments;
this.interceptorsAndDynamicMethodMatchers = interceptorsAndDynamicMethodMatchers;
}
public Object proceed() throws Throwable {
//从拦截器链中按顺序依次调用拦截器,直到所有的拦截器调用完毕,开始调用目标方法,
//对目标方法的调用是在 invokeJoinpoint() 中通过 AopUtils 的 invokeJoinpointUsingReflection() 方法完成的
// 从拦截器链中按顺序依次调用拦截器,直到所有的拦截器调用完毕,开始调用目标方法,对目标方法的调用
// 是在 invokeJoinpoint() 中通过 AopUtils 的 invokeJoinpointUsingReflection() 方法完成的
if (this.currentInterceptorIndex == this.interceptorsAndDynamicMethodMatchers.size() - 1) {
//invokeJoinpoint() 直接通过 AopUtils 进行目标方法的调用
// invokeJoinpoint() 直接通过 AopUtils 进行目标方法的调用
return invokeJoinpoint();
}
//这里沿着定义好的 interceptorsAndDynamicMethodMatchers 拦截器链进行处理,
//它是一个 List也没有定义泛型interceptorOrInterceptionAdvice 是其中的一个元素,
// 这里沿着定义好的 interceptorsAndDynamicMethodMatchers拦截器链 进行处理,
// 它是一个 List也没有定义泛型interceptorOrInterceptionAdvice 是其中的一个元素
Object interceptorOrInterceptionAdvice =
this.interceptorsAndDynamicMethodMatchers.get(++this.currentInterceptorIndex);
if (interceptorOrInterceptionAdvice instanceof InterceptorAndDynamicMethodMatcher) {
//这里通过拦截器的方法匹配器 methodMatcher 进行方法匹配,
//如果目标类的目标方法和配置的 Pointcut 匹配,那么这个增强行为 advice 将会被执行,
//Pointcut 定义了切面advice 定义了增强的行为
// 这里通过拦截器的 方法匹配器methodMatcher 进行方法匹配,
// 如果 目标类 的 目标方法 和配置的 Pointcut 匹配,那么这个 增强行为advice 将会被执行,
// Pointcut 定义了切面方法(要进行增强的方法)advice 定义了增强的行为
InterceptorAndDynamicMethodMatcher dm = (InterceptorAndDynamicMethodMatcher) interceptorOrInterceptionAdvice;
//目标类的目标方法是否为 Pointcut 所定义的切面
// 目标类的目标方法是否为 Pointcut 所定义的切面
if (dm.methodMatcher.matches(this.method, this.targetClass, this.arguments)) {
//执行增强方法
// 执行当前这个 拦截器interceptor 的 增强方法
return dm.interceptor.invoke(this);
}
else {
//如果不匹配,那么 process() 方法会被递归调用,直到所有的拦截器都被运行过为止
// 如果不匹配,那么 process()方法 会被递归调用,直到所有的拦截器都被运行过为止
return proceed();
}
}
else {
//如果 interceptorOrInterceptionAdvice 是一个 MethodInterceptor
//则直接调用其对应的方法
// 如果 interceptorOrInterceptionAdvice 是一个 MethodInterceptor
// 则直接调用其对应的方法
return ((MethodInterceptor) interceptorOrInterceptionAdvice).invoke(this);
}
}
}
```
### 3.5配置通知器
### 3.5 配置通知器
AdvisedSupport 中实现了获取拦截器链的方法,并使用了缓存。
```java
public class AdvisedSupport extends ProxyConfig implements Advised {
/** TargetSource持有一个比较重要的属性targetClass */
TargetSource targetSource = EMPTY_TARGET_SOURCE;
/** 缓存 Method对象 和其对应的 拦截器链列表List<Advisor> */
private transient Map<MethodCacheKey, List<Object>> methodCache;
/** The AdvisorChainFactory to use */
AdvisorChainFactory advisorChainFactory = new DefaultAdvisorChainFactory();
/**
* 获取拦截器链,为提高效率,同时设置了缓存
*/
public List<Object> getInterceptorsAndDynamicInterceptionAdvice(Method method, Class targetClass) {
//这里使用了缓存 cached如果缓存中有就从缓存中获取拦截器链
//没有,则调用 (DefaultAdvisorChainFactory)advisorChainFactory 的
//getInterceptorsAndDynamicInterceptionAdvice() 方法进行获取,并缓存到 cached
// 如果 缓存methodCache 中有就从缓存中获取 该Method对象 对应的拦截器链
// 没有,则调用 (DefaultAdvisorChainFactory)advisorChainFactory 的
// getInterceptorsAndDynamicInterceptionAdvice() 方法进行获取,并缓存到 methodCache 中
MethodCacheKey cacheKey = new MethodCacheKey(method);
List<Object> cached = this.methodCache.get(cacheKey);
if (cached == null) {
//缓存中没有,则从 AdvisorChainFactory 中获取,然后放进缓存
// 缓存中没有,则从 AdvisorChainFactory 中获取,然后放进缓存
cached = this.advisorChainFactory.getInterceptorsAndDynamicInterceptionAdvice(
this, method, targetClass);
this.methodCache.put(cacheKey, cached);
}
return cached;
}
}
```
获取拦截器链的工作是由 AdvisorChainFactory 完成的,他是一个拦截器链的生成工厂。由于 AdvisorChainFactory 接口只有一个实现类 DefaultAdvisorChainFactory所以我们直接看这个类中的实现就行咯。
@ -642,25 +693,25 @@ public class DefaultAdvisorChainFactory implements AdvisorChainFactory, Serializ
public List<Object> getInterceptorsAndDynamicInterceptionAdvice(
Advised config, Method method, Class targetClass) {
//advisor 链已经在传进来的 config 中持有了,这里可以直接使用
//advisor 中持有切面 和 增强行为的引用
// Advisor链 已经在传进来的 config 中持有了,这里可以直接使用
// Advisor 中持有 切面Pointcut 和 增强行为Advice 两个重要属性
List<Object> interceptorList = new ArrayList<Object>(config.getAdvisors().length);
//判断 config 中的 Advisors 是否符合配置要求
// 判断 config 中的 Advisors 是否符合配置要求
boolean hasIntroductions = hasMatchingIntroductions(config, targetClass);
//获取注册器,这是一个单例模式的实现
// 获取注册器,这是一个单例模式的实现
AdvisorAdapterRegistry registry = GlobalAdvisorAdapterRegistry.getInstance();
for (Advisor advisor : config.getAdvisors()) {
//advisor 如果是 PointcutAdvisor 的实例
// advisor 如果是 PointcutAdvisor 的实例
if (advisor instanceof PointcutAdvisor) {
PointcutAdvisor pointcutAdvisor = (PointcutAdvisor) advisor;
if (config.isPreFiltered() || pointcutAdvisor.getPointcut().getClassFilter().matches(targetClass)) {
//拦截器链是通过 AdvisorAdapterRegistry 的实例对象 registry 来加入的,
//AdvisorAdapterRegistry 对 advisor 的织入起到了很大的作用
// 拦截器链是通过 AdvisorAdapterRegistry 的实例对象 registry 来加入的,
// AdvisorAdapterRegistry 对 advisor 的织入起到了很大的作用
MethodInterceptor[] interceptors = registry.getInterceptors(advisor);
//从 pointcutAdvisor 中获取切面的方法匹配器
// 从 pointcutAdvisor 中获取切面的方法匹配器
MethodMatcher mm = pointcutAdvisor.getPointcut().getMethodMatcher();
//使用 MethodMatchers 的 matches() 方法对目标类的目标方法进行匹配判断
// 使用 MethodMatchers 的 matches()方法 对目标类的目标方法进行匹配判断
if (MethodMatchers.matches(mm, method, targetClass, hasIntroductions)) {
if (mm.isRuntime()) {
for (MethodInterceptor interceptor : interceptors) {
@ -673,7 +724,7 @@ public class DefaultAdvisorChainFactory implements AdvisorChainFactory, Serializ
}
}
}
//advisor 如果是 IntroductionAdvisor 的实例
// advisor 如果是 IntroductionAdvisor 的实例
else if (advisor instanceof IntroductionAdvisor) {
IntroductionAdvisor ia = (IntroductionAdvisor) advisor;
if (config.isPreFiltered() || ia.getClassFilter().matches(targetClass)) {
@ -704,21 +755,23 @@ public class DefaultAdvisorChainFactory implements AdvisorChainFactory, Serializ
}
return false;
}
}
```
这里的 advisor 通知器是从 AdvisedSupport 中获取的,而 advisor 的初始化则是在 ProxyFactoryBean 的 getObject() 方法中完成的。
```java
public class ProxyFactoryBean extends ProxyCreatorSupport
implements FactoryBean<Object>, BeanClassLoaderAware, BeanFactoryAware {
/**
* 返回一个代理对象,当用户从 FactoryBean 中获取 bean 时调用,
* 创建此工厂要返回的 AOP 代理的实例,该实例将作为一个单例被缓存
*/
public Object getObject() throws BeansException {
//初始化通知器链
// 初始化通知器链
initializeAdvisorChain();
//这里对 Singleton 和 prototype 的类型进行区分,生成对应的 proxy
// 这里对 Singleton 和 Prototype 的类型进行区分,生成对应的 proxy
if (isSingleton()) {
return getSingletonInstance();
}
@ -732,10 +785,10 @@ public class DefaultAdvisorChainFactory implements AdvisorChainFactory, Serializ
}
/**
* 初始化 Advisor 链,可以发现,其中有通过对 IoC 容器的 getBean() 方法的调用来获取配置好的 advisor 通知器
* 初始化 Advisor链可以发现其中有通过对 IoC容器 的 getBean() 方法的调用来获取配置好的 advisor 通知器
*/
private synchronized void initializeAdvisorChain() throws AopConfigException, BeansException {
//如果通知器链已经完成初始化,则直接返回
// 如果通知器链已经完成初始化,则直接返回
if (this.advisorChainInitialized) {
return;
}
@ -751,10 +804,10 @@ public class DefaultAdvisorChainFactory implements AdvisorChainFactory, Serializ
throw new AopConfigException("Target required after globals");
}
//这里添加了 Advisor 链的调用,下面的 interceptorNames 是在配置文件中
//通过 interceptorNames 进行配置的。由于每一个 Advisor 都是被配置为 bean 的,
//所以通过遍历 interceptorNames 得到的 name其实就是 bean 的 id通过这个 nameid
//我们就可以从 IoC 容器中获取对应的实例化 bean
// 这里添加了 Advisor链 的调用,下面的 interceptorNames 是在配置文件中
// 通过 interceptorNames 进行配置的。由于每一个 Advisor 都是被配置为 bean 的,
// 所以通过遍历 interceptorNames 得到的 name其实就是 beanAdvisor 的 id通过这个 nameid
// 我们就可以从 IoC 容器中获取对应的实例化 beanAdvisor
for (String name : this.interceptorNames) {
if (logger.isTraceEnabled()) {
logger.trace("Configuring advisor or advice '" + name + "'");
@ -770,56 +823,59 @@ public class DefaultAdvisorChainFactory implements AdvisorChainFactory, Serializ
}
else {
//对当前的 factoryBean 进行类型判断,是属于单例 bean 还是原型 bean
// 对当前的 factoryBean 进行类型判断,是属于 单例bean还是 原型bean
Object advice;
if (this.singleton || this.beanFactory.isSingleton(name)) {
//advisor 在文件中配置为 bean所以这里通过 beanFactory 的 getBean() 方法
//获取 advisor这个 name 是从 interceptorNames 中获取的
// advisor 在文件中配置为 bean所以这里通过 beanFactory 的 getBean()方法
// 获取 advisor这个 name 是从 interceptorNames 中获取的
advice = this.beanFactory.getBean(name);
}
else {
//如果是原型 bean
// 如果是 原型bean
advice = new PrototypePlaceholderAdvisor(name);
}
//把从 IoC 容器中获取的 advice 放进 advisors 拦截器链,这个拦截器链是由 ProxyFactoryBean
//的父类 AdvisedSupport 持有的
// 把从 IoC容器 中获取的 advice 放进 advisors 拦截器链,这个拦截器链是由 ProxyFactoryBean
// 的父类 AdvisedSupport 持有的
addAdvisorOnChainCreation(advice, name);
}
}
}
this.advisorChainInitialized = true;
}
}
```
注意Advisor 本身就被配置为 bean所以它的获取也是通过 IoC 容器获得的。
注意Advisor 本身就被配置为 bean所以它的获取也是通过 IoC容器 获得的。
### 3.6 Advice 通知的实现
### 3.6、Advice 通知的实现
从 DefaultAdvisorChainFactory 类中的 getInterceptorsAndDynamicInterceptionAdvice() 方法我们可以看到,其通过 AdvisorAdapterRegistry 实例对象的 getInterceptors() 方法,利用配置的 advisor 完成了对拦截器的适配和注册。
```java
public class DefaultAdvisorChainFactory implements AdvisorChainFactory, Serializable {
public List<Object> getInterceptorsAndDynamicInterceptionAdvice(
Advised config, Method method, Class targetClass) {
//advisor 链已经在传进来的 config 中持有了,这里可以直接使用
//advisor 中持有切面 和 增强行为的引用
// Advisor链 已经在传进来的 config 中持有了,这里可以直接使用
// Advisor 中持有 切面Pointcut 和 增强行为Advice 的引用
List<Object> interceptorList = new ArrayList<Object>(config.getAdvisors().length);
//判断 config 中的 Advisors 是否符合配置要求
// 判断 config 中的 Advisors 是否符合配置要求
boolean hasIntroductions = hasMatchingIntroductions(config, targetClass);
//获取注册器,这是一个单例模式的实现
// 获取注册器,这是一个单例模式的实现
AdvisorAdapterRegistry registry = GlobalAdvisorAdapterRegistry.getInstance();
for (Advisor advisor : config.getAdvisors()) {
//advisor 如果是 PointcutAdvisor 的实例
// advisor 如果是 PointcutAdvisor 的实例
if (advisor instanceof PointcutAdvisor) {
PointcutAdvisor pointcutAdvisor = (PointcutAdvisor) advisor;
if (config.isPreFiltered() || pointcutAdvisor.getPointcut().getClassFilter().matches(targetClass)) {
//拦截器链是通过 AdvisorAdapterRegistry 的实例对象 registry 来加入的,
//AdvisorAdapterRegistry 对 advisor 的织入起到了很大的作用
// 拦截器链是通过 AdvisorAdapterRegistry 的实例对象 registry 来加入的,
// AdvisorAdapterRegistry 对 advisor 的织入起到了很大的作用
MethodInterceptor[] interceptors = registry.getInterceptors(advisor);
//从 pointcutAdvisor 中获取切面的方法匹配器
// 从 pointcutAdvisor 中获取切面的方法匹配器
MethodMatcher mm = pointcutAdvisor.getPointcut().getMethodMatcher();
//使用 MethodMatchers 的 matches() 方法对目标类的目标方法进行匹配判断
// 使用 MethodMatchers 的 matches()方法 对目标类的目标方法进行匹配判断
if (MethodMatchers.matches(mm, method, targetClass, hasIntroductions)) {
if (mm.isRuntime()) {
for (MethodInterceptor interceptor : interceptors) {
@ -832,7 +888,7 @@ public class DefaultAdvisorChainFactory implements AdvisorChainFactory, Serializ
}
}
}
//advisor 如果是 IntroductionAdvisor 的实例
// advisor 如果是 IntroductionAdvisor 的实例
else if (advisor instanceof IntroductionAdvisor) {
IntroductionAdvisor ia = (IntroductionAdvisor) advisor;
if (config.isPreFiltered() || ia.getClassFilter().matches(targetClass)) {
@ -847,15 +903,16 @@ public class DefaultAdvisorChainFactory implements AdvisorChainFactory, Serializ
}
return interceptorList;
}
}
```
DefaultAdvisorAdapterRegistry 的 getInterceptors() 方法封装了 advice 织入实现的入口。
DefaultAdvisorAdapterRegistry 的 getInterceptors()方法 封装了 advice 织入实现的入口。
```java
public class DefaultAdvisorAdapterRegistry implements AdvisorAdapterRegistry, Serializable {
//持有 AdvisorAdapter 的 list这个 list 中的 AdvisorAdapter 与
//实现 spring AOP 的 advice 增强功能相对应
// 持有 AdvisorAdapter 的 list这个 list 中的 AdvisorAdapter 与
// 实现 Spring AOP 的 advice 增强功能相对应
private final List<AdvisorAdapter> adapters = new ArrayList<AdvisorAdapter>(3);
/**
@ -892,19 +949,18 @@ public class DefaultAdvisorAdapterRegistry implements AdvisorAdapterRegistry, Se
public MethodInterceptor[] getInterceptors(Advisor advisor) throws UnknownAdviceTypeException {
List<MethodInterceptor> interceptors = new ArrayList<MethodInterceptor>(3);
//从 Advisor 通知器中获取配置的 Advice
// 从 Advisor 通知器中获取配置的 Advice
Advice advice = advisor.getAdvice();
//如果 advice 是 MethodInterceptor 类型的,直接加进 interceptors不用适配
// 如果 advice 是 MethodInterceptor 类型的,直接加进 interceptors不用适配
if (advice instanceof MethodInterceptor) {
interceptors.add((MethodInterceptor) advice);
}
//对通知进行适配,使用已经配置好的三种 AdvisorAdapter然后从对应的
//adapter 中取出封装好的 AOP 编织功能的拦截器
// 对通知进行适配,使用已经配置好的三种 AdvisorAdapter然后从对应的
// adapter 中取出封装好的 AOP 编织功能的拦截器
for (AdvisorAdapter adapter : this.adapters) {
//adapter.supportsAdvice(advice) 方法中对 advice 的
//类型进行校验
// adapter.supportsAdvice(advice) 方法中对 advice 的类型进行校验
if (adapter.supportsAdvice(advice)) {
interceptors.add(adapter.getInterceptor(advisor));
}
@ -917,8 +973,7 @@ public class DefaultAdvisorAdapterRegistry implements AdvisorAdapterRegistry, Se
}
```
从 DefaultAdvisorAdapterRegistry 的实现中可以看到,其使用了一系列的 AdviceAdapter 适配器MethodBeforeAdviceAdapter、AfterReturningAdviceAdapter、ThrowsAdviceAdapter它们完全和 advice 的类型
一一对应,它们都是实现了 AdviceAdapter 接口的同一层次类,各自承担着不同的适配任务,一对一地服务于不同的 advice 实现。下面我们以 MethodBeforeAdviceAdapter 为例,看一下其源码实现。
从 DefaultAdvisorAdapterRegistry 的实现中可以看到,其使用了一系列的 AdviceAdapter 适配器MethodBeforeAdviceAdapter、AfterReturningAdviceAdapter、ThrowsAdviceAdapter它们完全和 Advice 的类型一一对应,它们都是实现了 AdviceAdapter 接口的同一层次类,各自承担着不同的适配任务,一对一地服务于不同的 Advice 实现。下面我们以 MethodBeforeAdviceAdapter 为例,看一下其源码实现。
```java
class MethodBeforeAdviceAdapter implements AdvisorAdapter, Serializable {
@ -931,11 +986,10 @@ class MethodBeforeAdviceAdapter implements AdvisorAdapter, Serializable {
MethodBeforeAdvice advice = (MethodBeforeAdvice) advisor.getAdvice();
return new MethodBeforeAdviceInterceptor(advice);
}
}
```
可以看到,其中的 getInterceptor() 方法把 advice 从 advisor 中取出来,然后创建了一个 MethodBeforeAdviceInterceptor 对象,并返回,这个对象中持有对 advice 的引用。下面我们看一下 MethodBeforeAdviceInterceptor 拦截器的源码实现。
可以看到,其中的 getInterceptor()方法 把 Advice 从 Advisor 中取出来,然后创建了一个 MethodBeforeAdviceInterceptor对象并返回这个对象中持有对 Advice 的引用。下面我们看一下 MethodBeforeAdviceInterceptor 拦截器的源码实现。
```java
public class MethodBeforeAdviceInterceptor implements MethodInterceptor, Serializable {
@ -951,21 +1005,20 @@ public class MethodBeforeAdviceInterceptor implements MethodInterceptor, Seriali
}
/**
* 这个 invoke 方法是拦截器的回调方法,会在代理对象的方法被调用时触发回调
* 这个 invoke()方法 是拦截器的回调方法,会在代理对象的方法被调用时触发回调
*/
public Object invoke(MethodInvocation mi) throws Throwable {
//首先触发了 advice 的 before() 方法的回调
//然后才是 MethodInvocation 的 process() 方法回调
// 首先触发了 advice对象 的 before()方法 的回调
// 然后才是 MethodInvocation 的 process()方法 回调
this.advice.before(mi.getMethod(), mi.getArguments(), mi.getThis() );
return mi.proceed();
}
}
```
可以看到MethodBeforeAdviceInterceptor 的 invoke() 方法先是触发了 advice 的 before() 方法,然后才是 MethodInvocation 的 proceed() 方法调用。
回顾一下之前的代码,在 AopProxy 代理对象触发的 ReflectiveMethodInvocation 的 proceed() 中,在取得拦截器 interceptor 后调用了其 invoke() 方法。按照 AOP 的配置规则ReflectiveMethodInvocation 触发的拦截器 invoke() 回调,最终会根据 advice 类型的不同,触发 spring 对不同的 advice 的拦截器封装,比如 MethodBeforeAdvice 最终会触发 MethodBeforeAdviceInterceptor 的 invoke() 回调,其它两个以此类推,这里就不逐一分析咯。
可以看到MethodBeforeAdviceInterceptor 的 invoke()方法 先是触发了 advice 的 before()方法,然后才是 MethodInvocation 的 proceed()方法调用。
另外,可以结合我 GitHub 上对 spring 框架源码的阅读及个人理解一起看,会更有助于各位开发大佬理解,如果对你们有帮助的,还望各位老爷 watchstarfork素质三连一波地址https://github.com/AmyliaY/spring-aop-reading
回顾一下之前的代码,在 AopProxy代理对象 触发的 ReflectiveMethodInvocation 的 proceed() 中,在取得 拦截器interceptor 后调用了其 invoke()方法。按照 AOP 的配置规则ReflectiveMethodInvocation 触发的拦截器 invoke()回调,最终会根据 Advice 类型的不同,触发 Spring 对不同的 Advice 的拦截器封装,比如 MethodBeforeAdvice 最终会触发 MethodBeforeAdviceInterceptor 的 invoke()回调,其它两个以此类推,这里就不逐一分析咯。
另外,可以结合我 GitHub 上对 Spring框架源码 的阅读及个人理解一起看,会更有助于各位开发大佬理解,如果本内容对你们有帮助的,还望各位同学 watchstarfork素质三连一波地址
https://github.com/AmyliaY/spring-aop-reading

@ -1,11 +1,11 @@
最近在看spring AOP部分的源码所以对JDK动态代理具体是如何实现的这件事产生了很高的兴趣而且能从源码上了解这个原理的话也有助于对spring-aop模块的理解。话不多说上代码。
最近在看 Spring AOP 部分的源码所以对JDK动态代理具体是如何实现的这件事产生了很高的兴趣而且能从源码上了解这个原理的话也有助于对 spring-aop 模块的理解。话不多说,上代码。
```java
/**
* 一般会使用实现了 InvocationHandler 的类作为代理对象的生产工厂,
* 并且通过持有被代理对象target来在invoke()方法中对被代理对象的目标方法进行调用和增强,
* 这些我们都能通过下面这段代码看懂但代理对象是如何生成的invoke()方法又是如何被调用的呢?
* 一般会使用实现了 InvocationHandler接口 的类作为代理对象的生产工厂,
* 并且通过持有 被代理对象target来在 invoke()方法 中对被代理对象的目标方法进行调用和增强,
* 这些我们都能通过下面这段代码看懂但代理对象是如何生成的invoke()方法 又是如何被调用的呢?
*/
public class ProxyFactory implements InvocationHandler{
public class ProxyFactory implements InvocationHandler {
private Object target = null;
@ -29,14 +29,13 @@ public class ProxyFactory implements InvocationHandler{
}
/**
* 实现了接口MyInterface和接口的play()方法,可以作为被代理类
* 实现了 接口MyInterface 和接口的 play()方法,可以作为被代理类
*/
public class TargetObject implements MyInterface {
@Override
public void play() {
System.out.println("妲己,陪你玩 ~");
}
}
@ -47,26 +46,26 @@ public class ProxyTest {
public static void main(String[] args) {
TargetObject target = new TargetObject();
// ProxyFactory 实现了 InvocationHandler接口其中的 getInstanse() 方法利用 Proxy
// 生成了target目标对象的代理对象并返回且ProxyFactory持有对target的引用,可以在
// ProxyFactory 实现了 InvocationHandler接口其中的 getInstanse()方法 利用 Proxy类
// 生成了 target目标对象 的代理对象,并返回;且 ProxyFactory 持有对 target 的引用,可以在
// invoke() 中完成对 target 相应方法的调用,以及目标方法前置后置的增强处理
ProxyFactory proxyFactory = new ProxyFactory();
// 这个mi就是JDK的 Proxy 类动态生成的代理类 $Proxy0 的实例,该实例中的方法都持有对
// invoke() 方法的回调,所以当调用其方法时,就能够执行 invoke() 中的增强处理
MyInterface mi = (MyInterface)proxyFactory.getInstanse(target);
// 这个 mi 就是 JDK 的 Proxy类 动态生成的代理类 $Proxy0 的实例,该实例中的方法都持有对
// invoke()方法 的回调,所以当调用其方法时,就能够执行 invoke() 中的增强处理
MyInterface mi = (MyInterface) proxyFactory.getInstanse(target);
// 这样可以看到 mi 的 Class 到底是什么
System.out.println(mi.getClass());
// 这里实际上调用的就是 $Proxy0代理类中对 play() 方法的实现,结合下面的代码可以看到
// play() 方法通过 super.h.invoke() 完成了对 InvocationHandler对象(proxyFactory)中
// invoke()方法的回调,所以我们才能够通过 invoke() 方法实现对 target 对象方法的
// 这里实际上调用的就是 $Proxy0代理类 中对 play()方法 的实现,结合下面的代码可以看到
// play()方法 通过 super.h.invoke() 完成了对 InvocationHandler对象(proxyFactory)中
// invoke()方法 的回调,所以我们才能够通过 invoke()方法 实现对 target对象 方法的
// 前置后置增强处理
mi.play();
// 总的来说就是在invoke()方法中完成target目标方法的调用,及前置后置增强,
// JDK动态生成的代理类中对 invoke() 方法进行了回调
// 总的来说,就是在 invoke()方法 中完成 target目标方法 的调用,及前置后置增强,
// JDK 动态生成的代理类中对 invoke()方法 进行了回调
}
/**
* 将ProxyGenerator生成的动态代理类的输出到文件中利用反编译工具luyten等就可
* 将 ProxyGenerator 生成的动态代理类的输出到文件中,利用反编译工具 luyten 等就可
* 以看到生成的代理类的源码咯,下面给出了其反编译好的代码实现
*/
@Test
@ -93,10 +92,9 @@ public class ProxyTest {
}
/**
* Proxy生成的代理类可以看到其继承了Proxy并且实现了被代理类的接口
* Proxy 生成的代理类,可以看到,其继承了 Proxy并且实现了 被代理类的接口MyInterface
*/
public final class $Proxy0 extends Proxy implements MyInterface
{
public final class $Proxy0 extends Proxy implements MyInterface {
private static Method m1;
private static Method m0;
private static Method m3;
@ -106,7 +104,7 @@ public final class $Proxy0 extends Proxy implements MyInterface
try {
$Proxy0.m1 = Class.forName("java.lang.Object").getMethod("equals", Class.forName("java.lang.Object"));
$Proxy0.m0 = Class.forName("java.lang.Object").getMethod("hashCode", (Class<?>[])new Class[0]);
//实例化MyInterface的play方法
// 实例化 MyInterface play()方法
$Proxy0.m3 = Class.forName("com.shuitu.test.MyInterface").getMethod("play", (Class<?>[])new Class[0]);
$Proxy0.m2 = Class.forName("java.lang.Object").getMethod("toString", (Class<?>[])new Class[0]);
}
@ -124,9 +122,9 @@ public final class $Proxy0 extends Proxy implements MyInterface
public final void play() {
try {
// 这个 h 其实就是我们调用 Proxy.newProxyInstance() 方法时传进去的ProxyFactory(InvocationHandler对象)
// 该对象的 invoke() 方法中实现了对目标对象的目标方法的增强。看到这里,利用动态代理实现方法增强的
// 实现原理就全部理清咯
// 这个 h 其实就是我们调用 Proxy.newProxyInstance()方法 时传进去的 ProxyFactory对象(它实现了
// InvocationHandler接口),该对象的 invoke()方法 中实现了对目标对象的目标方法的增强。
// 看到这里,利用动态代理实现方法增强的实现原理就全部理清咯
super.h.invoke(this, $Proxy0.m3, null);
}
catch (Error | RuntimeException error) {

@ -1,17 +1,21 @@
这篇文章分享一下 spring IoC 容器初始化第三部分的代码,也就是将前面解析得到的 BeanDefinition 注册进 IoC 容器,其实就是存入一个 ConcurrentHashMap<String, BeanDefinition> 中。
PS可以结合我 GitHub 上对 spring 框架源码的翻译注解一起看,会更有助于各位同学理解,地址:
## 前言
这篇文章分享一下 spring IoC 容器初始化第三部分的代码,也就是将前面解析出来的 BeanDefinition对象 注册进 IoC 容器,其实就是存入一个 ConcurrentHashMap<String, BeanDefinition> 中。
PS可以结合我 GitHub 上对 Spring 框架源码的翻译注释一起看,会更有助于各位同学理解,地址:
spring-beans https://github.com/AmyliaY/spring-beans-reading
spring-context https://github.com/AmyliaY/spring-context-reading
## 1、回过头看一下前面在 DefaultBeanDefinitionDocumentReader 中实现的 processBeanDefinition(Element ele, BeanDefinitionParserDelegate delegate) 方法
## 正文
回过头看一下前面在 DefaultBeanDefinitionDocumentReader 中实现的 processBeanDefinition(Element ele, BeanDefinitionParserDelegate delegate) 方法。
```java
// 解析 Bean 定义资源 Document 对象的普通元素
protected void processBeanDefinition(Element ele, BeanDefinitionParserDelegate delegate) {
/**
* 将 .xml 文件中的元素解析成 BeanDefinition对象并注册到 IoC容器 中
*/
protected void processBeanDefinition(Element ele, BeanDefinitionParserDelegate delegate) {
// BeanDefinitionHolder 是对 BeanDefinition 的封装,即 BeanDefinition 的封装类
// BeanDefinitionHolder 是对 BeanDefinition 的进一步封装,它持有一个 BeanDefinition 对象 及其对应
// 的 beanName、aliases别名。
// 对 Document 对象中 <Bean> 元素的解析由 BeanDefinitionParserDelegate 实现
BeanDefinitionHolder bdHolder = delegate.parseBeanDefinitionElement(ele);
if (bdHolder != null) {
@ -20,7 +24,7 @@ spring-context https://github.com/AmyliaY/spring-context-reading
try {
/**
*
* 向 Spring IoC 容器注册解析 BeanDefinition,这是 BeanDefinition 向 IoC 容器注册的入口
* 向 IoC 容器注册解析完成的 BeanDefinition对象,这是 BeanDefinition 向 IoC 容器注册的入口
*
*/
BeanDefinitionReaderUtils.registerBeanDefinition(bdHolder, getReaderContext().getRegistry());
@ -29,46 +33,59 @@ spring-context https://github.com/AmyliaY/spring-context-reading
getReaderContext().error("Failed to register bean definition with name '" +
bdHolder.getBeanName() + "'", ele, ex);
}
// 在完成向 Spring IOC 容器注册解析得到的 Bean 定义之后,发送注册事件
// 在完成向 IOC容器 注册 BeanDefinition对象 之后,发送注册事件
getReaderContext().fireComponentRegistered(new BeanComponentDefinition(bdHolder));
}
}
}
```
## 2、BeanDefinitionReaderUtils 的 registerBeanDefinition(BeanDefinitionHolder definitionHolder, BeanDefinitionRegistry registry) 方法
接着看一下 BeanDefinitionReaderUtils 的 registerBeanDefinition(BeanDefinitionHolder definitionHolder, BeanDefinitionRegistry registry) 方法
```java
// 将解析的 BeanDefinitionHold 注册到容器中
public static void registerBeanDefinition(
/**
* 将解析到的 BeanDefinition对象 注册到 IoC容器
*/
public static void registerBeanDefinition(
BeanDefinitionHolder definitionHolder, BeanDefinitionRegistry registry)
throws BeanDefinitionStoreException {
// 获取解析的 BeanDefinition 的名称
// 获取解析的 <bean>元素 的名称 beanName
String beanName = definitionHolder.getBeanName();
/**
*
* 开始向 IOC 容器注册 BeanDefinition
* 开始向 IoC容器 注册 BeanDefinition对象
*
*/
registry.registerBeanDefinition(beanName, definitionHolder.getBeanDefinition());
// 如果解析的 BeanDefinition 有别名,向容器为其注册别名
// 如果解析的 <bean>元素 有别名alias向容器中注册别名
String[] aliases = definitionHolder.getAliases();
if (aliases != null) {
for (String aliase : aliases) {
registry.registerAlias(beanName, aliase);
}
}
}
}
```
## 3、BeanDefinitionRegistry 中的 registerBeanDefinition(String beanName, BeanDefinition beanDefinition) 方法在 DefaultListableBeanFactory 实现类中的具体实现
BeanDefinitionRegistry 中的 registerBeanDefinition(String beanName, BeanDefinition beanDefinition) 方法在 DefaultListableBeanFactory 实现类中的具体实现
```java
// 向 IoC 容器注册解析的 BeanDefinito
public class DefaultListableBeanFactory extends AbstractAutowireCapableBeanFactory
implements ConfigurableListableBeanFactory, BeanDefinitionRegistry, Serializable {
/** 按注册顺序排列的 beanDefinition名称列表(即 beanName) */
private final List<String> beanDefinitionNames = new ArrayList<String>();
/** IoC容器 的实际体现key --> beanNamevalue --> BeanDefinition对象 */
private final Map<String, BeanDefinition> beanDefinitionMap = new ConcurrentHashMap<String, BeanDefinition>(64);
/**
* 向 IoC容器 注册解析的 beanName 和 BeanDefinition对象
*/
public void registerBeanDefinition(String beanName, BeanDefinition beanDefinition)
throws BeanDefinitionStoreException {
Assert.hasText(beanName, "Bean name must not be empty");
Assert.notNull(beanDefinition, "BeanDefinition must not be null");
// 校验解析的 BeanDefiniton
// 校验解析的 BeanDefiniton对象
if (beanDefinition instanceof AbstractBeanDefinition) {
try {
((AbstractBeanDefinition) beanDefinition).validate();
@ -83,34 +100,34 @@ spring-context https://github.com/AmyliaY/spring-context-reading
synchronized (this.beanDefinitionMap) {
Object oldBeanDefinition = this.beanDefinitionMap.get(beanName);
// 检查是否有同名的 BeanDefinition 已经在 IOC 容器中注册,如果已经注册,
// 并且不允许覆盖已注册的 BeanDefinition则抛出注册失败异常
// allowBeanDefinitionOverriding 默认为 true
// 检查是否有同名(beanName)的 BeanDefinition 存在于 IoC容器 中,如果已经存在,且不允许覆盖
// 已注册的 BeanDefinition则抛出注册异常allowBeanDefinitionOverriding 默认为 true
if (oldBeanDefinition != null) {
if (!this.allowBeanDefinitionOverriding) {
throw new BeanDefinitionStoreException(beanDefinition.getResourceDescription(), beanName,
"Cannot register bean definition [" + beanDefinition + "] for bean '" + beanName +
"': There is already [" + oldBeanDefinition + "] bound.");
}
else {// 如果允许覆盖,则同名的 Bean后注册的覆盖先注册的
// 如果允许覆盖同名的 bean后注册的会覆盖先注册的
else {
if (this.logger.isInfoEnabled()) {
this.logger.info("Overriding bean definition for bean '" + beanName +
"': replacing [" + oldBeanDefinition + "] with [" + beanDefinition + "]");
}
}
}
else {// IOC 容器中没有已经注册同名的 Bean按正常注册流程注册
// 若该 beanName 在 IoC容器 中尚未注册,将其注册到 IoC容器中
else {
// 将 beanName 注册到 beanDefinitionNames列表
this.beanDefinitionNames.add(beanName);
this.frozenBeanDefinitionNames = null;
}
// beanDefinitionMap 是 IoC容器 的最主要体现,他是一个 ConcurrentHashMap
// 直接存储了 bean的唯一标识 beanName及其对应的 BeanDefinition对象
this.beanDefinitionMap.put(beanName, beanDefinition);
}
// 重置所有已经注册过的 BeanDefinition 的缓存
resetBeanDefinition(beanName);
}
```
## 最后看一下 spring 的 IoC 容器在代码中最直接的体现
```java
// 存储注册信息的 BeanDefinition 集合,也就是所谓的 IoC 容器
private final Map<String, BeanDefinition> beanDefinitionMap = new ConcurrentHashMap<String, BeanDefinition>(64);
}
```

@ -1,53 +1,55 @@
前面我们主要分析了 FileSystemXmlApplicationContext 这个具体的 IoC 容器的初始化源码实现,在 IoC 容器中建立了 BeanDefinition 的数据映射,将其和 beanName 一起绑定在一个 ConcurrentHashMap 中。现在我们来看一下 spring 是如何将 IoC 容器中的 Bean 根据配置关联在一起的。
## 前言
前面我们主要分析了 FileSystemXmlApplicationContext 这个具体的 IoC容器实现类 的初始化源码,在 IoC容器 中建立了 beanName 到 BeanDefinition 的数据映射,通过一个 ConcurrentHashMap。现在我们来看一下 Spring 是如何将 IoC 容器中存在依赖关系的 bean 根据配置联系在一起的。
Spring 中触发 IoC 容器“依赖注入”的方式有两种,一个是通过 getBean() 向容器索要 bean 时触发依赖注入;另一个是给 bean 配置 lazy-init 属性spring 会自动调用此 bean 的 getBean() 方法,提前完成依赖注入。总的来说,想提高运行时获取 bean 的效率,可以考虑配置此属性。
Spring 中触发 IoC容器“依赖注入” 的方式有两种,一个是应用程序通过 getBean()方法 向容器索要 bean实例 时触发依赖注入;另一个是提前给 bean 配置了 lazy-init 属性为 falseSpring 在 IoC容器 初始化会自动调用此 bean 的 getBean() 方法,提前完成依赖注入。总的来说,想提高运行时获取 bean 的效率,可以考虑配置此属性。
下面我将分别解读这两种依赖注入的触发方式,先看 getBean() 的,因为 lazy-init 最后也是通过调用 getBean 完成的依赖注入。
PS可以结合我 GitHub 上对 spring 框架源码的阅读及个人理解一起看,会更有助于各位开发姥爷理解,地址:
下面我将分别解读这两种依赖注入的触发方式,先看 getBean() 的,因为 lazy-init 最后也是通过调用 getBean() 完成的依赖注入。
PS可以结合我 GitHub 上对 Spring 框架源码的阅读及个人理解一起看,会更有助于各位开发姥爷理解,地址:
spring-beans https://github.com/AmyliaY/spring-beans-reading
spring-context https://github.com/AmyliaY/spring-context-reading
## 1、AbstractBeanFactory 中的 getBean() 系列方法及 doGetBean() 具体实现
## 正文
首先看一下 AbstractBeanFactory 中的 getBean() 系列方法及 doGetBean() 具体实现。
```java
public abstract class AbstractBeanFactory extends FactoryBeanRegistrySupport implements ConfigurableBeanFactory {
//---------------------------------------------------------------------
// BeanFactory 接口的实现,下列的 getBean() 方法不论是哪种重载,最后都会走
// doGetBean(final String name, final Class<T> requiredType, final Object[] args, boolean typeCheckOnly) 的具体实现
//---------------------------------------------------------------------
// 获取 IOC 容器中指定名称的 Bean
// 获取 IoC容器 中指定名称的 bean
public Object getBean(String name) throws BeansException {
return doGetBean(name, null, null, false);
}
// 获取 IOC 容器中指定名称和类型的 Bean
// 获取 IoC容器 中指定名称和类型的 bean
public <T> T getBean(String name, Class<T> requiredType) throws BeansException {
return doGetBean(name, requiredType, null, false);
}
// 获取 IOC 容器中指定名称和参数的 Bean
// 获取 IoC容器 中指定名称和参数的 bean
public Object getBean(String name, Object... args) throws BeansException {
return doGetBean(name, null, args, false);
}
// 获取 IOC 容器中指定名称、类型和参数的 Bean
// 获取 IoC容器 中指定名称、类型和参数的 bean
public <T> T getBean(String name, Class<T> requiredType, Object... args) throws BeansException {
return doGetBean(name, requiredType, args, false);
}
// 真正实现向 IOC 容器获取 Bean 的功能,也是触发依赖注入 (DI) 功能的地方
// 真正实现向 IoC容器 获取 bean 的功能,也是触发 依赖注入(DI) 的地方
@SuppressWarnings("unchecked")
protected <T> T doGetBean(final String name, final Class<T> requiredType, final Object[] args,
boolean typeCheckOnly) throws BeansException {
// 根据用户指定的名称获取 IoC 容器中与 BeanDefinition 对应的 beanName
// 如果指定的是别名,则将别名转换为规范的 beanName
// 根据用户给定的名称(也可能是别名alias) 获取 IoC容器 中与 BeanDefinition 唯一对应的 beanName
final String beanName = transformedBeanName(name);
Object bean;
// 先查看缓存中是否有对应的,已创建的单例 Bean对于单例 Bean整个 IOC 容器中只创建一次
// 根据 beanName 查看缓存中是否有已实例化的 单例bean对于 单例bean整个 IoC容器 只创建一次
Object sharedInstance = getSingleton(beanName);
if (sharedInstance != null && args == null) {
if (logger.isDebugEnabled()) {
@ -59,12 +61,12 @@ spring-context https://github.com/AmyliaY/spring-context-reading
logger.debug("Returning cached instance of singleton bean '" + beanName + "'");
}
}
// 获取给定 Bean 的实例对象,主要是完成 FactoryBean 的相关处理
// 注意BeanFactory 本质上是一个 IoC 容器,而 FactoryBean 是 IoC 容器中一种特殊的工厂 bean
// 能够生产其他对象,注意两者之间的区别
// 获取给定 bean 的实例对象,主要是完成 FactoryBean 的相关处理
// 注意BeanFactory 是一个 IoC容器它保存了 bean 的基本配置信息。
// 而 FactoryBean 是 IoC容器 中一种特殊的 bean它能够实例化 bean对象,注意两者之间的区别
bean = getObjectForBeanInstance(sharedInstance, name, beanName, null);
}
// 如果应用程序要获取的 bean 还未创建
else {
if (isPrototypeCurrentlyInCreation(beanName)) {
throw new BeanCurrentlyInCreationException(beanName);
@ -75,7 +77,7 @@ spring-context https://github.com/AmyliaY/spring-context-reading
// 如果当前容器中没有指定的 bean且当前容器的父容器不为空
// 则从父容器中去找,如果父容器也没有,则沿着当前容器的继承体系一直向上查找
if (parentBeanFactory != null && !containsBeanDefinition(beanName)) {
// 解析指定 Bean 名称的原始名称
// 根据用户传入的 name(有可能是别名alias),获取唯一标识的 beanName
String nameToLookup = originalBeanName(name);
if (args != null) {
// 委派父级容器根据指定名称和显式的参数查找
@ -87,9 +89,9 @@ spring-context https://github.com/AmyliaY/spring-context-reading
}
}
// 创建的 Bean 是否需要进行类型验证,一般不需要
// 创建的 bean 是否需要进行类型验证,一般不需要
if (!typeCheckOnly) {
// 向容器标记指定的 Bean 已经被创建
// 向容器标记指定的 bean 已经被创建
markBeanAsCreated(beanName);
}
@ -98,26 +100,28 @@ spring-context https://github.com/AmyliaY/spring-context-reading
final RootBeanDefinition mbd = getMergedLocalBeanDefinition(beanName);
checkMergedBeanDefinition(mbd, beanName, args);
// 获取当前 Bean 依赖的所有 Bean下面的 getBean(dependsOnBean) 方法会触发 getBean() 的递归调用,
// 直到取到一个不依赖任何其它 bean 的 bean 为止
// 获取当前 bean 所依赖bean 的 beanName下面的 getBean(dependsOnBean) 方法会触发
// getBean() 的递归调用,直到取到一个不依赖任何其它 bean 的 bean 为止。
// 比如beanA 依赖了 beanB而 beanB 依赖了 beanC那么在实例化 beanA 时会先实例化
// beanC然后实例化 beanB 并将 beanC 注入进去,最后实例化 beanA 时将 beanB 注入
String[] dependsOn = mbd.getDependsOn();
if (dependsOn != null) {
for (String dependsOnBean : dependsOn) {
// 递归调用 getBean() 方法,获取当前 Bean 所依赖的 bean
// 递归调用 getBean() 方法,从末级节点依次实例化 依赖的bean
getBean(dependsOnBean);
// 把当前 bean 所依赖的 bean 进行注入
// 把 当前bean 直接依赖的bean 进行注册
//(也就是通过 setter 或构造方法将依赖的 bean 赋值给当前 bean 对应的属性)
registerDependentBean(dependsOnBean, beanName);
}
}
// 创建单例模式 bean 的实例对象
// 如果当前 bean 是单例的
if (mbd.isSingleton()) {
// 这里使用了一个匿名内部类,创建 Bean 实例对象,并且注册给所依赖的对象
// 这里使用了一个匿名内部类,创建 bean实例对象
sharedInstance = getSingleton(beanName, new ObjectFactory<Object>() {
public Object getObject() throws BeansException {
try {
// 创建一个指定 Bean 实例对象,如果有父级继承,则合并子类和父类的定义
// 根据给定的 beanName 及 RootBeanDefinition对象创建 bean 实例对象
return createBean(beanName, mbd, args);
}
catch (BeansException ex) {
@ -126,41 +130,42 @@ spring-context https://github.com/AmyliaY/spring-context-reading
}
}
});
// 获取给定 Bean 的实例对象
// 获取给定 bean 的实例对象
bean = getObjectForBeanInstance(sharedInstance, name, beanName, mbd);
}
// IoC 容器创建原型模式 Bean 实例对象
// 创建原型模式的 bean 实例对象
else if (mbd.isPrototype()) {
// 原型模式 (Prototype) 是每次都会创建一个新的对象
// 原型模式 (Prototype) 每次都会创建一个新的对象
Object prototypeInstance = null;
try {
// 回调 beforePrototypeCreation 方法,默认的功能是注册当前创建的原型对象
// 回调 beforePrototypeCreation() 方法,默认的功能是注册当前创建的原型对象
beforePrototypeCreation(beanName);
// 创建指定 Bean 对象实例
// 创建指定 bean 对象实例
prototypeInstance = createBean(beanName, mbd, args);
}
finally {
// 回调 afterPrototypeCreation 方法,默认的功能告诉 IoC 容器指定 Bean 的原型对象不再创建了
// 回调 afterPrototypeCreation() 方法,默认的功能是告诉 IoC容器
// 指定 bean 的原型对象不再创建了
afterPrototypeCreation(beanName);
}
// 获取给定 Bean 的实例对象
// 获取给定 bean 的实例对象
bean = getObjectForBeanInstance(prototypeInstance, name, beanName, mbd);
}
// 要创建的 Bean 既不是单例模式,也不是原型模式,则根据 Bean 定义资源
// 配置的生命周期范围,选择实例化 Bean 的合适方法,这种在 Web 应用程序中
// 要创建的 bean 既不是单例模式,也不是原型模式,则根据该 bean元素 在配置文件
// 配置的生命周期范围,选择实例化 bean 的合适方法,这种在 Web 应用程序中
// 比较常用request、session、application 等的生命周期
else {
// 获取此 bean 生命周期的范围
String scopeName = mbd.getScope();
final Scope scope = this.scopes.get(scopeName);
// Bean 定义资源中没有配置生命周期范围,则 Bean 定义不合法
// bean 定义资源中没有配置生命周期范围,则该 bean 的配置不合法
if (scope == null) {
throw new IllegalStateException("No Scope registered for scope '" + scopeName + "'");
}
try {
// 这里又使用了一个匿名内部类,获取一个指定生命周期范围的实例
// 这里又使用了一个 ObjectFactory 的匿名内部类,获取一个指定生命周期范围的实例
Object scopedInstance = scope.get(beanName, new ObjectFactory<Object>() {
public Object getObject() throws BeansException {
beforePrototypeCreation(beanName);
@ -172,7 +177,7 @@ spring-context https://github.com/AmyliaY/spring-context-reading
}
}
});
// 获取给定 Bean 的实例对象
// 获取给定 bean 的实例对象
bean = getObjectForBeanInstance(scopedInstance, name, beanName, mbd);
}
catch (IllegalStateException ex) {
@ -189,7 +194,7 @@ spring-context https://github.com/AmyliaY/spring-context-reading
}
}
// 对创建的 bean 实例对象进行非空验证和类型检查,如果没问题就返回这个已经完成依赖注入的 bean
// 对要返回的 bean实例对象 进行非空验证和类型检查,如果没问题就返回这个已经完成 依赖注入的bean
if (requiredType != null && bean != null && !requiredType.isAssignableFrom(bean.getClass())) {
try {
return getTypeConverter().convertIfNecessary(bean, requiredType);
@ -204,12 +209,16 @@ spring-context https://github.com/AmyliaY/spring-context-reading
}
return (T) bean;
}
}
```
总的来说getBean() 方法是依赖注入的起点,之后会调用 createBean(),根据 BeanDefinition 的定义生成 bean 对象,下面我们看看 AbstractBeanFactory 的子类 AbstractAutowireCapableBeanFactory 中对 createBean() 的具体实现。
## 2、AbstractAutowireCapableBeanFactory 中的 createBean() 和 doCreateBean() 具体实现
总的来说getBean() 方法是依赖注入的起点,之后会调用 createBean(),根据之前解析生成的 BeanDefinition对象 生成 bean 对象,下面我们看看 AbstractBeanFactory 的子类 AbstractAutowireCapableBeanFactory 中对 createBean() 的具体实现。
```java
// 创建指定的 bean 实例对象
public abstract class AbstractAutowireCapableBeanFactory extends AbstractBeanFactory
implements AutowireCapableBeanFactory {
/**
* 创建指定的 bean 实例对象
*/
@Override
protected Object createBean(final String beanName, final RootBeanDefinition mbd, final Object[] args)
throws BeanCreationException {
@ -217,11 +226,11 @@ spring-context https://github.com/AmyliaY/spring-context-reading
if (logger.isDebugEnabled()) {
logger.debug("Creating instance of bean '" + beanName + "'");
}
// 判断需要创建的 Bean 是否可以实例化,是否可以通过当前的类加载器加载
// 判断需要创建的 bean 是否可实例化,是否可以通过当前的类加载器加载
resolveBeanClass(mbd, beanName);
try {
// 校验和准备 Bean 中的方法覆盖
// 校验和准备 bean 中的方法覆盖
mbd.prepareMethodOverrides();
}
catch (BeanDefinitionValidationException ex) {
@ -230,7 +239,7 @@ spring-context https://github.com/AmyliaY/spring-context-reading
}
try {
// 如果 Bean 配置了后置处理器 PostProcessor则这里返回一个 proxy 代理对象
// 如果 bean 配置了后置处理器 PostProcessor则这里返回一个 proxy 代理对象
Object bean = resolveBeforeInstantiation(beanName, mbd);
if (bean != null) {
return bean;
@ -241,7 +250,7 @@ spring-context https://github.com/AmyliaY/spring-context-reading
"BeanPostProcessor before instantiation of bean failed", ex);
}
// 创建 Bean 实例对象的具体实现
// 创建 bean 实例对象的具体实现
Object beanInstance = doCreateBean(beanName, mbd, args);
if (logger.isDebugEnabled()) {
logger.debug("Finished creating instance of bean '" + beanName + "'");
@ -249,17 +258,17 @@ spring-context https://github.com/AmyliaY/spring-context-reading
return beanInstance;
}
// 创建 Bean 实例对象的具体实现spring 中以 do 开头的都是方法的具体实现
/**
* 创建 bean 实例对象的具体实现Spring 中以 do 开头的都是方法的具体实现
*/
protected Object doCreateBean(final String beanName, final RootBeanDefinition mbd, final Object[] args) {
// 封装被创建的 Bean 对象
BeanWrapper instanceWrapper = null;
// 如果这个 bean 是单例的,则从缓存中获取这个 BeanWrapper 实例并清除
// 如果这个 bean 是单例的,则从缓存中获取这个 beanName 对应的 BeanWrapper实例并清除
if (mbd.isSingleton()) {
instanceWrapper = this.factoryBeanInstanceCache.remove(beanName);
}
if (instanceWrapper == null) {
/**
*
* 创建实例对象
@ -280,7 +289,7 @@ spring-context https://github.com/AmyliaY/spring-context-reading
}
}
// 向容器中缓存单例模式的 Bean 对象,以防循环引用
// 向容器中缓存单例模式的 bean 对象,以防循环引用
boolean earlySingletonExposure = (mbd.isSingleton() && this.allowCircularReferences &&
isSingletonCurrentlyInCreation(beanName));
if (earlySingletonExposure) {
@ -288,7 +297,7 @@ spring-context https://github.com/AmyliaY/spring-context-reading
logger.debug("Eagerly caching bean '" + beanName +
"' to allow for resolving potential circular references");
}
// 这里是一个匿名内部类,为了防止循环引用,尽早持有对象的引用
// 这里是一个 ObjectFactory 的匿名内部类,为了防止循环引用,尽早持有对象的引用
addSingletonFactory(beanName, new ObjectFactory<Object>() {
public Object getObject() throws BeansException {
return getEarlyBeanReference(beanName, mbd, bean);
@ -296,20 +305,18 @@ spring-context https://github.com/AmyliaY/spring-context-reading
});
}
// Bean 对象的初始化,依赖注入在此触发
// 这个 exposedObject 在初始化完成之后,将返回作为依赖注入完成后的 Bean
// bean 对象的初始化,依赖注入在此触发
// 这个 exposedObject 在初始化完成之后,将返回作为依赖注入完成后的 bean
Object exposedObject = bean;
try {
/**
*
* 把生成的 bean 对象的依赖关系设置好,完成整个依赖注入过程
* 把生成的 bean对象 的依赖关系设置好,完成整个依赖注入过程
*
*/
populateBean(beanName, mbd, instanceWrapper);
if (exposedObject != null) {
// 初始化 Bean 对象
// 在对 Bean 实例对象生成和依赖注入完成以后,开始对 Bean 实例对象
// 进行初始化 ,为 Bean 实例对象应用 BeanPostProcessor 后置处理器
// 初始化 bean对象
exposedObject = initializeBean(beanName, exposedObject, mbd);
}
}
@ -323,21 +330,21 @@ spring-context https://github.com/AmyliaY/spring-context-reading
}
if (earlySingletonExposure) {
// 获取指定名称的已注册的单例模式 Bean 对象
// 获取指定名称的已注册的 单例bean对象
Object earlySingletonReference = getSingleton(beanName, false);
if (earlySingletonReference != null) {
// 如果根据名称获取的已注册的 Bean 和正在实例化的 Bean 是同一个
// 如果根据名称获取的已注册的 bean 和正在实例化的 bean 是同一个
if (exposedObject == bean) {
// 当前实例化的 Bean 初始化完成
// 当前实例化的 bean 初始化完成
exposedObject = earlySingletonReference;
}
// 如果当前 Bean 依赖其他 Bean并且当发生循环引用时不允许新创建实例对象
// 如果当前 bean 依赖其他 bean并且当发生循环引用时不允许新创建实例对象
else if (!this.allowRawInjectionDespiteWrapping && hasDependentBean(beanName)) {
String[] dependentBeans = getDependentBeans(beanName);
Set<String> actualDependentBeans = new LinkedHashSet<String>(dependentBeans.length);
// 获取当前 Bean 所依赖的其他 Bean
// 获取当前 bean 所依赖的其他 bean
for (String dependentBean : dependentBeans) {
// 对依赖 Bean 进行类型检查
// 对 依赖bean 进行类型检查
if (!removeSingletonIfCreatedForTypeCheckOnly(dependentBean)) {
actualDependentBeans.add(dependentBean);
}
@ -356,7 +363,7 @@ spring-context https://github.com/AmyliaY/spring-context-reading
}
try {
// 注册完成依赖注入的 Bean
// 注册 成完依赖注入的bean
registerDisposableBeanIfNecessary(beanName, bean, mbd);
}
catch (BeanDefinitionValidationException ex) {
@ -366,23 +373,23 @@ spring-context https://github.com/AmyliaY/spring-context-reading
// 为应用返回所需要的实例对象
return exposedObject;
}
}
```
从源码中可以看到 createBeanInstance() 和 populateBean() 这两个方法与依赖注入的实现非常密切createBeanInstance() 方法中生成了 Bean 所包含的 Java 对象populateBean() 方法对这些生成的 bean 对象之间的依赖关系进行了处理。下面我们先看一下 createBeanInstance() 方法的实现。
## 3、createBeanInstance() 方法的具体实现
从源码中可以看到 createBeanInstance() 和 populateBean() 这两个方法与依赖注入的实现非常密切createBeanInstance() 方法中生成了 bean 所包含的 Java 对象populateBean() 方法对这些生成的 bean 对象之间的依赖关系进行了处理。下面我们先看一下 createBeanInstance() 方法的实现。
```java
// 创建 Bean 的实例对象
protected BeanWrapper createBeanInstance(String beanName, RootBeanDefinition mbd, Object[] args) {
/**
* 创建 bean 的实例对象
*/
protected BeanWrapper createBeanInstance(String beanName, RootBeanDefinition mbd, Object[] args) {
// 检查确认 Bean 是可实例化的
// 检查确认 bean 是可实例化的
Class<?> beanClass = resolveBeanClass(mbd, beanName);
if (beanClass != null && !Modifier.isPublic(beanClass.getModifiers()) && !mbd.isNonPublicAccessAllowed()) {
throw new BeanCreationException(mbd.getResourceDescription(), beanName,
"Bean class isn't public, and non-public access not allowed: " + beanClass.getName());
}
// 使用工厂方法对 Bean 进行实例化
// 使用 RootBeanDefinition对象 中的 factoryMethodName 对 bean 进行实例化
if (mbd.getFactoryMethodName() != null) {
return instantiateUsingFactoryMethod(beanName, mbd, args);
}
@ -400,8 +407,7 @@ spring-context https://github.com/AmyliaY/spring-context-reading
}
if (resolved) {
if (autowireNecessary) {
// 配置了自动装配属性,使用容器的自动装配实例化,
// 即,根据参数类型匹配 Bean 的构造方法
// 配置了自动装配属性,使用容器的自动装配实例化,即,根据参数类型匹配 bean 的构造方法
return autowireConstructor(beanName, mbd, null, null);
}
else {
@ -410,7 +416,7 @@ spring-context https://github.com/AmyliaY/spring-context-reading
}
}
// 使用 Bean 的构造方法进行实例化
// 使用 bean 的构造方法进行实例化
Constructor<?>[] ctors = determineConstructorsFromBeanPostProcessors(beanClass, beanName);
if (ctors != null ||
mbd.getResolvedAutowireMode() == RootBeanDefinition.AUTOWIRE_CONSTRUCTOR ||
@ -420,16 +426,16 @@ spring-context https://github.com/AmyliaY/spring-context-reading
}
return instantiateBean(beanName, mbd);
}
}
// 使用默认的无参构造方法实例化 Bean 对象
protected BeanWrapper instantiateBean(final String beanName, final RootBeanDefinition mbd) {
// 使用默认的无参构造方法实例化 bean对象
protected BeanWrapper instantiateBean(final String beanName, final RootBeanDefinition mbd) {
try {
Object beanInstance;
final BeanFactory parent = this;
// 获取系统的安全管理接口JDK 标准的安全管理 API
if (System.getSecurityManager() != null) {
// 这里是一个匿名内置类,根据实例化策略创建实例对象
// 这里使用了一个 PrivilegedAction 的匿名内部类,根据实例化策略创建实例对象
beanInstance = AccessController.doPrivileged(new PrivilegedAction<Object>() {
public Object run() {
return getInstantiationStrategy().instantiate(mbd, beanName, parent);
@ -437,10 +443,9 @@ spring-context https://github.com/AmyliaY/spring-context-reading
}, getAccessControlContext());
}
else {
/**
*
* 使用初始化策略实例化 Bean 对象
* 使用初始化策略实例化 bean 对象
*
*/
beanInstance = getInstantiationStrategy().instantiate(mbd, beanName, parent);
@ -452,24 +457,23 @@ spring-context https://github.com/AmyliaY/spring-context-reading
catch (Throwable ex) {
throw new BeanCreationException(mbd.getResourceDescription(), beanName, "Instantiation of bean failed", ex);
}
}
}
```
从源码中我们可以看到其调用了 SimpleInstantiationStrategy 实现类来生成 bean 对象,这个类是 spring 用来生成 bean 对象的默认类,它提供了两种策略来实例化 bean 对象,一种是利用 Java 的反射机制,另一种是直接使用 CGLIB。
## 4、SimpleInstantiationStrategy 中的 instantiate() 方法实现
从源码中我们可以看到其调用了 SimpleInstantiationStrategy 实现类来生成 bean 对象,这个类是 Spring 用来生成 bean对象 的默认类,它提供了两种策略来实例化 bean对象一种是利用 Java 的反射机制,另一种是直接使用 CGLIB。
```java
// 使用初始化策略实例化 Bean 对象
public class SimpleInstantiationStrategy implements InstantiationStrategy {
// 使用初始化策略实例化 bean对象
public Object instantiate(RootBeanDefinition beanDefinition, String beanName, BeanFactory owner) {
// 如果 Bean 定义中没有方法覆盖,则使用 Java 的反射机制实例化对象,否则使用 CGLIB
// 如果 配置的bean 中没有方法覆盖,则使用 Java 的反射机制实例化对象,否则使用 CGLIB
if (beanDefinition.getMethodOverrides().isEmpty()) {
Constructor<?> constructorToUse;
synchronized (beanDefinition.constructorArgumentLock) {
// 获取对象的构造方法或生成对象的工厂方法对 bean 进行实例化
// 获取对象的构造方法对 bean 进行实例化
constructorToUse = (Constructor<?>) beanDefinition.resolvedConstructorOrFactoryMethod;
// 如果前面没有获取到构造方法,则通过反射获取
if (constructorToUse == null) {
// 使用 JDK 的反射机制,判断要实例化的 Bean 是否是接口
// 使用 JDK 的反射机制,判断要实例化的 bean 是否是接口
final Class clazz = beanDefinition.getBeanClass();
// 如果 clazz 是一个接口,直接抛出异常
if (clazz.isInterface()) {
@ -477,7 +481,7 @@ spring-context https://github.com/AmyliaY/spring-context-reading
}
try {
if (System.getSecurityManager() != null) {
// 这里是一个匿名内置类,使用反射机制获取 Bean 的构造方法
// 这里使用了一个 PrivilegedExceptionAction 的匿名内部类,使用反射机制获取 bean 的构造方法
constructorToUse = AccessController.doPrivileged(new PrivilegedExceptionAction<Constructor>() {
public Constructor run() throws Exception {
return clazz.getDeclaredConstructor((Class[]) null);
@ -494,7 +498,8 @@ spring-context https://github.com/AmyliaY/spring-context-reading
}
}
}
// 使用 BeanUtils 实例化,通过反射机制调用”构造方法.newInstance(arg)”来进行实例化
// 根据传入的 Constructor在 BeanUtils 中调用该 Constructor 的
// newInstance(Object...) 方法,实例化指定对象
return BeanUtils.instantiateClass(constructorToUse);
}
else {
@ -507,17 +512,19 @@ spring-context https://github.com/AmyliaY/spring-context-reading
return instantiateWithMethodInjection(beanDefinition, beanName, owner);
}
}
}
```
在 SimpleInstantiationStrategy 的子类 CglibSubclassingInstantiationStrategy 中可以看到使用 CGLIB 进行实例化的源码实现。
## 5、CglibSubclassingInstantiationStrategy 中使用 CGLIB 进行实例化的源码实现
```java
// 下面两个方法都通过实例化自己的私有静态内部类 CglibSubclassCreator
// 然后调用该内部类对象的实例化方法 instantiate() 完成实例化
public class CglibSubclassingInstantiationStrategy extends SimpleInstantiationStrategy {
/**
* 下面两个方法都通过实例化自己的私有静态内部类 CglibSubclassCreator
* 然后调用该内部类对象的实例化方法 instantiate() 完成实例化
*/
protected Object instantiateWithMethodInjection(
RootBeanDefinition beanDefinition, String beanName, BeanFactory owner) {
// 必须生成 cglib 子类
return new CglibSubclassCreator(beanDefinition, owner).instantiate(null, null);
}
@ -545,11 +552,11 @@ spring-context https://github.com/AmyliaY/spring-context-reading
this.owner = owner;
}
// 使用 CGLIB 进行 Bean 对象实例化
// 使用 CGLIB 进行 bean对象 实例化
public Object instantiate(Constructor ctor, Object[] args) {
// 实例化 Enhancer 对象,并为 Enhancer 对象设置父类,生成 Java 对象的参数,比如:基类、回调方法等
// 实例化 Enhancer对象并为 Enhancer对象 设置父类,生成 Java 对象的参数,比如:基类、回调方法等
Enhancer enhancer = new Enhancer();
// 将 Bean 本身作为其父类
// 将 bean 本身作为其父类
enhancer.setSuperclass(this.beanDefinition.getBeanClass());
enhancer.setCallbackFilter(new CallbackFilterImpl());
enhancer.setCallbacks(new Callback[] {
@ -558,33 +565,38 @@ spring-context https://github.com/AmyliaY/spring-context-reading
new ReplaceOverrideMethodInterceptor()
});
// 使用 CGLIB 的 create 方法生成实例对象
// 使用 CGLIB 的 create() 方法生成实例对象
return (ctor == null) ? enhancer.create() : enhancer.create(ctor.getParameterTypes(), args);
}
}
}
```
至此,完成了 bean 对象的实例化,然后就可以根据解析得到的 BeanDefinition 完成对各个属性的赋值处理,也就是依赖注入。这个实现方法就是前面 AbstractAutowireCapableBeanFactory 类中的 populateBean() 方法。
## 6、AbstractAutowireCapableBeanFactory 中的 populateBean(),完成依赖注入
至此,完成了 bean对象 的实例化,然后就可以根据解析得到的 BeanDefinition对象 完成对各个属性的赋值处理,也就是依赖注入。这个实现方法就是前面 AbstractAutowireCapableBeanFactory 类中的 populateBean() 方法。
```java
// 为属性赋值,完成依赖注入
public abstract class AbstractAutowireCapableBeanFactory extends AbstractBeanFactory
implements AutowireCapableBeanFactory {
/**
* 为属性赋值,完成依赖注入
*/
protected void populateBean(String beanName, RootBeanDefinition mbd, BeanWrapper bw) {
// 获取 BeanDefinition 中设置的 property这些 property 来自对 BeanDefinition 的解析
// 获取 RootBeanDefinition 中设置的 属性值PropertyValues这些属性值来自对
// .xml 文件中 bean元素 的解析
PropertyValues pvs = mbd.getPropertyValues();
// 如果实例对象为 null而要注入的属性值不为空则抛出下述异常
// 如果 BeanWrapper对象 为 null而要注入的属性值不为空则抛出下述异常
if (bw == null) {
if (!pvs.isEmpty()) {
throw new BeanCreationException(
mbd.getResourceDescription(), beanName, "Cannot apply property values to null instance");
}
else {
// 实例对象为 null属性值也为空不需要设置属性值直接返回
// BeanWrapper对象 为 null属性值也为空不需要设置属性值直接返回
return;
}
}
// 在设置属性之前调用 Bean 的 PostProcessor 后置处理器
// 在设置属性之前调用 bean 的 PostProcessor 后置处理器
boolean continueWithPropertyPopulation = true;
if (!mbd.isSynthetic() && hasInstantiationAwareBeanPostProcessors()) {
@ -608,12 +620,12 @@ spring-context https://github.com/AmyliaY/spring-context-reading
mbd.getResolvedAutowireMode() == RootBeanDefinition.AUTOWIRE_BY_TYPE) {
MutablePropertyValues newPvs = new MutablePropertyValues(pvs);
// 对 autowire 自动装配的处理,根据 Bean 名称自动装配注入
// 对 autowire 自动装配的处理,根据 bean 名称自动装配注入
if (mbd.getResolvedAutowireMode() == RootBeanDefinition.AUTOWIRE_BY_NAME) {
autowireByName(beanName, mbd, bw, newPvs);
}
// 根据 Bean 类型自动装配注入
// 根据 bean 类型自动装配注入
if (mbd.getResolvedAutowireMode() == RootBeanDefinition.AUTOWIRE_BY_TYPE) {
autowireByType(beanName, mbd, bw, newPvs);
}
@ -621,9 +633,9 @@ spring-context https://github.com/AmyliaY/spring-context-reading
pvs = newPvs;
}
// 检查容器是否持有用于处理单例模式 Bean 关闭时的后置处理器
// 检查容器是否持有用于处理单例模式 bean 关闭时的后置处理器
boolean hasInstAwareBpps = hasInstantiationAwareBeanPostProcessors();
// Bean 实例对象没有依赖,即没有继承基类
// bean实例对象 没有依赖,即没有继承基类
boolean needsDepCheck = (mbd.getDependencyCheck() != RootBeanDefinition.DEPENDENCY_CHECK_NONE);
if (hasInstAwareBpps || needsDepCheck) {
@ -654,7 +666,9 @@ spring-context https://github.com/AmyliaY/spring-context-reading
applyPropertyValues(beanName, mbd, bw, pvs);
}
// 解析并注入依赖属性的过程
/**
* 解析并注入依赖属性的过程
*/
protected void applyPropertyValues(String beanName, BeanDefinition mbd, BeanWrapper bw, PropertyValues pvs) {
if (pvs == null || pvs.isEmpty()) {
return;
@ -697,7 +711,7 @@ spring-context https://github.com/AmyliaY/spring-context-reading
if (converter == null) {
converter = bw;
}
// 创建一个 BeanDefinition 属性值解析器,将 Bean 定义中的属性值解析为 Bean 实例对象的实际值
// 创建一个 BeanDefinition 属性值解析器,将 BeanDefinition 中的属性值解析为 bean 实例对象的实际值
BeanDefinitionValueResolver valueResolver = new BeanDefinitionValueResolver(this, beanName, mbd, converter);
// 为属性的解析值创建一个副本,最后将属性值注入到实例对象中
@ -771,15 +785,17 @@ spring-context https://github.com/AmyliaY/spring-context-reading
mbd.getResourceDescription(), beanName, "Error setting property values", ex);
}
}
}
```
## 7、BeanDefinitionValueResolver 中解析属性值,对注入类型进行转换的具体实现
BeanDefinitionValueResolver 中解析属性值,对注入类型进行转换的具体实现。
```java
// 解析属性值,对注入类型进行转换
class BeanDefinitionValueResolver {
/**
* 解析属性值,对注入类型进行转换
*/
public Object resolveValueIfNecessary(Object argName, Object value) {
// 对引用类型的属性进行解析RuntimeBeanReference 是在对
// BeanDefinition 进行解析时生成的数据对象
// 对引用类型的属性进行解析RuntimeBeanReference 是在对 BeanDefinition 进行解析时生成的数据对象
if (value instanceof RuntimeBeanReference) {
RuntimeBeanReference ref = (RuntimeBeanReference) value;
/**
@ -790,7 +806,7 @@ spring-context https://github.com/AmyliaY/spring-context-reading
return resolveReference(argName, ref);
}
// 对属性值是引用容器中另一个 Bean 名称的解析
// 对属性值是引用容器中另一个 bean 名称的解析
else if (value instanceof RuntimeBeanNameReference) {
String refName = ((RuntimeBeanNameReference) value).getBeanName();
refName = String.valueOf(evaluate(refName));
@ -800,7 +816,7 @@ spring-context https://github.com/AmyliaY/spring-context-reading
}
return refName;
}
// 对 BeanDefinitionHolder 类型属性的解析,主要是 Bean 中的内部类
// 对 BeanDefinitionHolder 类型属性的解析,主要是 bean 中的内部类
else if (value instanceof BeanDefinitionHolder) {
BeanDefinitionHolder bdHolder = (BeanDefinitionHolder) value;
return resolveInnerBean(argName, bdHolder.getBeanName(), bdHolder.getBeanDefinition());
@ -898,10 +914,12 @@ spring-context https://github.com/AmyliaY/spring-context-reading
}
}
// 解析引用类型的属性值
/**
* 解析引用类型的属性值
*/
private Object resolveReference(Object argName, RuntimeBeanReference ref) {
try {
// 获取引用的 BeanName
// 获取 所引用bean 的 beanName
String refName = ref.getBeanName();
refName = String.valueOf(evaluate(refName));
// 如果引用的对象在父容器中,则从父容器中获取指定的引用对象
@ -914,11 +932,11 @@ spring-context https://github.com/AmyliaY/spring-context-reading
}
return this.beanFactory.getParentBeanFactory().getBean(refName);
}
// 从当前的容器中获取指定的引用 Bean 对象,如果指定的 Bean 没有被实例化
// 则会递归触发引用 Bean 的初始化和依赖注入
// 从当前的容器中获取指定的引用 bean对象如果指定的 bean 没有被实例化
// 则会递归触发引用 bean 的初始化和依赖注入
else {
Object bean = this.beanFactory.getBean(refName);
// 为 refName 对应的 BeanDefinition 注入依赖的 Bean
// 为 refName对应的bean 注入 它所依赖的bean
this.beanFactory.registerDependentBean(refName, this.beanName);
return bean;
}
@ -930,7 +948,9 @@ spring-context https://github.com/AmyliaY/spring-context-reading
}
}
// 解析 array 类型的属性
/**
* 解析 array 类型的属性
*/
private Object resolveManagedArray(Object argName, List<?> ml, Class<?> elementType) {
// 创建一个指定类型的数组,用于存放和返回解析后的数组
Object resolved = Array.newInstance(elementType, ml.size());
@ -942,7 +962,9 @@ spring-context https://github.com/AmyliaY/spring-context-reading
return resolved;
}
// 解析 list 类型的属性
/**
* 解析 list 类型的属性
*/
private List resolveManagedList(Object argName, List<?> ml) {
List<Object> resolved = new ArrayList<Object>(ml.size());
for (int i = 0; i < ml.size(); i++) {
@ -953,7 +975,9 @@ spring-context https://github.com/AmyliaY/spring-context-reading
return resolved;
}
// 解析 set 类型的属性
/**
* 解析 set 类型的属性
*/
private Set resolveManagedSet(Object argName, Set<?> ms) {
Set<Object> resolved = new LinkedHashSet<Object>(ms.size());
int i = 0;
@ -965,7 +989,9 @@ spring-context https://github.com/AmyliaY/spring-context-reading
return resolved;
}
// 解析 map 类型的属性
/**
* 解析 map 类型的属性
*/
private Map resolveManagedMap(Object argName, Map<?, ?> mm) {
Map<Object, Object> resolved = new LinkedHashMap<Object, Object>(mm.size());
// 递归解析 map 中每一个元素的 key 和 value
@ -977,11 +1003,24 @@ spring-context https://github.com/AmyliaY/spring-context-reading
}
return resolved;
}
}
```
至此,已经为依赖注入做好了准备,下面就该将 bean 对象设置到它所依赖的另一个 bean 的属性中去。AbstractPropertyAccessor 和其子类 BeanWrapperImpl 完成了依赖注入的详细过程。
## 8、AbstractPropertyAccessor 中的实现
至此,已经为依赖注入做好了准备,下面就该将 bean对象 设置到它所依赖的另一个 bean 的属性中去。AbstractPropertyAccessor 和其子类 BeanWrapperImpl 完成了依赖注入的详细过程。先看一下 AbstractPropertyAccessor 中的实现。
```java
public abstract class AbstractPropertyAccessor extends TypeConverterSupport implements ConfigurablePropertyAccessor {
/**
* setPropertyValues() 方法有多种重载,但最终都走的是
* setPropertyValues(PropertyValues pvs, boolean ignoreUnknown, boolean ignoreInvalid)重载方法
*/
public void setPropertyValues(Map<?, ?> map) throws BeansException {
setPropertyValues(new MutablePropertyValues(map));
}
public void setPropertyValues(PropertyValues pvs, boolean ignoreUnknown) throws BeansException {
setPropertyValues(pvs, ignoreUnknown, false);
}
public void setPropertyValues(PropertyValues pvs) throws BeansException {
setPropertyValues(pvs, false, false);
}
@ -996,7 +1035,7 @@ spring-context https://github.com/AmyliaY/spring-context-reading
try {
/**
*
* 走 BeanWrapperImpl 中的实现,也是 bean 属性值注入具体实现的入口
* 该方法走 BeanWrapperImpl 中的实现,这是 bean属性值注入 具体实现的入口
*
*/
setPropertyValue(pv);
@ -1019,18 +1058,20 @@ spring-context https://github.com/AmyliaY/spring-context-reading
}
}
// 如果遇到个别异常,则抛出复合异常
// 如果出现 PropertyAccessException 异常,则将这些异常积累起来放到一个集合中,然后一次性抛出!!!
// 这种抛异常的方式 在实际的开发中也时常使用,可以好好看一下,对比一下
if (propertyAccessExceptions != null) {
PropertyAccessException[] paeArray =
propertyAccessExceptions.toArray(new PropertyAccessException[propertyAccessExceptions.size()]);
throw new PropertyBatchUpdateException(paeArray);
}
}
}
```
## 9、BeanWrapperImpl 中的实现
最后看一下 BeanWrapperImpl 中的实现。
```java
public class BeanWrapperImpl extends AbstractPropertyAccessor implements BeanWrapper {
@Override
public void setPropertyValue(PropertyValue pv) throws BeansException {
// PropertyTokenHolder 是一个用于内部使用的内部类
@ -1064,17 +1105,17 @@ spring-context https://github.com/AmyliaY/spring-context-reading
/**
*
* 实现属性值依赖注入的具体实现
* 依赖注入将某个bean所依赖的值 注入到这个bean中 的具体实现
*
*/
@SuppressWarnings("unchecked")
private void setPropertyValue(PropertyTokenHolder tokens, PropertyValue pv) throws BeansException {
// PropertyTokenHolder 主要保存属性的名称、路径、以及集合的 size 等信息
// PropertyTokenHolder 是定义在 BeanWrapperImpl 中的内部类,主要保存属性的名称、路径、
// 以及集合的 size 等信息
String propertyName = tokens.canonicalName;
String actualName = tokens.actualName;
// 对集合类型的属性注入
// keys 是用来保存集合类型属性的 size
// 对集合类型的属性注入PropertyTokenHolder 的 keys 是用来保存集合类型属性的 size
if (tokens.keys != null) {
// 将属性信息从 tokens 拷贝到 getterTokens
PropertyTokenHolder getterTokens = new PropertyTokenHolder();
@ -1284,11 +1325,11 @@ spring-context https://github.com/AmyliaY/spring-context-reading
}
pv.getOriginalPropertyValue().conversionNecessary = (valueToApply != originalValue);
}
// 根据 Java 的内省机制,获取属性的 setter(写方法) 方法
// 根据 Java 的内省机制,获取属性的 setter方法
final Method writeMethod = (pd instanceof GenericTypeAwarePropertyDescriptor ?
((GenericTypeAwarePropertyDescriptor) pd).getWriteMethodForActualAccess() :
pd.getWriteMethod());
// 如果属性的 setter 方法无法访问,则强行设置 setter 方法可访问 (暴力为属性赋值)
// 如果属性的 setter方法 无法访问,则强行设置 setter方法 可访问 (暴力为属性赋值)
if (!Modifier.isPublic(writeMethod.getDeclaringClass().getModifiers()) && !writeMethod.isAccessible()) {
if (System.getSecurityManager()!= null) {
AccessController.doPrivileged(new PrivilegedAction<Object>() {
@ -1343,27 +1384,35 @@ spring-context https://github.com/AmyliaY/spring-context-reading
}
}
}
}
```
终于完成了对 bean 的各种属性的依赖注入,在 bean 的实例化和依赖注入的过程中,需要依据 BeanDefinition 中的信息来递归地完成依赖注入。另外,在此过程中存在许多递归调用,一个递归是在上下文体系中查找需要的 bean 和创建 bean 的递归调用;另一个是在依赖注入时,通过递归调用容器的 getBean 方法,得到当前 bean 的依赖 bean同时也触发对依赖 bean 的创建和注入;在对 bean 的属性进行依赖注入时,解析的过程也是递归的。这样,根据依赖关系,一层一层地完成 bean 的创建和注入,直到最后完成当前 bean 的创建。
至此,完成了对 bean 的各种属性的依赖注入,在 bean 的实例化和依赖注入的过程中,需要依据 BeanDefinition 中的信息来递归地完成依赖注入。另外,在此过程中存在许多递归调用,一个递归是在上下文体系中查找 当前bean依赖的bean 和创建 当前bean依赖的bean 的递归调用;另一个是在依赖注入时,通过递归调用容器的 getBean() 方法,得到当前 bean 的依赖 bean同时也触发对依赖 bean 的创建和注入;在对 bean 的属性进行依赖注入时,解析的过程也是递归的。这样,根据依赖关系从最末层的依赖bean开始,一层一层地完成 bean 的创建和注入,直到最后完成当前 bean 的创建。
## 最后补充一下通过 lazy-init 属性触发的依赖注入
lazy-init 触发的预实例化和依赖注入,发生在 IoC 容器完成对 BeanDefinition 的定位、载入、解析和注册之后。虽然会影响 IoC 容器初始化的性能,但确能有效提高应用第一次获取该 bean 的效率。
## lazy-init 属性触发的依赖注入
最后看一下 lazy-init 触发的预实例化和依赖注入,发生在 IoC 容器完成对 BeanDefinition 的定位、载入、解析和注册之后。通过牺牲 IoC 容器初始化的性能,来有效提升应用第一次获取该 bean 的效率。
lazy-init 实现的入口方法在我们前面解读过的 AbstractApplicationContext 的 refresh() 中,它是 IoC 容器正式启动的标志。
```java
public abstract class AbstractApplicationContext extends DefaultResourceLoader
implements ConfigurableApplicationContext, DisposableBean {
/**
* 容器初始化的过程BeanDefinition 的 Resource 定位、BeanDefinition 的载入、BeanDefinition 的注册。
* BeanDefinition 的载入和 bean 的依赖注入是两个独立的过程,依赖注入一般发生在 应用第一次通过 getBean() 方法从容器获取 bean 时。
* 容器初始化的过程:
* 1、根据指定规则扫描指定目录获取所有 用于配置bean的配置文件
* 2、根据 Spring定义的规则解析配置文件中的各个元素将其封装成 IoC容器 可以装载的 BeanDefinition对象
* 3、将封装好的 BeanDefinition 注册进 IoC容器。
*
* 另外需要注意的是IoC 容器有一个预实例化的配置(即,将 AbstractBeanDefinition 中的 lazyInit 属性设为 true使用户可以对容器的初始化
* 过程做一个微小的调控lazyInit 设为 true 的 bean 将在容器初始化时进行依赖注入,而不会等到 getBean() 方法调用时才进行
* IoC容器的初始化 和 bean的依赖注入 是两个独立的过程,依赖注入一般发生在应用第一次通过 getBean()方法
* 从容器获取 bean 时。
* 另外需要注意的是IoC容器 有一个预实例化的配置(即,将 <bean>元素 的 lazyInit属性 设为 false
* 使该 <bean>元素对应的 bean可以提前实例化而不用等到调用 getBean()方法 时才开始实例化。
*/
public void refresh() throws BeansException, IllegalStateException {
synchronized (this.startupShutdownMonitor) {
// 调用容器准备刷新的方法,获取容器的当前时间,同时给容器设置同步标识
prepareRefresh();
// 告诉子类启动 refreshBeanFactory() 方法Bean 定义资源文件的载入从子类的 refreshBeanFactory() 方法启动开始
// 告诉子类启动 refreshBeanFactory() 方法BeanDefinition 资源文件的载入从子类的
// refreshBeanFactory() 方法启动开始
ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
// 为 BeanFactory 配置容器特性,例如类加载器、事件处理器等
@ -1373,7 +1422,7 @@ lazy-init 触发的预实例化和依赖注入,发生在 IoC 容器完成对 B
// 为容器的某些子类指定特殊的 BeanPost 事件处理器
postProcessBeanFactory(beanFactory);
// 调用所有注册的 BeanFactoryPostProcessor 的 Bean
// 调用所有注册的 BeanFactoryPostProcessor
invokeBeanFactoryPostProcessors(beanFactory);
// 为 BeanFactory 注册 BeanPost 事件处理器.
@ -1394,7 +1443,7 @@ lazy-init 触发的预实例化和依赖注入,发生在 IoC 容器完成对 B
/**
*
* 初始化 Bean并对 lazy-init 属性进行处理
* 对配置了 lazy-init属性为true的bean 进行预实例化
*
*/
finishBeanFactoryInitialization(beanFactory);
@ -1415,16 +1464,18 @@ lazy-init 触发的预实例化和依赖注入,发生在 IoC 容器完成对 B
}
}
// 对配置了 lazy-init 属性的 Bean 进行预实例化处理
/**
* 对配置了 lazy-init属性为true的bean 进行预实例化
*/
protected void finishBeanFactoryInitialization(ConfigurableListableBeanFactory beanFactory) {
// 这是 Spring3 以后新加的代码,为容器指定一个转换服务 (ConversionService)
// 在对某些 Bean 属性进行转换时使用
// 在对某些 bean 属性进行转换时使用
if (beanFactory.containsBean(CONVERSION_SERVICE_BEAN_NAME) &&
beanFactory.isTypeMatch(CONVERSION_SERVICE_BEAN_NAME, ConversionService.class)) {
beanFactory.setConversionService(
/**
*
* 在这里调用了 getBean() 方法,触发依赖注入
* 在这里 通过调用 getBean()方法,触发依赖注入
*
*/
beanFactory.getBean(CONVERSION_SERVICE_BEAN_NAME, ConversionService.class));
@ -1441,8 +1492,8 @@ lazy-init 触发的预实例化和依赖注入,发生在 IoC 容器完成对 B
// 缓存容器中所有注册的 BeanDefinition 元数据,以防被修改
beanFactory.freezeConfiguration();
// 对配置了 lazy-init 属性的单态模式 Bean 进行预实例化处理
// 对配置了 lazy-init属性 的 单例bean 进行预实例化处理
beanFactory.preInstantiateSingletons();
}
}
```

@ -1,9 +1,9 @@
## 1 Web环境中的SpringMVC
Web环境中SpringMVC是建立在IoC容器基础上的。了解SpringMVC首先要了解Spring的IoC容器是如何在Web环境中被载人并起作用的。
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请求。
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相关的部署描述。
下面以 Tomcat 作为 Web容器 的例子进行分析。在 Tomcat web.xml 是应用的部署描述文件。在 web.xml 中常常经常能看到与 Spring 相关的部署描述。
```xml
<servlet>
<servlet-name>sample</servlet-name>
@ -22,80 +22,78 @@ Spring的IoC是一个独立模块它并不直接在Web容器中发挥作用
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
```
web.xml是SpringMVC与Tomcat的接口部分。这个部署描述文件中首先定义了一个Servlet对象它是SpringMVC的DispatcherServlet。这个DispatcherServlet是MVC中很重要的一个类起着分发请求的作用。
web.xml SpringMVC Tomcat 的接口部分。这个部署描述文件中,首先定义了一个 Servlet对象它是 SpringMVC DispatcherServlet。这个 DispatcherServlet MVC 中很重要的一个类,起着分发请求的作用。
同时在部署描述中还为这个DispatcherServlet定义了对应的URL映射以指定这个Servlet需要处理的HTTP请求范围。context-param参数用来指定IoC容器读取Bean的XML文件的路径在这里这个配置文件被定义为WEB-INF/applicationContext.xml。其中可以看到Spring应用的Bean配置。
同时,在部署描述中,还为这个 DispatcherServlet 定义了对应的 URL映射以指定这个 Servlet 需要处理的 HTTP请求范围。context-param参数 用来指定 IoC容器 读取 Bean XML文件 的路径,在这里,这个配置文件被定义为 WEB-INF/applicationContext.xml。其中可以看到 Spring应用 Bean配置。
最后作为Spring MVC的启动类ContextLoaderListener被定义为一个监听器这个监听器是与Web服务器的生命周期相关联的由ContextLoaderListener监听器负责完成 IoC容器在Web环境中的启动工作。
最后,作为 Spring MVC 的启动类ContextLoaderListener 被定义为一个监听器,这个监听器是与 Web服务器 的生命周期相关联的,由 ContextLoaderListener监听器 负责完成 IoC容器 Web环境 中的启动工作。
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就可以正常地发挥作用了。下面我们看一下loC容器在Web容器中的启动代码实现。
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容器 中的启动代码实现。
## 2 IoC容器启动的基本过程
IoC容器的启动过程就是建立上下文的过程该上下文是与ServletContext相伴而生的同时也是IoC容器在Web应用环境中的具体表现之一。由ContextLoaderListener启动的上下文为根上下文。在根上下文的基础上还有一个与Web MVC相关的上下文用来保存控制器(DispatcherServlet)需要的MVC对象作为根上下文的子上下文构成一个层次化的上下文体系。在Web容器中启动Spring应用程序时首先建立根上下文然后建立这个上下文体系这个上下文体系的建立是由ContextLoder来完成的其UML时序图如下图所示。
IoC容器 的启动过程就是建立上下文的过程,该上下文是与 ServletContext 相伴而生的,同时也是 IoC容器 Web应用环境 中的具体表现之一。由 ContextLoaderListener 启动的上下文为根上下文。在根上下文的基础上,还有一个与 Web MVC 相关的上下文用来保存控制器(DispatcherServlet)需要的 MVC对象作为根上下文的子上下文构成一个层次化的上下文体系。在 Web容器 中启动 Spring应用程序 时,首先建立根上下文,然后建立这个上下文体系,这个上下文体系的建立是由 ContextLoder 来完成的,其 UML时序图 如下图所示。
![avatar](/images/springMVC/Web容器启动spring应用程序过程图.png)
在web.xml中已经配置了ContextLoaderListener它是Spring提供的类是为在Web容器中建立IoC容器服务的它实现了ServletContextListener接口这个接口是在Servlet API中定义的提供了与Servlet生命周期结合的回调比如上下文初始化contextInitialized()方法和上下文销毁contextDestroyed()方法。而在Web容器中建立WebApplicationContext的过程是在contextInitialized()方法中完成的。另外ContextLoaderListener还继承了ContextLoader具体的载入IoC容器的过程是由ContextLoader来完成的。
web.xml 中,已经配置了 ContextLoaderListener它是 Spring 提供的类,是为在 Web容器 中建立 IoC容器 服务的,它实现了 ServletContextListener接口这个接口是在 Servlet API 中定义的,提供了与 Servlet生命周期 结合的回调,比如上下文初始化 contextInitialized()方法 上下文销毁 contextDestroyed()方法。而在 Web容器 中,建立 WebApplicationContext 的过程,是在 contextInitialized()方法 中完成的。另外ContextLoaderListener 还继承了 ContextLoader具体的载入 IoC容器 的过程是由 ContextLoader 来完成的。
在ContextLoader中完成了两个IoC容器建立的基本过程一个是在Web容器中建立起双亲IoC容器另一个是生成相应的WebApplicationContext并将其初始化。
ContextLoader 中,完成了两个 IoC容器 建立的基本过程,一个是在 Web容器 中建立起 双亲IoC容器另一个是生成相应的 WebApplicationContext 并将其初始化。
## 3 Web容器中的上下文设计
先从Web容器中的上下文入手看看Web环境中的上下文设置有哪些特别之处然后再
到ContextLoaderListener中去了解整个容器启动的过程。为了方便在Web环境中使用IoC容器
Spring为Web应用提供了上下文的扩展接口WebApplicationContext来满足启动过程的需要其继承关系如下图所示。
先从 Web容器 中的上下文入手,看看 Web环境 中的上下文设置有哪些特别之处,然后再到 ContextLoaderListener 中去了解整个容器启动的过程。为了方便在 Web环境 中使用 IoC容器
Spring 为 Web应用 提供了上下文的扩展接口 WebApplicationContext 来满足启动过程的需要,其继承关系如下图所示。
![avatar](/images/springMVC/WebApplicationContext接口的类继承关系.png)
在这个类继承关系中可以从熟悉的XmlWebApplicationContext入手来了解它的接口实现。在接口设计中最后是通过ApplicationContex接口与BeanFactory接口对接的而对于具体的功能实现很多都是封装在其基类AbstractRefreshableWebApplicationContext中完成的。
在这个类继承关系中,可以从熟悉的 XmlWebApplicationContext 入手来了解它的接口实现。在接口设计中,最后是通过 ApplicationContex接口 BeanFactory接口 对接的,而对于具体的功能实现,很多都是封装在其基类 AbstractRefreshableWebApplicationContext 中完成的。
同样在源代码中也可以分析出类似的继承关系在WebApplicationContext中可以看到相关的常量设计比如ROOT_ WEB_ APPLICATION_CONTEXT_ATTRIBUTE等这个常量是用来索引在ServletContext中存储的根上下文的。这个接口类定义的接口方法比较简单在这个接口中定义了一
个getServletContext方法通过这个方法可以得到当前Web容器的Servlet上下文环境通过
这个方法相当于提供了一个Web容器级别的全局环境。
同样,在源代码中,也可以分析出类似的继承关系,在 WebApplicationContext 中可以看到相关的常量设计,比如 ROOT_ WEB_ APPLICATION_CONTEXT_ATTRIBUTE 等,这个常量是用来索引在 ServletContext 中存储的根上下文的。这个接口类定义的接口方法比较简单,在这个接口中,定义了一
getServletContext()方法,通过这个方法可以得到当前 Web容器 Servlet上下文环境通过
这个方法,相当于提供了一个 Web容器级别的 全局环境。
```java
public interface WebApplicationContext extends ApplicationContext {
/**
* 该常量用于在ServletContext中存取根上下文
* 该常量用于在 ServletContext 中存取根上下文
*/
String ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE = WebApplicationContext.class.getName() + ".ROOT";
/**
* 对于WebApplicationContext来说需要得到Web容器的ServletContext
* 对于 WebApplicationContext 来说,需要得到 Web容器 的 ServletContext
*/
ServletContext getServletContext();
}
```
在启动过程中Spring会使用一个默认的WebApplicationContext实现作为IoC容器这个默认使用的IoC容器就是XmlWebApplicationContext它继承了ApplicationContext在ApplicationContext的基础上增加了对Web环境和XML配置定义的处理。在XmlWebApplicationContext的初始化过程中Web容器中的IoC容器被建立起来从而在Web容器中建立起整个Spring应用。与前面博文中分析的IoC容器的初始化一样这个过程也有loadBeanDefinition对BeanDefinition的载入。在Web环境中对定位BeanDefinition的Resource有特别的要求这个要求的处理体现在对getDefaultConfigLocations方法的处理中。这里使用了默认的BeanDefinition的配置路径这个路径在XmlWebApplicationContext中作为一个常量定义好了即/WEB-INF/applicationContext.xml。
在启动过程中Spring 会使用一个默认的 WebApplicationContext 实现作为 IoC容器这个默认使用的 IoC容器 就是 XmlWebApplicationContext它继承了 ApplicationContext ApplicationContext 的基础上,增加了对 Web环境 XML配置定义 的处理。在 XmlWebApplicationContext 的初始化过程中Web容器 中的 IoC容器 被建立起来,从而在 Web容器 中建立起整个 Spring应用。与前面博文中分析的 IoC容器 的初始化一样,这个过程也有 loadBeanDefinition()方法 BeanDefinition 的载入。在 Web环境 中,对定位 BeanDefinition Resource 有特别的要求,这个要求的处理体现在对 getDefaultConfigLocations()方法 的处理中。这里使用了默认的 BeanDefinition 的配置路径,这个路径在 XmlWebApplicationContext 中作为一个常量定义好了,即 /WEB-INF/applicationContext.xml。
```java
public class XmlWebApplicationContext extends AbstractRefreshableWebApplicationContext {
/** 若不指定其它文件spring默认从"/WEB-INF/applicationContext.xml"目录文件 初始化IoC容器 */
/** 若不指定其它文件Spring 默认从 "/WEB-INF/applicationContext.xml" 目录文件 初始化 IoC容器 */
public static final String DEFAULT_CONFIG_LOCATION = "/WEB-INF/applicationContext.xml";
/** 默认的配置文件在 /WEB-INF/ 目录下 */
public static final String DEFAULT_CONFIG_LOCATION_PREFIX = "/WEB-INF/";
/** 默认的配置文件后缀名为.xml */
/** 默认的配置文件后缀名为 .xml */
public static final String DEFAULT_CONFIG_LOCATION_SUFFIX = ".xml";
/**
* 此加载过程在 容器refresh()时启动
* 此加载过程在容器 refresh() 时启动
*/
@Override
protected void loadBeanDefinitions(DefaultListableBeanFactory beanFactory) throws BeansException, IOException {
// 使用XmlBeanDefinitionReader对指定的BeanFactory进行解析
// 使用 XmlBeanDefinitionReader 对指定的 BeanFactory 进行解析
XmlBeanDefinitionReader beanDefinitionReader = new XmlBeanDefinitionReader(beanFactory);
// 初始化beanDefinitionReader的属性其中设置ResourceLoader是因为
// XmlBeanDefinitionReader是DefaultResource的子类所有这里同样会使用
// DefaultResourceLoader来定位BeanDefinition
// 初始化 beanDefinitionReader 的属性,其中,设置 ResourceLoader 是因为 XmlBeanDefinitionReader
// 是 DefaultResource 的子类,所有这里同样会使用 DefaultResourceLoader 来定位 BeanDefinition
beanDefinitionReader.setEnvironment(this.getEnvironment());
beanDefinitionReader.setResourceLoader(this);
beanDefinitionReader.setEntityResolver(new ResourceEntityResolver(this));
// 该方法是一个空实现
initBeanDefinitionReader(beanDefinitionReader);
// 使用初始化完成的beanDefinitionReader来加载BeanDefinitions
// 使用初始化完成的 beanDefinitionReader 来加载 BeanDefinitions
loadBeanDefinitions(beanDefinitionReader);
}
@ -103,7 +101,7 @@ public class XmlWebApplicationContext extends AbstractRefreshableWebApplicationC
}
/**
* 获取所有的配置文件然后一个一个载入BeanDefinition
* 获取所有的配置文件,然后一个一个载入 BeanDefinition
*/
protected void loadBeanDefinitions(XmlBeanDefinitionReader reader) throws IOException {
String[] configLocations = getConfigLocations();
@ -115,8 +113,8 @@ public class XmlWebApplicationContext extends AbstractRefreshableWebApplicationC
}
/**
* 获取默认路径"/WEB-INF/***.xml"下的配置文件,
* 或者获取"/WEB-INF/applicationContext.xml"配置文件
* 获取默认路径 "/WEB-INF/***.xml" 下的配置文件,
* 或者获取 "/WEB-INF/applicationContext.xml" 配置文件
*/
@Override
protected String[] getDefaultConfigLocations() {
@ -129,28 +127,28 @@ public class XmlWebApplicationContext extends AbstractRefreshableWebApplicationC
}
}
```
从上面的代码中可以看到在XmlWebApplicationContext中基本的上下文功能都已经通过类的继承获得这里需要处理的是如何获取Bean定义信息在这里就转化为如何在Web容器环境中获得Bean定义信息。在获得Bean定义信息之后后面的过程基本上就和前面分析的XmlFileSystemBeanFactory一样是通过XmlBeanDefinitionReader来载人Bean定义信息的,最终完成整个上下文的初始化过程。
从上面的代码中可以看到,在 XmlWebApplicationContext 中,基本的上下文功能都已经通过类的继承获得,这里需要处理的是,如何获取 BeanDefinition信息在这里就转化为如何在 Web容器环境 中获得 BeanDefinition信息。在获得 BeanDefinition信息 之后,后面的过程基本上就和前面分析的 XmlFileSystemBeanFactory 一样,是通过 XmlBeanDefinitionReader 来载入 BeanDefinition信息 的,最终完成整个上下文的初始化过程。
## 4 ContextLoader的设计与实现
对于Spring承载的Web应用而言可以指定在Web应用程序启动时载入IoC容器或者称为WebApplicationContext。这个功能是由ContextLoaderListener来完成的它是在Web容器中配置的监听器会监听Web容器的启动然后载入IoC容器。这个ContextLoaderListener通过使用ContextLoader来完成实际的WebApplicationContext也就是IoC容器的初始化工作。这个ContextLoader就像Spring应用程序在Web容器中的启动器。这个启动过程是在Web容器中发生的所以需要根据Web容器部署的要求来定义ContextLoader相关的配置在概述中已经看到了这里就不重复了。
对于 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类的静态方法中得到。
为了了解 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容器的初始化。
下面分析具体的根上下文的载人过程。在 ContextLoaderListener 中,实现的是 **ServletContextListener接口这个接口里的函数会结合 Web容器 的生命周期被调用**。因为 ServletContextListener ServletContext 的监听者,如果 ServletContext 发生变化,会触发出相应的事件,而监听器一直在对这些事件进行监听,如果接收到了监听的事件,就会做出预先设计好的响应动作。由于 ServletContext 的变化而触发的监听器的响应具体包括在服务器启动时ServletContext 被创建的时候服务器关闭时ServletContext 将被销毁的时候等。对应这些事件及 Web容器状态 的变化在监听器中定义了对应的事件响应的回调方法。比如在服务器启动时ServletContextListener contextInitialized()方法 被调用服务器将要关闭时ServletContextListener contextDestroyed()方法 被调用。了解了 Web容器 中监听器的工作原理,下面看看服务器启动时 ContextLoaderListener 的调用完成了什么。在这个初始化回调中,创建了 ContextLoader同时会利用创建出来的 ContextLoader 来完成 IoC容器 的初始化。
```java
public class ContextLoaderListener extends ContextLoader implements ServletContextListener {
private ContextLoader contextLoader;
/**
* 启动web应用的 根上下文
* 启动 web应用 的根上下文
*/
public void contextInitialized(ServletContextEvent event) {
// 由于本类直接继承了ContextLoader所以能直接使用ContextLoader来初始化IoC容器
// 由于本类直接继承了 ContextLoader所以能直接使用 ContextLoader 来初始化 IoC容器
this.contextLoader = createContextLoader();
if (this.contextLoader == null) {
this.contextLoader = this;
}
// 具体的初始化工作交给ContextLoader完成
// 具体的初始化工作交给 ContextLoader 完成
this.contextLoader.initWebApplicationContext(event.getServletContext());
}
}
@ -188,12 +186,12 @@ public class ContextLoader {
}
/**
* 由ContextLoader完成根上下文在Web容器中的创建。这个根上下文是作为Web容器中唯一的实例而存在的,
* 根上下文创建成功后 会被存到Web容器的ServletContext中,供需要时使用。存取这个根上下文的路径是由
* Spring预先设置好的WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE中进行了定义
* 由 ContextLoader 完成根上下文在 Web容器 中的创建。这个根上下文是作为 Web容器 中唯一的实例而存在的,
* 根上下文创建成功后会被存到 Web容器 的 ServletContext 中,供需要时使用。存取这个根上下文的路径是由
* Spring 预先设置好的,在 WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE 中进行了定义
*/
public WebApplicationContext initWebApplicationContext(ServletContext servletContext) {
// 如果ServletContext中已经包含了根上下文,则抛出异常
// 如果 ServletContext 中已经包含了根上下文,则抛出异常
if (servletContext.getAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE) != null) {
throw new IllegalStateException(
"Cannot initialize context because there is already a root application context present - " +
@ -208,7 +206,7 @@ public class ContextLoader {
try {
if (this.context == null) {
// 这里创建在ServletContext中存储的根上下文
// 这里创建在 ServletContext 中存储的根上下文
this.context = createWebApplicationContext(servletContext);
}
if (this.context instanceof ConfigurableWebApplicationContext) {
@ -219,12 +217,12 @@ public class ContextLoader {
ApplicationContext parent = loadParentContext(servletContext);
cwac.setParent(parent);
}
// 配置 并且初始化IoC容器看到Refresh应该能想到AbstractApplicationContext
// 中的refresh()方法猜到它是前面介绍的IoC容器的初始化入口
// 配置并初始化 IoC容器看到下面方法中的 Refresh单词 应该能想到
// AbstractApplicationContext 中的 refresh()方法,猜到它是前面介绍的 IoC容器 的初始化入口
configureAndRefreshWebApplicationContext(cwac, servletContext);
}
}
// 将上面创建的WebApplicationContext实例 存到ServletContext中,注意同时被存入的常量
// 将上面创建的 WebApplicationContext实例 存到 ServletContext 中,注意同时被存入的常量
// ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE以后的应用都会根据这个属性获取根上下文
servletContext.setAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, this.context);
@ -260,30 +258,30 @@ public class ContextLoader {
}
/**
* 创建WebApplicationContext的实例化对象
* 创建 WebApplicationContext 的实例化对象
*/
protected WebApplicationContext createWebApplicationContext(ServletContext sc) {
// 判断使用什么样的类在Web容器中作为IoC容器
// 判断使用什么样的类在 Web容器 中作为 IoC容器
Class<?> contextClass = determineContextClass(sc);
if (!ConfigurableWebApplicationContext.class.isAssignableFrom(contextClass)) {
throw new ApplicationContextException("Custom context class [" + contextClass.getName() +
"] is not of type [" + ConfigurableWebApplicationContext.class.getName() + "]");
}
// 直接实例化需要产生的IoC容器
// 直接实例化需要产生的 IoC容器
return (ConfigurableWebApplicationContext) BeanUtils.instantiateClass(contextClass);
}
/**
* 在确定使用何种IoC容器的过程中可以看到应用可以在部署描述符中指定使用什么样的IoC容器
* 这个指定操作是通过CONTEXT_ CLASS_ PARAM参数的设置完成的。如果没有指定特定的IoC容器
* 将使用默认的IoC容器也就是XmlWebApplicationContext对象作为在Web环境中使用的IoC容器。
* 在确定使用何种 IoC容器 的过程中可以看到,应用可以在部署描述符中指定使用什么样的 IoC容器
* 这个指定操作是通过 CONTEXT_ CLASS_ PARAM参数 的设置完成的。如果没有指定特定的 IoC容器
* 将使用默认的 IoC容器也就是 XmlWebApplicationContext对象 作为在 Web环境 中使用的 IoC容器。
*/
protected Class<?> determineContextClass(ServletContext servletContext) {
// 获取servletContext中对CONTEXT_CLASS_PARAMcontextClass参数的配置
// 获取 servletContext 中对 CONTEXT_CLASS_PARAMcontextClass参数 的配置
String contextClassName = servletContext.getInitParameter(CONTEXT_CLASS_PARAM);
if (contextClassName != null) {
try {
// 获取配置的contextClassName对应的clazz对象
// 获取配置的 contextClassName 对应的 clazz对象
return ClassUtils.forName(contextClassName, ClassUtils.getDefaultClassLoader());
}
catch (ClassNotFoundException ex) {
@ -292,7 +290,7 @@ public class ContextLoader {
}
}
else {
// 如果没有配置CONTEXT_CLASS_PARAM则使用默认的ContextClass
// 如果没有配置 CONTEXT_CLASS_PARAM则使用默认的 ContextClass
contextClassName = defaultStrategies.getProperty(WebApplicationContext.class.getName());
try {
return ClassUtils.forName(contextClassName, ContextLoader.class.getClassLoader());
@ -326,16 +324,16 @@ public class ContextLoader {
}
}
// 设置ServletContext 及配置文件的位置参数
// 设置 ServletContext 及配置文件的位置参数
wac.setServletContext(sc);
String initParameter = sc.getInitParameter(CONFIG_LOCATION_PARAM);
if (initParameter != null) {
wac.setConfigLocation(initParameter);
}
customizeContext(sc, wac);
// IoC容器初始化的入口想不起来的把前面IoC容器初始化的博文再读10遍
// IoC容器 初始化的入口,想不起来的把前面 IoC容器 初始化的博文再读10遍
wac.refresh();
}
}
```
这就是IoC容器在Web容器中的启动过程 应用中启动IoC容器的方式相类似所不同的是这里需要考虑Web容器的环境特点比如各种参数的设置IoC容器与Web容器ServletContext的结合等。在初始化这个上下文以后该上下文会被存储到SevletContext中这样就建立了一个全局的关于整个应用的上下文。同时在启动Spring MVC时我们还会看到这个上下文被以后的DispatcherServlet在进行自己持有的上下文的初始化时设置为DispatcherServlet自带的上下文的双亲上下文。
这就是 IoC容器 Web容器 中的启动过程,与应用中启动 IoC容器 的方式相类似,所不同的是这里需要考虑 Web容器 的环境特点比如各种参数的设置IoC容器 Web容器 ServletContext 的结合等。在初始化这个上下文以后,该上下文会被存储到 SevletContext 中,这样就建立了一个全局的关于整个应用的上下文。同时,在启动 SpringMVC 时,我们还会看到这个上下文被以后的 DispatcherServlet 在进行自己持有的上下文的初始化时,设置为 DispatcherServlet 自带的上下文的双亲上下文。

@ -1,35 +1,36 @@
## 1 SpringMVC应用场景
在使用SpringMVC时除了要在web.xml中配置ContextLoaderListener外还要对DispatcherServlet进行配置。作为一个Servlet这个DispatcherServlet实现的是Sun的J2EE核心模式中的前端控制器模式(Front Controller) 作为一个前端控制器所有的Web请求都需要通过它来处理,进行转发、匹配、数据处理后,并转由页面进行展现因此这个DispatcerServlet可以看成是Spring MVC实现中最为核心的部分。
在使用 SpringMVC 时,除了要在 web.xml 中配置 ContextLoaderListener 外,还要对 DispatcherServlet 进行配置。作为一个 Servlet这个 DispatcherServlet 实现的是 Sun J2EE核心模式 中的 前端控制器模式(Front Controller) 作为一个前端控制器,所有的 Web请求 都需要通过它来进行转发、匹配、数据处理,然后转由页面进行展现,因此这个 DispatcerServlet 可以看成是 SpringMVC实现 中最为核心的部分。
在Spring MVC中对于不同的Web请求的映射需求Spring MVC提供了不同的HandlerMapping的实现可以让应用开发选取不同的映射策略。DispatcherSevlet默认了BeanNameUrlHandlerMapping作为映射策略实现。除了映射策略可以定制外Spring MVC提供了各种Controller的实现来供应用扩展和使用以应对不同的控制器使用场景这些Controller控制器需要实现handleRequest接口方法并返回ModelAndView对象。Spring MVC还提供了各种视图实现比如常用的JSP视图。除此之外Spring MVC还提供了拦截器供应用使用允许应用对Web请求进行拦截以及前置处理和后置处理。
SpringMVC 中,对于不同的 Web请求 的映射需求SpringMVC 提供了不同的 HandlerMapping 的实现可以让应用开发选取不同的映射策略。DispatcherSevlet 默认了 BeanNameUrlHandlerMapping 作为映射策略实现。除了映射策略可以定制外SpringMVC提供了各种 Controller 的实现来供应用扩展和使用,以应对不同的控制器使用场景,这些 Controller控制器 需要实现 handleRequest()接口方法,并返回 ModelAndView对象。SpringMVC 还提供了各种视图实现,比如常用的 JSP视图。除此之外SpringMVC 还提供了拦截器供应用使用,允许应用对 Web请求 进行拦截,以及前置处理和后置处理。
## 2 SpringMVC设计概览
在完成对ContextLoaderListener的初始化以后Web容器开始初始化DispatcherServlet这个初始化的启动与在web.xml中对载入次序的定义有关。DispatcherServlet会建立自己的上下文来持有Spring MVC的Bean对象在建立这个自己持有的IoC容器时会**从ServletContext中得到根上下文**作为DispatcherServlet持有上下文的双亲上下文。有了这个根上下文再对自己持有的上下文进行初始化最后把自己持有的这个上下文保存到ServletContext中供以后检索和使用。
在完成对 ContextLoaderListener 的初始化以后Web容器 开始初始化 DispatcherServlet这个初始化的启动与在 web.xml 中对载入次序的定义有关。DispatcherServlet 会建立自己的上下文来持有SpringMVC Bean对象在建立这个自己持有的 IoC容器 时,会**从 ServletContext 中得到根上下文**作为 DispatcherServlet 持有上下文的双亲上下文。有了这个根上下文,再对自己持有的上下文进行初始化,最后把自己持有的这个上下文保存到 ServletContext 中,供以后检索和使用。
为了解这个过程可以从DispatcherServlet的父类FrameworkServlet的代码入手去探寻DispatcherServlet的启动过程它同时也是SpringMVC的启动过程。ApplicationContext的创建过程和ContextLoader创建根上下文的过程有许多类似的地方。下面来看一下这个DispatcherServlet类的继承关系。
为了解这个过程,可以从 DispatcherServlet 的父类 FrameworkServlet 的代码入手,去探寻 DispatcherServlet 的启动过程,它同时也是 SpringMVC 的启动过程。ApplicationContext 的创建过程和 ContextLoader 创建根上下文的过程有许多类似的地方。下面来看一下这个 DispatcherServlet类 的继承关系。
![avatar](/images/springMVC/DispatcherServlet的继承关系.png)
DispatcherServlet通过继承FrameworkServlet和HttpServletBean而继承了HttpServlet通过使用Servlet API来对HTTP请求进行响应成为Spring MVC的前端处理器同时成为MVC模块与Web容器集成的处理前端。
DispatcherServlet 通过继承 FrameworkServlet HttpServletBean 而继承了 HttpServlet通过使用Servlet API 来对 HTTP请求 进行响应,成为 SpringMVC 的前端处理器,同时成为 MVC模块 Web容器 集成的处理前端。
DispatcherServlet的工作大致可以分为两个部分一个是初始化部分由initServletBean()启动通过initWebApplicationContext()方法最终调用DispatcherServlet的initStrategies()方法在这个方法里DispatcherServlet对MVC模块的其他部分进行了初始化比如handlerMapping、ViewResolver等另一个是对HTTP请求进行响应作为一个ServletWeb容器会调用Servlet的doGet()和doPost()方法在经过FrameworkServlet的processRequest()简单处理后会调用DispatcherServlet的doService()方法在这个方法调用中封装了doDispatch()这个doDispatch()是Dispatcher实现MVC模式的主要部分下图为DispatcherServlet的处理过程时序图。
DispatcherServlet 的工作大致可以分为两个部分:一个是初始化部分,由 initServletBean()方法 启动,通过 initWebApplicationContext()方法 最终调用 DispatcherServlet initStrategies()方法在这个方法里DispatcherServlet MVC模块 的其他部分进行了初始化,比如 handlerMapping、ViewResolver 等;另一个是对 HTTP请求 进行响应,作为一个 ServletWeb容器 会调用 Servlet 的doGet() doPost()方法,在经过 FrameworkServlet processRequest() 简单处理后,会调用 DispatcherServlet doService()方法,在这个方法调用中封装了 doDispatch(),这个 doDispatch() Dispatcher 实现 MVC模式 的主要部分,下图为 DispatcherServlet 的处理过程时序图。
![avatar](/images/springMVC/DispatcherServlet的处理过程.png)
## 3 DispatcherServlet的启动和初始化
前面大致描述了Spring MVC的工作流程下面看一下DispatcherServlet的启动和初始化的代码设计及实现。
前面大致描述了 SpringMVC 的工作流程,下面看一下 DispatcherServlet 的启动和初始化的代码设计及实现。
作为ServletDispatcherServlet的启动与Servlet的启动过程是相联系的。在Servlet的初始化过程中Servlet的init()方法会被调用以进行初始化DispatcherServlet的基类HttpServletBean实现了该方法。在初始化开始时需要读取配置在ServletContext中的Bean属性参数这些属性参数设置在web.xml的Web容器初始化参数中。使用编程式的方式来设置这些Bean属性在这里可以看到对PropertyValues和BeanWrapper的使用。对于这些和依赖注人相关的类的使用在分析IoC容器的初始化时尤其是在依赖注入实现分析时有过“亲密接触”。只是这里的依赖注人是与Web容器初始化相关的。
作为 ServletDispatcherServlet 的启动与 Servlet 的启动过程是相联系的。在 Servlet 的初始化过程中Servlet init()方法 会被调用以进行初始化DispatcherServlet 的基类 HttpServletBean 实现了该方法。在初始化开始时,需要读取配置在 ServletContext 中的 Bean属性参数这些属性参数设置在 web.xml Web容器初始化参数 中。使用编程式的方式来设置这些 Bean属性在这里可以看到对 PropertyValues BeanWrapper 的使用。对于这些和依赖注人相关的类的使用,在分析 IoC容器 的初始化时,尤其是在依赖注入实现分析时,有过“亲密接触”。只是这里的依赖注人是与 Web容器 初始化相关的。
接着会执行DispatcherServlet持有的IoC容器的初始化过程在这个初始化过程中一个新的上下文被建立起来这个DispatcherServlet持有的上下文被设置为根上下文的子上下文。一个Web应用中可以容纳多个Servlet存在与此相对应对于应用在Web容器中的上下体系一个根上下文可以作为许多Servlet上下文的双亲上下文。了解IoC工作原理的读者知道在向IoC容器getBean()时IoC容器会首先向其双亲上下文去getBean()也就是说在根上下文中定义的Bean是可以被各个Servlet持有的上下文得到和共享的。DispatcherServlet持有的 上下文被建立起来以后也需要和其他IoC容器一样完成初始化这个初始化也是通过refresh()方法来完成的。最后DispatcherServlet给这个自己持有的上下文命名并把它设置到Web容器的上下文中这个名称和在web.xml中设置的DispatcherServlet的Servlet名称有关从而保证了这个上下文在Web环境上下文体系中的唯一性。
接着会执行 DispatcherServlet 持有的 IoC容器 的初始化过程,在这个初始化过程中,一个新的上下文被建立起来,这个 DispatcherServlet 持有的上下文被设置为根上下文的子上下文。一个 Web应用 中可以容纳多个 Servlet 存在;与此相对应,对于应用在 Web容器 中的上下体系,一个根上下文可以作为许多 Servlet上下文 的双亲上下文。了解 IoC 工作原理的读者知道,在向 IoC容器 getBean() IoC容器 会首先向其双亲上下文去 getBean(),也就是说,在根上下文中定义的 Bean 是可以被各个 Servlet 持有的上下文得到和共享的。DispatcherServlet 持有的 上下文被建立起来以后,也需要和其他 IoC容器 一样完成初始化,这个初始化也是通过 refresh()方法 来完成的。最后DispatcherServlet 给这个自己持有的上下文命名,并把它设置到 Web容器 的上下文中,这个名称和在 web.xml 中设置的 DispatcherServlet Servlet名称 有关,从而保证了这个上下文在 Web环境上下文体系 中的唯一性。
```java
public abstract class HttpServletBean extends HttpServlet implements EnvironmentCapable, EnvironmentAware {
public final void init() throws ServletException {
if (logger.isDebugEnabled()) {
logger.debug("Initializing servlet '" + getServletName() + "'");
}
// 获取Servlet的初始化参数对bean属性进行配置
// 获取 Servlet 的初始化参数,对 bean属性 进行配置
try {
PropertyValues pvs = new ServletConfigPropertyValues(getServletConfig(), this.requiredProperties);
BeanWrapper bw = PropertyAccessorFactory.forBeanPropertyAccess(this);
@ -54,10 +55,11 @@ public abstract class HttpServletBean extends HttpServlet implements Environment
public abstract class FrameworkServlet extends HttpServletBean {
/** 此servlet的WebApplicationContext */
/** 此 servlet 的 WebApplicationContext */
private WebApplicationContext webApplicationContext;
/** 我们是否应该将当前Servlet的上下文webApplicationContext设为ServletContext的属性 */
/** 我们是否应该将当前 Servlet 的上下文 webApplicationContext 设为 ServletContext 的属性 */
private boolean publishContext = true;
public FrameworkServlet() {
@ -68,7 +70,7 @@ public abstract class FrameworkServlet extends HttpServletBean {
}
/**
* 覆盖了父类HttpServletBean的空实现
* 覆盖了父类 HttpServletBean 的空实现
*/
@Override
protected final void initServletBean() throws ServletException {
@ -100,16 +102,16 @@ public abstract class FrameworkServlet extends HttpServletBean {
}
/**
* 为这个Servlet初始化一个公共的WebApplicationContext实例
* 为这个 Servlet 初始化一个公共的 WebApplicationContext实例
*/
protected WebApplicationContext initWebApplicationContext() {
// 获取 根上下文 作为当前MVC上下文的双亲上下文这个根上下文保存在ServletContext中
// 获取根上下文作为当前 MVC上下文 的双亲上下文,这个根上下文保存在 ServletContext
WebApplicationContext rootContext =
WebApplicationContextUtils.getWebApplicationContext(getServletContext());
WebApplicationContext wac = null;
if (this.webApplicationContext != null) {
// 可以在本对象被构造时注入一个webApplicationContext实例
// 可以在本对象被构造时注入一个 webApplicationContext实例
wac = this.webApplicationContext;
if (wac instanceof ConfigurableWebApplicationContext) {
ConfigurableWebApplicationContext cwac = (ConfigurableWebApplicationContext) wac;
@ -126,24 +128,24 @@ public abstract class FrameworkServlet extends HttpServletBean {
}
if (wac == null) {
// 在本对象被构造时没有注入上下文实例 ->
// 查看是否已在servlet上下文中注册了上下文实例。
// 查看是否已在 servlet上下文 中注册了上下文实例。
// 如果存在一个,则假定父上下文(如果有的话)已经被设置,
// 并且用户已经执行了任何初始化例如设置上下文ID
wac = findWebApplicationContext();
}
if (wac == null) {
// 没有为此servlet定义上下文实例 -> 创建本地实例
// 没有为此 servlet 定义上下文实例 -> 创建本地实例
wac = createWebApplicationContext(rootContext);
}
if (!this.refreshEventReceived) {
// 上下文 不是支持刷新的ConfigurableApplicationContext或者
// 在构造时注入的上下文已经完成刷新 -> 在此处手动触发onRefresh()方法
// 上下文不是支持刷新的 ConfigurableApplicationContext或者
// 在构造时注入的上下文已经完成刷新 -> 在此处手动触发 onRefresh()方法
onRefresh(wac);
}
if (this.publishContext) {
// 把当前建立的上下文保存到ServletContext中使用的属性名是和当前servlet名相关的
// 把当前建立的上下文保存到 ServletContext 中,使用的属性名是和 当前servlet名 相关的
String attrName = getServletContextAttributeName();
getServletContext().setAttribute(attrName, wac);
if (this.logger.isDebugEnabled()) {
@ -156,7 +158,7 @@ public abstract class FrameworkServlet extends HttpServletBean {
}
}
```
至此这个MVC的上下文就建立起来了具体取得根上下文的过程在WebApplicationContextUtils中实现。这个根上下文是ContextLoader设置到ServletContext中去的使用的属性是ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTEContextLoader还对这个IoC容器的Bean配置文件进行了设置默认的位置是在/WEB-INF/applicationContext.xml文件中。由于这个根上下文是DispatcherServlet建立的上下文的 双亲上下文所以根上下文中管理的Bean也可以被DispatcherServlet的上下文使用。通过getBean()向IoC容器获取Bean时容器会先到它的双亲IoC容器中获取。
至此,这个 MVC 的上下文就建立起来了,具体取得根上下文的过程在 WebApplicationContextUtils 中实现。这个根上下文是 ContextLoader 设置到 ServletContext 中去的,使用的属性是 ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTEContextLoader 还对这个 IoC容器 Bean 配置文件进行了设置,默认的位置是在 /WEB-INF/applicationContext.xml文件 中。由于这个根上下文是 DispatcherServlet 建立的上下文的 双亲上下文,所以根上下文中管理的 Bean 也可以被 DispatcherServlet 的上下文使用。通过 getBean() IoC容器 获取 Bean 时,容器会先到它的 双亲IoC容器 中获取。
```java
/**
* 这是一个封装了很多静态方法的抽象工具类,所以只能调用其静态方法,
@ -164,8 +166,8 @@ public abstract class FrameworkServlet extends HttpServletBean {
*/
public abstract class WebApplicationContextUtils {
/**
* 使用了WebApplicationContext的ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE属性获取
* ServletContext中的根上下文这个属性代表的根上下文在ContextLoaderListener初始化的
* 使用了 WebApplicationContext ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE属性获取
* ServletContext 中的根上下文,这个属性代表的根上下文在 ContextLoaderListener 初始化的
* 过程中被建立
*/
public static WebApplicationContext getWebApplicationContext(ServletContext sc) {
@ -173,7 +175,7 @@ public abstract class WebApplicationContextUtils {
}
/**
* 查找此web应用程序的自定义WebApplicationContext
* 查找此 web应用程序 的自定义 WebApplicationContext
*/
public static WebApplicationContext getWebApplicationContext(ServletContext sc, String attrName) {
Assert.notNull(sc, "ServletContext must not be null");
@ -197,20 +199,20 @@ public abstract class WebApplicationContextUtils {
}
)
```
回到FrameworkServlet的实现中来看一下DispatcherServlet的上下文是怎样建立的这个建立过程与前面建立根上下文的过程非常类似。建立DispatcherServlet的上下文需要把根上下文作为参数传递给它。然后使用反射技术来实例化上下文对象并为它设置参数。根据默认的配置这个上下文对象也是XmlWebApplicationContext对象这个类型是在DEFAULT_CONTEXT_CLASS参数中设置好并允许BeanUtilis使用的。在实例化结束后需要为这个上下文对象设置好一些基本的配置这些配置包括它的双亲上下文、Bean配置文件的位置等。完成这些配置以后最后通过调用IoC容器的refresh()方法来完成IoC容器的最终初始化这和前面我们对IoC容器实现原理的分析中所看到的IoC容器初始化的过程是一致的。
回到 FrameworkServlet 的实现中来看一下DispatcherServlet 的上下文是怎样建立的,这个建立过程与前面建立根上下文的过程非常类似。建立 DispatcherServlet 的上下文,需要把根上下文作为参数传递给它。然后使用反射技术来实例化上下文对象,并为它设置参数。根据默认的配置,这个上下文对象也是 XmlWebApplicationContext对象这个类型是在 DEFAULT_CONTEXT_CLASS参数 中设置好并允许 BeanUtilis 使用的。在实例化结束后需要为这个上下文对象设置好一些基本的配置这些配置包括它的双亲上下文、Bean配置文件 的位置等。完成这些配置以后,最后通过调用 IoC容器 refresh()方法 来完成 IoC容器 的最终初始化,这和前面我们对 IoC容器实现原理 的分析中所看到的 IoC容器初始化 的过程是一致的。
```java
public abstract class FrameworkServlet extends HttpServletBean {
/**
* 为此servlet实例化一个WebApplicationContext可以是默认的XmlWebApplicationContext
* 也可以是用户设置的自定义Context上下文
* 为此 servlet 实例化一个 WebApplicationContext可以是默认的 XmlWebApplicationContext
* 也可以是用户设置的自定义 Context上下文
*/
protected WebApplicationContext createWebApplicationContext(WebApplicationContext parent) {
return createWebApplicationContext((ApplicationContext) parent);
}
protected WebApplicationContext createWebApplicationContext(ApplicationContext parent) {
// 默认为XmlWebApplicationContext.class
// 默认为 XmlWebApplicationContext.class
Class<?> contextClass = getContextClass();
if (this.logger.isDebugEnabled()) {
this.logger.debug("Servlet with name '" + getServletName() +
@ -228,11 +230,11 @@ public abstract class FrameworkServlet extends HttpServletBean {
(ConfigurableWebApplicationContext) BeanUtils.instantiateClass(contextClass);
wac.setEnvironment(getEnvironment());
// 这里设置的 双亲上下文就是在ContextLoader中建立的根上下文
// 这里设置的 双亲上下文,就是在 ContextLoader 中建立的根上下文
wac.setParent(parent);
wac.setConfigLocation(getContextConfigLocation());
// 配置并且刷新wac
// 配置并且刷新 WebApplicationContext对象
configureAndRefreshWebApplicationContext(wac);
return wac;
@ -240,15 +242,15 @@ public abstract class FrameworkServlet extends HttpServletBean {
protected void configureAndRefreshWebApplicationContext(ConfigurableWebApplicationContext wac) {
if (ObjectUtils.identityToString(wac).equals(wac.getId())) {
// 应用程序上下文id仍设置为其原始默认值如果该id不为空的话
// 应用程序上下文id 仍设置为其原始默认值,如果该 id 不为空的话
if (this.contextId != null) {
wac.setId(this.contextId);
}
else {
// 生成默认的id
// 生成默认的 id
ServletContext sc = getServletContext();
if (sc.getMajorVersion() == 2 && sc.getMinorVersion() < 5) {
// 当Servlet<=2.4如果有请使用web.xml中指定的名称。
// 当 Servlet <= 2.4:如果有,请使用 web.xml 中指定的名称。
String servletContextName = sc.getServletContextName();
if (servletContextName != null) {
wac.setId(ConfigurableWebApplicationContext.APPLICATION_CONTEXT_ID_PREFIX + servletContextName +
@ -259,7 +261,7 @@ public abstract class FrameworkServlet extends HttpServletBean {
}
}
else {
// Servlet 2.5的getContextPath可用
// Servlet 2.5 getContextPath 可用!
wac.setId(ConfigurableWebApplicationContext.APPLICATION_CONTEXT_ID_PREFIX +
ObjectUtils.getDisplayString(sc.getContextPath()) + "/" + getServletName());
}
@ -272,8 +274,8 @@ public abstract class FrameworkServlet extends HttpServletBean {
wac.setNamespace(getNamespace());
wac.addApplicationListener(new SourceFilteringListener(wac, new ContextRefreshListener()));
// 在刷新上下文的任何情况下,都将会调用wac环境的initPropertySources()方法。
// 在此处执行此方法以确保在刷新上下文之前servlet属性源已准备就绪
// 在刷新上下文的任何情况下,都将会调用 此wac 的 env的 initPropertySources()方法。
// 在此处执行此方法以确保在刷新上下文之前servlet属性源 已准备就绪
ConfigurableEnvironment env = wac.getEnvironment();
if (env instanceof ConfigurableWebEnvironment) {
((ConfigurableWebEnvironment)env).initPropertySources(getServletContext(), getServletConfig());
@ -283,20 +285,20 @@ public abstract class FrameworkServlet extends HttpServletBean {
applyInitializers(wac);
// IoC容器都是通过该方法完成 容器初始化的
// IoC容器 都是通过该方法完成 容器初始化的
wac.refresh();
}
}
```
这时候DispatcherServlet中的IoC容器已经建立起来了这个IoC容器是 根上下文 的子容器。如果要查找一个由DispatcherServlet所持有的IoC容器来管理的Bean系统会首先到 根上下文 中去查找。如果查找不到才会到DispatcherServlet所管理的IoC容器去进行查找这是由IoC容器getBean()的实现来决定的。通过一系列在Web容器中执行的动作在这个上下文体系建立和初始化完毕的基础上Spring MVC就可以发挥其作用了。下面来分析一下Spring MVC的具体实现。
这时候 DispatcherServlet 中的 IoC容器 已经建立起来了,这个 IoC容器 是 根上下文 的子容器。如果要查找一个由 DispatcherServlet 所持有的 IoC容器 来管理的 Bean系统会首先到 根上下文 中去查找。如果查找不到,才会到 DispatcherServlet 所管理的 IoC容器 去进行查找,这是由 IoC容器getBean() 的实现来决定的。通过一系列在 Web容器 中执行的动作在这个上下文体系建立和初始化完毕的基础上SpringMVC 就可以发挥其作用了。下面来分析一下 SpringMVC 的具体实现。
在前面分析DispatchServlet的初始化过程中可以看到DispatchServlet持有一个以自己的Servlet名称命名的IoC容器。这个IoC容器是一个WebApplicationContext对象这个IoC容器建立起来以后意味着DispatcherServlet拥有自己的Bean定义空间这为使用各个独立的XML文件来配置MVC中各个Bean创造了条件。由于在初始化结束以后与Web容器相关的加载过程实际上已经完成了SpringMVC的具体实现和普通的Spring应用程序的实现并没有太大的差别。
在前面分析 DispatchServlet 的初始化过程中可以看到DispatchServlet 持有一个以自己的 Servlet名称 命名的 IoC容器。这个 IoC容器 是一个 WebApplicationContext对象这个 IoC容器 建立起来以后,意味着 DispatcherServlet 拥有自己的 Bean定义空间这为使用各个独立的 XML文件 来配置 MVC 中各个 Bean 创造了条件。由于在初始化结束以后,与 Web容器 相关的加载过程实际上已经完成了SpringMVC 的具体实现和普通的 Spring应用程序 的实现并没有太大的差别。
在DispatcherServlet的初始化过程中以对HandlerMapping的初始化调用作为触发点了解SpringMVC模块初始化的方法调用关系。这个调用关系最初是由HttpServletBean的init()方法触发的这个HttpServletBean是HttpServlet的子类。接着会在HttpServletBean的子类FrameworkServlet中对IoC容器完成初始化在这个初始化方法中会调用DispatcherServlet的initStrategies()方法该方法包括对各种MVC框架的实现元素比如支持国际化的LocalResolver、支持request映射的HandlerMappings以及视图生成的ViewResolver等。由该方法启动整个Spring MVC框架的初始化。
DispatcherServlet 的初始化过程中,以对 HandlerMapping 的初始化调用作为触发点,了解 SpringMVC模块 初始化的方法调用关系。这个调用关系最初是由 HttpServletBean init()方法 触发的,这个 HttpServletBean HttpServlet 的子类。接着会在 HttpServletBean 的子类 FrameworkServlet 中对 IoC容器 完成初始化,在这个初始化方法中,会调用 DispatcherServlet initStrategies()方法,该方法包括对各种 MVC框架 的实现元素,比如支持国际化的 LocalResolver、支持 request 映射的 HandlerMappings以及视图生成的 ViewResolver 等。由该方法启动整个 SpringMVC框架 的初始化。
```java
public class DispatcherServlet extends FrameworkServlet {
/**
* 初始化此servlet使用的策略对象。
* 初始化此 servlet 使用的策略对象。
* 可以在子类中重写以便初始化进一步的策略对象U8C
*/
protected void initStrategies(ApplicationContext context) {
@ -306,7 +308,7 @@ public class DispatcherServlet extends FrameworkServlet {
initLocaleResolver(context);
// 主题view层
initThemeResolver(context);
// 解析url和Method的对应关系
// 解析 url Method 的对应关系
initHandlerMappings(context);
// 适配器匹配
initHandlerAdapters(context);
@ -321,42 +323,42 @@ public class DispatcherServlet extends FrameworkServlet {
}
}
```
对于具体的初始化过程根据上面的方法名称很容易理解。以HandlerMapping为例来说明这个initHandlerMappings()过程。这里的Mapping关系的作用是为HTTP请求找到相应的Controller控制器从而利用这些控制器Controller去完成设计好的数据处理工作。
对于具体的初始化过程,根据上面的方法名称,很容易理解。以 HandlerMapping 为例来说明这个 initHandlerMappings()过程。这里的 Mapping关系 的作用是,为 HTTP请求 找到相应的 Controller控制器从而利用这些 控制器Controller 去完成设计好的数据处理工作。
HandlerMappings完成对MVC中Controller的定义和配置只不过在Web这个特定的应用环境中这些控制器是与具体的HTTP请求相对应的。在HandlerMapping初始化的过程中把在Bean配置文件中配置好的HandlerMapping从IoC容器中取得。
HandlerMappings 完成对 MVC Controller 的定义和配置,只不过在 Web 这个特定的应用环境中,这些控制器是与具体的 HTTP请求 相对应的。在 HandlerMapping初始化 的过程中,把在 Bean配置文件 中配置好的 HandlerMapping IoC容器 中取得。
```java
/**
* 初始化此类使用的HandlerMappings。
* 如果在BeanFactory中没有为此命名空间定义的HandlerMapping bean则默认为BeanNameUrlHandlerMapping
* 初始化此类使用的 HandlerMappings。
* 如果在 BeanFactory 中没有为此命名空间定义的 HandlerMapping bean则默认为 BeanNameUrlHandlerMapping
*/
private void initHandlerMappings(ApplicationContext context) {
this.handlerMappings = null;
// 这个detectAllHandlerMappings默认为true表示从所有的IoC容器中获取所有的HandlerMappings
// 这个 detectAllHandlerMappings 默认为 true表示从所有的 IoC容器 中获取所有的HandlerMappings
if (this.detectAllHandlerMappings) {
// 查找所有的HandlerMapping从应用上下文context及其双亲上下文中
// 查找所有的 HandlerMapping 应用上下文context 及其双亲上下文中
Map<String, HandlerMapping> matchingBeans = BeanFactoryUtils.beansOfTypeIncludingAncestors(
context, HandlerMapping.class, true, false);
if (!matchingBeans.isEmpty()) {
this.handlerMappings = new ArrayList<HandlerMapping>(
matchingBeans.values());
// 保持HandlerMappings的有序性
// 保持 HandlerMappings 的有序性
OrderComparator.sort(this.handlerMappings);
}
}
else {
try {
// 根据名称从当前的IoC容器中通过getBean()获取HandlerMapping
// 根据名称从当前的 IoC容器 中通过 getBean() 取HandlerMapping
HandlerMapping hm = context.getBean(HANDLER_MAPPING_BEAN_NAME,
HandlerMapping.class);
this.handlerMappings = Collections.singletonList(hm);
}
catch (NoSuchBeanDefinitionException ex) {
// 忽略稍后将添加默认的HandlerMapping
// 忽略,稍后将添加默认的 HandlerMapping
}
}
// 如果找不到其他映射请通过注册默认的HandlerMapping确保至少有一个HandlerMapping
// 如果找不到其他映射,请通过注册默认的 HandlerMapping 确保至少有一个 HandlerMapping
if (this.handlerMappings == null) {
this.handlerMappings = getDefaultStrategies(context, HandlerMapping.class);
if (logger.isDebugEnabled()) {
@ -366,20 +368,18 @@ HandlerMappings完成对MVC中Controller的定义和配置只不过在Web这
}
}
```
经过以上读取过程handlerMappings变量就已经获取了在Bean中配置好的映射关系。其他的初始化过程和handlerMappings比较类似都是直接从IoC容器中读入配置所以这里的MVC初始化过程是建立在IoC容器已经初始化完成的基础上的。
经过以上读取过程handlerMappings变量 就已经获取了在 Bean 中配置好的映射关系。其他的初始化过程和 handlerMappings 比较类似,都是直接从 IoC容器 中读入配置,所以这里的 MVC初始化过程 是建立在 IoC容器 已经初始化完成的基础上的。
## 4 SpringMVC处理分发HTTP请求
### 4.1 HandlerMapping的配置和设计原理
前面分析了DispatcherServlet对Spring MVC框架的初始化过程在此基础上我们再进一步分析HandlerMapping的实现原理看看这个MVC框架中比较关键的控制部分是如何实现的。
前面分析了 DispatcherServlet SpringMVC框架 的初始化过程,在此基础上,我们再进一步分析 HandlerMapping 的实现原理,看看这个 MVC框架 中比较关键的控制部分是如何实现的。
在初始化完成时在上下文环境中已定义的所有HandlerMapping都已经被加载了这些加载的handlerMappings被放在一个List中并被排序存储着HTTP请求对应的映射数据。这个List中的每一个元素都对应着一个具体handlerMapping的配置一般每一个handlerMapping
可以持有一系列从URL请求到Controller的映射而Spring MVC提供了一系列的HandlerMapping实现。
在初始化完成时,在上下文环境中已定义的所有 HandlerMapping 都已经被加载了,这些加载的 handlerMappings 被放在一个 List 中并被排序,存储着 HTTP请求 对应的映射数据。这个 List 中的每一个元素都对应着一个具体 handlerMapping 的配置,一般每一个 handlerMapping 可以持有一系列从 URL请求 到 Controller 的映射,而 SpringMVC 提供了一系列的 HandlerMapping 实现。
![avatar](/images/springMVC/HandlerMapping组件.png)
以SimpleUrlHandlerMapping这个handlerMapping为例来分析HandlerMapping的设计与实现。在SimpleUrlHandlerMapping中定义了一个map来 持有 一系列的映射关系。通过这些在HandlerMapping中定义的映射关系即这些URL请求和控制器的对应关系使Spring MVC
应用可以根据HTTP请求确定一个对应的Controller。具体来说这些映射关系是通过接口HandlerMapping来封装的在HandlerMapping接 口中定义了一个getHandler方法通过这个方法可以获得与HTTP请求对应的HandlerExecutionChain在这个HandlerExecutionChain
封装了具体的Controller对象。
以 SimpleUrlHandlerMapping 为例来分析 HandlerMapping 的设计与实现。在 SimpleUrlHandlerMapping 中,定义了一个 Map 来持有一系列的映射关系。通过这些在 HandlerMapping 中定义的映射关系,即这些 URL请求 和控制器的对应关系,使 SpringMVC
应用 可以根据 HTTP请求 确定一个对应的 Controller。具体来说这些映射关系是通过 HandlerMapping接口 来封装的,在 HandlerMapping接口 中定义了一个 getHandler()方法,通过这个方法,可以获得与 HTTP请求 对应的 HandlerExecutionChain在这个 HandlerExecutionChain 中,封装了具体的 Controller对象。
```java
public interface HandlerMapping {
@ -396,15 +396,13 @@ public interface HandlerMapping {
String PRODUCIBLE_MEDIA_TYPES_ATTRIBUTE = HandlerMapping.class.getName() + ".producibleMediaTypes";
/**
* 返回的这个HandlerExecutionChain不但持有handler本身还包括了处理这个HTTP请求的拦截器
* 返回的这个 HandlerExecutionChain 不但持有 handler本身还包括了处理这个 HTTP请求 的拦截器
*/
HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception;
}
```
这个HandlerExecutionChain的实现看起来比较简洁它持有一个Interceptor链和一个handler对象这个handler对象实际上就是HTTP请求对应的Controller在持有这个handler对象的同时还在HandlerExecutionChain中设置了一个拦截器链通过这个拦截器链中的拦截器,
可以为handler对象提供功能的增强。要完成这些工作需要对拦截器链和handler都进行配置这些配置都是在HandlerExecutionChain的初始化函数中完成的。为了维护这个拦截器链和handlerHandlerExecutionChain还提供了一系列与拦截器链维护相关的操作比如为拦
截器链增加拦截器的addInterceptor()方法。
这个 HandlerExecutionChain 的实现看起来比较简洁,它持有一个 拦截器链(HandlerInterceptor对象列表) 和一个 handler对象这个 handler对象 实际上就是 HTTP请求 对应的 Controller在持有这个 handler对象 的同时,还在 HandlerExecutionChain 中设置了一个拦截器链,通过这个拦截器链中的拦截器,可以为 handler对象 提供功能的增强。要完成这些工作,需要对拦截器链和 handler 都进行配置,这些配置都是在 HandlerExecutionChain 的初始化函数中完成的。为了维护这个拦截器链和 handlerHandlerExecutionChain 还提供了一系列与拦截器链维护相关的操作,比如,为拦截器链增加拦截器的 addInterceptor()方法。
```java
public class HandlerExecutionChain {
@ -460,7 +458,7 @@ public class HandlerExecutionChain {
}
/**
* 延迟初始化interceptorList和interceptors集合
* 延迟初始化 interceptorList interceptors集合
*/
private void initInterceptorList() {
if (this.interceptorList == null) {
@ -496,12 +494,13 @@ public class HandlerExecutionChain {
}
}
```
HandlerExecutionChain中定义的Handler和Interceptor需要在定义HandlerMapping时配置好例如对具体的SimpleURLHandlerMapping要做的就是根据URL映射的方式注册Handler和Interceptor从而维护一个反映这种映射关系的handlerMap。当需要匹配HTTP请求时需要查询这个handlerMap中的信息来得到对应的HandlerExecutionChain。这些信息是什么时候配置好的呢?这里有一个注册过程这个注册过程在容器对Bean进行依赖注入时发生它实际上是通过一个Bean的postProcessor()来完成的。以SimpleHandlerMapping为例需要注意的是这里用到了对容器的回调只有SimpleHandlerMapping是ApplicationContextAware的子类才能启动这个注册过程。这个注册过程完成的是反映URL和Controller之间映射关系的handlerMap的建立。
HandlerExecutionChain 中定义的 Handler HandlerInterceptor[]属性 需要在定义 HandlerMapping 时配置好,例如对具体的 SimpleURLHandlerMapping要做的就是根据 URL映射 的方式,注册 Handler HandlerInterceptor[],从而维护一个反映这种映射关系的 handlerMap。当需要匹配 HTTP请求 时,需要查询这个 handlerMap 中的信息来得到对应的 HandlerExecutionChain。这些信息是什么时候配置好的呢?这里有一个注册过程,这个注册过程在容器对 Bean 进行依赖注入时发生,它实际上是通过一个 Bean postProcessor() 来完成的。以 SimpleHandlerMapping 为例,需要注意的是,这里用到了对容器的回调,只有 SimpleHandlerMapping ApplicationContextAware 的子类才能启动这个注册过程。这个注册过程完成的是反映 URL Controller 之间映射关系的 handlerMap 的建立。
![avatar](/images/springMVC/SimpleUrlHandlerMapping的继承关系.png)
```java
public class SimpleUrlHandlerMapping extends AbstractUrlHandlerMapping {
@Override
public void initApplicationContext() throws BeansException {
super.initApplicationContext();
@ -509,18 +508,18 @@ public class SimpleUrlHandlerMapping extends AbstractUrlHandlerMapping {
}
/**
* 为相应的路径注册URL映射中指定的所有handlers处理程序
* 为相应的路径注册 URL映射 中指定的所有 handlers处理程序
*/
protected void registerHandlers(Map<String, Object> urlMap) throws BeansException {
if (urlMap.isEmpty()) {
logger.warn("Neither 'urlMap' nor 'mappings' set on SimpleUrlHandlerMapping");
}
else {
// 这里对bean的配置进行解析然后调用父类的registerHandler()方法进行解析
// 这里对 bean 的配置进行解析,然后调用父类的 registerHandler()方法 进行解析
for (Map.Entry<String, Object> entry : urlMap.entrySet()) {
String url = entry.getKey();
Object handler = entry.getValue();
// 如果url没有斜线就在前面加上斜线
// 如果 url 没有斜线,就在前面加上斜线
if (!url.startsWith("/")) {
url = "/" + url;
}
@ -535,12 +534,12 @@ public class SimpleUrlHandlerMapping extends AbstractUrlHandlerMapping {
}
}
```
这个SimpleUrlHandlerMapping注册过程的完成很大一部分需要它的基类来配合这个基类就是AbstractUrlHandlerMapping。在AbstractUrlHandlerMapping的处理过程中如果使用Bean的名称作为映射那么直接从容器中获取这个HTTP映射对应的Bean然后还要对不同的URL配置进行解析处理比如在HTTP请求中配置成“/”和通配符“/*” 的URL以及正常的URL请求完成这个解析处理过程以后
把URL和handler作为键值对放到一个handlerMap中去。
这个 SimpleUrlHandlerMapping 注册过程的完成,很大一部分需要它的基类来配合,这个基类就是 AbstractUrlHandlerMapping。在 AbstractUrlHandlerMapping 的处理过程中,如果使用 Bean 的名称作为映射,那么直接从容器中获取这个 HTTP映射 对应的 Bean然后还要对不同的 URL配置 进行解析处理,比如在 HTTP请求 中配置成 “/” 和 通配符“/*” 的 URL以及正常的 URL请求完成这个解析处理过程以后会把 URL 和 handler 作为键值对放到一个 handlerMap 中去。
```java
public abstract class AbstractHandlerMapping extends WebApplicationObjectSupport implements HandlerMapping, Ordered {
/**
* 为给定的URL路径注册指定的handler处理程序
* 为给定的 URL路径 注册指定的 handler处理程序
*/
protected void registerHandler(String[] urlPaths, String beanName) throws BeansException, IllegalStateException {
Assert.notNull(urlPaths, "URL path array must not be null");
@ -550,14 +549,14 @@ public abstract class AbstractHandlerMapping extends WebApplicationObjectSupport
}
/**
* 为给定的URL路径注册指定的handler处理程序
* 为给定的 URL路径 注册指定的 handler处理程序
*/
protected void registerHandler(String urlPath, Object handler) throws BeansException, IllegalStateException {
Assert.notNull(urlPath, "URL path must not be null");
Assert.notNull(handler, "Handler object must not be null");
Object resolvedHandler = handler;
// 如果使用bean名称进行映射就直接从IoC容器中获取该bean名称对应的handler
// 如果使用 bean名称 进行映射,就直接从 IoC容器 中获取该 bean名称 对应的 handler
if (!this.lazyInitHandlers && handler instanceof String) {
String handlerName = (String) handler;
if (getApplicationContext().isSingleton(handlerName)) {
@ -574,21 +573,21 @@ public abstract class AbstractHandlerMapping extends WebApplicationObjectSupport
}
}
else {
// 处理URL是"/"的映射,把这个"/"映射的controller设置到rootHandler中
// 处理 URL "/" 的映射,把这个 "/" 映射的 controller 设置到 rootHandler
if (urlPath.equals("/")) {
if (logger.isInfoEnabled()) {
logger.info("Root mapping to " + getHandlerDescription(handler));
}
setRootHandler(resolvedHandler);
}
// 处理URL是"/"的映射,把这个"/"映射的controller设置到defaultHandler中
// 处理 URL "/" 的映射,把这个 "/" 映射的 controller 设置到 defaultHandler
else if (urlPath.equals("/*")) {
if (logger.isInfoEnabled()) {
logger.info("Default mapping to " + getHandlerDescription(handler));
}
setDefaultHandler(resolvedHandler);
}
// 处理正常的URL映射此handlerMap的key和value分别代表URL和映射的Controller
// 处理正常的 URL映射 handlerMap key value 分别代表 URL 映射的Controller
else {
this.handlerMap.put(urlPath, resolvedHandler);
if (logger.isInfoEnabled()) {
@ -599,7 +598,7 @@ public abstract class AbstractHandlerMapping extends WebApplicationObjectSupport
}
/**
* 为此handler映射设置根handler即要为根路径"/"注册的handler
* 为此 handler映射 设置 根handler即要为根路径"/")注册的 handler
* <p>Default is {@code null}, indicating no root handler.
*/
public void setRootHandler(Object rootHandler) {
@ -611,7 +610,7 @@ public abstract class AbstractHandlerMapping extends WebApplicationObjectSupport
}
/**
* 设置此handler映射的默认handler。如果未找到特定映射则将返回此handler
* 设置 此handler映射 的默认 handler。如果未找到特定映射则将返回 此handler
*/
public void setDefaultHandler(Object defaultHandler) {
this.defaultHandler = defaultHandler;
@ -622,50 +621,50 @@ public abstract class AbstractHandlerMapping extends WebApplicationObjectSupport
}
}
```
这里的handlerMap是一个HashMap其中保存了URL请求和Controller的映射关系这个handlerMap是在AbstractUrlHandlerMapping中定义的 Map<String, object> handlerMap = new LinkedHashMap<String, object>() 这个配置好URL请求和handler映射数据的handlerMap为Spring MVC响应HTTP请求准备好了基本的映射数据根据这个handlerMap以及设置于其中的映射数据可以方便地由
URL请求得到它所对应的handler。有了这些准备工作Spring MVC就可以等待HTTP请求的到来了。
这里的 handlerMap 是一个 HashMap其中保存了 “URL请求” --> “Controller对象” 的映射关系,这个 handlerMap 是在 AbstractUrlHandlerMapping 中定义的( Map<String, object> handlerMap = new LinkedHashMap<String, object>() ),这个配置好 URL请求 和 handler映射数据 的 handlerMap为 SpringMVC 响应 HTTP请求 准备好了基本的映射数据,根据这个 handlerMap 以及设置于其中的映射数据,可以方便地由 URL请求 得到它所对应的 handler。有了这些准备工作SpringMVC 就可以等待 HTTP请求 的到来了。
### 4.2 使用HandlerMapping完成请求的映射处理
继续通过SimpleUrlHandlerMapping的实现来分析HandlerMapping的接口方法getHandler()该方法会根据初始化时得到的映射关系来生成DispatcherServlet需要的HandlerExecutionChain也就是说这个getHandler()方法是实际使用HandlerMapping完成请求的映射处理的地方。在前面的HandlerExecutionChain的执行过程中首先在AbstractHandlerMapping中启动getHandler的调用。
继续通过 SimpleUrlHandlerMapping的实现 来分析 HandlerMapping 接口方法getHandler(),该方法会根据初始化时得到的映射关系来生成 DispatcherServlet 需要的 HandlerExecutionChain也就是说这个 getHandler()方法 是实际使用 HandlerMapping 完成请求的映射处理的地方。在前面的 HandlerExecutionChain 的执行过程中,首先在 AbstractHandlerMapping 中启动 getHandler() 的调用。
```java
public abstract class AbstractHandlerMapping extends WebApplicationObjectSupport implements HandlerMapping, Ordered {
/**
* 查找给定请求的handler如果找不到特定的handler则返回到defaultHandler
* 查找给定请求的 handler如果找不到特定的 handler则返回到 defaultHandler
*/
public final HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
// 模板方法模式
// 这里用到了模板方法模式getHandler() 是一个模板方法定义了流程getHandlerInternal() 则是
// 一个抽象方法,交由子类实现
Object handler = getHandlerInternal(request);
// 如果找不到特定的handler则取defaultHandler
// 如果找不到特定的 handler则取 defaultHandler
if (handler == null) {
handler = getDefaultHandler();
}
// defaultHandler也没有则返回null
// defaultHandler 也没有则返回 null
if (handler == null) {
return null;
}
// 如果该handler是String类型的说明它是一个beanname
// 根据该beanname从IoC容器中获取真正的handler对象
// 如果该 handler 是 String类型的说明它是一个 beanName
// 根据该 beanName 从 IoC容器 中获取真正的 handler对象
if (handler instanceof String) {
String handlerName = (String) handler;
handler = getApplicationContext().getBean(handlerName);
}
// 这里把handler添加到到HandlerExecutionChain中
// 这里把 handler 添加到到 HandlerExecutionChain
return getHandlerExecutionChain(handler, request);
}
}
```
取得handler的具体过程在getHandlerInternal()方法中实现这个方法接受HTTP请求作为参数它的实现在AbstractHandlerMapping的子类AbstractUrlHandlerMapping中这个实现过程包括从HTTP请求中得到URL并根据URL到urlMapping中获得handler。
取得 handler 的具体过程在 getHandlerInternal()方法 中实现,这个方法接受 HTTP请求 作为参数,它的实现在 AbstractHandlerMapping 的子类 AbstractUrlHandlerMapping 中,这个实现过程包括从 HTTP请求 中得到 URL并根据 URL urlMapping 中获得 handler。
```java
public abstract class AbstractUrlHandlerMapping extends AbstractHandlerMapping {
/**
* 查找给定请求的URL路径 对应的handler
* 查找 给定请求的URL 对应的 handler
*/
@Override
protected Object getHandlerInternal(HttpServletRequest request) throws Exception {
// 从request中获取请求的URL路径
// 从 request 中获取请求的 URL路径
String lookupPath = getUrlPathHelper().getLookupPathForRequest(request);
// 将得到的URL路径与handler进行匹配得到对应的handler如果没有对应的handler
// 则返回null这样默认的handler会被使用
// 将得到的 URL路径 handler 进行匹配,得到对应的 handler如果没有对应的 handler
// 则返回 null这样 默认的handler 会被使用
Object handler = lookupHandler(lookupPath, request);
if (handler == null) {
// We need to care for the default handler directly, since we need to
@ -674,7 +673,7 @@ public abstract class AbstractUrlHandlerMapping extends AbstractHandlerMapping {
if ("/".equals(lookupPath)) {
rawHandler = getRootHandler();
}
// 使用默认的handler
// 使用 默认的handler
if (rawHandler == null) {
rawHandler = getDefaultHandler();
}
@ -698,7 +697,7 @@ public abstract class AbstractUrlHandlerMapping extends AbstractHandlerMapping {
}
/**
* 查找给定URL路径的handler实例
* 查找给定 URL路径 handler实例
*/
protected Object lookupHandler(String urlPath, HttpServletRequest request) throws Exception {
// 直接匹配
@ -758,28 +757,28 @@ public abstract class AbstractUrlHandlerMapping extends AbstractHandlerMapping {
}
}
```
经过这一系列对HTTP请求进行解析和匹配handler的过程得到了与请求对应的handler处理器。在返回的handler中已经完成了在HandlerExecutionChain中进行封装的工作为handler对HTTP请求的响应做好了准备。
经过这一系列对 HTTP请求 进行解析和匹配 handler 的过程,得到了与请求对应的 handler处理器。在返回的 handler 中,已经完成了在 HandlerExecutionChain 中进行封装的工作,为 handler HTTP请求 的响应做好了准备。
### 4.3 DispatcherServlet对HTTP请求的分发处理
DispatcherServlet是Spring MVC框架中非常重要的一个类不但建立了自己持有的IoC容器还肩负着请求分发处理的重任对HTTP请求的处理是在doService()方法中完成的。DispatcherServlet是HttpServlet的子类 与其他HttpServlet一样可以通过doService()来响应HTTP的请求。然而依照Spring MVC的使用业务逻辑的调用入口是在handler的handler()方法中实现的这是连接Spring MVC和应用业务逻辑实现的地方。
DispatcherServlet SpringMVC框架 中非常重要的一个类,不但建立了自己持有的 IoC容器还肩负着请求分发处理的重任 HTTP请求 的处理是在 doService()方法 中完成的。DispatcherServlet HttpServlet 的子类 ,与其他 HttpServlet 一样,可以通过 doService() 来响应 HTTP的请求。然而依照 SpringMVC 的使用,业务逻辑的调用入口是在 handler handler()方法 中实现的,这是连接 SpringMVC 和应用业务逻辑实现的地方。
```java
public class DispatcherServlet extends FrameworkServlet {
/** 此servlet使用的HandlerMappings列表 */
/** 此 DispatcherServlet 使用的 HandlerMapping对象列表 */
private List<HandlerMapping> handlerMappings;
/** 此servlet使用的HandlerAdapter列表 */
/** 此 DispatcherServlet 使用的 HandlerAdapter对象列表 */
private List<HandlerAdapter> handlerAdapters;
/**
* 公开DispatcherServlet特定的请求属性并将其委托给doDispatch()方法进行实际的分发
* 公开 DispatcherServlet 特定的请求属性,并将其委托给 doDispatch()方法 进行实际的分发
*/
@Override
protected void doService(HttpServletRequest request, HttpServletResponse response)
throws Exception {
if (logger.isDebugEnabled()) {
// 得到请求的URI
// 得到 请求的URI
String requestUri = urlPathHelper.getRequestUri(request);
String resumed = WebAsyncUtils.getAsyncManager(request).hasConcurrentResult()
? " resumed" : "";
@ -836,8 +835,8 @@ public class DispatcherServlet extends FrameworkServlet {
/**
* 中央控制器,控制请求的转发
* 对请求的处理实际上是由doDispatch()来完成的它是DispatcherServlet完成HTTP请求分发处理的主要方法,
* 包括准备ModelAndView调用getHandler()方法来响应HTTP请求然后通过执行Handler的处理来获取请求的
* 对请求的处理实际上是由 doDispatch() 来完成的,它是 DispatcherServlet 完成 HTTP请求 分发处理的主要方法,
* 包括准备 ModelAndView调用 getHandler()方法 来响应 HTTP请求然后通过执行 Handler的处理 来获取请求的
* 处理结果,最后把结果返回出去
*/
protected void doDispatch(HttpServletRequest request, HttpServletResponse response)
@ -849,7 +848,7 @@ public class DispatcherServlet extends FrameworkServlet {
WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);
try {
// 为视图准备好一个ModelAndView这个ModelAndView持有handler处理请求的结果
// 为视图准备好一个 ModelAndView这个 ModelAndView 持有 handler处理请求的结果
ModelAndView mv = null;
Exception dispatchException = null;
@ -858,20 +857,20 @@ public class DispatcherServlet extends FrameworkServlet {
processedRequest = checkMultipart(request);
multipartRequestParsed = processedRequest != request;
// 2.取得处理当前请求的controller这里也称为hanlder处理器这里并不是
// 直接返回controller而是返回的HandlerExecutionChain请求处理器链对象
// 该对象封装了handler和interceptors
// 2.取得处理当前请求的 Controller对象这里也称为 hanlder处理器这里并不是
// 直接返回 controller对象,而是返回的 HandlerExecutionChain请求处理器链对象
// 该对象封装了 handler interceptors
mappedHandler = getHandler(processedRequest, false);
// 如果handler为空,则返回404
// 如果 handler 为空,则返回 404
if (mappedHandler == null || mappedHandler.getHandler() == null) {
noHandlerFound(processedRequest, response);
return;
}
// 3. 获取处理request的处理器适配器handler adapter
// 3. 获取处理 request 的处理器适配器 HandlerAdapter
HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());
// 获取请求方式GET, POST, PUT
// 获取 请求方式GET, POST, PUT
String method = request.getMethod();
boolean isGet = "GET".equals(method);
if (isGet || "HEAD".equals(method)) {
@ -939,7 +938,7 @@ public class DispatcherServlet extends FrameworkServlet {
}
/**
* 返回此请求的HandlerExecutionChain按顺序尝试所有的HandlerMapping
* 返回此请求的 HandlerExecutionChain按顺序尝试所有的 HandlerMapping
*/
@Deprecated
protected HandlerExecutionChain getHandler(HttpServletRequest request, boolean cache)
@ -948,17 +947,17 @@ public class DispatcherServlet extends FrameworkServlet {
}
/**
* 返回此请求的HandlerExecutionChain
* 返回此请求的 HandlerExecutionChain
*/
protected HandlerExecutionChain getHandler(HttpServletRequest request)
throws Exception {
// 遍历 此servlet使用的HandlerMapping列表
// 遍历 此servlet 使用的 HandlerMapping列表
for (HandlerMapping hm : this.handlerMappings) {
if (logger.isTraceEnabled()) {
logger.trace("Testing handler map [" + hm
+ "] in DispatcherServlet with name '" + getServletName() + "'");
}
// 查找给定请求的handler
// 查找给定请求的 handler
HandlerExecutionChain handler = hm.getHandler(request);
if (handler != null) {
return handler;
@ -968,10 +967,10 @@ public class DispatcherServlet extends FrameworkServlet {
}
/**
* 返回此处理程序对象handler的HandlerAdapter
* 返回 此处理程序对象handler HandlerAdapter
*/
protected HandlerAdapter getHandlerAdapter(Object handler) throws ServletException {
// 对所有持有的HandlerAdapter进行匹配
// 对所有持有的 HandlerAdapter 进行匹配
for (HandlerAdapter ha : this.handlerAdapters) {
if (logger.isTraceEnabled()) {
logger.trace("Testing handler adapter [" + ha + "]");
@ -985,11 +984,11 @@ public class DispatcherServlet extends FrameworkServlet {
}
}
```
通过判断可以知道这个handler是不是Controller接口的实现比如可以通过具体HandlerAdapter的实现来了解这个适配过程。以SimpleControllerHandlerAdapter的实现为例来了解这个判断是怎样起作用的。
通过判断,可以知道这个 handler 是不是 Controller接口 的实现,比如可以通过具体 HandlerAdapter 的实现来了解这个适配过程。以 SimpleControllerHandlerAdapter的实现 为例来了解这个判断是怎样起作用的。
```java
public class SimpleControllerHandlerAdapter implements HandlerAdapter {
// 判断要执行的handler是不是Controller类型的
// 判断要执行的 handler 是不是 Controller类型的
public boolean supports(Object handler) {
return (handler instanceof Controller);
}
@ -1009,8 +1008,4 @@ public class SimpleControllerHandlerAdapter implements HandlerAdapter {
}
```
经过上面一系列的处理得到了handler对象接着就可以开始调用handler对象中的HTTP响应动作了。在handler中封装了应用业务逻辑由这些逻辑对HTTP请求进行相应的处理生成需要的数据并把这些数据封装到ModelAndView对象中去这个ModelAndView的数据封装是Spring MVC框架的要求。对handler来说 这些都是通过调用handler()方法中的handleRequest()方法来触发完成的。在得到ModelAndView对象以后这个ModelAndView对象会被交给MVC模式中的视图类由视图类对ModelAndView对象中的数据进行呈现。
经过上面一系列的处理,得到了 handler对象接着就可以开始调用 handler对象 中的 HTTP响应动作了。在 handler 中封装了应用业务逻辑,由这些逻辑对 HTTP请求 进行相应的处理,生成需要的数据,并把这些数据封装到 ModelAndView对象 中去,这个 ModelAndView 的数据封装是 SpringMVC框架 的要求。对 handler 来说, 这些都是通过调用 handler()方法 中的 handleRequest()方法 来触发完成的。在得到 ModelAndView对象 以后,这个 ModelAndView对象 会被交给 MVC模式 中的视图类,由视图类对 ModelAndView对象 中的数据进行呈现。

Loading…
Cancel
Save