spring的IoC

Posted *青锋*

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了spring的IoC相关的知识,希望对你有一定的参考价值。

IoC的基本认识

Inversion of Control:控制反转,就是将对象的创建权反转交给spring

IoC的好处

传统方式的程序编写,底层的实现切换了,需要修改源代码

使用spring之后,实现类都交给IoC容器中的BeanFactory来管理,通过工厂+反射+配置文件来实现程序的解耦合

<bean id="user" class="com.qf.demo.User">
class BeanFactory{
	public static Object getBean(String id) {//id:bean标签的id
		Class clazz = Class.forName(className);//className:bean标签的class
		return clazz.newInstance();
	}
}

IoC和DI

        <bean id="user" class="com.qf.demo.User">
		<property name="id" value="1"/>
		<property name="name" value="qf"/>
		<property name="age" value="18"/>
	</bean>    

IoC:控制反转,就是将对象的创建权反转给spring

DI:依赖注入,前提必须有IoC的环境,然后Spring管理这个类的时候把这个类依赖的属性注入进来

描述:  
  Class A中用到了Class B的对象b,一般情况下,需要在A的代码中显式的new一个B的对象。采用依赖注入技术之后,A的代码只需要定义一个私有的B对象,不需要直接new来获得这个对象,而是通过相关的容器控制程序来将B对象在外部new出来并注入到A类里的引用中。而具体获取的方法、对象被获取时的状态由配置文件(如XML)来指定

Spring的工厂类

  • ApplicationContext是新版本spring的工厂类、BeanFactory是老版本spring的工厂类
  • ApplicationContext继承了BeanFactory接口
  • BeanFactory在调用getBean方法时才会生成类的实例;ApplicationContext在加载配置文件时就会生成类的实例
  • ApplicationContext接口有两个实现类
    • ClassPathXmlApplicationContext:加载类路径下的配置文件
    • FileSystemXmlApplicationContext :加载文件系统下的配置文件

配置spring

applicationContext.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 id="user" class="com.qf.demo.User">
		<property name="id" value="1"/>
		<property name="name" value="qf"/>
		<property name="age" value="18"/>
	</bean>

</beans> 

bean配置

bean标签

  1. 标识
    • id:使用了唯一约束;不能使用特殊字符
    • name:未使用唯一约束;可以使用特殊字符,例如 /user
  2. 生命周期
    • init-method:bean被初始化时执行的方法
    • destroy-method:bean被销毁时执行的方法(bean必须是单例创建的才可以进行工厂关闭,多例的情况下无法工厂关闭) 
    • 测试,User类中定义两个方法init和destroy
       1 package com.qf.demo;
       2 
       3 public class User {
       4 
       5     private Long id;
       6     private String name;
       7     private Integer age;
       8     
       9     public void setId(Long id) {
      10         this.id = id;
      11     }
      12     public void setName(String name) {
      13         this.name = name;
      14     }
      15     public void setAge(Integer age) {
      16         this.age = age;
      17     }
      18     
      19     @Override
      20     public String toString() {
      21         return "User [id=" + id + ", name=" + name + ", age=" + age + "]";
      22     }
      23     
      24     public User(Long id, String name, Integer age) {
      25         super();
      26         this.id = id;
      27         this.name = name;
      28         this.age = age;
      29     }
      30     public User() {
      31         super();
      32     }
      33     
      34     public void init() {
      35         System.out.println("初始化----------");
      36     }
      37     public void destroy() {
      38         System.out.println("销毁----------");
      39     }
      40 }
      View Code

      applicationContext.xml中配置spring管理User对象时配置init属性和destroy属性

      <?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 id="user" class="com.qf.demo.User" init-method="init" destroy-method="destroy">
      		<property name="id" value="1"/>
      		<property name="name" value="qf"/>
      		<property name="age" value="18"/>
      	</bean>
      
      </beans>
      

      测试类

      public class TestDemo {
      
      	@Test
      	public void test() {
      //		ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
      //		ApplicationContext类里没有close方法
      		ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
      		User user1 = (User) context.getBean("user");
      		System.out.println(user1);
      //		User user2 = (User) context.getBean("user");
      //		System.out.println(user2);
      		context.close();
      	}
      }
      

      console输出结果

      初始化----------
      User [id=1, name=qf, age=18]
      销毁----------  

      注:如果bean中配置scope="prototype",测试会发现destroy不执行,即工厂无法close  

  3. 作用域
    • scope:bean的作用域属性
      • singleton:默认的,spring采用单例模式创建对象
      • prototype:spring采用多例模式创建对象 
      • request:在web项目中使用,spring创建完这个类对象后,将这个对象存入到request中
      • session:在web项目中使用,spring创建完这个类对象后,将这个对象存入到session中
      • globalSession:在web项目中使用,在porlet环境下,spring创建完这个类对象后,这个对象在其子系统中可以使用;没有porlet环境,相当于session
    • 测试1
      • 修改applicationContext.xml的bean配置,配置scope属性
        <bean id="user" class="com.qf.demo.User" scope="prototype">
      •  测试方法
        @Test
        public void test() {
        	ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
        	User user1 = (User) context.getBean("user");
        	User user2 = (User) context.getBean("user");
        	System.out.println(user2 == user1);
        }
      • console输出结果
        false  
      • 结论:采用多例模式创建对象,两次调用getBean方法创建了两个不同的对象
    • 测试2
      • 在测试1的基础上修改scope属性值为singleton
      • console输出结果
        true
      • 结论:采用单例模式创建对象,两次调用getBean方法创建了两个相同的对象

