IOC[inversion Of Control 反转控制]

IOC[inversion Of Control 反转控制]

​ 在传统的开发模式下,需要使用JdbcUtils / 反射,由程序员编写程序, 在程序中读取配置信息 ,然后创建对象, new Object???() // 反射方式,再使用对象完成任务。程序员不能够专注的实现对象对业务的处理。

​ IOC的开发模式很好的解决了这个问题。Spring 根据配置文件 xml/注解 创建对象,并将对象放入到容器(ConcurrentHashMap)中, 可以完成对象之间的依赖。

当需要使用某个对象实例的时候, 可以直接从容器中获取。程序员就可以更加关注如何使用对象完成相应的业务, (以前需要 new … ==> 注解/配置 方式)。

​ DI(Dependency Injection)依赖注入,可以理解成是 IOC 的另外叫法。Spring 最大的价值是通过配置,给程序提供需要使用的web 层[Servlet(Action/Controller)]/Service/Dao/[JavaBean/entity]对象, 这个是核心价值所在,也是 ioc 的具体体现,实现解耦。

Spring 快速入门实例

在开始之前我们需要下载spring5的开发包。

commons-logging-1.1.3.jar:spring日志需要的jar。

spring开发ioc基本包:

​ spring-beans-5.3.8.jar
​ spring-context-5.3.8.jar
​ spring-core-5.3.8.jar
​ spring-expression-5.3.8.jar

还要导入Dom4j.jar包

创建一个beans.xml存放bean。

<!--配置Monster对象-->
    <bean id="monster" class="com.spring.bean.Monster">
        <property name="monsterId" value="1"/>
        <property name="name" value="蜈蚣精"/>
        <property name="skill" value="蜇人"/>
    </bean>

获取对象并给对象属性赋值输出。

package com.spring.test;
import com.spring.beans.Monster;
import org.dom4j.Document;
import org.dom4j.DocumentException;
import org.dom4j.Element;
import org.dom4j.io.SAXReader;
import java.io.File;
import java.util.List;
import java.util.concurrent.ConcurrentHashMap;

public class ApplicationContext {
    private ConcurrentHashMap<String, Object> ioc =
    new ConcurrentHashMap<>();

    public ApplicationContext(String iocBeanXmlFile) throws DocumentException,
    ClassNotFoundException, IllegalAccessException, InstantiationException {
        //反射的方式得到->使用dom4j
        //得到一个解析器
        SAXReader reader = new SAXReader();
        Document document = reader.read(new File(iocBeanXmlFile));
        //1. 得到rootElement
        Element rootElement = document.getRootElement();
        //2. 获取第1 个bean, 如果有多个就遍历所有
        Element bean = (Element) rootElement.elements("bean").get(0);
        String id = bean.attributeValue("id");
        String classFullPath = bean.attributeValue("class");
        List<Element> property = bean.elements("property");
        Integer monsterId = Integer.parseInt(property.get(0).attributeValue("value"));
        String name = property.get(1).attributeValue("value");
        String skill = property.get(2).attributeValue("value");
        //3. 使用反射创建bean 实例,并放入到ioc 中
        Class cls = Class.forName(classFullPath);
            Monster instance = (Monster)cls.newInstance();
        instance.setMonsterId(monsterId);
        instance.setName(name);
        instance.setSkill(skill);
        ioc.put(id, instance);
        }
        public Object getBean(String id) {
        return ioc.get(id);
    }
}

创建一个测试类

package com.spring.test;
import com.spring.beans.Monster;
import org.dom4j.DocumentException;
public class SpringIOCTest {
    public static void main(String[] args) throws DocumentException,
    IllegalAccessException, InstantiationException, ClassNotFoundException {
        //得到beans.xml 文件.
        ApplicationContext ioc = new ApplicationContext("src/beans.xml");
        Monster monster01 = (Monster)ioc.getBean("monster01");
        System.out.println("ioc 的monster01= " + monster01);
    }
}

获取bean的方法

通过类型获取bean

//通过类型来获取容器的bean 对象
@Test
public void getMonsterByType() {
ApplicationContext ioc = new ClassPathXmlApplicationContext("beans.xml");
    //创建容器
Monster monster = ioc.getBean(Monster.class);
    //获取容器中Monster类型的bean对象
System.out.println("monster=" + monster);
    Monster monster1 = ioc.getBean(Monster.class);
System.out.println("monster == monster1 的值= " + (monster == monster1));
}

