一、IOC概述及作用
(一)IOC的简介,设计思想
SpringIOC:IOC是Inversion of Control的缩写,多数书籍翻译成“控制反转”。
IOC这个概念简单来说就是把复杂系统分解成相互合作的对象,这些对象类通过封装以后,内部实现对外部透明的,从而降低了解决问题的复杂度,而且可以灵活的被重用和扩展。
IOC理论提出的观点大体是这样的:借助于第三方实现具有依赖关系的对象之间的解耦。
编辑
由于引进了中间位置“第三方”,也就是IOC容器,使得A、B、C、D这四个对象没有了耦合关系,齿轮之间的传动全部依靠IOC容器,全部对象的控制权上交给IOC容器,所以IOC容器成了整个系统的关键核心,它起到一个“粘合剂”的作用,把系统中所有对象粘合在一起发挥作用。
编辑
当把图中的IOC容器拿掉,可以发现A、B、C、D这四个 对象之间没有耦合关系。这样的话,当我们去实现A的时候,根本无需去考虑其他几个对象,对象之间的依赖关系已经讲到了最低程度。
(二)IOC作用
IOc本质上就是一个大工程,大容器。主要作用就是创建和管理对象的依赖关系,削减计算机程序的耦合(解除我们代码间的依赖关系),提高程序的可扩展性和可维护性。
二、SpringIOC入门案例前期准备
(一)构建maven工程,添加Spring框架的依赖
<properties> <spring.version>5.2.5.RELEASE</spring.version> </properties> <!--导入spring的context坐标,context依赖core、beans、expression aop--> <dependencies> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-context</artifactId> <version>${spring.version}</version> </dependency> </dependencies>
(二)创建持久层接口和实现类
public interface UserDao { public void save(); } public class UserDaoImpl implements UserDao { @Override public void save() { System.out.println("userDao save method running...... "); } }
(三)创建业务层接口和实现类
public interface UserService { public void saveService(); } public class UserServiceImpl implements UserService { @Override public void saveService() { System.out.println("userSerivce save method running......"); } }
(四)在resources文件夹中创建一个任意名称的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/springbeans.xsd"> </beans>
(五)让Spring管理资源,在配置文件中配置service和dao
<?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"> <!--配置userDaoImpl--> <bean id="userDao" class="com.ujiuye.dao.impl.UserDaoImpl"></bean> <!--配置userServiceImpl--> <bean id="userService" class="com.ujiuye.service.UserServiceImpl"></bean> </beans>
(六)测试IOC配置是否成功
@Test public void test1(){ //1:获得spring IOC容器对象: ApplicationContext applicationContext = new ClassPathXmlApplicationContext("applicationContext.xml"); //2:从容器当中根据id获得UserDaoImpl 对象,执行userDao对象的save方法 UserDao userDao = (UserDao) applicationContext.getBean("userDao"); userDao.save(); //3:从容器当中根据id获得UserServiceImpl 对象,执行userService对象的saveService 方法 UserService userService = (UserService)applicationContext.getBean("userService"); userService.saveService(); }
运行结果:
编辑
三、Spring基于XML的IOC细节
(一)IOC配置文件详解:配置标签书写规范,配置标签的属性
bean标签:用于配置对象交给Spring来创建
默认情况下他会调用类中无参数的构造器,如果没有无参数构造器则不能创建成功。
基本属性:
id:Bean实例对象在Spring容器中的唯一标识
class:Bean的全限定类名
(二)SpringIOC机制源码解析
1、IOC容器解析
IOC思想基于IOC容器完成,IOC容器底层就是对象工厂,Spring中工厂的类结构图如下;
编辑
(1)BeanFactory
IOC容器的基本实现,是Spring内部使用的接口,不提供开发人员使用,加载配置文件时,不会创建对象,在获得(使用)对象时才采取创建对象。
(2)HierarchicalBeanFactory
这个工厂接口非常简单,实现了Bean工厂的分层。工厂接口也是继承自BeanFactory,也是一个二级接口,相对于父接口,他只是扩展了一个重要的功能———工厂分层
(3)AutowireCapableBeanFactory
该接口有自动配置能力,需要注意的是,ApplicationContext接口并实现此接口,因为应用代码很少用到此功能,如果确实需要的话,可以调用ApplicationContext getAutowireCapableBeanFactory方法,来获取此接口的实例。
(4)ListableBeanFactory
获取bean时,Spring鼓励使用这个接口定义的api,如查看bean的个数、获取某一类型Bean的配置名、查看容器中是否包括某一Bean等方法。
(5)ApplicationContext
BeanFactory接口的子接口,提供更多强大的功能,一般由开发人员使用。接口提供了bean基础性操作同时,扩展了国际化等功能。ApplicationContext接口在加载配置文件时候就会配置文件当中的对象进行创建,存放IOC容器当中
(6)AnnotationConfigApplicationContext
当使用注解配置容器对象时,需要使用此类来创建spring容器。他用来读取注解。
(7)ClassPathXmlApplicationContext
它是从类的根路径下加载配置文件
(8)FileSystemXmlApplicationContext
它是从磁盘路径上加载配置文件,配置文件可以在磁盘的任意位置。
2、IOC容器底层bean初始化过程
编辑
(1)BeanFactionPostProcessor
作用:定义了在bean工厂对象创建后,bean对象创建前执行的动作,用于对工厂进行创建后业务处理
运行时机:操作用于对工厂进行处理,仅运行一次
(2) BeanPostProcessor
作用:定义了所有bean初始化前后进行的统一动作,用于对bean进行创建前业务处理与创建后业务处理
运行时机:当前操作伴随着每个bean的创建过程,每次创建bean均运行该操作。
(3)InitializingBean
作用:定义了每个bean的初始化前进行的动作,属于非统一性动作,用于对bean进行创建前业务处理。
运行时机:当前操作伴随着任意一个bean的创建过程,保障其个性化业务处理
四、手动实现自己的IOC容器
(一)分析IOC实现思路
编辑
(二)IOC原理实现—环境搭建:构建maven工程,引入依赖
<properties>
<spring.version>5.2.5.RELEASE</spring.version>
</properties>
<dependencies>
<!--导入spring的context坐标-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>${spring.version}</version>
</dependency>
<!--导入junit单元测试-->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
<scope>test</scope>
</dependency>
</dependencies>
(三)设计接口和类以及编写配置文件
//设定UserDao接口
public interface UserDao {
public void save();
}
//设定UserDao接口实现类
public class UserDaoImpl implements UserDao {
@Override
public void save() {
System.out.println("userDao save method running...... ");
}
}
配置文件:
<!--配置userDaoImpl-->
<bean id="userDao" class="com.ujiuye.dao.impl.UserDaoImpl"></bean>
(四)使用xml技术解析配置文件
<!--引入dom4J-->
<dependency>
<groupId>dom4j</groupId>
<artifactId>dom4j</artifactId>
<version>1.6.1</version>
</dependency>
/**
* 创建自己的工厂类
*/
public class MyBeanFactory {
//创建一个map集合,模拟IOC容器
private static Map<String,Object> map = new HashMap<>();
static{
try {
//使用dom4J 解析xml文件:
//第一步:获得一个解析器:
SAXReader reader = new SAXReader();
//第二: 读取外部的配置文件:
String path = "src/main/resources/applicationContext.xml";
//第三: 读取了整个文档对象
Document document = reader.read(path);
//第四: 获得根节点:
Element rootElement = document.getRootElement();//beans
//第五: 获得根节点下的所有的bean 标签对应的节点:
List<Element> bean = rootElement.elements("bean");// bean
for (Element element : bean) {
//获得id属性对应的值:
String id1 = element.attributeValue("id");
//获得class属性对应的值:【全限定类名】
String aClass = element.attributeValue("class");//获得class对应的值: 全限定类名。
//通过反射创建对象:
Class clz = Class.forName(aClass);
Object object = clz.newInstance();
//存容器 id做key,创建出来的对象value
map.put(id1,object);
}
} catch (Exception e) {
e.printStackTrace();
}
}
/**
* 根据id从容器当中获得对象
* @param id id的名称
* @return 返回Object类型对象
*/
public static Object getBean(String id){
Object o = map.get(id);
return o;
}
}
(五)编写测试文件,展示测试结果
@Test
public void testMyFactory(){
//1:创建工厂对象
MyBeanFactory factory =new MyBeanFactory();
//2:从容器当中根据id获得对象
UserDao userDao = (UserDao) factory.getBean("userDao");
System.out.println(userDao);
userDao.save();
}
五、Spring管理bean细节
(一)bean实例化方式
1、构造方法的方式
(1)创建User类
public class User implements Serializable {
public User(){
System.out.println("user created...");
}
}
(2)配置Spring容器管理user类型对象
<!--配置user对象-->
<bean id="user" class="com.ujiuye.pojo.User"></bean>
(3)测试容器实例化User对象是否成功)
@Test
public void testUser(){
ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
User user = (User) context.getBean("user");
System.out.println(user);
}
2、静态工厂方式
(1)创建静态工厂ItemFactory
public class ItemFactory {
//静态方法返回实例bean
public static User createUser(){
System.out.println("static method running create bean ......");
return new User();
}
}
(2)配置Spring容器管理User类型对象
<!--静态工厂实例化对象-->
<bean id="user" class="com.ujiuye.factory.ItemFactory"></bean>
(3)测试容器实例化User对象是否成功
@Test
public void testItemFactory(){
ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
User user = (User) context.getBean("user");
System.out.println(user);
}
3、实例工厂方式
(1)创建实例工厂NewItemFactory
public class NewItemFactory {
//工厂的非静态方法返回bean实例
public User createUser(){
System.out.println("Dynamic method running create bean ......");
return new User();
}
}
(2)配置Spring容器管理NewItemFactory类型对象
<!--实例工厂-->
<bean id="itemFactory" class="com.ujiuye.factory.NewItemFactory"></bean>
<bean id="user" factory-bean="itemFactory" factory-method="createUser"></bean>
(3)测试容器实例化User对象是否成功
@Test
public void testNewItemFactory(){
ApplicationContext context =
new ClassPathXmlApplicationContext("applicationContext.xml");
User user = (User) context.getBean("user");
System.out.println(user);
}
(二)bean作用域
1、bean作用域介绍
所谓Bean的作用域其实就是指Spring给我们创建出的对象的存活范围,在配置文件中通过bean的scope属性指定
scope:指对象的作用范围,取值如下:
取值范围 | 说明 |
---|---|
singleton | 默认值,单例的 |
prototype | 多例的 |
request | WEB 项目中,Spring 创建一个 Bean 的对象,将对象存入到 request 域中 |
session | WEB 项目中,Spring 创建一个 Bean 的对象,将对象存入到 session 域中 |
global session | WEB 项目中,应用在 Portlet 环境,如果没有 Portlet 环境那么globalSession 相当于 session |
2、bean作用域的解析
(1)当**scope的取值为singleton时**
Bean的实例化个数:1个
Bean的实例化时机:当Spring核心文件被加载时,实例化配置的Bean实例
(2)当**scope的取值为prototype时**
Bean的实例化个数:多个
Bean的实例化时机:当调用getBean()方法时实例化Bean
(3)当**Scope的取值为其他值**
scope指定为其他值,需要在特定的环境下使用。