目前的水平是:能看懂部分代码,对于为什么这样设计不明所以。
在说Spring IOC的源码前,先说下IOC是什么?
IOC是一个容器,是一个编程元素,它又叫控制反转(Inverse of CONTROL),又或者叫做:依赖注入。
在开始学习的时候,我只知道:
以前:正常情况下编写代码都是在程序里面硬编码A a=new A(),并且还需要我们手
动的去创建A对象的所有协作对象。
这种创建对象方式的缺点是什么尼?A对象和它所依赖的协作对象高度耦合,在业务变动不是频繁的情况下,
这样的代码无可厚非,毕竟简单是美,但是我们假设有如下一个业务场景:用户登录。
一个简单的用户登录有什么好说的? 现在你的登录系统提出如下几个需求(需求并不合理,只是说明问
题):
1、登录失败次数过多,禁用该ip
2、登录失败次数过多,禁用登录账号
对于这样的场景,最简单的做法:实现2个类,然后在需要的地方直接new一个具体的类。如果提倡面向接口编
程,那么你可能会抽象出一个接口或者抽象类,但是你还是需要直接new一个具体的类,尽管你使用了多态,
让父类引用指向的子类对象。为了解决紧耦合,你可能会使用工厂模式来根据不同的条件生成不同的策略来实
现松耦合,所以你的代码会有部分条件判断(if else 或者switch),如果你要使用新的策略,就会去改动
你的条件判断代码,以提供新的策略被使用,这违反了开闭原则。然后我的做法是:使用xml文件来配置所有
的策略,给每个策略分配唯一标识,解析的xml策略存放在一个Map结构中,当有新的登录策略,我不用去改动
代码,只要重启下程序即可(或者提供一个动态更新的接口)。客户端(调用登录策略的对象)会根据这个唯
一标识来获取策略(又或者我可以在配置文件配置一个primary属性来标识当前使用的策略,客户端可以按照
类型或者唯一标识来选择相应的策略)
你的代码可能经历如下过程:
(UML类型画的可能不对,说明问题就好)
上图中没有画出xml的解决方案,因为从我的理解来说,这种解决方案其实就是简单的ioc容器了,但是没有
spring ioc那么复杂。
现在:现在有了IOC容器,我们只需从容器去getBean一个对象即可(ioc容器会使用push的DI风格帮我们注入该
对象所需要的所有协作对象,而不必我们自己去硬编码),并且我们可以随时去动态扩展我们新的登录策略,只要
你配置在xml里(或者通过注解和javaConfig的形式)。并且可能我们的某个的登录策略并不是线程安全的,你
可以直接在xml配置它的scope(Singleton,prototype等等)。
什么是控制反转?什么东西的控制被反转了?
控制反转又可以叫做依赖注入,也就是说:对象之间的依赖关系被反转了。
我们为了解决紧耦合的问题,一般都会面向接口编程,比如:A类依赖B类,为了解决紧耦合,提高可扩展性,
我们引入一个中间层,也就是引入一个接口C,A类和B类的关系由这个接口C来管理(这也叫作:依赖倒置原
则),本来A类直接依赖B类,现在变成:A类依赖接口C,B类也依赖接口C。(对于java来说,jdbc和jms都
是java定义的数据库访问接口和Java消息服务应用程序接口,具体的实现由不同的开发者去实现,对于使用
jdbc和jms的开发者,只要导入对应的jar即可)。
对于IOC容器来说,它就是一个中间层,一个用来管理对象生命周期和对象之间依赖关系的容器。
Spring IOC容器的缺点?
从Spring IOC的代码来看:
1、慢:
从IOC容器获取一个对象比直接在程序中new一个对象要慢。
比如:对于有参构造器的解析匹配,需要通过解析构造器参数,来确定该使用哪个构造器来创建对象。
而其中又使用了ASM(字节码操作框架,Spring 使用它来读取字节码中的每个方法栈帧中的局部变量
表,来获取方法参数名)或java8提供的新的反射API来获取方法(包括构造方法)的参数名称。
(spring采用了缓存,基本上应该只有第一次获取对象时,会有构造器的解析匹配)
并且spring ioc中有很多代码都在检测判断对象之间的循环依赖。
2、占用内存
Spring ioc将xml中的每个bean都解析成BeanDefinition,并创建了各种不同作用的缓存,
比如:会缓存单例对象。
其他的缺点暂时水平不够。