Spring的bean管理方式

XML方式:适用于任何场景。结构清晰,便于维护

注解方式:如果类不是自己提供的就不能使用(没办法改源码)。开发更加简单方便

XML方式管理Bean

spring的bean的实例化

  1. 无参构造方式实例化bean
    • 自定义bean
      public class TestBean {
      
      	public TestBean() {
      		System.out.println("无参构造方式实例化完成");
      	}
      }
    • 配置bean 
      <bean id="test" class="com.qf.demo.TestBean"></bean>
    • 测试方法
      @Test
      public void test() {
      	ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
      	TestBean test = (TestBean) context.getBean("test");
      	System.out.println(test);
      }
    • console输出
      无参构造方式实例化完成
      com.qf.demo.TestBean@635c714a
  2. 静态工厂方式实例化bean
    • 创建静态工厂类
      package com.qf.demo;
      
      public class TestBeanFactory {
      	public static Bean getBean() {
      		System.out.println("静态工厂实例化完成");
      		return new Bean();
      	}
      }
      class Bean{
      	
      }  
    • 配置bean
      <bean id="test" class="com.qf.demo.TestBeanFactory" factory-method="getBean"></bean>
  3. 实例工厂方式实例化bean
    • 创建实例工厂类
      package com.qf.demo;
      
      public class BeanInstance {
      
      	public Bean getInstance() {
      		System.out.println("实例工厂方式实例化bean完成");
      		return new Bean();
      	}
      }
    • 配置bean
      <bean id="instance" class="com.qf.demo.BeanInstance"/>
      <bean id="test" factory-bean="instance" factory-method="getInstance"/>  

属性注入

  1. 构造方法
    • bean类中定义带参数的构造方法
      package com.qf.demo;
      
      public class User {
      	private Long id;
      	private String name;
      	private Integer age;
      	private Address address;
      	
      	public User(Long id, String name, Integer age, Address address) {
      		super();
      		this.id = id;
      		this.name = name;
      		this.age = age;
      		this.address = address;
      	}
      	public User() {
      		super();
      	}
      
      	@Override
      	public String toString() {
      		return "User [id=" + id + ", name=" + name + ", age=" + age + ", address=" + address + "]";
      	}
      }
    • 配置bean
      <bean id="address" class="com.qf.demo.Address"></bean>    
      <!-- 构造方法注入属性值,通过index索引注入 -->    
      <bean id="user" class="com.qf.demo.User">
      	<constructor-arg index="0" value="1"/>
      	<constructor-arg index="1" value="wxf"/>
      	<constructor-arg index="2" value="24"/>
      	<!-- 注入的属性值是另一个bean对象:使用ref属性设置 -->
      	<constructor-arg index="3" ref="address"/>
      </bean>
      
      <!-- 构造方法注入属性值,通过name参数名称注入 -->    
      <bean id="user1" class="com.qf.demo.User">
      	<constructor-arg name="id" value="2"/>
      	<constructor-arg name="name" value="qf"/>
      	<constructor-arg name="age" value="18"/>
      	<constructor-arg name="address" ref="address"/>
      </bean>
  2. set方法
    • bean类中定义属性的setter方法
      package com.qf.demo;
      
      public class User {
      	private Long id;
      	private String name;
      	private Integer age;
      	private Address address;
      	
      	public void setId(Long id) {
      		this.id = id;
      	}
      	public void setName(String name) {
      		this.name = name;
      	}
      	public void setAge(Integer age) {
      		this.age = age;
      	}
      	public void setAddress(Address address) {
      		this.address = address;
      	}
      	
      	@Override
      	public String toString() {
      		return "User [id=" + id + ", name=" + name + ", age=" + age + ", address=" + address + "]";
      	}
      }
    • 配置bean
      <bean id="address" class="com.qf.demo.Address"></bean>   
      <!-- set方法属性注入 -->
      <bean id="user2" class="com.qf.demo.User">
      	<property name="id" value="3"/>
      	<property name="name" value="hz"/>
      	<property name="age" value="21"/>
      	<property name="address" ref="address"/>
      </bean>
  3. p名称空间
    • 用法
      • p名称空间的引入
        • xml的beans标签中添加 xmlns:p="http://www.springframework.org/schema/p" 
      • p名称空间的使用
        • 注入普通属性:p:属性名="属性值"
        • 注入对象属性:p:属性名-ref="属性值"
    • bean类中定义属性的setter方法(类中必须有属性的setter方法,否则抛出NotWritablePropertyException提示缺少属性的setter方法)
    • 配置bean
      <?xml version="1.0" encoding="UTF-8"?>
      <beans xmlns="http://www.springframework.org/schema/beans"
      	xmlns:p="http://www.springframework.org/schema/p"
          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 id="address" class="com.qf.demo.Address"/>
          <bean id="user" class="com.qf.demo.User" p:id="4" p:name="wxf" p:age="18" p:address-ref="address"/>    
          
      </beans>
  4. SpEL注入
    • 基本使用
      • #{SpEL表达式}
        • 字面量表示:#{5}(整数)、#{12.6}(小数)、#{1e3}(科学记数法)、#{\'admin\'}(字符串)、#{false}(boolean类型)
        • 引用bean表示
          • 引用其它对象:#{address}(address对象是User类的属性)
          • 引用其它对象的属性:#{address.province}(引用address对象的province属性值)
          • 引用其它对象的方法:#{address.getProvice()}、#{address?.getProvice()}(如果address是null,就不调用getProvince()方法了)
    • bean类中定义定义属性的setter方法
    • 配置bean
      <bean id="address" class="com.qf.demo.Address" p:province="AnHui" />
      <bean id="user" class="com.qf.demo.User">
      	<property name="id" value="#{5}"/>
      	<property name="name" value="#{\'qf\'}"/>
      	<property name="age" value="#{21}"/>
      	<property name="address" value="#{address.getInstance()}"/>
      </bean>  

