diff --git a/README.md b/README.md index eb3af38..bf31b9c 100644 --- a/README.md +++ b/README.md @@ -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) ### 编码规范 diff --git a/docs/Spring/AOP/AOP源码实现及分析.md b/docs/Spring/AOP/AOP源码实现及分析.md index bb577c1..1a133a0 100644 --- a/docs/Spring/AOP/AOP源码实现及分析.md +++ b/docs/Spring/AOP/AOP源码实现及分析.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 mappedNames = new LinkedList(); 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 } /** - * 自己定义了Pointcut,Advice则使用父类中的定义 + * 自己定义了 Pointcut,Advice 则使用父类中的定义 */ 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 - + @@ -104,23 +110,24 @@ AOP的实现代码中,主要使用了JDK动态代理,在特定场景下( - + myAdvisor ``` -#### 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,通过这个name(id) - //我们就可以从IoC容器中获取对应的实例化bean + //这里添加了 Advisor 链的调用,下面的 interceptorNames 是在配置文件中 + //通过 interceptorNames 进行配置的。由于每一个 Advisor 都是被配置为 bean 的, + //所以通过遍历 interceptorNames 得到的 name,其实就是 bean 的 id,通过这个 name(id) + //我们就可以从 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 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 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 getInterceptorsAndDynamicInterceptionAdvice(Method method, Class targetClass) { - //这里使用了缓存cached,如果缓存中有就从缓存中获取拦截器链 - //没有,则调用(DefaultAdvisorChainFactory)advisorChainFactory的 - //getInterceptorsAndDynamicInterceptionAdvice()方法进行获取,并缓存到cached + //这里使用了缓存 cached,如果缓存中有就从缓存中获取拦截器链 + //没有,则调用 (DefaultAdvisorChainFactory)advisorChainFactory 的 + //getInterceptorsAndDynamicInterceptionAdvice() 方法进行获取,并缓存到 cached MethodCacheKey cacheKey = new MethodCacheKey(method); List 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 getInterceptorsAndDynamicInterceptionAdvice( Advised config, Method method, Class targetClass) { - //advisor链已经在传进来的config中持有了,这里可以直接使用 - //advisor中持有切面 和 增强行为的引用 + //advisor 链已经在传进来的 config 中持有了,这里可以直接使用 + //advisor 中持有切面 和 增强行为的引用 List interceptorList = new ArrayList(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,通过这个name(id) - //我们就可以从IoC容器中获取对应的实例化bean + //这里添加了 Advisor 链的调用,下面的 interceptorNames 是在配置文件中 + //通过 interceptorNames 进行配置的。由于每一个 Advisor 都是被配置为 bean 的, + //所以通过遍历 interceptorNames 得到的 name,其实就是 bean 的 id,通过这个 name(id) + //我们就可以从 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 getInterceptorsAndDynamicInterceptionAdvice( Advised config, Method method, Class targetClass) { - //advisor链已经在传进来的config中持有了,这里可以直接使用 - //advisor中持有切面 和 增强行为的引用 + //advisor 链已经在传进来的 config 中持有了,这里可以直接使用 + //advisor 中持有切面 和 增强行为的引用 List interceptorList = new ArrayList(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 adapters = new ArrayList(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 interceptors = new ArrayList(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框架源码的阅读及个人理解一起看,会更有助于各位开发大佬理解,如果对你们有帮助的,还望各位老爷watch,star,fork,素质三连一波,地址: +回顾一下之前的代码,在 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 框架源码的阅读及个人理解一起看,会更有助于各位开发大佬理解,如果对你们有帮助的,还望各位老爷 watch,star,fork,素质三连一波,地址:https://github.com/AmyliaY/spring-aop-reading diff --git a/docs/Spring/AOP/JDK动态代理的实现原理解析.md b/docs/Spring/AOP/JDK动态代理的实现原理解析.md index a1ca610..17c6541 100644 --- a/docs/Spring/AOP/JDK动态代理的实现原理解析.md +++ b/docs/Spring/AOP/JDK动态代理的实现原理解析.md @@ -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 } -``` +``` \ No newline at end of file diff --git a/docs/Spring/IoC/一、BeanDefinition的资源定位过程.md b/docs/Spring/IoC/一、BeanDefinition的资源定位过程.md index 4b03abc..79be178 100644 --- a/docs/Spring/IoC/一、BeanDefinition的资源定位过程.md +++ b/docs/Spring/IoC/一、BeanDefinition的资源定位过程.md @@ -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解析器,SAX(simple API for XML)是另一种XML解析方法。相比于DOM,SAX速度更快,占用内存更小。 - // 它逐行扫描文档,一边扫描一边解析。相比于先将整个XML文件扫描近内存,再进行解析的DOM,SAX可以在解析文档的任意时刻停止解析,但操作也比DOM复杂。 + // 设置 SAX 解析器,SAX(simple API for XML)是另一种 XML 解析方法。相比于 DOM,SAX 速度更快,占用内存更小。 + // 它逐行扫描文档,一边扫描一边解析。相比于先将整个 XML 文件扫描近内存,再进行解析的 DOM,SAX 可以在解析文档的任意时刻停止解析,但操作也比 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)方法 + // 重载方法之一,调用了下面的 loadBeanDefinitions(String, Set) 方法 public int loadBeanDefinitions(String location) throws BeanDefinitionStoreException { return loadBeanDefinitions(location, null); } - // 获取在IoC容器初始化过程中设置的资源加载器 + // 获取在 IoC 容器初始化过程中设置的资源加载器 public int loadBeanDefinitions(String location, Set 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中加载BeanDefinition,loadCount为加载的BeanDefinition个数 - // 该loadBeanDefinitions()方法来自其implements的BeanDefinitionReader接口, - // 且本类是一个抽象类,并未对该方法进行实现。而是交由子类进行实现,如果是用xml文件进行 - // IoC容器初始化的,则调用XmlBeanDefinitionReader中的实现 + // 从 resource 中加载 BeanDefinition,loadCount 为加载的 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 的读取和载入了。 \ No newline at end of file diff --git a/docs/Spring/IoC/三、将BeanDefinition注册进IoC容器.md b/docs/Spring/IoC/三、将BeanDefinition注册进IoC容器.md index 8d82156..bf6fb44 100644 --- a/docs/Spring/IoC/三、将BeanDefinition注册进IoC容器.md +++ b/docs/Spring/IoC/三、将BeanDefinition注册进IoC容器.md @@ -1,26 +1,26 @@ -这篇文章分享一下spring IoC容器初始化第三部分的代码,也就是将前面解析得到的BeanDefinition注册进IoC容器,其实就是存入一个ConcurrentHashMap中。 +这篇文章分享一下 spring IoC 容器初始化第三部分的代码,也就是将前面解析得到的 BeanDefinition 注册进 IoC 容器,其实就是存入一个 ConcurrentHashMap 中。 -(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对象中元素的解析由BeanDefinitionParserDelegate实现 + // BeanDefinitionHolder 是对 BeanDefinition 的封装,即 BeanDefinition 的封装类 + // 对 Document 对象中 元素的解析由 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 beanDefinitionMap = new ConcurrentHashMap(64); -``` - - +``` \ No newline at end of file diff --git a/docs/Spring/IoC/二、将bean解析封装成BeanDefinition.md b/docs/Spring/IoC/二、将bean解析封装成BeanDefinition.md index a6456f9..813e6da 100644 --- a/docs/Spring/IoC/二、将bean解析封装成BeanDefinition.md +++ b/docs/Spring/IoC/二、将bean解析封装成BeanDefinition.md @@ -1,27 +1,27 @@ -接着上一篇的BeanDefinition资源定位开始讲。Spring IoC容器BeanDefinition解析过程就是把用户在配置文件中定义好的bean,解析并封装成容器可以装载的BeanDefinition,BeanDefinition是spring定义的基本数据结构,也是为了方便对bean进行管理和操作。 -(PS:可以结合我GitHub上对spring框架源码的阅读及个人理解一起看,会更有助于各位开发大佬理解。 +接着上一篇的 BeanDefinition 资源定位开始讲。Spring IoC 容器 BeanDefinition 解析过程就是把用户在配置文件中定义好的 bean,解析并封装成容器可以装载的 BeanDefinition,BeanDefinition 是 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解析器,SAX(simple API for XML)是另一种XML解析方法。相比于DOM,SAX速度更快,占用内存更小。 - // 它逐行扫描文档,一边扫描一边解析。相比于先将整个XML文件扫描近内存,再进行解析的DOM,SAX可以在解析文档的任意时刻停止解析,但操作也比DOM复杂。 + // 设置 SAX 解析器,SAX(simple API for XML)是另一种 XML 解析方法。相比于 DOM,SAX 速度更快,占用内存更小。 + // 它逐行扫描文档,一边扫描一边解析。相比于先将整个 XML 文件扫描近内存,再进行解析的 DOM,SAX 可以在解析文档的任意时刻停止解析,但操作也比 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)方法 + // 重载方法之一,调用了下面的 loadBeanDefinitions(String, Set) 方法 public int loadBeanDefinitions(String location) throws BeanDefinitionStoreException { return loadBeanDefinitions(location, null); } - // 获取在IoC容器初始化过程中设置的资源加载器 + // 获取在 IoC 容器初始化过程中设置的资源加载器 public int loadBeanDefinitions(String location, Set 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中加载BeanDefinition,loadCount为加载的BeanDefinition个数 - // 该loadBeanDefinitions()方法来自其implements的BeanDefinitionReader接口, - // 且本类是一个抽象类,并未对该方法进行实现。而是交由子类进行实现,如果是用xml文件进行 - // IoC容器初始化的,则调用XmlBeanDefinitionReader中的实现 + // 从 resource 中加载 BeanDefinition,loadCount 为加载的 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 } 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) { - // 如果元素节点是导入元素,进行导入解析 + // 如果元素节点是 导入元素,进行导入解析 if (delegate.nodeNameEquals(ele, IMPORT_ELEMENT)) { importBeanDefinitionResource(ele); } - // 如果元素节点是别名元素,进行别名解析 + // 如果元素节点是 别名元素,进行别名解析 else if (delegate.nodeNameEquals(ele, ALIAS_ELEMENT)) { processAliasRegistration(ele); } - // 元素节点既不是导入元素,也不是别名元素,即普通的元素, - // 按照Spring的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); } } - // 解析导入元素,从给定的导入路径加载Bean定义资源到Spring IoC容器中 + // 解析 导入元素,从给定的导入路径加载 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 actualResources = new LinkedHashSet(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()]); - // 在解析完元素之后,发送容器导入其他资源处理完成事件 + // 在解析完 元素之后,发送容器导入其他资源处理完成事件 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. */ - // 解析别名元素,为Bean向Spring IoC容器注册别名 + // 解析 别名元素,为 Bean 向 Spring IoC 容器注册别名 protected void processAliasRegistration(Element ele) { - // 获取别名元素中name的属性值 + // 获取 别名元素中 name 的属性值 String name = ele.getAttribute(NAME_ATTRIBUTE); - // 获取别名元素中alias的属性值 + // 获取 别名元素中 alias 的属性值 String alias = ele.getAttribute(ALIAS_ATTRIBUTE); boolean valid = true; - // 别名元素的name属性值为空 + // 别名元素的 name 属性值为空 if (!StringUtils.hasText(name)) { getReaderContext().error("Name must not be empty", ele); valid = false; } - // 别名元素的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); } - // 在解析完元素之后,发送容器别名处理完成事件 + // 在解析完 元素之后,发送容器别名处理完成事件 getReaderContext().fireAliasRegistered(name, alias, extractSource(ele)); } } - // 解析Bean定义资源Document对象的普通元素 + // 解析 Bean 定义资源 Document 对象的普通元素 protected void processBeanDefinition(Element ele, BeanDefinitionParserDelegate delegate) { - // BeanDefinitionHolder是对BeanDefinition的封装,即BeanDefinition的封装类 - // 对Document对象中元素的解析由BeanDefinitionParserDelegate实现 + // BeanDefinitionHolder 是对 BeanDefinition 的封装,即 BeanDefinition 的封装类 + // 对 Document 对象中 元素的解析由 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 - // 解析元素的入口 + // 解析 元素的入口 public BeanDefinitionHolder parseBeanDefinitionElement(Element ele) { return parseBeanDefinitionElement(ele, null); } - // 解析BeanDefinition资源文件中的元素,这个方法中主要处理元素的id,name和别名属性 + // 解析 BeanDefinition 资源文件中的 元素,这个方法中主要处理 元素的 id,name 和别名属性 public BeanDefinitionHolder parseBeanDefinitionElement(Element ele, BeanDefinition containingBean) { - // 获取元素中的id属性值 + // 获取 元素中的 id 属性值 String id = ele.getAttribute(ID_ATTRIBUTE); - // 获取元素中的name属性值 + // 获取 元素中的 name 属性值 String nameAttr = ele.getAttribute(NAME_ATTRIBUTE); - // 获取元素中的alias属性值 + // 获取 元素中的 alias 属性值 List aliases = new ArrayList(); - // 将元素中的所有name属性值存放到别名中 + // 将 元素中的所有 name 属性值存放到别名中 if (StringUtils.hasLength(nameAttr)) { String[] nameArr = StringUtils.tokenizeToStringArray(nameAttr, MULTI_VALUE_ATTRIBUTE_DELIMITERS); aliases.addAll(Arrays.asList(nameArr)); } String beanName = id; - // 如果元素中没有配置id属性时,将别名中的第一个值赋值给beanName + // 如果 元素中没有配置 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 } } - // 检查元素所配置的id或者name的唯一性,containingBean标识 - // 元素中是否包含子元素 + // 检查 元素所配置的 id 或者 name 的唯一性,containingBean 标识 + // 元素中是否包含子 元素 if (containingBean == null) { - // 检查元素所配置的id、name或者别名是否重复 + // 检查 元素所配置的 id、name 或者别名是否重复 checkNameUniqueness(beanName, aliases, ele); } - // 详细对元素中配置的Bean定义进行解析的地方 + // 详细对 元素中配置的 Bean 定义进行解析的地方 AbstractBeanDefinition beanDefinition = parseBeanDefinitionElement(ele, beanName, containingBean); if (beanDefinition != null) { if (!StringUtils.hasText(beanName)) { try { if (containingBean != null) { - // 如果元素中没有配置id、别名或者name,且没有包含子元素 - // 元素,为解析的Bean生成一个唯一beanName并注册 + // 如果 元素中没有配置 id、别名或者 name,且没有包含子元素 + // 元素,为解析的 Bean 生成一个唯一 beanName 并注册 beanName = BeanDefinitionReaderUtils.generateBeanName( beanDefinition, this.readerContext.getRegistry(), true); } else { - // 如果元素中没有配置id、别名或者name,且包含了子元素 - // 元素,为解析的Bean使用别名向IOC容器注册 + // 如果 元素中没有配置 id、别名或者 name,且包含了子元素 + // 元素,为解析的 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的id、name和别名等属性进行了处理,该方法中主要处理除这三个以外的其他属性数据 + // 详细对 元素中配置的 Bean 定义其他属性进行解析,由于上面的方法中已经对 + // Bean 的 id、name 和别名等属性进行了处理,该方法中主要处理除这三个以外的其他属性数据 public AbstractBeanDefinition parseBeanDefinitionElement( Element ele, String beanName, BeanDefinition containingBean) { - // 记录解析的 + // 记录解析的 this.parseState.push(new BeanEntry(beanName)); - // 这里只读取元素中配置的class名字,然后载入到BeanDefinition中去 - // 只是记录配置的class名字,不做实例化,对象的实例化在依赖注入时完成 + // 这里只读取 元素中配置的 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; - // 如果元素中配置了parent属性,则获取parent属性的值 + // 如果 元素中配置了 parent 属性,则获取 parent 属性的值 if (ele.hasAttribute(PARENT_ATTRIBUTE)) { parent = ele.getAttribute(PARENT_ATTRIBUTE); } - // 根据元素配置的class名称和parent属性值创建BeanDefinition - // 为载入Bean定义信息做准备 + // 根据 元素配置的 class 名称和 parent 属性值创建 BeanDefinition + // 为载入 Bean 定义信息做准备 AbstractBeanDefinition bd = createBeanDefinition(className, parent); - // 对当前的元素中配置的一些属性进行解析和设置,如配置的单态(singleton)属性等 + // 对当前的 元素中配置的一些属性进行解析和设置,如配置的单态 (singleton) 属性等 parseBeanDefinitionAttributes(ele, beanName, containingBean, bd); - // 为元素解析的Bean设置description信息 + // 为 元素解析的 Bean 设置 description 信息 bd.setDescription(DomUtils.getChildElementValueByTagName(ele, DESCRIPTION_ELEMENT)); - // 对元素的meta(元信息)属性解析 + // 对 元素的 meta(元信息) 属性解析 parseMetaElements(ele, bd); - // 对元素的lookup-method属性解析 + // 对 元素的 lookup-method 属性解析 parseLookupOverrideSubElements(ele, bd.getMethodOverrides()); - // 对元素的replaced-method属性解析 + // 对 元素的 replaced-method 属性解析 parseReplacedMethodSubElements(ele, bd.getMethodOverrides()); - // 解析元素的构造方法属性 + // 解析 元素的构造方法属性 parseConstructorArgElements(ele, bd); - // 解析元素所有的属性 + // 解析 元素所有的 属性 parsePropertyElements(ele, bd); - // 解析元素的qualifier属性 + // 解析 元素的 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(); } - // 解析元素出错时,返回null + // 解析 元素出错时,返回 null return null; } ``` -## 8、对bean的部分子元素进行解析的具体实现 +## 8、对 bean 的部分子元素进行解析的具体实现 ```java - // 解析元素中所有的子元素 + // 解析 元素中所有的 子元素 public void parsePropertyElements(Element beanEle, BeanDefinition bd) { - // 获取对应bean元素中所有的子元素,逐一解析 + // 获取对应 bean 元素中所有的 子元素,逐一解析 NodeList nl = beanEle.getChildNodes(); for (int i = 0; i < nl.getLength(); i++) { Node node = nl.item(i); - // 对子元素进行详细解析 + // 对 子元素进行详细解析 if (isCandidateElement(node) && nodeNameEquals(node, PROPERTY_ELEMENT)) { parsePropertyElement((Element) node, bd); } } } - // 详细解析元素 + // 详细解析 元素 public void parsePropertyElement(Element ele, BeanDefinition bd) { - // 获取元素的名字 + // 获取 元素的名字 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); - // 解析元素中的属性 + // 解析 元素中的属性 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) ? " element for property '" + propertyName + "'" : " element"; - // 获取的所有子元素,只能是其中一种类型:ref,value,list等 + // 获取 的所有子元素,只能是其中一种类型: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 {// 当前元素包含有子元素 + else {// 当前 元素包含有子元素 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; } - // 如果当前元素还有子元素 + // 如果当前 元素还有子元素 else if (subElement != null) { - // 解析的子元素 + // 解析 的子元素 return parsePropertySubElement(subElement, bd); } else { - // propery属性中既不是ref,也不是value属性,解析出错返回null + // propery 属性中既不是 ref,也不是 value 属性,解析出错返回 null error(elementName + " must specify a ref or value", ele); return null; } } - // 解析元素中ref,value或者集合等子元素 + // 解析 元素中 ref,value 或者集合等子元素 public Object parsePropertySubElement(Element ele, BeanDefinition bd) { return parsePropertySubElement(ele, bd, null); } public Object parsePropertySubElement(Element ele, BeanDefinition bd, String defaultValueType) { - // 如果没有使用Spring默认的命名空间,则使用用户自定义的规则解析 + // 如果 没有使用 Spring 默认的命名空间,则使用用户自定义的规则解析 // 内嵌元素 if (!isDefaultNamespace(ele)) { return parseNestedCustomElement(ele, bd); } - // 如果子元素是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; } - // 如果子元素是ref,ref中只能有以下3个属性:bean、local、parent + // 如果子元素是 ref,ref 中只能有以下 3 个属性:bean、local、parent else if (nodeNameEquals(ele, REF_ELEMENT)) { - // 获取元素中的bean属性值,引用其他解析的Bean的名称 - // 可以不再同一个Spring配置文件中,具体请参考Spring对ref的配置规则 + // 获取 元素中的 bean 属性值,引用其他解析的 Bean 的名称 + // 可以不再同一个 Spring 配置文件中,具体请参考 Spring 对 ref 的配置规则 String refName = ele.getAttribute(BEAN_REF_ATTRIBUTE); boolean toParent = false; if (!StringUtils.hasLength(refName)) { - // 获取元素中的local属性值,引用同一个Xml文件中配置 - // 的Bean的id,local和ref不同,local只能引用同一个配置文件中的Bean + // 获取 元素中的 local 属性值,引用同一个 Xml 文件中配置 + // 的 Bean 的 id,local 和 ref 不同,local 只能引用同一个配置文件中的 Bean refName = ele.getAttribute(LOCAL_REF_ATTRIBUTE); if (!StringUtils.hasLength(refName)) { - // 获取元素中parent属性值,引用父级容器中的Bean + // 获取 元素中 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(" element contains empty target attribute", ele); return null; } - // 创建ref类型数据,指向被引用的对象 + // 创建 ref 类型数据,指向被引用的对象 RuntimeBeanReference ref = new RuntimeBeanReference(refName, toParent); // 设置引用类型值是被当前子元素所引用 ref.setSource(extractSource(ele)); return ref; } - // 如果子元素是,使用解析ref元素的方法解析 + // 如果子元素是 ,使用解析 ref 元素的方法解析 else if (nodeNameEquals(ele, IDREF_ELEMENT)) { return parseIdRefElement(ele); } - // 如果子元素是,使用解析value元素的方法解析 + // 如果子元素是 ,使用解析 value 元素的方法解析 else if (nodeNameEquals(ele, VALUE_ELEMENT)) { return parseValueElement(ele, defaultValueType); } - //如果子元素是null,为设置一个封装null值的字符串数据 + //如果子元素是 null,为 设置一个封装 null 值的字符串数据 else if (nodeNameEquals(ele, NULL_ELEMENT)) { TypedStringValue nullHolder = new TypedStringValue(null); nullHolder.setSource(extractSource(ele)); return nullHolder; } - // 如果子元素是,使用解析array集合子元素的方法解析 + // 如果子元素是 ,使用解析 array 集合子元素的方法解析 else if (nodeNameEquals(ele, ARRAY_ELEMENT)) { return parseArrayElement(ele, bd); } - // 如果子元素是,使用解析list集合子元素的方法解析 + // 如果子元素是 ,使用解析 list 集合子元素的方法解析 else if (nodeNameEquals(ele, LIST_ELEMENT)) { return parseListElement(ele, bd); } - // 如果子元素是,使用解析set集合子元素的方法解析 + // 如果子元素是 ,使用解析 set 集合子元素的方法解析 else if (nodeNameEquals(ele, SET_ELEMENT)) { return parseSetElement(ele, bd); } - // 如果子元素是,使用解析map集合子元素的方法解析 + // 如果子元素是 ,使用解析 map 集合子元素的方法解析 else if (nodeNameEquals(ele, MAP_ELEMENT)) { return parseMapElement(ele, bd); } - // 如果子元素是,使用解析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; } } - // 解析集合子元素 + // 解析 集合子元素 public List parseListElement(Element collectionEle, BeanDefinition bd) { - // 获取元素中的value-type属性,即获取集合元素的数据类型 + // 获取 元素中的 value-type 属性,即获取集合元素的数据类型 String defaultElementType = collectionEle.getAttribute(VALUE_TYPE_ATTRIBUTE); - // 获取集合元素中的所有子节点 + // 获取 集合元素中的所有子节点 NodeList nl = collectionEle.getChildNodes(); - // Spring中将List封装为ManagedList + // Spring 中将 List 封装为 ManagedList ManagedList target = new ManagedList(nl.getLength()); target.setSource(extractSource(collectionEle)); // 设置集合目标数据类型 target.setElementTypeName(defaultElementType); target.setMergeEnabled(parseMergeAttribute(collectionEle)); - // 具体的元素解析 + // 具体的 元素解析 parseCollectionElements(nl, target, bd, defaultElementType); return target; } - // 具体解析集合元素,都使用该方法解析 + // 具体解析 集合元素, 都使用该方法解析 protected void parseCollectionElements(NodeList elementNodes, Collection 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 容器中咯。 \ No newline at end of file diff --git a/docs/Spring/IoC/四、依赖注入(DI).md b/docs/Spring/IoC/四、依赖注入(DI).md index 16d9267..2bc0370 100644 --- a/docs/Spring/IoC/四、依赖注入(DI).md +++ b/docs/Spring/IoC/四、依赖注入(DI).md @@ -1,51 +1,51 @@ -前面我们主要分析了FileSystemXmlApplicationContext这个具体的IoC容器的初始化源码实现,在IoC容器中建立了BeanDefinition的数据映射,将其和beanName一起绑定在一个ConcurrentHashMap中。现在我们来看一下spring是如何将IoC容器中的Bean根据配置关联在一起的。 -Spring中触发IoC容器“依赖注入”的方式有两种,一个是通过getBean()向容器索要bean时触发依赖注入;另一个是给bean配置lazy-init属性,spring会自动调用此bean的getBean()方法,提前完成依赖注入。总的来说,想提高运行时获取bean的效率,可以考虑配置此属性。 -下面我将分别解读这两种依赖注入的触发方式,先看getBean()的,因为lazy-init最后也是通过调用getBean完成的依赖注入。 +前面我们主要分析了 FileSystemXmlApplicationContext 这个具体的 IoC 容器的初始化源码实现,在 IoC 容器中建立了 BeanDefinition 的数据映射,将其和 beanName 一起绑定在一个 ConcurrentHashMap 中。现在我们来看一下 spring 是如何将 IoC 容器中的 Bean 根据配置关联在一起的。 +Spring 中触发 IoC 容器“依赖注入”的方式有两种,一个是通过 getBean() 向容器索要 bean 时触发依赖注入;另一个是给 bean 配置 lazy-init 属性,spring 会自动调用此 bean 的 getBean() 方法,提前完成依赖注入。总的来说,想提高运行时获取 bean 的效率,可以考虑配置此属性。 +下面我将分别解读这两种依赖注入的触发方式,先看 getBean() 的,因为 lazy-init 最后也是通过调用 getBean 完成的依赖注入。 -(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、AbstractBeanFactory中的getBean()系列方法及doGetBean()具体实现 +## 1、AbstractBeanFactory 中的 getBean() 系列方法及 doGetBean() 具体实现 ```java //--------------------------------------------------------------------- - // BeanFactory接口的实现,下列的getBean()方法不论是哪种重载,最后都会走 - // doGetBean(final String name, final Class requiredType, final Object[] args, boolean typeCheckOnly)的具体实现 + // BeanFactory 接口的实现,下列的 getBean() 方法不论是哪种重载,最后都会走 + // doGetBean(final String name, final Class requiredType, final Object[] args, boolean typeCheckOnly) 的具体实现 //--------------------------------------------------------------------- - // 获取IOC容器中指定名称的Bean + // 获取 IOC 容器中指定名称的 Bean public Object getBean(String name) throws BeansException { return doGetBean(name, null, null, false); } - // 获取IOC容器中指定名称和类型的Bean + // 获取 IOC 容器中指定名称和类型的 Bean public T getBean(String name, Class requiredType) throws BeansException { return doGetBean(name, requiredType, null, false); } - // 获取IOC容器中指定名称和参数的Bean + // 获取 IOC 容器中指定名称和参数的 Bean public Object getBean(String name, Object... args) throws BeansException { return doGetBean(name, null, args, false); } - // 获取IOC容器中指定名称、类型和参数的Bean + // 获取 IOC 容器中指定名称、类型和参数的 Bean public T getBean(String name, Class requiredType, Object... args) throws BeansException { return doGetBean(name, requiredType, args, false); } - // 真正实现向IOC容器获取Bean的功能,也是触发依赖注入(DI)功能的地方 + // 真正实现向 IOC 容器获取 Bean 的功能,也是触发依赖注入 (DI) 功能的地方 @SuppressWarnings("unchecked") protected T doGetBean(final String name, final Class requiredType, final Object[] args, boolean typeCheckOnly) throws BeansException { - // 根据用户指定的名称获取IoC容器中与BeanDefinition对应的beanName - // 如果指定的是别名,则将别名转换为规范的beanName + // 根据用户指定的名称获取 IoC 容器中与 BeanDefinition 对应的 beanName + // 如果指定的是别名,则将别名转换为规范的 beanName final String beanName = transformedBeanName(name); Object bean; - // 先查看缓存中是否有对应的,已创建的单例Bean,对于单例Bean,整个IOC容器中只创建一次 + // 先查看缓存中是否有对应的,已创建的单例 Bean,对于单例 Bean,整个 IOC 容器中只创建一次 Object sharedInstance = getSingleton(beanName); if (sharedInstance != null && args == null) { if (logger.isDebugEnabled()) { @@ -57,8 +57,8 @@ spring-context https://github.com/AmyliaY/spring-context-reading logger.debug("Returning cached instance of singleton bean '" + beanName + "'"); } } - // 获取给定Bean的实例对象,主要是完成FactoryBean的相关处理 - // 注意:BeanFactory本质上是一个IoC容器,而FactoryBean是IoC容器中一种特殊的工厂bean + // 获取给定 Bean 的实例对象,主要是完成 FactoryBean 的相关处理 + // 注意:BeanFactory 本质上是一个 IoC 容器,而 FactoryBean 是 IoC 容器中一种特殊的工厂 bean // 能够生产其他对象,注意两者之间的区别 bean = getObjectForBeanInstance(sharedInstance, name, beanName, null); } @@ -70,10 +70,10 @@ spring-context https://github.com/AmyliaY/spring-context-reading // 获取当前容器的父容器 BeanFactory parentBeanFactory = getParentBeanFactory(); - // 如果当前容器中没有指定的bean,且当前容器的父容器不为空 + // 如果当前容器中没有指定的 bean,且当前容器的父容器不为空 // 则从父容器中去找,如果父容器也没有,则沿着当前容器的继承体系一直向上查找 if (parentBeanFactory != null && !containsBeanDefinition(beanName)) { - // 解析指定Bean名称的原始名称 + // 解析指定 Bean 名称的原始名称 String nameToLookup = originalBeanName(name); if (args != null) { // 委派父级容器根据指定名称和显式的参数查找 @@ -85,37 +85,37 @@ spring-context https://github.com/AmyliaY/spring-context-reading } } - // 创建的Bean是否需要进行类型验证,一般不需要 + // 创建的 Bean 是否需要进行类型验证,一般不需要 if (!typeCheckOnly) { - // 向容器标记指定的Bean已经被创建 + // 向容器标记指定的 Bean 已经被创建 markBeanAsCreated(beanName); } try { - // 根据beanName获取对应的RootBeanDefinition + // 根据 beanName 获取对应的 RootBeanDefinition final RootBeanDefinition mbd = getMergedLocalBeanDefinition(beanName); checkMergedBeanDefinition(mbd, beanName, args); - // 获取当前Bean依赖的所有Bean,下面的getBean(dependsOnBean)方法会触发getBean()的递归调用, - // 直到取到一个不依赖任何其它bean的bean为止 + // 获取当前 Bean 依赖的所有 Bean,下面的 getBean(dependsOnBean) 方法会触发 getBean() 的递归调用, + // 直到取到一个不依赖任何其它 bean 的 bean 为止 String[] dependsOn = mbd.getDependsOn(); if (dependsOn != null) { for (String dependsOnBean : dependsOn) { - // 递归调用getBean()方法,获取当前Bean所依赖的bean + // 递归调用 getBean() 方法,获取当前 Bean 所依赖的 bean getBean(dependsOnBean); - // 把当前bean所依赖的bean进行注入 - //(也就是通过setter或构造方法将依赖的bean赋值给当前bean对应的属性) + // 把当前 bean 所依赖的 bean 进行注入 + //(也就是通过 setter 或构造方法将依赖的 bean 赋值给当前 bean 对应的属性) registerDependentBean(dependsOnBean, beanName); } } - // 创建单例模式bean的实例对象 + // 创建单例模式 bean 的实例对象 if (mbd.isSingleton()) { - // 这里使用了一个匿名内部类,创建Bean实例对象,并且注册给所依赖的对象 + // 这里使用了一个匿名内部类,创建 Bean 实例对象,并且注册给所依赖的对象 sharedInstance = getSingleton(beanName, new ObjectFactory() { public Object getObject() throws BeansException { try { - // 创建一个指定Bean实例对象,如果有父级继承,则合并子类和父类的定义 + // 创建一个指定 Bean 实例对象,如果有父级继承,则合并子类和父类的定义 return createBean(beanName, mbd, args); } catch (BeansException ex) { @@ -124,36 +124,36 @@ spring-context https://github.com/AmyliaY/spring-context-reading } } }); - // 获取给定Bean的实例对象 + // 获取给定 Bean 的实例对象 bean = getObjectForBeanInstance(sharedInstance, name, beanName, mbd); } - // IoC容器创建原型模式Bean实例对象 + // IoC 容器创建原型模式 Bean 实例对象 else if (mbd.isPrototype()) { - // 原型模式(Prototype)是每次都会创建一个新的对象 + // 原型模式 (Prototype) 是每次都会创建一个新的对象 Object prototypeInstance = null; try { - // 回调beforePrototypeCreation方法,默认的功能是注册当前创建的原型对象 + // 回调 beforePrototypeCreation 方法,默认的功能是注册当前创建的原型对象 beforePrototypeCreation(beanName); - // 创建指定Bean对象实例 + // 创建指定 Bean 对象实例 prototypeInstance = createBean(beanName, mbd, args); } finally { - // 回调afterPrototypeCreation方法,默认的功能告诉IoC容器指定Bean的原型对象不再创建了 + // 回调 afterPrototypeCreation 方法,默认的功能告诉 IoC 容器指定 Bean 的原型对象不再创建了 afterPrototypeCreation(beanName); } - // 获取给定Bean的实例对象 + // 获取给定 Bean 的实例对象 bean = getObjectForBeanInstance(prototypeInstance, name, beanName, mbd); } - // 要创建的Bean既不是单例模式,也不是原型模式,则根据Bean定义资源中 - // 配置的生命周期范围,选择实例化Bean的合适方法,这种在Web应用程序中 - // 比较常用,如:request、session、application等的生命周期 + // 要创建的 Bean 既不是单例模式,也不是原型模式,则根据 Bean 定义资源中 + // 配置的生命周期范围,选择实例化 Bean 的合适方法,这种在 Web 应用程序中 + // 比较常用,如:request、session、application 等的生命周期 else { - // 获取此bean生命周期的范围 + // 获取此 bean 生命周期的范围 String scopeName = mbd.getScope(); final Scope scope = this.scopes.get(scopeName); - // Bean定义资源中没有配置生命周期范围,则Bean定义不合法 + // Bean 定义资源中没有配置生命周期范围,则 Bean 定义不合法 if (scope == null) { throw new IllegalStateException("No Scope registered for scope '" + scopeName + "'"); } @@ -170,7 +170,7 @@ spring-context https://github.com/AmyliaY/spring-context-reading } } }); - // 获取给定Bean的实例对象 + // 获取给定 Bean 的实例对象 bean = getObjectForBeanInstance(scopedInstance, name, beanName, mbd); } catch (IllegalStateException ex) { @@ -187,7 +187,7 @@ spring-context https://github.com/AmyliaY/spring-context-reading } } - // 对创建的bean实例对象进行非空验证和类型检查,如果没问题就返回这个已经完成依赖注入的bean + // 对创建的 bean 实例对象进行非空验证和类型检查,如果没问题就返回这个已经完成依赖注入的 bean if (requiredType != null && bean != null && !requiredType.isAssignableFrom(bean.getClass())) { try { return getTypeConverter().convertIfNecessary(bean, requiredType); @@ -203,11 +203,11 @@ spring-context https://github.com/AmyliaY/spring-context-reading return (T) bean; } ``` -总的来说,getBean()方法是依赖注入的起点,之后会调用createBean(),根据BeanDefinition的定义生成bean对象,下面我们看看AbstractBeanFactory的子类AbstractAutowireCapableBeanFactory中对createBean()的具体实现。 +总的来说,getBean() 方法是依赖注入的起点,之后会调用 createBean(),根据 BeanDefinition 的定义生成 bean 对象,下面我们看看 AbstractBeanFactory 的子类 AbstractAutowireCapableBeanFactory 中对 createBean() 的具体实现。 -## 2、AbstractAutowireCapableBeanFactory中的createBean()和doCreateBean()具体实现 +## 2、AbstractAutowireCapableBeanFactory 中的 createBean() 和 doCreateBean() 具体实现 ```java - // 创建指定的bean实例对象 + // 创建指定的 bean 实例对象 @Override protected Object createBean(final String beanName, final RootBeanDefinition mbd, final Object[] args) throws BeanCreationException { @@ -215,11 +215,11 @@ spring-context https://github.com/AmyliaY/spring-context-reading if (logger.isDebugEnabled()) { logger.debug("Creating instance of bean '" + beanName + "'"); } - // 判断需要创建的Bean是否可以实例化,是否可以通过当前的类加载器加载 + // 判断需要创建的 Bean 是否可以实例化,是否可以通过当前的类加载器加载 resolveBeanClass(mbd, beanName); try { - // 校验和准备Bean中的方法覆盖 + // 校验和准备 Bean 中的方法覆盖 mbd.prepareMethodOverrides(); } catch (BeanDefinitionValidationException ex) { @@ -228,7 +228,7 @@ spring-context https://github.com/AmyliaY/spring-context-reading } try { - // 如果Bean配置了后置处理器PostProcessor,则这里返回一个proxy代理对象 + // 如果 Bean 配置了后置处理器 PostProcessor,则这里返回一个 proxy 代理对象 Object bean = resolveBeforeInstantiation(beanName, mbd); if (bean != null) { return bean; @@ -239,7 +239,7 @@ spring-context https://github.com/AmyliaY/spring-context-reading "BeanPostProcessor before instantiation of bean failed", ex); } - // 创建Bean实例对象的具体实现 + // 创建 Bean 实例对象的具体实现 Object beanInstance = doCreateBean(beanName, mbd, args); if (logger.isDebugEnabled()) { logger.debug("Finished creating instance of bean '" + beanName + "'"); @@ -247,12 +247,12 @@ spring-context https://github.com/AmyliaY/spring-context-reading return beanInstance; } - // 创建Bean实例对象的具体实现,spring中以do开头的都是方法的具体实现 + // 创建 Bean 实例对象的具体实现,spring 中以 do 开头的都是方法的具体实现 protected Object doCreateBean(final String beanName, final RootBeanDefinition mbd, final Object[] args) { - // 封装被创建的Bean对象 + // 封装被创建的 Bean 对象 BeanWrapper instanceWrapper = null; - // 如果这个bean是单例的,则从缓存中获取这个BeanWrapper实例并清除 + // 如果这个 bean 是单例的,则从缓存中获取这个 BeanWrapper 实例并清除 if (mbd.isSingleton()) { instanceWrapper = this.factoryBeanInstanceCache.remove(beanName); } @@ -270,7 +270,7 @@ spring-context https://github.com/AmyliaY/spring-context-reading final Object bean = (instanceWrapper != null ? instanceWrapper.getWrappedInstance() : null); Class beanType = (instanceWrapper != null ? instanceWrapper.getWrappedClass() : null); - // 调用PostProcessor后置处理器 + // 调用 PostProcessor 后置处理器 synchronized (mbd.postProcessingLock) { if (!mbd.postProcessed) { applyMergedBeanDefinitionPostProcessors(mbd, beanType, beanName); @@ -278,7 +278,7 @@ spring-context https://github.com/AmyliaY/spring-context-reading } } - // 向容器中缓存单例模式的Bean对象,以防循环引用 + // 向容器中缓存单例模式的 Bean 对象,以防循环引用 boolean earlySingletonExposure = (mbd.isSingleton() && this.allowCircularReferences && isSingletonCurrentlyInCreation(beanName)); if (earlySingletonExposure) { @@ -294,20 +294,20 @@ spring-context https://github.com/AmyliaY/spring-context-reading }); } - // Bean对象的初始化,依赖注入在此触发 - // 这个exposedObject在初始化完成之后,将返回作为依赖注入完成后的Bean + // Bean 对象的初始化,依赖注入在此触发 + // 这个 exposedObject 在初始化完成之后,将返回作为依赖注入完成后的 Bean Object exposedObject = bean; try { /** * !!!!!!!!!!!!!!!!!!!!!!!!!!! - * 把生成的bean对象的依赖关系设置好,完成整个依赖注入过程 + * 把生成的 bean 对象的依赖关系设置好,完成整个依赖注入过程 * !!!!!!!!!!!!!!!!!!!!!!!!!!! */ populateBean(beanName, mbd, instanceWrapper); if (exposedObject != null) { - // 初始化Bean对象 - // 在对Bean实例对象生成和依赖注入完成以后,开始对Bean实例对象 - // 进行初始化 ,为Bean实例对象应用BeanPostProcessor后置处理器 + // 初始化 Bean 对象 + // 在对 Bean 实例对象生成和依赖注入完成以后,开始对 Bean 实例对象 + // 进行初始化 ,为 Bean 实例对象应用 BeanPostProcessor 后置处理器 exposedObject = initializeBean(beanName, exposedObject, mbd); } } @@ -321,21 +321,21 @@ spring-context https://github.com/AmyliaY/spring-context-reading } if (earlySingletonExposure) { - // 获取指定名称的已注册的单例模式Bean对象 + // 获取指定名称的已注册的单例模式 Bean 对象 Object earlySingletonReference = getSingleton(beanName, false); if (earlySingletonReference != null) { - // 如果根据名称获取的已注册的Bean和正在实例化的Bean是同一个 + // 如果根据名称获取的已注册的 Bean 和正在实例化的 Bean 是同一个 if (exposedObject == bean) { - // 当前实例化的Bean初始化完成 + // 当前实例化的 Bean 初始化完成 exposedObject = earlySingletonReference; } - // 如果当前Bean依赖其他Bean,并且当发生循环引用时不允许新创建实例对象 + // 如果当前 Bean 依赖其他 Bean,并且当发生循环引用时不允许新创建实例对象 else if (!this.allowRawInjectionDespiteWrapping && hasDependentBean(beanName)) { String[] dependentBeans = getDependentBeans(beanName); Set actualDependentBeans = new LinkedHashSet(dependentBeans.length); - // 获取当前Bean所依赖的其他Bean + // 获取当前 Bean 所依赖的其他 Bean for (String dependentBean : dependentBeans) { - // 对依赖Bean进行类型检查 + // 对依赖 Bean 进行类型检查 if (!removeSingletonIfCreatedForTypeCheckOnly(dependentBean)) { actualDependentBeans.add(dependentBean); } @@ -354,7 +354,7 @@ spring-context https://github.com/AmyliaY/spring-context-reading } try { - // 注册完成依赖注入的Bean + // 注册完成依赖注入的 Bean registerDisposableBeanIfNecessary(beanName, bean, mbd); } catch (BeanDefinitionValidationException ex) { @@ -366,21 +366,21 @@ spring-context https://github.com/AmyliaY/spring-context-reading } ``` -从源码中可以看到createBeanInstance()和populateBean()这两个方法与依赖注入的实现非常密切,createBeanInstance()方法中生成了Bean所包含的Java对象,populateBean()方法对这些生成的bean对象之间的依赖关系进行了处理。下面我们先看一下createBeanInstance()方法的实现。 +从源码中可以看到 createBeanInstance() 和 populateBean() 这两个方法与依赖注入的实现非常密切,createBeanInstance() 方法中生成了 Bean 所包含的 Java 对象,populateBean() 方法对这些生成的 bean 对象之间的依赖关系进行了处理。下面我们先看一下 createBeanInstance() 方法的实现。 -## 3、createBeanInstance()方法的具体实现 +## 3、createBeanInstance() 方法的具体实现 ```java - // 创建Bean的实例对象 + // 创建 Bean 的实例对象 protected BeanWrapper createBeanInstance(String beanName, RootBeanDefinition mbd, Object[] args) { - // 检查确认Bean是可实例化的 + // 检查确认 Bean 是可实例化的 Class beanClass = resolveBeanClass(mbd, beanName); if (beanClass != null && !Modifier.isPublic(beanClass.getModifiers()) && !mbd.isNonPublicAccessAllowed()) { throw new BeanCreationException(mbd.getResourceDescription(), beanName, "Bean class isn't public, and non-public access not allowed: " + beanClass.getName()); } - // 使用工厂方法对Bean进行实例化 + // 使用工厂方法对 Bean 进行实例化 if (mbd.getFactoryMethodName() != null) { return instantiateUsingFactoryMethod(beanName, mbd, args); } @@ -399,7 +399,7 @@ spring-context https://github.com/AmyliaY/spring-context-reading if (resolved) { if (autowireNecessary) { // 配置了自动装配属性,使用容器的自动装配实例化, - // 即,根据参数类型匹配Bean的构造方法 + // 即,根据参数类型匹配 Bean 的构造方法 return autowireConstructor(beanName, mbd, null, null); } else { @@ -408,7 +408,7 @@ spring-context https://github.com/AmyliaY/spring-context-reading } } - // 使用Bean的构造方法进行实例化 + // 使用 Bean 的构造方法进行实例化 Constructor[] ctors = determineConstructorsFromBeanPostProcessors(beanClass, beanName); if (ctors != null || mbd.getResolvedAutowireMode() == RootBeanDefinition.AUTOWIRE_CONSTRUCTOR || @@ -420,12 +420,12 @@ spring-context https://github.com/AmyliaY/spring-context-reading return instantiateBean(beanName, mbd); } - // 使用默认的无参构造方法实例化Bean对象 + // 使用默认的无参构造方法实例化 Bean 对象 protected BeanWrapper instantiateBean(final String beanName, final RootBeanDefinition mbd) { try { Object beanInstance; final BeanFactory parent = this; - // 获取系统的安全管理接口,JDK标准的安全管理API + // 获取系统的安全管理接口,JDK 标准的安全管理 API if (System.getSecurityManager() != null) { // 这里是一个匿名内置类,根据实例化策略创建实例对象 beanInstance = AccessController.doPrivileged(new PrivilegedAction() { @@ -438,7 +438,7 @@ spring-context https://github.com/AmyliaY/spring-context-reading /** * !!!!!!!!!!!!!! - * 使用初始化策略实例化Bean对象 + * 使用初始化策略实例化 Bean 对象 * !!!!!!!!!!!!!! */ beanInstance = getInstantiationStrategy().instantiate(mbd, beanName, parent); @@ -453,29 +453,29 @@ spring-context https://github.com/AmyliaY/spring-context-reading } ``` -从源码中我们可以看到其调用了SimpleInstantiationStrategy实现类来生成bean对象,这个类是spring用来生成bean对象的默认类,它提供了两种策略来实例化bean对象,一种是利用Java的反射机制,另一种是直接使用CGLIB。 -## 4、SimpleInstantiationStrategy中的instantiate()方法实现 +从源码中我们可以看到其调用了 SimpleInstantiationStrategy 实现类来生成 bean 对象,这个类是 spring 用来生成 bean 对象的默认类,它提供了两种策略来实例化 bean 对象,一种是利用 Java 的反射机制,另一种是直接使用 CGLIB。 +## 4、SimpleInstantiationStrategy 中的 instantiate() 方法实现 ```java - // 使用初始化策略实例化Bean对象 + // 使用初始化策略实例化 Bean 对象 public Object instantiate(RootBeanDefinition beanDefinition, String beanName, BeanFactory owner) { - // 如果Bean定义中没有方法覆盖,则使用Java的反射机制实例化对象,否则使用CGLIB + // 如果 Bean 定义中没有方法覆盖,则使用 Java 的反射机制实例化对象,否则使用 CGLIB if (beanDefinition.getMethodOverrides().isEmpty()) { Constructor constructorToUse; synchronized (beanDefinition.constructorArgumentLock) { - // 获取对象的构造方法或生成对象的工厂方法对bean进行实例化 + // 获取对象的构造方法或生成对象的工厂方法对 bean 进行实例化 constructorToUse = (Constructor) beanDefinition.resolvedConstructorOrFactoryMethod; // 如果前面没有获取到构造方法,则通过反射获取 if (constructorToUse == null) { - // 使用JDK的反射机制,判断要实例化的Bean是否是接口 + // 使用 JDK 的反射机制,判断要实例化的 Bean 是否是接口 final Class clazz = beanDefinition.getBeanClass(); - // 如果clazz是一个接口,直接抛出异常 + // 如果 clazz 是一个接口,直接抛出异常 if (clazz.isInterface()) { throw new BeanInstantiationException(clazz, "Specified class is an interface"); } try { if (System.getSecurityManager() != null) { - // 这里是一个匿名内置类,使用反射机制获取Bean的构造方法 + // 这里是一个匿名内置类,使用反射机制获取 Bean 的构造方法 constructorToUse = AccessController.doPrivileged(new PrivilegedExceptionAction() { public Constructor run() throws Exception { return clazz.getDeclaredConstructor((Class[]) null); @@ -492,14 +492,14 @@ spring-context https://github.com/AmyliaY/spring-context-reading } } } - // 使用BeanUtils实例化,通过反射机制调用”构造方法.newInstance(arg)”来进行实例化 + // 使用 BeanUtils 实例化,通过反射机制调用”构造方法.newInstance(arg)”来进行实例化 return BeanUtils.instantiateClass(constructorToUse); } else { /** * !!!!!!!!!!!!!! - * 使用CGLIB来实例化对象 - * 调用了CglibSubclassingInstantiationStrategy中的实现 + * 使用 CGLIB 来实例化对象 + * 调用了 CglibSubclassingInstantiationStrategy 中的实现 * !!!!!!!!!!!!!! */ return instantiateWithMethodInjection(beanDefinition, beanName, owner); @@ -507,15 +507,15 @@ spring-context https://github.com/AmyliaY/spring-context-reading } ``` -在SimpleInstantiationStrategy的子类CglibSubclassingInstantiationStrategy中可以看到使用CGLIB进行实例化的源码实现。 -## 5、CglibSubclassingInstantiationStrategy中使用CGLIB进行实例化的源码实现 +在 SimpleInstantiationStrategy 的子类 CglibSubclassingInstantiationStrategy 中可以看到使用 CGLIB 进行实例化的源码实现。 +## 5、CglibSubclassingInstantiationStrategy 中使用 CGLIB 进行实例化的源码实现 ```java - // 下面两个方法都通过实例化自己的私有静态内部类CglibSubclassCreator, - // 然后调用该内部类对象的实例化方法instantiate()完成实例化 + // 下面两个方法都通过实例化自己的私有静态内部类 CglibSubclassCreator, + // 然后调用该内部类对象的实例化方法 instantiate() 完成实例化 protected Object instantiateWithMethodInjection( RootBeanDefinition beanDefinition, String beanName, BeanFactory owner) { - // 必须生成cglib子类 + // 必须生成 cglib 子类 return new CglibSubclassCreator(beanDefinition, owner).instantiate(null, null); } @@ -528,7 +528,7 @@ spring-context https://github.com/AmyliaY/spring-context-reading } /** - * 为避免3.2之前的Spring版本中的外部cglib依赖而创建的内部类。 + * 为避免 3.2 之前的 Spring 版本中的外部 cglib 依赖而创建的内部类。 */ private static class CglibSubclassCreator { @@ -543,11 +543,11 @@ spring-context https://github.com/AmyliaY/spring-context-reading this.owner = owner; } - // 使用CGLIB进行Bean对象实例化 + // 使用 CGLIB 进行 Bean 对象实例化 public Object instantiate(Constructor ctor, Object[] args) { - // 实例化Enhancer对象,并为Enhancer对象设置父类,生成Java对象的参数,比如:基类、回调方法等 + // 实例化 Enhancer 对象,并为 Enhancer 对象设置父类,生成 Java 对象的参数,比如:基类、回调方法等 Enhancer enhancer = new Enhancer(); - // 将Bean本身作为其父类 + // 将 Bean 本身作为其父类 enhancer.setSuperclass(this.beanDefinition.getBeanClass()); enhancer.setCallbackFilter(new CallbackFilterImpl()); enhancer.setCallbacks(new Callback[] { @@ -556,33 +556,33 @@ spring-context https://github.com/AmyliaY/spring-context-reading new ReplaceOverrideMethodInterceptor() }); - // 使用CGLIB的create方法生成实例对象 + // 使用 CGLIB 的 create 方法生成实例对象 return (ctor == null) ? enhancer.create() : enhancer.create(ctor.getParameterTypes(), args); } } ``` -至此,完成了bean对象的实例化,然后就可以根据解析得到的BeanDefinition完成对各个属性的赋值处理,也就是依赖注入。这个实现方法就是前面AbstractAutowireCapableBeanFactory类中的populateBean()方法。 -## 6、AbstractAutowireCapableBeanFactory中的populateBean(),完成依赖注入 +至此,完成了 bean 对象的实例化,然后就可以根据解析得到的 BeanDefinition 完成对各个属性的赋值处理,也就是依赖注入。这个实现方法就是前面 AbstractAutowireCapableBeanFactory 类中的 populateBean() 方法。 +## 6、AbstractAutowireCapableBeanFactory 中的 populateBean(),完成依赖注入 ```java // 为属性赋值,完成依赖注入 protected void populateBean(String beanName, RootBeanDefinition mbd, BeanWrapper bw) { - // 获取BeanDefinition中设置的property,这些property来自对BeanDefinition的解析 + // 获取 BeanDefinition 中设置的 property,这些 property 来自对 BeanDefinition 的解析 PropertyValues pvs = mbd.getPropertyValues(); - // 如果实例对象为null,而要注入的属性值不为空,则抛出下述异常 + // 如果实例对象为 null,而要注入的属性值不为空,则抛出下述异常 if (bw == null) { if (!pvs.isEmpty()) { throw new BeanCreationException( mbd.getResourceDescription(), beanName, "Cannot apply property values to null instance"); } else { - // 实例对象为null,属性值也为空,不需要设置属性值,直接返回 + // 实例对象为 null,属性值也为空,不需要设置属性值,直接返回 return; } } - // 在设置属性之前调用Bean的PostProcessor后置处理器 + // 在设置属性之前调用 Bean 的 PostProcessor 后置处理器 boolean continueWithPropertyPopulation = true; if (!mbd.isSynthetic() && hasInstantiationAwareBeanPostProcessors()) { @@ -601,17 +601,17 @@ spring-context https://github.com/AmyliaY/spring-context-reading return; } - // 依赖注入开始,首先处理autowire自动装配的注入 + // 依赖注入开始,首先处理 autowire 自动装配的注入 if (mbd.getResolvedAutowireMode() == RootBeanDefinition.AUTOWIRE_BY_NAME || mbd.getResolvedAutowireMode() == RootBeanDefinition.AUTOWIRE_BY_TYPE) { MutablePropertyValues newPvs = new MutablePropertyValues(pvs); - // 对autowire自动装配的处理,根据Bean名称自动装配注入 + // 对 autowire 自动装配的处理,根据 Bean 名称自动装配注入 if (mbd.getResolvedAutowireMode() == RootBeanDefinition.AUTOWIRE_BY_NAME) { autowireByName(beanName, mbd, bw, newPvs); } - // 根据Bean类型自动装配注入 + // 根据 Bean 类型自动装配注入 if (mbd.getResolvedAutowireMode() == RootBeanDefinition.AUTOWIRE_BY_TYPE) { autowireByType(beanName, mbd, bw, newPvs); } @@ -619,9 +619,9 @@ spring-context https://github.com/AmyliaY/spring-context-reading pvs = newPvs; } - // 检查容器是否持有用于处理单例模式Bean关闭时的后置处理器 + // 检查容器是否持有用于处理单例模式 Bean 关闭时的后置处理器 boolean hasInstAwareBpps = hasInstantiationAwareBeanPostProcessors(); - // Bean实例对象没有依赖,即没有继承基类 + // Bean 实例对象没有依赖,即没有继承基类 boolean needsDepCheck = (mbd.getDependencyCheck() != RootBeanDefinition.DEPENDENCY_CHECK_NONE); if (hasInstAwareBpps || needsDepCheck) { @@ -631,7 +631,7 @@ spring-context https://github.com/AmyliaY/spring-context-reading for (BeanPostProcessor bp : getBeanPostProcessors()) { if (bp instanceof InstantiationAwareBeanPostProcessor) { InstantiationAwareBeanPostProcessor ibp = (InstantiationAwareBeanPostProcessor) bp; - // 使用BeanPostProcessor处理器处理属性值 + // 使用 BeanPostProcessor 处理器处理属性值 pvs = ibp.postProcessPropertyValues(pvs, filteredPds, bw.getWrappedInstance(), beanName); if (pvs == null) { return; @@ -664,7 +664,7 @@ spring-context https://github.com/AmyliaY/spring-context-reading if (System.getSecurityManager()!= null) { if (bw instanceof BeanWrapperImpl) { - // 设置安全上下文,JDK安全机制 + // 设置安全上下文,JDK 安全机制 ((BeanWrapperImpl) bw).setSecurityContext(getAccessControlContext()); } } @@ -695,14 +695,14 @@ spring-context https://github.com/AmyliaY/spring-context-reading if (converter == null) { converter = bw; } - // 创建一个BeanDefinition属性值解析器,将Bean定义中的属性值解析为Bean实例对象的实际值 + // 创建一个 BeanDefinition 属性值解析器,将 Bean 定义中的属性值解析为 Bean 实例对象的实际值 BeanDefinitionValueResolver valueResolver = new BeanDefinitionValueResolver(this, beanName, mbd, converter); // 为属性的解析值创建一个副本,最后将属性值注入到实例对象中 List deepCopy = new ArrayList(original.size()); boolean resolveNecessary = false; for (PropertyValue pv : original) { - // 如果属性值已经转换,直接添加到deepCopy列表中 + // 如果属性值已经转换,直接添加到 deepCopy 列表中 if (pv.isConverted()) { deepCopy.add(pv); } @@ -758,8 +758,8 @@ spring-context https://github.com/AmyliaY/spring-context-reading try { /** * !!!!!!!!!!!!!!!!!!!!! - * 完成bean的属性值注入的入口 - * 走AbstractPropertyAccessor中的实现方法 + * 完成 bean 的属性值注入的入口 + * 走 AbstractPropertyAccessor 中的实现方法 * !!!!!!!!!!!!!!!!!!!!! */ bw.setPropertyValues(new MutablePropertyValues(deepCopy)); @@ -772,12 +772,12 @@ spring-context https://github.com/AmyliaY/spring-context-reading ``` -## 7、BeanDefinitionValueResolver中解析属性值,对注入类型进行转换的具体实现 +## 7、BeanDefinitionValueResolver 中解析属性值,对注入类型进行转换的具体实现 ```java // 解析属性值,对注入类型进行转换 public Object resolveValueIfNecessary(Object argName, Object value) { - // 对引用类型的属性进行解析,RuntimeBeanReference是在对 - // BeanDefinition进行解析时生成的数据对象 + // 对引用类型的属性进行解析,RuntimeBeanReference 是在对 + // BeanDefinition 进行解析时生成的数据对象 if (value instanceof RuntimeBeanReference) { RuntimeBeanReference ref = (RuntimeBeanReference) value; /** @@ -788,7 +788,7 @@ spring-context https://github.com/AmyliaY/spring-context-reading return resolveReference(argName, ref); } - // 对属性值是引用容器中另一个Bean名称的解析 + // 对属性值是引用容器中另一个 Bean 名称的解析 else if (value instanceof RuntimeBeanNameReference) { String refName = ((RuntimeBeanNameReference) value).getBeanName(); refName = String.valueOf(evaluate(refName)); @@ -798,7 +798,7 @@ spring-context https://github.com/AmyliaY/spring-context-reading } return refName; } - // 对BeanDefinitionHolder类型属性的解析,主要是Bean中的内部类 + // 对 BeanDefinitionHolder 类型属性的解析,主要是 Bean 中的内部类 else if (value instanceof BeanDefinitionHolder) { BeanDefinitionHolder bdHolder = (BeanDefinitionHolder) value; return resolveInnerBean(argName, bdHolder.getBeanName(), bdHolder.getBeanDefinition()); @@ -828,7 +828,7 @@ spring-context https://github.com/AmyliaY/spring-context-reading } } // 没有获取到数组的类型,也没有获取到数组元素的类型 - // 则直接设置数组的类型为Object + // 则直接设置数组的类型为 Object else { elementType = Object.class; } @@ -836,22 +836,22 @@ spring-context https://github.com/AmyliaY/spring-context-reading // 创建指定类型的数组 return resolveManagedArray(argName, (List) value, elementType); } - // 解析list类型的属性值 + // 解析 list 类型的属性值 else if (value instanceof ManagedList) { // May need to resolve contained runtime references. return resolveManagedList(argName, (List) value); } - // 解析set类型的属性值 + // 解析 set 类型的属性值 else if (value instanceof ManagedSet) { // May need to resolve contained runtime references. return resolveManagedSet(argName, (Set) value); } - // 解析map类型的属性值 + // 解析 map 类型的属性值 else if (value instanceof ManagedMap) { // May need to resolve contained runtime references. return resolveManagedMap(argName, (Map) value); } - // 解析Properties类型的属性值,Properties其实就是key和value均为字符串的map + // 解析 Properties 类型的属性值,Properties 其实就是 key 和 value 均为字符串的 map else if (value instanceof ManagedProperties) { Properties original = (Properties) value; // 创建一个拷贝,用于作为解析后的返回值 @@ -880,7 +880,7 @@ spring-context https://github.com/AmyliaY/spring-context-reading // 对目标类型的属性进行解析,递归调用 return this.typeConverter.convertIfNecessary(valueObject, resolvedTargetType); } - // 没有获取到属性的目标对象,则按Object类型返回 + // 没有获取到属性的目标对象,则按 Object 类型返回 else { return valueObject; } @@ -899,7 +899,7 @@ spring-context https://github.com/AmyliaY/spring-context-reading // 解析引用类型的属性值 private Object resolveReference(Object argName, RuntimeBeanReference ref) { try { - // 获取引用的BeanName + // 获取引用的 BeanName String refName = ref.getBeanName(); refName = String.valueOf(evaluate(refName)); // 如果引用的对象在父容器中,则从父容器中获取指定的引用对象 @@ -912,11 +912,11 @@ spring-context https://github.com/AmyliaY/spring-context-reading } return this.beanFactory.getParentBeanFactory().getBean(refName); } - // 从当前的容器中获取指定的引用Bean对象,如果指定的Bean没有被实例化 - // 则会递归触发引用Bean的初始化和依赖注入 + // 从当前的容器中获取指定的引用 Bean 对象,如果指定的 Bean 没有被实例化 + // 则会递归触发引用 Bean 的初始化和依赖注入 else { Object bean = this.beanFactory.getBean(refName); - // 为refName对应的BeanDefinition注入依赖的Bean + // 为 refName 对应的 BeanDefinition 注入依赖的 Bean this.beanFactory.registerDependentBean(refName, this.beanName); return bean; } @@ -928,34 +928,34 @@ spring-context https://github.com/AmyliaY/spring-context-reading } } - // 解析array类型的属性 + // 解析 array 类型的属性 private Object resolveManagedArray(Object argName, List ml, Class elementType) { // 创建一个指定类型的数组,用于存放和返回解析后的数组 Object resolved = Array.newInstance(elementType, ml.size()); for (int i = 0; i < ml.size(); i++) { - // 递归解析array的每一个元素,并将解析后的值设置到resolved数组中,索引为i + // 递归解析 array 的每一个元素,并将解析后的值设置到 resolved 数组中,索引为 i Array.set(resolved, i, resolveValueIfNecessary(new KeyedArgName(argName, i), ml.get(i))); } return resolved; } - // 解析list类型的属性 + // 解析 list 类型的属性 private List resolveManagedList(Object argName, List ml) { List resolved = new ArrayList(ml.size()); for (int i = 0; i < ml.size(); i++) { - // 递归解析list的每一个元素 + // 递归解析 list 的每一个元素 resolved.add( resolveValueIfNecessary(new KeyedArgName(argName, i), ml.get(i))); } return resolved; } - // 解析set类型的属性 + // 解析 set 类型的属性 private Set resolveManagedSet(Object argName, Set ms) { Set resolved = new LinkedHashSet(ms.size()); int i = 0; - // 递归解析set的每一个元素 + // 递归解析 set 的每一个元素 for (Object m : ms) { resolved.add(resolveValueIfNecessary(new KeyedArgName(argName, i), m)); i++; @@ -963,10 +963,10 @@ spring-context https://github.com/AmyliaY/spring-context-reading return resolved; } - // 解析map类型的属性 + // 解析 map 类型的属性 private Map resolveManagedMap(Object argName, Map mm) { Map resolved = new LinkedHashMap(mm.size()); - // 递归解析map中每一个元素的key和value + // 递归解析 map 中每一个元素的 key 和 value for (Map.Entry entry : mm.entrySet()) { Object resolvedKey = resolveValueIfNecessary(argName, entry.getKey()); Object resolvedValue = resolveValueIfNecessary( @@ -977,8 +977,8 @@ spring-context https://github.com/AmyliaY/spring-context-reading } ``` -至此,已经为依赖注入做好了准备,下面就该将bean对象设置到它所依赖的另一个bean的属性中去。AbstractPropertyAccessor和其子类BeanWrapperImpl完成了依赖注入的详细过程。 -## 8、AbstractPropertyAccessor中的实现 +至此,已经为依赖注入做好了准备,下面就该将 bean 对象设置到它所依赖的另一个 bean 的属性中去。AbstractPropertyAccessor 和其子类 BeanWrapperImpl 完成了依赖注入的详细过程。 +## 8、AbstractPropertyAccessor 中的实现 ```java public void setPropertyValues(PropertyValues pvs) throws BeansException { setPropertyValues(pvs, false, false); @@ -994,7 +994,7 @@ spring-context https://github.com/AmyliaY/spring-context-reading try { /** * !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! - * 走BeanWrapperImpl中的实现,也是bean属性值注入具体实现的入口 + * 走 BeanWrapperImpl 中的实现,也是 bean 属性值注入具体实现的入口 * !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! */ setPropertyValue(pv); @@ -1027,11 +1027,11 @@ spring-context https://github.com/AmyliaY/spring-context-reading ``` -## 9、BeanWrapperImpl中的实现 +## 9、BeanWrapperImpl 中的实现 ```java @Override public void setPropertyValue(PropertyValue pv) throws BeansException { - // PropertyTokenHolder是一个用于内部使用的内部类 + // PropertyTokenHolder 是一个用于内部使用的内部类 PropertyTokenHolder tokens = (PropertyTokenHolder) pv.resolvedTokens; if (tokens == null) { String propertyName = pv.getName(); @@ -1050,7 +1050,7 @@ spring-context https://github.com/AmyliaY/spring-context-reading /** * !!!!!!!!!!!!!!! - * 进入bean属性值注入的具体实现 + * 进入 bean 属性值注入的具体实现 * !!!!!!!!!!!!!!! */ nestedBw.setPropertyValue(tokens, pv); @@ -1067,14 +1067,14 @@ spring-context https://github.com/AmyliaY/spring-context-reading */ @SuppressWarnings("unchecked") private void setPropertyValue(PropertyTokenHolder tokens, PropertyValue pv) throws BeansException { - // PropertyTokenHolder主要保存属性的名称、路径、以及集合的size等信息 + // PropertyTokenHolder 主要保存属性的名称、路径、以及集合的 size 等信息 String propertyName = tokens.canonicalName; String actualName = tokens.actualName; // 对集合类型的属性注入 - // keys是用来保存集合类型属性的size + // keys 是用来保存集合类型属性的 size if (tokens.keys != null) { - // 将属性信息从tokens拷贝到getterTokens + // 将属性信息从 tokens 拷贝到 getterTokens PropertyTokenHolder getterTokens = new PropertyTokenHolder(); getterTokens.canonicalName = tokens.canonicalName; getterTokens.actualName = tokens.actualName; @@ -1082,7 +1082,7 @@ spring-context https://github.com/AmyliaY/spring-context-reading System.arraycopy(tokens.keys, 0, getterTokens.keys, 0, tokens.keys.length - 1); Object propValue; try { - // 通过反射机制,调用属性的getter方法获取属性值 + // 通过反射机制,调用属性的 getter 方法获取属性值 propValue = getPropertyValue(getterTokens); } catch (NotReadablePropertyException ex) { @@ -1104,7 +1104,7 @@ spring-context https://github.com/AmyliaY/spring-context-reading "in indexed property path '" + propertyName + "': returned null"); } } - // 如果属性值是Array数组类型的,则注入array类型的属性值 + // 如果属性值是 Array 数组类型的,则注入 array 类型的属性值 if (propValue.getClass().isArray()) { // 获取属性的描述符 PropertyDescriptor pd = getCachedIntrospectionResults().getPropertyDescriptor(actualName); @@ -1128,10 +1128,10 @@ spring-context https://github.com/AmyliaY/spring-context-reading "Invalid array index in property path '" + propertyName + "'", ex); } } - // 如果属性值是List类型的,则注入list类型的属性值 + // 如果属性值是 List 类型的,则注入 list 类型的属性值 else if (propValue instanceof List) { PropertyDescriptor pd = getCachedIntrospectionResults().getPropertyDescriptor(actualName); - // 获取list集合中元素的类型 + // 获取 list 集合中元素的类型 Class requiredType = GenericCollectionTypeResolver.getCollectionReturnType( pd.getReadMethod(), tokens.keys.length); List list = (List) propValue; @@ -1141,12 +1141,12 @@ spring-context https://github.com/AmyliaY/spring-context-reading if (isExtractOldValueForEditor() && index < list.size()) { oldValue = list.get(index); } - // 获取list解析后的属性值 + // 获取 list 解析后的属性值 Object convertedValue = convertIfNecessary(propertyName, oldValue, pv.getValue(), requiredType, TypeDescriptor.nested(property(pd), tokens.keys.length)); - // 获取list集合的size + // 获取 list 集合的 size int size = list.size(); - // 如果list的长度大于属性值的长度,则多余的元素赋值为null + // 如果 list 的长度大于属性值的长度,则多余的元素赋值为 null if (index >= size && index < this.autoGrowCollectionLimit) { for (int i = size; i < index; i++) { try { @@ -1163,7 +1163,7 @@ spring-context https://github.com/AmyliaY/spring-context-reading } else { try { - // 为list属性赋值 + // 为 list 属性赋值 list.set(index, convertedValue); } catch (IndexOutOfBoundsException ex) { @@ -1172,28 +1172,28 @@ spring-context https://github.com/AmyliaY/spring-context-reading } } } - // 如果属性值是Map类型的,则注入Map类型的属性值 + // 如果属性值是 Map 类型的,则注入 Map 类型的属性值 else if (propValue instanceof Map) { PropertyDescriptor pd = getCachedIntrospectionResults().getPropertyDescriptor(actualName); - // 获取map集合key的类型 + // 获取 map 集合 key 的类型 Class mapKeyType = GenericCollectionTypeResolver.getMapKeyReturnType( pd.getReadMethod(), tokens.keys.length); - // 获取map集合value的类型 + // 获取 map 集合 value 的类型 Class mapValueType = GenericCollectionTypeResolver.getMapValueReturnType( pd.getReadMethod(), tokens.keys.length); Map map = (Map) propValue; TypeDescriptor typeDescriptor = (mapKeyType != null ? TypeDescriptor.valueOf(mapKeyType) : TypeDescriptor.valueOf(Object.class)); - // 解析map类型属性key值 + // 解析 map 类型属性 key 值 Object convertedMapKey = convertIfNecessary(null, null, key, mapKeyType, typeDescriptor); Object oldValue = null; if (isExtractOldValueForEditor()) { oldValue = map.get(convertedMapKey); } - // 解析map类型属性value值 + // 解析 map 类型属性 value 值 Object convertedMapValue = convertIfNecessary(propertyName, oldValue, pv.getValue(), mapValueType, TypeDescriptor.nested(property(pd), tokens.keys.length)); - // 将解析后的key和value值赋值给map集合属性 + // 将解析后的 key 和 value 值赋值给 map 集合属性 map.put(convertedMapKey, convertedMapValue); } else { @@ -1207,7 +1207,7 @@ spring-context https://github.com/AmyliaY/spring-context-reading PropertyDescriptor pd = pv.resolvedDescriptor; if (pd == null || !pd.getWriteMethod().getDeclaringClass().isInstance(this.object)) { pd = getCachedIntrospectionResults().getPropertyDescriptor(actualName); - // 如果无法获取到属性名或者属性没有提供setter赋值方法 + // 如果无法获取到属性名或者属性没有提供 setter 赋值方法 if (pd == null || pd.getWriteMethod() == null) { // 如果属性值是可选的,即不是必须的,则忽略该属性值 if (pv.isOptional()) { @@ -1215,7 +1215,7 @@ spring-context https://github.com/AmyliaY/spring-context-reading "' - property not found on bean class [" + getRootClass().getName() + "]"); return; } - // 如果属性值是必须的,则抛出无法给属性赋值,因为没提供setter方法的异常 + // 如果属性值是必须的,则抛出无法给属性赋值,因为没提供 setter 方法的异常 else { PropertyMatches matches = PropertyMatches.forProperty(propertyName, getRootClass()); throw new NotWritablePropertyException( @@ -1236,9 +1236,9 @@ spring-context https://github.com/AmyliaY/spring-context-reading } else { if (isExtractOldValueForEditor() && pd.getReadMethod() != null) { - // 获取属性的getter方法 + // 获取属性的 getter 方法 final Method readMethod = pd.getReadMethod(); - // 如果属性的getter方法无法访问,则使用Java的反射机制强行访问(暴力读取属性值) + // 如果属性的 getter 方法无法访问,则使用 Java 的反射机制强行访问 (暴力读取属性值) if (!Modifier.isPublic(readMethod.getDeclaringClass().getModifiers()) && !readMethod.isAccessible()) { if (System.getSecurityManager()!= null) { @@ -1255,7 +1255,7 @@ spring-context https://github.com/AmyliaY/spring-context-reading } } try { - // 属性没有提供getter方法时,调用潜在的读取属性值的方法,获取属性值 + // 属性没有提供 getter 方法时,调用潜在的读取属性值的方法,获取属性值 if (System.getSecurityManager() != null) { oldValue = AccessController.doPrivileged(new PrivilegedExceptionAction() { public Object run() throws Exception { @@ -1282,11 +1282,11 @@ spring-context https://github.com/AmyliaY/spring-context-reading } pv.getOriginalPropertyValue().conversionNecessary = (valueToApply != originalValue); } - // 根据Java的内省机制,获取属性的setter(写方法)方法 + // 根据 Java 的内省机制,获取属性的 setter(写方法) 方法 final Method writeMethod = (pd instanceof GenericTypeAwarePropertyDescriptor ? ((GenericTypeAwarePropertyDescriptor) pd).getWriteMethodForActualAccess() : pd.getWriteMethod()); - // 如果属性的setter方法无法访问,则强行设置setter方法可访问(暴力为属性赋值) + // 如果属性的 setter 方法无法访问,则强行设置 setter 方法可访问 (暴力为属性赋值) if (!Modifier.isPublic(writeMethod.getDeclaringClass().getModifiers()) && !writeMethod.isAccessible()) { if (System.getSecurityManager()!= null) { AccessController.doPrivileged(new PrivilegedAction() { @@ -1301,7 +1301,7 @@ spring-context https://github.com/AmyliaY/spring-context-reading } } final Object value = valueToApply; - // 如果使用了Java的安全机制,则需要权限验证 + // 如果使用了 Java 的安全机制,则需要权限验证 if (System.getSecurityManager() != null) { try { AccessController.doPrivileged(new PrivilegedExceptionAction() { @@ -1343,39 +1343,39 @@ spring-context https://github.com/AmyliaY/spring-context-reading } ``` -终于完成了对bean的各种属性的依赖注入,在bean的实例化和依赖注入的过程中,需要依据BeanDefinition中的信息来递归地完成依赖注入。另外,在此过程中存在许多递归调用,一个递归是在上下文体系中查找需要的bean和创建bean的递归调用;另一个是在依赖注入时,通过递归调用容器的getBean方法,得到当前bean的依赖bean,同时也触发对依赖bean的创建和注入;在对bean的属性进行依赖注入时,解析的过程也是递归的。这样,根据依赖关系,一层一层地完成bean的创建和注入,直到最后完成当前bean的创建。 +终于完成了对 bean 的各种属性的依赖注入,在 bean 的实例化和依赖注入的过程中,需要依据 BeanDefinition 中的信息来递归地完成依赖注入。另外,在此过程中存在许多递归调用,一个递归是在上下文体系中查找需要的 bean 和创建 bean 的递归调用;另一个是在依赖注入时,通过递归调用容器的 getBean 方法,得到当前 bean 的依赖 bean,同时也触发对依赖 bean 的创建和注入;在对 bean 的属性进行依赖注入时,解析的过程也是递归的。这样,根据依赖关系,一层一层地完成 bean 的创建和注入,直到最后完成当前 bean 的创建。 -## 最后补充一下通过lazy-init属性触发的依赖注入 -lazy-init触发的预实例化和依赖注入,发生在IoC容器完成对BeanDefinition的定位、载入、解析和注册之后。虽然会影响IoC容器初始化的性能,但确能有效提高应用第一次获取该bean的效率。 - lazy-init实现的入口方法在我们前面解读过的AbstractApplicationContext的refresh()中,它是IoC容器正式启动的标志。 +## 最后补充一下通过 lazy-init 属性触发的依赖注入 +lazy-init 触发的预实例化和依赖注入,发生在 IoC 容器完成对 BeanDefinition 的定位、载入、解析和注册之后。虽然会影响 IoC 容器初始化的性能,但确能有效提高应用第一次获取该 bean 的效率。 + lazy-init 实现的入口方法在我们前面解读过的 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设为true的bean将在容器初始化时进行依赖注入,而不会等到getBean()方法调用时才进行 + * 另外需要注意的是,IoC 容器有一个预实例化的配置(即,将 AbstractBeanDefinition 中的 lazyInit 属性设为 true),使用户可以对容器的初始化 + * 过程做一个微小的调控,lazyInit 设为 true 的 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); // 初始化信息源,和国际化相关. @@ -1384,7 +1384,7 @@ lazy-init触发的预实例化和依赖注入,发生在IoC容器完成对BeanD // 初始化容器事件传播器 initApplicationEventMulticaster(); - // 调用子类的某些特殊Bean初始化方法 + // 调用子类的某些特殊 Bean 初始化方法 onRefresh(); // 为事件传播器注册事件监听器. @@ -1392,7 +1392,7 @@ lazy-init触发的预实例化和依赖注入,发生在IoC容器完成对BeanD /** * !!!!!!!!!!!!!!!!!!!!! - * 初始化Bean,并对lazy-init属性进行处理 + * 初始化 Bean,并对 lazy-init 属性进行处理 * !!!!!!!!!!!!!!!!!!!!! */ finishBeanFactoryInitialization(beanFactory); @@ -1402,10 +1402,10 @@ lazy-init触发的预实例化和依赖注入,发生在IoC容器完成对BeanD } catch (BeansException ex) { - // 销毁以创建的单态Bean + // 销毁以创建的单态 Bean destroyBeans(); - // 取消refresh操作,重置容器的同步标识. + // 取消 refresh 操作,重置容器的同步标识. cancelRefresh(ex); throw ex; @@ -1413,16 +1413,16 @@ lazy-init触发的预实例化和依赖注入,发生在IoC容器完成对BeanD } } - // 对配置了lazy-init属性的Bean进行预实例化处理 + // 对配置了 lazy-init 属性的 Bean 进行预实例化处理 protected void finishBeanFactoryInitialization(ConfigurableListableBeanFactory beanFactory) { - // 这是Spring3以后新加的代码,为容器指定一个转换服务(ConversionService) - // 在对某些Bean属性进行转换时使用 + // 这是 Spring3 以后新加的代码,为容器指定一个转换服务 (ConversionService) + // 在对某些 Bean 属性进行转换时使用 if (beanFactory.containsBean(CONVERSION_SERVICE_BEAN_NAME) && beanFactory.isTypeMatch(CONVERSION_SERVICE_BEAN_NAME, ConversionService.class)) { beanFactory.setConversionService( /** * !!!!!!!!!!!!!!!!!!!! - * 在这里调用了getBean()方法,触发依赖注入 + * 在这里调用了 getBean() 方法,触发依赖注入 * !!!!!!!!!!!!!!!!!!!! */ beanFactory.getBean(CONVERSION_SERVICE_BEAN_NAME, ConversionService.class)); @@ -1436,14 +1436,11 @@ lazy-init触发的预实例化和依赖注入,发生在IoC容器完成对BeanD // 为了类型匹配,停止使用临时的类加载器 beanFactory.setTempClassLoader(null); - // 缓存容器中所有注册的BeanDefinition元数据,以防被修改 + // 缓存容器中所有注册的 BeanDefinition 元数据,以防被修改 beanFactory.freezeConfiguration(); - // 对配置了lazy-init属性的单态模式Bean进行预实例化处理 + // 对配置了 lazy-init 属性的单态模式 Bean 进行预实例化处理 beanFactory.preInstantiateSingletons(); } -``` - - - +``` \ No newline at end of file diff --git a/docs/Spring/Spring源码故事(瞎编版)/面筋哥IoC容器的一天(上).md b/docs/Spring/Spring源码故事(瞎编版)/面筋哥IoC容器的一天(上).md index 7056fc3..054a8a5 100644 --- a/docs/Spring/Spring源码故事(瞎编版)/面筋哥IoC容器的一天(上).md +++ b/docs/Spring/Spring源码故事(瞎编版)/面筋哥IoC容器的一天(上).md @@ -1,26 +1,30 @@ - 引言:庞大的代码量让人心生怠倦,有趣的故事让技术也疯狂。 -大家好,我是IoC容器家族的第17代传人,我们家族世世代代在spring商业街上卖烤面筋,大家都叫我“面筋哥”,另外我爹还给我起了个高大上的英文名字,叫“FileSystemXmlApplicationContext”,但有群臭猴子嫌麻烦,就天天叫我的外号,害得我差点忘了自己的本名。不过无所谓咯,只要生意兴隆,这都是小事。 +引言:庞大的代码量让人心生怠倦,有趣的故事让技术也疯狂。 -前几天出摊卖烤面筋时,灵感大作,即兴唱了一首“我的烤面筋”,被网友拍下来传到某站上 成了网红,现在我要趁势而上,把自己祖传的烤面筋工艺宣传出去,让我那个臭弟弟“ClassPathXmlApplicationContext”知道,谁才是IoC容器的正统传人! -#### 第一阶段:BeanDefinition资源定位(Reader,beanDefinitionReader,documentReader) -新的一天从new开始,但我却还躺在床上各种伸懒腰,毕竟我现在也是个小老板了,很多杂七杂八的活雇几个小弟干就行咯。我拿起我的iBanana11看了看商业街董事(某程序员)发的“精选优质面筋批发市场地址”,然后深吸一口气refresh(),闭上眼obtainFreshBeanFactory(),气沉丹田refreshBeanFactory(),大喊一声: +大家好,我是 IoC 容器家族的第 17 代传人,我们家族世世代代在 spring 商业街上卖烤面筋,大家都叫我“面筋哥”,另外我爹还给我起了个高大上的英文名字,叫“FileSystemXmlApplicationContext”,但有群臭猴子嫌麻烦,就天天叫我的外号,害得我差点忘了自己的本名。不过无所谓咯,只要生意兴隆,这都是小事。 + +前几天出摊卖烤面筋时,灵感大作,即兴唱了一首“我的烤面筋”,被网友拍下来传到某站上 成了网红,现在我要趁势而上,把自己祖传的烤面筋工艺宣传出去,让我那个臭弟弟“ClassPathXmlApplicationContext”知道,谁才是 IoC 容器的正统传人! + +## 第一阶段:BeanDefinition 资源定位(Reader,beanDefinitionReader,documentReader) +新的一天从 new 开始,但我却还躺在床上各种伸懒腰,毕竟我现在也是个小老板了,很多杂七杂八的活雇几个小弟干就行咯。我拿起我的 iBanana11 看了看商业街董事(某程序员)发的“精选优质面筋批发市场地址”,然后深吸一口气 refresh(),闭上眼 obtainFreshBeanFactory(),气沉丹田 refreshBeanFactory(),大喊一声: “loadBeanDefinitions()!” 我虎背熊腰的小弟“beanDefinitionReader” 破门而入,尖声细语地问道: “老板有何吩咐 ~ ?” -我起身叮嘱了他几件事后,把自己的联系方式(引用)、面筋批发市场的地址(spring配置文件地址)交给他,就又躺回去盯着天花板上的钻石吊灯继续发呆。 -Reader家有一对兄妹,哥哥beanDefinitionReader虎背熊腰大老粗,却尖声细语;妹妹documentReader心灵手巧,可惜比较宅,我们几乎没怎么见过。兄妹俩相互配合把上午的准备工作做了大半。 -不要看我天天躺着,彗星晒屁股了还眯着眼,ta们兄妹俩在几点几分打个喷嚏我都能算到,毕竟我基因里都写满了“烤面筋工艺完整详细流程”。 -哥哥现在肯定在开着小面包车拿着我给他的地址(locations)到处找面筋等原材料,然后把找到的面筋打包进Document对象,拉回来交给妹妹documentReader进行精心处理,连同Document给她的还有一个“神秘人”的联系方式。 -妹妹会打开Document取出其中最大的几个箱子(<beans>、<import>、<alias>等一级标签),分别进行处理。其中beans箱最为重要,里面放满了夜市的主角,烤面筋的核心材料。 -#### 第二阶段:将bean解析封装成BeanDefinitionHolder(BeanDefinitionParserDelegate) -之后妹妹会拿起我们IoC家族祖传的面筋处理神器BeanDefinitionParserDelegate,从beans箱里面一个一个取出形态各异的面筋bean分别进行加工处理。刚拿出来的面筋bean是不会直接烤了卖的,我们会将bean用神器ParserDelegate进行九九八十一道细致处理,所以我们家烤出来的面筋才会如此劲道美味,世世代代延绵不断。 - 不过处理程序再怎么细致复杂,也不过就是分为两大部分:第一,处理bean的属性信息,如id,class,scope等;第二,处理bean的子元素,主要是标签,而标签又有属性和子元素,且子元素类型更加丰富复杂,可能是<map>,<set>,<list>,<array>等。所以如果你们想学我家的祖传秘方,开个同样的摊子干倒我,也不是这么容易的哦。 - 经过上面的步骤,一个配置文件中的面筋bean就被处理包装成了半成品BeanDefinitionHolder。 -#### 第三阶段:将BeanDefinition注册进IoC容器(BeanDefinitionReaderUtils) -妹妹在用神器BeanDefinitionParserDelegate经过一顿疯狂操作之后,将包装好的半成品BeanDefinitionHolder扔进传输机BeanDefinitionReaderUtils,并且输入哥哥给她的神秘人地址,就继续处理下一个面筋bean咯。 -之后,传输机将BeanDefinitionHolder的包装打开,分别取出beanName(面筋的唯一标识)和BeanDefinition(面筋本筋),传输的目的地是BeanDefinitionRegistry的工作室(这就是我前面给哥哥beanDefinitionReader的地址)。 -这家工作室的BeanDefinitionRegistry其实就是我的影分身之一,因为我的祖先实现了这个接口。影分身Registry检查一下传输过来的beanName(面筋的唯一标识)和BeanDefinition(面筋本筋),如果没什么问题,就把它们用根绳子系在一起扔进我的“王之面筋宝库”,一个ConcurrentHashMap(64),也有人把我的“面筋宝库”称作“IoC容器本器”,我也无可辩驳,谁让他们吃面筋付钱了呢。 -就这样,每一种取出来的面筋都会经过这些处理。等到所有的面筋处理完了,也差不多到了傍晚,每到这时我就会拿起梳子和发油,对着镶满钻石的镜子,梳理整齐与徐峥同款的明星发型,唱着魔性的“我的烤面筋 ~”,骑着小车车,出摊咯 ~ +我起身叮嘱了他几件事后,把自己的联系方式(引用)、面筋批发市场的地址(spring 配置文件地址)交给他,就又躺回去盯着天花板上的钻石吊灯继续发呆。 +Reader 家有一对兄妹,哥哥 beanDefinitionReader 虎背熊腰大老粗,却尖声细语;妹妹 documentReader 心灵手巧,可惜比较宅,我们几乎没怎么见过。兄妹俩相互配合把上午的准备工作做了大半。 +不要看我天天躺着,彗星晒屁股了还眯着眼,ta 们兄妹俩在几点几分打个喷嚏我都能算到,毕竟我基因里都写满了“烤面筋工艺完整详细流程”。 +哥哥现在肯定在开着小面包车拿着我给他的地址(locations)到处找面筋等原材料,然后把找到的面筋打包进 Document 对象,拉回来交给妹妹 documentReader 进行精心处理,连同 Document 给她的还有一个“神秘人”的联系方式。 +妹妹会打开 Document 取出其中最大的几个箱子(<beans>、<import>、<alias> 等一级标签),分别进行处理。其中 beans 箱最为重要,里面放满了夜市的主角,烤面筋的核心材料。 -面筋等原材料基本上都已经处理完毕,但把这些原材料变成程序员手中的“烤面筋”也是一门复杂而精细的手艺,老铁们记得 watch、star、fork,素质三连一波,下一期我将带领你们走进spring商业街的夜市,一起烤出香喷喷的面筋,成为这条gai上最亮的仔! +## 第二阶段:将 bean 解析封装成 BeanDefinitionHolder(BeanDefinitionParserDelegate) +之后妹妹会拿起我们 IoC 家族祖传的面筋处理神器 BeanDefinitionParserDelegate,从 beans 箱里面一个一个取出形态各异的面筋 bean 分别进行加工处理。刚拿出来的面筋 bean 是不会直接烤了卖的,我们会将 bean 用神器 ParserDelegate 进行九九八十一道细致处理,所以我们家烤出来的面筋才会如此劲道美味,世世代代延绵不断。 + 不过处理程序再怎么细致复杂,也不过就是分为两大部分:第一,处理 bean 的属性信息,如 id,class,scope 等;第二,处理 bean 的子元素,主要是 标签,而 标签又有属性和子元素,且子元素类型更加丰富复杂,可能是<map>,<set>,<list>,<array> 等。所以如果你们想学我家的祖传秘方,开个同样的摊子干倒我,也不是这么容易的哦。 + 经过上面的步骤,一个配置文件中的面筋 bean 就被处理包装成了半成品 BeanDefinitionHolder。 + +## 第三阶段:将 BeanDefinition 注册进 IoC 容器(BeanDefinitionReaderUtils) + +妹妹在用神器 BeanDefinitionParserDelegate 经过一顿疯狂操作之后,将包装好的半成品 BeanDefinitionHolder 扔进传输机 BeanDefinitionReaderUtils,并且输入哥哥给她的神秘人地址,就继续处理下一个面筋 bean 咯。 +之后,传输机将 BeanDefinitionHolder 的包装打开,分别取出 beanName(面筋的唯一标识)和 BeanDefinition(面筋本筋),传输的目的地是 BeanDefinitionRegistry 的工作室(这就是我前面给哥哥 beanDefinitionReader 的地址)。 +这家工作室的 BeanDefinitionRegistry 其实就是我的影分身之一,因为我的祖先实现了这个接口。影分身 Registry 检查一下传输过来的 beanName(面筋的唯一标识)和 BeanDefinition(面筋本筋),如果没什么问题,就把它们用根绳子系在一起扔进我的“王之面筋宝库”,一个 ConcurrentHashMap(64),也有人把我的“面筋宝库”称作“IoC 容器本器”,我也无可辩驳,谁让他们吃面筋付钱了呢。 +就这样,每一种取出来的面筋都会经过这些处理。等到所有的面筋处理完了,也差不多到了傍晚,每到这时我就会拿起梳子和发油,对着镶满钻石的镜子,梳理整齐与徐峥同款的明星发型,唱着魔性的“我的烤面筋 ~”,骑着小车车,出摊咯 ~ +面筋等原材料基本上都已经处理完毕,但把这些原材料变成程序员手中的“烤面筋”也是一门复杂而精细的手艺,老铁们记得 watch、star、fork,素质三连一波,下一期我将带领你们走进 spring 商业街的夜市,一起烤出香喷喷的面筋,成为这条 gai 上最亮的仔! \ No newline at end of file diff --git a/docs/学习心得/个人经验/初级开发者应该从spring源码中学什么.md b/docs/学习心得/个人经验/初级开发者应该从spring源码中学什么.md index 80f1bf7..7211052 100644 --- a/docs/学习心得/个人经验/初级开发者应该从spring源码中学什么.md +++ b/docs/学习心得/个人经验/初级开发者应该从spring源码中学什么.md @@ -1,27 +1,31 @@ -       作为一名初入职场的开发者,最开始是在逛B站刷视频时看到的一个spring源码阅读解析,当时作为一个只知道SSH和CRUD的boy,看完后心里就两个词儿“卧槽!牛B啊!”而且在去年秋招面试阿里时几乎每次都会被面试官问道“有阅读过什么开源框架吗?”每次我都只能一脸便秘的“嗯…,呃…,啊…,木得…”。这在我心里埋下了一个想法,硬着头皮也要把spring框架源码读一遍,再不济也要看看猪是怎么跑的。 +作为一名初入职场的开发者,最开始是在逛 B 站刷视频时看到的一个 Spring 源码阅读解析,当时作为一个只知道 SSH 和 CRUD 的 boy,看完后心里就两个词儿“卧槽!牛 B 啊!”而且在去年秋招面试阿里时几乎每次都会被面试官问道“有阅读过什么开源框架吗?”每次我都只能一脸便秘的“嗯…,呃…,啊…,木得…”。这在我心里埋下了一个想法,硬着头皮也要把 Spring 框架源码读一遍,再不济也要看看猪是怎么跑的。 -       从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 上。 -       这三个月来,阅读spring源码给我带来的提升,主要在专业技能上,但同时也辐射到了我的工作、学习、社交等方面。所以,写这篇文章一方面是应“码农翻身”专栏——刘欣老师的建议,做个经验谈,另一方面也是对自己这三个月学习成果的总结。 +学习方法的话,我个人比较喜欢先在 B 站上看相关视频,知道怎么读,从哪下口。然后自己买了本 计文柯老师的《Spring 技术内幕》,比对着从 Spring 官网下载的源码包潜心研读。第一遍读,什么都不懂,按图索骥,迷迷糊糊的读完了;第二遍读,就轻车熟路一些咯,“卧槽!原来如此!”的感叹声也络绎不绝;第三遍就能够在整体代码设计和细节实现两个不同的层次上去吸收 Spring 框架的优点咯。 + +这三个月来,阅读 Spring 源码给我带来的提升,主要在专业技能上,但同时也辐射到了我的工作、学习、社交等方面。所以,写这篇文章一方面是应“码农翻身”专栏——刘欣老师的建议,做个经验谈,另一方面也是对自己这三个月学习成果的总结。 + +下面我将分三个部分,谈一谈自己的经验。 -       下面我将分三个部分,谈一谈自己的经验。 ### 一、工作方面(编码规范、编码能力、设计模式、英文阅读) -       我所从事的行业做的是to B的业务,产品底层平台的框架,代码累累,堆积成山,很多框架都是零几年写的,有的甚至比spring还早。且最近国产化、中台、云服务等概念都在不断落地中,有框架源码的阅读经验,让我能够更从容地面对公司研发的新框架,所维护的产品适配华为高斯数据库时,也更清楚可能是JDBC框架中哪里做了特殊处理所导致的问题。当然,最主要的还是对个人编码规范的养成,设计模式的理解应用,英文阅读的能力提升。 +我所从事的行业做的是 toB 的业务,产品底层平台的框架,代码累累,堆积成山,很多框架都是零几年写的,有的甚至比 Spring 还早。且最近国产化、中台、云服务等概念都在不断落地中,有框架源码的阅读经验,让我能够更从容地面对公司研发的新框架,所维护的产品适配华为高斯数据库时,也更清楚可能是 JDBC 框架中哪里做了特殊处理所导致的问题。当然,最主要的还是对个人编码规范的养成,设计模式的理解应用,英文阅读的能力提升。 -       作为一个初入职场的开发者,编码规范是一个很重要的点,能够让你写出的代码易于维护、阅读和理解。比如,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。 -       另外,部门也经常会做代码评审,规范的编码不但能够获得同事的认可,一点一滴的细节也会使你的leader对你刮目相看。 +另外,部门也经常会做代码评审,规范的编码不但能够获得同事的认可,一点一滴的细节也会使你的 leader 对你刮目相看。 -       从IoC的各顶层接口到中间一层一层的抽象类,再到最后的实现类,这一整套体系的设计和实现,对自己在日常工作中设计某些功能的接口、抽象类和具体实现,都带来了很有价值的参考,设计模式和巧妙的编码技巧也渐渐变得触手可及。比如,设计一个VO字段校验功能时,会先定义一个顶层接口,抽象出公共方法,抽象类中有做必输项字段非空校验的,在其中利用模板方法模式对公共功能做具体实现,特性化功能写成抽象方法交由各子类具体实现即可。 +从 IoC 的各顶层接口到中间一层一层的抽象类,再到最后的实现类,这一整套体系的设计和实现,对自己在日常工作中设计某些功能的接口、抽象类和具体实现,都带来了很有价值的参考,设计模式和巧妙的编码技巧也渐渐变得触手可及。比如,设计一个 VO 字段校验功能时,会先定义一个顶层接口,抽象出公共方法,抽象类中有做必输项字段非空校验的,在其中利用模板方法模式对公共功能做具体实现,特性化功能写成抽象方法交由各子类具体实现即可。 + +Spring 上很多接口和抽象类,其注解甚至比代码还多,我也经常尝试着去阅读理解这些注释,看看自己的理解与书上的差异,用这种方式来提升英文技术文档的阅读能力,往往更实在一些。 -       Spring上很多接口和抽象类,其注解甚至比代码还多,我也经常尝试着去阅读理解这些注释,看看自己的理解与书上的差异,用这种方式来提升英文技术文档的阅读能力,往往更实在一些。 ### 二、学习方面(学习模式的构建、学以致用) -       虽然是做技术的,但我也是一个很爱出去耍的人。构建好自己的学习模式能够让你更从容地面对工作和生活。不加班的情况下(所幸部门加班并不太多),我一般会在晚饭之后以及周日时间充电。不管是学技术还是其它什么东西,我认为 以视频为入口,以业界公认的名书继续深入理解,以社交圈的同行或网上社区为输出交流管道,最后持久化到思维导图及学习文档中。Spring源码学习是我工作之后对自己学习模式构建的一个尝试,构建起这种学习模式之后,个人的工作和生活也变得更加协调平衡,不至于在繁杂忙碌的工作中渐渐丧失学习能力。另外一个比较重要的就是,看spring源码时经常能看到一些与公司框架有异曲同工之妙的编码技巧及实现,比如异常的批量抛出,ConcurrentHashMap初始化其容量,ThreadLocal的使用等等,这些都是在读spring源码之前很少会注意或使用的。 +虽然是做技术的,但我也是一个很爱出去耍的人。构建好自己的学习模式能够让你更从容地面对工作和生活。不加班的情况下(所幸部门加班并不太多),我一般会在晚饭之后以及周日时间充电。不管是学技术还是其它什么东西,我认为 以视频为入口,以业界公认的名书继续深入理解,以社交圈的同行或网上社区为输出交流管道,最后持久化到思维导图及学习文档中。Spring 源码学习是我工作之后对自己学习模式构建的一个尝试,构建起这种学习模式之后,个人的工作和生活也变得更加协调平衡,不至于在繁杂忙碌的工作中渐渐丧失学习能力。另外一个比较重要的就是,看 Spring 源码时经常能看到一些与公司框架有异曲同工之妙的编码技巧及实现,比如异常的批量抛出,ConcurrentHashMap 初始化其容量,ThreadLocal 的使用等等,这些都是在读 Spring 源码之前很少会注意或使用的。 + ### 三、社交方面(GitHub、事业部内部授课) -       对于我来说,既然辛辛苦苦搞懂了一个技术,那就一定得输出自己的理解和经验,装波逼,不然辛辛苦苦几个月,什么产出都没有,过一段时间又把学得给忘了,这和被白嫖有什么区别。而输出知识的话当然要选一些比较优质的平台,比如GayHub,Doocs组织和其创建者就是我在GitHub上认识的,这些大佬之所以牛逼,能够成事,必然有其原因,加入他们的组织跟着混,准能学到更多我想要的东西(不仅仅是技术方面)。 +对于我来说,既然辛辛苦苦搞懂了一个技术,那就一定得输出自己的理解和经验,装波逼,不然辛辛苦苦几个月,什么产出都没有,过一段时间又把学得给忘了,这和被白嫖有什么区别。而输出知识的话当然要选一些比较优质的平台,比如 GayHub,Doocs 组织和其创建者就是我在 GitHub 上认识的,这些大佬之所以牛逼,能够成事,必然有其原因,加入他们的组织跟着混,准能学到更多我想要的东西(不仅仅是技术方面)。 -       另外,我所在的事业部也有一个“王者荣耀”的学习进阶活动,将自己的学习成果整理成简单、易于理解的内部授课也更容易获得同事的认可与信赖。 -### 个人建议: -       对于初级开发者学习spring源码来说,我建议配合阿里的《Java开发手册》一起看,因为编码能力和框架设计能力是需要很长时间的经验积累才能得到大幅提升的,而编码规范则是我们最开始就能做到并做好的事情,也是很多成熟公司越来越重视的东西。另外,阿里的《Java开发手册》中不少规范都是参考了spring框架的,这也从侧面体现了spring作为业界知名框架,其编码的规范性是深受认可的。 +另外,我所在的事业部也有一个“王者荣耀”的学习进阶活动,将自己的学习成果整理成简单、易于理解的内部授课也更容易获得同事的认可与信赖。 +### 个人建议 +对于初级开发者学习 Spring 源码来说,我建议配合阿里的《Java 开发手册》一起看,因为编码能力和框架设计能力是需要很长时间的经验积累才能得到大幅提升的,而编码规范则是我们最开始就能做到并做好的事情,也是很多成熟公司越来越重视的东西。另外,阿里的《Java 开发手册》中不少规范都是参考了 Spring 框架的,这也从侧面体现了 Spring 作为业界知名框架,其编码的规范性是深受认可的。 \ No newline at end of file