​ 按类型来获取bean, 要求ioc 容器中的同一个类的bean 只能有一个, 否则会抛出异常NoUniqueBeanDefinitionException。一般应用于在一个线程中只需要一个对象实例(单例)的情况。在容器配置文件(比如beans.xml)中给属性赋值, 底层是通过setter 方法完成的。

通过构造器配置bean

在spring的ioc容器,可以通过构造器来配置bean对象。

<bean id="monster02" class="com.spring.beans.Monster">
<constructor-arg value="2" index="0"/>
<constructor-arg value="蜘蛛精" index="1"/>
<constructor-arg value="蛛丝束缚" index="2"/>
     //1. 通过index 属性来区分是第几个参数
</bean>
<!--数据类型就是对应的Java 数据类型,按构造器参数顺序-->
<bean id="monster03" class="com.spring.beans.Monster">
<constructor-arg value="3" type="java.lang.Integer"/>
<constructor-arg value="白骨精" type="java.lang.String"/>
<constructor-arg value="白骨鞭" type="java.lang.String"/>
</bean>
//2. 通过type 属性来区分是什么类型(按照顺序)

通过p名称空间配置bean

<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:p="http://www.springframework.org/schema/p"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd">
<!--在spring 的ioc 容器, 可以通过p 名称空间来配置bean 对象-->
<bean id="monster04" class="com.spring.beans.Monster"
p:monsterId="4"
p:name="红孩儿"
p:skill="吐火~"
/>

引用/注入其它bean 对象

在spring 的ioc 容器, 可以通过ref 来实现bean 对象的相互引用。

<!-- bean 对象的相互引用
        1. 其它含义和前面一样
        2. ref 表示memberDAO 这个属性将引用/指向id = memberDAOImpl 对象
-->
<bean id="memberServiceImpl" class="com.spring.service.MemberServiceImpl">
<property name="memberDAO" ref="memberDAOImpl"/>
</bean>
<bean id="memberDAOImpl" class="com.spring.dao.MemberDAOImpl"/>

引用/注入内部bean 对象

<bean id="memberServiceImpl02"
class="com.spring.service.MemberServiceImpl">
    <property name="memberDAO">
    	<bean class="com.spring.dao.MemberDAOImpl"/>
    </property>
</bean>

引用/注入集合/数组类型

<bean id="master01" class="com.spring.beans.Master">
    <property name="name" value="太上老君"/>
    <!-- 给bean 对象的list 集合赋值-->
    <property name="monsterList">
        <list>
            <ref bean="monster03"/>
            <ref bean="monster02"/>
        </list>
    </property>
    <!-- 给bean 对象的map 集合赋值-->
    <property name="monsterMap">
        <map>
            <entry>
            <key>
            	<value>monsterKey01</value>
            </key>
            <ref bean="monster01"/>
            </entry>
            <entry>
                <key>
            	<value>monsterKey02</value>
                </key>
           	   <ref bean="monster02"/>
            </entry>
        </map>
    </property>
    <!-- 给bean 对象的properties 集合赋值-->
    <property name="pros">
        <props>
            <prop key="k1">Java 工程师</prop>
            <prop key="k2">前端工程师</prop>
            <prop key="k3">大数据工程师</prop>
        </props>
    </property>
    <!-- 给bean 对象的数组属性注入值-->
    <property name="monsterName">
        <array>
            <value>银角大王</value>
            <value>金角大王</value>
        </array>
    </property>
        <!-- 给bean 对象的set 属性注入值-->
    <property name="monsterSet">
        <set>
            <ref bean="monster01"/>
            <bean class="com.spring.beans.Monster">
                <property name="monsterId" value="10"/>
                <property name="name" value="玉兔"/>
                <property name="skill" value="钻地洞"/>
            </bean>
        </set>
    </property>
</bean>

测试