注入集合属性

<bean id="collectionBean" class="com.qf.demo.CollectionBean">
    	<!-- 注入数组 -->
    	<property name="arr" >
    		<list>
    			<value>wxf</value>
    			<value>admin</value>
    			<value>qf</value>
    		</list>
    	</property>
    	
    	<!-- 注入list -->
    	<property name="list" >
    		<list>
    			<value>asd</value>
    			<value>zxc</value>
    			<value>wf</value>
    		</list>
    	</property>
    	
    	<!-- 注入set -->
    	<property name="set" >
    		<set>
    			<value>1</value>
    			<value>2</value>
    			<value>3</value>
    		</set>
    	</property>
    	
    	<!-- 注入数map -->
    	<property name="map" >
    		<map>
    			<entry key="wxf" value="24"></entry>
    			<entry key="qf" value="18"></entry>
    		</map>
    	</property>
    </bean>   

Spring的分模块开发

  • 加载配置文件时加载多个
    • ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext1.xml","applicationContext2.xml");  
  • 在配置文件中引入其它配置文件
    •  <import resource="xxx.xml"/>

注解方式管理Bean

IoC注解的基本使用

  1. 引入jar,使用spring注解方式管理bean,需要额外再引入aop的jar包:spring-aop-4.2.4.RELEASE.jar
    • spring-beans-4.2.4.RELEASE.jar
    • spring-context-4.2.4.RELEASE.jar
    • spring-core-4.2.4.RELEASE.jar
    • spring-expression-4.2.4.RELEASE.jar 
    • spring-aop-4.2.4.RELEASE.jar
    • com.springsource.org.apache.commons.logging-1.1.1.jar
    • com.springsource.org.apache.log4j-1.2.15.jar
  2. 引入配置文件
    1. 引入context约束
      <?xml version="1.0" encoding="UTF-8"?>
      <beans xmlns="http://www.springframework.org/schema/beans"
          xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
          xmlns:context="http://www.springframework.org/schema/context" 
          xsi:schemaLocation="
              http://www.springframework.org/schema/beans 
              http://www.springframework.org/schema/beans/spring-beans.xsd
              http://www.springframework.org/schema/context 
              http://www.springframework.org/schema/context/spring-context.xsd"> 
      	
      </beans>
    2. 开启组件扫描
      <?xml version="1.0" encoding="UTF-8"?>
      <beans xmlns="http://www.springframework.org/schema/beans"
          xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
          xmlns:context="http://www.springframework.org/schema/context" 
          xsi:schemaLocation="
              http://www.springframework.org/schema/beans 
              http://www.springframework.org/schema/beans/spring-beans.xsd
              http://www.springframework.org/schema/context 
              http://www.springframework.org/schema/context/spring-context.xsd"> 
      	<!-- IoC注解开发,配置组件扫描 base-package:哪些包下的类使用注解开发 -->
      	<context:component-scan base-package="com.qf.demo2"/>
      </beans>
  3. 创建spring管理的bean类
    • Address.java
      package com.qf.demo2;
      
      import org.springframework.beans.factory.annotation.Value;
      import org.springframework.stereotype.Component;
      
      @Component("address")
      public class Address {
      	@Value("安徽省")
      	private String province;
      	@Value("合肥市")
      	private String city;
      	@Override
      	public String toString() {
      		return "Address [province=" + province + ", city=" + city + "]";
      	}
      }
    • User.java
      package com.qf.demo2;
      
      import javax.annotation.Resource;
      
      import org.springframework.beans.factory.annotation.Value;
      import org.springframework.stereotype.Component;
      
      @Component("user")
      public class User {
      	@Value(value="1")
      	private Long id;
      	@Value(value="qf")
      	private String name;
      	@Value(value="18")
      	private Integer age;
      	@Resource(name="address")
      	private Address address;
      
      	@Override
      	public String toString() {
      		return "User [id=" + id + ", name=" + name + ", age=" + age + ", address=" + address + "]";
      	}
      }
  4. 测试
    • 编写测试类
      package com.qf.demo2;
      
      import org.junit.Test;
      import org.springframework.context.ApplicationContext;
      import org.springframework.context.support.ClassPathXmlApplicationContext;
      
      public class TestDemo {
      	@Test
      	public void demo() {
      		ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
      		User user = (User) context.getBean("user");
      		System.out.println(user);
      	}
      }
    • 测试结果
      User [id=1, name=qf, age=18, address=Address [province=安徽省, city=合肥市]]

