Spring IOC原理(初级版)
Spring框架是由于软件开发的复杂性而创建的。Spring使用的是基本的JavaBean来完成以前只可能由EJB完成的事情。然而,Spring的用途不仅仅限于服务器端的开发。从简单性、可测试性和松耦合性的角度而言,绝大部分Java应用都可以从Spring中受益。
------------------------------------------------------------↑ 以上都是废话,我也不懂 ↑ ↓ 下面是整体架构,可以略过 ↓ --------------------------------------------------------
整体框架:
Spring框架式一个分层架构,由 7 个定义良好的模块组成。Spring 模块构建在核心容器之上,核心容器定义了创建、配置和管理 bean 的方式。
组成 Spring 框架的每个模块(或组件)都可以单独存在,或者与其他一个或多个模块联合实现。每个模块的功能如下:
• 核心容器(下面几个模块与本文无关,可以暂时不用看):核心容器提供 Spring 框架的基本功能。核心容器的主要组件是 BeanFactory,它是工厂模式的实现。BeanFactory 使用控制反转 (IOC) 模式将应用程序的配置和依赖性规范与实际的应用程序代码分开。
• Spring 上下文:Spring 上下文是一个配置文件,向 Spring 框架提供上下文信息。Spring 上下文包括企业服务,例如 JNDI、EJB、电子邮件、国际化、校验和调度功能。
• Spring AOP:通过配置管理特性,Spring AOP 模块直接将面向方面的编程功能集成到了 Spring 框架中。所以,可以很容易地使 Spring 框架管理的任何对象支持 AOP。Spring AOP 模块为基于 Spring 的应用程序中的对象提供了事务管理服务。通过使用 Spring AOP,不用依赖 EJB 组件,就可以将声明性事务管理集成到应用程序中。
• Spring DAO:JDBC DAO 抽象层提供了有意义的异常层次结构,可用该结构来管理异常处理和不同数据库供应商抛出的错误消息。异常层次结构简化了错误处理,并且极大地降低了需要编写的异常代码数量(例如打开和关闭连接)。Spring DAO 的面向 JDBC 的异常遵从通用的 DAO 异常层次结构。
• Spring ORM:Spring 框架插入了若干个 ORM 框架,从而提供了 ORM 的对象关系工具,其中包括 JDO、Hibernate 和 iBatis SQL Map。所有这些都遵从 Spring 的通用事务和 DAO 异常层次结构。
• Spring Web 模块:Web 上下文模块建立在应用程序上下文模块之上,为基于 Web 的应用程序提供了上下文。所以,Spring 框架支持与 Jakarta Struts 的集成。Web 模块还简化了处理多部分请求以及将请求参数绑定到域对象的工作。
• Spring MVC 框架:MVC 框架是一个全功能的构建 Web 应用程序的 MVC 实现。通过策略接口,MVC 框架变成为高度可配置的,MVC 容纳了大量视图技术,其中包括 JSP、Velocity、Tiles、iText 和 POI。
------------------------------------------------------------↑上面是介绍,我也不懂↑,↓ 下面是IOC ↓ ----------------------------------------------------------------------------
核心容器提供 Spring 框架的基本功能。核心容器的主要组件是 BeanFactory,它是工厂模式的实现。BeanFactory 使用控制反转 (IOC) 模式将应用程序的配置和依赖性规范与实际的应用程序代码分开。
本文介绍的IOC,如上所言,是spring核心模块的核心原理。
IOC产生的背景
脑补一下这样的场景,应该会经常遇到:当A、B、C类需要E类对象来协作完成一件事情的时候,传统做法:直接在A、B、C类中分别new一个E类对象。突然某一天,发现A\B\C类不再需要E对象来协作了,需要F对象。这是你该怎么办?只有一个办法,你需要修改A、B、C类的源代码,将E换成F,突然某一天,又改成G了,啥都不说了,改吧。突然某一天、突然某一天、突然某一天.....妈蛋!老子不干了!(当然说的夸张了点)这就是耦合。
如何解耦合呢?我们自然而然想到了接口,如果能弄一个接口,让所有的E\F\G....类都是这个接口的实现该多好啊。虽然从接口的实现来看是不太可能了。直到Michael Mattson提出了IOC,散播福音啊。
什么是IOC
上文背景中介绍到当A类需要E类对象来协作时,传统做法是自己new一个E类,这时候,控制权在你手里。IOC什么意思呢?Inversion of Control!控制反转!即:控制权反转到了其他地方。至于什么地方,一会儿再说。至少,你说了不算了。用参考资料2中的一个很有意思的事情,当你想谈恋爱的时候(需要一个异性协作,当然,也可能是同性),传统做法是:我自己去找一个女朋友\男朋友,主动权在我手里(你别说妹子不答应啊,我呼死你)。IOC的做法是:当你说你想找个人谈恋爱,你说我想要一个完美的妹子,比如长得像刘亦菲,身材像林志玲,唱歌像孙燕姿,速度像梅西,技术像内马尔blahblah,有个人直接送给你一个,这个人可能是你父母,也可能是婚介所。反正不是你。明白了吧。控制权反转到了人家的手里了,人家塞给你谁就是谁,爱要不要。当然,你觉得不符合要求,不满意,可以对他们说“尼玛,这是凤姐”(抛出异常)。按照IOC的做法:你能做的,只能是到婚介所或者父母(Spring容器)那里,告诉对方,你是谁,想要什么样的妹子,然后spring登记了你的信息,并在你需要的时候给你想要的。这时候,控制权其实在spring容器手里,所有类的创建,销毁都是由spring控制,而不再由你(引用它的对象)来操心了。明白了吗?控制权反转到了spring容器手里了。还听不懂,下面的也不要看了,放弃吧。
IOC的重点是在系统运行过程中,动态的像某个对象提供它所需要的其它对象。这个提供的方法是通过DI(依赖注入)来实现的。比如,A类需要一个B类对象来完成某个动作,以前是A类自己new一个B,有了Spring之后,只需要告诉spring,A中需要一个B,至于B怎么构造,何时构造,A完全不需要care,只需要在它需要的时候张嘴要就行了。在系统运行时,spring会在适当时候制造一个B,然后像打针一样,注射到A中,从而完成了对象之间的关联控制。A需要依赖B才可以正常运行,而B对象是由spring注入到A中去的。依赖注入就是这么来的。
DI的实现
DI是通过反射来实现的,反射允许程序通过某个类的名字或者对象来得到类本身。不了解反射机制的可以点这里。spring就是通过反射来完成注入的。
接下来手动写一个屌丝版的Spring IOC容器。【以下内容摘自参考资料2】
首先,我们定义一个Bean类,这个类用来存放一个Bean拥有的属性。
1 /* Bean Id */ 2 private String id; 3 /* Bean Class */ 4 private String type; 5 /* Bean Property */ 6 private Map<String, Object> properties = new HashMap<String, Object>();
一个Bean包括id,type(类全域名),和Properties(属性域)。
接下来Spring 就开始加载我们的配置文件了,将我们配置的信息保存在一个HashMap中,HashMap的key就是Bean 的 Id ,HasMap 的value是这个Bean,只有这样我们才能通过context.getBean("animal")这个方法获得Animal这个类。我们都知道Spirng可以注入基本类型,而且可以注入像List,Map这样的类型,接下来就让我们以Map为例看看Spring是怎么保存的吧
Map配置可以像下面的。
<bean id="test" class="Test">
<property name="testMap">
<map>
<entry key="a">
<value>1</value>
</entry>
<entry key="b">
<value>2</value>
</entry>
</map>
</property>
</bean>
Spring是怎样保存上面的配置呢?,代码如下:
1 if (beanProperty.element("map") != null) { 2 Map<String, Object> propertiesMap = new HashMap<String, Object>(); 3 Element propertiesListMap = (Element) beanProperty 4 .elements().get(0); 5 Iterator<?> propertiesIterator = propertiesListMap 6 .elements().iterator(); 7 while (propertiesIterator.hasNext()) { 8 Element vet = (Element) propertiesIterator.next(); 9 if (vet.getName().equals("entry")) { 10 String key = vet.attributeValue("key"); 11 Iterator<?> valuesIterator = vet.elements() 12 .iterator(); 13 while (valuesIterator.hasNext()) { 14 Element value = (Element) valuesIterator.next(); 15 if (value.getName().equals("value")) { 16 propertiesMap.put(key, value.getText()); 17 } 18 if (value.getName().equals("ref")) { 19 propertiesMap.put(key, new String[] { value 20 .attributeValue("bean") }); 21 } 22 } 23 } 24 } 25 bean.getProperties().put(name, propertiesMap); 26 }
接下来就进入最核心部分了,让我们看看Spring 到底是怎么依赖注入的吧,其实依赖注入的思想也很简单,它是通过反射机制实现的,在实例化一个类时,它通过反射调用类中set方法将事先保存在HashMap中的类属性注入到类中。让我们看看具体它是怎么做的吧。
首先实例化一个类,像这样
1 public static Object newInstance(String className) { 2 Class<?> cls = null; 3 Object obj = null; 4 try { 5 cls = Class.forName(className); 6 obj = cls.newInstance(); 7 } catch (ClassNotFoundException e) { 8 throw new RuntimeException(e); 9 } catch (InstantiationException e) { 10 throw new RuntimeException(e); 11 } catch (IllegalAccessException e) { 12 throw new RuntimeException(e); 13 } 14 return obj; 15 }
接着它将这个类的依赖注入进去,像这样
1 public static void setProperty(Object obj, String name, String value) { 2 Class<? extends Object> clazz = obj.getClass(); 3 try { 4 String methodName = returnSetMthodName(name); 5 Method[] ms = clazz.getMethods(); 6 for (Method m : ms) { 7 if (m.getName().equals(methodName)) { 8 if (m.getParameterTypes().length == 1) { 9 Class<?> clazzParameterType = m.getParameterTypes()[0]; 10 setFieldValue(clazzParameterType.getName(), value, m, 11 obj); 12 break; 13 } 14 } 15 } 16 } catch (SecurityException e) { 17 throw new RuntimeException(e); 18 } catch (IllegalArgumentException e) { 19 throw new RuntimeException(e); 20 } catch (IllegalAccessException e) { 21 throw new RuntimeException(e); 22 } catch (InvocationTargetException e) { 23 throw new RuntimeException(e); 24 } 25 }
最后它将这个类的实例返回给我们,我们就可以用了。我们还是以Map为例看看它是怎么做的,我写的代码里面是创建一个HashMap并把该HashMap注入到需要注入的类中,像这样
if (value instanceof Map) { Iterator<?> entryIterator = ((Map<?, ?>) value).entrySet() .iterator(); Map<String, Object> map = new HashMap<String, Object>(); while (entryIterator.hasNext()) { Entry<?, ?> entryMap = (Entry<?, ?>) entryIterator.next(); if (entryMap.getValue() instanceof String[]) { map.put((String) entryMap.getKey(), getBean(((String[]) entryMap.getValue())[0])); } } BeanProcesser.setProperty(obj, property, map); }
这样我们就可以用Spring 给我们创建的类了(虽然细节没太弄明白,里面一些方法都不知道具体实现是什么)
当然Spring能做到的远不止这些,这个示例程序仅仅提供了Spring最核心的依赖注入功能中的一部分。
【评注】:从上文中,可以感觉到Ioc的实质有点想工厂模式,只不过这个工厂不再是java类,而是一个Ioc容器,生成对象的方法也不再由工厂直接生成,而是通过配置文件和反射来动态生成。毫无疑问:这样使得代码具有更好的灵活性、可扩展性。同时进一步的解耦合。
参考资料:http://blog.csdn.net/m13666368773/article/details/7802126
http://blog.csdn.net/it_man/article/details/4402245