【Spring】详解IOC容器(DI)原理和Bean的配置(中)

笔记大纲
  • 集合属性(List&Map)

    • List、数组和Set、 Map
  • FactoryBean(工厂Bean)

    • 普通bean&工厂bean的区别
    • 练习Demo1
  • bean的作用域(4种)

    • 四大作用域简介(singleton、prototype)
    • 练习Demo2
  • bean的生命周期

    • 容器对bean的生命周期进行管理的过程(5个)
    • bean的后置处理器(2个)

1.集合属性(List&Map)

  集合是Java中提供的一种容器,可以用来存储多个数据,Collection 、Map层次结构中的根接口。

  在Spring中可通过一组内置的XML标签来配置集合,比如<list><set><map>等。

1.1.List、数组和Set

  配置java.util.List类型的属性,需指定<list>标签,通过<value>指定简单的常量值,通过<ref>指定其他的Bean,通过<bean>指定内置bean定义。数组、java.util.Array的定义与List一样,都使用<list>,同样可以使用`标签。

创建员工类、部门类:

指定当前部门下的员工,员工类型为List,即部门与员工是一对多的关系!

package com.codinglin.beans;
import java.util.List;
//部门类
public class Department {
	private Integer depId; //部门编号
	private String depName; //部门名称
    private List<Employee> employees;//员工
	public Integer getDepId() {
		return depId;
	}
	public void setDepId(Integer depId) {
		this.depId = depId;
	}
	public String getDepName() {
		return depName;
	}
	public void setDepName(String depName) {
		this.depName = depName;
	}
	public List<Employee> getEmployees() {
		return employees;
	}
	public void setEmployees(List<Employee> employees) {
		this.employees = employees;
	}
	@Override
	public String toString() {
		return "Department [depId=" + depId + ", depName=" + depName + "]";
	}
}

package com.codinglin.beans;
//员工类
public class Employee {
	private Integer empId;// 编号
	private String name;// 姓名
	private Integer gender; // 性别 0表示女,1表示男
	private Double salary; // 薪水
	public Employee() {
	}
	public Employee(Integer empId, String name, Integer gender) {

		this.empId = empId;
		this.name = name;
		this.gender = gender;
	}
	public Employee(Integer empId, String name, Double salary) {

		this.empId = empId;
		this.name = name;
		this.salary = salary;
	}
	public Integer getEmpId() {
		return empId;
	}
	public void setEmpId(Integer empId) {
		this.empId = empId;
	}
	public String getName() {
		return name;
	}
	public void setName(String name) {
		this.name = name;
	}
	public Integer getGender() {
		return gender;
	}
	public void setGender(Integer gender) {
		this.gender = gender;
	}
	public Double getSalary() {
		return salary;
	}
	public void setSalary(Double salary) {
		this.salary = salary;
	}
	@Override
	public String toString() {
		return "Employee [empId=" + empId + ", name=" + name + ", gender=" + gender + ", salary=" + salary + "]";
	}
}

配置spring-ioc.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">
	<!-- 集合属性List -->
	<bean id="deptList01" class="com.codinglin.beans.Department">
		<property name="depId" value="5201"></property>
		<property name="depName" value="互联网开发部"></property>
		<property name="employees" >
		<!--数组和set的使用方式和list类似    <array></array> <set></set>-->
			<list>
			<!-- <ref>指定对其它Bean的作用,通过<bean>指定内置bean的定义 -->
				<ref bean="employee01"></ref>
				<ref bean="employee02"></ref>
				<ref bean="employee03"></ref>
			</list>
		</property>
	</bean>
	<bean id="employee01" class="com.codinglin.beans.Employee">
		<property name="empId" value="20170001"></property>
		<property name="name" value="林大侠"></property>
		<property name="gender" value="1"></property>
		<property name="salary" value="20000"></property>
	</bean>
		<bean id="employee02" class="com.codinglin.beans.Employee">
		<property name="empId" value="20170002"></property>
		<property name="name" value="神奇女侠"></property>
		<property name="gender" value="0"></property>
		<property name="salary" value="21000"></property>
	</bean>
		<bean id="employee03" class="com.codinglin.beans.Employee">
		<property name="empId" value="20170003"></property>
		<property name="name" value="钢铁侠"></property>
		<property name="gender" value="1"></property>
		<property name="salary" value="22000"></property>
	</bean>
</beans>

测试类:

package com.codinglin.test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

import com.codinglin.beans.Department;
import com.codinglin.beans.Employee;
public class Test {
	@SuppressWarnings("resource")
	public static void main(String[] args) {
		// 创建容器对象
		ApplicationContext applicationContext = new ClassPathXmlApplicationContext("spring-ioc.xml");
		Department daptList = applicationContext.getBean("deptList01",Department.class);
		System.out.println(daptList+","+daptList.getEmployees());
	}
}

后台打印结果:

Department [depId=5201, depName=互联网开发部],[Employee [empId=20170001, name=林大侠, gender=1, salary=20000.0], Employee [empId=20170002, name=神奇女侠, gender=0, salary=21000.0], Employee [empId=20170003, name=钢铁侠, gender=1, salary=22000.0]]
1.2.Map属性

  Java.util.Map通过<map>标签来定义,标签里可以使用多个作为子标签,每一个条目都是一个键值对。

  注意:必须要在标签里定义键!

将1.1.中Department类中部分属性、方法进行修改!

private List employees;//员工

public List getEmployees() {
return employees;
}
public void setEmployees(List employees) {
this.employees = employees;
}

在Department类添加Map类属性:

    private Map<String, Employee> employeeMap;  //员工Map集合属性
	public Map<String, Employee> getEmployeeMap() {
		return employeeMap;
	}
	public void setEmployeeMap(Map<String, Employee> employeeMap) {
		this.employeeMap = employeeMap;
	}

配置spring-ioc.xml文件:

<!-- 集合Map属性配置 -->
	<bean id="deptMap01" class="com.codinglin.beans.Department">
		<property name="depId" value="5202"></property>
		<property name="depName" value="互联网测试部"></property>
		<property name="employeeMap" >
			<map>
			<entry key="Jod5" value-ref="employee01"></entry>
			<entry key="Jod3" value-ref="employee02"></entry>
			</map>
		</property>
	</bean>
		<bean id="employee01" class="com.codinglin.beans.Employee">
		<property name="empId" value="20170001"></property>
		<property name="name" value="林大侠"></property>
		<property name="gender" value="1"></property>
		<property name="salary" value="20000"></property>
	</bean>
		<bean id="employee02" class="com.codinglin.beans.Employee">
		<property name="empId" value="20170002"></property>
		<property name="name" value="神奇女侠"></property>
		<property name="gender" value="0"></property>
		<property name="salary" value="21000"></property>
	</bean>

后台打印结果:

Department [depId=5202, depName=互联网测试部],{Jod5=Employee [empId=20170001, name=林大侠, gender=1, salary=20000.0], Jod3=Employee [empId=20170002, name=神奇女侠, gender=0, salary=21000.0]}

1.3.集合类型的Bean

  上面1.1、1.2的xml文件配置时我们发现,集合对象只能配置在某个bean内部,集合配置不能重用,如果我们将集合bean的配置拿到外面来,给其它bean引用,那么我们就需要在配置集合类型的bean需要引入util名称空间!

在这里插入图片描述

<?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: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-4.0.xsd">
    
<!-- 集合Map属性配置 -->
	<bean id="deptMap01" class="com.codinglin.beans.Department">
		<property name="depId" value="5202"></property>
		<property name="depName" value="互联网测试部"></property>
		<property name="employeeMap" ref="empMap" >
		<!--数组和set的使用方式和list类似    <array></array> <set></set>-->
			<!-- 
			<map>
			<entry key="Jod5" value-ref="employee01"></entry>
			<entry key="Jod3" value-ref="employee02"></entry>
			</map>
			 -->
		</property>
	</bean>

		<util:map id="empMap">
			<entry key="Jod5" value-ref="employee01"></entry>
			<entry key="Jod3" value-ref="employee02"></entry>
		</util:map>

		<bean id="employee01" class="com.codinglin.beans.Employee">
		<property name="empId" value="20170001"></property>
		<property name="name" value="林大侠"></property>
		<property name="gender" value="1"></property>
		<property name="salary" value="20000"></property>
	</bean>
		<bean id="employee02" class="com.codinglin.beans.Employee">
		<property name="empId" value="20170002"></property>
		<property name="name" value="神奇女侠"></property>
		<property name="gender" value="0"></property>
		<property name="salary" value="21000"></property>
	</bean> 
</beans>
2.FactoryBean(工厂Bean)

  Spring中bean有两种类型,一种是普通的bean、一种是工厂bean(FactoryBean)。

2.1.普通bean&工厂bean的区别

  工厂Bean返回的对象不是指定类的一个示例,返回的是该工厂bean的getObject()方法所返回的对象,使我们程序员参与到bean对象的创建过程中。

  工厂Bean必须要实现org.springframework.beans.factory.FactoryBean接口!!!

在这里插入图片描述

2.2.练习Demo1

创建普通bean–BlogInfo类:

package com.codinglin.beans;
public class BlogInfo {
	private String blogName;  //博客名
	private String blogUrl;  //博客地址
	
	public BlogInfo() {
		System.out.println("测试---BlogInfo无参函数被调用执行");
	}
	public String getBlogName() {
		return blogName;
	}
	public void setBlogName(String blogName) {
		this.blogName = blogName;
	}
	public String getBlogUrl() {
		return blogUrl;
	}
	public void setBlogUrl(String blogUrl) {
		this.blogUrl = blogUrl;
	}
	@Override
	public String toString() {
		return "BlogInfo [blogName=" + blogName + ", blogUrl=" + blogUrl + "]";
	}
}

创建工厂bean–MyFactoryBean类:

package com.codinglin.beans;
import org.springframework.beans.factory.FactoryBean;
/*
 * 工厂Bean
 * Spring允许我们(开发者)参与到Bean对象的创建过程中,创建完对象之后,将对象交给Spring的容器进行管理
 */
public class MyFactoryBean implements FactoryBean<BlogInfo> {
	

	public MyFactoryBean() {
		System.out.println("MyFactoryBean类被调用执行");
		
	}    
	/*
	 * 用于创建Bean的对象并将其返回给IOC容器
	 */
	@Override
	public BlogInfo getObject() throws Exception {
		BlogInfo blogInfo = new BlogInfo();
		blogInfo.setBlogName("林大侠Coding日常");
		blogInfo.setBlogUrl("https://coding0110lin.blog.csdn.net/");
		return blogInfo;
	}
	/*
	 * 返回自己创建bean对象的类型
	 */
	@Override
	public Class<?> getObjectType() {
		return BlogInfo.class;
	}
	/*
	 * 创建的对象是否是单例
	 */
	@Override
	public boolean isSingleton() {
		return false;
	}
}

配置spring-ioc.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的对象,而是返回工厂bean的getObject()返回的对象
	  id="blogInfo"表示真正返回的是数据bean的对象 (BlogInfo类)
	 -->
	<bean id="blogInfo" class="com.codinglin.beans.MyFactoryBean">
	</bean>

</beans>

测试类:

package com.codinglin.test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import com.codinglin.beans.BlogInfo;
public class MainTest {
	public static void main(String[] args) {
		//创建容器对象
		ApplicationContext applicationContext = new ClassPathXmlApplicationContext("spring-ioc.xml");
		BlogInfo blogInfo = applicationContext.getBean("blogInfo",BlogInfo.class);
		System.out.println(blogInfo);
	}
}

后台打印结果:

在这里插入图片描述

3.bean的作用域(scope)

  在Spring中,在元素的scope属性里设置bean的作用域,以此决定这个bean是单实例和多实例!默认情况下,Spring只为每个在IOC容器中声明的bean创建唯一的实例中,整个IOC容器范围内都能共享该实例:所有后续的getBean()调用和bean引用都返回这个唯一的bean实例,该作用域就被称为singleton,它也是默认的作用域!

3.1.四大作用域简介
作用域类型说明 (创建时机)
singleton单例的(默认取值)对象在容器初始化的时候被创建,且仅会被创建一次,所以通过getBean从容器中获取对象的时候,获取是同一个!
prototype多例的(原型)对象不会在容器初始化的时候被创建,每次在调用getBean方法从容器中获取对象的时候创建对象,所以每次在获取的对象不是同一个!
request在一次请求范围有效,每次HTTP请求都会创建一个新的bean对象,适合于WebApplication环境。
session在一次会话范围有效,一次会话可以包含多个请求。同一个HTTP session 共享一个Bean,不同HTTP session 使用不同的Bean,适合于WebApplication环境。
3.2.练习Demo2

创建BlogInfo类:

package com.codinglin.beans;
public class BlogInfo {
	private String blogName;  //博客名
	private String blogUrl;//博客地址
	public BlogInfo() {
		System.out.println("测试---BlogInfo无参函数被调用执行");
	}
	public String getBlogName() {
		return blogName;
	}
	public void setBlogName(String blogName) {
		this.blogName = blogName;
	}
	public String getBlogUrl() {
		return blogUrl;
	}
	public void setBlogUrl(String blogUrl) {
		this.blogUrl = blogUrl;
	}
	@Override
	public String toString() {
		return "BlogInfo [blogName=" + blogName + ", blogUrl=" + blogUrl + "]";
	}
}

【1】singleton作用域下配置spring-scope.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="blogInfoTest" class="com.codinglin.beans.BlogInfo" scope="singleton">
        
		<property name="blogName" value="林大侠Coding日常" ></property>
		<property name="blogUrl" value="https://coding0110lin.blog.csdn.net/"></property>
    </bean>
</beans>

测试类:

package com.codinglin.test;

import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

import com.codinglin.beans.BlogInfo;

public class MainTest {

	public static void main(String[] args) {
		//创建容器对象
		ApplicationContext applicationContext = new ClassPathXmlApplicationContext("spring-scope.xml");
		BlogInfo blogInfo1 = applicationContext.getBean("blogInfoTest",BlogInfo.class);
		System.out.println("=======测试blogInfo1实例======:"+blogInfo1);
		BlogInfo blogInfo2 = applicationContext.getBean("blogInfoTest",BlogInfo.class);
		System.out.println("=======测试blogInfo2实例======:"+blogInfo2);
		//后续的getBean()调用和bean引用都返回这个唯一的bean实例--singleton作用域
		System.out.print("=======singleton作用域下比较两个实例======:");
		System.out.print(blogInfo1==blogInfo2);
	}
}

后台打印结果:

在这里插入图片描述
【2】prototype作用域下仅配置spring-scope.xml文件:

<bean id="blogInfoTest" class="com.codinglin.beans.BlogInfo" scope="prototype">
        
		<property name="blogName" value="林大侠Coding日常" ></property>
		<property name="blogUrl" value="https://coding0110lin.blog.csdn.net/"></property>
</bean>		

后台打印结果:

在这里插入图片描述

4.bean的生命周期

Spring IOC容器可以管理bean的生命周期,即在bean生命周期内特定的时间执行指定的任务。

4.1.容器对bean的生命周期进行管理的过程
阶段说明
阶段一通过构造器或工厂方法创建bean实例
阶段二通过set()方法为bean的属性赋值以及对其它的bean的引用
阶段三调用bean的初始化方法 (需要手动指定)
阶段四使用bean
阶段五关闭容器时,调用bean的销毁方法 (需要手动指定)

  如有bean后置处理器,则是7个阶段,注意:

将bean实例传递给bean后置处理器的postProcessBeforeInitialization()方法
调用bean的初始化方法
将bean实例传递给bean后置处理器的postProcessAfterInitialization()方法

4.2.练习Demo3

创建BlogInfo类:

package com.codinglin.beans;
public class BlogInfo {
	private String blogName;  //博客名
	private String blogUrl;//博客地址
	public BlogInfo() {
		System.out.println("测试1===Bean生命周期阶段①【通过构造方法创建bean对象】===");
	}
	public String getBlogName() {
		return blogName;
	}
	public void setBlogName(String blogName) {
		System.out.println("测试2===Bean生命周期阶段②【给对象的属性进行赋值操作】===");
		this.blogName = blogName;
	}
	public String getBlogUrl() {
		return blogUrl;
	}
	public void setBlogUrl(String blogUrl) {
		this.blogUrl = blogUrl;
	}
	@Override
	public String toString() {
		return "BlogInfo [blogName=" + blogName + ", blogUrl=" + blogUrl + "]";
	}
	/**
	 * 初始化操作
	 */
	public void init() {
		System.out.println("测试3===Bean生命周期阶段③【调用bean的初始化方法】===");
	}
	/**
	 * 销毁操作
	 */
	public void destroy() {
		System.out.println("测试5===Bean生命周期阶段⑤【调用bean的销毁方法】===");
	}
}

配置spring-lifecycle.xml文件:

  正常配置bean时需要指定init-method初始化方法和destroy-method销毁的方法!

<?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="blogInfoTest" class="com.codinglin.beans.BlogInfo" 
    init-method="init" destroy-method="destroy">
        
		<property name="blogName" value="林大侠Coding日常" ></property>
		<!-- <property name="blogUrl" value="<<https://coding0110lin.blog.csdn.net/">>></property> 报错!!-->
		<property name="blogUrl" >
			<value><![CDATA[<<https://coding0110lin.blog.csdn.net>>]]></value>
		</property>
        
    </bean>
</beans>

测试类:

public class MainTest {
	public static void main(String[] args) {
		//创建容器对象
		/*ApplicationContext applicationContext = new ClassPathXmlApplicationContext("spring-lifecycle.xml");*/
	    //注意!!!!使用ApplicationContext的子接口
		ConfigurableApplicationContext configurableApplicationContext = new ClassPathXmlApplicationContext("spring-lifecycle.xml");
		BlogInfo blogInfo = configurableApplicationContext.getBean("blogInfoTest",BlogInfo.class);
		System.out.println("测试4===Bean生命周期阶段④【使用bean对象】===");
		/*System.out.println(blogInfo);*/
		configurableApplicationContext.close();
	}
}

后台打印结果:

在这里插入图片描述

4.3.bean的后置处理器

  bean后置处理器允许在调用初始化方法前后对bean进行额外的处理;

  bean后置处理器对IOC容器里所有bean实例都会逐一处理,非单一实例;

  bean后置处理器需要实现接口:
  org.springframwork.beans.factory.config.BeanPostProcessor;

  在初始化方法调用前后,Spring将把每一个bean实例分别传递给上面接口的方法:

   postProcessBeforeInitalization(Object,String)

   postProcessAftereInitalization(Object,String)

练习Demo

在初始化方法执行前,将博客地址中的<<>>替换为(())全部去掉

新建MyBeanPostProcessor类

package com.codinglin.beans;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanPostProcessor;
public class MyBeanPostProcessor implements BeanPostProcessor{
   /*
    * 在初始化方法执行之前执行,因为后置处理器对IOC容器里所有的bean实例逐一处理,所以参数为Object,不能使用泛型设计
    * Object bean:表示当前正在被创建的bean对象
    * String beanName :表示当前正在被创建的bean对象的名字,也就是配置文件中bean的id
    *
    */
	@Override
	public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
		System.out.println(" ========测试postProcessBeforeInitialization(之前)=======:"+bean+","+beanName);
		BlogInfo bInfo=(BlogInfo)bean;
		bInfo.setBlogUrl(bInfo.getBlogUrl().replaceAll("<", "{").replaceAll(">", "}"));
		return bean;
	}
	@Override
	public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
		System.out.println(" ========测试postProcessAfterInitialization(之后)=======:"+bean+","+beanName);
	
		return bean;
	}
}

配置spring-lifecycle.xml文件添加:

 <bean class="com.codinglin.beans.MyBeanPostProcessor"></bean>

后台打印结果:

在这里插入图片描述


 ☝上述分享来源个人总结,如果分享对您有帮忙,希望您积极转载;如果您有不同的见解,希望您积极留言,让我们一起探讨,您的鼓励将是我前进道路上一份助力,非常感谢!我会不定时更新相关技术动态,同时我也会不断完善自己,提升技术,希望与君同成长同进步!

☞本人博客:https://coding0110lin.blog.csdn.net/  欢迎转载,一起技术交流吧!

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值