@Test
public void setCollectionByPro() {
    ApplicationContext ioc = new ClassPathXmlApplicationContext("beans.xml");
        Master master01 = ioc.getBean("master01", Master.class);
    //获取list 集合
    System.out.println("======list=======");
    List<Monster> monster_list = master01.getMonsterList();
    for (Monster monster : monster_list) {
   	 	System.out.println(monster);
    }
    //获取map 集合
    System.out.println("======map=======");
    Map<String, Monster> monster_map = master01.getMonsterMap();
    Set<Map.Entry<String, Monster>> entrySet = monster_map.entrySet();
    for (Map.Entry<String, Monster> entry : entrySet) {
   		 System.out.println(entry);
    }
    //获取properties 集合
    System.out.println("======properties=======");
    Properties pros = master01.getPros();
    String property1 = pros.getProperty("k1");
    String property2 = pros.getProperty("k2");
    String property3 = pros.getProperty("k3");
    System.out.println(property1 + "\t" + property2 + "\t" + property3);
    //获取数组
    System.out.println("======数组=======");
    String[] monsterName = master01.getMonsterName();
    for (String s : monsterName) {
   	 	System.out.println("妖怪名= " + s);
    }
    //获取set
    System.out.println("======set=======");
    Set<Monster> monsterSet = master01.getMonsterSet();
    for (Monster monster : monsterSet) {
    	System.out.println(monster);
    }
}

细节

  1. 主要掌握List/Map/Properties 三种集合的使用。
  2. Properties 集合的特点。
    1. 这个Properties 是Hashtable 的子类, 是key-value 的形式。
    2. key 是string 而value 也是string。

通过util 名称空间创建list

<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns:p="http://www.springframework.org/schema/p"
    xmlns:util="http://www.springframework.org/schema/util"
    xsi:schemaLocation="http://www.springframework.org/schema/beans
    http://www.springframework.org/schema/beans/spring-beans.xsd
    http://www.springframework.org/schema/util
    http://www.springframework.org/schema/util/spring-util.xsd">
    <!--通过util 名称空间来创建list 集合,可以当做创建bean 对象的工具来使用-->
    <util:list id="myListBook">
            <value>三国演义</value>
            <value>西游记</value>
            <value>红楼梦</value>
            <value>水浒传</value>
    </util:list>
    <bean id="bookStore" class="com.spring.beans.BookStore">
    	<property name="bookList" ref="myListBook"/>
    </bean>

级联属性赋值

spring 的ioc 容器, 可以直接给对象属性的属性赋值, 即级联属性赋值。

<bean id="emp" class="com.spring.beans.Emp">
        <property name="name" value="jack"/>
        <property name="dept" ref="dept"/>
        <property name="dept.name" value="Java 开发部"/> 
</bean>
<bean id="dept" class="com.spring.beans.Dept"/>
package com.spring.beans;
public class Dept {
        private String name;
        public Dept() {
        }
        public String getName() {
            return name;
        }
        public void setName(String name) {
            this.name = name;
        }
}
package com.spring.beans;
public class Emp {
    private String name;
    private Dept dept;
    public Emp() {
    }
    public String getName() {
        return name;
    }
    public void setName(String name) {
   		this.name = name;
    }
    public Dept getDept() {
   		 return dept;
    }
    public void setDept(Dept dept) {
    	this.dept = dept;
    }
}

通过静态工厂获取对象

package com.spring.factory;
import com.spring.beans.Monster;
import java.util.HashMap;
import java.util.Map;
public class MyStaticFactory {
    private static Map<String, Monster> monsterMap;
    static {
        monsterMap = new HashMap<String, Monster>();
            monsterMap.put("monster_01", new Monster(100, "黄袍怪", "一阳指"));
        monsterMap.put("monster_02", new Monster(200, "九头金雕", "如来神掌"));
    }
    public static Monster getMonster(String key) {
    	return monsterMap.get(key);
    }
}
<!-- 通过静态工厂来获取bean 对象-->
<bean id="my_monster" class="com.spring.factory.MyStaticFactory" factory-method="getMonster">
<!-- constructor-arg 标签提供key -->
<constructor-arg value="monster_01"/>
</bean>

通过实例工厂获取对象

