zoukankan      html  css  js  c++  java
  • Spring-3--IOC/DI

    一、IOC /DI 概述

    1-1、IOC-控制反转

    IOC(Inversion of Control):其思想是反转资源获取的方向. 传统的资源查找方式要求组件向容器发起请求查找资源. 作为回应, 容器适时的返回资源. 而应用了 IOC 之后, 则是容器主动地将资源推送给它所管理的组件, 组件所要做的仅是选择一种合适的方式来接受资源. 这种行为也被称为查找的被动形式。


    1-2、DI-依赖注入

    DI(Dependency Injection) — IOC 的另一种表述方式:即组件以一些预先定义好的方式(例如: setter 方法)接受来自如容器的资源注入. 相对于 IOC 而言,这种表述更直接。


    1-3、在 Spring IOC 容器中配置 Bean
    • 1、在 Spring IOC 容器读取 Bean 配置创建 Bean 实例之前, 必须对它进行实例化. 只有在容器实例化后, 才可以从 IOC 容器里获取 Bean 实例并使用。

    • 2、Spring 提供了两种类型的 IOC 容器实现。

      • BeanFactory: IOC 容器的基本实现:
      • ApplicationContext: 提供了更多的高级特性. 是 BeanFactory 的子接口。

    二、配置 Bean

    2-1、配置形式
    • 基于 XML 文件的配置方式
    • 基于注解的方式(实际开发中主要使用该方式)

    2-2、Bean 的配置方式
    • 通过全类名(反射)
    • 通过工厂方法(静态工厂方法&实例工厂方法)
    • FactoryBean

    2-3、IOC 容器 BeanFactory & ApplicationContext 概述
    • BeanFactory 是 Spring 框架的基础设施,面向 Spring 本身;ApplicationContext 面向使用 Spring 框架的开发者,几乎所有的应用场合都直接使用 ApplicationContext 而非底层的 BeanFactory
    • ApplicationContext 接口

    ApplicationContext 类图
    ApplicationContext 类图

    ApplicationContext 的主要实现类:
    	ClassPathXmlApplicationContext:从 类路径下加载配置文件
    	FileSystemXmlApplicationContext: 从文件系统中加载配置文件
    	
    ConfigurableApplicationContext 扩展于 ApplicationContext,新增加两个主要方法:refresh() 和 close(), 让 ApplicationContext 具有启动、刷新和关闭上下文的能力。
    
    ApplicationContext 在初始化上下文时就实例化所有单例的 Bean。
    
    WebApplicationContext 是专门为 WEB 应用而准备的,它允许从相对于 WEB 根目录的路径中完成初始化工作。

    从容器中 获取 Bean

    getBean()方法
    getBean()方法


    2-4、依赖注入的方式
    • 属性注入(注入细节)
      • 字面值
      • 引用其他 Bean
      • 内部Bean
      • 注入参数:(null 值和级联属性)
      • 集合属性& utility scheme 定义集合
      • 使用 P 命名空间
    • 构造器注入
    • 泛型依赖注入

    1、Beans
    com.atguigu.spring.helloworld.HelloWorld
    com.atguigu.spring.ref.Dao
    com.atguigu.spring.ref.Service

    package com.atguigu.spring.helloworld;
    
    public class HelloWorld {
    
    	private String user;
    
    	public HelloWorld() {
    		System.out.println("HelloWorld's constructor...");
    	}
    
    	public HelloWorld(String user) {
    		this.user = user;
    	}
    
    	public void setUser(String user) {
    		System.out.println("setUser:" + user);
    		this.user = user;
    	}
    
    	public void hello(){
    		System.out.println("Hello: " + user);
    	}
    	
    }
    
    package com.atguigu.spring.ref;
    
    public class Dao {
    
    	private String dataSource = "dbcp";
    	
    	public void setDataSource(String dataSource) {
    		this.dataSource = dataSource;
    	}
    	
    	public void save(){
    		System.out.println("save by " + dataSource);
    	}
    	
    	public Dao() {
    		System.out.println("Dao's Constructor...");
    	}
    	
    	public String toString(){
    		return "[dataSource]:"+dataSource;
    	}
    	
    }
    
    package com.atguigu.spring.ref;
    
    public class Service {
    
    	private Dao dao;
    	
    	public void setDao(Dao dao) {
    		this.dao = dao;
    	}
    	
    	public Dao getDao() {
    		return dao;
    	}
    	
    	public void save(){
    		System.out.println("Service's save");
    		dao.save();
    	}
    	
    }
    
    

    2、Spring xml配置Beans

    <beans xmlns="http://www.springframework.org/schema/beans"
           xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
           xmlns:util="http://www.springframework.org/schema/util"
           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
    		http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util-4.0.xsd">
    
        <!-- 1、属性注入:配置一个 bean 通过 setter 方法 为属性赋值 -->
        <bean id="helloWorld1" class="com.atguigu.spring.helloworld.HelloWorld">
            <property name="user" value="Jerry"/><!-- 为属性赋值 -->
        </bean>
    
    
        <!-- 2、构造器注入:通过构造器注入属性值 -->
        <bean id="helloWorld3" class="com.atguigu.spring.helloworld.HelloWorld">
            <constructor-arg value="Mike"/><!-- 要求: 在 Bean 中必须有对应的构造器.  -->
        </bean>
    
        <!-- 2-1、若一个 bean 有多个构造器, 如何通过构造器来为 bean 的属性赋值 -->
        <!-- 可以根据 index 和 value 进行更加精确的定位. (了解) -->
        <bean id="car" class="com.atguigu.spring.helloworld.Car">
            <constructor-arg value="KUGA" index="1"/>
            <constructor-arg value="ChangAnFord" index="0"/>
            <constructor-arg value="250000" type="float"/>
        </bean>
    
        <!--3、属性注入细节:字面值-->
        <bean id="car2" class="com.atguigu.spring.helloworld.Car">
            <constructor-arg value="ChangAnMazda"/>
            <constructor-arg>
                <value><![CDATA[<ATARZA>]]></value><!-- 若字面值中包含特殊字符, 则可以使用 CDATA 来进行赋值. (了解) -->
            </constructor-arg>
            <constructor-arg value="180" type="int"/>
        </bean>
    
        <!-- 4、属性注入细节:引用其他 bean 配置 -->
        <bean id="dao5" class="com.atguigu.spring.ref.Dao"/>
    
        <bean id="service" class="com.atguigu.spring.ref.Service">
            <property name="dao" ref="dao5"/><!-- 通过 ref 属性值指定当前属性指向哪一个 bean! -->
        </bean>
    
        <!-- 5、属性注入细节:声明使用内部 bean -->
        <bean id="service2" class="com.atguigu.spring.ref.Service">
            <property name="dao">
                <bean class="com.atguigu.spring.ref.Dao"><!-- 内部 bean, 类似于匿名内部类对象. 不能被外部的 bean 来引用, 也没有必要设置 id 属性 -->
                    <property name="dataSource" value="c3p0"/>
                </bean>
            </property>
        </bean>
    
        <!--6、属性注入细节:注入参数之级联属性-->
        <bean id="action" class="com.atguigu.spring.ref.Action">
            <property name="service" ref="service2"/>
            <property name="service.dao.dataSource" value="DBCP2"/><!-- 设置级联属性(了解) -->
        </bean>
    
        <bean id="dao2" class="com.atguigu.spring.ref.Dao">
            <property name="dataSource">
                <null/>
            </property><!-- 为 Dao 的 dataSource 属性赋值为 null, 若某一个 bean 的属性值不是 null, 使用时需要为其设置为 null(了解) -->
        </bean>
    
        <!-- 7、属性注入细节:装配集合属性 -->
        <bean id="user" class="com.atguigu.spring.helloworld.User">
            <property name="userName" value="Jack"/>
            <property name="cars">
                <list><!-- 使用 list 元素来装配集合属性 -->
                    <ref bean="car"/>
                    <ref bean="car2"/>
                </list>
            </property>
        </bean>
    
        <!-- 7-1、声明集合类型的 bean -->
        <util:list id="cars">
            <ref bean="car"/>
            <ref bean="car2"/>
        </util:list>
    
        <!--7-2、引用外部声明的集合-->
        <bean id="user2" class="com.atguigu.spring.helloworld.User">
            <property name="userName" value="Rose"/>
            <property name="cars" ref="cars"/><!-- 引用外部声明的 list -->
        </bean>
    
    
        <!--8、属性注入细节:定义 P 命名空间-->
        <bean id="user3" class="com.atguigu.spring.helloworld.User"
              p:cars-ref="cars" p:userName="Titannic"/>
    
        <!--
             bean 的配置能够继承吗 ? 使用 parent 来完成继承
        -->
        <bean id="user4" parent="user" p:userName="Bob"/>
    
        <bean id="user6" parent="user" p:userName="维多利亚"/>
    
        <!-- 测试 depents-on -->
        <bean id="user5" parent="user" p:userName="Backham" depends-on="user6"/>
    
    </beans>

    3、测试 Main

    package com.atguigu.spring.helloworld;
    
    import com.atguigu.spring.ref.*;
    import org.springframework.context.support.ClassPathXmlApplicationContext;
    
    public class Main {
    	
    	public static void main(String[] args) {
    
    		/*测试未使用Spring 前对象的实例化操作*/
    		HelloWorld helloWorld = new HelloWorld();
    		helloWorld.setUser("Tom");
    		helloWorld.hello();
    
    		/* 测试使用 Spring 容器 后 对象实例化的操作*/
    		ClassPathXmlApplicationContext ctx = new ClassPathXmlApplicationContext("beans.xml");//创建 Spring 的 IOC 容器
    
    		//1、测试 通过 setter 方法注入属性值
    		HelloWorld helloWorld1= (HelloWorld) ctx.getBean("helloWorld1");
    		helloWorld1.hello();
    
    		//根据类型来获取 bean 的实例: 要求在  IOC 容器中只有一个与之类型匹配的 bean, 若有多个则会抛出异常.
    		// 一般情况下, 该方法可用, 因为一般情况下, 在一个 IOC 容器中一个类型对应的 bean 也只有一个.
    		//HelloWorld helloWorld1 = ctx.getBean(HelloWorld.class);
    
    		// 2、测试通过构造器为属性赋值
    		HelloWorld helloWorld3 = (HelloWorld)ctx.getBean("helloWorld3");
    		helloWorld3.hello();
    
    		/* 2-1、测试 bean 有多个构造器, 如何通过构造器来为 bean 的属性赋值*/
    		Car car = (Car) ctx.getBean("car");
    		System.out.println(car);//Car [company=ChangAnFord, brand=KUGA, maxSpeed=0, price=250000.0]
    
    		/*3、属性注入细节:字面值*/
    		Car car2= (Car) ctx.getBean("car2");
    		System.out.println(car2);//Car [company=ChangAnMazda, brand=<ATARZA>, maxSpeed=180, price=0.0]
    
    		//4. 属性注入细节:引用其他 Bean+级联属性设置
    		Service service2= (Service) ctx.getBean("service2");
    		System.out.print("service2:"+service2.getDao().toString());//由于级联属性的设置,输出结果为:[dataSource]:DBCP2
    
    		//7、属性注入细节:装配集合属性
    
    
    	}
    	
    }

    2-5、自动装配
    • XML 配置里的 Bean 自动装配。
    • XML 配置里的 Bean 自动装配的缺点。
    <!-- 1、自动装配: 只声明 bean, 而把 bean 之间的关系交给 IOC 容器来完成 -->
    	<!--  
    		byType: 根据类型进行自动装配. 但要求 IOC 容器中只有一个类型对应的 bean, 若有多个则无法完成自动装配.
    		byName: 若属性名和某一个 bean 的 id 名一致, 即可完成自动装配. 若没有 id 一致的, 则无法完成自动装配
    	-->
    	<!-- 在使用 XML 配置时, 自动装配用的不多. 但在基于 注解 的配置时, 自动装配使用的较多.  -->
    	<bean id="dao" class="com.atguigu.spring.ref.Dao">
    		<property name="dataSource" value="C3P0"></property>				
    	</bean>

    注:实际项目中很少使用xml方式的自动装配,在基于 注解 的配置时, 自动装配使用的较多,此处XML 配置方式的自动装配仅做了解。


    2-6、Bean 之间的关系
    • 继承
    Spring 允许继承 bean 的配置, 被继承的 bean 称为父 bean. 继承这个父 Bean Bean 称为子 Bean
    Bean 从父 Bean 中继承配置, 包括 Bean 的属性配置
    子 Bean 也可以覆盖从父 Bean 继承过来的配置
    父 Bean 可以作为配置模板, 也可以作为 Bean 实例. 若只想把父 Bean 作为模板, 可以设置 <bean> 的abstract 属性为 true, 这样 Spring 将不会实例化这个 Bean
    并不是 <bean> 元素里的所有属性都会被继承. 比如: autowire, abstract 等.
    也可以忽略父 Bean 的 class 属性, 让子 Bean 指定自己的类, 而共享相同的属性配置. 但此时 abstract 必须设为 true
    • 依赖
    Spring 允许用户通过 depends-on 属性设定 Bean 前置依赖的Bean,前置依赖的 Bean 会在本 Bean 实例化之前创建好
    如果前置依赖于多个 Bean,则可以通过逗号,空格或的方式配置 Bean 的名称

    2-7、Bean 的作用域

    在 Spring 中, 可以在 元素的 scope 属性里设置 Bean 的作用域。

    • singleton & prototype

    beans-auto.xml

    	<!-- 1、自动装配: 只声明 bean, 而把 bean 之间的关系交给 IOC 容器来完成 -->
    	<!--  
    		byType: 根据类型进行自动装配. 但要求 IOC 容器中只有一个类型对应的 bean, 若有多个则无法完成自动装配.
    		byName: 若属性名和某一个 bean 的 id 名一致, 即可完成自动装配. 若没有 id 一致的, 则无法完成自动装配
    	-->
    	<!-- 在使用 XML 配置时, 自动装配用的不多. 但在基于 注解 的配置时, 自动装配使用的较多.  -->
    	<bean id="dao" class="com.atguigu.spring.ref.Dao">
    		<property name="dataSource" value="C3P0"></property>				
    	</bean>
    	
    	<!-- 2、默认情况下 bean 是单例的! 即为 singleton-->
    	<!-- 但有的时候, bean 就不能使单例的. 例如: Struts2 的 Action 就不是单例的! 可以通过 scope 属性来指定 bean 的作用域 -->
    	<!--  
    		prototype: 原型的. 每次调用 getBean 方法都会返回一个新的 bean. 且在第一次调用 getBean 方法时才创建实例
    		singleton: 单例的. 每次调用 getBean 方法都会返回同一个 bean. 且在 IOC 容器初始化时即创建 bean 的实例. 默认值 
    	-->
    	<bean id="dao2" class="com.atguigu.spring.ref.Dao" scope="prototype"></bean>
    	
    	<bean id="service" class="com.atguigu.spring.ref.Service" autowire="byName"></bean>
    	
    	<bean id="action" class="com.atguigu.spring.ref.Action" autowire="byType"></bean>

    测试 Main

    ClassPathXmlApplicationContext ctx = new ClassPathXmlApplicationContext("beans-auto.xml");
    		
    		
    //测试 bean 的作用域
    Dao dao1 = (Dao) ctx.getBean("dao2");
    Dao dao2 = (Dao) ctx.getBean("dao2");
    
    System.out.println(dao1 == dao2);//false 非单例 Bean
    
    • WEB 环境作用域

    2-8、使用外部属性文件
    • 在配置文件里配置 Bean 时, 有时需要在 Bean 的配置里混入系统部署的细节信息(例如: 文件路径, 数据源配置信息等). 而这些部署细节实际上需要和 Bean 配置相分离。
    • Spring 提供了一个 PropertyPlaceholderConfigurer 的 BeanFactory 后置处理器, 这个处理器允许用户将 Bean 配置的部分内容外移到属性文件中. 可以在 Bean 配置文件里使用形式为 ${var} 的变量, PropertyPlaceholderConfigurer 从属性文件里加载属性, 并使用这些属性来替换变量.
    • Spring 还允许在属性文件中使用 ${propName},以实现属性之间的相互引用。
    • Spring 2.5 之后不要再配置 PropertyPlaceholderConfigurer 类,简化为:<context:property-placeholder>
    <context:property-placeholder location="classpath:db.properties"/>
    
    <!-- 配置数据源 -->
    	<bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
    		<property name="user" value="${jdbc.user}"></property>
    		<property name="password" value="${jdbc.password}"></property>
    		<property name="driverClass" value="${jdbc.driverClass}"></property>
    		<property name="jdbcUrl" value="${jdbc.jdbcUrl}"></property>
    		
    		<property name="initialPoolSize" value="${jdbc.initPoolSize}"></property>
    		<property name="maxPoolSize" value="${jdbc.maxPoolSize}"></property>
    	</bean>

    测试 Main

    
    ClassPathXmlApplicationContext ctx = new ClassPathXmlApplicationContext("beans-auto.xml");
    //测试使用外部属性文件
    DataSource dataSource = (DataSource) ctx.getBean("dataSource");
    System.out.println(dataSource.getConnection());

    2-9、SpEL(Spring 表达式语言)
    • SpEL 简介

      • Spring 表达式语言(简称SpEL):是一个支持运行时查询和操作对象图的强大的表达式语言。
      • 语法类似于 EL:SpEL 使用 #{…} 作为定界符,所有在大框号中的字符都将被认为是 SpEL。
      • SpEL 为 bean 的属性进行动态赋值提供了便利。
      • 通过 SpEL 可以实现:
        • 通过 bean 的 id 对 bean 进行引用。
        • 调用方法以及引用对象中的属性。
        • 计算表达式的值。
        • 正则表达式的匹配。
    • SpEL:字面量

    字面量的表示
    字面量的表示

    • SpEL:引用 Bean、属性和方法

    引用 Bean、属性和方法
    引用 Bean、属性和方法

    • SpEL 支持的运算符号

    支持的运算符号1
    支持的运算符号1

    支持的运算符号2
    支持的运算符号2

    动态赋值

    <!-- 4、测试 SpEL: 可以为属性进行动态的赋值(了解) -->
    	<bean id="girl" class="com.atguigu.spring.helloworld.User">
    		<property name="userName" value="周迅"></property>
    	</bean>
    	
    	<bean id="boy" class="com.atguigu.spring.helloworld.User" init-method="init" destroy-method="destroy"><!--User 类中定义了init、destroy方法-->
    		<property name="userName" value="高胜远"></property>
    		<property name="wifeName" value="#{girl.userName}"></property>
    	</bean>

    测试 main

    ClassPathXmlApplicationContext ctx = new ClassPathXmlApplicationContext("beans-auto.xml");
    //测试 spEL
    User boy = (User) ctx.getBean("boy");
    System.out.println(boy.getUserName() + ":" + boy.getWifeName());

    2-10、IOC 容器中 Bean 的生命周期
    • 1、Spring 配置 Bean 的后置通知
    <!-- 5、配置 bean 后置处理器: 不需要配置 id 属性, IOC 容器会识别到他是一个 bean 后置处理器, 并调用其方法 -->
    	<bean class="com.atguigu.spring.ref.MyBeanPostProcessor"></bean>

    Bean

    package com.atguigu.spring.ref;
    
    import org.springframework.beans.BeansException;
    import org.springframework.beans.factory.config.BeanPostProcessor;
    
    import com.atguigu.spring.helloworld.User;
    
    public class MyBeanPostProcessor implements BeanPostProcessor {
    
    	//该方法在 init 方法之后被调用
    	@Override
    	public Object postProcessAfterInitialization(Object arg0, String arg1)
    			throws BeansException {
    		if(arg1.equals("boy")){
    			System.out.println("postProcessAfterInitialization..." + arg0 + "," + arg1);
    			User user = (User) arg0;
    			user.setUserName("李大齐");
    		}
    		return arg0;
    	}
    
    	//该方法在 init 方法之前被调用
    	//可以工作返回的对象来决定最终返回给 getBean 方法的对象是哪一个, 属性值是什么
    	/**
    	 * @param arg0: 实际要返回的对象
    	 * @param arg1: bean 的 id 值
    	 */
    	@Override
    	public Object postProcessBeforeInitialization(Object arg0, String arg1)
    			throws BeansException {
    		if(arg1.equals("boy"))
    			System.out.println("postProcessBeforeInitialization..." + arg0 + "," + arg1);
    		return arg0;
    	}
    
    }

    测试 Main

    ClassPathXmlApplicationContext ctx = new ClassPathXmlApplicationContext("beans-auto.xml");
    //测试 spEL
    User boy = (User) ctx.getBean("boy");
    System.out.println(boy.getUserName() + ":" + boy.getWifeName());
    • 2、Spring IOC 容器对 Bean 的生命周期进行管理的过程:
    通过构造器或工厂方法创建 Bean 实例。
    为 Bean 的属性设置值和对其他 Bean 的引用。
    将 Bean 实例传递给 Bean 后置处理器的 postProcessBeforeInitialization 方法。
    调用 Bean 的初始化方法。
    将 Bean 实例传递给 Bean 后置处理器的 postProcessAfterInitialization方法。
    Bean 可以使用了。
    当容器关闭时, 调用 Bean 的销毁方法。
    • 3、工厂方法&FactroyBean 方式配置 Bean

    beans-auto.xml

    <!-- 6、通过工厂方法的方式来配置 bean -->
    	<!-- 6-1. 通过静态工厂方法: 一个类中有一个静态方法, 可以返回一个类的实例(了解) -->
    	<!-- 在 class 中指定静态工厂方法的全类名, 在 factory-method 中指定静态工厂方法的方法名 -->
    	<bean id="dateFormat" class="java.text.DateFormat" factory-method="getDateInstance">
    		<constructor-arg value="2"></constructor-arg><!-- 可以通过 constructor-arg 子节点为静态工厂方法指定参数 -->
    	</bean>
    	
    	<!-- 6-2. 实例工厂方法: 先需要创建工厂对象, 再调用工厂的非静态方法返回实例(了解) -->
    	<!-- ①. 创建工厂对应的 bean -->
    	<bean id="simpleDateFormat" class="java.text.SimpleDateFormat">
    		<constructor-arg value="yyyy-MM-dd hh:mm:ss"></constructor-arg>
    	</bean>
    	
    	<!-- ②. 有实例工厂方法来创建 bean 实例 -->
    	<!-- factory-bean 指向工厂 bean, factory-method 指定工厂方法(了解) -->
    	<bean id="datetime" factory-bean="simpleDateFormat" factory-method="parse">
    		<!-- 通过 constructor-arg 执行调用工厂方法需要传入的参数 -->
    		<constructor-arg value="1990-12-12 12:12:12"></constructor-arg>
    	</bean>
    	
    	<!-- 7、配置通过 FactroyBean 的方式来创建 bean 的实例(了解) -->
    	<bean id="user" class="com.atguigu.spring.ref.UserBean"></bean>

    测试 Main

    //测试工厂方法配置 Bean
    //		DateFormat dateFormat = DateFormat.getDateInstance(DateFormat.FULL);
    DateFormat dateFormat = (DateFormat) ctx.getBean("dateFormat");
    System.out.println(dateFormat.format(new Date()));
    
    Date date = (Date) ctx.getBean("datetime");
    System.out.println(date);
    
    
    //测试通过 FactroyBean 来配置 Bean
    User user = (User) ctx.getBean("user");
    System.out.println(user);
    
    ctx.close();

    2-11、Spring4.x 新特性 泛型依赖注入

    泛型依赖注入
    泛型依赖注入


    2-12、整合多个配置文件

    整合多个配置文件
    整合多个配置文件


  • 相关阅读:
    Android 下拉刷新上啦加载SmartRefreshLayout + RecyclerView
    Maven 本地打war包
    数据库性能优化一
    SessionStateMode之Redis共享session
    SessionStateMode之SQL Server共享session
    iframe跨域
    Jenkins发布MVC应用程序
    Docker入门之一Docker在Window下安装
    Window下SVN服务器搭建以及客户端使用
    Window下Jenkins的安装
  • 原文地址:https://www.cnblogs.com/pengguozhen/p/14776928.html
Copyright © 2011-2022 走看看