Spring-Framework-官方文档阅读(一)Spring IoC Container
前言
通读Spring IoC容器官方文档,对IoC容器有一个大致的了解。
环境
- JDK1.8
- Spring Framework Version :4.3.18.RELEASE
容器概述
接口
org.springframework.context.ApplicationContext
代表Spring IoC容器,负责实例化,配置和组装bean。
在独立应用程序中,通常会创建一个ClassPathXmlApplicationContext
或者FileSystemXmlApplicationContext
的实例。
Spring工作原理的高级视图
1.配置元数据
创建SimpleBean
1 | public class SimpleBean { |
config.xml
1 |
|
2.实例化容器
1 | ApplicationContext context = new ClassPathXmlApplicationContext("config.xml"); |
3.使用容器
1 | // 检索Spring容器中的bean |
还有更灵活的方式来从配置文件获取bean,使用GenericApplicationContext
与BeanDefinitionReader
结合,直接读取bean定义
1 | GenericApplicationContext context = new GenericApplicationContext(); |
Bean概述
Spring IoC容器管理一个或多个bean。这些bean是使用您提供给容器的配置元数据创建的,例如,以XML
<bean/>
定义的形式 。
在容器本身内,这些bean定义表示为BeanDefinition
对象。
除了创建配置好的bean之外,ApplicationContext
还允许用户注册在容器外部创建的现有对象。通过getBeanFactory()
获得DefaultListableBeanFactory
,然后使用registerSingleton()
或者registerBeanDefinition()
来注册bean。
1 | DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory(); |
或者是以下做法:
1 | ClassPathXmlApplicationContext applicationContext = new ClassPathXmlApplicationContext("config.xml"); |
命名bean
每个bean都有一个或多个标识符。这些标识符在托管bean的容器中必须是唯一的。bean通常只有一个标识符,但如果它需要多个标识符,则额外的标识符可以被视为别名。
在基于XML的配置元数据中,使用id和/或name属性指定bean标识符。
实例化bean
1.构造函数实例化
1 | <bean id="exampleBean" class="examples.ExampleBean"/> |
2.静态工厂方法实例化
1 | <bean id="clientService" |
1 | public class ClientService { |
3.实例工厂方法实例化
1 | <!-- the factory bean, which contains a method called createInstance() --> |
1 | public class DefaultServiceLocator { |
一个工厂类也可以包含多个工厂方法:
1 | <bean id="serviceLocator" class="examples.DefaultServiceLocator"> |
1 | public class DefaultServiceLocator { |
依赖注入
构造器注入
基于构造函数的 DI由容器调用具有多个参数的构造函数来完成,每个参数表示一个依赖项。
1 | public class SimpleMovieLister { |
构造函数参数解析
1 | package x.y; |
1 | <beans> |
显式指定构造函数参数的类型:
1 | <bean id="exampleBean" class="examples.ExampleBean"> |
使用index属性显式指定构造函数参数的索引:
1 | <bean id="exampleBean" class="examples.ExampleBean"> |
或者指定构造函数参数名称:
1 | <bean id="exampleBean" class="examples.ExampleBean"> |
setter注入
基于setter的 DI是在调用无参数构造函数或无参数static工厂方法来实例化bean之后,通过容器调用bean上的setter方法来完成的。
1 | public class SimpleMovieLister { |
小结
ApplicationContext
的依赖注入支持构造器注入和setter注入两种方式。在通过构造函数方法注入了一些依赖项之后,它还支持基于setter的依赖注入。可以用BeanDefinition
与PropertyEditor
实例结合使用的方式来配置依赖项。 不过,我们一般不直接使用BeanDefinition
与PropertyEditor
,而是用XML 定义bean或者是注解方式(@Component
, @Controller
等等),或者是直接编写@Configuration类。然后,这些类在内部转换为实例BeanDefinition
并用于加载整个Spring IoC容器实例。
解决循环依赖
如果主要使用构造函数注入,则可能出现无法解析的循环依赖关系场景。
例如:类A通过构造函数注入需要类B的实例,而类B通过构造函数注入类A的实例。如果将A类和B类的bean配置为相互注入,则Spring IoC容器会在运行时检测到此循环引用,并抛出BeanCurrentlyInCreationException
异常。
一种可行的解决方案是仅使用setter注入。
与典型情况(没有循环依赖)不同,bean A和bean B之间的循环依赖强制其中一个bean在完全初始化之前被注入另一个bean(一个经典的鸡/蛋场景)。
使用 depends-on
depends-on
可以在初始化bean之前,显式地强制初始化一个或多个bean。下面的例子,在初始化beanOne
之前,将强制初始化manager
1 | <bean id="beanOne" class="ExampleBean" depends-on="manager"/> |
懒加载的bean
默认情况下,ApplicationContext
会立即配置并初始化所有单例bean,但是我们可以使用lazy-init="true"
将其设置为按需加载。
1 | <bean id="lazy" class="com.foo.ExpensiveToCreateBean" lazy-init="true"/> |
注意:懒加载不要使用在数据库连接池上,因为无法立即获知数据库连接状态,将导致运行时创建连接池失败,不可预知的后果。
自动装配协作者
Spring容器可以自动连接协作bean之间的关系。您可以通过检查ApplicationContext
的内容,允许Spring自动为您的bean解析协作者(其他bean)。
自动装配模式
- no:无自动装配,必须使用ref来定义Bean引用。
- byName:按属性名称自动装配。
- byType:按属性类型自动装配,如果存在多个同类型Bean,则抛出致命异常。
- constructor:类似于byType,如果容器中没有构造函数参数类型的一个bean,则抛出致命异常。
Bean 作用域
singleton
Spring IoC容器只创建该bean定义的对象的一个实例。此单个实例存储在此类单例bean的缓存中,并且该Bean的所有后续请求和引用都将返回缓存对象。
1 | <bean id="accountService" class="com.foo.DefaultAccountService"/> |
prototype
和单例对立,通常,对所有有状态bean使用原型范围,对无状态bean使用单例范围。
1 | <bean id="accountService" class="com.foo.DefaultAccountService" scope="prototype"/> |
Request, session, global session, application, and WebSocket
在web程序中使用,对应于HTTP请求作用域
自定义bean的性质
生命周期回调
初始化回调
实现org.springframework.beans.factory.InitializingBean
接口,可以为bean设置初始化方法,该接口定义了一个方法:
1 | void afterPropertiesSet() throws Exception; |
官方不建议使用该接口,因为会增加与Spring的耦合度。可以使用@PostConstruct
或指定bean的初始化方法。
- 使用xml配置文件
1 | <bean id="exampleInitBean" class="examples.ExampleBean" init-method="init"/> |
- 使用Java @Bean注解
1 |
销毁回调
实现org.springframework.beans.factory.DisposableBean
可以为bean设置销毁回调方法,该接口定义了一个方法:
1 | void destroy() throws Exception; |
同样的,不建议实现该接口,可以使用@PreDestroy
或指定bean的初始化方法。
- 使用xml配置文件
1 | <bean id="exampleInitBean" class="examples.ExampleBean" destroy-method="cleanup"/> |
- 使用Java @Bean注解
1 |
从Spring 2.5开始,您有三个控制bean生命周期行为的选项:
-
InitializingBean
和DisposableBean
回调接口 - init()和destroy()方法
-
@PostConstruct
和@PreDestroy
注解
如果为一个bean同时配置了上述方法,则执行方法顺序为:
@PostConstruct
定义的方法InitializingBean
回调接口定义的afterPropertiesSet()
- 自定义配置的
init()
方法
销毁:
@PreDestroy
定义的方法DisposableBean
回调接口 定义的destroy()
- 自定义配置的
destroy()
方法
ApplicationContextAware
和BeanNameAware
ApplicationContextAware
:实现该接口,将注入ApplicationContext
实例的引用BeanNameAware
:实现该接口,将注入BeanName
除了ApplicationContextAware
和BeanNameAware
,Spring还提供了一系列Aware接口,这些接口将为实现类注入对应的实例。
- ApplicationContextAware:声明 ApplicationContext
- ApplicationEventPublisherAware:ApplicationContext的事件发布者
- BeanClassLoaderAware:用于加载bean类的类加载器。
- BeanFactoryAware:声明 BeanFactory
- BeanNameAware:声明bean的名称
- BootstrapContextAware
- LoadTimeWeaverAware
- MessageSourceAware
- NotificationPublisherAware:Spring JMX通知发布者
- PortletConfigAware:当前PortletConfig容器
- PortletContextAware:当前PortletContext容器
- ResourceLoaderAware:配置的加载程序,用于对资源进行低级访问
- ServletConfigAware:当前ServletConfig容器
- ServletContextAware:当前ServletContext容器
Bean的继承
在xml配置文件里,我们可以定义bean的继承体系,使用parent
属性定义父类。
1 | <bean id="inheritedTestBean" abstract="true" |
在源码里,子类是通过ChildBeanDefinition
来定义的。
容器扩展点
一般来说,我们不需要去继承ApplicationContext
实现类,不过Spring预留了一些接口,让我们可以扩展Spring IoC容器。
BeanPostProcessor
1 | public interface BeanPostProcessor { |
来手动注册BeanPostProcessor
,这些BeanPostProcessor
不需要遵循Orderd
排序规则,总是在自动注入的BeanPostProcessor
之前执行。
一个BeanPostProcessor
的实现例子RequiredAnnotationBeanPostProcessor
使用BeanFactoryPostProcessor
自定义配置元数据
1 | public interface BeanFactoryPostProcessor { |
类似于BeanPostProcessor
,不同的是,BeanFactoryPostProcessor
操作配置元数据。也就是说,Spring容器允许BeanFactoryPostProcessor
读取配置并更改。
这些BeanPostProcessor
将在每个bean初始化时自动执行,以便将更改应用于定义容器的配置元数据。Spring包含许多预定义的BeanPostProcessor
,例如PropertyOverrideConfigurer
和PropertyPlaceholderConfigurer
。
使用FactoryBean
自定义实例化逻辑
1 | public interface FactoryBean<T> { |
配置实现FactoryBean<T>
的bean是,返回的是getObject()
生成的bean,如果要返回 FactoryBean实例本身,应该使用getBean("&myBean")
基于注解的容器配置
- @Required
- @Autowired
- @Resource
- @Qualifier
- @PostConstruct and @PreDestroy
类路径扫描和托管组件
- @Component,@Controller,@Repository,@Service
- @Scope,@SessionScope
- @ComponentScan
JSR 330标准注解和Spring注解对照
Spring | javax.inject.* |
---|---|
@Autowired | @Inject |
@Component | @Named / @ManagedBean |
@Scope(“singleton”) | @Singleton |
@Qualifier | @Qualifier / @Named |
@Value | - |
@Required | - |
@Lazy | - |
ObjectFactory | Provider |
Environment 抽象
主要包含两个方面:profiles(多环境) and properties(配置).
多环境配置
- 代码方式
1 | AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext(); |
- 配置方式
1 | spring.profiles.active |
配置抽象
代码演示下:
1 | ApplicationContext ctx = new GenericApplicationContext(); |
使用@PropertySource
添加配置
1 |
|
BeanFactory还是ApplicationContext?
尽量使用ApplicationContext
,因为ApplicationContext
包含BeanFactory
的所有功能:
功能 | BeanFactory | ApplicationContext |
---|---|---|
bean初始化/编辑 | 支持 | 支持 |
自动注册BeanPostProcessor |
不支持 | 支持 |
自动注册BeanFactoryPostProcessor |
不支持 | 支持 |
方便的MessageSource访问(适用于i18n) | 不支持 | 支持 |
发布ApplicationEvent |
不支持 | 支持 |
要使用BeanFactory实现显式注册bean后置处理器,您需要编写如下代码:
1 | DefaultListableBeanFactory factory = new DefaultListableBeanFactory(); |
要使用BeanFactory
实现时显式注册BeanFactoryPostProcessor
,您必须编写如下代码:
1 | DefaultListableBeanFactory factory = new DefaultListableBeanFactory(); |