Spring Framework(下文简称为Spring)提供了很多功能,使得很多简单的应用开发,尤其是一些政府,小企业应用,变成Action->Service->DAO+一堆Interface(我讨厌很多莫名其妙的接口),just make skins for dababase.

本文的画图工具:mindmanager
原文地址:http://my.oschina.net/geecoodeer/blog/191815

当然,Spring 的框架实现还是很强大的,虽然里面没啥高深算法,但是的确使开发工作简便了不少(Joel曾说,OOP太简单了,不足以分辨出优秀的程序员… )。但是,对一个对未知事物充满好奇心的人来说,理解Spring的内部实现还是很有必要的。

本文主要分为2个章节,分别是IOC实现原理,AOP实现原理

注:本文不会太多涉及Spring Framework 源码内部实现框架细节,细节部分请阅读本文最后的参考章节。

====

引论

为什么需要IOC

对象之间的依赖关系本来应该是直接是通过在代码中,A类直接创建另外一个B类的实例,结果变成是在IOC容器中,通过配置文件来配置对象依赖关系。至于为什么需要IOC,网上的说法是主要是因为复杂对象之间的依赖关系通过IOC管理起来比较简单,不需要手写大量代码。

其实,我更喜欢这几个理由,比如说

  1. 只要改配置文件,不需要改代码就完成对象依赖修改
  2. 提供方便的单例类
  3. 结合AOP,能够实现更强大的功能
  4. Spring对很多常用功能,如jdbc,rmi,jms进行了一些封装,方便我们的使用。
  5. 由于Java语言的限制,以及Spring可以控制对象的整个生命周期,从而使框架的实现难度大大降低,框架的上手难度也大大降低。

====

如何描述对象的依赖关系

我们需要配置文件来保存对象的依赖关系。

既然说到配置文件,那么就必须涉及到配置文件的解析,解析前需要校验文件合法性,解析后的内容再与相应的java类映射。

但是在开始解析文件之前,首先要搞清楚2个问题:

  1. 解析的文件在哪里,是指定的绝对路径还是相对路径还是classpath
  2. 文件的格式是什么,是xml还是properties,甚至是文本文件还是二进制文件。如果是xml,它相应的dtd或xsd是什么?

搞清楚上述问题后,我们再进行选择解析组件。就XML而言,简单的文件直接通过JAXB/Digester来解决;复杂的一般是使用SAX来解决,例如Spring和Activiti。

====

正文

解析配置文件

回到正题,从spring的单元测试中,找到这段代码: 
void org.springframework.beans.factory.xml.XmlBeanFactoryTests.testInitMethodIsInvoked() throws Exception

    @Test
public void testInitMethodIsInvoked() throws Exception {DefaultListableBeanFactory xbf = new DefaultListableBeanFactory();new XmlBeanDefinitionReader(xbf).loadBeanDefinitions(INITIALIZERS_CONTEXT);DoubleInitializer in = (DoubleInitializer) xbf.getBean("init-method1");// Initializer should have doubled valueassertEquals(14, in.getNum());
}

执行核心时序如下:

 
时序解释如下:

  1. 标记1,2,4,5,7 都是用于扩展框架功能.标记1,2支持解析bean定义前后前后做些处理;标记4,7主要是用来支持解析自定义标签,详细见 基于Spring可扩展Schema提供自定义配置支持;标记5用来支持当解析完成后,做一些事件触发
  2. 标记3主要完成解析bean的xml配置文件id,name以及其他所有属性
  3. 标记5主要完成将解析完成的bean放到ConcurrentHashMap中

====

获取bean

在单元测试中void org.springframework.context.support.ClassPathXmlApplicationContextTests.testSingleConfigLocation() ,我们找到这段话

@Test
public void testSingleConfigLocation() {
ClassPathXmlApplicationContext ctx = new ClassPathXmlApplicationContext(FQ_SIMPLE_CONTEXT);
assertTrue(ctx.containsBean("someMessageSource"));
ctx.close();
}

执行核心时序如下:

 
时序解释如下:

  1. 标记1获取bean的Class对象
  2. 标记2获取bean的实例
  3. 标记3对bean的属性进行赋值

====

AOP实现原理

引论

AOP完成了初学者一些看起来很神奇的功能,其实底层都是通过字节码增强的方式来透明地完成了一些Magic.AOP过于强大,以至于常有一些不适合的场景,比如用AOP进行打日志