package com.spring.factory;
import com.spring.beans.Monster;
import java.util.HashMap;
import java.util.Map;
public class MyInstanceFactory {
        private Map<String, Monster> monster_map;
        //非静态代码块
        {
            monster_map = new HashMap<String, Monster>();
            monster_map.put("monster_01", new Monster(100, "猴子精", "吃桃"));
            monster_map.put("monster_02", new Monster(200, "佛祖", "如来神掌"));
        }
        public Monster getMonster(String key) {
            return monster_map.get(key);
        }
}
<bean id="myInstanceFactory" class="com.spring.factory.MyInstanceFactory"/>
<bean id="my_monster2" factory-bean="myInstanceFactory"
factory-method="getMonster">
	<constructor-arg value="monster_02"/>
</bean>

bean 配置信息重用(继承)

在spring 的ioc 容器, 提供了一种继承的方式来实现bean 配置信息的重用。

<!-- 继承的方式来实现bean 配置信息的重用-->
<bean id="monster10" class="com.spring.beans.Monster">
    <property name="monsterId" value="10"/>
    <property name="name" value="蜈蚣精"/>
    <property name="skill" value="蜇人"/>
</bean>
<!-- parent="monster10" 就是继承使用了monster10 的配置信息-->
<bean id="monster11" class="com.spring.beans.Monster"
parent="monster10"/>
<!-- 当我们把某个bean设置为abstract="true" 这个bean只能被继承,而不能实例化了-->
<bean id="monster12" class="com.spring.beans.Monster" abstract="true">
        <property name="monsterId" value="12"/>
        <property name="name" value="美女蛇"/>
        <property name="skill" value="诱惑"/>
</bean>
<!-- parent="monster12" 就是继承使用了monster12 的配置信息-->
<bean id="monster13" class="com.spring.beans.Monster"
parent="monster12"/>

测试

@Test
public void getBeanByExtends() {
    ApplicationContext ioc = new ClassPathXmlApplicationContext("beans.xml");
    Monster monster1 = ioc.getBean("monster11", Monster.class);
    System.out.println(monster1);
    Monster monster2 = (Monster) ioc.getBean("monster13", Monster.class);
    System.out.println(monster2);
}

bean 创建顺序

  1. 在spring 的ioc 容器, 默认是按照配置的顺序创建bean 对象
<bean id="student01" class="com.bean.Student" />
<bean id="department01" class="com.bean.Department" />

会先创建student01 这个bean 对象,然后创建department01 这个bean 对象。

  1. 如果这样配置
<bean id="student01" class="com.bean.Student" depends-on="department01"/>
<bean id="department01" class="com.bean.Department" />

会先创建department01 对象,再创建student01 对象.

bean 对象的单例和多例

在spring 的ioc 容器, 在默认是按照单例创建的,即配置一个bean 对象后,ioc 容器只会创建一个bean 实例。
如果我们希望ioc容器配置某个bean 对象,以多个实例形式创建,则可以通过配置scope=“prototype” 来指定。

//配置beans.xml
<bean name="car" scope="prototype" class="com.spring.beans.Car"/>

测试

@Test
public void getBeanByPrototype() {
  ApplicationContext ioc = new ClassPathXmlApplicationContext("beans.xml");
  for (int i = 0; i < 3; i++) {
      Car car = ioc.getBean("car", Car.class);
      System.out.println(car);
  }
}
注意
1. 默认是单例singleton, 在启动容器时, 默认就会创建, 并放入到singletonObjects 集合。
2. 当<bean scope="prototype" > 设置为多实例机制后, 该bean 是在getBean()时才创建。
3. 如果是单例singleton, 同时希望在getBean 时才创建, 可以指定懒加载

lazy-init="true"。

4. 通常情况下, lazy-init 使用默认值false , 在开发看来用空间换时间是值得的, 除非有特殊的要求。
5. 如果scope="prototype" 多实例机制,lazy-init 属性的值不管是ture, 还是false 都是在getBean 时候创建对象。

bean 的生命周期

