在前面博客中介绍什么是依赖注入时有提到:依赖注入是组件之间依赖关系由容器在运行期决定,即由容器动态的将某个依赖关系注入到组件之中。那什么是容器?既然Spring框架实现了IOC,那Spring中的容器是什么呢?
 一、容器介绍
在日常生活中容器是指用以容纳物料并以壳体为主的基本装置,它是用来盛放东西的。在编程中容器是用来存储和组织其他对象的对象,首先要确定容器也是对象,也可以当做bean,只是这个对象是用来存储和组织其他对象,那其他对象是什么呢?其他对象其实就是bean对象,这也是面向对象编程的一种体现,万物皆对象。在Spring提供了BeanFactory、ApplicationContext两个IOC容器,来管理众多的bean对象。
二、BeanFactory
一提到工厂我们生活当中可能会想到富某康,工厂是一类用以生产货物的大型工业建筑物。而BeanFactory不是用来生产货物的而是用来生产管理bean的。BeanFactory会在bean的生命周期的各个阶段中对bean进行各种管理,并且Spring将这些阶段通过各种接口暴露给我们,让我们可以对bean进行各种处理,我们只要让bean实现对应的接口,那么Spring就会在bean的生命周期调用我们实现的接口来处理该bean。那它是怎么实现的呢?它主要分两个阶段。
1)、Bean容器的启动
工厂要生产货物那首先得把工厂运转起来之后才能生产货物。同样bean容器要管理bean也需要先把容器启动起来,获取到bean的定义信息之后才能管理。
1. 读取bean的xml配置文件,然后将xml中每个bean元素分别转换成BeanDefinition对象。

public abstract class AbstractBeanDefinition extends BeanMetadataAttributeAccessor
implements BeanDefinition, Cloneable {
private volatile Object beanClass;
private String scope = SCOPE_DEFAULT;
private boolean abstractFlag = false;
private boolean lazyInit = false;
private int autowireMode = AUTOWIRE_NO;
private int dependencyCheck = DEPENDENCY_CHECK_NONE;
private String[] dependsOn;
private ConstructorArgumentValues constructorArgumentValues;
private MutablePropertyValues propertyValues;
private String factoryBeanName;
private String factoryMethodName;
private String initMethodName;
private String destroyMethodName;

BeanClass保存bean的class属性,scop保存Bean的作用域,abstractFlag保存该bean是否抽象,lazyInit保存是否延迟初始化,autowireMode保存是否自动装配,dependencyCheck保存是否坚持依赖,dependsOn保存该bean依赖于哪些bean(这些bean必须提取初始化),constructorArgumentValues保存通过构造函数注入的依赖,propertyValues保存通过setter方法注入的依赖,factoryBeanName和factoryMethodName用于factorybean,也就是工厂类型的bean,initMethodName和destroyMethodName分别对应bean的init-method和destory-method属性。后面会对这些内容进行详细介绍。

2. 通过BeanDefinitionRegistry将bean注册到beanFactory中

上面获取到bean的信息之后,是怎么注册到BeanFactory中的呢?其实是通过BeanDefinitionRegistry将bean注册到beanFactory中。因为BeanFactory的实现类,需要实现BeanDefinitionRegistry 接口。

public interface BeanDefinitionRegistry extends AliasRegistry {
void registerBeanDefinition(String beanName, BeanDefinition beanDefinition)throws BeanDefinitionStoreException;
void removeBeanDefinition(String beanName) throws NoSuchBeanDefinitionException;
BeanDefinition getBeanDefinition(String beanName) throws NoSuchBeanDefinitionException;
boolean containsBeanDefinition(String beanName);
String[] getBeanDefinitionNames();
int getBeanDefinitionCount();
boolean isBeanNameInUse(String beanName);
}

BeanDefinitionRegistry接口提供了根据beanName注册对应beanDefinition的方法,而在DefaultListableBeanFactory中实现了该方法,并将beanDefinition保存在了ConcurrentHashMap中。

@SuppressWarnings("serial")
public class DefaultListableBeanFactory extends AbstractAutowireCapableBeanFactory
implements ConfigurableListableBeanFactory, BeanDefinitionRegistry, Serializable {
/** Map of bean definition objects, keyed by bean name */
private final Map<String, BeanDefinition> beanDefinitionMap = new ConcurrentHashMap<String, BeanDefinition>(64); @Override
public void registerBeanDefinition(String beanName, BeanDefinition beanDefinition)
throws BeanDefinitionStoreException {
// ... ...
this.beanDefinitionMap.put(beanName, beanDefinition);
}

另外Spring还对外暴露了一些接口用来对bean初始化,例如BeanFactoryPostProcessor。

public interface BeanFactoryPostProcessor {
/**
* Modify the application context's internal bean factory after its standard
* initialization. All bean definitions will have been loaded, but no beans
* will have been instantiated yet. This allows for overriding or adding
* properties even to eager-initializing beans.
* @param beanFactory the bean factory used by the application context
* @throws org.springframework.beans.BeansException in case of errors
*/
void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException;
}

我们可以翻译一下postProcessBeanFactory的注释信息,postProcessBeanFactory可以修改应用上下文中已经进行standard初始化的beanFactory,此时所有bean的定义信息已经加载完成,但还未实例化,允许覆盖、新增甚至重新初始化bean信息,一个典型的例子就是属性覆盖器PropertyOverrideConfigurer。对于一些参数可以配置在properties中,而不用配置在Spring的XML配置文件中。

2)、容器Bean的实例化

上面把bean容器启动之后,工厂算是运转起来了,配方(beanDefinition)也已经准备充分,然后就是生产(实例化)、管理货物(bean)了。实例化bean主要通过反射和CGLIB两种方式,在bean的实例化过程中,Spring也暴露了一些接口。
BeanNameAware 获取该bean在配置文件中对应的id
BeanFactoryAware 获取实例化该bean的BeanFactory
InitializingBean bean实例化、所有属性设置后调用初始化方法
DisposableBean 在bean丢弃的时候调用销毁方法
我们可以通过示例演示一下这几个接口的使用。
1. 首先创建了Maven project,pom.xml引入spring-core、spring-context。

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion> <groupId>com.demo</groupId>
<artifactId>BeanFactoryDemo</artifactId>
<version>0.0.1-SNAPSHOT</version>
<packaging>jar</packaging> <name>BeanFactoryDemo</name>
<url>http://maven.apache.org</url>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<spring.version>5.0.0.RELEASE</spring.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-core</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>3.8.1</version>
<scope>test</scope>
</dependency>
</dependencies>
</project>

2. 创建bean对象,实现上面列出的接口

package com.demo.model;

import org.springframework.beans.BeansException;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.BeanFactoryAware;
import org.springframework.beans.factory.BeanNameAware;
import org.springframework.beans.factory.DisposableBean;
import org.springframework.beans.factory.InitializingBean; public class UserBean implements BeanNameAware,BeanFactoryAware,InitializingBean,DisposableBean { public void setBeanName(String name) {
System.out.println(name);
} public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
System.out.println(beanFactory);
} public void afterPropertiesSet() throws Exception {
System.out.println("InitializingBean");
} public void destroy() throws Exception {
System.out.println("DisposableBean");
}
}

