Spring技术内幕:SpringIOC原理学习总结

前一段时候我把Spring技术内幕的关于IOC原理一章看完,感觉代码太多,不好掌握,我特意又各方搜集了一些关于IOC原理的资料,特加深一下印象,以便真正掌握IOC的原理。
IOC的思想是:Spring容器来实现这些相互依赖对象的创建、协调工作。对象只需要关系业务逻辑本身就可以了。
SpringIOC容器的执行步骤是:
1、资源定位,即首先要找到applicationContext.xml文件
2、BeanDefinition的载入,把XML文件中的数据统一加载到BeanDefinition中,方便以后的处理
3、向IOC容器中注入BeanDefinition数据
4、将BeanDefinition中的数据进行依赖注入
IoC的一个重点是在系统运行中,动态的向某个对象提供它所需要的其他对象。这一点是通过DI(Dependency Injection,依赖注入)来实现的。比如对象A需要操作数据库,以前我们总是要在A中自己编写代码来获得一个Connection对象,有了 spring我们就只需要告诉spring,A中需要一个Connection,至于这个Connection怎么构造,何时构造,A不需要知道。在系统运行时,spring会在适当的时候制造一个Connection,然后像打针一样,注射到A当中,这样就完成了对各个对象之间关系的控制。A需要依赖 Connection才能正常运行,而这个Connection是由spring注入到A中的,依赖注入的名字就这么来的。那么DI是如何实现的呢? Java 1.3之后一个重要特征是反射(reflection),它允许程序在运行的时候动态的生成对象、执行对象的方法、改变对象的属性,spring就是通过反射来实现注入的。
举个现实生活中例子,现在有很多中介公司,例如我爱我家。我爱我家会有各种各样的房源资料,当我们找房子时我们只需把需要的房子告诉我爱我家,我爱我家会根据我们的需求给我们提供适合的房子。这样我们就不用自己去网上、小区广告等各个地方去搜集寻求适合自己的房子,整个过程由我爱我家中介来控制就行了,现在想想有个这个中介是不是使整个过程变得简单愉快了。当然,在实际生活中面对中介我们是需要交中介费的,好比SpringIOC我们也需要维护ApplicationContext这个配置文件,都是成本,面对Spring我们可以忽略成本,哈哈。
下面让我看看SpringIOC的运行过程:
1、首先加载ApplicationContext.xml,资源定位
首先资源定位最终是在Springframe下的AbstractBeanDefinitionReader类中的LoaderBeanDefinitions(String location,Set<Resource> actualResources) line 98,下面举例说明:

public static void main(String[] args) {
        ApplicationContext context = new FileSystemXmlApplicationContext(
                "applicationContext.xml");
        User user= (User) context.getBean("user");
        user.write();
    }
 <bean id="user" class="com.jader.spring.Model.User">
        <property name="name" value="jader" />
 </bean>
 public class User{
    private String name;
    public void write() {
        System.out.println("my name is:"+name);
    }
    public void setName(String name) {
        this.name = name;
    }
}

运行程序会输出:my name is jader,很明显,SpringIOC已经把property里的value注入到User的name属性当中。
下面我们来分析Spring是怎么实现的
2、BeanDefinition的载入
载入和解析最终是在Springframework下的AbstractApplicationContext类的refresh()方法实现的,这个方法非常关键,里面包含了资源载入和解析,包括对lazy-init的额外处理等,line 445,下面举例说明:
首先我们定义一个Bean,用来存放Bean拥有的属性

  /* Bean Id */
    private String id;
    /* Bean Class */
    private String type;
    /* Bean Property */
    private Map<String, Object> properties = new HashMap<String, Object>();

