style: format documents

格式化文档
pull/2/head
bingoyang 6 years ago
parent 1ff7bb0851
commit 4594990bb2

@ -11,13 +11,15 @@
## spring系列
### IoC容器
- [BeanDefinition的资源定位过程](/docs/Spring/IoC/一、BeanDefinition的资源定位过程.md)
- [bean解析封装成BeanDefinition](/docs/Spring/IoC/二、将bean解析封装成BeanDefinition.md)
- [BeanDefinition注册进IoC容器](/docs/Spring/IoC/三、将BeanDefinition注册进IoC容器.md)
- [BeanDefinition 的资源定位过程](/docs/Spring/IoC/一、BeanDefinition的资源定位过程.md)
- [ bean 解析封装成 BeanDefinition](/docs/Spring/IoC/二、将bean解析封装成BeanDefinition.md)
- [ BeanDefinition 注册进 IoC 容器](/docs/Spring/IoC/三、将BeanDefinition注册进IoC容器.md)
- [依赖注入(DI)](/docs/Spring/IoC/四、依赖注入(DI).md)
### AOP
- [AOP源码实现及分析](/docs/Spring/AOP/AOP源码实现及分析.md)
- [JDK动态代理的实现原理解析](/docs/Spring/AOP/JDK动态代理的实现原理解析.md)
- [AOP 源码实现及分析](/docs/Spring/AOP/AOP源码实现及分析.md)
- [JDK 动态代理的实现原理解析](/docs/Spring/AOP/JDK动态代理的实现原理解析.md)
### SpringMVC
### SpringJDBC
@ -25,7 +27,7 @@
### Spring事务
### Spring源码故事瞎编版
- [面筋哥IoC容器的一天(上)](/docs/Spring/IoC/面筋哥IoC容器的一天(上).md)
- [面筋哥 IoC 容器的一天(上)](/docs/Spring/IoC/面筋哥IoC容器的一天(上).md)
## MyBatis
@ -35,7 +37,7 @@
## 学习心得
### 个人经验
- [初级开发者应该从spring源码中学什么](docs/学习心得/个人经验/初级开发者应该从spring源码中学什么.md)
- [初级开发者应该从 spring 源码中学什么](docs/学习心得/个人经验/初级开发者应该从spring源码中学什么.md)
### 编码规范