3. bean配置

<?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.demo.model.UserBean"></bean>
</beans>

4. 测试

使用ApplicationContext获取BeanFactory,再通过getBean方法获取到对应的bean,最后调用destroy方法进行销毁,从输出结果可以看到依次调用了BeanNameAware,BeanFactoryAware,InitializingBean,DisposableBean接口。

    public static void main( String[] args ) throws Exception
{
ApplicationContext context=new ClassPathXmlApplicationContext(new String[]{"applicationContext.xml"});
BeanFactory factory=context;
UserBean user=(UserBean)factory.getBean("user");
user.destroy();
}

输出结果:user

org.springframework.beans.factory.support.DefaultListableBeanFactory@6bf256fa: defining beans [user]; root of factory hierarchy
InitializingBean
DisposableBean
三、ApplicationContext
在上面的示例中使用了ApplicationContext获取bean的配置,然后直接将ApplicationContext接口 对象赋值给了BeanFactory接口对象,为什么可以赋值呢?其实ApplicationContext接口实现了BeanFactory接口。

public interface ApplicationContext extends EnvironmentCapable, ListableBeanFactory, HierarchicalBeanFactory,
MessageSource, ApplicationEventPublisher, ResourcePatternResolver

从上面ApplicationContext接口的继承关系可以看到,它还通过继承其他接口扩展了BeanFactory的功能。MessageSource:为应用提供国际化访问功能。ResourceLoader:提供资源(如URL和文件系统)的访问支持。ApplicationEventPublisher:引入事件机制,包括启动事件、关闭事件等,让容器在上下文中提供了对应用事件的支持。它代表的是一个应用的上下文环境。beanFactory主要是面对与 spring 框架的基础设施,面对 spring 自己。而 Applicationcontex 主要面对与 spring 使用的开发者。基本都会使用 Applicationcontex 并非 beanFactory 。所以在上面实例使用的ApplicationContext获取BeanFactory接口对象。

上面获取ApplicationContext对象是通过ClassPathXmlApplicationContext方法获取的,还有一种获取方式,使用FileSystemXmlApplicationContext方法获取。ClassPathXmlApplicationContext : 从类路径下加载配置文件。文件路径:默认指的是项目的classpath路径下面,所以不需要写前缀classpath:。如果指向绝对路径,需要加上file:。New  ClassPathXmlApplicationContext(new String[]{"ApplicationContext.xml"});FileSystemXmlApplicationContext:从文件系统中加载配置文件。文件路径:默认指的是项目的根目录下,想使用项目的classpath路径下面,需要加上classpath:。new  FileSystemXmlApplicationContext(new String[]{"classpath:ApplicationContext.xml"});

