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>();
View Code

 

 

一个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>
View Code

 

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                 }
View Code

 

接下来就进入最核心部分了,让我们看看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     }
View Code

 

接着它将这个类的依赖注入进去,像这样

 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 }
View Code

最后它将这个类的实例返回给我们,我们就可以用了。我们还是以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);
            }
View Code

 

这样我们就可以用Spring 给我们创建的类了(虽然细节没太弄明白,里面一些方法都不知道具体实现是什么)

当然Spring能做到的远不止这些,这个示例程序仅仅提供了Spring最核心的依赖注入功能中的一部分。 

 

 【评注】:从上文中,可以感觉到Ioc的实质有点想工厂模式,只不过这个工厂不再是java类,而是一个Ioc容器,生成对象的方法也不再由工厂直接生成,而是通过配置文件和反射来动态生成。毫无疑问:这样使得代码具有更好的灵活性、可扩展性。同时进一步的解耦合。

参考资料:http://blog.csdn.net/m13666368773/article/details/7802126

http://blog.csdn.net/it_man/article/details/4402245

posted on 2014-08-06 01:06  喵星人与汪星人  阅读(218)  评论(0编辑  收藏  举报