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
public class SimpleBean {
public void send() {
System.out.println("Hello Spring Bean!");
}
}
config.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"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd">
<bean id="simple" class="base.SimpleBeanFactoryBean"/>
</beans>
2.实例化容器
ApplicationContext context = new ClassPathXmlApplicationContext("config.xml");
3.使用容器
// 检索Spring容器中的bean
SimpleBean simpleBean = context.getBean(SimpleBean.class);
// 使用bean
simpleBean.send();
还有更灵活的方式来从配置文件获取bean,使用GenericApplicationContext
与BeanDefinitionReader
结合,直接读取bean定义
GenericApplicationContext context = new GenericApplicationContext();
new XmlBeanDefinitionReader(context).loadBeanDefinitions("config.xml");
context.refresh();
SimpleBean simpleBean = (SimpleBean) context.getBean("simple");
simpleBean.send();
Bean概述
Spring IoC容器管理一个或多个bean。这些bean是使用您提供给容器的配置元数据创建的,例如,以XML
<bean/>
定义的形式 。
在容器本身内,这些bean定义表示为BeanDefinition
对象。
除了创建配置好的bean之外,ApplicationContext
还允许用户注册在容器外部创建的现有对象。通过getBeanFactory()
获得DefaultListableBeanFactory
,然后使用registerSingleton()
或者registerBeanDefinition()
来注册bean。
DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();
ClassPathXmlApplicationContext applicationContext = new ClassPathXmlApplicationContext("config.xml");
User user = new User();
user.setId(1L);
user.setName("xiaoming");
beanFactory.registerSingleton("user", user);
User bean = (User) applicationContext.getBean("user");
System.out.println(bean);
或者是以下做法:
ClassPathXmlApplicationContext applicationContext = new ClassPathXmlApplicationContext("config.xml");
DefaultListableBeanFactory beanFactory = (DefaultListableBeanFactory) applicationContext.getBeanFactory();
BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition(User.class);
builder.addPropertyValue("id", 1);
builder.addPropertyValue("name", "xiaoming");
AbstractBeanDefinition beanDefinition = builder.getBeanDefinition();
beanFactory.registerBeanDefinition("user", beanDefinition);
User bean = (User) applicationContext.getBean("user");
System.out.println(bean);
命名bean
每个bean都有一个或多个标识符。这些标识符在托管bean的容器中必须是唯一的。bean通常只有一个标识符,但如果它需要多个标识符,则额外的标识符可以被视为别名。
在基于XML的配置元数据中,使用id和/或name属性指定bean标识符。
实例化bean
1.构造函数实例化
<bean id="exampleBean" class="examples.ExampleBean"/>
<bean name="anotherExample" class="examples.ExampleBeanTwo"/>
2.静态工厂方法实例化
<bean id="clientService"
class="examples.ClientService"
factory-method="createInstance"/>
public class ClientService {
private static ClientService clientService = new ClientService();
private ClientService() {}
public static ClientService createInstance() {
return clientService;
}
}
3.实例工厂方法实例化
<!-- the factory bean, which contains a method called createInstance() -->
<bean id="serviceLocator" class="examples.DefaultServiceLocator">
<!-- inject any dependencies required by this locator bean -->
</bean>
<!-- the bean to be created via the factory bean -->
<bean id="clientService"
factory-bean="serviceLocator"
factory-method="createClientServiceInstance"/>
public class DefaultServiceLocator {
private static ClientService clientService = new ClientServiceImpl();
public ClientService createClientServiceInstance() {
return clientService;
}
}
一个工厂类也可以包含多个工厂方法:
<bean id="serviceLocator" class="examples.DefaultServiceLocator">
<!-- inject any dependencies required by this locator bean -->
</bean>
<bean id="clientService"
factory-bean="serviceLocator"
factory-method="createClientServiceInstance"/>
<bean id="accountService"
factory-bean="serviceLocator"
factory-method="createAccountServiceInstance"/>
public class DefaultServiceLocator {
private static ClientService clientService = new ClientServiceImpl();
private static AccountService accountService = new AccountServiceImpl();
public ClientService createClientServiceInstance() {
return clientService;
}
public AccountService createAccountServiceInstance() {
return accountService;
}
}
依赖注入
构造器注入
基于构造函数的 DI由容器调用具有多个参数的构造函数来完成,每个参数表示一个依赖项。
public class SimpleMovieLister {
// SimpleMovieLister依赖于MovieFinder
private MovieFinder movieFinder;
// 一个构造函数,以便Spring容器可以注入一个MovieFinder
public SimpleMovieLister(MovieFinder movieFinder) {
this.movieFinder = movieFinder;
}
// business logic that actually uses the injected MovieFinder is omitted...
}
构造函数参数解析
package x.y;
public class Foo {
public Foo(Bar bar, Baz baz) {
// ...
}
}
<beans>
<bean id="foo" class="x.y.Foo">
<constructor-arg ref="bar"/>
<constructor-arg ref="baz"/>
</bean>
<bean id="bar" class="x.y.Bar"/>
<bean id="baz" class="x.y.Baz"/>
</beans>
显式指定构造函数参数的类型:
<bean id="exampleBean" class="examples.ExampleBean">
<constructor-arg type="int" value="7500000"/>
<constructor-arg type="java.lang.String" value="42"/>
</bean>
使用index属性显式指定构造函数参数的索引:
<bean id="exampleBean" class="examples.ExampleBean">
<constructor-arg index="0" value="7500000"/>
<constructor-arg index="1" value="42"/>
</bean>
或者指定构造函数参数名称:
<bean id="exampleBean" class="examples.ExampleBean">
<constructor-arg name="years" value="7500000"/>
<constructor-arg name="ultimateAnswer" value="42"/>
</bean>
setter注入
基于setter的 DI是在调用无参数构造函数或无参数static工厂方法来实例化bean之后,通过容器调用bean上的setter方法来完成的。
public class SimpleMovieLister {
// SimpleMovieLister依赖于MovieFinder
private MovieFinder movieFinder;
// 一个setter方法,以便Spring容器可以注入一个MovieFinder
public void setMovieFinder(MovieFinder movieFinder) {
this.movieFinder = movieFinder;
}
// business logic that actually uses the injected MovieFinder is omitted...
}
小结
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
<bean id="beanOne" class="ExampleBean" depends-on="manager"/>
<bean id="manager" class="ManagerBean" />
懒加载的bean
默认情况下,ApplicationContext
会立即配置并初始化所有单例bean,但是我们可以使用lazy-init="true"
将其设置为按需加载。
<bean id="lazy" class="com.foo.ExpensiveToCreateBean" lazy-init="true"/>
<bean name="not.lazy" class="com.foo.AnotherBean"/>
注意:懒加载不要使用在数据库连接池上,因为无法立即获知数据库连接状态,将导致运行时创建连接池失败,不可预知的后果。
自动装配协作者
Spring容器可以自动连接协作bean之间的关系。您可以通过检查ApplicationContext
的内容,允许Spring自动为您的bean解析协作者(其他bean)。
自动装配模式
- no:无自动装配,必须使用ref来定义Bean引用。
- byName:按属性名称自动装配。
- byType:按属性类型自动装配,如果存在多个同类型Bean,则抛出致命异常。
- constructor:类似于byType,如果容器中没有构造函数参数类型的一个bean,则抛出致命异常。
Bean 作用域
singleton
Spring IoC容器只创建该bean定义的对象的一个实例。此单个实例存储在此类单例bean的缓存中,并且该Bean的所有后续请求和引用都将返回缓存对象。
<bean id="accountService" class="com.foo.DefaultAccountService"/>
<!-- the following is equivalent, though redundant (singleton scope is the default) -->
<bean id="accountService" class="com.foo.DefaultAccountService" scope="singleton"/>
prototype
和单例对立,通常,对所有有状态bean使用原型范围,对无状态bean使用单例范围。
<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设置初始化方法,该接口定义了一个方法:
void afterPropertiesSet() throws Exception;
官方不建议使用该接口,因为会增加与Spring的耦合度。可以使用@PostConstruct
或指定bean的初始化方法。
- 使用xml配置文件
<bean id="exampleInitBean" class="examples.ExampleBean" init-method="init"/>
- 使用Java @Bean注解
@Bean(initMethod = "init")
销毁回调
实现org.springframework.beans.factory.DisposableBean
可以为bean设置销毁回调方法,该接口定义了一个方法:
void destroy() throws Exception;
同样的,不建议实现该接口,可以使用@PreDestroy
或指定bean的初始化方法。
- 使用xml配置文件
<bean id="exampleInitBean" class="examples.ExampleBean" destroy-method="cleanup"/>
- 使用Java @Bean注解
@Bean(destroyMethod = "cleanup")
从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
属性定义父类。
<bean id="inheritedTestBean" abstract="true"
class="org.springframework.beans.TestBean">
<property name="name" value="parent"/>
<property name="age" value="1"/>
</bean>
<bean id="inheritsWithDifferentClass"
class="org.springframework.beans.DerivedTestBean"
parent="inheritedTestBean" init-method="initialize">
<property name="name" value="override"/>
<!-- the age property value of 1 will be inherited from parent -->
</bean>
在源码里,子类是通过ChildBeanDefinition
来定义的。
容器扩展点
一般来说,我们不需要去继承ApplicationContext
实现类,不过Spring预留了一些接口,让我们可以扩展Spring IoC容器。
BeanPostProcessor
public interface BeanPostProcessor {
//在每个bean初始化之前调用
Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException;
//在每个bean初始化完毕后调用
Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException;
}
可以定义多个`BeanPostProcessor`,然后实现`Ordered`接口并修改属性order来控制`BeanPostProcessor`的执行顺序。
注意:`ConfigurableBeanFactory`提供
```java
void addBeanPostProcessor(BeanPostProcessor beanPostProcessor);
来手动注册BeanPostProcessor
,这些BeanPostProcessor
不需要遵循Orderd
排序规则,总是在自动注入的BeanPostProcessor
之前执行。
一个BeanPostProcessor
的实现例子RequiredAnnotationBeanPostProcessor
使用BeanFactoryPostProcessor
自定义配置元数据
public interface BeanFactoryPostProcessor {
void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException;
}
类似于BeanPostProcessor
,不同的是,BeanFactoryPostProcessor
操作配置元数据。也就是说,Spring容器允许BeanFactoryPostProcessor
读取配置并更改。
这些BeanPostProcessor
将在每个bean初始化时自动执行,以便将更改应用于定义容器的配置元数据。Spring包含许多预定义的BeanPostProcessor
,例如PropertyOverrideConfigurer
和PropertyPlaceholderConfigurer
。
使用FactoryBean
自定义实例化逻辑
public interface FactoryBean<T> {
// 自定义bean的初始化逻辑
T getObject() throws Exception;
Class<?> getObjectType();
boolean isSingleton();
}
配置实现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(配置).
多环境配置
- 代码方式
AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext();
ctx.getEnvironment().setActiveProfiles("development");
ctx.register(SomeConfig.class, StandaloneDataConfig.class, JndiDataConfig.class);
ctx.refresh();
- 配置方式
spring.profiles.active
配置抽象
代码演示下:
ApplicationContext ctx = new GenericApplicationContext();
Environment env = ctx.getEnvironment();
// 是否包含foo的配置
boolean containsFoo = env.containsProperty("foo");
System.out.println("Does my environment contain the 'foo' property? " + containsFoo);
// 向环境中添加配置
MutablePropertySources sources = ctx.getEnvironment().getPropertySources();
sources.addFirst(new MyPropertySource());
使用@PropertySource
添加配置
@Configuration
@PropertySource("classpath:/com/myco/app.properties")
public class AppConfig {
@Autowired
Environment env;
@Bean
public TestBean testBean() {
TestBean testBean = new TestBean();
testBean.setName(env.getProperty("testbean.name"));
return testBean;
}
}
BeanFactory还是ApplicationContext?
尽量使用ApplicationContext
,因为ApplicationContext
包含BeanFactory
的所有功能:
功能 | BeanFactory | ApplicationContext |
---|---|---|
bean初始化/编辑 | 支持 | 支持 |
自动注册BeanPostProcessor |
不支持 | 支持 |
自动注册BeanFactoryPostProcessor |
不支持 | 支持 |
方便的MessageSource访问(适用于i18n) | 不支持 | 支持 |
发布ApplicationEvent |
不支持 | 支持 |
要使用BeanFactory实现显式注册bean后置处理器,您需要编写如下代码:
DefaultListableBeanFactory factory = new DefaultListableBeanFactory();
// populate the factory with bean definitions
// now register any needed BeanPostProcessor instances
MyBeanPostProcessor postProcessor = new MyBeanPostProcessor();
factory.addBeanPostProcessor(postProcessor);
// now start using the factory
要使用BeanFactory
实现时显式注册BeanFactoryPostProcessor
,您必须编写如下代码:
DefaultListableBeanFactory factory = new DefaultListableBeanFactory();
XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader(factory);
reader.loadBeanDefinitions(new FileSystemResource("beans.xml"));
// bring in some property values from a Properties file
PropertyPlaceholderConfigurer cfg = new PropertyPlaceholderConfigurer();
cfg.setLocation(new FileSystemResource("jdbc.properties"));
// now actually do the replacement
cfg.postProcessBeanFactory(factory);
Spring-Framework-官方文档阅读(一)Spring IoC Container的更多相关文章
- Spring Framework 官方文档学习(四)之Validation、Data Binding、Type Conversion(一)
题外话:本篇是对之前那篇的重排版.并拆分成两篇,免得没了看的兴趣. 前言 在Spring Framework官方文档中,这三者是放到一起讲的,但没有解释为什么放到一起.大概是默认了读者都是有相关经验的 ...
- Spring Framework 官方文档学习(四)之Validation、Data Binding、Type Conversion(二)
接前一篇 Spring Framework 官方文档学习(四)之Validation.Data Binding.Type Conversion(一) 本篇主要内容:Spring Type Conver ...
- Spring Framework 官方文档学习(四)之Validation、Data Binding、Type Conversion
本篇太乱,请移步: Spring Framework 官方文档学习(四)之Validation.Data Binding.Type Conversion(一) 写了删删了写,反复几次,对自己的描述很不 ...
- Spring Framework 官方文档学习(二)之IoC容器与bean lifecycle
到目前为止,已经看了一百页.再次感慨下,如果想使用Spring,那可以看视频或者找例子,但如果想深入理解Spring,最好还是看官方文档. 原计划是把一些基本接口的功能.层次以及彼此的关系罗列一下.同 ...
- Spring Framework 官方文档学习(一)介绍
http://docs.spring.io/spring/docs/current/spring-framework-reference/htmlsingle/#overview-maven-bom ...
- Spring Framework 官方文档学习(三)之Resource
起因 标准JDK中使用 java.net.URL 来处理资源,但有很多不足,例如不能限定classpath,不能限定 ServletContext 路径. 所以,Spring提供了 Resource ...
- 《Spring 5官方文档》 Spring AOP的经典用法
原文链接 在本附录中,我们会讨论一些初级的Spring AOP接口,以及在Spring 1.2应用中所使用的AOP支持. 对于新的应用,我们推荐使用 Spring AOP 2.0来支持,在AOP章节有 ...
- Spring 通读官方文档
Spring 通读官方文档 这部分参考文档涵盖了Spring Framework绝对不可或缺的所有技术. 其中最重要的是Spring Framework的控制反转(IoC)容器.Spring框架的Io ...
- Spring 4 官方文档学习(十二)View技术
关键词:view technology.template.template engine.markup.内容较多,按需查用即可. 介绍 Thymeleaf Groovy Markup Template ...
- Spring 4 官方文档学习(十一)Web MVC 框架
介绍Spring Web MVC 框架 Spring Web MVC的特性 其他MVC实现的可插拔性 DispatcherServlet 在WebApplicationContext中的特殊的bean ...
随机推荐
- Codeforces_734_F
http://codeforces.com/problemset/problem/734/F x|y + x&y = x+y. #include<iostream> #includ ...
- BZOJ 2653 middle (可持久化线段树+中位数+线段树维护最大子序和)
题意: 左端点在[a,b],右端点在[c,d],求这个线段里中位数(上取整)最大值 思路: 对数组离散化,对每一个值建中位数的可持久化线段树(有重复也没事),就是对于root[i],大于等于i的值为1 ...
- Java并发编程-扩展可回调的Future
前提 最近在看JUC线程池java.util.concurrent.ThreadPoolExecutor的源码实现,其中了解到java.util.concurrent.Future的实现原理.从目前j ...
- nginx单个ip访问频率限制
一.限制所有单个ip的访问频率 1.http中的配置 http { #$limit_conn_zone:限制并发连接数 limit_conn_zone $binary_remote_addr zone ...
- Linux恢复删除的文件
linux恢复删除的文件 先介绍下一些文件的基本概念: · 文件实际上是一个指向inode的链接, inode链接包含了文件的所有属性, 比如权限和所有者, 数据块地址(文件存储在磁盘 ...
- vue自带的实例属性和方法($打头)
Vue 实例内置了一些有用的实例属性与方法.它们都有前缀 $,以便与用户定义的属性区分开来.例如: var data = { a: 1 } var vm = new Vue({ el: '#examp ...
- asp.net MVC项目开发之统计图echarts后台数据的处理(三)
前台显示的东西,有相应的文档很容易修改,后台传递数据方式才是我们最关心的 首先要记住,我们一步数据使用的是post,那么后台代码我们要给方法加上 [HttpPost]注解 不然异步没有效果 下面上代码 ...
- 国产安全自主可控IT智能运维管理解决方案
新一轮科技革命和产业变革席卷全球,大数据.云计算.物联网.人工智能.区块链等新技术不断涌现,数字经济正深刻地改变着人类的生产和生活方式,作为经济增长新动能的作用日益凸显.伴随增长的,还有网络中不断涌现 ...
- 静态存储SRAM设计
SRAM即静态随机存取存储器.它是具有静止存取功能的内存,不需要刷新电路便能保存它内部存储的数据.在工业与科学用的很多子系统,汽车电子等等都用到了SRAM.现代设备中很多都嵌入了几千字节的SRAM.实 ...
- proptypes介绍
开始 prop-types的主要作用:对props中数据类型进行检测及限制 引用方法:import PropTypes from 'prop-types' 用法: // 基本用法 用来检测数据类型 c ...