阅读本章之前需要了解一下几个概念:静态代理,JDK动态代理,字节码增强动态代理

====

获得代理类实例

配置文件如下:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN 2.0//EN" "http://www.springframework.org/dtd/spring-beans-2.0.dtd">
<beans>
<bean id="human" class="com.vavi.proxy.Person">
</bean>
<bean id="sleepHelper" class="com.vavi.spring.aop.jdk.SleepAdvice">
</bean>
<bean id="sleepPointcut" class="org.springframework.aop.support.JdkRegexpMethodPointcut"><property name="pattern" value=".*sleep" />
</bean>
<bean id="sleepHelperAdvisor" class="org.springframework.aop.support.DefaultPointcutAdvisor"><property name="advice" ref="sleepHelper" /><property name="pointcut" ref="sleepPointcut" />
</bean>
<bean id="humanProxy" class="org.springframework.aop.framework.ProxyFactoryBean"><property name="target" ref="human" /><property name="interceptorNames" value="sleepHelperAdvisor" /><property name="proxyInterfaces" value="com.vavi.proxy.Sleepable" />
</bean>
</beans>

测试类代码如下:

public class SpringProxyTest {public static void main(String[] args) {
ApplicationContext appCtx = new ClassPathXmlApplicationContext("applicationContext.xml");
Sleepable sleeper = (Sleepable) appCtx.getBean("humanProxy");
sleeper.sleep();
}
}

核心时序如下: 
 
时序解释如下:

  1. 标记1表示初始化advisor链
  2. 标记2表示获取默认的JDK动态代理
  3. 标记4表示通过AopProxyUtils对代理类的接口的个数进行了增加,interface org.springframework.aop.SpringProxy, interface org.springframework.aop.framework.Advised
  4. 标记5表示通过JDK API完成代理类实例的创建

====

完成代理方法执行

核心时序如下: 
 
时序解释如下:

  1. 获得拦截器链,依次执行
  2. 里面有个比较好玩的计数器,用来实现拦截器调用;感兴趣的读者请自行翻源码吧.

====

总结

  1. 刚开始看源码时,其实不明白ApplicationContext和DefaultListableBeanFactory的区别,从源码的实现细节来看,ApplicationContext的内部实现是调用了DefaultListableBeanFactory的方法的,其他还做了一些增强;具体增强了哪些,目前还没有时间去一探究竟.
  2. sax解析,这个是体力活,但是xml解析命名实在比较操蛋;针对xml,有些必须知道的知识:namespace,xsd,dtd
  3. Spring对象继承,接口实现层次较深,并不是很好。对于一般学习者来讲,尤其有些类的命名还不是很讲究,其实很难记住. 所以,不需要记住这些类的主要职责,还是记住它主要做些什么,对我们的日常开发有什么作用,如何扩展。
  4. 代码里面存在大量的不鼓励的instanceof,比如针对FactoryBean的特殊处理
  5. BeanDefinition –> AbstractBeanDefinition ->GenericBeanDefinition 接口,抽象类,实现类,模板模式也经常看到
  6. 之所以不贴代码,主要是现在网页代码显示不好看,没有IDE看起来舒服,二是给出重点关注的地方,真正需要理解的话还是自己去看代码去.自己debug 
    需要了解核心实现思想,关键算法以及 关注扩展点 ,需要调试bug或进行代码修改时再进一步理解里面的繁杂细节。
  7. AOP那几个拦截点的拦截的确不错,但是解释成英文就是有点不好理解. pointcut,表示拦截哪些类,哪些方法,强调where;joinPoint,表示在方法调用前,调用后,返回值或抛出异常时进行拦截,强调when;advice,强调在拦截时做什么,强调what.
  8. 初步看了编译原理 , 以及何登成上次的 mysql 源码调用微博(代码本质上是一棵树), 我觉得用树应该可以很清晰描述代码调用关系 ; 现在结合思维导图工具 , 我觉得可以很好地描述代码调用关系.因为复杂代码情况下,时序图横向较长, 很容易看错 ; 并且要来回拖动 ; 所以我个人感觉这种表达方式 , 我还是蛮喜欢的 .

=====

参考

Why need IOC :http://stackoverflow.com/questions/871405/why-do-i-need-an-ioc-container-as-opposed-to-straightforward-di-code

Spring 框架的设计理念与设计模式分析

Spring 技术内幕

