Spring 官方文档笔记

本文基于 Spring 官方文档 5.3.5

The IoC Container

Introduction to the Spring IoC Container and Beans

In short, the BeanFactory provides the configuration framework and basic functionality, and the ApplicationContext adds more enterprise-specific functionality. The ApplicationContext is a complete superset of the BeanFactory and is used exclusively in this chapter in descriptions of Spring’s IoC container. For more information on using the BeanFactory instead of the ApplicationContext, see The BeanFactory.

org.springframework.beansorg.springframework.context两个包是IOC的基础。

Spring 中的容器相关的类是 BeanFactory以及ApplicationContext,且ApplicationContextBeanFactory的 superset(超集)。BeanFactory提供了配置框架和基础功能,ApplicationContext扩展了企业级的能力。

看了下BeanFactory,是一个顶级接口,里面主要提供了getBeangetType之类的接口。

Container Overview

Configuration Metadata

org.springframework.context.ApplicationContext 接口就是 Spring IoC container,负责实例化、配置和组装(assembling)。

像我们单机模式(stand-alone)下,常用的容器实现类是 ClassPathXmlApplicationContext or FileSystemXmlApplicationContext

For information about using other forms of metadata with the Spring container, see:

  • Annotation-based configuration: Spring 2.5 introduced support for annotation-based configuration metadata.
  • Java-based configuration: Starting with Spring 3.0, many features provided by the Spring JavaConfig project became part of the core Spring Framework. Thus, you can define beans external to your application classes by using Java rather than XML files. To use these new features, see the @Configuration, @Bean, @Import, and @DependsOn annotations.

我们通过将 bean 信息定义在 xml 文件中,容器会将这些 metadata 组装起来。

除了 xml 可以定义 metadata 之外,还有注解和Java代码形式。

Instantiating a Container

ApplicationContext context = new ClassPathXmlApplicationContext("services.xml", "daos.xml");

像这么一段话就是创建了一个容器。容器需要加载Resource

After you learn about Spring’s IoC container, you may want to know more about Spring’s Resource abstraction (as described in Resources), which provides a convenient mechanism for reading an InputStream from locations defined in a URI syntax. In particular, Resource paths are used to construct applications contexts, as described in Application Contexts and Resource Paths.

Resource为读取输入流提供了一个便捷的方式。创建容器时传入的 xml 文件最后都要转换成 Resouce 存放到 IoC 容器中。

Spring 不推荐 XML 文件互相引用的时候,使用 “../“ 来引用父级目录的文件。按我的理解,根据后面的解释,classpath 指定一批目录,在查找父级目录的文件时会按照 classpath 指定的目录查找,找到第一个就不继续找了,从而 lead to the choice of a different, incorrect directory

更推荐的做法是使用占位符,去解析 JVM 的环境变量。

Using the Container

ApplicationContext相比较BeanFactory而言,功能更加丰富,它可以管理 bean 的注册以及 bean 的依赖。

// create and configure beans
ApplicationContext context = new ClassPathXmlApplicationContext("services.xml", "daos.xml");

// retrieve configured instance
PetStoreService service = context.getBean("petStore", PetStoreService.class);

// use configured instance
List<String> userList = service.getUsernameList();

Bean Overview

我们可以通过ApplicationContext.getBeanFacotry()方法返回 beanFactory 来将我们自己 new 出来的对象放入容器来管理。

前面说了 BeanFactory 是顶级接口,其默认实现是DefaultListableBeanFactory,具体实现见 Spring 源码 AbstractRefreshableApplicationContext#refreshBeanFactory()函数,从创建一个容器开始,比如 ClassPathXmlApplicationContext的构造函数,一直到AbstractApplicationContextrefresh()创建容器的主流程函数,到获取一个 refresh 的 BeanFactory。

@Override
protected final void refreshBeanFactory() throws BeansException {
if (hasBeanFactory()) {
destroyBeans();
closeBeanFactory();
}
try {
DefaultListableBeanFactory beanFactory = createBeanFactory();
beanFactory.setSerializationId(getId());
customizeBeanFactory(beanFactory);
loadBeanDefinitions(beanFactory);
this.beanFactory = beanFactory; // 在这里指定了默认的 BeanFactory.
}
catch (IOException ex) {
throw new ApplicationContextException("I/O error parsing bean definition source for " + getDisplayName(), ex);
}
}

需要注意的是,bean 的元数据最好尽可能早的注册进去,这个可以帮助容器更合理的推断这些 bean。

同时文档上还提及在运行时(runtime)注册 bean 可能会覆盖元数据(metadata)和单例,导致并发异常以及 bean 状态不一致的问题。

关于 bean 的命名,我们可以不给 bean 命名(像 Springboot 中我们都是按 type 注入的话,也没有去显示标明名字),容器会自动生成名字,但是如果我们想在 xml 中使用 ref 来指定依赖的 bean 的话,就必须指定 name。

Dependencies

Since you can mix constructor-based and setter-based DI, it is a good rule of thumb to use constructors for mandatory dependencies and setter methods or configuration methods for optional dependencies. Note that use of the @Required annotation on a setter method can be used to make the property be a required dependency; however, constructor injection with programmatic validation of arguments is preferable.

构造函数注入常用于必选的参数,set 注入用于 optional 可选的参数。

xmlns是 Xml Namespace 的缩写,Spring 的默认命名空间为 “http://www.springframework.org/schema/beans",`xmlns:abc`声明一个叫 abc 的 namespace。

在引用其他 bean 时(即组合模式),被引用的 bean 会被按需优先加载,避免 set 进来一个 null 值。

Inner Beans

