zoukankan      html  css  js  c++  java
  • Spring学习笔记——Spring依赖注入原理分析

    我们知道Spring的依赖注入有四种方式,各自是get/set方法注入、构造器注入、静态工厂方法注入、实例工厂方法注入
    以下我们先分析下这几种注入方式
    1、get/set方法注入

    public class SpringAction {
            //注入对象springDao
        private SpringDao springDao;
            //一定要写被注入对象的set方法
            public void setSpringDao(SpringDao springDao) {
            this.springDao = springDao;
        }
            public void ok(){
            springDao.ok();
        }
    }

    配置文件例如以下:

    <!--配置bean,配置后该类由spring管理-->
        <bean name="springAction" class="com.bless.springdemo.action.SpringAction">
            <!--(1)依赖注入,配置当前类中相应的属性-->
            <property name="springDao" ref="springDao"></property>
        </bean>
    <bean name="springDao" class="com.bless.springdemo.dao.impl.SpringDaoImpl"></bean>

    2、构造器注入

    public class SpringAction {
        //注入对象springDao
        private SpringDao springDao;
        private User user;
    
        public SpringAction(SpringDao springDao,User user){
            this.springDao = springDao;
            this.user = user;
            System.out.println("构造方法调用springDao和user");
        }
    
            public void save(){
            springDao.save(user);
        }
    }

    在XML文件里相同不用的形式,而是使用标签,ref属性相同指向其他标签的name属性:

    <!--配置bean,配置后该类由spring管理-->
        <bean name="springAction" class="com.bless.springdemo.action.SpringAction">
            <!--(2)创建构造器注入,假设主类有带參的构造方法则需加入此配置-->
            <constructor-arg ref="springDao"></constructor-arg>
            <constructor-arg ref="user"></constructor-arg>
        </bean>
            <bean name="springDao" class="com.bless.springdemo.dao.impl.SpringDaoImpl"></bean>
            <bean name="user" class="com.bless.springdemo.vo.User"></bean>

    在XML文件里相同不用的形式,而是使用标签。ref属性相同指向其他标签的name属性:
    解决构造方法參数的不确定性。你可能会遇到构造方法传入的两參数都是同类型的,为了分清哪个该赋相应值,则须要进行一些小处理:

    <bean name="springAction" class="com.bless.springdemo.action.SpringAction">  
            <constructor-arg index="0" ref="springDao"></constructor-arg>  
            <constructor-arg index="1" ref="user"></constructor-arg>  
    </bean>  

    还有一种是设置參数类型:

    <constructor-arg type="java.lang.String" ref=""/>  

    3、静态工厂方法注入
    通过调用静态工厂方法来获取自己须要的对象,为了让Spring管理全部对象,我们不能直接通过类名加方法来获取对象,那样就脱离了Spring的管理,而是通过Spring注入的形式来获取

    package com.bless.springdemo.factory;
    import com.bless.springdemo.dao.FactoryDao;
    import com.bless.springdemo.dao.impl.FactoryDaoImpl;
    import com.bless.springdemo.dao.impl.StaticFacotryDaoImpl;
    public class DaoFactory {
        //静态工厂
        public static final FactoryDao getStaticFactoryDaoImpl(){
            return new StaticFacotryDaoImpl();
        }
    }

    相同看关键类,这里我须要注入一个FactoryDao对象,这里看起来跟第一种注入一模一样。可是看随后的xml会发现有非常大区别:

     public class SpringAction {
            //注入对象
        private FactoryDao staticFactoryDao;
    
        public void staticFactoryOk(){
            staticFactoryDao.saveFactory();
        }
        //注入对象的set方法
        public void setStaticFactoryDao(FactoryDao staticFactoryDao) {
            this.staticFactoryDao = staticFactoryDao;
        }
    }

    配置文件例如以下:

    <!--配置bean,配置后该类由spring管理-->
        <bean name="springAction" class="com.bless.springdemo.action.SpringAction" >
            <!--(3)使用静态工厂的方法注入对象,相应以下的配置文件(3)-->
            <property name="staticFactoryDao" ref="staticFactoryDao"></property>
                    </property>
        </bean>
        <!--(3)此处获取对象的方式是从工厂类中获取静态方法-->
        <bean name="staticFactoryDao" class="com.bless.springdemo.factory.DaoFactory" factory-method="getStaticFactoryDaoImpl"></bean>

    4、实例工厂方法注入
    实例工厂的意思是获取对象实例的方法不是静态的,所以你须要首先new工厂类。再调用普通的实例方法:

    public class DaoFactory {
        //实例工厂
        public FactoryDao getFactoryDaoImpl(){
            return new FactoryDaoImpl();
        }
    }
    public class SpringAction {
        //注入对象
        private FactoryDao factoryDao;
    
        public void factoryOk(){
            factoryDao.saveFactory();
        }
        public void setFactoryDao(FactoryDao factoryDao) {
            this.factoryDao = factoryDao;
        }
    }
    <!--配置bean,配置后该类由spring管理-->
        <bean name="springAction" class="com.bless.springdemo.action.SpringAction">
            <!--(4)使用实例工厂的方法注入对象,相应以下的配置文件(4)-->
            <property name="factoryDao" ref="factoryDao"></property>
        </bean>
    
        <!--(4)此处获取对象的方式是从工厂类中获取实例方法-->
        <bean name="daoFactory" class="com.bless.springdemo.factory.DaoFactory"></bean>
        <bean name="factoryDao" factory-bean="daoFactory" factory-method="getFactoryDaoImpl"></bean>

    对于第1、2种我们用的比較多,对后两种可能比較陌生。


    以下我们来分析下Spring是怎样完毕依赖注入的。假设我们去看Spring的源代码可能涉及的类和接口相当多,不易掌握。在此我用自己的代码和方式来帮助我们Spring依赖注入的过程。
    当我们启动Spring容器的时候他会运行以下几个过程:
    1、载入Xml配置文件(readXML(String filename))在Spring这个由ApplicationContext类完毕
    这一步会解析Xml属性。把bean的属性存放到BeanDefinition类中
    代码例如以下:

    /**
         * 读取xml配置文件
         * @param filename
         */
        private void readXML(String filename) {
               SAXReader saxReader = new SAXReader();   
                Document document=null;   
                try{
                 URL xmlpath = this.getClass().getClassLoader().getResource(filename);
                 document = saxReader.read(xmlpath);
                 Map<String,String> nsMap = new HashMap<String,String>();
                 nsMap.put("ns","http://www.springframework.org/schema/beans");//加入命名空间
                 XPath xsub = document.createXPath("//ns:beans/ns:bean");//创建beans/bean查询路径
                 xsub.setNamespaceURIs(nsMap);//设置命名空间
                 List<Element> beans = xsub.selectNodes(document);//获取文档下全部bean节点 
                 for(Element element: beans){
                    String id = element.attributeValue("id");//获取id属性值
                    String clazz = element.attributeValue("class"); //获取class属性值        
                    BeanDefinition beanDefine = new BeanDefinition(id, clazz);
                    XPath propertysub =  element.createXPath("ns:property");
                    propertysub.setNamespaceURIs(nsMap);//设置命名空间
                    List<Element> propertys = propertysub.selectNodes(element);
                    for(Element property : propertys){                  
                        String propertyName = property.attributeValue("name");
                        String propertyref = property.attributeValue("ref");
                        PropertyDefinition propertyDefinition = new PropertyDefinition(propertyName, propertyref);
                        beanDefine.getPropertys().add(propertyDefinition);
                    }
                    beanDefines.add(beanDefine);
                 } 
                }catch(Exception e){   
                    e.printStackTrace();
                }
        }

    2、Bean的实例化
    在配置文件以bean的id为key。BeanDefinition为value放到Map中

    /**
         * 完毕bean的实例化
         */
        private void instanceBeans() {
            for(BeanDefinition beanDefinition : beanDefines){
                try {
                    if(beanDefinition.getClassName()!=null && !"".equals(beanDefinition.getClassName().trim()))
                        sigletons.put(beanDefinition.getId(), Class.forName(beanDefinition.getClassName()).newInstance());
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
    
        }

    3、为Bean的输入注入值。完毕依赖注入

    /**
         * 为bean对象的属性注入值
         */
        private void injectObject() {
            for(BeanDefinition beanDefinition : beanDefines){
                Object bean = sigletons.get(beanDefinition.getId());
                if(bean!=null){
                    try {
                        PropertyDescriptor[] ps = Introspector.getBeanInfo(bean.getClass()).getPropertyDescriptors();
                        for(PropertyDefinition propertyDefinition : beanDefinition.getPropertys()){
                            for(PropertyDescriptor properdesc : ps){
                                if(propertyDefinition.getName().equals(properdesc.getName())){
                                    Method setter = properdesc.getWriteMethod();//获取属性的setter方法 ,private
                                    if(setter!=null){
                                        Object value = sigletons.get(propertyDefinition.getRef());
                                        setter.setAccessible(true);
                                        setter.invoke(bean, value);//把引用对象注入到属性
                                    }
                                    break;
                                }
                            }
                        }
                    } catch (Exception e) {
                    }
                }
            }
        }

    事实上Spring依赖注入的过程就是这么简单,再就是各种细节了。比方懒载入、单例等的额外处理了。

  • 相关阅读:
    java中的堆、栈、常量池
    java中int和Integer的区别
    python linecache模块读取文件的方法
    Python 字符串中 startswith()方法
    Python中的filter()函数的用法
    python sort、sorted高级排序技巧
    二级指针内存模型(一)
    Linux下多线程模拟停车场停车
    linux线程操作
    C语言实现多线程排序
  • 原文地址:https://www.cnblogs.com/mthoutai/p/7278427.html
Copyright © 2011-2022 走看看