Spring ioc
问题
- javaSE获取一个类的Class有几种方式?
- ioc的自动装载有几种方式?
- ioc的底层通过哪些原理机制实现的?
- 实现类ClassPathXmlApplicationContext的底层AbstractApplicationContext的源码核心方法是什么?
Spring的两大核心机制 IoC(控制反转)和 AOP(面向切面编程),从开发者的角度来讲,我们使用Spring框架就是用它的IoC和AOP。IoC是典型的工厂模式,通过工厂来注入对象,AOP是代理模式。
IoC是Spring框架的基石,IoC也叫控制反转,传统的开发方式中,需要调用对象时,需要手动来创建对象,即对象是由调用者主动创建出来的。
通过IoC容器来创建对象
- 搭建Spring环境
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.1.2.RELEASE</version>
</dependency>
- 创建IoC配置文件,可以自定义名称,spring.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"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:p="http://www.springframework.org/schema/p"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.2.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.3.xsd
">
<bean id="student" class="com.m.entity.Student">
<property name="id" value="1"></property>
<property name="name">
<value><![CDATA[<杨三>]]></value>
</property>
<property name="age" value="21"></property>
</bean>
<bean id="student3" class="com.southwind.entity.Student">
<constructor-arg name="id" value="2" ></constructor-arg>
<constructor-arg name="name" value="小王"></constructor-arg>
<constructor-arg name="age" value="18"></constructor-arg>
<constructor-arg index="0" value="3"></constructor-arg>
<constructor-arg index="1" value="小明"></constructor-arg>
<constructor-arg index="2" value="19"></constructor-arg>
</bean>
<!--p命名空间-->
<bean id="clazz" class="com.m.entity.Classes" p:id="1" p:name="一班" p:teacher-ref="teacher"></bean>
</beans>
- 调用API获取IoC创建好的对象
//创建Class的方式
//1
Class clazz1 = Student.class;
//2
Class clazz2 = null;
try {
clazz2 = Class.forName("com.m.ioc.entity.Student");
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
System.out.println(clazz1 == clazz2); //true
//3
Student student = new Student();
Class clazz3 = student.getClass();
System.out.println(clazz2 == clazz3); //true
//加载spring.xml配置文件
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("spring.xml");
//获取IoC中的对象
Student student = (Student) applicationContext.getBean("student");
//Student student2 = (Student) applicationContext.getBean(Student.class);
System.out.println(student);
AbstractApplicationContext是ioc-bean的核心类
这个抽象类的子类就是 ClassPathXmlApplicationContext
这个抽象类乃至ioc的核心方法就是实例方法refresh()
这个抽象类有三个抽象方法 getBeanFactory(),closeBeanFactory(),refreshBeanFactory()
obtainFreshBeanFactory()调用了refreshBeanFactory() 下图的refresh()则调用了obtainFreshBeanFactory();
注意,通过IoC容器来管理对象时,对象对应的实体类必须有无参构造函数,否则IoC无法实例化对象,因为IoC底层是通过反射机制来创建对象的,反射机制默认调用实体类的无参构造函数。
再通过调用每个属性的setter方法来完成属性赋值的,所以实体类必须有setter方法,否则无法完成属性的赋值。
IoC容器管理多个对象,并且对象之间有及联关系,如何实现?
<!-- 创建Classes对象 -->
<bean id="classes" class="com.m.entity.Classes">
<property name="id" value="1"></property>
<property name="name" value="一班"></property>
</bean>
<!-- 创建学生对象 -->
<bean id="stu" class="com.m.entity.Student">
<property name="id" value="1"></property>
<property name="name" value="小红"></property>
<property name="age" value="16"></property>
<!-- 将IoC容器中的classes对象赋给stu对象的classes -->
<property name="classes" ref="classes"></property>
</bean>
<bean id="classes" class="com.southwind.entity.Classes">
<constructor-arg name="id" value="1"></constructor-arg>
<constructor-arg name="name" value="一班"></constructor-arg>
<constructor-arg name="students">
<list>
<ref bean="student"></ref>
<ref bean="student2"></ref>
<ref bean="student3"></ref>
</list>
</constructor-arg>
</bean>
集合属性通过list标签和ref标签完成注入,ref的bean属性指向需要注入的bean对象。
Spring中bean是根据scope来生成的,scope表示bean的作用域。
scope有4种类型:
-
singleton:单例,表示通过IoC容器获取的对象是唯一的。
-
prototype:原型,表示通过IoC容器获取的对象是不同的。
-
request:请求,表示通过IoC容器获取的对象在一次HTTP请求内有效。
-
session:会话,表示通过IoC容器获取的对象在一个用户会话内有效。
request和session只适用于web项目,大多数情况下,我们只会使用singleton和prototype两种scope,并且scope的默认值是singleton。
scope=singleton时,一旦加载spring.xml配置文件就会创建bean对象,并且只创建一次,单例模式。
scope=prototype时,加载spring.xml配置文件时不会创建bean对象,每次获取对象时再来创建,获取多少次就创建多少个对象,原型模式。
Spring的继承
Spring的继承,与Java中的继承不一样,但是思想是类似的,Spring中的继承表示子bean可以继承父bean中的属性,对象层面的继承,Java中的继承表示子类可以继承父类的结构,类层面的继承。
子bean在继承父bean的属性值的基础之上,可以对属性值进行覆盖,两个不同类型的对象之间也可以实现继承,前提是两个对象的属性完全一致,User中有int id和String name,Student中有int id和String name,同样可以完成继承,读取父bean的属性值,赋给对应的子bean的属性。
Spring的依赖-depends-on="student"
与Spring的继承类似,依赖也是bean与bean之间的一种关联方式,配置依赖关系之后,被依赖的bean一定优先创建,再创建依赖的bean,A依赖于B,先创建B,再创建A。
IoC容器创建bean的顺序是由spring.xml中
Spring IoC工厂方法创建对象
IoC是典型的工厂模式,IoC通过工厂模式创建bean有两种方式:
- 静态工厂方法
public class StaticCarFactory {
private static Map<Integer, Car> carMap;
static {
carMap = new HashMap<>();
carMap.put(1,new Car(1,"奥迪"));
carMap.put(2,new Car(2,"宝马"));
}
public static Car getCar(int num){
return carMap.get(num);
}
}
<!-- 配置静态工厂创建Car对象 -->
<bean id="car" class="com.southwind.factory.StaticCarFactory" factory-method="getCar">
<constructor-arg value="2"></constructor-arg>
</bean>
- 实例工厂方法
public class InstanceCarFactory {
private Map<Integer, Car> carMap;
public InstanceCarFactory(){
carMap = new HashMap<>();
carMap.put(1,new Car(1,"奔驰"));
carMap.put(2,new Car(2,"宝马"));
}
public Car getCar(int num){
return carMap.get(num);
}
}
<!-- 配置实例工厂对象 -->
<bean id="instanceCarFactory" class="com.southwind.factory.InstanceCarFactory"></bean>
<!-- 通过实例工厂对象获取Car对象 -->
<bean id="car" factory-bean="instanceCarFactory" factory-method="getCar" >
<constructor-arg value="1"></constructor-arg>
</bean>
IoC的自动装载(autowire)
IocDI可以通过配置property的ref属性将bean进行依赖注入,同时Spring还提供了另外一种更加简便的方式:自动装载,不需要手动配置property,IoC容器会自动选择bean完成依赖注入。
自动装载有两种方式:
-
byName:通过属性名完成自动装载。
-
byType:通过属性的数据类型完成自动装载。
<bean id="testDao" class="com.m.ioc.autowire.impl.TestDaoImpl">
<property name="id" value="001"></property>
<property name="name" value="TestDaoImpl"></property>
</bean>
<bean id="service" class="com.m.ioc.autowire.TestService" autowire="byType">
<property name="id" value="1"></property>
</bean>
如果IoC容器同时存在多个数据类型相同的bean,会抛出异常。