Spring IoC之旅(一)

一、容器以及依赖注入

  • 什么是依赖注入

    新的一年到了,小明要开始购置全新的衣服,他去以纯买了裤子,耐克买了鞋子,森马买了上衣等等,为此他跑遍了整个城市的专卖店,他觉得很累很麻烦,自己过个年什么衣服都要自己跑去专卖店买。

    新的一年到了,小妮也要购置全新的衣服,她在家里跟保姆说,我要一套漂亮的新年衣服,保姆便从某个神秘的地方拿了一套新的衣服给了小妮,这样小妮就不用到处跑来跑去了。

    小明跟小妮的故事其实就是一个依赖反转的过程,小明需要的衣服需要自己从每个地方自己去买,而小妮的衣服则是由保姆给她的。对于获取衣服的方式而言,两者的方式从一个主动获取转换为被注入到过程。

    控制的反转指的是获取方式的反转。

    在Spring中,控制反转指的是原先对象需要自己去关心,创建自己依赖的对象的过程转换为由容器自动生成注入依赖的过程,而对象自己不需要再关心这个过程了。

  • 什么是容器?

    当我们去打水的时候,水桶是一个容器,这个容器能盛许多单位的水,不管你是红茶、绿茶等等,只要你是符合水的特性的,都能够往容器里面存放。
    而容器能够帮助我们,在需要喝水的时候,自动把水装好并且递给主人的过程。

  • 容器与依赖反转的好处?

    容器以及依赖注入的出现,使得开发者不需要去创建、生成以及管理bean之间的依赖关系,极大的解放了bean的责任。

二、Spring容器

同样,在Spring中,核心也是容器跟依赖注入的概念。

1、什么是IoC容器

在日常生活中,容器就是用来盛放东西的,比如水桶,水桶可以抽象为一个接口,这个接口定义了要有容量,要有挂钩的就是容器。而水桶的类型有很多种,只要实现了水桶的接口,那么就是一个水桶,也就是一个容器。

在Spring中同样也不例外,我们可以看到BeanFactory就是一个接口,而它其实就是容器的“水桶接口”,BeanFactory简单理解为一个模型,模型定义了Spring的容器的规范。

而在Spring容器中,盛放的bean的方式就是BeanDefinition,BeanDefinition抽象的描述了bean的各种属性。

2、Spring IoC容器总体设计

前面已经说到,Spring有一系列的容器,它的定义是在BeanFactory里面。
先看一下官方文档是怎么定义BeanFactory的

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
/**
* The root interface for accessing a Spring bean container.
* This is the basic client view of a bean container;
* further interfaces such as {@link ListableBeanFactory} and
* {@link org.springframework.beans.factory.config.ConfigurableBeanFactory}
* are available for specific purposes.
*
* <p>This interface is implemented by objects that hold a number of bean definitions,
* each uniquely identified by a String name. Depending on the bean definition,
* the factory will return either an independent instance of a contained object
* (the Prototype design pattern), or a single shared instance (a superior
* alternative to the Singleton design pattern, in which the instance is a
* singleton in the scope of the factory). Which type of instance will be returned
* depends on the bean factory configuration: the API is the same. Since Spring
* 2.0, further scopes are available depending on the concrete application
* context (e.g. "request" and "session" scopes in a web environment).
*
* <p>The point of this approach is that the BeanFactory is a central registry
* of application components, and centralizes configuration of application
* components (no more do individual objects need to read properties files,
* for example). See chapters 4 and 11 of "Expert One-on-One J2EE Design and
* Development" for a discussion of the benefits of this approach.
*
* <p>Note that it is generally better to rely on Dependency Injection
* ("push" configuration) to configure application objects through setters
* or constructors, rather than use any form of "pull" configuration like a
* BeanFactory lookup. Spring's Dependency Injection functionality is
* implemented using this BeanFactory interface and its subinterfaces.
*
* <p>Normally a BeanFactory will load bean definitions stored in a configuration
* source (such as an XML document), and use the {@code org.springframework.beans}
* package to configure the beans. However, an implementation could simply return
* Java objects it creates as necessary directly in Java code. There are no
* constraints on how the definitions could be stored: LDAP, RDBMS, XML,
* properties file, etc. Implementations are encouraged to support references
* amongst beans (Dependency Injection).
*
* <p>In contrast to the methods in {@link ListableBeanFactory}, all of the
* operations in this interface will also check parent factories if this is a
* {@link HierarchicalBeanFactory}. If a bean is not found in this factory instance,
* the immediate parent factory will be asked. Beans in this factory instance
* are supposed to override beans of the same name in any parent factory.
*
* <p>Bean factory implementations should support the standard bean lifecycle interfaces
* as far as possible. The full set of initialization methods and their standard order is:<br>
* 1. BeanNameAware's {@code setBeanName}<br>
* 2. BeanClassLoaderAware's {@code setBeanClassLoader}<br>
* 3. BeanFactoryAware's {@code setBeanFactory}<br>
* 4. ResourceLoaderAware's {@code setResourceLoader}
* (only applicable when running in an application context)<br>
* 5. ApplicationEventPublisherAware's {@code setApplicationEventPublisher}
* (only applicable when running in an application context)<br>
* 6. MessageSourceAware's {@code setMessageSource}
* (only applicable when running in an application context)<br>
* 7. ApplicationContextAware's {@code setApplicationContext}
* (only applicable when running in an application context)<br>
* 8. ServletContextAware's {@code setServletContext}
* (only applicable when running in a web application context)<br>
* 9. {@code postProcessBeforeInitialization} methods of BeanPostProcessors<br>
* 10. InitializingBean's {@code afterPropertiesSet}<br>
* 11. a custom init-method definition<br>
* 12. {@code postProcessAfterInitialization} methods of BeanPostProcessors
*
* <p>On shutdown of a bean factory, the following lifecycle methods apply:<br>
* 1. DisposableBean's {@code destroy}<br>
* 2. a custom destroy-method definition
*
* @author Rod Johnson
* @author Juergen Hoeller
* @author Chris Beams
* @since 13 April 2001
* @see BeanNameAware#setBeanName
* @see BeanClassLoaderAware#setBeanClassLoader
* @see BeanFactoryAware#setBeanFactory
* @see org.springframework.context.ResourceLoaderAware#setResourceLoader
* @see org.springframework.context.ApplicationEventPublisherAware#setApplicationEventPublisher
* @see org.springframework.context.MessageSourceAware#setMessageSource
* @see org.springframework.context.ApplicationContextAware#setApplicationContext
* @see org.springframework.web.context.ServletContextAware#setServletContext
* @see org.springframework.beans.factory.config.BeanPostProcessor#postProcessBeforeInitialization
* @see InitializingBean#afterPropertiesSet
* @see org.springframework.beans.factory.support.RootBeanDefinition#getInitMethodName
* @see org.springframework.beans.factory.config.BeanPostProcessor#postProcessAfterInitialization
* @see DisposableBean#destroy
* @see org.springframework.beans.factory.support.RootBeanDefinition#getDestroyMethodName
*/