接下来Spring 就开始加载我们的配置文件了,将我们配置的信息保存在一个HashMap中,HashMap的key就是Bean 的 Id ,HasMap 的value是这个Bean,只有这样我们才能通过context.getBean(“user”)这个方法获得Animal这个类。我们都知道Spirng可以注入基本类型,而且可以注入像List,Map这样的类型,接下来就让我们以Map为例看看Spring是怎么保存的


   <bean id="beanMap" class="BeanMap">
        <property name="myBeanMap">
            <map>
                <entry key="name">
                    <value>jader</value>
                </entry>
                <entry key="age">
                    <value>26</value>
                </entry>
            </map>
        </property>
    </bean>

3、向IOC容器中注册BeanDefinition
注册最终是在Spring frame的DefaultListableBeanFactory类下的registerBeanDefinition(String beanName,BeanDefinition beanDefinition)方法中实现,line 664;下面举例说明:

            if (beanProperty.element("map") != null) {
                    Map<String, Object> propertiesMap = new HashMap<String, Object>();
                    Element propertiesListMap = (Element) beanProperty
                            .elements().get(0);
                    Iterator<?> propertiesIterator = propertiesListMap
                            .elements().iterator();
                    while (propertiesIterator.hasNext()) {
                        Element vet = (Element) propertiesIterator.next();
                        if (vet.getName().equals("entry")) {
                            String key = vet.attributeValue("key");
                            Iterator<?> valuesIterator = vet.elements()
                                    .iterator();
                            while (valuesIterator.hasNext()) {
                                Element value = (Element) valuesIterator.next();
                                if (value.getName().equals("value")) {
                                    propertiesMap.put(key, value.getText());
                                }
                                if (value.getName().equals("ref")) {
                                    propertiesMap.put(key, new String[] { value
                                            .attributeValue("bean") });
                                }
                            }
                        }
                    }
                    bean.getProperties().put(name, propertiesMap);
                }

4、依赖注入
SpringIOC的最终依赖注入是是通过AbstractAutowireCapableBeanFactory的doCreateBean(finale String beanName,final RootBeanDefinition mbd,final Object[] args)方法(line 480)的,这个方法里的关键方法有populateBean()和createBeanInstance(),正在实现注入是通过BeanWrapperImpl extends AbstractPropertyAccessor类的setPropertyValue()方法实现 line 911
原理是:通过反射机制实现的,在实例化一个类时,它通过反射调用类中set方法将事先保存在HashMap中的类属性注入到类中

public static Object newInstance(String className) {
        Class<?> cls = null;
        Object obj = null;
        try {
            cls = Class.forName(className);
            obj = cls.newInstance();
        } catch (ClassNotFoundException e) {
            throw new RuntimeException(e);
        } catch (InstantiationException e) {
            throw new RuntimeException(e);
        } catch (IllegalAccessException e) {
            throw new RuntimeException(e);
        }
        return obj;
    }
public static void setProperty(Object obj, String name, String value) {
        Class<? extends Object> clazz = obj.getClass();
        try {
            String methodName = returnSetMthodName(name);
            Method[] ms = clazz.getMethods();
            for (Method m : ms) {
                if (m.getName().equals(methodName)) {
                    if (m.getParameterTypes().length == 1) {
                        Class<?> clazzParameterType = m.getParameterTypes()[0];
                        setFieldValue(clazzParameterType.getName(), value, m,
                                obj);
                        break;
                    }
                }
            }
        } catch (SecurityException e) {
            throw new RuntimeException(e);
        } catch (IllegalArgumentException e) {
            throw new RuntimeException(e);
        } catch (IllegalAccessException e) {
            throw new RuntimeException(e);
        } catch (InvocationTargetException e) {
            throw new RuntimeException(e);
        }
}

最后它将这个类的实例返回给我们,我们就可以用了。我们还是以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技术内幕的第一章介绍SpringIOC原理,难用理解,借助于这边文章可以帮我们理解一下。书读百遍,其义自现,当你读一遍不明白的时候可以反复阅读,这篇文章我都是从网上多方搜集的资料,在这里我表示感谢,希望我们共同进步。

  • 2
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值