IoC注解的详细使用

  1. bean上的注解
    • @Component:针对所有Spring管理的bean都可以使用
    • @Contoller:针对web层
    • @Service:针对service层
    • @Repository:针对dao层
    • 目前和使用@Component没有区别,但是推荐在web层使用@Contoller、service层使用@Service、dao层使用@Repository,结构更加清晰,而且在新版本的spring可能会扩展新的属性
  2. 注解设置属性值
    • 属性有setter方法,需要将属性注入的注解添加在setter方法上
      private String city;
      @Value("合肥市")
      public void setCity(String city) {
      	this.city = city;
      }
    • 属性没有setter方法,需要将属性注入的注解添加在属性定义上
      @Value(value="qf")
      private String name;
    • 属性上的注解
      • 普通属性
        • @Value  
      • 对象属性
        • @Autowired:设置对象类型属性的值,按照类型注入
        • @Autowired+@Qualifier("名称"):设置对象类型属性的值,按照名称注入
        • @Resource(name="名称"):设置对象类型属性的值,相当于@Autowired+@Qualifier("名称")
        • @Autowired和@Qualifier是spring框架的注解(org.springframework.beans.factory.annotation.*),@Resource不是spring的注解(javax.annotation.Resource)
  3. bean生命周期的注解
    • @PostConstruct:在bean方法上配置,相当于bean标签的init-method属性(javax.annotation.PostConstruct)
    • @PreDestroy:在bean方法上配置,相当于bean标签的destroy-method属性(javax.annotation.PreDestroy) 
  4. bean作用范围的注解
    • @Scope("可选值")
      • singleton
      • prototype
      • request
      • session
      • globalSession 

XML和注解结合使用

使用XML管理类,使用注解控制属性注入

applicationContext.xml配置文件

  1. 配置组件扫描
    <!-- IoC注解开发,配置组件扫描 base-package:哪些包下的类使用注解开发 -->
    <context:component-scan base-package="com.qf.demo2"/>
    <bean id="user" class="com.qf.demo2.User"></bean>
  2. 不配置组件扫描
    <!-- 
    	激活那些已经在spring容器里注册过的bean,
    	让我们可以在没有配置扫描的情况下,使用属性注入的注解@Resource、@Autowired、@Qulifier、@Value 
    -->
    <context:annotation-config/>
    <bean id="user" class="com.qf.demo2.User"></bean>

    

以上是关于spring的IoC的主要内容,如果未能解决你的问题,请参考以下文章

Spring IOC源代码具体解释之整体结构

Spring之IOC原理及代码详解

Spring 框架学习——IOC思想原型及实质

Spring IOC源代码具体解释之容器依赖注入

[Spring 源解系列] 重温 IOC 设计理念

Spring IoC