Spring Framework 源码阅读相关推荐

  1. Spring Framework 源码阅读+吐槽

    2019独角兽企业重金招聘Python工程师标准>>> 吐槽 有人说JAVA企业级应用没啥技术含量,玩不了大数据的技术人员是很杯具的存在.大部分生命的时间是处理复杂的业务,沉迷于一堆 ...

  2. Android Framework源码阅读计划(2)——LocationManagerService.java

    Android Framework源码阅读计划 Android Framework源码阅读计划(1)--LocationManager.java Android Framework源码阅读计划(2)- ...

  3. Spring框架源码阅读读后感

    Spring框架源码阅读读后感 spring的bean生命周期,从上到下依次完成,本人在阅读源码时总结得出此步骤,当然,spring是一个强大的框架,其对bean的生命周期管理只是其中的一部分,本人也 ...

  4. Spring Framework 源码解析课程大纲

    首先明确我们读Spring Framework的源码的目的是什么? 是为了显示自己很牛X么? Spring源码阅读的难度在于其呢内容庞大,并不是里面有很复杂的算法,需要高智商才能理解.所以你搞懂了Sp ...

  5. Spring Framework源码使用 spring-aspects AOP遇到的问题

    在编译好的Spring Framework使用Aspects AOP 继续上一篇,编译Spring Framework5.1源码文章.现在要使用Aspects AOP也可能会遇到一些问题,现在这篇文章 ...

  6. 【面试】足够应付面试的Spring事务源码阅读梳理(建议珍藏)

    Starting from a joke 问:把大象放冰箱里,分几步? 答:三步啊,第一.把冰箱门打开,第二.把大象放进去,第三.把冰箱门带上. 问:实现Spring事务,分几步? 答:三步啊,第一. ...

  7. 足够应付面试的Spring事务源码阅读梳理

    来源:编程新说 Starting from a joke 问:把大象放冰箱里,分几步? 答:三步啊,第一.把冰箱门打开,第二.把大象放进去,第三.把冰箱门带上. 问:实现Spring事务,分几步? 答 ...

  8. 事务注解放到类上面 下面私有方法有效吗_【面试】足够应付面试的Spring事务源码阅读梳理(建议珍藏)...

    Starting from a joke 问:把大象放冰箱里,分几步? 答:三步啊,第一.把冰箱门打开,第二.把大象放进去,第三.把冰箱门带上. 问:实现Spring事务,分几步? 答:三步啊,第一. ...

  9. Spring Cloud源码阅读(一)

    问题 Spring Cloud如何创建两个上下文环境的 Spring Cloud如何加载bootstrap.yml配置文件的 Spring Cloud Config是如何获取远程配置的 Spring ...

最新文章

  1. numpy常用函数之random.normal函数
  2. C#中的参数传递(转)
  3. oracle怎么判断主库还是备库,Oracle查看归档是否被备库应用
  4. 全国计算机等级考试题库二级C操作题100套(第93套)
  5. 一学就废的并查集它来了
  6. 犹豫了几个月,我还是跳槽了....
  7. 156万在校大学生!中国高校第一城诞生
  8. Codeforces Gym 100203G G - Good elements 暴力
  9. 目录启动CXF启动报告LinkageError异常以及Java的endorsed机制
  10. mysql 5.7 sql mode_MySQL 5.7版本sql_mode=only_full_group_by问题
  11. 最简洁的PHP把PHP生成HTML代码
  12. 骨传导耳机听歌音质怎么样、公认音质好的骨传导耳机排名
  13. Edify Script (Android Shell)定制Twrp刷机包
  14. (二)代理模式详解(包含原理详解)
  15. Python常见主流框架简介
  16. windows远程桌面反向代理
  17. 写得真励志,深度发展,成为不可替代的技术员
  18. 网络市场与群体练习题
  19. 碉堡了: 兜宝让iPhone双卡双待成为现实
  20. CNN卷积神经网络案例程序源代码合集matlab/Python等

热门文章

  1. 便利贴--46{HbuildX连接夜神模拟器}
  2. 自制Ping(2) Structures
  3. 剑指Offer——顺丰笔试题+知识点总结
  4. Unity Shader 模板测试
  5. 当Android Studio安装时提示couldn‘t detect a java development kit
  6. BZOJ1191 超级英雄Hero
  7. 卢松松:美丽说的SEO有多牛
  8. 【springboot+vue项目学习】3引入element-ui插件
  9. 实时道路路况计算的认识
  10. java jdbctemplate update_java - Spring JdbcTemplate.update()不更新行 - 堆栈内存溢出