Spring之IOC容器的更多相关文章

  1. Spring框架IOC容器和AOP解析

    主要分析点: 一.Spring开源框架的简介  二.Spring下IOC容器和DI(依赖注入Dependency injection) 三.Spring下面向切面编程(AOP)和事务管理配置  一.S ...

  2. Spring之IOC容器加载初始化的方式

    引言 我们知道IOC容器时Spring的核心,可是如果我们要依赖IOC容器对我们的Bean进行管理,那么我们就需要告诉IOC容易他需要管理哪些Bean而且这些Bean有什么要求,这些工作就是通过通过配 ...

  3. spring框架--IOC容器,依赖注入

    思考: 1. 对象创建创建能否写死? 2. 对象创建细节 对象数量 action  多个   [维护成员变量] service 一个   [不需要维护公共变量] dao     一个   [不需要维护 ...

  4. 在Servlet(或者Filter,或者Listener)中使用spring的IOC容器

    web.xml中的加载顺序为:listener > filter > servlet > spring. 其中filter的执行顺序是filter-mapping在web.xml中出 ...

  5. Spring的IoC容器

    Spring是一个轻量级的Java开发框架,其提供的两大基础功能为IoC和AOP,其中IoC为依赖反转(Inversion of Control).IOC容器的基本理念就是"为别人服务&qu ...

  6. Spring的IOC容器第一辑

    一.Spring的IOC容器概述 Spring的IOC的过程也被称为依赖注入(DI),那么对象可以通过构造函数参数,工厂方法的参数或在工厂方法构造或返回的对象实例上设置的属性来定义它们的依赖关系,然后 ...

  7. SpringMVC系列(十五)Spring MVC与Spring整合时实例被创建两次的解决方案以及Spring 的 IOC 容器和 SpringMVC 的 IOC 容器的关系

    一.Spring MVC与Spring整合时实例被创建两次的解决方案 1.问题产生的原因 Spring MVC的配置文件和Spring的配置文件里面都使用了扫描注解<context:compon ...

  8. spring的Ioc容器与AOP机制

    为什么要使用Spring的Ioc容器? 1.首先,spring是一个框架,框架存在的目的就是给我们的编程提供简洁的接口,可以使得我们专注于业务的开发,模块化,代码简洁,修改方便. 通过使用spring ...

  9. Spring扩展:Spring的IoC容器(注入对象的方式和编码方式)

    二.Spring的IoC容器 IoC:Inversion of Control(控制反转) DI:Dependency Injection(依赖注入) 三.依赖注入的方式 (1)构造注入 (2)set ...

  10. spring之IOC容器创建对象

    1.术语了解 1.1组件/框架设计 侵入式设计 引入了框架,对现有的类的结构有影响:即需要实现或继承某些特定类. 例如: Struts框架非侵入式设计 引入了框架,对现有的类结构没有影响. 例如:Hi ...

随机推荐

  1. STL基础4:deque

    #include <iostream> #include <queue> #include <string> using namespace std; #defin ...

  2. Shell脚本学习-echo命令

    跟着RUNOOB网站的教程学习的笔记 shell的echo指令与PHP的echo指令类似,都是用于字符串的输出.命令格式: echo string 1 显示普通字符串 echo "It is ...

  3. 让photoshop cc 支持 webp格式

    下载WebP.8bi文件,看PS cc 是32位还是64位,找到对应的文件. brushes8.com-2017-11-03_08-29-21_654098.7z 把  WebP.8bi 复制到pho ...

  4. ROM、RAM、DRAM、SRAM和FLASH、PSRAM

    1.ROM和RAM指的都是半导体存储器,ROM是Read Only Memory的缩写,RAM是Random Access Memory的缩写.ROM在系统停止供电的时候仍然可以保持数据,而RAM通常 ...

  5. Android-Java-构造函数间调用&this内存图

    构造函数间调用: 描述Person对象: package android.java.oop08; // 描述Person对象 public class Person { public String n ...

  6. 微信小游戏 three.js jsonloader request:fail invalid url

    微信小游戏中,用 THREE.JSONLoader 直接加载本地的 json 文件,报错.估计是跨域访问的问题 解决:把 json 文件放到服务器上,通过 url 来访问. 临时测试的话,在本地起一个 ...

  7. Django(序列化、SweetAlert插件)

    day72 参考:https://www.cnblogs.com/liwenzhou/p/8718861.html#autoid-6-1-2 前端序列化 后端序列化 day73中 补充一个SweetA ...

  8. OkHttp 入门篇

    OkHttp是一个HTTP & HTTP2的客户端,能够用来进行Android 和 Java 开发. HTTP是现代应用的最基本的网络环境.让你的HTTP更加有效的工作能够让你的东西加载更快而 ...

  9. struts2框架学习笔记5:OGNL表达式

    OGNL取值范围分两部分,root.Context两部分 可以放置任何对象作为ROOT,CONTEXT中必须是Map键值对 示例: 准备工作: public void fun1() throws Ex ...

  10. 从Redis生成数据表主键标识

    对于MySql的全局ID(主键),我们一般采用自增整数列.程序生成GUID.单独的表作为ID生成器,这几种方案各有优劣,最终效率都不能说十分理想(尤其海量数据下),其实通过Redis的INCR可以很方 ...