这个注释也太长了吧

从上面可以看到BeanFactory有如下的特点:

  • 它是Spring容器的根接口,实现它的接口并且符合规范的都是容器,同时它也是Spring中最基础的一个容器。
  • 实现这个接口可以包含很多的bean definition,而且每个definition的ID都是不一样的,是唯一的。
  • BeanFactory能解析配置文件,如xml文件等,转成definition并且存放在容器中。
  • 定义了实现BeanFactory的实现类的要求。

而从实现而言,BeanFactory实现了几个容器的必备功能,例如获取Bean,判断Bean是否存在等等。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
public interface BeanFactory {

String FACTORY_BEAN_PREFIX = "&";

Object getBean(String name) throws BeansException;

<T> T getBean(String name, Class<T> requiredType) throws BeansException;

<T> T getBean(Class<T> requiredType) throws BeansException;

Object getBean(String name, Object... args) throws BeansException;

boolean containsBean(String name);

boolean isSingleton(String name) throws NoSuchBeanDefinitionException;


boolean isPrototype(String name) throws NoSuchBeanDefinitionException;


boolean isTypeMatch(String name, Class<?> targetType) throws NoSuchBeanDefinitionException;

Class<?> getType(String name) throws NoSuchBeanDefinitionException;

String[] getAliases(String name);

}

3、Spring IoC容器细节

因为BeanFactory都比较大同小异,所以在这里只使用已经废弃的XMLBeanfactory进行学习。

主要是因为比较简单易懂

同样我们需要摘代码出来看一下:

@Deprecated
@SuppressWarnings({"serial", "all"})
public class XmlBeanFactory extends DefaultListableBeanFactory {

    private final XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader(this);


    /**
     * Create a new XmlBeanFactory with the given resource,
     * which must be parsable using DOM.
     * @param resource XML resource to load bean definitions from
     * @throws BeansException in case of loading or parsing errors
     */
    public XmlBeanFactory(Resource resource) throws BeansException {
        this(resource, null);
    }

    /**
     * Create a new XmlBeanFactory with the given input stream,
     * which must be parsable using DOM.
     * @param resource XML resource to load bean definitions from
     * @param parentBeanFactory parent bean factory
     * @throws BeansException in case of loading or parsing errors
     */
    public XmlBeanFactory(Resource resource, BeanFactory parentBeanFactory) throws BeansException {
        super(parentBeanFactory);
        this.reader.loadBeanDefinitions(resource);
    }

}

从上面可以总结这个类做的事情:

  • 使用Resource,把有着bean信息的XML文件转成Resource类型。
  • 先创建了一个XmlBeanDefinitionReader,顾名思义就是用来读BeanDefinition资源的。
  • 使用reader读取已经被转成Resource类型的XML定义的bean。
  • 完成容器的构建

看起来够简单明了,简单可以理解为寻找到XML文件,将XML文件转换为Resource类型,使用XmlBeanDefinitionReader加载bean。

4、Spring IoC容器的主要工作

  • 寻址:寻找bean的配置信息,并且以Resource方式进行读取。
  • 载入:将Resource资源文件读取完毕后,将bean的配置信息转换为BeanFactory的BeanDefinition。
  • 注册:将载入的BeanDefinition注册到IoC容器中,以HashMap的方式进行存储。

    这一阶段可以总结为寻址->载入->注册阶段,称之为容器的初始化阶段,这是单独的一个过程。

  • 注入:当触发getBean操作的时候会触发将Bean注入的过程。

    这一阶段可以总结为注入阶段,是容器发挥作用的阶段,同样跟容器的初始化阶段是两个不同的过程。

坚持原创技术分享,您的支持将鼓励我继续创作!