style: format documents

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

@ -15,9 +15,11 @@
- [将 bean 解析封装成 BeanDefinition](/docs/Spring/IoC/二、将bean解析封装成BeanDefinition.md) - [将 bean 解析封装成 BeanDefinition](/docs/Spring/IoC/二、将bean解析封装成BeanDefinition.md)
- [将 BeanDefinition 注册进 IoC 容器](/docs/Spring/IoC/三、将BeanDefinition注册进IoC容器.md) - [将 BeanDefinition 注册进 IoC 容器](/docs/Spring/IoC/三、将BeanDefinition注册进IoC容器.md)
- [依赖注入(DI)](/docs/Spring/IoC/四、依赖注入(DI).md) - [依赖注入(DI)](/docs/Spring/IoC/四、依赖注入(DI).md)
### AOP ### AOP
- [AOP 源码实现及分析](/docs/Spring/AOP/AOP源码实现及分析.md) - [AOP 源码实现及分析](/docs/Spring/AOP/AOP源码实现及分析.md)
- [JDK 动态代理的实现原理解析](/docs/Spring/AOP/JDK动态代理的实现原理解析.md) - [JDK 动态代理的实现原理解析](/docs/Spring/AOP/JDK动态代理的实现原理解析.md)
### SpringMVC ### SpringMVC
### SpringJDBC ### SpringJDBC