@ -1,13 +1,14 @@
理论性的文字,我觉得就没必要再扯一遍咯,大道理讲这么多,越听越迷糊。不如直接看源码+注解来的明白痛快。所以话不多说,直接上源码。
## 1、主要的接口
#### 1.1、Advice通知
定义了切面的增强方式前置增强BeforeAdvice后置增强AfterAdvice异常增强ThrowsAdvice等。下面看两个主要的子接口的源码。
### 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;
@ -16,16 +17,17 @@ public interface MethodBeforeAdvice extends BeforeAdvice {
public interface AfterReturningAdvice extends AfterAdvice {
/**
* 目标方法method执行后AOP会回调此方法注意它还传入了method的返回值
* 目标方法 method 执行后AOP 会回调此方法,注意,它还传入了 method 的返回值
*/
void afterReturning(Object returnValue, Method method, Object[] args, Object target) throws Throwable;
}
```
#### 1.2、Pointcut方法的横切面
用来定义需要增强的目标方法的集合一般使用正则表达式去匹配筛选指定范围内的所有满足条件的目标方法。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) {
@ -33,7 +35,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) {
@ -45,8 +47,9 @@ public interface AfterReturningAdvice extends AfterAdvice {
return false;
}
```
#### 1.3、Advisor通知器
将Pointcut和Advice有效地结合在一起。它定义了在哪些方法Pointcut上执行哪些动作Advice。下面看一下DefaultPointcutAdvisor的源码实现它通过持有Pointcut和Advice属性来将两者有效地结合在一起。
### 1.3、Advisor 通知器
将 Pointcut 和 Advice 有效地结合在一起。它定义了在哪些方法Pointcut上执行哪些动作Advice。下面看一下 DefaultPointcutAdvisor 的源码实现,它通过持有 Pointcut 和 Advice 属性来将两者有效地结合在一起。
```java
public class DefaultPointcutAdvisor extends AbstractGenericPointcutAdvisor implements Serializable {
@ -60,7 +63,7 @@ public class DefaultPointcutAdvisor extends AbstractGenericPointcutAdvisor imple
}
/**
* 自己定义了PointcutAdvice则使用父类中的定义
* 自己定义了 PointcutAdvice 则使用父类中的定义
*/
public DefaultPointcutAdvisor(Pointcut pointcut, Advice advice) {
this.pointcut = pointcut;
@ -70,7 +73,7 @@ public class DefaultPointcutAdvisor extends AbstractGenericPointcutAdvisor imple
public abstract class AbstractGenericPointcutAdvisor extends AbstractPointcutAdvisor {
//本类是一个抽象类其持有Advice的引用而对Pointcut的引用则在具体的子类中持有
//本类是一个抽象类,其持有 Advice 的引用,而对 Pointcut 的引用,则在具体的子类中持有
private Advice advice;
public void setAdvice(Advice advice) {
@ -88,13 +91,16 @@ public abstract class AbstractGenericPointcutAdvisor extends AbstractPointcutAdv
}
```
## 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、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 的配置和使用。
```xml
<!-- 定义自己的Advisor实现其中包含了Pointcut和Advice -->
<!-- 定义自己的 Advisor 实现,其中包含了 Pointcut Advice -->
<bean id="myAdvisor" class="com.shuitu.MyAdvisor"/>
<bean id="myAOP" class="org.springframework.aop.framework.ProxyFactoryBean">
@ -104,23 +110,24 @@ AOP的实现代码中主要使用了JDK动态代理在特定场景下
<property name="target">
<bean class="com.shuitu.MyTarget"/>
</property>
<!-- 配置相应的Advisor -->
<!-- 配置相应的 Advisor -->
<property name="interceptorNames">
<list><value>myAdvisor</value></list>
</property>
</bean>
```
#### 2.2、ProxyFactoryBean为配置的target生成AopProxy代理对象
ProxyFactoryBean的getObject()方法先对通知器链进行了初始化,然后根据被代理对象类型的不同,生成代理对象。
### 2.2、ProxyFactoryBean 为配置的 target 生成 AopProxy 代理对象
ProxyFactoryBean 的 getObject() 方法先对通知器链进行了初始化,然后根据被代理对象类型的不同,生成代理对象。
```java
/**
* 返回一个代理对象当用户从FactoryBean中获取bean时调用
* 创建此工厂要返回的AOP代理的实例该实例将作为一个单例被缓存
* 返回一个代理对象,当用户从 FactoryBean 中获取 bean 时调用,
* 创建此工厂要返回的 AOP 代理的实例,该实例将作为一个单例被缓存
*/
public Object getObject() throws BeansException {
//初始化通知器链
initializeAdvisorChain();
//这里对Singleton和prototype的类型进行区分生成对应的proxy
//这里对 Singleton prototype 的类型进行区分,生成对应的 proxy
if (isSingleton()) {
return getSingletonInstance();
}
@ -133,10 +140,11 @@ ProxyFactoryBean的getObject()方法先对通知器链进行了初始化,然
}
}
```
#### 2.3、initializeAdvisorChain()初始化Advisor链
### 2.3、initializeAdvisorChain() 初始化 Advisor 链
```java
/**
* 初始化Advisor链可以发现其中有通过对IoC容器的getBean()方法的调用来获取配置好的advisor通知器
* 初始化 Advisor 链,可以发现,其中有通过对 IoC 容器的 getBean() 方法的调用来获取配置好的 advisor 通知器
*/
private synchronized void initializeAdvisorChain() throws AopConfigException, BeansException {
//如果通知器链已经完成初始化,则直接返回
@ -155,10 +163,10 @@ 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 + "'");
@ -174,15 +182,15 @@ ProxyFactoryBean的getObject()方法先对通知器链进行了初始化,然
}
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);
@ -193,8 +201,9 @@ ProxyFactoryBean的getObject()方法先对通知器链进行了初始化,然
this.advisorChainInitialized = true;
}
```
生成singleton的代理对象在getSingletonInstance方法中完成这是ProxyFactoryBean生成AopProxy代理对象的调用入口。代理对象会封装对target对象的调用针对target对象的方法调用会被这里生成的代理对象所拦截。
#### 2.4、getSingletonInstance()生成单例代理对象
生成 singleton 的代理对象在 getSingletonInstance 方法中完成,这是 ProxyFactoryBean 生成 AopProxy 代理对象的调用入口。代理对象会封装对 target 对象的调用,针对 target 对象的方法调用会被这里生成的代理对象所拦截。
### 2.4、getSingletonInstance() 生成单例代理对象
```java
/**
* 返回此类代理对象的单例实例,如果尚未创建该实例,则使用单例模式的懒汉式 创建它
@ -203,7 +212,7 @@ ProxyFactoryBean的getObject()方法先对通知器链进行了初始化,然
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");
@ -212,28 +221,30 @@ ProxyFactoryBean的getObject()方法先对通知器链进行了初始化,然
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);
}
```
上面的createAopProxy()方法调用了ProxyFactoryBean的父类ProxyCreatorSupport中的实现。
上面的 createAopProxy() 方法,调用了 ProxyFactoryBean 的父类 ProxyCreatorSupport 中的实现。
```java
public class ProxyCreatorSupport extends AdvisedSupport {
private AopProxyFactory aopProxyFactory;
public ProxyCreatorSupport() {
//注意这里实例化的是一个DefaultAopProxyFactory所以下面的createAopProxy()方法
//中调用的也是DefaultAopProxyFactory的实现
//注意这里实例化的是一个 DefaultAopProxyFactory所以下面的 createAopProxy() 方法
//中调用的也是 DefaultAopProxyFactory 的实现
this.aopProxyFactory = new DefaultAopProxyFactory();
}
@ -241,7 +252,7 @@ public class ProxyCreatorSupport extends AdvisedSupport {
if (!this.active) {
activate();
}
//调用的是DefaultAopProxyFactory的实现
//调用的是 DefaultAopProxyFactory 的实现
return getAopProxyFactory().createAopProxy(this);
}
@ -255,12 +266,14 @@ public class ProxyCreatorSupport extends AdvisedSupport {
}
```
下面看一下AopProxyFactory接口的实现类DefaultAopProxyFactory的代码。
下面看一下 AopProxyFactory 接口的实现类 DefaultAopProxyFactory 的代码。
```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) {
@ -270,7 +283,7 @@ public class ProxyCreatorSupport extends AdvisedSupport {
/**
*
* 如果目标类是接口则使用JDK动态代理否则使用CGLIB
* 如果目标类是接口,则使用 JDK 动态代理,否则使用 CGLIB
*
*/
if (targetClass.isInterface()) {
@ -283,8 +296,10 @@ public class ProxyCreatorSupport extends AdvisedSupport {
}
}
```
可以看到其根据目标对象是否实现了接口而决定是使用JDK动态代理还是CGLIB去生成代理对象而AopProxy接口的实现类也只有JdkDynamicAopProxy和CglibAopProxy这两个。
#### 2.5、JDK生成AopProxy代理对象
可以看到其根据目标对象是否实现了接口,而决定是使用 JDK 动态代理还是 CGLIB 去生成代理对象,而 AopProxy 接口的实现类也只有 JdkDynamicAopProxy 和 CglibAopProxy 这两个。
### 2.5、JDK 生成 AopProxy 代理对象
```java
final class JdkDynamicAopProxy implements AopProxy, InvocationHandler, Serializable {
@ -293,8 +308,8 @@ final class JdkDynamicAopProxy implements AopProxy, InvocationHandler, Serializa
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;
}
@ -311,13 +326,15 @@ final class JdkDynamicAopProxy implements AopProxy, InvocationHandler, Serializa
Class[] proxiedInterfaces = AopProxyUtils.completeProxiedInterfaces(this.advised);
findDefinedEqualsAndHashCodeMethods(proxiedInterfaces);
//通过Proxy生成代理对象并返回
//通过 Proxy 生成代理对象并返回
return Proxy.newProxyInstance(classLoader, proxiedInterfaces, this);
}
}
```
通过JdkDynamicAopProxy的源码可以非常清楚地看到其使用了JDK动态代理的方式生成了代理对象。JdkDynamicAopProxy实现了InvocationHandler接口并通过Proxy.newProxyInstance()方法生成代理对象并返回。
#### 2.6、CGLIB生成AopProxy代理对象CglibAopProxy
通过 JdkDynamicAopProxy 的源码可以非常清楚地看到,其使用了 JDK 动态代理的方式生成了代理对象。JdkDynamicAopProxy 实现了 InvocationHandler 接口,并通过 Proxy.newProxyInstance() 方法生成代理对象并返回。
### 2.6、CGLIB 生成 AopProxy 代理对象CglibAopProxy
```java
public Object getProxy(ClassLoader classLoader) {
if (logger.isDebugEnabled()) {
@ -339,7 +356,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);
@ -364,7 +381,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);
@ -393,36 +410,37 @@ final class JdkDynamicAopProxy implements AopProxy, InvocationHandler, Serializa
}
}
```
为目标对象target生成代理对象之后在调用代理对象的目标方法时目标方法会进行invoke()回调或callbacks()回调,然后就可以在回调方法中对目标对象的目标方法进行拦截和增强处理了。
为目标对象 target 生成代理对象之后,在调用代理对象的目标方法时,目标方法会进行 invoke() 回调或 callbacks() 回调,然后就可以在回调方法中对目标对象的目标方法进行拦截和增强处理了。
## 3、spring AOP拦截器调用的实现
在spring AOP通过JDK的Proxy类生成代理对象时相关的拦截器已经配置到了代理对象持有的InvocationHandler(即ProxyBeanFactory)的invoke()方法中拦截器最后起作用是通过调用代理对象的目标方法时在代理类中触发了InvocationHandler的invoke()回调。通过CGLIB实现的AOP原理与此相似。
#### 3.1、JdkDynamicAopProxy的invoke()拦截
前面已经通过两种不同的方式生成了AopProxy代理对象下面我们先看一下JdkDynamicAopProxy中的invoke()回调方法中对拦截器调用的实现。
## 3、spring AOP 拦截器调用的实现
在 spring AOP 通过 JDK 的 Proxy 类生成代理对象时,相关的拦截器已经配置到了代理对象持有的 InvocationHandler(即ProxyBeanFactory) 的 invoke() 方法中,拦截器最后起作用,是通过调用代理对象的目标方法时,在代理类中触发了 InvocationHandler 的 invoke() 回调。通过 CGLIB 实现的 AOP原理与此相似。
### 3.1、JdkDynamicAopProxy 的 invoke() 拦截
前面已经通过两种不同的方式生成了 AopProxy 代理对象,下面我们先看一下 JdkDynamicAopProxy 中的 invoke() 回调方法中对拦截器调用的实现。
```java
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
MethodInvocation invocation;
Object oldProxy = null;
boolean setProxyContext = false;
//通过targetSource可以获取被代理对象
//通过 targetSource 可以获取被代理对象
TargetSource targetSource = this.advised.targetSource;
Class targetClass = null;
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);
}
@ -443,24 +461,24 @@ final class JdkDynamicAopProxy implements AopProxy, InvocationHandler, Serializa
//获取定义好的拦截器链
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将无法处理
//注意,如果 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);
@ -469,18 +487,19 @@ 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.2、CglibAopProxy的intercept()拦截
CglibAopProxy的intercept()回调方法实现和JdkDynamicAopProxy的invoke()非常相似只是在CglibAopProxy中构造CglibMethodInvocation对象来完成拦截器链的调用而在JdkDynamicAopProxy中则是通过构造ReflectiveMethodInvocation对象来完成的。
### 3.2、CglibAopProxy 的 intercept() 拦截
CglibAopProxy 的 intercept() 回调方法实现和 JdkDynamicAopProxy 的 invoke() 非常相似,只是在 CglibAopProxy 中构造 CglibMethodInvocation 对象来完成拦截器链的调用,而在 JdkDynamicAopProxy 中则是通过构造 ReflectiveMethodInvocation 对象来完成的。
```java
public Object intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
Object oldProxy = null;
@ -496,18 +515,18 @@ CglibAopProxy的intercept()回调方法实现和JdkDynamicAopProxy的invoke()非
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);
@ -524,19 +543,20 @@ CglibAopProxy的intercept()回调方法实现和JdkDynamicAopProxy的invoke()非
}
}
```
#### 3.3、目标对象中目标方法的调用
对目标对象中目标方法的调用是在AopUtils工具类中利用反射机制完成的。具体代码如下
### 3.3、目标对象中目标方法的调用
对目标对象中目标方法的调用,是在 AopUtils 工具类中利用反射机制完成的。具体代码如下:
```java
public abstract class AopUtils {
/**
* 使用spring的反射机制调用目标方法method的invoke方法
* 使用 spring 的反射机制,调用目标方法 method invoke 方法
*/
public static Object invokeJoinpointUsingReflection(Object target, Method method, Object[] args)
throws Throwable {
try {
//如果method是private等不可访问状态则设置为public公开可访问
//如果 method private 等不可访问状态,则设置为 public 公开可访问
ReflectionUtils.makeAccessible(method);
//最后利用反射完成调用
return method.invoke(target, args);
@ -554,57 +574,58 @@ public abstract class AopUtils {
}
}
```
#### 3.4、AOP拦截器链的调用
JdkDynamicAopProxy和CglibAopProxy虽然使用了不同的代理对象但对AOP拦截的处理却是相同的都是通过ReflectiveMethodInvocation的proceed()方法实现的。
### 3.4、AOP 拦截器链的调用
JdkDynamicAopProxy 和 CglibAopProxy 虽然使用了不同的代理对象,但对 AOP 拦截的处理却是相同的,都是通过 ReflectiveMethodInvocation 的 proceed() 方法实现的。
```java
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)) {
//执行增强方法
return dm.interceptor.invoke(this);
}
else {
//如果不匹配那么process()方法会被递归调用,直到所有的拦截器都被运行过为止
//如果不匹配,那么 process() 方法会被递归调用,直到所有的拦截器都被运行过为止
return proceed();
}
}
else {
//如果interceptorOrInterceptionAdvice是一个MethodInterceptor
//如果 interceptorOrInterceptionAdvice 是一个 MethodInterceptor
//则直接调用其对应的方法
return ((MethodInterceptor) interceptorOrInterceptionAdvice).invoke(this);
}
}
```
#### 3.5、配置通知器
AdvisedSupport中实现了获取拦截器链的方法并使用了缓存。
### 3.5、配置通知器
AdvisedSupport 中实现了获取拦截器链的方法,并使用了缓存。
```java
/**
* 获取拦截器链,为提高效率,同时设置了缓存
*/
public List<Object> getInterceptorsAndDynamicInterceptionAdvice(Method method, Class targetClass) {
//这里使用了缓存cached如果缓存中有就从缓存中获取拦截器链
//没有,则调用(DefaultAdvisorChainFactory)advisorChainFactory的
//getInterceptorsAndDynamicInterceptionAdvice()方法进行获取并缓存到cached
//这里使用了缓存 cached如果缓存中有就从缓存中获取拦截器链
//没有,则调用 (DefaultAdvisorChainFactory)advisorChainFactory
//getInterceptorsAndDynamicInterceptionAdvice() 方法进行获取,并缓存到 cached
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);
@ -612,32 +633,34 @@ AdvisedSupport中实现了获取拦截器链的方法并使用了缓存。
return cached;
}
```
获取拦截器链的工作是由AdvisorChainFactory完成的他是一个拦截器链的生成工厂。由于AdvisorChainFactory接口只有一个实现类DefaultAdvisorChainFactory所以我们直接看这个类中的实现就行咯。
获取拦截器链的工作是由 AdvisorChainFactory 完成的,他是一个拦截器链的生成工厂。由于 AdvisorChainFactory 接口只有一个实现类 DefaultAdvisorChainFactory所以我们直接看这个类中的实现就行咯。
```java
public class DefaultAdvisorChainFactory implements AdvisorChainFactory, Serializable {
public List<Object> getInterceptorsAndDynamicInterceptionAdvice(
Advised config, Method method, Class targetClass) {
//advisor链已经在传进来的config中持有了这里可以直接使用
//advisor中持有切面 和 增强行为的引用
//advisor 链已经在传进来的 config 中持有了,这里可以直接使用
//advisor 中持有切面 和 增强行为的引用
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) {
@ -650,7 +673,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)) {
@ -667,7 +690,7 @@ public class DefaultAdvisorChainFactory implements AdvisorChainFactory, Serializ
}
/**
* 判断config中的Advisors是否符合配置要求
* 判断 config 中的 Advisors 是否符合配置要求
*/
private static boolean hasMatchingIntroductions(Advised config, Class targetClass) {
for (int i = 0; i < config.getAdvisors().length; i++) {
@ -684,16 +707,18 @@ public class DefaultAdvisorChainFactory implements AdvisorChainFactory, Serializ
}
```
这里的advisor通知器是从AdvisedSupport中获取的而advisor的初始化则是在ProxyFactoryBean的getObject()方法中完成的。
这里的 advisor 通知器是从 AdvisedSupport 中获取的,而 advisor 的初始化则是在 ProxyFactoryBean 的 getObject() 方法中完成的。
```java
/**
* 返回一个代理对象当用户从FactoryBean中获取bean时调用
* 创建此工厂要返回的AOP代理的实例该实例将作为一个单例被缓存
* 返回一个代理对象,当用户从 FactoryBean 中获取 bean 时调用,
* 创建此工厂要返回的 AOP 代理的实例,该实例将作为一个单例被缓存
*/
public Object getObject() throws BeansException {
//初始化通知器链
initializeAdvisorChain();
//这里对Singleton和prototype的类型进行区分生成对应的proxy
//这里对 Singleton prototype 的类型进行区分,生成对应的 proxy
if (isSingleton()) {
return getSingletonInstance();
}
@ -707,7 +732,7 @@ public class DefaultAdvisorChainFactory implements AdvisorChainFactory, Serializ
}
/**
* 初始化Advisor链可以发现其中有通过对IoC容器的getBean()方法的调用来获取配置好的advisor通知器
* 初始化 Advisor 链,可以发现,其中有通过对 IoC 容器的 getBean() 方法的调用来获取配置好的 advisor 通知器
*/
private synchronized void initializeAdvisorChain() throws AopConfigException, BeansException {
//如果通知器链已经完成初始化,则直接返回
@ -726,10 +751,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其实就是 bean id通过这个 nameid
//我们就可以从 IoC 容器中获取对应的实例化 bean
for (String name : this.interceptorNames) {
if (logger.isTraceEnabled()) {
logger.trace("Configuring advisor or advice '" + name + "'");
@ -745,19 +770,19 @@ 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);
}
}
@ -766,32 +791,35 @@ public class DefaultAdvisorChainFactory implements AdvisorChainFactory, Serializ
this.advisorChainInitialized = true;
}
```
注意Advisor本身就被配置为bean所以它的获取也是通过IoC容器获得的。
#### 3.6、Advice通知的实现
从DefaultAdvisorChainFactory类中的getInterceptorsAndDynamicInterceptionAdvice()方法我们可以看到其通过AdvisorAdapterRegistry实例对象的getInterceptors()方法利用配置的advisor完成了对拦截器的适配和注册。
注意Advisor 本身就被配置为 bean所以它的获取也是通过 IoC 容器获得的。
### 3.6、Advice 通知的实现
从 DefaultAdvisorChainFactory 类中的 getInterceptorsAndDynamicInterceptionAdvice() 方法我们可以看到,其通过 AdvisorAdapterRegistry 实例对象的 getInterceptors() 方法,利用配置的 advisor 完成了对拦截器的适配和注册。
```java
public List<Object> getInterceptorsAndDynamicInterceptionAdvice(
Advised config, Method method, Class targetClass) {
//advisor链已经在传进来的config中持有了这里可以直接使用
//advisor中持有切面 和 增强行为的引用
//advisor 链已经在传进来的 config 中持有了,这里可以直接使用
//advisor 中持有切面 和 增强行为的引用
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) {
@ -804,7 +832,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)) {
@ -820,16 +848,18 @@ 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);
/**
* 将已实现的AdviceAdapter加入list
* 将已实现的 AdviceAdapter 加入 list
*/
public DefaultAdvisorAdapterRegistry() {
registerAdvisorAdapter(new MethodBeforeAdviceAdapter());
@ -838,7 +868,7 @@ public class DefaultAdvisorAdapterRegistry implements AdvisorAdapterRegistry, Se
}
/**
* 如果adviceObject是Advisor的实例则将adviceObject转换成Advisor类型并返回
* 如果 adviceObject Advisor 的实例,则将 adviceObject 转换成 Advisor 类型并返回
*/
public Advisor wrap(Object adviceObject) throws UnknownAdviceTypeException {
if (adviceObject instanceof Advisor) {
@ -862,18 +892,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));
@ -886,7 +916,10 @@ 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 {
@ -901,14 +934,16 @@ class MethodBeforeAdviceAdapter implements AdvisorAdapter, Serializable {
}
```
可以看到其中的getInterceptor()方法把advice从advisor中取出来然后创建了一个MethodBeforeAdviceInterceptor对象并返回这个对象中持有对advice的引用。下面我们看一下MethodBeforeAdviceInterceptor拦截器的源码实现。
可以看到,其中的 getInterceptor() 方法把 advice 从 advisor 中取出来,然后创建了一个 MethodBeforeAdviceInterceptor 对象,并返回,这个对象中持有对 advice 的引用。下面我们看一下 MethodBeforeAdviceInterceptor 拦截器的源码实现。
```java
public class MethodBeforeAdviceInterceptor implements MethodInterceptor, Serializable {
private MethodBeforeAdvice advice;
/**
* 为指定的advice创建对应的MethodBeforeAdviceInterceptor对象
* 为指定的 advice 创建对应的 MethodBeforeAdviceInterceptor 对象
*/
public MethodBeforeAdviceInterceptor(MethodBeforeAdvice advice) {
Assert.notNull(advice, "Advice must not be null");
@ -916,22 +951,21 @@ 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素质三连一波地址
回顾一下之前的代码,在 AopProxy 代理对象触发的 ReflectiveMethodInvocation 的 proceed() 中,在取得拦截器 interceptor 后调用了其 invoke() 方法。按照 AOP 的配置规则ReflectiveMethodInvocation 触发的拦截器 invoke() 回调,最终会根据 advice 类型的不同,触发 spring 对不同的 advice 的拦截器封装,比如 MethodBeforeAdvice 最终会触发 MethodBeforeAdviceInterceptor 的 invoke() 回调,其它两个以此类推,这里就不逐一分析咯。
spring-aop-reading https://github.com/AmyliaY/spring-aop-reading
另外,可以结合我 GitHub 上对 spring 框架源码的阅读及个人理解一起看,会更有助于各位开发大佬理解,如果对你们有帮助的,还望各位老爷 watchstarfork素质三连一波地址https://github.com/AmyliaY/spring-aop-reading

@ -1,9 +1,10 @@
最近在看springAOP部分的源码所以对JDK动态代理具体是如何实现的这件事产生了很高的兴趣而且能从源码上了解这个原理的话也有助于对spring-aop模块的理解。话不多说上代码。
最近在看 SpringAOP 部分的源码,所以对 JDK 动态代理具体是如何实现的这件事产生了很高的兴趣,而且能从源码上了解这个原理的话,也有助于对 spring-aop 模块的理解。话不多说,上代码。
```java
/**
* 一般会使用实现了InvocationHandler的类 作为代理对象的生产工厂,
* 并且通过持有被代理对象target来在invoke()方法中对被代理对象的目标方法进行调用和增强,
* 这些我们都能通过下面这段代码看懂但代理对象是如何生成的invoke()方法又是如何被调用的呢?
* 一般会使用实现了 InvocationHandler 的类 作为代理对象的生产工厂,
* 并且通过持有被代理对象 target来在 invoke() 方法中对被代理对象的目标方法进行调用和增强,
* 这些我们都能通过下面这段代码看懂但代理对象是如何生成的invoke() 方法又是如何被调用的呢?
*/
public class ProxyFactory implements InvocationHandler{
@ -29,7 +30,7 @@ public class ProxyFactory implements InvocationHandler{
}
/**
* 实现了接口MyInterface和接口的play()方法,可以作为被代理类
* 实现了接口 MyInterface 和接口的 play() 方法,可以作为被代理类
*/
public class TargetObject implements MyInterface {
@ -47,25 +48,25 @@ public class ProxyTest {
public static void main(String[] args) {
TargetObject target = new TargetObject();
//ProxyFactory实现了InvocationHandler接口其中的getInstanse()方法利用Proxy类帮助生成了
//target目标对象的代理对象并返回且ProxyFactory持有对target的引用可以在invoke()中完成对target相应方法
//ProxyFactory 实现了 InvocationHandler 接口,其中的 getInstanse() 方法利用 Proxy 类帮助生成了
//target 目标对象的代理对象,并返回;且 ProxyFactory 持有对 target 的引用,可以在 invoke() 中完成对 target 相应方法
//的调用,以及目标方法前置后置的增强处理
ProxyFactory proxyFactory = new ProxyFactory();
//这个mi就是JDK的Proxy类生成的代理类$Proxy0的对象这个对象中的方法都持有对invoke()方法的回调
//所以当调用其方法时就能够执行invoke()中的增强处理
//这个 mi 就是 JDK Proxy 类生成的代理类$Proxy0 的对象,这个对象中的方法都持有对 invoke() 方法的回调
//所以当调用其方法时,就能够执行 invoke() 中的增强处理
MyInterface mi = (MyInterface)proxyFactory.getInstanse(target);
//这样可以看到mi的Class到底是什么
//这样可以看到 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目标方法的调用及前置后置增强
//然后通过生成的代理类完成对对invoke()的回调
//总的来说,就是在 invoke() 方法中完成 target 目标方法的调用,及前置后置增强
//然后通过生成的代理类完成对对 invoke() 的回调
}
/**
* 将ProxyGenerator生成的动态代理类的输出到文件中利用反编译工具luyten等就可
* 将 ProxyGenerator 生成的动态代理类的输出到文件中,利用反编译工具 luyten 等就可
* 以看到生成的代理类的源码咯,下面给出了其反编译好的代码实现
*/
@Test
@ -92,7 +93,7 @@ public class ProxyTest {
}
/**
* Proxy生成的代理类可以看到其继承了Proxy并且实现了被代理类的接口
* Proxy 生成的代理类,可以看到,其继承了 Proxy并且实现了被代理类的接口
*/
public final class $Proxy0 extends Proxy implements MyInterface
{
@ -105,7 +106,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]);
}
@ -123,8 +124,8 @@ public final class $Proxy0 extends Proxy implements MyInterface
public final void play() {
try {
//这个h其实就是我们调用Proxy.newProxyInstance()方法时传进去的ProxyFactory对象
//该对象的invoke()方法中实现了对目标对象的目标方法的增强。看到这里,利用动态代理实现方法增强的
//这个 h 其实就是我们调用 Proxy.newProxyInstance() 方法时传进去的 ProxyFactory 对象,
//该对象的 invoke() 方法中实现了对目标对象的目标方法的增强。看到这里,利用动态代理实现方法增强的
//调用原理就全部理清咯
super.h.invoke(this, $Proxy0.m3, null);
}
@ -174,4 +175,4 @@ public final class $Proxy0 extends Proxy implements MyInterface
}
```
```

@ -1,33 +1,33 @@
## 前言
之前一直想系统的拜读一下spring的源码看看它到底是如何吸引身边的大神们对它的设计赞不绝口虽然每天工作很忙每天下班后总感觉脑子内存溢出想去放松一下但总是以此为借口恐怕会一直拖下去。所以每天下班虽然有些疲惫但还是按住自己啃下这块硬骨头。
spring源码这种东西真的是一回生二回熟第一遍会被各种设计模式和繁杂的方法调用搞得晕头转向不知道这个方法调用的是哪个父类的实现另一个方法又调的是哪个子类的实现但当你耐下心来多走几遍会发现越看越熟练每次都能get到新的点。
另外对于第一次看spring源码的同学建议先在B站上搜索相关视频看一下然后再结合计文柯老师的《spring技术内幕》深入理解最后再输出自己的理解加强印象。
首先对于我们新手来说还是从我们最常用的两个IoC容器开始分析这次我们先分析FileSystemXmlApplicationContext这个IoC容器的具体实现ClassPathXmlApplicationContext留着下次讲解。
PS可以结合我GitHub上对spring框架源码的翻译注解一起看会更有助于各位开发姥爷的理解。
之前一直想系统的拜读一下 spring 的源码,看看它到底是如何吸引身边的大神们对它的设计赞不绝口,虽然每天工作很忙,每天下班后总感觉脑子内存溢出,想去放松一下,但总是以此为借口,恐怕会一直拖下去。所以每天下班虽然有些疲惫,但还是按住自己啃下这块硬骨头。
spring 源码这种东西真的是一回生二回熟,第一遍会被各种设计模式和繁杂的方法调用搞得晕头转向,不知道这个方法调用的是哪个父类的实现,另一个方法又调的是哪个子类的实现,但当你耐下心来多走几遍,会发现越看越熟练,每次都能 get 到新的点。
另外,对于第一次看 spring 源码的同学,建议先在 B 站上搜索相关视频看一下然后再结合计文柯老师的《spring 技术内幕》深入理解,最后再输出自己的理解加强印象。
首先对于我们新手来说,还是从我们最常用的两个 IoC 容器开始分析,这次我们先分析 FileSystemXmlApplicationContext 这个 IoC 容器的具体实现ClassPathXmlApplicationContext 留着下次讲解。
PS可以结合我 GitHub 上对 spring 框架源码的翻译注解一起看,会更有助于各位开发姥爷的理解。
地址:
spring-beans https://github.com/AmyliaY/spring-beans-reading
spring-context https://github.com/AmyliaY/spring-context-reading
## FileSystemXmlApplicationContext的构造方法
当我们传入一个spring配置文件去实例化FileSystemXmlApplicationContext()时,可以看一下它的构造方法都做了什么。
## FileSystemXmlApplicationContext 的构造方法
当我们传入一个 spring 配置文件去实例化 FileSystemXmlApplicationContext() 时,可以看一下它的构造方法都做了什么。
```java
/**
* 下面这4个构造方法都调用了第5个构造方法
* 下面这 4 个构造方法都调用了第 5 个构造方法
* @param configLocation
* @throws BeansException
*/
// configLocation包含了BeanDefinition所在的文件路径
// configLocation 包含了 BeanDefinition 所在的文件路径
public FileSystemXmlApplicationContext(String configLocation) throws BeansException {
this(new String[] {configLocation}, true, null);
}
// 可以定义多个BeanDefinition所在的文件路径
// 可以定义多个 BeanDefinition 所在的文件路径
public FileSystemXmlApplicationContext(String... configLocations) throws BeansException {
this(configLocations, true, null);
}
// 在定义多个BeanDefinition所在的文件路径 的同时还能指定自己的双亲IoC容器
// 在定义多个 BeanDefinition 所在的文件路径 的同时,还能指定自己的双亲 IoC 容器
public FileSystemXmlApplicationContext(String[] configLocations, ApplicationContext parent) throws BeansException {
this(configLocations, true, parent);
}
@ -37,7 +37,7 @@ spring-context https://github.com/AmyliaY/spring-context-reading
}
/**
* 如果应用直接使用FileSystemXmlApplicationContext进行实例化则都会进到这个构造方法中来
* 如果应用直接使用 FileSystemXmlApplicationContext 进行实例化,则都会进到这个构造方法中来
* @param configLocations
* @param refresh
* @param parent
@ -48,7 +48,7 @@ spring-context https://github.com/AmyliaY/spring-context-reading
//动态地确定用哪个加载器去加载我们的配置文件
super(parent);
//告诉读取器 配置文件放在哪里该方法继承于爷类AbstractRefreshableApplicationContext
//告诉读取器 配置文件放在哪里,该方法继承于爷类 AbstractRefreshableApplicationContext
setConfigLocations(configLocations);
if (refresh) {
//容器初始化
@ -58,8 +58,8 @@ spring-context https://github.com/AmyliaY/spring-context-reading
/**
* 实例化一个FileSystemResource并返回以便后续对资源的IO操作
* 本方法是在其父类DefaultResourceLoader的getResource方法中被调用的
* 实例化一个 FileSystemResource 并返回,以便后续对资源的 IO 操作
* 本方法是在其父类 DefaultResourceLoader getResource 方法中被调用的,
*/
@Override
protected Resource getResourceByPath(String path) {
@ -69,35 +69,35 @@ spring-context https://github.com/AmyliaY/spring-context-reading
return new FileSystemResource(path);
}
```
## 看看其父类AbstractApplicationContext实现的refresh()方法该方法就是IoC容器初始化的入口类
## 看看其父类 AbstractApplicationContext 实现的 refresh() 方法,该方法就是 IoC 容器初始化的入口类
```java
/**
* 容器初始化的过程BeanDefinition的Resource定位、BeanDefinition的载入、BeanDefinition的注册。
* BeanDefinition的载入和bean的依赖注入是两个独立的过程依赖注入一般发生在 应用第一次通过getBean()方法从容器获取bean时。
* 容器初始化的过程BeanDefinition Resource 定位、BeanDefinition 的载入、BeanDefinition 的注册。
* BeanDefinition 的载入和 bean 的依赖注入是两个独立的过程,依赖注入一般发生在 应用第一次通过 getBean() 方法从容器获取 bean 时。
*
* 另外需要注意的是IoC容器有一个预实例化的配置将AbstractBeanDefinition中的lazyInit属性设为true使用户可以对容器的初始化
* 过程做一个微小的调控lazyInit设为false的bean将在容器初始化时进行依赖注入而不会等到getBean()方法调用时才进行
* 另外需要注意的是IoC 容器有一个预实例化的配置(即,将 AbstractBeanDefinition 中的 lazyInit 属性设为 true使用户可以对容器的初始化
* 过程做一个微小的调控lazyInit 设为 false bean 将在容器初始化时进行依赖注入,而不会等到 getBean() 方法调用时才进行
*/
public void refresh() throws BeansException, IllegalStateException {
synchronized (this.startupShutdownMonitor) {
// 调用容器准备刷新的方法,获取容器的当前时间,同时给容器设置同步标识
prepareRefresh();
// 告诉子类启动refreshBeanFactory()方法Bean定义资源文件的载入从子类的refreshBeanFactory()方法启动开始
// 告诉子类启动 refreshBeanFactory() 方法Bean 定义资源文件的载入从子类的 refreshBeanFactory() 方法启动开始
ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
// 为BeanFactory配置容器特性例如类加载器、事件处理器等
// 为 BeanFactory 配置容器特性,例如类加载器、事件处理器等
prepareBeanFactory(beanFactory);
try {
// 为容器的某些子类指定特殊的BeanPost事件处理器
// 为容器的某些子类指定特殊的 BeanPost 事件处理器
postProcessBeanFactory(beanFactory);
// 调用所有注册的BeanFactoryPostProcessor的Bean
// 调用所有注册的 BeanFactoryPostProcessor Bean
invokeBeanFactoryPostProcessors(beanFactory);
// 为BeanFactory注册BeanPost事件处理器.
// BeanPostProcessor是Bean后置处理器用于监听容器触发的事件
// 为 BeanFactory 注册 BeanPost 事件处理器.
// BeanPostProcessor Bean 后置处理器,用于监听容器触发的事件
registerBeanPostProcessors(beanFactory);
// 初始化信息源,和国际化相关.
@ -106,13 +106,13 @@ spring-context https://github.com/AmyliaY/spring-context-reading
// 初始化容器事件传播器
initApplicationEventMulticaster();
// 调用子类的某些特殊Bean初始化方法
// 调用子类的某些特殊 Bean 初始化方法
onRefresh();
// 为事件传播器注册事件监听器.
registerListeners();
// 初始化Bean并对lazy-init属性进行处理
// 初始化 Bean并对 lazy-init 属性进行处理
finishBeanFactoryInitialization(beanFactory);
// 初始化容器的生命周期事件处理器,并发布容器的生命周期事件
@ -120,10 +120,10 @@ spring-context https://github.com/AmyliaY/spring-context-reading
}
catch (BeansException ex) {
// 销毁以创建的单态Bean
// 销毁以创建的单态 Bean
destroyBeans();
// 取消refresh操作重置容器的同步标识.
// 取消 refresh 操作,重置容器的同步标识.
cancelRefresh(ex);
throw ex;
@ -131,16 +131,16 @@ spring-context https://github.com/AmyliaY/spring-context-reading
}
}
```
## 看看obtainFreshBeanFactory方法该方法告诉了子类去刷新内部的beanFactory
## 看看 obtainFreshBeanFactory 方法,该方法告诉了子类去刷新内部的 beanFactory
```java
/**
* Tell the subclass to refresh the internal bean factory.
* 告诉子类去刷新内部的beanFactory
* 告诉子类去刷新内部的 beanFactory
*/
protected ConfigurableListableBeanFactory obtainFreshBeanFactory() {
// 自己定义了抽象的refreshBeanFactory()方法,具体实现交给了自己的子类
// 自己定义了抽象的 refreshBeanFactory() 方法,具体实现交给了自己的子类
refreshBeanFactory();
// getBeanFactory()也是一个抽象方法,委派给子类实现
// getBeanFactory() 也是一个抽象方法,委派给子类实现
ConfigurableListableBeanFactory beanFactory = getBeanFactory();
if (logger.isDebugEnabled()) {
logger.debug("Bean factory for " + getDisplayName() + ": " + beanFactory);
@ -148,25 +148,25 @@ spring-context https://github.com/AmyliaY/spring-context-reading
return beanFactory;
}
```
## AbstractRefreshableApplicationContext中对refreshBeanFactory()方法的实现
FileSystemXmlApplicationContext从上层体系的各抽象类中继承了大量的方法实现抽象类中抽取大量公共行为进行具体实现留下abstract的个性化方法交给具体的子类实现这是一个很好的OOP编程设计我们在自己编码时也可以尝试这样设计自己的类图。理清FileSystemXmlApplicationContext的上层体系设计就不易被各种设计模式搞晕咯。
## AbstractRefreshableApplicationContext 中对 refreshBeanFactory() 方法的实现
FileSystemXmlApplicationContext 从上层体系的各抽象类中继承了大量的方法实现,抽象类中抽取大量公共行为进行具体实现,留下 abstract 的个性化方法交给具体的子类实现,这是一个很好的 OOP 编程设计,我们在自己编码时也可以尝试这样设计自己的类图。理清 FileSystemXmlApplicationContext 的上层体系设计,就不易被各种设计模式搞晕咯。
```java
// 在这里完成了容器的初始化并赋值给自己private的beanFactory属性为下一步调用做准备
// 从父类AbstractApplicationContext继承的抽象方法自己做了实现
// 在这里完成了容器的初始化,并赋值给自己 private beanFactory 属性,为下一步调用做准备
// 从父类 AbstractApplicationContext 继承的抽象方法,自己做了实现
@Override
protected final void refreshBeanFactory() throws BeansException {
// 如果已经建立了IoC容器则销毁并关闭容器
// 如果已经建立了 IoC 容器,则销毁并关闭容器
if (hasBeanFactory()) {
destroyBeans();
closeBeanFactory();
}
try {
// 创建IoC容器DefaultListableBeanFactory类实现了ConfigurableListableBeanFactory接口
// 创建 IoC 容器DefaultListableBeanFactory 类实现了 ConfigurableListableBeanFactory 接口
DefaultListableBeanFactory beanFactory = createBeanFactory();
beanFactory.setSerializationId(getId());
// 对IoC容器进行定制化如设置启动参数开启注解的自动装配等
// 对 IoC 容器进行定制化,如设置启动参数,开启注解的自动装配等
customizeBeanFactory(beanFactory);
// 载入BeanDefinition在当前类中只定义了抽象的loadBeanDefinitions方法具体实现 调用子类容器
// 载入 BeanDefinition在当前类中只定义了抽象的 loadBeanDefinitions 方法,具体实现 调用子类容器
loadBeanDefinitions(beanFactory);
synchronized (this.beanFactoryMonitor) {
// 给自己的属性赋值
@ -178,63 +178,63 @@ FileSystemXmlApplicationContext从上层体系的各抽象类中继承了大量
}
}
```
## AbstractXmlApplicationContext中对loadBeanDefinitions(DefaultListableBeanFactory beanFactory)的实现
## AbstractXmlApplicationContext 中对 loadBeanDefinitions(DefaultListableBeanFactory beanFactory) 的实现
```java
/*
* 实现了爷类AbstractRefreshableApplicationContext的抽象方法
* 实现了爷类 AbstractRefreshableApplicationContext 的抽象方法
*/
@Override
protected void loadBeanDefinitions(DefaultListableBeanFactory beanFactory) throws BeansException, IOException {
// DefaultListableBeanFactory实现了BeanDefinitionRegistry接口在初始化XmlBeanDefinitionReader时
// 将BeanDefinition注册器注入该BeanDefinition读取器
// 创建 用于从Xml中读取BeanDefinition的读取器并通过回调设置到IoC容器中去容器使用该读取器读取BeanDefinition资源
// DefaultListableBeanFactory 实现了 BeanDefinitionRegistry 接口,在初始化 XmlBeanDefinitionReader
// 将 BeanDefinition 注册器注入该 BeanDefinition 读取器
// 创建 用于从 Xml 中读取 BeanDefinition 的读取器,并通过回调设置到 IoC 容器中去,容器使用该读取器读取 BeanDefinition 资源
XmlBeanDefinitionReader beanDefinitionReader = new XmlBeanDefinitionReader(beanFactory);
beanDefinitionReader.setEnvironment(this.getEnvironment());
// 为beanDefinition读取器设置 资源加载器由于本类的基类AbstractApplicationContext
// 继承了DefaultResourceLoader因此本容器自身也是一个资源加载器
// 为 beanDefinition 读取器设置 资源加载器,由于本类的基类 AbstractApplicationContext
// 继承了 DefaultResourceLoader因此本容器自身也是一个资源加载器
beanDefinitionReader.setResourceLoader(this);
// 设置SAX解析器SAXsimple API for XML是另一种XML解析方法。相比于DOMSAX速度更快占用内存更小。
// 它逐行扫描文档一边扫描一边解析。相比于先将整个XML文件扫描近内存再进行解析的DOMSAX可以在解析文档的任意时刻停止解析但操作也比DOM复杂。
// 设置 SAX 解析器SAXsimple API for XML是另一种 XML 解析方法。相比于 DOMSAX 速度更快,占用内存更小。
// 它逐行扫描文档,一边扫描一边解析。相比于先将整个 XML 文件扫描近内存,再进行解析的 DOMSAX 可以在解析文档的任意时刻停止解析,但操作也比 DOM 复杂。
beanDefinitionReader.setEntityResolver(new ResourceEntityResolver(this));
// 初始化beanDefinition读取器该方法同时启用了Xml的校验机制
// 初始化 beanDefinition 读取器,该方法同时启用了 Xml 的校验机制
initBeanDefinitionReader(beanDefinitionReader);
// Bean读取器真正实现加载的方法
// Bean 读取器真正实现加载的方法
loadBeanDefinitions(beanDefinitionReader);
}
```
## 继续看AbstractXmlApplicationContext中loadBeanDefinitions的重载方法
## 继续看 AbstractXmlApplicationContext loadBeanDefinitions 的重载方法
```java
// 用传进来的XmlBeanDefinitionReader读取器加载Xml文件中的BeanDefinition
// 用传进来的 XmlBeanDefinitionReader 读取器加载 Xml 文件中的 BeanDefinition
protected void loadBeanDefinitions(XmlBeanDefinitionReader reader) throws BeansException, IOException {
/**
* ClassPathXmlApplicationContext与FileSystemXmlApplicationContext
* 在这里的调用出现分歧各自按不同的方式加载解析Resource资源
* 最后在具体的解析和BeanDefinition定位上又会殊途同归
* ClassPathXmlApplicationContext FileSystemXmlApplicationContext
* 在这里的调用出现分歧,各自按不同的方式加载解析 Resource 资源
* 最后在具体的解析和 BeanDefinition 定位上又会殊途同归
*/
// 获取存放了BeanDefinition的所有Resource
// FileSystemXmlApplicationContext类未对getConfigResources()进行重新,
// 获取存放了 BeanDefinition 的所有 Resource
// FileSystemXmlApplicationContext 类未对 getConfigResources() 进行重新,
// 所以调用父类的return null。
// 而ClassPathXmlApplicationContext对该方法进行了重写返回设置的值
// 而 ClassPathXmlApplicationContext 对该方法进行了重写,返回设置的值
Resource[] configResources = getConfigResources();
if (configResources != null) {
// Xml Bean读取器调用其父类AbstractBeanDefinitionReader读取定位的Bean定义资源
// Xml Bean 读取器调用其父类 AbstractBeanDefinitionReader 读取定位的 Bean 定义资源
reader.loadBeanDefinitions(configResources);
}
// 调用父类AbstractRefreshableConfigApplicationContext实现的返回值为String[]的getConfigLocations()方法,
// 优先返回FileSystemXmlApplicationContext构造方法中调用setConfigLocations()方法设置的资源
// 调用父类 AbstractRefreshableConfigApplicationContext 实现的返回值为 String[] getConfigLocations() 方法,
// 优先返回 FileSystemXmlApplicationContext 构造方法中调用 setConfigLocations() 方法设置的资源
String[] configLocations = getConfigLocations();
if (configLocations != null) {
// XmlBeanDefinitionReader读取器调用其父类AbstractBeanDefinitionReader的方法从配置位置加载BeanDefinition
// XmlBeanDefinitionReader 读取器调用其父类 AbstractBeanDefinitionReader 的方法从配置位置加载 BeanDefinition
reader.loadBeanDefinitions(configLocations);
}
}
```
## AbstractBeanDefinitionReader中对loadBeanDefinitions方法的各种重载及调用
## AbstractBeanDefinitionReader 中对 loadBeanDefinitions 方法的各种重载及调用
```java
// loadBeanDefinitions()方法的重载方法之一调用了另一个重载方法loadBeanDefinitions(String)
// loadBeanDefinitions() 方法的重载方法之一,调用了另一个重载方法 loadBeanDefinitions(String)
public int loadBeanDefinitions(String... locations) throws BeanDefinitionStoreException {
Assert.notNull(locations, "Location array must not be null");
// 计数 加载了多少个配置文件
@ -245,14 +245,14 @@ FileSystemXmlApplicationContext从上层体系的各抽象类中继承了大量
return counter;
}
// 重载方法之一调用了下面的loadBeanDefinitions(String, Set<Resource>)方法
// 重载方法之一,调用了下面的 loadBeanDefinitions(String, Set<Resource>) 方法
public int loadBeanDefinitions(String location) throws BeanDefinitionStoreException {
return loadBeanDefinitions(location, null);
}
// 获取在IoC容器初始化过程中设置的资源加载器
// 获取在 IoC 容器初始化过程中设置的资源加载器
public int loadBeanDefinitions(String location, Set<Resource> actualResources) throws BeanDefinitionStoreException {
// 在实例化XmlBeanDefinitionReader后IoC容器将自己注入进该读取器作为resourceLoader属性
// 在实例化 XmlBeanDefinitionReader IoC 容器将自己注入进该读取器作为 resourceLoader 属性
ResourceLoader resourceLoader = getResourceLoader();
if (resourceLoader == null) {
throw new BeanDefinitionStoreException(
@ -261,10 +261,10 @@ FileSystemXmlApplicationContext从上层体系的各抽象类中继承了大量
if (resourceLoader instanceof ResourcePatternResolver) {
try {
// 将指定位置的BeanDefinition资源文件解析为IoC容器封装的资源
// 加载多个指定位置的BeanDefinition资源文件
// 将指定位置的 BeanDefinition 资源文件解析为 IoC 容器封装的资源
// 加载多个指定位置的 BeanDefinition 资源文件
Resource[] resources = ((ResourcePatternResolver) resourceLoader).getResources(location);
// 委派调用其子类XmlBeanDefinitionReader的方法实现加载功能
// 委派调用其子类 XmlBeanDefinitionReader 的方法,实现加载功能
int loadCount = loadBeanDefinitions(resources);
if (actualResources != null) {
for (Resource resource : resources) {
@ -284,16 +284,16 @@ FileSystemXmlApplicationContext从上层体系的各抽象类中继承了大量
else {
/**
*
* AbstractApplicationContext继承了DefaultResourceLoader所以AbstractApplicationContext
* 及其子类都会调用DefaultResourceLoader中的实现将指定位置的资源文件解析为Resource
* 至此完成了对BeanDefinition的资源定位
* AbstractApplicationContext 继承了 DefaultResourceLoader所以 AbstractApplicationContext
* 及其子类都会调用 DefaultResourceLoader 中的实现,将指定位置的资源文件解析为 Resource
* 至此完成了对 BeanDefinition 的资源定位
*
*/
Resource resource = resourceLoader.getResource(location);
// 从resource中加载BeanDefinitionloadCount为加载的BeanDefinition个数
// 该loadBeanDefinitions()方法来自其implements的BeanDefinitionReader接口
// 且本类是一个抽象类并未对该方法进行实现。而是交由子类进行实现如果是用xml文件进行
// IoC容器初始化的则调用XmlBeanDefinitionReader中的实现
// 从 resource 中加载 BeanDefinitionloadCount 为加载的 BeanDefinition 个数
// 该 loadBeanDefinitions() 方法来自其 implements BeanDefinitionReader 接口,
// 且本类是一个抽象类,并未对该方法进行实现。而是交由子类进行实现,如果是用 xml 文件进行
// IoC 容器初始化的,则调用 XmlBeanDefinitionReader 中的实现
int loadCount = loadBeanDefinitions(resource);
if (actualResources != null) {
actualResources.add(resource);
@ -305,37 +305,37 @@ FileSystemXmlApplicationContext从上层体系的各抽象类中继承了大量
}
}
```
## resourceLoader的getResource()方法有多种实现看清FileSystemXmlApplicationContext的继承体系就可以明确其走的是DefaultResourceLoader中的实现
## resourceLoader getResource() 方法有多种实现,看清 FileSystemXmlApplicationContext 的继承体系就可以明确,其走的是 DefaultResourceLoader 中的实现
```java
// 获取Resource的具体实现方法
// 获取 Resource 的具体实现方法
public Resource getResource(String location) {
Assert.notNull(location, "Location must not be null");
// 如果location是类路径的方式返回ClassPathResource类型的文件资源对象
// 如果 location 是类路径的方式,返回 ClassPathResource 类型的文件资源对象
if (location.startsWith(CLASSPATH_URL_PREFIX)) {
return new ClassPathResource(location.substring(CLASSPATH_URL_PREFIX.length()), getClassLoader());
}
else {
try {
// 如果是URL方式返回UrlResource类型的文件资源对象
// 否则将抛出的异常进入catch代码块返回另一种资源对象
// 如果是 URL 方式,返回 UrlResource 类型的文件资源对象,
// 否则将抛出的异常进入 catch 代码块,返回另一种资源对象
URL url = new URL(location);
return new UrlResource(url);
}
catch (MalformedURLException ex) {
// 如果既不是classpath标识又不是URL标识的Resource定位则调用
// 容器本身的getResourceByPath方法获取Resource
// 如果既不是 classpath 标识,又不是 URL 标识的 Resource 定位,则调用
// 容器本身的 getResourceByPath 方法获取 Resource
// 根据实例化的子类对象,调用其子类对象中重写的此方法,
// 如FileSystemXmlApplicationContext子类中对此方法的重新
// 如 FileSystemXmlApplicationContext 子类中对此方法的重新
return getResourceByPath(location);
}
}
}
```
## 其中的getResourceByPath(location)方法的实现则是在FileSystemXmlApplicationContext中完成的
## 其中的 getResourceByPath(location) 方法的实现则是在 FileSystemXmlApplicationContext 中完成的
```java
/**
* 实例化一个FileSystemResource并返回以便后续对资源的IO操作
* 本方法是在DefaultResourceLoader的getResource方法中被调用的
* 实例化一个 FileSystemResource 并返回,以便后续对资源的 IO 操作
* 本方法是在 DefaultResourceLoader getResource 方法中被调用的,
*/
@Override
protected Resource getResourceByPath(String path) {
@ -345,7 +345,4 @@ FileSystemXmlApplicationContext从上层体系的各抽象类中继承了大量
return new FileSystemResource(path);
}
```
至此我们可以看到FileSystemXmlApplicationContext的getResourceByPath()方法返回了一个FileSystemResource对象接下来spring就可以对这个对象进行相关的I/O操作进行BeanDefinition的读取和载入了。
至此我们可以看到FileSystemXmlApplicationContext 的 getResourceByPath() 方法返回了一个 FileSystemResource 对象,接下来 spring 就可以对这个对象进行相关的 I/O 操作,进行 BeanDefinition 的读取和载入了。

@ -1,26 +1,26 @@
这篇文章分享一下spring IoC容器初始化第三部分的代码也就是将前面解析得到的BeanDefinition注册进IoC容器其实就是存入一个ConcurrentHashMap<String, BeanDefinition>中。
这篇文章分享一下 spring IoC 容器初始化第三部分的代码,也就是将前面解析得到的 BeanDefinition 注册进 IoC 容器,其实就是存入一个 ConcurrentHashMap<String, BeanDefinition> 中。
PS可以结合我GitHub上对spring框架源码的翻译注解一起看会更有助于各位同学理解地址
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)方法
## 1、回过头看一下前面在 DefaultBeanDefinitionDocumentReader 中实现的 processBeanDefinition(Element ele, BeanDefinitionParserDelegate delegate) 方法
```java
// 解析Bean定义资源Document对象的普通元素
// 解析 Bean 定义资源 Document 对象的普通元素
protected void processBeanDefinition(Element ele, BeanDefinitionParserDelegate delegate) {
// BeanDefinitionHolder是对BeanDefinition的封装即BeanDefinition的封装类
// 对Document对象中<Bean>元素的解析由BeanDefinitionParserDelegate实现
// BeanDefinitionHolder 是对 BeanDefinition 的封装,即 BeanDefinition 的封装类
// 对 Document 对象中 <Bean> 元素的解析由 BeanDefinitionParserDelegate 实现
BeanDefinitionHolder bdHolder = delegate.parseBeanDefinitionElement(ele);
if (bdHolder != null) {
// 对bdHolder进行包装处理
// 对 bdHolder 进行包装处理
bdHolder = delegate.decorateBeanDefinitionIfRequired(ele, bdHolder);
try {
/**
*
* 向Spring IoC容器注册解析BeanDefinition这是BeanDefinition向IoC容器注册的入口
* 向 Spring IoC 容器注册解析 BeanDefinition这是 BeanDefinition IoC 容器注册的入口
*
*/
BeanDefinitionReaderUtils.registerBeanDefinition(bdHolder, getReaderContext().getRegistry());
@ -29,28 +29,28 @@ 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定义之后发送注册事件
// 在完成向 Spring IOC 容器注册解析得到的 Bean 定义之后,发送注册事件
getReaderContext().fireComponentRegistered(new BeanComponentDefinition(bdHolder));
}
}
```
## 2、BeanDefinitionReaderUtils的registerBeanDefinition(BeanDefinitionHolder definitionHolder, BeanDefinitionRegistry registry)方法
## 2、BeanDefinitionReaderUtils registerBeanDefinition(BeanDefinitionHolder definitionHolder, BeanDefinitionRegistry registry) 方法
```java
// 将解析的BeanDefinitionHold注册到容器中
// 将解析的 BeanDefinitionHold 注册到容器中
public static void registerBeanDefinition(
BeanDefinitionHolder definitionHolder, BeanDefinitionRegistry registry)
throws BeanDefinitionStoreException {
// 获取解析的BeanDefinition的名称
// 获取解析的 BeanDefinition 的名称
String beanName = definitionHolder.getBeanName();
/**
*
* 开始向IOC容器注册BeanDefinition
* 开始向 IOC 容器注册 BeanDefinition
*
*/
registry.registerBeanDefinition(beanName, definitionHolder.getBeanDefinition());
// 如果解析的BeanDefinition有别名向容器为其注册别名
// 如果解析的 BeanDefinition 有别名,向容器为其注册别名
String[] aliases = definitionHolder.getAliases();
if (aliases != null) {
for (String aliase : aliases) {
@ -59,16 +59,16 @@ spring-context https://github.com/AmyliaY/spring-context-reading
}
}
```
## 3、BeanDefinitionRegistry中的registerBeanDefinition(String beanName, BeanDefinition beanDefinition)方法在DefaultListableBeanFactory实现类中的具体实现
## 3、BeanDefinitionRegistry 中的 registerBeanDefinition(String beanName, BeanDefinition beanDefinition) 方法在 DefaultListableBeanFactory 实现类中的具体实现
```java
// 向IoC容器注册解析的BeanDefinito
// 向 IoC 容器注册解析的 BeanDefinito
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,36 +83,34 @@ spring-context https://github.com/AmyliaY/spring-context-reading
synchronized (this.beanDefinitionMap) {
Object oldBeanDefinition = this.beanDefinitionMap.get(beanName);
// 检查是否有同名的BeanDefinition已经在IOC容器中注册如果已经注册
// 并且不允许覆盖已注册的BeanDefinition则抛出注册失败异常
// allowBeanDefinitionOverriding默认为true
// 检查是否有同名的 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后注册的覆盖先注册的
else {// 如果允许覆盖,则同名的 Bean后注册的覆盖先注册的
if (this.logger.isInfoEnabled()) {
this.logger.info("Overriding bean definition for bean '" + beanName +
"': replacing [" + oldBeanDefinition + "] with [" + beanDefinition + "]");
}
}
}
else {// IOC容器中没有已经注册同名的Bean按正常注册流程注册
else {// IOC 容器中没有已经注册同名的 Bean按正常注册流程注册
this.beanDefinitionNames.add(beanName);
this.frozenBeanDefinitionNames = null;
}
this.beanDefinitionMap.put(beanName, beanDefinition);
}
// 重置所有已经注册过的BeanDefinition的缓存
// 重置所有已经注册过的 BeanDefinition 的缓存
resetBeanDefinition(beanName);
}
```
## 最后看一下spring的IoC容器在代码中最直接的体现
## 最后看一下 spring IoC 容器在代码中最直接的体现
```java
// 存储注册信息的BeanDefinition集合也就是所谓的IoC容器
// 存储注册信息的 BeanDefinition 集合,也就是所谓的 IoC 容器
private final Map<String, BeanDefinition> beanDefinitionMap = new ConcurrentHashMap<String, BeanDefinition>(64);
```
```

@ -1,27 +1,27 @@
接着上一篇的BeanDefinition资源定位开始讲。Spring IoC容器BeanDefinition解析过程就是把用户在配置文件中定义好的bean解析并封装成容器可以装载的BeanDefinitionBeanDefinition是spring定义的基本数据结构也是为了方便对bean进行管理和操作。
PS可以结合我GitHub上对spring框架源码的阅读及个人理解一起看会更有助于各位开发大佬理解。
接着上一篇的 BeanDefinition 资源定位开始讲。Spring IoC 容器 BeanDefinition 解析过程就是把用户在配置文件中定义好的 bean解析并封装成容器可以装载的 BeanDefinitionBeanDefinition spring 定义的基本数据结构,也是为了方便对 bean 进行管理和操作。
PS可以结合我 GitHub 上对 spring 框架源码的阅读及个人理解一起看,会更有助于各位开发大佬理解。
spring-beans https://github.com/AmyliaY/spring-beans-reading
spring-context https://github.com/AmyliaY/spring-context-reading
## 1、先看一下AbstractRefreshableApplicationContext中refreshBeanFactory()方法的loadBeanDefinitions(beanFactory)
## 1、先看一下 AbstractRefreshableApplicationContext refreshBeanFactory() 方法的 loadBeanDefinitions(beanFactory)
```java
// 在这里完成了容器的初始化并赋值给自己private的beanFactory属性为下一步调用做准备
// 从父类AbstractApplicationContext继承的抽象方法自己做了实现
// 在这里完成了容器的初始化,并赋值给自己 private beanFactory 属性,为下一步调用做准备
// 从父类 AbstractApplicationContext 继承的抽象方法,自己做了实现
@Override
protected final void refreshBeanFactory() throws BeansException {
// 如果已经建立了IoC容器则销毁并关闭容器
// 如果已经建立了 IoC 容器,则销毁并关闭容器
if (hasBeanFactory()) {
destroyBeans();
closeBeanFactory();
}
try {
// 创建IoC容器DefaultListableBeanFactory类实现了ConfigurableListableBeanFactory接口
// 创建 IoC 容器DefaultListableBeanFactory 类实现了 ConfigurableListableBeanFactory 接口
DefaultListableBeanFactory beanFactory = createBeanFactory();
beanFactory.setSerializationId(getId());
// 对IoC容器进行定制化如设置启动参数开启注解的自动装配等
// 对 IoC 容器进行定制化,如设置启动参数,开启注解的自动装配等
customizeBeanFactory(beanFactory);
// 载入BeanDefinition当前类中只定义了抽象的loadBeanDefinitions方法具体的实现调用子类容器
// 载入 BeanDefinition当前类中只定义了抽象的 loadBeanDefinitions 方法,具体的实现调用子类容器
loadBeanDefinitions(beanFactory);
synchronized (this.beanFactoryMonitor) {
// 给自己的属性赋值
@ -33,62 +33,62 @@ spring-context https://github.com/AmyliaY/spring-context-reading
}
}
```
## 2、实现类AbstractXmlApplicationContext中的loadBeanDefinitions(DefaultListableBeanFactory beanFactory)
## 2、实现类 AbstractXmlApplicationContext 中的 loadBeanDefinitions(DefaultListableBeanFactory beanFactory)
```java
/*
* 实现了爷类AbstractRefreshableApplicationContext的抽象方法
* 实现了爷类 AbstractRefreshableApplicationContext 的抽象方法
*/
@Override
protected void loadBeanDefinitions(DefaultListableBeanFactory beanFactory) throws BeansException, IOException {
// DefaultListableBeanFactory实现了BeanDefinitionRegistry接口在初始化XmlBeanDefinitionReader时
// 将BeanDefinition注册器注入该BeanDefinition读取器
// 创建 用于从Xml中读取BeanDefinition的读取器并通过回调设置到IoC容器中去容器使用该读取器读取BeanDefinition资源
// DefaultListableBeanFactory 实现了 BeanDefinitionRegistry 接口,在初始化 XmlBeanDefinitionReader
// 将 BeanDefinition 注册器注入该 BeanDefinition 读取器
// 创建 用于从 Xml 中读取 BeanDefinition 的读取器,并通过回调设置到 IoC 容器中去,容器使用该读取器读取 BeanDefinition 资源
XmlBeanDefinitionReader beanDefinitionReader = new XmlBeanDefinitionReader(beanFactory);
beanDefinitionReader.setEnvironment(this.getEnvironment());
// 为beanDefinition读取器设置 资源加载器由于本类的太爷爷类AbstractApplicationContext
// 继承了DefaultResourceLoader因此本容器自身也是一个资源加载器
// 为 beanDefinition 读取器设置 资源加载器,由于本类的太爷爷类 AbstractApplicationContext
// 继承了 DefaultResourceLoader因此本容器自身也是一个资源加载器
beanDefinitionReader.setResourceLoader(this);
// 设置SAX解析器SAXsimple API for XML是另一种XML解析方法。相比于DOMSAX速度更快占用内存更小。
// 它逐行扫描文档一边扫描一边解析。相比于先将整个XML文件扫描近内存再进行解析的DOMSAX可以在解析文档的任意时刻停止解析但操作也比DOM复杂。
// 设置 SAX 解析器SAXsimple API for XML是另一种 XML 解析方法。相比于 DOMSAX 速度更快,占用内存更小。
// 它逐行扫描文档,一边扫描一边解析。相比于先将整个 XML 文件扫描近内存,再进行解析的 DOMSAX 可以在解析文档的任意时刻停止解析,但操作也比 DOM 复杂。
beanDefinitionReader.setEntityResolver(new ResourceEntityResolver(this));
// 初始化beanDefinition读取器该方法同时启用了Xml的校验机制
// 初始化 beanDefinition 读取器,该方法同时启用了 Xml 的校验机制
initBeanDefinitionReader(beanDefinitionReader);
// Bean读取器真正实现加载的方法
// Bean 读取器真正实现加载的方法
loadBeanDefinitions(beanDefinitionReader);
}
```
## 3、loadBeanDefinitions(XmlBeanDefinitionReader reader)
```java
// 用传进来的XmlBeanDefinitionReader读取器加载Xml文件中的BeanDefinition
// 用传进来的 XmlBeanDefinitionReader 读取器加载 Xml 文件中的 BeanDefinition
protected void loadBeanDefinitions(XmlBeanDefinitionReader reader) throws BeansException, IOException {
/**
* ClassPathXmlApplicationContext与FileSystemXmlApplicationContext
* 在这里的调用出现分歧各自按不同的方式加载解析Resource资源
* 最后在具体的解析和BeanDefinition定位上又会殊途同归
* ClassPathXmlApplicationContext FileSystemXmlApplicationContext
* 在这里的调用出现分歧,各自按不同的方式加载解析 Resource 资源
* 最后在具体的解析和 BeanDefinition 定位上又会殊途同归
*/
// 获取存放了BeanDefinition的所有Resource
// FileSystemXmlApplicationContext类未对getConfigResources()进行重新,
// 获取存放了 BeanDefinition 的所有 Resource
// FileSystemXmlApplicationContext 类未对 getConfigResources() 进行重新,
// 所以调用父类的return null。
// 而ClassPathXmlApplicationContext对该方法进行了重写返回设置的值
// 而 ClassPathXmlApplicationContext 对该方法进行了重写,返回设置的值
Resource[] configResources = getConfigResources();
if (configResources != null) {
// Xml Bean读取器调用其父类AbstractBeanDefinitionReader读取定位的Bean定义资源
// Xml Bean 读取器调用其父类 AbstractBeanDefinitionReader 读取定位的 Bean 定义资源
reader.loadBeanDefinitions(configResources);
}
// 调用父类AbstractRefreshableConfigApplicationContext实现的返回值为String[]的getConfigLocations()方法,
// 优先返回FileSystemXmlApplicationContext构造方法中调用setConfigLocations()方法设置的资源
// 调用父类 AbstractRefreshableConfigApplicationContext 实现的返回值为 String[] getConfigLocations() 方法,
// 优先返回 FileSystemXmlApplicationContext 构造方法中调用 setConfigLocations() 方法设置的资源
String[] configLocations = getConfigLocations();
if (configLocations != null) {
// XmlBeanDefinitionReader读取器调用其父类AbstractBeanDefinitionReader的方法从配置位置加载BeanDefinition
// XmlBeanDefinitionReader 读取器调用其父类 AbstractBeanDefinitionReader 的方法从配置位置加载 BeanDefinition
reader.loadBeanDefinitions(configLocations);
}
}
```
## 4、AbstractBeanDefinitionReader对loadBeanDefinitions()方法的三重重载
## 4、AbstractBeanDefinitionReader loadBeanDefinitions() 方法的三重重载
```java
// loadBeanDefinitions()方法的重载方法之一调用了另一个重载方法loadBeanDefinitions(String)
// loadBeanDefinitions() 方法的重载方法之一,调用了另一个重载方法 loadBeanDefinitions(String)
public int loadBeanDefinitions(String... locations) throws BeanDefinitionStoreException {
Assert.notNull(locations, "Location array must not be null");
// 计数 加载了多少个配置文件
@ -99,14 +99,14 @@ spring-context https://github.com/AmyliaY/spring-context-reading
return counter;
}
// 重载方法之一调用了下面的loadBeanDefinitions(String, Set<Resource>)方法
// 重载方法之一,调用了下面的 loadBeanDefinitions(String, Set<Resource>) 方法
public int loadBeanDefinitions(String location) throws BeanDefinitionStoreException {
return loadBeanDefinitions(location, null);
}
// 获取在IoC容器初始化过程中设置的资源加载器
// 获取在 IoC 容器初始化过程中设置的资源加载器
public int loadBeanDefinitions(String location, Set<Resource> actualResources) throws BeanDefinitionStoreException {
// 在实例化XmlBeanDefinitionReader后IoC容器将自己注入进该读取器作为resourceLoader属性
// 在实例化 XmlBeanDefinitionReader IoC 容器将自己注入进该读取器作为 resourceLoader 属性
ResourceLoader resourceLoader = getResourceLoader();
if (resourceLoader == null) {
throw new BeanDefinitionStoreException(
@ -115,10 +115,10 @@ spring-context https://github.com/AmyliaY/spring-context-reading
if (resourceLoader instanceof ResourcePatternResolver) {
try {
// 将指定位置的BeanDefinition资源文件解析为IoC容器封装的资源
// 加载多个指定位置的BeanDefinition资源文件
// 将指定位置的 BeanDefinition 资源文件解析为 IoC 容器封装的资源
// 加载多个指定位置的 BeanDefinition 资源文件
Resource[] resources = ((ResourcePatternResolver) resourceLoader).getResources(location);
// 委派调用其子类XmlBeanDefinitionReader的方法实现加载功能
// 委派调用其子类 XmlBeanDefinitionReader 的方法,实现加载功能
int loadCount = loadBeanDefinitions(resources);
if (actualResources != null) {
for (Resource resource : resources) {
@ -138,16 +138,16 @@ spring-context https://github.com/AmyliaY/spring-context-reading
else {
/**
*
* AbstractApplicationContext继承了DefaultResourceLoader所以AbstractApplicationContext
* 及其子类都会调用DefaultResourceLoader中的实现将指定位置的资源文件解析为Resource
* 至此完成了对BeanDefinition的资源定位
* AbstractApplicationContext 继承了 DefaultResourceLoader所以 AbstractApplicationContext
* 及其子类都会调用 DefaultResourceLoader 中的实现,将指定位置的资源文件解析为 Resource
* 至此完成了对 BeanDefinition 的资源定位
*
*/
Resource resource = resourceLoader.getResource(location);
// 从resource中加载BeanDefinitionloadCount为加载的BeanDefinition个数
// 该loadBeanDefinitions()方法来自其implements的BeanDefinitionReader接口
// 且本类是一个抽象类并未对该方法进行实现。而是交由子类进行实现如果是用xml文件进行
// IoC容器初始化的则调用XmlBeanDefinitionReader中的实现
// 从 resource 中加载 BeanDefinitionloadCount 为加载的 BeanDefinition 个数
// 该 loadBeanDefinitions() 方法来自其 implements BeanDefinitionReader 接口,
// 且本类是一个抽象类,并未对该方法进行实现。而是交由子类进行实现,如果是用 xml 文件进行
// IoC 容器初始化的,则调用 XmlBeanDefinitionReader 中的实现
int loadCount = loadBeanDefinitions(resource);
if (actualResources != null) {
actualResources.add(resource);
@ -159,16 +159,16 @@ spring-context https://github.com/AmyliaY/spring-context-reading
}
}
```
## 5、XmlBeanDefinitionReader读取器中的方法执行流按代码的先后顺序
## 5、XmlBeanDefinitionReader 读取器中的方法执行流,按代码的先后顺序
```java
// XmlBeanDefinitionReader加载资源的入口方法
// XmlBeanDefinitionReader 加载资源的入口方法
public int loadBeanDefinitions(Resource resource) throws BeanDefinitionStoreException {
// 调用本类的重载方法通过new EncodedResource(resource)获得的
// EncodedResource对象能够将资源与读取资源所需的编码组合在一起
// 调用本类的重载方法,通过 new EncodedResource(resource) 获得的
// EncodedResource 对象能够将资源与读取资源所需的编码组合在一起
return loadBeanDefinitions(new EncodedResource(resource));
}
// 通过encodedResource进行资源解析encodedResource对象持有resource对象和encoding编码格式
// 通过 encodedResource 进行资源解析encodedResource 对象持有 resource 对象和 encoding 编码格式
public int loadBeanDefinitions(EncodedResource encodedResource) throws BeanDefinitionStoreException {
Assert.notNull(encodedResource, "EncodedResource must not be null");
if (logger.isInfoEnabled()) {
@ -185,12 +185,12 @@ spring-context https://github.com/AmyliaY/spring-context-reading
"Detected cyclic loading of " + encodedResource + " - check your import definitions!");
}
try {
// 从resource中获取输入流对resource中的内容进行读取
// 从 resource 中获取输入流,对 resource 中的内容进行读取
InputStream inputStream = encodedResource.getResource().getInputStream();
try {
// 实例化一个"XML实体的单个输入源"将inputStream作为自己的属性
// 实例化一个"XML 实体的单个输入源",将 inputStream 作为自己的属性
InputSource inputSource = new InputSource(inputStream);
// 如果encodedResource中的encoding属性不为空就为inputSource设置读取XML的编码格式
// 如果 encodedResource 中的 encoding 属性不为空,就为 inputSource 设置读取 XML 的编码格式
if (encodedResource.getEncoding() != null) {
inputSource.setEncoding(encodedResource.getEncoding());
}
@ -198,7 +198,7 @@ spring-context https://github.com/AmyliaY/spring-context-reading
return doLoadBeanDefinitions(inputSource, encodedResource.getResource());
}
finally {
// 关闭从Resource中得到的IO流
// 关闭从 Resource 中得到的 IO
inputStream.close();
}
}
@ -214,15 +214,15 @@ spring-context https://github.com/AmyliaY/spring-context-reading
}
}
// 从指定XML文件中实际载入BeanDefinition资源的方法
// 从指定 XML 文件中实际载入 BeanDefinition 资源的方法
protected int doLoadBeanDefinitions(InputSource inputSource, Resource resource)
throws BeanDefinitionStoreException {
try {
int validationMode = getValidationModeForResource(resource);
// 通过documentLoader获取XML文件的Document对象
// 通过 documentLoader 获取 XML 文件的 Document 对象
Document doc = this.documentLoader.loadDocument(
inputSource, getEntityResolver(), this.errorHandler, validationMode, isNamespaceAware());
// 启动对BeanDefinition的详细解析过程该解析过程会用到Spring的Bean配置规则
// 启动对 BeanDefinition 的详细解析过程,该解析过程会用到 Spring Bean 配置规则
return registerBeanDefinitions(doc, resource);
}
catch (BeanDefinitionStoreException ex) {
@ -250,28 +250,28 @@ spring-context https://github.com/AmyliaY/spring-context-reading
}
}
// 按照Spring的Bean语义要求将BeanDefinition资源解析并转换为容器内部数据结构
// 按照 Spring Bean 语义要求将 BeanDefinition 资源解析并转换为容器内部数据结构
public int registerBeanDefinitions(Document doc, Resource resource) throws BeanDefinitionStoreException {
// 得到BeanDefinitionDocumentReader来对xml格式的BeanDefinition解析
// 得到 BeanDefinitionDocumentReader 来对 xml 格式的 BeanDefinition 解析
BeanDefinitionDocumentReader documentReader = createBeanDefinitionDocumentReader();
documentReader.setEnvironment(this.getEnvironment());
// 获得容器中注册的Bean数量
// 获得容器中注册的 Bean 数量
int countBefore = getRegistry().getBeanDefinitionCount();
// 解析过程入口这里使用了委派模式BeanDefinitionDocumentReader只是个接口
// 具体的解析实现过程由实现类DefaultBeanDefinitionDocumentReader完成
// 解析过程入口这里使用了委派模式BeanDefinitionDocumentReader 只是个接口,
// 具体的解析实现过程由实现类 DefaultBeanDefinitionDocumentReader 完成
documentReader.registerBeanDefinitions(doc, createReaderContext(resource));
// 统计解析的Bean数量
// 统计解析的 Bean 数量
return getRegistry().getBeanDefinitionCount() - countBefore;
}
```
## 6、文档解析器DefaultBeanDefinitionDocumentReader对配置文件中元素的解析
## 6、文档解析器 DefaultBeanDefinitionDocumentReader 对配置文件中元素的解析
```java
// 根据Spring对Bean的定义规则进行解析
// 根据 Spring Bean 的定义规则进行解析
public void registerBeanDefinitions(Document doc, XmlReaderContext readerContext) {
// 获得XML描述符
// 获得 XML 描述符
this.readerContext = readerContext;
logger.debug("Loading bean definitions");
// 获得Document的根元素
// 获得 Document 的根元素
Element root = doc.getDocumentElement();
// 解析的具体实现
doRegisterBeanDefinitions(root);
@ -280,7 +280,7 @@ spring-context https://github.com/AmyliaY/spring-context-reading
/**
* Register each bean definition within the given root {@code <beans/>} element.
* 依次注册BeanDefinition使用给定的根元素
* 依次注册 BeanDefinition使用给定的根元素
*/
protected void doRegisterBeanDefinitions(Element root) {
String profileSpec = root.getAttribute(PROFILE_ATTRIBUTE);
@ -293,39 +293,39 @@ spring-context https://github.com/AmyliaY/spring-context-reading
}
}
// 具体的解析过程由BeanDefinitionParserDelegate实现
// BeanDefinitionParserDelegate中定义了Spring Bean定义XML文件的各种元素
// 具体的解析过程由 BeanDefinitionParserDelegate 实现,
// BeanDefinitionParserDelegate 中定义了 Spring Bean 定义 XML 文件的各种元素
BeanDefinitionParserDelegate parent = this.delegate;
this.delegate = createDelegate(this.readerContext, root, parent);
// 在解析BeanDefinition之前进行自定义的解析增强解析过程的可扩展性
// 在解析 BeanDefinition 之前,进行自定义的解析,增强解析过程的可扩展性
preProcessXml(root);
// 从Document的根元素开始进行Bean定义的Document对象
// 从 Document 的根元素开始进行 Bean 定义的 Document 对象
parseBeanDefinitions(root, this.delegate);
// 在解析Bean定义之后进行自定义的解析增加解析过程的可扩展性
// 在解析 Bean 定义之后,进行自定义的解析,增加解析过程的可扩展性
postProcessXml(root);
this.delegate = parent;
}
// 使用Spring的Bean规则从Document的根元素开始进行Bean Definition的解析
// 使用 Spring Bean 规则从 Document 的根元素开始进行 Bean Definition 的解析
protected void parseBeanDefinitions(Element root, BeanDefinitionParserDelegate delegate) {
// Bean定义的Document对象是否使用了Spring默认的XML命名空间
// Bean 定义的 Document 对象是否使用了 Spring 默认的 XML 命名空间
if (delegate.isDefaultNamespace(root)) {
// 获取root根元素的所有子节点
// 获取 root 根元素的所有子节点
NodeList nl = root.getChildNodes();
for (int i = 0; i < nl.getLength(); i++) {
Node node = nl.item(i);
if (node instanceof Element) {
Element ele = (Element) node;
// 如果ele定义的Document的元素节点使用的是Spring默认的XML命名空间
// 如果 ele 定义的 Document 的元素节点使用的是 Spring 默认的 XML 命名空间
if (delegate.isDefaultNamespace(ele)) {
// 使用Spring的Bean规则解析元素节点
// 使用 Spring Bean 规则解析元素节点
parseDefaultElement(ele, delegate);
}
else {
// 没有使用Spring默认的XML命名空间则使用用户自定义的解
// 没有使用 Spring 默认的 XML 命名空间,则使用用户自定义的解
// 析规则解析元素节点
delegate.parseCustomElement(ele);
}
@ -333,62 +333,62 @@ spring-context https://github.com/AmyliaY/spring-context-reading
}
}
else {
// Document的根节点没有使用Spring默认的命名空间则使用用户自定义的
// 解析规则解析Document根节点
// Document 的根节点没有使用 Spring 默认的命名空间,则使用用户自定义的
// 解析规则解析 Document 根节点
delegate.parseCustomElement(root);
}
}
// 使用Spring的Bean规则解析Document元素节点
// 使用 Spring Bean 规则解析 Document 元素节点
private void parseDefaultElement(Element ele, BeanDefinitionParserDelegate delegate) {
// 如果元素节点是<Import>导入元素,进行导入解析
// 如果元素节点是 <Import> 导入元素,进行导入解析
if (delegate.nodeNameEquals(ele, IMPORT_ELEMENT)) {
importBeanDefinitionResource(ele);
}
// 如果元素节点是<Alias>别名元素,进行别名解析
// 如果元素节点是 <Alias> 别名元素,进行别名解析
else if (delegate.nodeNameEquals(ele, ALIAS_ELEMENT)) {
processAliasRegistration(ele);
}
// 元素节点既不是导入元素,也不是别名元素,即普通的<Bean>元素,
// 按照Spring的Bean规则解析元素
// 元素节点既不是导入元素,也不是别名元素,即普通的 <Bean> 元素,
// 按照 Spring Bean 规则解析元素
else if (delegate.nodeNameEquals(ele, BEAN_ELEMENT)) {
processBeanDefinition(ele, delegate);
}
// 如果被解析的元素是beans则递归调用doRegisterBeanDefinitions(Element root)方法
// 如果被解析的元素是 beans则递归调用 doRegisterBeanDefinitions(Element root) 方法
else if (delegate.nodeNameEquals(ele, NESTED_BEANS_ELEMENT)) {
doRegisterBeanDefinitions(ele);
}
}
// 解析<Import>导入元素从给定的导入路径加载Bean定义资源到Spring IoC容器中
// 解析 <Import> 导入元素,从给定的导入路径加载 Bean 定义资源到 Spring IoC 容器中
protected void importBeanDefinitionResource(Element ele) {
// 获取给定的导入元素的location属性
// 获取给定的导入元素的 location 属性
String location = ele.getAttribute(RESOURCE_ATTRIBUTE);
// 如果导入元素的location属性值为空则没有导入任何资源直接返回
// 如果导入元素的 location 属性值为空,则没有导入任何资源,直接返回
if (!StringUtils.hasText(location)) {
getReaderContext().error("Resource location must not be empty", ele);
return;
}
// 使用系统变量值解析location属性值
// 使用系统变量值解析 location 属性值
location = environment.resolveRequiredPlaceholders(location);
Set<Resource> actualResources = new LinkedHashSet<Resource>(4);
// 标识给定的导入元素的location是否是绝对路径
// 标识给定的导入元素的 location 是否是绝对路径
boolean absoluteLocation = false;
try {
absoluteLocation = ResourcePatternUtils.isUrl(location) || ResourceUtils.toURI(location).isAbsolute();
}
catch (URISyntaxException ex) {
// 给定的导入元素的location不是绝对路径
// 给定的导入元素的 location 不是绝对路径
}
// 给定的导入元素的location是绝对路径
// 给定的导入元素的 location 是绝对路径
if (absoluteLocation) {
try {
// 使用资源读入器加载给定路径的Bean定义资源
// 使用资源读入器加载给定路径的 Bean 定义资源
int importCount = getReaderContext().getReader().loadBeanDefinitions(location, actualResources);
if (logger.isDebugEnabled()) {
logger.debug("Imported " + importCount + " bean definitions from URL location [" + location + "]");
@ -400,22 +400,22 @@ spring-context https://github.com/AmyliaY/spring-context-reading
}
}
else {
// 给定的导入元素的location是相对路径
// 给定的导入元素的 location 是相对路径
try {
int importCount;
// 将给定导入元素的location封装为相对路径资源
// 将给定导入元素的 location 封装为相对路径资源
Resource relativeResource = getReaderContext().getResource().createRelative(location);
// 封装的相对路径资源存在
if (relativeResource.exists()) {
// 使用资源读入器加载Bean定义资源
// 使用资源读入器加载 Bean 定义资源
importCount = getReaderContext().getReader().loadBeanDefinitions(relativeResource);
actualResources.add(relativeResource);
}
// 封装的相对路径资源不存在
else {
// 获取Spring IOC容器资源读入器的基本路径
// 获取 Spring IOC 容器资源读入器的基本路径
String baseLocation = getReaderContext().getResource().getURL().toString();
// 根据Spring IoC容器资源读入器的基本路径加载给定导入路径的资源
// 根据 Spring IoC 容器资源读入器的基本路径加载给定导入路径的资源
importCount = getReaderContext().getReader().loadBeanDefinitions(
StringUtils.applyRelativePath(baseLocation, location), actualResources);
}
@ -432,7 +432,7 @@ spring-context https://github.com/AmyliaY/spring-context-reading
}
}
Resource[] actResArray = actualResources.toArray(new Resource[actualResources.size()]);
// 在解析完<Import>元素之后,发送容器导入其他资源处理完成事件
// 在解析完 <Import> 元素之后,发送容器导入其他资源处理完成事件
getReaderContext().fireImportProcessed(location, actResArray, extractSource(ele));
}
@ -440,19 +440,19 @@ spring-context https://github.com/AmyliaY/spring-context-reading
* Process the given alias element, registering the alias with the registry.
*/
// 解析<Alias>别名元素为Bean向Spring IoC容器注册别名
// 解析 <Alias> 别名元素,为 Bean Spring IoC 容器注册别名
protected void processAliasRegistration(Element ele) {
// 获取<Alias>别名元素中name的属性值
// 获取 <Alias> 别名元素中 name 的属性值
String name = ele.getAttribute(NAME_ATTRIBUTE);
// 获取<Alias>别名元素中alias的属性值
// 获取 <Alias> 别名元素中 alias 的属性值
String alias = ele.getAttribute(ALIAS_ATTRIBUTE);
boolean valid = true;
// <alias>别名元素的name属性值为空
// <alias> 别名元素的 name 属性值为空
if (!StringUtils.hasText(name)) {
getReaderContext().error("Name must not be empty", ele);
valid = false;
}
// <alias>别名元素的alias属性值为空
// <alias> 别名元素的 alias 属性值为空
if (!StringUtils.hasText(alias)) {
getReaderContext().error("Alias must not be empty", ele);
valid = false;
@ -466,24 +466,24 @@ spring-context https://github.com/AmyliaY/spring-context-reading
getReaderContext().error("Failed to register alias '" + alias +
"' for bean with name '" + name + "'", ele, ex);
}
// 在解析完<Alias>元素之后,发送容器别名处理完成事件
// 在解析完 <Alias> 元素之后,发送容器别名处理完成事件
getReaderContext().fireAliasRegistered(name, alias, extractSource(ele));
}
}
// 解析Bean定义资源Document对象的普通元素
// 解析 Bean 定义资源 Document 对象的普通元素
protected void processBeanDefinition(Element ele, BeanDefinitionParserDelegate delegate) {
// BeanDefinitionHolder是对BeanDefinition的封装即BeanDefinition的封装类
// 对Document对象中<Bean>元素的解析由BeanDefinitionParserDelegate实现
// BeanDefinitionHolder 是对 BeanDefinition 的封装,即 BeanDefinition 的封装类
// 对 Document 对象中 <Bean> 元素的解析由 BeanDefinitionParserDelegate 实现
BeanDefinitionHolder bdHolder = delegate.parseBeanDefinitionElement(ele);
if (bdHolder != null) {
// 对bdHolder进行包装处理
// 对 bdHolder 进行包装处理
bdHolder = delegate.decorateBeanDefinitionIfRequired(ele, bdHolder);
try {
/**
*
* 向Spring IoC容器注册解析BeanDefinition这是BeanDefinition向IoC容器注册的入口
* 向 Spring IoC 容器注册解析 BeanDefinition这是 BeanDefinition IoC 容器注册的入口
*
*/
BeanDefinitionReaderUtils.registerBeanDefinition(bdHolder, getReaderContext().getRegistry());
@ -492,37 +492,37 @@ 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定义之后发送注册事件
// 在完成向 Spring IOC 容器注册解析得到的 Bean 定义之后,发送注册事件
getReaderContext().fireComponentRegistered(new BeanComponentDefinition(bdHolder));
}
}
```
## 7、看一下BeanDefinitionParserDelegate中对bean元素的详细解析过程
## 7、看一下 BeanDefinitionParserDelegate 中对 bean 元素的详细解析过程
```java
// 解析<Bean>元素的入口
// 解析 <Bean> 元素的入口
public BeanDefinitionHolder parseBeanDefinitionElement(Element ele) {
return parseBeanDefinitionElement(ele, null);
}
// 解析BeanDefinition资源文件中的<Bean>元素,这个方法中主要处理<Bean>元素的idname和别名属性
// 解析 BeanDefinition 资源文件中的 <Bean> 元素,这个方法中主要处理 <Bean> 元素的 idname 和别名属性
public BeanDefinitionHolder parseBeanDefinitionElement(Element ele, BeanDefinition containingBean) {
// 获取<Bean>元素中的id属性值
// 获取 <Bean> 元素中的 id 属性值
String id = ele.getAttribute(ID_ATTRIBUTE);
// 获取<Bean>元素中的name属性值
// 获取 <Bean> 元素中的 name 属性值
String nameAttr = ele.getAttribute(NAME_ATTRIBUTE);
// 获取<Bean>元素中的alias属性值
// 获取 <Bean> 元素中的 alias 属性值
List<String> aliases = new ArrayList<String>();
// 将<Bean>元素中的所有name属性值存放到别名中
// 将 <Bean> 元素中的所有 name 属性值存放到别名中
if (StringUtils.hasLength(nameAttr)) {
String[] nameArr = StringUtils.tokenizeToStringArray(nameAttr, MULTI_VALUE_ATTRIBUTE_DELIMITERS);
aliases.addAll(Arrays.asList(nameArr));
}
String beanName = id;
// 如果<Bean>元素中没有配置id属性时将别名中的第一个值赋值给beanName
// 如果 <Bean> 元素中没有配置 id 属性时,将别名中的第一个值赋值给 beanName
if (!StringUtils.hasText(beanName) && !aliases.isEmpty()) {
beanName = aliases.remove(0);
if (logger.isDebugEnabled()) {
@ -531,29 +531,29 @@ spring-context https://github.com/AmyliaY/spring-context-reading
}
}
// 检查<Bean>元素所配置的id或者name的唯一性containingBean标识<Bean>
// 元素中是否包含子<Bean>元素
// 检查 <Bean> 元素所配置的 id 或者 name 的唯一性containingBean 标识 <Bean>
// 元素中是否包含子 <Bean> 元素
if (containingBean == null) {
// 检查<Bean>元素所配置的id、name或者别名是否重复
// 检查 <Bean> 元素所配置的 id、name 或者别名是否重复
checkNameUniqueness(beanName, aliases, ele);
}
// 详细对<Bean>元素中配置的Bean定义进行解析的地方
// 详细对 <Bean> 元素中配置的 Bean 定义进行解析的地方
AbstractBeanDefinition beanDefinition = parseBeanDefinitionElement(ele, beanName, containingBean);
if (beanDefinition != null) {
if (!StringUtils.hasText(beanName)) {
try {
if (containingBean != null) {
// 如果<Bean>元素中没有配置id、别名或者name且没有包含子元素
// <Bean>元素为解析的Bean生成一个唯一beanName并注册
// 如果 <Bean> 元素中没有配置 id、别名或者 name且没有包含子元素
// <Bean> 元素,为解析的 Bean 生成一个唯一 beanName 并注册
beanName = BeanDefinitionReaderUtils.generateBeanName(
beanDefinition, this.readerContext.getRegistry(), true);
}
else {
// 如果<Bean>元素中没有配置id、别名或者name且包含了子元素
// <Bean>元素为解析的Bean使用别名向IOC容器注册
// 如果 <Bean> 元素中没有配置 id、别名或者 name且包含了子元素
// <Bean> 元素,为解析的 Bean 使用别名向 IOC 容器注册
beanName = this.readerContext.generateBeanName(beanDefinition);
// 为解析的Bean使用别名注册时为了向后兼容
// 为解析的 Bean 使用别名注册时,为了向后兼容
// Spring1.2/2.0,给别名添加类名后缀
String beanClassName = beanDefinition.getBeanClassName();
if (beanClassName != null &&
@ -575,20 +575,20 @@ spring-context https://github.com/AmyliaY/spring-context-reading
String[] aliasesArray = StringUtils.toStringArray(aliases);
return new BeanDefinitionHolder(beanDefinition, beanName, aliasesArray);
}
// 当解析出错时返回null
// 当解析出错时,返回 null
return null;
}
// 详细对<Bean>元素中配置的Bean定义其他属性进行解析由于上面的方法中已经对
// Bean的id、name和别名等属性进行了处理该方法中主要处理除这三个以外的其他属性数据
// 详细对 <Bean> 元素中配置的 Bean 定义其他属性进行解析,由于上面的方法中已经对
// Bean id、name 和别名等属性进行了处理,该方法中主要处理除这三个以外的其他属性数据
public AbstractBeanDefinition parseBeanDefinitionElement(
Element ele, String beanName, BeanDefinition containingBean) {
// 记录解析的<Bean>
// 记录解析的 <Bean>
this.parseState.push(new BeanEntry(beanName));
// 这里只读取<Bean>元素中配置的class名字然后载入到BeanDefinition中去
// 只是记录配置的class名字不做实例化对象的实例化在依赖注入时完成
// 这里只读取 <Bean> 元素中配置的 class 名字,然后载入到 BeanDefinition 中去
// 只是记录配置的 class 名字,不做实例化,对象的实例化在依赖注入时完成
String className = null;
if (ele.hasAttribute(CLASS_ATTRIBUTE)) {
className = ele.getAttribute(CLASS_ATTRIBUTE).trim();
@ -596,35 +596,35 @@ spring-context https://github.com/AmyliaY/spring-context-reading
try {
String parent = null;
// 如果<Bean>元素中配置了parent属性则获取parent属性的值
// 如果 <Bean> 元素中配置了 parent 属性,则获取 parent 属性的值
if (ele.hasAttribute(PARENT_ATTRIBUTE)) {
parent = ele.getAttribute(PARENT_ATTRIBUTE);
}
// 根据<Bean>元素配置的class名称和parent属性值创建BeanDefinition
// 为载入Bean定义信息做准备
// 根据 <Bean> 元素配置的 class 名称和 parent 属性值创建 BeanDefinition
// 为载入 Bean 定义信息做准备
AbstractBeanDefinition bd = createBeanDefinition(className, parent);
// 对当前的<Bean>元素中配置的一些属性进行解析和设置,如配置的单态(singleton)属性等
// 对当前的 <Bean> 元素中配置的一些属性进行解析和设置,如配置的单态 (singleton) 属性等
parseBeanDefinitionAttributes(ele, beanName, containingBean, bd);
// 为<Bean>元素解析的Bean设置description信息
// 为 <Bean> 元素解析的 Bean 设置 description 信息
bd.setDescription(DomUtils.getChildElementValueByTagName(ele, DESCRIPTION_ELEMENT));
// 对<Bean>元素的meta(元信息)属性解析
// 对 <Bean> 元素的 meta(元信息) 属性解析
parseMetaElements(ele, bd);
// 对<Bean>元素的lookup-method属性解析
// 对 <Bean> 元素的 lookup-method 属性解析
parseLookupOverrideSubElements(ele, bd.getMethodOverrides());
// 对<Bean>元素的replaced-method属性解析
// 对 <Bean> 元素的 replaced-method 属性解析
parseReplacedMethodSubElements(ele, bd.getMethodOverrides());
// 解析<Bean>元素的构造方法属性
// 解析 <Bean> 元素的构造方法属性
parseConstructorArgElements(ele, bd);
// 解析<Bean>元素所有的<property>属性
// 解析 <Bean> 元素所有的 <property> 属性
parsePropertyElements(ele, bd);
// 解析<Bean>元素的qualifier属性
// 解析 <Bean> 元素的 qualifier 属性
parseQualifierElements(ele, bd);
//为当前解析的Bean设置所需的资源和依赖对象
//为当前解析的 Bean 设置所需的资源和依赖对象
bd.setResource(this.readerContext.getResource());
bd.setSource(extractSource(ele));
@ -642,28 +642,28 @@ spring-context https://github.com/AmyliaY/spring-context-reading
finally {
this.parseState.pop();
}
// 解析<Bean>元素出错时返回null
// 解析 <Bean> 元素出错时,返回 null
return null;
}
```
## 8、对bean的部分子元素进行解析的具体实现
## 8、对 bean 的部分子元素进行解析的具体实现
```java
// 解析<Bean>元素中所有的<property>子元素
// 解析 <Bean> 元素中所有的 <property> 子元素
public void parsePropertyElements(Element beanEle, BeanDefinition bd) {
// 获取对应bean元素中所有的<property>子元素,逐一解析
// 获取对应 bean 元素中所有的 <property> 子元素,逐一解析
NodeList nl = beanEle.getChildNodes();
for (int i = 0; i < nl.getLength(); i++) {
Node node = nl.item(i);
// 对<property>子元素进行详细解析
// 对 <property> 子元素进行详细解析
if (isCandidateElement(node) && nodeNameEquals(node, PROPERTY_ELEMENT)) {
parsePropertyElement((Element) node, bd);
}
}
}
// 详细解析<property>元素
// 详细解析 <property> 元素
public void parsePropertyElement(Element ele, BeanDefinition bd) {
// 获取<property>元素的名字
// 获取 <property> 元素的名字
String propertyName = ele.getAttribute(NAME_ATTRIBUTE);
if (!StringUtils.hasLength(propertyName)) {
error("Tag 'property' must have a 'name' attribute", ele);
@ -671,18 +671,18 @@ spring-context https://github.com/AmyliaY/spring-context-reading
}
this.parseState.push(new PropertyEntry(propertyName));
try {
// 如果一个Bean中已经有同名的property存在则不进行解析直接返回。
// 即如果在同一个Bean中配置同名的property则只有第一个起作用
// 如果一个 Bean 中已经有同名的 property 存在,则不进行解析,直接返回。
// 即如果在同一个 Bean 中配置同名的 property则只有第一个起作用
if (bd.getPropertyValues().contains(propertyName)) {
error("Multiple 'property' definitions for property '" + propertyName + "'", ele);
return;
}
// 解析获取property的值返回的对象对应 对bean定义的property属性设置的
// 解析结果这个解析结果会封装到PropertyValue对象中然后设置到BeanDefinitionHolder中去
// 解析获取 property 的值,返回的对象对应 对 bean 定义的 property 属性设置的
// 解析结果,这个解析结果会封装到 PropertyValue 对象中,然后设置到 BeanDefinitionHolder 中去
Object val = parsePropertyValue(ele, bd, propertyName);
// 根据property的名字和值创建property实例
// 根据 property 的名字和值创建 property 实例
PropertyValue pv = new PropertyValue(propertyName, val);
// 解析<property>元素中的属性
// 解析 <property> 元素中的属性
parseMetaElements(ele, pv);
pv.setSource(extractSource(ele));
bd.getPropertyValues().addPropertyValue(pv);
@ -692,31 +692,31 @@ spring-context https://github.com/AmyliaY/spring-context-reading
}
}
// 解析获取property值
// 解析获取 property
public Object parsePropertyValue(Element ele, BeanDefinition bd, String propertyName) {
String elementName = (propertyName != null) ?
"<property> element for property '" + propertyName + "'" :
"<constructor-arg> element";
// 获取<property>的所有子元素,只能是其中一种类型:ref,value,list等
// 获取 <property> 的所有子元素,只能是其中一种类型:ref,value,list
NodeList nl = ele.getChildNodes();
Element subElement = null;
for (int i = 0; i < nl.getLength(); i++) {
Node node = nl.item(i);
// 如果子元素不是description和meta属性
// 如果子元素不是 description meta 属性
if (node instanceof Element && !nodeNameEquals(node, DESCRIPTION_ELEMENT) &&
!nodeNameEquals(node, META_ELEMENT)) {
// Child element is what we're looking for.
if (subElement != null) {
error(elementName + " must not contain more than one sub-element", ele);
}
else {// 当前<property>元素包含有子元素
else {// 当前 <property> 元素包含有子元素
subElement = (Element) node;
}
}
}
// 判断property的属性值是ref还是value不允许既是ref又是value
// 判断 property 的属性值是 ref 还是 value不允许既是 ref 又是 value
boolean hasRefAttribute = ele.hasAttribute(REF_ATTRIBUTE);
boolean hasValueAttribute = ele.hasAttribute(VALUE_ATTRIBUTE);
if ((hasRefAttribute && hasValueAttribute) ||
@ -725,8 +725,8 @@ spring-context https://github.com/AmyliaY/spring-context-reading
" is only allowed to contain either 'ref' attribute OR 'value' attribute OR sub-element", ele);
}
// 如果属性是ref创建一个ref的数据对象RuntimeBeanReference
// 这个对象封装了ref信息
// 如果属性是 ref创建一个 ref 的数据对象 RuntimeBeanReference
// 这个对象封装了 ref 信息
if (hasRefAttribute) {
String refName = ele.getAttribute(REF_ATTRIBUTE);
if (!StringUtils.hasText(refName)) {
@ -734,43 +734,43 @@ spring-context https://github.com/AmyliaY/spring-context-reading
}
// 一个指向运行时所依赖对象的引用
RuntimeBeanReference ref = new RuntimeBeanReference(refName);
// 设置这个ref的数据对象是被当前的property对象所引用
// 设置这个 ref 的数据对象是被当前的 property 对象所引用
ref.setSource(extractSource(ele));
return ref;
}
// 如果属性是value创建一个value的数据对象TypedStringValue
// 这个对象封装了value信息
// 如果属性是 value创建一个 value 的数据对象 TypedStringValue
// 这个对象封装了 value 信息
else if (hasValueAttribute) {
// 一个持有String类型值的对象
// 一个持有 String 类型值的对象
TypedStringValue valueHolder = new TypedStringValue(ele.getAttribute(VALUE_ATTRIBUTE));
// 设置这个value数据对象是被当前的property对象所引用
// 设置这个 value 数据对象是被当前的 property 对象所引用
valueHolder.setSource(extractSource(ele));
return valueHolder;
}
// 如果当前<property>元素还有子元素
// 如果当前 <property> 元素还有子元素
else if (subElement != null) {
// 解析<property>的子元素
// 解析 <property> 的子元素
return parsePropertySubElement(subElement, bd);
}
else {
// propery属性中既不是ref也不是value属性解析出错返回null
// propery 属性中既不是 ref也不是 value 属性,解析出错返回 null
error(elementName + " must specify a ref or value", ele);
return null;
}
}
// 解析<property>元素中ref,value或者集合等子元素
// 解析 <property> 元素中 ref,value 或者集合等子元素
public Object parsePropertySubElement(Element ele, BeanDefinition bd) {
return parsePropertySubElement(ele, bd, null);
}
public Object parsePropertySubElement(Element ele, BeanDefinition bd, String defaultValueType) {
// 如果<property>没有使用Spring默认的命名空间则使用用户自定义的规则解析
// 如果 <property> 没有使用 Spring 默认的命名空间,则使用用户自定义的规则解析
// 内嵌元素
if (!isDefaultNamespace(ele)) {
return parseNestedCustomElement(ele, bd);
}
// 如果子元素是bean则使用解析<Bean>元素的方法解析
// 如果子元素是 bean则使用解析 <Bean> 元素的方法解析
else if (nodeNameEquals(ele, BEAN_ELEMENT)) {
BeanDefinitionHolder nestedBd = parseBeanDefinitionElement(ele, bd);
if (nestedBd != null) {
@ -778,18 +778,18 @@ spring-context https://github.com/AmyliaY/spring-context-reading
}
return nestedBd;
}
// 如果子元素是refref中只能有以下3个属性bean、local、parent
// 如果子元素是 refref 中只能有以下 3 个属性bean、local、parent
else if (nodeNameEquals(ele, REF_ELEMENT)) {
// 获取<property>元素中的bean属性值引用其他解析的Bean的名称
// 可以不再同一个Spring配置文件中具体请参考Spring对ref的配置规则
// 获取 <property> 元素中的 bean 属性值,引用其他解析的 Bean 的名称
// 可以不再同一个 Spring 配置文件中,具体请参考 Spring ref 的配置规则
String refName = ele.getAttribute(BEAN_REF_ATTRIBUTE);
boolean toParent = false;
if (!StringUtils.hasLength(refName)) {
// 获取<property>元素中的local属性值引用同一个Xml文件中配置
// 的Bean的idlocal和ref不同local只能引用同一个配置文件中的Bean
// 获取 <property> 元素中的 local 属性值,引用同一个 Xml 文件中配置
// 的 Bean idlocal ref 不同local 只能引用同一个配置文件中的 Bean
refName = ele.getAttribute(LOCAL_REF_ATTRIBUTE);
if (!StringUtils.hasLength(refName)) {
// 获取<property>元素中parent属性值引用父级容器中的Bean
// 获取 <property> 元素中 parent 属性值,引用父级容器中的 Bean
refName = ele.getAttribute(PARENT_REF_ATTRIBUTE);
toParent = true;
@ -800,83 +800,83 @@ spring-context https://github.com/AmyliaY/spring-context-reading
}
}
// 没有配置ref的目标属性值
// 没有配置 ref 的目标属性值
if (!StringUtils.hasText(refName)) {
error("<ref> element contains empty target attribute", ele);
return null;
}
// 创建ref类型数据指向被引用的对象
// 创建 ref 类型数据,指向被引用的对象
RuntimeBeanReference ref = new RuntimeBeanReference(refName, toParent);
// 设置引用类型值是被当前子元素所引用
ref.setSource(extractSource(ele));
return ref;
}
// 如果子元素是<idref>使用解析ref元素的方法解析
// 如果子元素是 <idref>,使用解析 ref 元素的方法解析
else if (nodeNameEquals(ele, IDREF_ELEMENT)) {
return parseIdRefElement(ele);
}
// 如果子元素是<value>使用解析value元素的方法解析
// 如果子元素是 <value>,使用解析 value 元素的方法解析
else if (nodeNameEquals(ele, VALUE_ELEMENT)) {
return parseValueElement(ele, defaultValueType);
}
//如果子元素是null<property>设置一个封装null值的字符串数据
//如果子元素是 null <property> 设置一个封装 null 值的字符串数据
else if (nodeNameEquals(ele, NULL_ELEMENT)) {
TypedStringValue nullHolder = new TypedStringValue(null);
nullHolder.setSource(extractSource(ele));
return nullHolder;
}
// 如果子元素是<array>使用解析array集合子元素的方法解析
// 如果子元素是 <array>,使用解析 array 集合子元素的方法解析
else if (nodeNameEquals(ele, ARRAY_ELEMENT)) {
return parseArrayElement(ele, bd);
}
// 如果子元素是<list>使用解析list集合子元素的方法解析
// 如果子元素是 <list>,使用解析 list 集合子元素的方法解析
else if (nodeNameEquals(ele, LIST_ELEMENT)) {
return parseListElement(ele, bd);
}
// 如果子元素是<set>使用解析set集合子元素的方法解析
// 如果子元素是 <set>,使用解析 set 集合子元素的方法解析
else if (nodeNameEquals(ele, SET_ELEMENT)) {
return parseSetElement(ele, bd);
}
// 如果子元素是<map>使用解析map集合子元素的方法解析
// 如果子元素是 <map>,使用解析 map 集合子元素的方法解析
else if (nodeNameEquals(ele, MAP_ELEMENT)) {
return parseMapElement(ele, bd);
}
// 如果子元素是<props>使用解析props集合子元素的方法解析
// 如果子元素是 <props>,使用解析 props 集合子元素的方法解析
else if (nodeNameEquals(ele, PROPS_ELEMENT)) {
return parsePropsElement(ele);
}
// 既不是ref又不是value也不是集合则子元素配置错误返回null
// 既不是 ref又不是 value也不是集合则子元素配置错误返回 null
else {
error("Unknown property sub-element: [" + ele.getNodeName() + "]", ele);
return null;
}
}
// 解析<list>集合子元素
// 解析 <list> 集合子元素
public List parseListElement(Element collectionEle, BeanDefinition bd) {
// 获取<list>元素中的value-type属性即获取集合元素的数据类型
// 获取 <list> 元素中的 value-type 属性,即获取集合元素的数据类型
String defaultElementType = collectionEle.getAttribute(VALUE_TYPE_ATTRIBUTE);
// 获取<list>集合元素中的所有子节点
// 获取 <list> 集合元素中的所有子节点
NodeList nl = collectionEle.getChildNodes();
// Spring中将List封装为ManagedList
// Spring 中将 List 封装为 ManagedList
ManagedList<Object> target = new ManagedList<Object>(nl.getLength());
target.setSource(extractSource(collectionEle));
// 设置集合目标数据类型
target.setElementTypeName(defaultElementType);
target.setMergeEnabled(parseMergeAttribute(collectionEle));
// 具体的<list>元素解析
// 具体的 <list> 元素解析
parseCollectionElements(nl, target, bd, defaultElementType);
return target;
}
// 具体解析<list>集合元素,<array><list><set>都使用该方法解析
// 具体解析 <list> 集合元素,<array><list> <set> 都使用该方法解析
protected void parseCollectionElements(NodeList elementNodes, Collection<Object> target,
BeanDefinition bd, String defaultElementType) {
// 遍历集合所有节点
for (int i = 0; i < elementNodes.getLength(); i++) {
Node node = elementNodes.item(i);
// 节点不是description节点
// 节点不是 description 节点
if (node instanceof Element && !nodeNameEquals(node, DESCRIPTION_ELEMENT)) {
// 将解析的元素加入集合中,递归调用下一个子元素
target.add(parsePropertySubElement((Element) node, bd, defaultElementType));
@ -884,14 +884,4 @@ spring-context https://github.com/AmyliaY/spring-context-reading
}
}
```
经过这样逐层地解析我们在配置文件中定义的Bean就被整个解析成了可以被IoC容器装载和使用的BeanDefinition这种数据结构可以让IoC容器执行索引、查询等操作。经过上述解析得到的BeanDefinition接下来我们就可以将它注册到IoC容器中咯。
经过这样逐层地解析,我们在配置文件中定义的 Bean 就被整个解析成了可以被 IoC 容器装载和使用的 BeanDefinition这种数据结构可以让 IoC 容器执行索引、查询等操作。经过上述解析得到的 BeanDefinition接下来我们就可以将它注册到 IoC 容器中咯。

File diff suppressed because it is too large Load Diff

@ -1,26 +1,30 @@
 引言:庞大的代码量让人心生怠倦,有趣的故事让技术也疯狂。
大家好我是IoC容器家族的第17代传人我们家族世世代代在spring商业街上卖烤面筋大家都叫我“面筋哥”另外我爹还给我起了个高大上的英文名字叫“FileSystemXmlApplicationContext”但有群臭猴子嫌麻烦就天天叫我的外号害得我差点忘了自己的本名。不过无所谓咯只要生意兴隆这都是小事。
引言:庞大的代码量让人心生怠倦,有趣的故事让技术也疯狂。
前几天出摊卖烤面筋时,灵感大作,即兴唱了一首“我的烤面筋”,被网友拍下来传到某站上 成了网红现在我要趁势而上把自己祖传的烤面筋工艺宣传出去让我那个臭弟弟“ClassPathXmlApplicationContext”知道谁才是IoC容器的正统传人
#### 第一阶段BeanDefinition资源定位ReaderbeanDefinitionReaderdocumentReader
新的一天从new开始但我却还躺在床上各种伸懒腰毕竟我现在也是个小老板了很多杂七杂八的活雇几个小弟干就行咯。我拿起我的iBanana11看了看商业街董事某程序员发的“精选优质面筋批发市场地址”然后深吸一口气refresh()闭上眼obtainFreshBeanFactory()气沉丹田refreshBeanFactory(),大喊一声:
大家好,我是 IoC 容器家族的第 17 代传人,我们家族世世代代在 spring 商业街上卖烤面筋大家都叫我“面筋哥”另外我爹还给我起了个高大上的英文名字叫“FileSystemXmlApplicationContext”但有群臭猴子嫌麻烦就天天叫我的外号害得我差点忘了自己的本名。不过无所谓咯只要生意兴隆这都是小事。
前几天出摊卖烤面筋时,灵感大作,即兴唱了一首“我的烤面筋”,被网友拍下来传到某站上 成了网红现在我要趁势而上把自己祖传的烤面筋工艺宣传出去让我那个臭弟弟“ClassPathXmlApplicationContext”知道谁才是 IoC 容器的正统传人!
## 第一阶段BeanDefinition 资源定位ReaderbeanDefinitionReaderdocumentReader
新的一天从 new 开始,但我却还躺在床上各种伸懒腰,毕竟我现在也是个小老板了,很多杂七杂八的活雇几个小弟干就行咯。我拿起我的 iBanana11 看了看商业街董事(某程序员)发的“精选优质面筋批发市场地址”,然后深吸一口气 refresh(),闭上眼 obtainFreshBeanFactory(),气沉丹田 refreshBeanFactory(),大喊一声:
“loadBeanDefinitions()!”
我虎背熊腰的小弟“beanDefinitionReader” 破门而入,尖声细语地问道:
“老板有何吩咐 ~ ?”
我起身叮嘱了他几件事后把自己的联系方式引用、面筋批发市场的地址spring配置文件地址交给他就又躺回去盯着天花板上的钻石吊灯继续发呆。
Reader家有一对兄妹哥哥beanDefinitionReader虎背熊腰大老粗却尖声细语妹妹documentReader心灵手巧可惜比较宅我们几乎没怎么见过。兄妹俩相互配合把上午的准备工作做了大半。
不要看我天天躺着彗星晒屁股了还眯着眼ta们兄妹俩在几点几分打个喷嚏我都能算到毕竟我基因里都写满了“烤面筋工艺完整详细流程”。
哥哥现在肯定在开着小面包车拿着我给他的地址locations到处找面筋等原材料然后把找到的面筋打包进Document对象拉回来交给妹妹documentReader进行精心处理连同Document给她的还有一个“神秘人”的联系方式。
妹妹会打开Document取出其中最大的几个箱子&lt;beans>、&lt;import>、&lt;alias>等一级标签分别进行处理。其中beans箱最为重要里面放满了夜市的主角烤面筋的核心材料。
#### 第二阶段将bean解析封装成BeanDefinitionHolderBeanDefinitionParserDelegate
之后妹妹会拿起我们IoC家族祖传的面筋处理神器BeanDefinitionParserDelegate从beans箱里面一个一个取出形态各异的面筋bean分别进行加工处理。刚拿出来的面筋bean是不会直接烤了卖的我们会将bean用神器ParserDelegate进行九九八十一道细致处理所以我们家烤出来的面筋才会如此劲道美味世世代代延绵不断。
不过处理程序再怎么细致复杂也不过就是分为两大部分第一处理bean的属性信息如idclassscope等第二处理bean的子元素主要是<property>标签,而<property>标签又有属性和子元素,且子元素类型更加丰富复杂,可能是&lt;map>&lt;set>&lt;list>&lt;array>等。所以如果你们想学我家的祖传秘方,开个同样的摊子干倒我,也不是这么容易的哦。
经过上面的步骤一个配置文件中的面筋bean就被处理包装成了半成品BeanDefinitionHolder。
#### 第三阶段将BeanDefinition注册进IoC容器BeanDefinitionReaderUtils
妹妹在用神器BeanDefinitionParserDelegate经过一顿疯狂操作之后将包装好的半成品BeanDefinitionHolder扔进传输机BeanDefinitionReaderUtils并且输入哥哥给她的神秘人地址就继续处理下一个面筋bean咯。
之后传输机将BeanDefinitionHolder的包装打开分别取出beanName面筋的唯一标识和BeanDefinition面筋本筋传输的目的地是BeanDefinitionRegistry的工作室这就是我前面给哥哥beanDefinitionReader的地址
这家工作室的BeanDefinitionRegistry其实就是我的影分身之一因为我的祖先实现了这个接口。影分身Registry检查一下传输过来的beanName面筋的唯一标识和BeanDefinition面筋本筋如果没什么问题就把它们用根绳子系在一起扔进我的“王之面筋宝库”一个ConcurrentHashMap<String, BeanDefinition>(64)也有人把我的“面筋宝库”称作“IoC容器本器”我也无可辩驳谁让他们吃面筋付钱了呢。
就这样,每一种取出来的面筋都会经过这些处理。等到所有的面筋处理完了,也差不多到了傍晚,每到这时我就会拿起梳子和发油,对着镶满钻石的镜子,梳理整齐与徐峥同款的明星发型,唱着魔性的“我的烤面筋 ~”,骑着小车车,出摊咯 ~
我起身叮嘱了他几件事后把自己的联系方式引用、面筋批发市场的地址spring 配置文件地址)交给他,就又躺回去盯着天花板上的钻石吊灯继续发呆。
Reader 家有一对兄妹,哥哥 beanDefinitionReader 虎背熊腰大老粗,却尖声细语;妹妹 documentReader 心灵手巧,可惜比较宅,我们几乎没怎么见过。兄妹俩相互配合把上午的准备工作做了大半。
不要看我天天躺着彗星晒屁股了还眯着眼ta 们兄妹俩在几点几分打个喷嚏我都能算到,毕竟我基因里都写满了“烤面筋工艺完整详细流程”。
哥哥现在肯定在开着小面包车拿着我给他的地址locations到处找面筋等原材料然后把找到的面筋打包进 Document 对象,拉回来交给妹妹 documentReader 进行精心处理,连同 Document 给她的还有一个“神秘人”的联系方式。
妹妹会打开 Document 取出其中最大的几个箱子(&lt;beans>、&lt;import>、&lt;alias> 等一级标签),分别进行处理。其中 beans 箱最为重要,里面放满了夜市的主角,烤面筋的核心材料。
面筋等原材料基本上都已经处理完毕,但把这些原材料变成程序员手中的“烤面筋”也是一门复杂而精细的手艺,老铁们记得 watch、star、fork素质三连一波下一期我将带领你们走进spring商业街的夜市一起烤出香喷喷的面筋成为这条gai上最亮的仔
## 第二阶段:将 bean 解析封装成 BeanDefinitionHolderBeanDefinitionParserDelegate
之后妹妹会拿起我们 IoC 家族祖传的面筋处理神器 BeanDefinitionParserDelegate从 beans 箱里面一个一个取出形态各异的面筋 bean 分别进行加工处理。刚拿出来的面筋 bean 是不会直接烤了卖的,我们会将 bean 用神器 ParserDelegate 进行九九八十一道细致处理,所以我们家烤出来的面筋才会如此劲道美味,世世代代延绵不断。
不过处理程序再怎么细致复杂,也不过就是分为两大部分:第一,处理 bean 的属性信息,如 idclassscope 等;第二,处理 bean 的子元素,主要是 <property> 标签,而 <property> 标签又有属性和子元素,且子元素类型更加丰富复杂,可能是&lt;map>&lt;set>&lt;list>&lt;array> 等。所以如果你们想学我家的祖传秘方,开个同样的摊子干倒我,也不是这么容易的哦。
经过上面的步骤,一个配置文件中的面筋 bean 就被处理包装成了半成品 BeanDefinitionHolder。
## 第三阶段:将 BeanDefinition 注册进 IoC 容器BeanDefinitionReaderUtils
妹妹在用神器 BeanDefinitionParserDelegate 经过一顿疯狂操作之后,将包装好的半成品 BeanDefinitionHolder 扔进传输机 BeanDefinitionReaderUtils并且输入哥哥给她的神秘人地址就继续处理下一个面筋 bean 咯。
之后,传输机将 BeanDefinitionHolder 的包装打开,分别取出 beanName面筋的唯一标识和 BeanDefinition面筋本筋传输的目的地是 BeanDefinitionRegistry 的工作室(这就是我前面给哥哥 beanDefinitionReader 的地址)。
这家工作室的 BeanDefinitionRegistry 其实就是我的影分身之一,因为我的祖先实现了这个接口。影分身 Registry 检查一下传输过来的 beanName面筋的唯一标识和 BeanDefinition面筋本筋如果没什么问题就把它们用根绳子系在一起扔进我的“王之面筋宝库”一个 ConcurrentHashMap<String, BeanDefinition>(64)也有人把我的“面筋宝库”称作“IoC 容器本器”,我也无可辩驳,谁让他们吃面筋付钱了呢。
就这样,每一种取出来的面筋都会经过这些处理。等到所有的面筋处理完了,也差不多到了傍晚,每到这时我就会拿起梳子和发油,对着镶满钻石的镜子,梳理整齐与徐峥同款的明星发型,唱着魔性的“我的烤面筋 ~”,骑着小车车,出摊咯 ~
面筋等原材料基本上都已经处理完毕,但把这些原材料变成程序员手中的“烤面筋”也是一门复杂而精细的手艺,老铁们记得 watch、star、fork素质三连一波下一期我将带领你们走进 spring 商业街的夜市,一起烤出香喷喷的面筋,成为这条 gai 上最亮的仔!

@ -1,27 +1,31 @@
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;作为一名初入职场的开发者,最开始是在逛B站刷视频时看到的一个spring源码阅读解析当时作为一个只知道SSH和CRUD的boy看完后心里就两个词儿“卧槽牛B啊”而且在去年秋招面试阿里时几乎每次都会被面试官问道“有阅读过什么开源框架吗”每次我都只能一脸便秘的“嗯…呃…啊…木得…”。这在我心里埋下了一个想法硬着头皮也要把spring框架源码读一遍,再不济也要看看猪是怎么跑的。
作为一名初入职场的开发者,最开始是在逛 B 站刷视频时看到的一个 Spring 源码阅读解析,当时作为一个只知道 SSH 和 CRUD 的 boy看完后心里就两个词儿“卧槽 B 啊!”而且在去年秋招面试阿里时几乎每次都会被面试官问道“有阅读过什么开源框架吗?”每次我都只能一脸便秘的“嗯…,呃…,啊…,木得…”。这在我心里埋下了一个想法,硬着头皮也要把 Spring 框架源码读一遍,再不济也要看看猪是怎么跑的。
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;从7月份开始到现在利用业余时间完成了spring核心实现IoC、DI、AOP及重要组件实现MVC、事务、JDBC的源码阅读并输出相关博客7篇在spring源码上做的详细注解也维护到了个人GitHub上并且将其整合到了开源学习社区Doocs上。
学习方法的话我个人比较喜欢先在B站上看相关视频知道怎么读从哪下口。然后自己买了本 计文柯老师的《Spring技术内幕》比对着从spring官网下载的源码包潜心研读。第一遍读什么都不懂按图索骥迷迷糊糊的读完了第二遍读就轻车熟路一些咯“卧槽原来如此”的感叹声也络绎不绝第三遍就能够在整体代码设计和细节实现两个不同的层次上去吸收spring框架的优点咯。
从 7 月份开始到现在,利用业余时间完成了 Spring 核心实现IoC、DI、AOP及重要组件实现MVC、事务、JDBC的源码阅读并输出相关博客 7 篇,在 Spring 源码上做的详细注解也维护到了个人 GitHub 上,并且将其整合到了开源学习社区 Doocs 上。
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;这三个月来阅读spring源码给我带来的提升主要在专业技能上但同时也辐射到了我的工作、学习、社交等方面。所以写这篇文章一方面是应“码农翻身”专栏——刘欣老师的建议做个经验谈另一方面也是对自己这三个月学习成果的总结。
学习方法的话,我个人比较喜欢先在 B 站上看相关视频,知道怎么读,从哪下口。然后自己买了本 计文柯老师的《Spring 技术内幕》,比对着从 Spring 官网下载的源码包潜心研读。第一遍读,什么都不懂,按图索骥,迷迷糊糊的读完了;第二遍读,就轻车熟路一些咯,“卧槽!原来如此!”的感叹声也络绎不绝;第三遍就能够在整体代码设计和细节实现两个不同的层次上去吸收 Spring 框架的优点咯。
这三个月来,阅读 Spring 源码给我带来的提升,主要在专业技能上,但同时也辐射到了我的工作、学习、社交等方面。所以,写这篇文章一方面是应“码农翻身”专栏——刘欣老师的建议,做个经验谈,另一方面也是对自己这三个月学习成果的总结。
下面我将分三个部分,谈一谈自己的经验。
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;下面我将分三个部分,谈一谈自己的经验。
### 一、工作方面(编码规范、编码能力、设计模式、英文阅读)
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;我所从事的行业做的是to B的业务产品底层平台的框架代码累累堆积成山很多框架都是零几年写的有的甚至比spring还早。且最近国产化、中台、云服务等概念都在不断落地中有框架源码的阅读经验让我能够更从容地面对公司研发的新框架所维护的产品适配华为高斯数据库时也更清楚可能是JDBC框架中哪里做了特殊处理所导致的问题。当然最主要的还是对个人编码规范的养成设计模式的理解应用英文阅读的能力提升。
我所从事的行业做的是 toB 的业务,产品底层平台的框架,代码累累,堆积成山,很多框架都是零几年写的,有的甚至比 Spring 还早。且最近国产化、中台、云服务等概念都在不断落地中,有框架源码的阅读经验,让我能够更从容地面对公司研发的新框架,所维护的产品适配华为高斯数据库时,也更清楚可能是 JDBC 框架中哪里做了特殊处理所导致的问题。当然,最主要的还是对个人编码规范的养成,设计模式的理解应用,英文阅读的能力提升。
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;作为一个初入职场的开发者编码规范是一个很重要的点能够让你写出的代码易于维护、阅读和理解。比如Spring框架虽然类图体系复杂丰富但对于类、方法、参数等的命名非常规范注释注解也非常严谨注重格式不会偷懒对于异常和日志的处理也具有很好的参考价值。比如之前产品中有遇到一个“将业务表单中的小数从科学计数法转换成普通计数法”数值过大的Double类型数字默认会以科学记数法显示这是用户无法接受的研读了复杂的业务代码之后发现填充到表单前的数据都是Object类型的且丢失了原本类型无法通过instanceof判断应该转成String还是Double这让我和我的师傅都有点头疼spring源码中有过一段以异常捕获机制处理逻辑代码的片段让我灵光乍现于是我直接将Object强转成Double并使其不做科学记数法的处理并将这段代码try住如果没抛异常就转换成了Double抛了异常就在catch中强转成String。
作为一个初入职场的开发者编码规范是一个很重要的点能够让你写出的代码易于维护、阅读和理解。比如Spring 框架虽然类图体系复杂丰富,但对于类、方法、参数等的命名非常规范;注释注解也非常严谨,注重格式,不会偷懒;对于异常和日志的处理也具有很好的参考价值。比如,之前产品中有遇到一个“将业务表单中的小数从科学计数法转换成普通计数法”(数值过大的 Double 类型数字默认会以科学记数法显示,这是用户无法接受的),研读了复杂的业务代码之后,发现填充到表单前的数据都是 Object 类型的,且丢失了原本类型,无法通过 instanceof 判断应该转成 String 还是 Double这让我和我的师傅都有点头疼 Spring 源码中有过一段以异常捕获机制处理逻辑代码的片段让我灵光乍现,于是我直接将 Object 强转成 Double 并使其不做科学记数法的处理,并将这段代码 try 住,如果没抛异常,就转换成了 Double抛了异常就在 catch 中强转成 String。
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;另外部门也经常会做代码评审规范的编码不但能够获得同事的认可一点一滴的细节也会使你的leader对你刮目相看。
另外,部门也经常会做代码评审,规范的编码不但能够获得同事的认可,一点一滴的细节也会使你的 leader 对你刮目相看。
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;从IoC的各顶层接口到中间一层一层的抽象类再到最后的实现类这一整套体系的设计和实现对自己在日常工作中设计某些功能的接口、抽象类和具体实现都带来了很有价值的参考设计模式和巧妙的编码技巧也渐渐变得触手可及。比如设计一个VO字段校验功能时会先定义一个顶层接口抽象出公共方法抽象类中有做必输项字段非空校验的在其中利用模板方法模式对公共功能做具体实现特性化功能写成抽象方法交由各子类具体实现即可。
从 IoC 的各顶层接口到中间一层一层的抽象类,再到最后的实现类,这一整套体系的设计和实现,对自己在日常工作中设计某些功能的接口、抽象类和具体实现,都带来了很有价值的参考,设计模式和巧妙的编码技巧也渐渐变得触手可及。比如,设计一个 VO 字段校验功能时,会先定义一个顶层接口,抽象出公共方法,抽象类中有做必输项字段非空校验的,在其中利用模板方法模式对公共功能做具体实现,特性化功能写成抽象方法交由各子类具体实现即可。
Spring 上很多接口和抽象类,其注解甚至比代码还多,我也经常尝试着去阅读理解这些注释,看看自己的理解与书上的差异,用这种方式来提升英文技术文档的阅读能力,往往更实在一些。
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Spring上很多接口和抽象类其注解甚至比代码还多我也经常尝试着去阅读理解这些注释看看自己的理解与书上的差异用这种方式来提升英文技术文档的阅读能力往往更实在一些。
### 二、学习方面(学习模式的构建、学以致用)
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;虽然是做技术的,但我也是一个很爱出去耍的人。构建好自己的学习模式能够让你更从容地面对工作和生活。不加班的情况下(所幸部门加班并不太多),我一般会在晚饭之后以及周日时间充电。不管是学技术还是其它什么东西,我认为 以视频为入口以业界公认的名书继续深入理解以社交圈的同行或网上社区为输出交流管道最后持久化到思维导图及学习文档中。Spring源码学习是我工作之后对自己学习模式构建的一个尝试构建起这种学习模式之后个人的工作和生活也变得更加协调平衡不至于在繁杂忙碌的工作中渐渐丧失学习能力。另外一个比较重要的就是看spring源码时经常能看到一些与公司框架有异曲同工之妙的编码技巧及实现比如异常的批量抛出ConcurrentHashMap初始化其容量ThreadLocal的使用等等这些都是在读spring源码之前很少会注意或使用的。
虽然是做技术的,但我也是一个很爱出去耍的人。构建好自己的学习模式能够让你更从容地面对工作和生活。不加班的情况下(所幸部门加班并不太多),我一般会在晚饭之后以及周日时间充电。不管是学技术还是其它什么东西,我认为 以视频为入口以业界公认的名书继续深入理解以社交圈的同行或网上社区为输出交流管道最后持久化到思维导图及学习文档中。Spring 源码学习是我工作之后对自己学习模式构建的一个尝试,构建起这种学习模式之后,个人的工作和生活也变得更加协调平衡,不至于在繁杂忙碌的工作中渐渐丧失学习能力。另外一个比较重要的就是,看 Spring 源码时经常能看到一些与公司框架有异曲同工之妙的编码技巧及实现比如异常的批量抛出ConcurrentHashMap 初始化其容量ThreadLocal 的使用等等,这些都是在读 Spring 源码之前很少会注意或使用的。
### 三、社交方面GitHub、事业部内部授课
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;对于我来说既然辛辛苦苦搞懂了一个技术那就一定得输出自己的理解和经验装波逼不然辛辛苦苦几个月什么产出都没有过一段时间又把学得给忘了这和被白嫖有什么区别。而输出知识的话当然要选一些比较优质的平台比如GayHubDoocs组织和其创建者就是我在GitHub上认识的这些大佬之所以牛逼能够成事必然有其原因加入他们的组织跟着混准能学到更多我想要的东西不仅仅是技术方面
对于我来说,既然辛辛苦苦搞懂了一个技术,那就一定得输出自己的理解和经验,装波逼,不然辛辛苦苦几个月,什么产出都没有,过一段时间又把学得给忘了,这和被白嫖有什么区别。而输出知识的话当然要选一些比较优质的平台,比如 GayHubDoocs 组织和其创建者就是我在 GitHub 上认识的,这些大佬之所以牛逼,能够成事,必然有其原因,加入他们的组织跟着混,准能学到更多我想要的东西(不仅仅是技术方面)。
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;另外,我所在的事业部也有一个“王者荣耀”的学习进阶活动,将自己的学习成果整理成简单、易于理解的内部授课也更容易获得同事的认可与信赖。
### 个人建议:
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;对于初级开发者学习spring源码来说我建议配合阿里的《Java开发手册》一起看因为编码能力和框架设计能力是需要很长时间的经验积累才能得到大幅提升的而编码规范则是我们最开始就能做到并做好的事情也是很多成熟公司越来越重视的东西。另外阿里的《Java开发手册》中不少规范都是参考了spring框架的这也从侧面体现了spring作为业界知名框架其编码的规范性是深受认可的。
另外,我所在的事业部也有一个“王者荣耀”的学习进阶活动,将自己的学习成果整理成简单、易于理解的内部授课也更容易获得同事的认可与信赖。
### 个人建议
对于初级开发者学习 Spring 源码来说我建议配合阿里的《Java 开发手册》一起看因为编码能力和框架设计能力是需要很长时间的经验积累才能得到大幅提升的而编码规范则是我们最开始就能做到并做好的事情也是很多成熟公司越来越重视的东西。另外阿里的《Java 开发手册》中不少规范都是参考了 Spring 框架的,这也从侧面体现了 Spring 作为业界知名框架,其编码的规范性是深受认可的。
Loading…
Cancel
Save