美文网首页
SSM框架系列学习总结1之Spring Ioc

SSM框架系列学习总结1之Spring Ioc

作者: 梦蓝樱飞2020 | 来源:发表于2018-01-20 20:31 被阅读141次
    Spring模块图.jpeg

    今天来总结Spring容器的Ioc(控制反转)和DI(依赖注入)!


    Ioc.jpeg

    Spring的核心思想,ioc 和 di
    Ioc:ioc强调由第三方容器根据客户的需求创建对象,然后根据客户提供的方法将对象传递给客户。

    Di: 强调第三方容器创建对象以后, 通过什么方法将对象传递过去.

    理解示例图:


    Ioc&DI.png

    模拟实现springIoc

    首先新建两个实体类Boy和Girl:

    package com.wtu.spring.base;
    
    public class Boy {
        private String id;
        private String name;
        private Double salary;
    
        public String getId() {
            return id;
        }
    
        public void setId(String id) {
            this.id = id;
        }
    
        public String getName() {
            return name;
        }
    
        public void setName(String name) {
            this.name = name;
        }
    
        public Double getSalary() {
            return salary;
        }
    
        public void setSalary(Double salary) {
            this.salary = salary;
        }
    
        @Override
        public String toString() {
            return "Boy [id=" + id + ", name=" + name + ", salary=" + salary + "]";
        }
    }
    
    package com.wtu.spring.base;
    
    public class Girl {
        private String id;
        private String name;
        private Double salary;
    
        public String getId() {
            return id;
        }
    
        public void setId(String id) {
            this.id = id;
        }
    
        public String getName() {
            return name;
        }
    
        public void setName(String name) {
            this.name = name;
        }
    
        public Double getSalary() {
            return salary;
        }
    
        public void setSalary(Double salary) {
            this.salary = salary;
        }
    
        @Override
        public String toString() {
            return "Girl [id=" + id + ", name=" + name + ", salary=" + salary + "]";
        }
    }
    

    在同目录下, 新建一个spring.xml文件, 模拟Spring的配置文件:

    <?xml version="1.0" encoding="UTF-8"?>
    <root>
        <bean id="boyId" class="com.wtu.spring.base.Boy">
            <property name="id" value="007"/>
            <property name="name" value="狗娃"/>
            <property name="salary" value="7000"/>
        </bean>
        <bean id="girlId" class="com.wtu.spring.base.Girl">
            <property name="id" value="008"/>
            <property name="name" value="翠花"/>
            <property name="salary" value="6000"/>
        </bean>
    </root>
    

    然后, 采用dom4j的方式解析xml文件来获取属性, 最后通过反射获取对象.
    SpringIOC.java

    package com.wtu.spring.base;
    
    import org.apache.commons.beanutils.BeanUtils;
    import org.dom4j.Document;
    import org.dom4j.Element;
    import org.dom4j.io.SAXReader;
    import org.xml.sax.SAXException;
    
    import java.io.File;
    import java.lang.reflect.InvocationTargetException;
    import java.util.List;
    
    public class SpringIOC {
    
        private String xmlPath;
    
        public SpringIOC(String xmlPath) {
            this.xmlPath = xmlPath;
        }
    
        public Object getBean(String id) throws Exception {
            //解析spring.xml文件  得到Document对象
            Document document = this.getDocument(xmlPath);
            //根据 bean的id 获得bean元素
            Element beanElement = this.getBeanElement(document, id);
            //获取bean元素的class属性值
            String classAttributeValue = this.getAttributeValue(beanElement);
            //根据class属性值通过反射创建对象
            Object obj = this.getObject(classAttributeValue);
            //获取bean下property元素的name属性值和value属性  并且赋值给obj
            obj = this.setProperty(obj, beanElement);
    
            return obj;
        }
    
    
        /**
         * 获取property元素  然后得到name 和 value的值 赋值给obj对象
         *
         * @param obj
         * @param beanElement
         * @return
         * @throws InvocationTargetException
         * @throws IllegalAccessException
         */
        private Object setProperty(Object obj, Element beanElement) throws Exception {
            List<Element> elementList = beanElement.elements("property");
            for (Element element : elementList) {
                String name = element.attributeValue("name");
                String value = element.attributeValue("value");
                // 通过beanUtils来给对象赋值
                BeanUtils.setProperty(obj, name, value);
            }
            return obj;
        }
    
        /**
         * 根据字符串 通过反射创建对象
         *
         * @param classAttributeValue
         * @return
         * @throws ClassNotFoundException
         * @throws IllegalAccessException
         * @throws InstantiationException
         */
        private Object getObject(String classAttributeValue) throws Exception {
    
            return Class.forName(classAttributeValue).newInstance();
        }
    
        /**
         * 根据bean元素 获取该元素中class属性的属性值
         *
         * @param beanElement
         * @return
         */
        private String getAttributeValue(Element beanElement) {
            String attributeValue = beanElement.attributeValue("class");
            return attributeValue;
        }
    
        /**
         * 根据bean标签的id属性值  获取到该bean标签对应的元素对象 采用xpath查找
         *
         * @param document
         * @param id
         * @return
         */
        private Element getBeanElement(Document document, String id) {
            //传递xpath路径  然后document根据路径找到该元素
            String xpath = "//bean[@id='" + id + "']";
            Element element = (Element) document.selectSingleNode(xpath);
            return element;
        }
    
        /**
         * 根据xml文件的路径  解析得到Document对象
         *
         * @param xmlPath
         * @return
         * @throws SAXException
         */
        private Document getDocument(String xmlPath) throws Exception {
            SAXReader read = new SAXReader();
            return read.read(new File(xmlPath));
        }
    }
    

    最后写测试类:

    package com.wtu.spring.base;
    
    public class TestSpringIOC {
    
        public static void main(String[] args) throws Exception {
            SpringIOC springIOC = new SpringIOC("src/com/wtu/spring/base/spring.xml");
            Boy boy = (Boy) springIOC.getBean("boyId");
            System.out.println(boy);
    
            Girl girl = (Girl) springIOC.getBean("girlId");
            System.out.println(girl);
    
            Girl girl2 = new Girl();
            System.out.println(girl2);
        }
    }
    

    运行结果:


    运行结果.png

    Ioc容器的两个实现类

    FileSystemXmlApplicationContext
    ClassPathXmpApplicationContext
    

    在web项目中能不能使用FileSystemXmlApplicationContext
    为什么?
    答:当项目部署服务器以后, src目录已经不存在了, 该目录下的配置文件全部到了classes目录下面, 这个目录是一个类路径.
    Spring配置文件

    <?xml version="1.0" encoding="UTF-8"?>
    <beans xmlns="http://www.springframework.org/schema/beans"
           xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
           xsi:schemaLocation="http://www.springframework.org/schema/beans
               http://www.springframework.org/schema/beans/spring-beans-3.0.xsd">
    
        <bean id="boyId" class="com.wtu.spring.ioc.type1.Boy">
            <property name="id" value="001"></property>
            <property name="name" value="狗剩"></property>
            <property name="salary" value="8000"></property>
        </bean> 
    
        <!-- 在一个xml文件中, id不能有相同的 -->
        <bean id="girlId" class="com.wtu.spring.ioc.type1.Girl">
            <property name="id" value="002"></property>
            <property name="name" value="翠花"></property>
            <property name="salary" value="7500"></property>
        </bean>
    </beans>
    
            //启动IOC容器  
            //FileSystemXmlApplicationContext(String... configLocations);
            ApplicationContext ac = new FileSystemXmlApplicationContext(
                    new String[]{"src/com/wtu/spring/ioc/type1/spring3.0.xml"});
            
            Boy boy = (Boy) ac.getBean("boyId");
            System.out.println(boy);
    
            // 启动IOC容器  
            // ClassPathXmpApplicationContext(String... configLocations);
            ApplicationContext ac = new ClassPathXmlApplicationContext("com/wtu/spring/ioc/type3/spring3.0.xml");
            Boy boy = (Boy) ac.getBean("boyId");
            System.out.println(boy);
    

    SpringIOC容器的本质

    它就是一个map集合


    map.png

    Spring中bean的一些操作

    1. bean的创建

    a) 通过无参构造方法创建对象

        public Boy() {
            System.out.println("spring容器创建对象");
        }
    
        <bean id="boyId" class="com.wtu.spring.bean.create.Boy">
            <property name="id" value="001"></property>
            <property name="name" value="李四"></property>
            <property name="salary" value="3000.0"></property>
        </bean>
    
            // 启动IOC容器  
            // ClassPathXmpApplicationContext(String... configLocations);
            ApplicationContext ac = new ClassPathXmlApplicationContext("com/wtu/spring/bean/create/spring3.0.xml");
            Boy boy = (Boy) ac.getBean("boyId");
            System.out.println(boy);
    
    无参构造方法.png

    b) 通过中间类的静态方法

    package com.wtu.spring.bean.create2;
    
    /**
     * 中间类 创建对象
     * @Author menglanyingfei
     * @Created on 2018.01.16 14:37
     */
    public class Middle {
        public Middle() {
            System.out.println("spring IOC 容器创建中间类对象");
        }
    
        public static IUserDao getInstance() {
            return new UserDaoImpl("带参");
        }
    }
    
        <!-- 注册UserDaoImpl对象
            通过中间类的静态方法来创建对象
             factory-method: 通过中间类的哪个方法创建对象 -->
        <bean id="userDaoId" class="com.wtu.spring.bean.create2.Middle" factory-method="getInstance">
        </bean>
    

    c) 通过中间类的非静态方法

    package com.wtu.spring.bean.create3;
    
    /**
     * 中间类 创建对象
     * @Author menglanyingfei
     * @Created on 2018.01.16 14:37
     */
    public class Middle {
    
        public Middle() {
            System.out.println("spring IOC 容器创建中间类对象");
        }
    
        public IUserDao getInstance() {
            return new UserDaoImpl("带参");
        }
    }
    
    
        <!--
            通过中间类的非静态方法来创建对象:
            factory-bean: 引用中间类的对象
            factory-method: 中间类创建UserDaoImpl对象的方法
        -->
        <!--注册中间类对象 -->
        <bean id="middleId" class="com.wtu.spring.bean.create3.Middle">
        </bean>
    
        <bean id="userDaoId" factory-bean="middleId"
              factory-method="getInstance" />
    
    2. bean的继承

    Spring配置:

        <!-- bean的继承: 一个bean可以继承另一个bean的property属性
             parent: 父bean的id
             -->
        <bean id="fatherId" class="com.wtu.spring.bean.extends_.Father">
            <property name="id" value="001"/>
            <property name="name" value="曹总"/>
            <property name="salary" value="1000000"/>
        </bean>
    
        <bean id="sonId" class="com.wtu.spring.bean.extends_.Son" parent="fatherId">
            <property name="id" value="002"/>
            <property name="name" value="曹丕"/>
        </bean>
    

    测试:

            //启动IOC容器  
            //ClassPathXmpApplicationContext(String... configLocations);
            ApplicationContext ac = new ClassPathXmlApplicationContext("com/wtu/spring/bean/extends_/spring3.0.xml");
    
            Son son = (Son) ac.getBean("sonId");
            System.out.println(son);
            //Son{id='002', name='曹丕', salary='1000000.0'}
    
    3. bean的生命周期
         <!-- 配置bean的生命周期方法
            init-method: 初始化的方法
            destroy-method: 销毁的方法
             -->
        <bean id="iUserDao" class="com.wtu.spring.bean.life.UserDaoImpl"
            init-method="myInit" destroy-method="myDestroy"/>
    

    测试类:

            //启动IOC容器  
            //ClassPathXmpApplicationContext(String... configLocations);
            AbstractApplicationContext aac = new ClassPathXmlApplicationContext("com/wtu/spring/bean/life/spring3.0.xml");
            /*
                为什么在bean中配置了destroy-method, 但是还是不能够执行销毁的方法?
                那是因为容器ApplicationContext不具备监听销毁的功能, 所以为了执行销毁, 得换一个容器
                AbstractApplicationContext
             */
            // 监听销毁执行bean的销毁的方法, 该方法会立即执行bean的销毁方法
    
    //        aac.close();
            // 监听销毁执行bean的销毁的方法, 该方法不会立即执行bean的销毁方法, 等到关闭JVM的
            // 那一刻才执行
            aac.registerShutdownHook();
    
            Thread.sleep(2000);
    
    4. bean的单例和多例
           <!--
            bean的scope属性: 默认是singleton, 单例模式, 那么启动容器时, 会创建对象
            scope: prototype, 多例模式, 那么启动容器时, 不创建对象
            注意: 以后遇到Spring框架和Struts2框架的整合, 那么需要对Struct2的Action对象进行配置
            一定要让scope="prototype"
              -->
        <bean id="userDaoImpl" class="com.wtu.spring.bean.scope.UserDaoImpl"
            scope="prototype"/>
    

    测试类

            //启动IOC容器  
            //ClassPathXmpApplicationContext(String... configLocations);
            ApplicationContext ac = new ClassPathXmlApplicationContext("com/wtu/spring/bean/scope/spring3.0.xml");
    
    //       spring IOC容器创建bean默认是单例模式
            IUserDao iUserDao = (IUserDao) ac.getBean("userDaoImpl");
            IUserDao iUserDao2 = (IUserDao) ac.getBean("userDaoImpl");
            System.out.println(iUserDao == iUserDao2);
    
    //      System.out.println(iUserDao);
    
    结果.png
    5. bean的创建时间
        <!--
        lazy-init: default 启动容器是 单例, 创建对象; 多例, 不创建对象.
            true: 懒, 不管是单例还是多例, 都不创建对象
            false: 不懒, 启动容器是很勤快,
            如果是单例就创建对象, 如果是多例, 也不创建对象
            因为不知道需要多少个对象.
        -->
        <bean id="IUserDao" class="com.wtu.spring.bean.time.UserDaoImpl"
            lazy-init="true"/>
    

    Spring的DI

    依赖注入第一种方式: 将bean中的属性通过set方式赋值给bean对象.

    1. 注入简单类型(基本数据类型+String)
    image.png
    2. 注入类类型(以Date类型为例)
        <!-- 注册一个Date对象 -->
        <bean id="dateId" class="java.util.Date"/>
        <bean id="customer" class="com.wtu.spring.di.set.Customer">
            <property name="birthday" ref="dateId"/>
        </bean>
    
    3.集合类型<里面的元素是基本类型>
    集合类型1.png
    4. 集合类型<里面的元素是自定义类型>
    image.png
    image.png

    完整代码:
    实体类:

    public class Customer {
        private Integer id;
        private String name;
        private String sex;
        private Integer age;
        private Date birthday;
        private List<String> address;
        private Set<String> phone;
        private Map<String, String> add_pho;
    
        public Customer() {
            System.out.println("IOC 创建对象!");
        }
        
        // getter and setter omitted
    

    Spring配置:

        <!-- 注册一个Date对象 -->
        <bean id="dateId" class="java.util.Date"/>
    
        <bean id="customer" class="com.wtu.spring.di.set.Customer">
            <property name="id" value="1"/>
            <property name="name" value="李四"/>
            <property name="sex" value="女"/>
            <property name="age" value="18"/>
    
            <property name="birthday" ref="dateId"/>
            
            <property name="address">
                <list>
                    <value>北京</value>
                    <value>上海</value>
                    <value>广州</value>
                </list>
            </property>
            <property name="phone">
                <set>
                    <value>010</value>
                    <value>020</value>
                    <value>023</value>
                </set>
            </property>
            <property name="add_pho">
                <map>
                    <entry key="北京">
                        <value>020</value>
                    </entry>
                    <entry key="上海">
                        <value>010</value>
                    </entry>
                    <entry key="广州">
                        <value>030</value>
                    </entry>
                </map>
            </property>
        </bean>
    

    测试类:

            // 启动容器
            ApplicationContext ac = new ClassPathXmlApplicationContext(
                    new String[]{"com/wtu/spring/di/set/spring3.0.xml"}
            );
    
            Customer customer = (Customer) ac.getBean("customer");
            System.out.println(customer.getId() + "--" + customer.getName());
            System.out.println(customer.getBirthday().toLocaleString());
    
            for (String addr : customer.getAddress()) {
                System.out.println(addr);
            }
            for (String phone : customer.getPhone()) {
                System.out.println(phone);
            }
            for (Map.Entry entry : customer.getAdd_pho().entrySet()) {
                System.out.println(entry.getKey() + "--" + entry.getValue());
            }
    
    结果.png

    为里面元素为自定义类型的集合DI:
    实体类:

    public class Addr {
        private String name;
    
        public String getName() {
            return name;
        }
    
        public void setName(String name) {
            this.name = name;
        }
    }
    
    public class Phone {
        private String number;
    
        public void setNumber(String number) {
            this.number = number;
        }
    
        public String getNumber() {
            return number;
        }
    }
    
    public class Customer {
        private List<Addr> address;
        private Set<Phone> phone;
        private Map<Addr, Phone> add_pho;
    
        public Customer() {
            System.out.println("IOC 创建对象!");
        }
    
        // getter and setter omitted
    }
    

    Spring配置:

        <!-- 注册Addr对象和Phone对象 -->
        <bean id="addr1" class="com.wtu.spring.di.set2.Addr">
            <property name="name" value="北京"/>
        </bean>
        <bean id="addr2" class="com.wtu.spring.di.set2.Addr">
            <property name="name" value="上海"/>
        </bean>
        <bean id="addr3" class="com.wtu.spring.di.set2.Addr">
            <property name="name" value="广州"/>
        </bean>
    
        <bean id="phone1" class="com.wtu.spring.di.set2.Phone">
            <property name="number" value="010"/>
        </bean>
        <bean id="phone2" class="com.wtu.spring.di.set2.Phone">
            <property name="number" value="020"/>
        </bean>
        <bean id="phone3" class="com.wtu.spring.di.set2.Phone">
            <property name="number" value="023"/>
        </bean>
    
        <!-- 注册Customer对象 -->
        <bean id="customer" class="com.wtu.spring.di.set2.Customer">
            <property name="address">
                <list>
                    <ref bean="addr1"/>
                    <ref bean="addr2"/>
                    <ref bean="addr3"/>
                </list>
            </property>
            <property name="phone">
                <set>
                    <ref bean="phone1"/>
                    <ref bean="phone2"/>
                    <ref bean="phone3"/>
                </set>
            </property>
            <property name="add_pho">
                <map>
                    <entry key-ref="addr1">
                        <ref bean="phone1"/>
                    </entry>
                    <entry key-ref="addr2">
                        <ref bean="phone2"/>
                    </entry>
                    <entry key-ref="addr3">
                        <ref bean="phone3"/>
                    </entry>
                </map>
            </property>
        </bean>
    

    测试类:

    import org.springframework.context.ApplicationContext;
    import org.springframework.context.support.ClassPathXmlApplicationContext;
    
    import java.util.Map;
    
    /**
     * @Author menglanyingfei
     * @Created on 2018.01.16 16:21
     */
    public class TestSpringIoc {
    
        public static void main(String[] args) {
            // 启动容器
            ApplicationContext ac = new ClassPathXmlApplicationContext(
                    new String[]{"com/wtu/spring/di/set2/spring3.0.xml"}
            );
    
            Customer customer = (Customer) ac.getBean("customer");
    
            for (Addr addr : customer.getAddress()) {
                System.out.println(addr.getName());
            }
    
            for (Phone phone : customer.getPhone()) {
                System.out.println(phone.getNumber());
            }
    
            for (Map.Entry<Addr, Phone> e : customer.getAdd_pho().entrySet()) {
                System.out.println(e.getKey() + "-->" + e.getValue());
            }
        }
    }
    

    运行结果:


    image.png

    至于DI(依赖注入)的构造器注入, 我留到AOP下一节内容总结!
    敬请期待!

    所有完整示例代码见Github

    https://github.com/menglanyingfei/SSMLearning/tree/master/spring_day01

    相关文章

      网友评论

          本文标题:SSM框架系列学习总结1之Spring Ioc

          本文链接:https://www.haomeiwen.com/subject/kqhtaxtx.html