@ -1,8 +1,9 @@
理论性的文字,我觉得就没必要再扯一遍咯,大道理讲这么多,越听越迷糊。不如直接看源码+注解来的明白痛快。所以话不多说,直接上源码。 理论性的文字,我觉得就没必要再扯一遍咯,大道理讲这么多,越听越迷糊。不如直接看源码+注解来的明白痛快。所以话不多说,直接上源码。
## 1、主要的接口 ## 1、主要的接口
#### 1.1、Advice通知 ### 1.1、Advice 通知
定义了切面的增强方式,如:前置增强 BeforeAdvice后置增强 AfterAdvice异常增强 ThrowsAdvice 等。下面看两个主要的子接口的源码。 定义了切面的增强方式,如:前置增强 BeforeAdvice后置增强 AfterAdvice异常增强 ThrowsAdvice 等。下面看两个主要的子接口的源码。
```java ```java
public interface MethodBeforeAdvice extends BeforeAdvice { public interface MethodBeforeAdvice extends BeforeAdvice {
@ -22,8 +23,9 @@ public interface AfterReturningAdvice extends AfterAdvice {
} }
``` ```
#### 1.2、Pointcut方法的横切面 ### 1.2、Pointcut 方法的横切面
用来定义需要增强的目标方法的集合一般使用正则表达式去匹配筛选指定范围内的所有满足条件的目标方法。Pointcut 接口有很多实现,我们主要看一下 JdkRegexpMethodPointcut 和 NameMatchMethodPointcut 的实现原理,前者主要通过正则表达式对方法名进行匹配,后者则通过匹配方法名进行匹配。 用来定义需要增强的目标方法的集合一般使用正则表达式去匹配筛选指定范围内的所有满足条件的目标方法。Pointcut 接口有很多实现,我们主要看一下 JdkRegexpMethodPointcut 和 NameMatchMethodPointcut 的实现原理,前者主要通过正则表达式对方法名进行匹配,后者则通过匹配方法名进行匹配。
```java ```java
//JdkRegexpMethodPointcut 的实现源码 //JdkRegexpMethodPointcut 的实现源码
private Pattern[] compiledPatterns = new Pattern[0]; private Pattern[] compiledPatterns = new Pattern[0];
@ -45,8 +47,9 @@ public interface AfterReturningAdvice extends AfterAdvice {
return false; return false;
} }
``` ```
#### 1.3、Advisor通知器 ### 1.3、Advisor 通知器
将 Pointcut 和 Advice 有效地结合在一起。它定义了在哪些方法Pointcut上执行哪些动作Advice。下面看一下 DefaultPointcutAdvisor 的源码实现,它通过持有 Pointcut 和 Advice 属性来将两者有效地结合在一起。 将 Pointcut 和 Advice 有效地结合在一起。它定义了在哪些方法Pointcut上执行哪些动作Advice。下面看一下 DefaultPointcutAdvisor 的源码实现,它通过持有 Pointcut 和 Advice 属性来将两者有效地结合在一起。
```java ```java
public class DefaultPointcutAdvisor extends AbstractGenericPointcutAdvisor implements Serializable { public class DefaultPointcutAdvisor extends AbstractGenericPointcutAdvisor implements Serializable {
@ -90,9 +93,12 @@ public abstract class AbstractGenericPointcutAdvisor extends AbstractPointcutAdv
``` ```
## 2、spring AOP 的设计与实现 ## 2、spring AOP 的设计与实现
AOP 的实现代码中,主要使用了 JDK 动态代理,在特定场景下(被代理对象无 implements 的接口)也用到了 CGLIB 生成代理对象。通过 AOP 的源码设计可以看到,其先为目标对象建立了代理对象,这个代理对象的生成可以使用 JDK 动态代理或 CGLIB 完成。然后启动为代理对象配置的拦截器,对横切面(目标方法集合)进行相应的增强,将 AOP 的横切面设计和 Proxy 模式有机地结合起来,实现了在 AOP 中定义好的各种织入方式。 AOP 的实现代码中,主要使用了 JDK 动态代理,在特定场景下(被代理对象无 implements 的接口)也用到了 CGLIB 生成代理对象。通过 AOP 的源码设计可以看到,其先为目标对象建立了代理对象,这个代理对象的生成可以使用 JDK 动态代理或 CGLIB 完成。然后启动为代理对象配置的拦截器,对横切面(目标方法集合)进行相应的增强,将 AOP 的横切面设计和 Proxy 模式有机地结合起来,实现了在 AOP 中定义好的各种织入方式。
#### 2.1、ProxyFactoryBean
### 2.1、ProxyFactoryBean
这里我们主要以 ProxyFactoryBean 的实现为例,对 AOP 的实现原理进行分析。ProxyFactoryBean 主要持有目标对象 target 的代理对象 aopProxy和 Advisor 通知器,而 Advisor 持有 Advice 和 Pointcut这样就可以判断 aopProxy 中的方法 是否是某个指定的切面 Pointcut然后根据其配置的织入方向前置增强/后置增强),通过反射为其织入相应的增强行为 Advice。 这里我们主要以 ProxyFactoryBean 的实现为例,对 AOP 的实现原理进行分析。ProxyFactoryBean 主要持有目标对象 target 的代理对象 aopProxy和 Advisor 通知器,而 Advisor 持有 Advice 和 Pointcut这样就可以判断 aopProxy 中的方法 是否是某个指定的切面 Pointcut然后根据其配置的织入方向前置增强/后置增强),通过反射为其织入相应的增强行为 Advice。
先看一下 ProxyFactoryBean 的配置和使用。 先看一下 ProxyFactoryBean 的配置和使用。
```xml ```xml
<!-- 定义自己的 Advisor 实现,其中包含了 Pointcut 和 Advice --> <!-- 定义自己的 Advisor 实现,其中包含了 Pointcut 和 Advice -->
<bean id="myAdvisor" class="com.shuitu.MyAdvisor"/> <bean id="myAdvisor" class="com.shuitu.MyAdvisor"/>
@ -110,8 +116,9 @@ AOP的实现代码中主要使用了JDK动态代理在特定场景下
</property> </property>
</bean> </bean>
``` ```
#### 2.2、ProxyFactoryBean为配置的target生成AopProxy代理对象 ### 2.2、ProxyFactoryBean 为配置的 target 生成 AopProxy 代理对象
ProxyFactoryBean 的 getObject() 方法先对通知器链进行了初始化,然后根据被代理对象类型的不同,生成代理对象。 ProxyFactoryBean 的 getObject() 方法先对通知器链进行了初始化,然后根据被代理对象类型的不同,生成代理对象。
```java ```java
/** /**
* 返回一个代理对象,当用户从 FactoryBean 中获取 bean 时调用, * 返回一个代理对象,当用户从 FactoryBean 中获取 bean 时调用,
@ -133,7 +140,8 @@ ProxyFactoryBean的getObject()方法先对通知器链进行了初始化,然
} }
} }
``` ```
#### 2.3、initializeAdvisorChain()初始化Advisor链 ### 2.3、initializeAdvisorChain() 初始化 Advisor 链
```java ```java
/** /**
* 初始化 Advisor 链,可以发现,其中有通过对 IoC 容器的 getBean() 方法的调用来获取配置好的 advisor 通知器 * 初始化 Advisor 链,可以发现,其中有通过对 IoC 容器的 getBean() 方法的调用来获取配置好的 advisor 通知器
@ -194,7 +202,8 @@ ProxyFactoryBean的getObject()方法先对通知器链进行了初始化,然
} }
``` ```
生成 singleton 的代理对象在 getSingletonInstance 方法中完成,这是 ProxyFactoryBean 生成 AopProxy 代理对象的调用入口。代理对象会封装对 target 对象的调用,针对 target 对象的方法调用会被这里生成的代理对象所拦截。 生成 singleton 的代理对象在 getSingletonInstance 方法中完成,这是 ProxyFactoryBean 生成 AopProxy 代理对象的调用入口。代理对象会封装对 target 对象的调用,针对 target 对象的方法调用会被这里生成的代理对象所拦截。
#### 2.4、getSingletonInstance()生成单例代理对象
### 2.4、getSingletonInstance() 生成单例代理对象
```java ```java
/** /**
* 返回此类代理对象的单例实例,如果尚未创建该实例,则使用单例模式的懒汉式 创建它 * 返回此类代理对象的单例实例,如果尚未创建该实例,则使用单例模式的懒汉式 创建它
@ -225,7 +234,9 @@ ProxyFactoryBean的getObject()方法先对通知器链进行了初始化,然
return aopProxy.getProxy(this.proxyClassLoader); return aopProxy.getProxy(this.proxyClassLoader);
} }
``` ```
上面的 createAopProxy() 方法,调用了 ProxyFactoryBean 的父类 ProxyCreatorSupport 中的实现。 上面的 createAopProxy() 方法,调用了 ProxyFactoryBean 的父类 ProxyCreatorSupport 中的实现。
```java ```java
public class ProxyCreatorSupport extends AdvisedSupport { public class ProxyCreatorSupport extends AdvisedSupport {
@ -255,7 +266,9 @@ public class ProxyCreatorSupport extends AdvisedSupport {
} }
``` ```
下面看一下 AopProxyFactory 接口的实现类 DefaultAopProxyFactory 的代码。 下面看一下 AopProxyFactory 接口的实现类 DefaultAopProxyFactory 的代码。
```java ```java
public AopProxy createAopProxy(AdvisedSupport config) throws AopConfigException { public AopProxy createAopProxy(AdvisedSupport config) throws AopConfigException {
//AopProxy 代理对象的生成过程: //AopProxy 代理对象的生成过程:
@ -283,8 +296,10 @@ public class ProxyCreatorSupport extends AdvisedSupport {
} }
} }
``` ```
可以看到其根据目标对象是否实现了接口,而决定是使用 JDK 动态代理还是 CGLIB 去生成代理对象,而 AopProxy 接口的实现类也只有 JdkDynamicAopProxy 和 CglibAopProxy 这两个。 可以看到其根据目标对象是否实现了接口,而决定是使用 JDK 动态代理还是 CGLIB 去生成代理对象,而 AopProxy 接口的实现类也只有 JdkDynamicAopProxy 和 CglibAopProxy 这两个。
#### 2.5、JDK生成AopProxy代理对象
### 2.5、JDK 生成 AopProxy 代理对象
```java ```java
final class JdkDynamicAopProxy implements AopProxy, InvocationHandler, Serializable { final class JdkDynamicAopProxy implements AopProxy, InvocationHandler, Serializable {
@ -316,8 +331,10 @@ final class JdkDynamicAopProxy implements AopProxy, InvocationHandler, Serializa
} }
} }
``` ```
通过 JdkDynamicAopProxy 的源码可以非常清楚地看到,其使用了 JDK 动态代理的方式生成了代理对象。JdkDynamicAopProxy 实现了 InvocationHandler 接口,并通过 Proxy.newProxyInstance() 方法生成代理对象并返回。 通过 JdkDynamicAopProxy 的源码可以非常清楚地看到,其使用了 JDK 动态代理的方式生成了代理对象。JdkDynamicAopProxy 实现了 InvocationHandler 接口,并通过 Proxy.newProxyInstance() 方法生成代理对象并返回。
#### 2.6、CGLIB生成AopProxy代理对象CglibAopProxy
### 2.6、CGLIB 生成 AopProxy 代理对象CglibAopProxy
```java ```java
public Object getProxy(ClassLoader classLoader) { public Object getProxy(ClassLoader classLoader) {
if (logger.isDebugEnabled()) { if (logger.isDebugEnabled()) {
@ -397,7 +414,8 @@ final class JdkDynamicAopProxy implements AopProxy, InvocationHandler, Serializa
## 3、spring AOP 拦截器调用的实现 ## 3、spring AOP 拦截器调用的实现
在 spring AOP 通过 JDK 的 Proxy 类生成代理对象时,相关的拦截器已经配置到了代理对象持有的 InvocationHandler(即ProxyBeanFactory) 的 invoke() 方法中,拦截器最后起作用,是通过调用代理对象的目标方法时,在代理类中触发了 InvocationHandler 的 invoke() 回调。通过 CGLIB 实现的 AOP原理与此相似。 在 spring AOP 通过 JDK 的 Proxy 类生成代理对象时,相关的拦截器已经配置到了代理对象持有的 InvocationHandler(即ProxyBeanFactory) 的 invoke() 方法中,拦截器最后起作用,是通过调用代理对象的目标方法时,在代理类中触发了 InvocationHandler 的 invoke() 回调。通过 CGLIB 实现的 AOP原理与此相似。
#### 3.1、JdkDynamicAopProxy的invoke()拦截
### 3.1、JdkDynamicAopProxy 的 invoke() 拦截
前面已经通过两种不同的方式生成了 AopProxy 代理对象,下面我们先看一下 JdkDynamicAopProxy 中的 invoke() 回调方法中对拦截器调用的实现。 前面已经通过两种不同的方式生成了 AopProxy 代理对象,下面我们先看一下 JdkDynamicAopProxy 中的 invoke() 回调方法中对拦截器调用的实现。
```java ```java
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
@ -479,8 +497,9 @@ final class JdkDynamicAopProxy implements AopProxy, InvocationHandler, Serializa
} }
} }
``` ```
#### 3.2、CglibAopProxy的intercept()拦截 ### 3.2、CglibAopProxy intercept() 拦截
CglibAopProxy 的 intercept() 回调方法实现和 JdkDynamicAopProxy 的 invoke() 非常相似,只是在 CglibAopProxy 中构造 CglibMethodInvocation 对象来完成拦截器链的调用,而在 JdkDynamicAopProxy 中则是通过构造 ReflectiveMethodInvocation 对象来完成的。 CglibAopProxy 的 intercept() 回调方法实现和 JdkDynamicAopProxy 的 invoke() 非常相似,只是在 CglibAopProxy 中构造 CglibMethodInvocation 对象来完成拦截器链的调用,而在 JdkDynamicAopProxy 中则是通过构造 ReflectiveMethodInvocation 对象来完成的。
```java ```java
public Object intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy) throws Throwable { public Object intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
Object oldProxy = null; Object oldProxy = null;
@ -524,8 +543,9 @@ CglibAopProxy的intercept()回调方法实现和JdkDynamicAopProxy的invoke()非
} }
} }
``` ```
#### 3.3、目标对象中目标方法的调用 ### 3.3、目标对象中目标方法的调用
对目标对象中目标方法的调用,是在 AopUtils 工具类中利用反射机制完成的。具体代码如下: 对目标对象中目标方法的调用,是在 AopUtils 工具类中利用反射机制完成的。具体代码如下:
```java ```java
public abstract class AopUtils { public abstract class AopUtils {
@ -554,8 +574,9 @@ public abstract class AopUtils {
} }
} }
``` ```
#### 3.4、AOP拦截器链的调用 ### 3.4、AOP 拦截器链的调用
JdkDynamicAopProxy 和 CglibAopProxy 虽然使用了不同的代理对象,但对 AOP 拦截的处理却是相同的,都是通过 ReflectiveMethodInvocation 的 proceed() 方法实现的。 JdkDynamicAopProxy 和 CglibAopProxy 虽然使用了不同的代理对象,但对 AOP 拦截的处理却是相同的,都是通过 ReflectiveMethodInvocation 的 proceed() 方法实现的。
```java ```java
public Object proceed() throws Throwable { public Object proceed() throws Throwable {
//从拦截器链中按顺序依次调用拦截器,直到所有的拦截器调用完毕,开始调用目标方法, //从拦截器链中按顺序依次调用拦截器,直到所有的拦截器调用完毕,开始调用目标方法,
@ -591,7 +612,7 @@ JdkDynamicAopProxy和CglibAopProxy虽然使用了不同的代理对象但对A
} }
} }
``` ```
#### 3.5、配置通知器 ### 3.5、配置通知器
AdvisedSupport 中实现了获取拦截器链的方法,并使用了缓存。 AdvisedSupport 中实现了获取拦截器链的方法,并使用了缓存。
```java ```java
/** /**
@ -612,7 +633,9 @@ AdvisedSupport中实现了获取拦截器链的方法并使用了缓存。
return cached; return cached;
} }
``` ```
获取拦截器链的工作是由 AdvisorChainFactory 完成的,他是一个拦截器链的生成工厂。由于 AdvisorChainFactory 接口只有一个实现类 DefaultAdvisorChainFactory所以我们直接看这个类中的实现就行咯。 获取拦截器链的工作是由 AdvisorChainFactory 完成的,他是一个拦截器链的生成工厂。由于 AdvisorChainFactory 接口只有一个实现类 DefaultAdvisorChainFactory所以我们直接看这个类中的实现就行咯。
```java ```java
public class DefaultAdvisorChainFactory implements AdvisorChainFactory, Serializable { public class DefaultAdvisorChainFactory implements AdvisorChainFactory, Serializable {
@ -684,7 +707,9 @@ public class DefaultAdvisorChainFactory implements AdvisorChainFactory, Serializ
} }
``` ```
这里的 advisor 通知器是从 AdvisedSupport 中获取的,而 advisor 的初始化则是在 ProxyFactoryBean 的 getObject() 方法中完成的。 这里的 advisor 通知器是从 AdvisedSupport 中获取的,而 advisor 的初始化则是在 ProxyFactoryBean 的 getObject() 方法中完成的。
```java ```java
/** /**
* 返回一个代理对象,当用户从 FactoryBean 中获取 bean 时调用, * 返回一个代理对象,当用户从 FactoryBean 中获取 bean 时调用,
@ -766,9 +791,12 @@ public class DefaultAdvisorChainFactory implements AdvisorChainFactory, Serializ
this.advisorChainInitialized = true; this.advisorChainInitialized = true;
} }
``` ```
注意Advisor 本身就被配置为 bean所以它的获取也是通过 IoC 容器获得的。 注意Advisor 本身就被配置为 bean所以它的获取也是通过 IoC 容器获得的。
#### 3.6、Advice通知的实现
### 3.6、Advice 通知的实现
从 DefaultAdvisorChainFactory 类中的 getInterceptorsAndDynamicInterceptionAdvice() 方法我们可以看到,其通过 AdvisorAdapterRegistry 实例对象的 getInterceptors() 方法,利用配置的 advisor 完成了对拦截器的适配和注册。 从 DefaultAdvisorChainFactory 类中的 getInterceptorsAndDynamicInterceptionAdvice() 方法我们可以看到,其通过 AdvisorAdapterRegistry 实例对象的 getInterceptors() 方法,利用配置的 advisor 完成了对拦截器的适配和注册。
```java ```java
public List<Object> getInterceptorsAndDynamicInterceptionAdvice( public List<Object> getInterceptorsAndDynamicInterceptionAdvice(
Advised config, Method method, Class targetClass) { Advised config, Method method, Class targetClass) {
@ -820,7 +848,9 @@ public class DefaultAdvisorChainFactory implements AdvisorChainFactory, Serializ
return interceptorList; return interceptorList;
} }
``` ```
DefaultAdvisorAdapterRegistry 的 getInterceptors() 方法封装了 advice 织入实现的入口。 DefaultAdvisorAdapterRegistry 的 getInterceptors() 方法封装了 advice 织入实现的入口。
```java ```java
public class DefaultAdvisorAdapterRegistry implements AdvisorAdapterRegistry, Serializable { public class DefaultAdvisorAdapterRegistry implements AdvisorAdapterRegistry, Serializable {
@ -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 ```java
class MethodBeforeAdviceAdapter implements AdvisorAdapter, Serializable { class MethodBeforeAdviceAdapter implements AdvisorAdapter, Serializable {
@ -901,7 +934,9 @@ class MethodBeforeAdviceAdapter implements AdvisorAdapter, Serializable {
} }
``` ```
可以看到,其中的 getInterceptor() 方法把 advice 从 advisor 中取出来,然后创建了一个 MethodBeforeAdviceInterceptor 对象,并返回,这个对象中持有对 advice 的引用。下面我们看一下 MethodBeforeAdviceInterceptor 拦截器的源码实现。 可以看到,其中的 getInterceptor() 方法把 advice 从 advisor 中取出来,然后创建了一个 MethodBeforeAdviceInterceptor 对象,并返回,这个对象中持有对 advice 的引用。下面我们看一下 MethodBeforeAdviceInterceptor 拦截器的源码实现。
```java ```java
public class MethodBeforeAdviceInterceptor implements MethodInterceptor, Serializable { public class MethodBeforeAdviceInterceptor implements MethodInterceptor, Serializable {
@ -927,11 +962,10 @@ public class MethodBeforeAdviceInterceptor implements MethodInterceptor, Seriali
} }
``` ```
可以看到MethodBeforeAdviceInterceptor 的 invoke() 方法先是触发了 advice 的 before() 方法,然后才是 MethodInvocation 的 proceed() 方法调用。 可以看到MethodBeforeAdviceInterceptor 的 invoke() 方法先是触发了 advice 的 before() 方法,然后才是 MethodInvocation 的 proceed() 方法调用。
回顾一下之前的代码,在 AopProxy 代理对象触发的 ReflectiveMethodInvocation 的 proceed() 中,在取得拦截器 interceptor 后调用了其 invoke() 方法。按照 AOP 的配置规则ReflectiveMethodInvocation 触发的拦截器 invoke() 回调,最终会根据 advice 类型的不同,触发 spring 对不同的 advice 的拦截器封装,比如 MethodBeforeAdvice 最终会触发 MethodBeforeAdviceInterceptor 的 invoke() 回调,其它两个以此类推,这里就不逐一分析咯。 回顾一下之前的代码,在 AopProxy 代理对象触发的 ReflectiveMethodInvocation 的 proceed() 中,在取得拦截器 interceptor 后调用了其 invoke() 方法。按照 AOP 的配置规则ReflectiveMethodInvocation 触发的拦截器 invoke() 回调,最终会根据 advice 类型的不同,触发 spring 对不同的 advice 的拦截器封装,比如 MethodBeforeAdvice 最终会触发 MethodBeforeAdviceInterceptor 的 invoke() 回调,其它两个以此类推,这里就不逐一分析咯。
另外可以结合我GitHub上对spring框架源码的阅读及个人理解一起看会更有助于各位开发大佬理解如果对你们有帮助的还望各位老爷watchstarfork素质三连一波地址 另外,可以结合我 GitHub 上对 spring 框架源码的阅读及个人理解一起看,会更有助于各位开发大佬理解,如果对你们有帮助的,还望各位老爷 watchstarfork素质三连一波地址https://github.com/AmyliaY/spring-aop-reading
spring-aop-reading https://github.com/AmyliaY/spring-aop-reading

@ -1,4 +1,5 @@
最近在看springAOP部分的源码所以对JDK动态代理具体是如何实现的这件事产生了很高的兴趣而且能从源码上了解这个原理的话也有助于对spring-aop模块的理解。话不多说上代码。 最近在看 SpringAOP 部分的源码,所以对 JDK 动态代理具体是如何实现的这件事产生了很高的兴趣,而且能从源码上了解这个原理的话,也有助于对 spring-aop 模块的理解。话不多说,上代码。
```java ```java
/** /**
* 一般会使用实现了 InvocationHandler 的类 作为代理对象的生产工厂, * 一般会使用实现了 InvocationHandler 的类 作为代理对象的生产工厂,

@ -346,6 +346,3 @@ FileSystemXmlApplicationContext从上层体系的各抽象类中继承了大量
} }
``` ```
至此我们可以看到FileSystemXmlApplicationContext 的 getResourceByPath() 方法返回了一个 FileSystemResource 对象,接下来 spring 就可以对这个对象进行相关的 I/O 操作,进行 BeanDefinition 的读取和载入了。 至此我们可以看到FileSystemXmlApplicationContext 的 getResourceByPath() 方法返回了一个 FileSystemResource 对象,接下来 spring 就可以对这个对象进行相关的 I/O 操作,进行 BeanDefinition 的读取和载入了。

@ -114,5 +114,3 @@ spring-context https://github.com/AmyliaY/spring-context-reading
// 存储注册信息的 BeanDefinition 集合,也就是所谓的 IoC 容器 // 存储注册信息的 BeanDefinition 集合,也就是所谓的 IoC 容器
private final Map<String, BeanDefinition> beanDefinitionMap = new ConcurrentHashMap<String, BeanDefinition>(64); private final Map<String, BeanDefinition> beanDefinitionMap = new ConcurrentHashMap<String, BeanDefinition>(64);
``` ```

@ -885,13 +885,3 @@ spring-context https://github.com/AmyliaY/spring-context-reading
} }
``` ```
经过这样逐层地解析,我们在配置文件中定义的 Bean 就被整个解析成了可以被 IoC 容器装载和使用的 BeanDefinition这种数据结构可以让 IoC 容器执行索引、查询等操作。经过上述解析得到的 BeanDefinition接下来我们就可以将它注册到 IoC 容器中咯。 经过这样逐层地解析,我们在配置文件中定义的 Bean 就被整个解析成了可以被 IoC 容器装载和使用的 BeanDefinition这种数据结构可以让 IoC 容器执行索引、查询等操作。经过上述解析得到的 BeanDefinition接下来我们就可以将它注册到 IoC 容器中咯。

@ -1444,6 +1444,3 @@ lazy-init触发的预实例化和依赖注入发生在IoC容器完成对BeanD
} }
``` ```

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

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