bean 对象创建是由JVM 完成的。通过如下步骤可以进行测试

  1. 执行构造器。

  2. 执行set 相关方法。

    package com.spring.beans;
    public class House {
        private String name;
        public House() {
        	System.out.println("House() 构造器");
        }
        public String getName() {
        return name;
        }
        public void setName(String name) {
        	System.out.println("House setName()...");
        	this.name = name;
        }
        public void init() {
        	System.out.println("House init()..");
        }
        public void destory() {
        	System.out.println("House destory()..");
        }
    }
    
  3. 调用bean 的初始化的方法(需要配置)。

    <!-- 配置bean 的初始化方法和销毁方法-->
    <bean id="house" class="com.spring.beans.House"
        init-method="init"
        destroy-method="destory">
        <property name="name" value="深圳"/>
    </bean>
    
  4. 使用bean。

  5. 当容器关闭时候,调用bean 的销毁方法(需要配置)。

    测试

    @Test
    public void beanLife() {
        ApplicationContext ioc = new ClassPathXmlApplicationContext("beans.xml");
        House house = ioc.getBean("house", House.class);
        System.out.println(house);
        ((ConfigurableApplicationContext) ioc).close();
    }
    
    注意
    1. 初始化init 方法和destory 方法, 由程序员设置。
    2. 销毁方法当关闭容器时,才会被调用。

配置bean 的后置处理器

  1. 在spring 的ioc 容器,可以配置bean 的后置处理器。
  2. 该处理器/对象会在bean 初始化方法调用前和初始化方法调用后被调用。
  3. 程序员可以在后置处理器中编写自己的代码。
public class MyBeanPostProcessor implements BeanPostProcessor {
    /**
    * 在bean 初始化之前完成某些任务
    * @param bean : 就是ioc 容器返回的bean 对象, 如果这里被替换会修改,则返
    回的bean 对象也会被修改
    * @param beanName: 就是ioc 容器配置的bean 的名称
    * @return Object: 就是返回的bean 对象
    */
    public Object postProcessBeforeInitialization(Object bean, String beanName)
    throws BeansException {
        // TODO Auto-generated method stub
        System.out.println("postProcessBeforeInitialization 被调用" + beanName + "
        bean= " + bean.getClass());
                           return bean;
    }
    /**
    * 在bean 初始化之后完成某些任务
    * @param bean : 就是ioc 容器返回的bean 对象, 如果这里被替换会修改,则返
    回的bean 对象也会被修改
    * @param beanName: 就是ioc 容器配置的bean 的名称
    * @return Object: 就是返回的bean 对象
    */
    public Object postProcessAfterInitialization(Object bean, String beanName)
    throws BeansException {
        System.out.println("postProcessAfterInitialization 被调用" + beanName + " bean= "
        + bean.getClass());
        return bean;
    }
}

配置beans.xml

<?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.xsd">
<!-- 配置bean 的初始化方法和销毁方法-->
<bean id="house" class="com.spring.beans.House"
init-method="init"
destroy-method="destory">
	<property name="name" value="北京豪宅"/>
</bean>
<!-- bean 后置处理器的配置-->
<bean id="myBeanPostProcessor"
class="com.spring.beans.MyBeanPostProcessor" />
</beans>

测试

@Test
public void testBeanPostProcessor() {
    ApplicationContext ioc = new ClassPathXmlApplicationContext("beans02.xml");
    House house = ioc.getBean("house", House.class);
    System.out.println(house);
    //关闭容器
    ((ConfigurableApplicationContext) ioc).close();
}

使用AOP(反射+动态代理+IO+容器+注解)执行后置处理器,可以对IOC 容器中所有的对象进行统一处理,比如日志处理/权限的校验/安全的验证/事务管理。是切面编程的特点。

通过属性文件给bean 注入值

<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns:p="http://www.springframework.org/schema/p"
    xmlns:util="http://www.springframework.org/schema/util"
    xmlns:context="http://www.springframework.org/schema/context"
    xmlns:aop="http://www.springframework.org/schema/aop"
    xsi:schemaLocation="http://www.springframework.org/schema/beans
    http://www.springframework.org/schema/beans/spring-beans.xsd
    http://www.springframework.org/schema/util
    http://www.springframework.org/schema/util/spring-util.xsd
    http://www.springframework.org/schema/context
    http://www.springframework.org/schema/context/spring-context.xsd">
<!--
    1. 通过属性文件给bean 注入值,
    2. 需要导入: xmlns:context 名字空间,并指定属性文件路径
-->
<context:property-placeholder location="classpath:my.properties"/>
<bean id="monster10" class="com.spring.beans.Monster">
    <property name="monsterId" value="${id}"/>
    <property name="name" value="${name}"/>
    <property name="skill" value="${skill}"/>
</bean>
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

晨犀

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值