inner bean 不需要定义 id 和 name,就算定义了也没用。

inner bean 的 scope 与它的 container bean 一致。

p 命名空间

简化 property 的编写

C 命名空间

简化 constructor 的编写

depends-on

可以显示化指定依赖关系,因为有些场景下两个 bean 的依赖关系 less direct。通过 depends-on(或者@DependsOn注解)来保证被依赖的 bean 优先实例化。

还可以指定 destruction-time dependency,被依赖的 bean 优先销毁。

Bean Scopes

Scope Description
singleton (Default) Scopes a single bean definition to a single object instance for each Spring IoC container.
prototype Scopes a single bean definition to any number of object instances.
request Scopes a single bean definition to the lifecycle of a single HTTP request. That is, each HTTP request has its own instance of a bean created off the back of a single bean definition. Only valid in the context of a web-aware Spring ApplicationContext.
session Scopes a single bean definition to the lifecycle of an HTTP Session. Only valid in the context of a web-aware Spring ApplicationContext.
application Scopes a single bean definition to the lifecycle of a ServletContext. Only valid in the context of a web-aware Spring ApplicationContext.
websocket Scopes a single bean definition to the lifecycle of a WebSocket. Only valid in the context of a web-aware Spring ApplicationContext.

Singleton Scope

When you create a bean definition, you create a recipe for creating actual instances of the class defined by that bean definition.

recipe: a set of instructions for preparing a particular dish, including a list of the ingredients required.

Spring 文档中对什么叫 bean definition 作出了定义,一个 bean definition 也就是一个 recipe,recipe 就是类似指导书、说明书的概念。

而单例模式,指的就是 a single recipe。

和设计模式 GoF 书中的单例模式不同,Spring 中的单例模式指的是per container per instance,而设计模式中的单例指的是per classloader per instance

Prototype

Spring 中的 prototype 类型的 scope,即per request per instance,这里的 request 指从 IoC 容器中获取 bean 的操作,比如getBean方法。

As a rule, you should use the prototype scope for all stateful beans and the singleton scope for stateless beans.

Spring 建议对于有状态的 bean,使用 Prototype scope。

对于 Prototype scope 的 bean,Spring does not manage the complete lifecycle of it.

Spring只会实例化、配置并组装,然后将它交给客户端,至于实例的回收需要客户端自行完成。Spring 建议使用 bean post-process 来实现。

Singleton Beans with Prototype-bean Dependencies

单例 bean 带有原型 bean 的依赖情况下,因为依赖注入只发生在实例化的时候,因此单例 bean 中的原型 bean 一直会是第一次注入的那个 bean。

如果想要实现每次访问单例 bean 时,内部的原型 bean 是一个新的实例的话,参考Method Injection

Request, Session, Application, and WebSocket Scopes

这几个 scope 只适用于ApplicationContext容器的 Web 实现,比如 XmlWebApplicationContext,否则会抛出 IllegalStateException

Custom Scopes

Customizing the Nature of a Bean

Lifecycle Callbacks

可以通过实现InitializingBean类的afterPropertiesSet()方法以及DisposableBean类的destroy()方法,在 bean 初始化后和 bean 回收前两个时间点自定义处理逻辑。

JSR-250 引入了@PostConstruct@PreDestroy对应上述的两个时机。

在 Spring 内部,Spring 使用BeanPostProcessor接口来实现 bean 的一切后处理回调。

但是 Spring 更推荐使用 init-method 和 destroy-method 来实现上述两个功能,它们是 the same effect, but not couple the code to Spring.

Spring 中存在默认的初始化回调和析构回调方法,通过 default-init-method 来指定以后,这样我们不用在 xml 中每个 bean 的定义中显示指定 init-method。

init 回调发送发生在 AOP 之前,当一个 bean 的所有 dependencies is supplyed,就会执行这个回调。

ApplicationContextAware and BeanNameAware

这两个都是 Aware 类接口,它们的回调在依赖注入之 init-method 之前执行。

Other Aware Interfaces

简单来讲,实现 aware 接口的 bean,可以获取容器中加载这个 bean 的一些资源。并且逆转了 IoC。

Bean Definition Inheritance

通过 getBean 方法去获取abstract=true的 bean,会报错。

Container Extension Points

容器扩展点:

  • BeanPostProcessor

Customizing Beans by Using a BeanPostProcessor

BeanPostProcessor 主要用到的就是 Ordered 接口了(Ordered 的作用机制以及 @Ordered 是如何实现的)

Container Extension Points

主要是三处扩展点:

  • BPP(BeanPostProcessor)
  • BFPP(BeanFactoryPostProcessor)
  • BF(BeanFactory)

BPP 和 BFPP的主要区别在于:

the Spring IoC container lets a BeanFactoryPostProcessor read the configuration metadata and potentially change it before the container instantiates any beans other than BeanFactoryPostProcessor instances.

BFPP 读取配置文件并且可以在容器实例化除 BFPP 之外的所有 bean 之前,修改配置文件的中的元数据。

Annotation-based Container Configuration

Annotation injection is performed before XML injection. Thus, the XML configuration overrides the annotations for properties wired through both approaches.

注解形式的注入早于 XML 形式的 bean 注入,因此 XML 配置会覆盖注解形式的注入。

@Required

为了防止某些依赖的 bean 为 null

Using @Autowired

@Autowired不推荐使用在 field 上的原因是:What exactly is Field Injection and how to avoid it?

总结一下就是会与 IoC 容器强绑定,不利于测试。

文章作者: yPhantom
文章链接: https://guoyuxiang.cn/2021/04/07/spring-document-note/
版权声明: 本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明来自 Life Note