Java server框架之(1):spring中的IoC
为什么需要IoC?
一般的对象耦合是在编译时确定的,也就是说当我们写如下类:
1 public class StaticCoupling {
2
3 String s = new String("hzg1981");
4
5 }
的时候,类StaticCoupling在编译期间就跟String类耦合在了一起。 在代码静态分析时,就可以确认它们之间的耦合。这种耦合使得代码的灵活性不够,因此需要一种方法对这种耦合关系进一步解耦。
什么是IoC?
IoC是Inversion of Control的缩写,翻译成中文就是控制反转。控制反转的意思就是说把对象的控制权进行转移,原来是对象A依赖对象B,那么就在A中new一个B的对象,在这种场景下,对象的控制权在对象A。实现控制反转后,对象的控制权转移到第三方,比如转移交了IoC容器。IoC容器可以理解成一个更高级的创建工厂(工厂模式):你要什么对象,它就给你什么对象,有了IoC容器,依赖关系就变了,原先的依赖关系就没了(依赖关系new没有了,对象A对象B之间的耦合也被解耦了),它们都依赖IoC容器了,通过IoC容器来建立它们之间的关系。
IoC,直观地讲,就是容器控制程序之间的关系,而非传统实现中,由程序代码直接操控。这也就是所谓“控制反转”的概念所在。控制权由应用代码中转到了外部容器,控制权的转移就是所谓的反转。IoC还有另一个名字——“依赖注入(Dependency Injection)”。从名字上理解,所谓依赖注入,即组件之间的依赖关系由容器的运行期决定,形象的说,就是容器动态地将某种依赖关系注入到组件中。
IOC 是在运行期间才使用assembler object绑定需要耦合的对象。绑定过程是由依赖注入(DI,Dependence Injection)实现的。或者说,创建被调用者的工作由spring来完成,然后注入调用者,因此也称为依赖注入。需要注意的是IOC和DI是有区别的。IOC是一种设计范式(design paradigm),IOC是依赖于DI的,DI用来实现IOC。 DI是一种模式,用来创建对象的实例,这些实例被其他对象依赖,但是它们在编译时并不知道这些实例的类型。也就是说,DI是一种可以实现IOC的设计模式。DI不是唯一可以实现IOC的设计模式,有以下三种设计模式可以实现IOC设计范式:
1 工厂模式: factory pattern
2 服务定位器模式:service locator pattern
3 依赖注入模式。
在Spring这种IoC容器中,IoC是通过DI来实现的。依赖注入又可以分为三种类型: 构造函数注入(constructor injection) Setter注入(setter injection) 以及接口注入(interface injection)。
构造函数注入:
1 public class DIByConstructor { 2 private final DataSource dataSource; 3 private final String message; 4 public DIByConstructor(DataSource ds, String msg) { 5 this.dataSource = ds; 6 ssage = msg; 7 } 8 …… 9 }
接口注入:
1 public class ClassA { 2 private InterfaceB clzB; 3 public init() { 4 Ojbect obj = Class.forName(Config.BImplementation).newInstance(); 5 clzB = (InterfaceB)obj; 6 } 7 …… 8 }
Spring中的IoC,通俗的说就是可以在项目或者服务启动使用的时候,给我们一次性准备好我们程序中配置好的所需要的对象,并且自动将对象set到需要的对象中。例如A类中的成 员变量中有一个B类的引用,那么生成对象的时候会生成一个A的对象和B的对象,并且将B的对象通过A的构造方法或者set方法将B对象设置到A对象中。其实看来完成的功能很简单就是为了让我们在代码中减少使用new关键字的次数,让大多数程序中的new的性能开销和代码开销集中在项目启动或者服务启动的时候。在程序的编写过程中不会一次又一次的写A a = new A()这种没有营养的代码,在程序运行的时候不会因为多次的new对象浪费时间。
除了Spring之外,Sun ONE技术体系下的IOC容器还有:轻量级的有Guice、Pico Container、Avalon、HiveMind;重量级的有EJB;不轻不重的有JBoss,Jdon等等。Spring框架作为Java开发中 SSH(Struts、Spring、Hibernate)三剑客之一,大中小项目中都有使用,非常成熟,应用广泛,EJB在关键性的工业级项目中也被使 用,比如某些电信业务。
IoC与其他概念的关系
1 在OOD中,有一个常用的概念就是SOLID,它指的是Single Responsibility、Open-Close、L里氏替换、Independent Interface独立接口和Dependency Inversion五大OOD设计原则,这些设计原则是是设计模式的基础,设计模式的目的就是为了实现这些原则。SOLID中的D,Dependency Inversion,可以使用IoC来实现。从这个角度讲,IoC更像是一种设计模式。
2 IoC是使用反射来实现的,反射是IoC的基础。反射的应用是很广泛的,很多的成熟的框架,比如象Java中的Hibernate、Spring框架,.Net中 NHibernate、Spring.Net框架都是把“反射”做为最基本的技术手段。 反射技术其实很早就出现了,但一直被忽略,没有被进一步的利用。当时的反射编程方式相对于正常的对象生成方式要慢至少得10倍。现在的反射技术经过改良优 化,已经非常成熟,反射方式生成对象和通常对象生成方式,速度已经相差不大了,大约为1-2倍的差距。
3 我们可以把IOC容器的工作模式看做是工厂模式的升华,可以把IOC容器看作是一个工厂,这个工厂里要生产的对象都在配置文件中给出定义,然后利用编程语 言的的反射编程,根据配置文件中给出的类名生成相应的对象。从实现来看,IOC是把以前在工厂方法里写死的对象生成代码,改变为由配置文件来定义,也就是把工厂和对象生成这两者独立分隔开来,目的就是提高灵活性和可维护性。
IoC的简单实现
IoC说白了就是由我们平常的new转成了使用反射来获取类的实例,相信任何人只要会用java的反射机制,那么自己写一个IOC框架也不是不可能的。
1 public ObjectgetInstance(String className) throws Exception { 2 Object obj = Class.forName(className).newInstance(); 3 Method[] methods = obj.getClass().getMethods(); 4 for (Method method : methods) { 5 if (method.getName().intern() == "setString") { 6 method.invoke(obj, "hello world!"); 7 } 8 } 9 }
上面的一个方法我们就很简单的使用了反射为指定的类的setString方法来设置一个hello world!字符串。其实可以看到IOC真的很简单,当然了IOC简单并不表示spring的IOC就简单,spring的IOC的功能强大就在于有一系列非常强大的配置文件维护类,它们可以维护spring配置文件中的各个类的关系,这才是spring的IOC真正强大的地方。在spring的Bean 定义文件中,不仅可以为定义Bean设置属性,还支持Bean之间的继承、Bean的抽象和不同的获取方式等等功能。在Spring中,对象是由IoC容器根据xml配置文件中的类名创建的,然后在应用程序需要的时候,使用构造函数注入或者其他注入方式,注入到应用程序中,从而避免了反复使用new操作创建对象。
Spring中的IoC
当前最新的Spring版本为4.3.Spring是一种IOC容器(Container),在Spring中,主要使用BeanFactory接口和ApplicationContext接口的实现类来实现IoC。客户代码甚至不需要实例化Spring的IoC容器。
BeanFactory
org.springframework.beans.factory.BeanFactory是一个接口,是Spring中的核心IOC容器接口。BeanFactory接口有若干个实现,其中最常用的实现就是XMLBeanFactory。 (XMLBeanFactory在Spring3.1中已经不鼓励使用了。替代者为 DefaultListableBeanFactory)
BeanFactory接口的主要方法为各种参数的getBean方法:
1 Object getBean(String name) throws BeansException; 2 <T> T getBean(String name, Class<T> requiredType) throws BeansException; 3 <T> T getBean(Class<T> requiredType) throws BeansException;
好像在这些getBean方法中,没有使用反射???那么,是如何实现的呢?
应用上下文 (com.springframework.context.ApplicationContext)建立在BeanFactory基础之上,提供了更多 面向应用的功能,它提供了国际化支持和框架事件体系,更易于创建实际应用。我们一般称BeanFactory为IoC容器,而称 ApplicationContext为应用上下文。但有时为了行文方便,我们也将ApplicationContext称为Spring容器。 一般而言,在构建应用程序时,我们会更多地使用ApplicationContext,而非BeanFactory。一个BeanFactory仅仅实例化并配置Java beans。一个ApplicationCongtext,不仅实例化并配置bean,还提供了针对以下多种企业特定特性的基础设施支持:事务、AOP,等。org.springframework.context.ApplicationContext
在xml配置文件中,可以配置使用不同的方式实例化Bean,可以使用静态工厂方法:
1 <?xml version="1.0" encoding="UTF-8"?> 2 <beans xmlns="http://www.springframework.org/schema/beans" 3 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 4 xsi:schemaLocation="http://www.springframework.org/schema/beans 5 http://www.springframework.org/schema/beans/spring-beans.xsd"> 6 7 <!-- Learn 03使用静态工厂方式实例化Bean --> 8 <!--使用有参数构造参数--> 9 <bean id="speaker03" class="com.mahaochen.spring.learn03.Speaker" factory-method="getInstance"> 10 </bean> 11 12 </beans>
使用实例工厂方法:
1 <?xml version="1.0" encoding="UTF-8"?> 2 <beans xmlns="http://www.springframework.org/schema/beans" 3 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 4 xsi:schemaLocation="http://www.springframework.org/schema/beans 5 http://www.springframework.org/schema/beans/spring-beans.xsd"> 6 7 8 <!-- Learn 04 使用实例工厂方法实例化Bean --> 9 <!-- 定义实例工厂Bean --> 10 <bean id="speakerFactory" class="com.mahaochen.spring.learn04.SpeakerFactory" /> 11 <!-- 使用实例工厂Bean创建Bean --> 12 <bean id="speaker04" class="com.mahaochen.spring.learn04.Speaker" 13 factory-bean="speakerFactory" factory-method="newInstance" > 14 </bean> 15 16 </beans>
在使用Spring时经常使用如下的语句:
1 BeanFactory beanFactory = new ClassPathXmlApplicationContext("beans.xml");
可见ApplicationContext的实现类往往也实现了BeanFactory接口。
Spring在DefaultSingletonBeanRegistry类中提供了一个用于缓存单实例Bean的缓存器,它是一个用HashMap实现的缓存器,单实例的Bean以beanName为键保存在这个HashMap中。
进一步问题
把对象生成放在配置文件里进行定义.只是把对象之间的耦合关系定义在了xml文档中,这种耦合仍在。但是对象A与B不再有耦合关系,取而代之的是对象A与IoC容器间的耦合,以及对象B与IoC容器间的耦合。
xml配置文件中的依赖,与pojo的constructor& setter不是重复信息。
Spring IoC解决了依赖具体这一问题了吗?? new SpecifcClass()是把this.Class与SpecificClass耦合在了一起,但是setter仍然保持了这种耦合关系?
still the question: what's the use of IoC compared to normal 'new' ? 是更松散的耦合吗? 意义在哪里呢? 避免多处的修改吗(多处出现new同一个bean)
运行时修改配置文件,实现更好的灵活性?
参考文献:
http://howtodoinjava.com/2013/03/19/inversion-of-control-ioc-and-dependency-injection-di-patterns-in-spring-framework-and-related-interview-questions/
http://www.informit.com/articles/article.aspx?p=174533
http://javabeginnerstutorial.com/spring-framework-tutorial/spring-aopaspect-oriented-programming/
IoC模式(in c#): http://www.cnblogs.com/qqlin/archive/2012/10/09/2707075.html
自己动手写IoC框架:http://www.cnblogs.com/rongdi/p/4115993.html
Spring Doc:http://docs.spring.io/spring/docs/2.0.x/reference/beans.html
http://www.devshed.com/c/a/Java/The-Spring-Framework-Understanding-IoC/
http://www.springbyexample.org/examples/intro-to-ioc.html
http://www.roseindia.net/tutorial/spring/spring3/ioc/index.html
http://www.roseindia.net/tutorial/spring/spring3/ioc/springbeanexample.html
http://www.roseindia.net/tutorial/spring/spring3/ioc/springbeanexample.html
http://blog.csdn.net/cutesource/article/details/6132650
http://www.codeproject.com/Articles/465173/Dependency-Inversion-Principle-IoC-Container-Depen
http://blog.csdn.net/wanghao72214/article/details/3969594
http://www.jdon.com/dl/best/spring.htm 讲注入讲的比较清楚
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· go语言实现终端里的倒计时
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 分享 3 个 .NET 开源的文件压缩处理库,助力快速实现文件压缩解压功能!
· Ollama——大语言模型本地部署的极速利器
· DeepSeek如何颠覆传统软件测试?测试工程师会被淘汰吗?