第二节 Spring框架之二IOC容器的继续研究
一. IOC容器的探索
- 从容器的创建来探索
用ClassPathXMLApplicationContext来创建,在这个类中,有很多个重载的构造方法,其中目前主要用以下这四个构造器,在内部实现上,这四个构造器调用的还是同一个构造方法。真正实现容器的刷新并且启动和创建bean组件的构造方法
在这个构造器中最重要的方法就是refresh()方法,由它来负责刷新IOC容器,启动和创建bean。这个方法的实现如下,重要的注释已经在代码中注释
public void refresh() throws BeansException, IllegalStateException {
synchronized (this.startupShutdownMonitor) {
// Prepare this context for refreshing.
prepareRefresh();
// Tell the subclass to refresh the internal bean factory.加载好了配置文件
//工厂中在解析XML的时候,解析完成后会保存每一个要创建的bean信息,但是并没有创建
ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
// Prepare the bean factory for use in this context.
prepareBeanFactory(beanFactory);
try {
// Allows post-processing of the bean factory in context subclasses.
postProcessBeanFactory(beanFactory);
// Invoke factory processors registered as beans in the context. invokeBeanFactoryPostProcessors(beanFactory);
// Register bean processors that intercept bean creation.
registerBeanPostProcessors(beanFactory);
// Initialize message source for this context.
initMessageSource();
// Initialize event multicaster for this context.
initApplicationEventMulticaster();
// Initialize other special beans in specific context subclasses.给子类实现的接口
onRefresh();
// Check for listener beans and register them.
registerListeners();
// Instantiate all remaining (non-lazy-init) singletons.
**finishBeanFactoryInitialization(beanFactory);//初始化所有的单例类**
// Last step: publish corresponding event.
finishRefresh();
}
catch (BeansException ex) {
// Destroy already created singletons to avoid dangling resources.
destroyBeans();
// Reset 'active' flag.
cancelRefresh(ex);
// Propagate exception to caller.
throw ex;
}
}
}
在这个方法中起决定作用的是finishBeanFactoryInitialization(beanFactory)这个方法,这个方法的具体实现如下
protected void finishBeanFactoryInitialization(ConfigurableListableBeanFactory beanFactory) {
// Initialize conversion service for this context.和类型转换有关的
if (beanFactory.containsBean(CONVERSION_SERVICE_BEAN_NAME) &&
beanFactory.isTypeMatch(CONVERSION_SERVICE_BEAN_NAME, ConversionService.class)) {
beanFactory.setConversionService(
beanFactory.getBean(CONVERSION_SERVICE_BEAN_NAME, ConversionService.class));
}
// Initialize LoadTimeWeaverAware beans early to allow for registering their transformers early.
String[] weaverAwareNames = beanFactory.getBeanNamesForType(LoadTimeWeaverAware.class, false, false);
for (String weaverAwareName : weaverAwareNames) {
getBean(weaverAwareName);
}
// Stop using the temporary ClassLoader for type matching.
beanFactory.setTempClassLoader(null);
// Allow for caching all bean definition metadata, not expecting further changes.冻结配置,bean的配置信息都在beanfactory的一个map中保存着,先把这个map冻结,防止其他的操作来对其进行修改,为了多线程安全问题
beanFactory.freezeConfiguration();
// Instantiate all remaining (non-lazy-init) singletons.初始化所以的懒加载的bean
beanFactory.preInstantiateSingletons();
}
这个方法中起决定作用的是preInstantiateSingletons()这个方法,实现是:
public void preInstantiateSingletons() throws BeansException {
if (this.logger.isDebugEnabled()) {
this.logger.debug("Pre-instantiating singletons in " + this);
}
List<String> beanNames;
//beanDefinitionMap保存了每个bean的详细的配置信息
synchronized (this.beanDefinitionMap) {
// Iterate over a copy to allow for init methods which in turn register new bean definitions.
// While this may not be part of the regular factory bootstrap, it does otherwise work fine.beanDefinitionNames保存了所有的bean的id
beanNames = new ArrayList<String>(this.beanDefinitionNames);
}
开始循环创建,按照配置顺序进行创建的
for (String beanName : beanNames) {
RootBeanDefinition bd = getMergedLocalBeanDefinition(beanName);//获取当前要创建的bean的详细的配置信息
//判断要创建的bean的配置信息中,是否是不是抽象的,是否是单列的,是否不是懒加载的
if (!bd.isAbstract() && bd.isSingleton() && !bd.isLazyInit()) {
//判断这个bean是否是工厂bean
if (isFactoryBean(beanName)) {
final FactoryBean<?> factory = (FactoryBean<?>) getBean(FACTORY_BEAN_PREFIX + beanName);
boolean isEagerInit;
if (System.getSecurityManager() != null && factory instanceof SmartFactoryBean) {
isEagerInit = AccessController.doPrivileged(new PrivilegedAction<Boolean>() {
@Override
public Boolean run() {
return ((SmartFactoryBean<?>) factory).isEagerInit();
}
}, getAccessControlContext());
}
else {
isEagerInit = (factory instanceof SmartFactoryBean &&
((SmartFactoryBean<?>) factory).isEagerInit());
}
if (isEagerInit) {
getBean(beanName);
}
}
else {
//是一个多功能方法,如果这个bean没有被创建,则这个方法就去创建bean,如果这个bean创建了,则就去获取这个bean,是在AbstractBeanFactory这个工厂类中
getBean(beanName);
}
}
}
}
在这个方法中实现起功能的是getBean这个方法,这个方法是在AbstractBeanFactory工厂类中,在这个工厂类中有多个这个方法的重载方法如下:
public Object getBean(String name) throws BeansException {
return doGetBean(name, null, null, false);
}
@Override
public <T> T getBean(String name, Class<T> requiredType) throws BeansException {
return doGetBean(name, requiredType, null, false);
}
@Override
public Object getBean(String name, Object... args) throws BeansException {
return doGetBean(name, null, args, false);
}
public <T> T getBean(String name, Class<T> requiredType, Object... args) throws BeansException {
return doGetBean(name, requiredType, args, false);
}
在这几个重载的方法中都调用了doGetBean方法,其实现为:
protected <T> T doGetBean(
final String name, final Class<T> requiredType, final Object[] args, boolean typeCheckOnly)
throws BeansException {
final String beanName = transformedBeanName(name);
Object bean;
// Eagerly check singleton cache for manually registered singletons.获取单实例bean,紧接着如果bean为null,则开始创建,方法往下走
Object sharedInstance = getSingleton(beanName);
if (sharedInstance != null && args == null) {
if (logger.isDebugEnabled()) {
if (isSingletonCurrentlyInCreation(beanName)) {
logger.debug("Returning eagerly cached instance of singleton bean '" + beanName +
"' that is not fully initialized yet - a consequence of a circular reference");
}
else {
logger.debug("Returning cached instance of singleton bean '" + beanName + "'");
}
}
bean = getObjectForBeanInstance(sharedInstance, name, beanName, null);
}
else {
// Fail if we're already creating this bean instance:
// We're assumably within a circular reference.
if (isPrototypeCurrentlyInCreation(beanName)) {
throw new BeanCurrentlyInCreationException(beanName);
}
// Check if bean definition exists in this factory.
BeanFactory parentBeanFactory = getParentBeanFactory();
if (parentBeanFactory != null && !containsBeanDefinition(beanName)) {
// Not found -> check parent.
String nameToLookup = originalBeanName(name);
if (args != null) {
// Delegation to parent with explicit args.
return (T) parentBeanFactory.getBean(nameToLookup, args);
}
else {
// No args -> delegate to standard getBean method.
return parentBeanFactory.getBean(nameToLookup, requiredType);
}
}
没创建之前先标记一下bean已经被创建了,防止多个线程同时要创建这个bean
if (!typeCheckOnly) {
markBeanAsCreated(beanName);\\[ protected void markBeanAsCreated(String beanName) {
this.alreadyCreated.add(beanName);
}]
}
创建bean
try {
//拿到bean的定义信息
final RootBeanDefinition mbd = getMergedLocalBeanDefinition(beanName);
checkMergedBeanDefinition(mbd, beanName, args);
// Guarantee initialization of beans that the current bean depends on.、
//优先创建有依赖的bean
String[] dependsOn = mbd.getDependsOn();
if (dependsOn != null) {
for (String dependsOnBean : dependsOn) {
if (isDependent(beanName, dependsOnBean)) {
throw new BeanCreationException("Circular depends-on relationship between '" +
beanName + "' and '" + dependsOnBean + "'");
}
registerDependentBean(dependsOnBean, beanName);
getBean(dependsOnBean);
}
}
// Create bean instance.
if (mbd.isSingleton()) {
sharedInstance = getSingleton(beanName, new ObjectFactory<Object>() {
@Override
public Object getObject() throws BeansException {
try {
return createBean(beanName, mbd, args);
}
catch (BeansException ex) {
// Explicitly remove instance from singleton cache: It might have been put there
// eagerly by the creation process, to allow for circular reference resolution.
// Also remove any beans that received a temporary reference to the bean.
destroySingleton(beanName);
throw ex;
}
}
});
bean = getObjectForBeanInstance(sharedInstance, name, beanName, mbd);
}
else if (mbd.isPrototype()) {
// It's a prototype -> create a new instance.
Object prototypeInstance = null;
try {
beforePrototypeCreation(beanName);
prototypeInstance = createBean(beanName, mbd, args);
}
finally {
afterPrototypeCreation(beanName);
}
bean = getObjectForBeanInstance(prototypeInstance, name, beanName, mbd);
}
else {
String scopeName = mbd.getScope();
final Scope scope = this.scopes.get(scopeName);
if (scope == null) {
throw new IllegalStateException("No Scope registered for scope '" + scopeName + "'");
}
try {
Object scopedInstance = scope.get(beanName, new ObjectFactory<Object>() {
@Override
public Object getObject() throws BeansException {
beforePrototypeCreation(beanName);
try {
return createBean(beanName, mbd, args);
}
finally {
afterPrototypeCreation(beanName);
}
}
});
bean = getObjectForBeanInstance(scopedInstance, name, beanName, mbd);
}
catch (IllegalStateException ex) {
throw new BeanCreationException(beanName,
"Scope '" + scopeName + "' is not active for the current thread; " +
"consider defining a scoped proxy for this bean if you intend to refer to it from a singleton",
ex);
}
}
}
catch (BeansException ex) {
cleanupAfterBeanCreationFailure(beanName);
throw ex;
}
}
// Check if required type matches the type of the actual bean instance.
if (requiredType != null && bean != null && !requiredType.isAssignableFrom(bean.getClass())) {
try {
return getTypeConverter().convertIfNecessary(bean, requiredType);
}
catch (TypeMismatchException ex) {
if (logger.isDebugEnabled()) {
logger.debug("Failed to convert bean '" + name + "' to required type [" +
ClassUtils.getQualifiedName(requiredType) + "]", ex);
}
throw new BeanNotOfRequiredTypeException(name, requiredType, bean.getClass());
}
}
return (T) bean;
}
接下来就是getSingleton这个方法,其实现为
public Object getSingleton(String beanName, ObjectFactory<?> singletonFactory) {
Assert.notNull(beanName, "'beanName' must not be null");
//singletonObjects IOC容器创建的所有单实例都在这个map里
synchronized (this.singletonObjects) {
Object singletonObject = this.singletonObjects.get(beanName);
if (singletonObject == null) {
if (this.singletonsCurrentlyInDestruction) {
throw new BeanCreationNotAllowedException(beanName,
"Singleton bean creation not allowed while the singletons of this factory are in destruction " +
"(Do not request a bean from a BeanFactory in a destroy method implementation!)");
}
if (logger.isDebugEnabled()) {
logger.debug("Creating shared instance of singleton bean '" + beanName + "'");
}
beforeSingletonCreation(beanName);
boolean recordSuppressedExceptions = (this.suppressedExceptions == null);
if (recordSuppressedExceptions) {
this.suppressedExceptions = new LinkedHashSet<Exception>();
}
try {
//创建了bean
singletonObject = singletonFactory.getObject();
}
catch (BeanCreationException ex) {
if (recordSuppressedExceptions) {
for (Exception suppressedException : this.suppressedExceptions) {
ex.addRelatedCause(suppressedException);
}
}
throw ex;
}
finally {
if (recordSuppressedExceptions) {
this.suppressedExceptions = null;
}
afterSingletonCreation(beanName);
}
addSingleton(beanName, singletonObject);
}
return (singletonObject != NULL_OBJECT ? singletonObject : null);
}
}
将创建好的bean组件放到singletonObjects中
protected void addSingleton(String beanName, Object singletonObject) {
synchronized (this.singletonObjects) {
this.singletonObjects.put(beanName, (singletonObject != null ? singletonObject : NULL_OBJECT));
this.singletonFactories.remove(beanName);
this.earlySingletonObjects.remove(beanName);
this.registeredSingletons.add(beanName);
}
}
总结一下,IOC容器最底层的实现就是一个Map集合
这些核心方法的底层调用顺序为:
- 继续研究起IOC容器的用法
1) 给bean的级联属性赋值,一个类的属性是另一个类的对象,即就是这个作为属性的类的对象的属性的值
<property name="bookName" value="红楼梦"></property>
<property name="price" value="998"></property>
</bean>
<bean id="person01" class="com.fgy.bean.Person" p:lastName="王五" p:gender="女" p:salary="19999">
<property name="myBook" ref="book01"></property>
<!-- 会改掉外部全局的book bean的属性值,因为ref是一个严格的引用 -->
<property name="myBook.price" value="1111"></property>
<!-- 不想改外部的bean,可以自己在内部创建bean -->
</bean>
2)静态工厂和实例工厂
静态工厂:调用工厂方法,获取对象。不需要创建工厂对象,即就是创建一个静态方法
实例工厂:调用工厂方法,获取对象。需要创建工厂对象
<!--class 静态工厂的全类名
factory-method 静态工厂的工厂方法
constructor-arg标签设置工厂方法的参数
-->
<bean id="myBookStaticFactory01" class="com.fgy.factory.MyBookStaticFactory" factory-method="getBook" >
<constructor-arg value="发恼天空"></constructor-arg>
</bean>
<!--
1.先把实例工厂的bean创建进来
-->
<bean id="myBookInstanceFactory" class="com.fgy.factory.MyBookInstanceFactory" ></bean>
<!--2.创建book的bean,告诉bean用的是那个工厂,调用的是哪个工厂方法 -->
<bean id="book03" class="com.fgy.bean.Book" factory-bean="myBookInstanceFactory" factory-method="getBook">
<constructor-arg value="孙子兵法"></constructor-arg>
</bean>
3)配置factoryBean
实现factoryBean的实现类
package com.fgy.factory;
import org.springframework.beans.factory.FactoryBean;
import com.fgy.bean.Book;
public class MyBookImplFactoryBean implements FactoryBean<Book> {
//获取对象,工厂用来创建对象的方法
public Book getObject() throws Exception {
Book book = new Book();
book.setBookName("国志");
book.setAuthor("佚名");
book.setPrice(99.8);
return book;
}
//工厂返回创建对象的类型
public Class<?> getObjectType() {
return Book.class;
}
//创建的对象是否是单例对象
public boolean isSingleton() {
return false;
}
}
<!--1.创建实现了factoryBean接口的类
1).实现了factoryBean接口的所类,都被Spring认识的工厂类
2).这个接口就是Spring配置的
3).Spring创建什么对象都是照这个类指定的
4).这个工厂是一个单例的
2.让Spring知道这样的一个工厂类,即注册
id是用来获取创建对象的
3.正常来说IOC容器启动时会创建所的单实例对象,但是对于实现factoryBean接口的单实例类,却不会在IOC容器启动时,创建和获取
-->
<bean id="myBookImplFactoryBean" class="com.fgy.factory.MyBookImplFactoryBean"></bean>
4.<!--通过继承实现bean配置信息的重用 -->
<bean id="book04" class="com.fgy.bean.Book" p:bookName="亮剑" p:author="小海" p:price="998"></bean>
<bean id="book05" class="com.fgy.bean.Book" p:price="9999" parent="book04"></bean>
4)通过abstract属性创建一个模板bean,当abstract属性被赋值为true时,这个bean组件只能被继承,不能获取对象
<bean id="book04" class="com.fgy.bean.Book" p:bookName="亮剑" p:author="小海" p:price="998" abstract="true"></bean>
<bean id="book05" class="com.fgy.bean.Book" p:price="9999" parent="book04"></bean>
5)bean之间的依赖关系,作用:决定bean执行的顺序
bean默认的创建顺序,按照配置优先的原则如
<bean id="dog01" class="com.fgy.bean.Dog"></bean>
<bean id="cat01" class="com.fgy.bean.Cat"></bean>
顺序:先创建了dog,然后创建了cat
自定义创建顺序依赖于depends-on这个属性
<bean id="dog01" class="com.fgy.bean.Dog" depends-on="cat01"></bean>
<bean id="cat01" class="com.fgy.bean.Cat"></bean>
6)测试bean的作用域,分别创建单实例和多实例的bean
作用域:指的就是bean是否是单实例,作用域默认是单实例的,整个容器中,只有一个bean,
修改:scope属性
scope属性值:prototype:多实例:容器启动时不会创建对象,每次获取的时候,采取创建对象,我们可以在获取的时候指定值
singleton:默认的就是,单实例bean
request:在Web环境下,同一个request创建一个对象
session:在Web环境下,同一个session创建一个对象
7)创建带有生命周期方法的bean
生命周期:bean的生命周期是受IOC容器管理的,IOC容器决定了Bean的生命周期
单实例:
创建:容器启动时
初始化:指定初始化方法后,在容器启动后,就初始化了
销毁:容器销毁的时候调用,ConfigurableApplicationContext接口有close()方法
我们可以自己去定义一个方法,告诉Spring,哪些方法是用来初始化,哪些用来做销毁方法,这些方法不能有参数,bean的属性都设置好了,单实例情况下
多实例:
创建:获取的时候,创建对象
初始化:对象创建完成后,初始化
销毁:不会被调用。因为容器底层不会保存创建多实例的对象,容器关闭时只会销毁保存的对象,即容器启动时创建的单实例对象
8)bean的后置处理器
作用:就是在bean初始化前后可以进行一些预处理
实际上bean的后置处理器就是Spring的一个接口BeanPostProcessor,在这个接口中有两个方法
/**
* bean 通过构造器刚创建好的对象,也是我们要初始化的对象
* beanName bean的名字,就是在XML配置中的bean的id值
*/
Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException;
在初始化之前做一些操作
初始化方法
/**
* bean 通过初始化后的对象,对象的内容可能已经发生了改变
* beanName bean的名字,就是在XML配置中的bean的id值
*/
Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException;
在初始化之后做一些操作
9)引用外部属性文件
①这是在Spring配置文件的XML文件中直接写的,这样写的缺点就是把数据都写死了,没有体现动态性
<!--举例数据库连接池,用Spring来管理数据库连接池 -->
<bean id="comboPooledDataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
<property name="user" value="root"></property>
<property name="password" value="123456"></property>
<property name="jdbcUrl" value="jdbc:mysql://localhost:3306/test"></property>
<property name="driverClass" value="com.mysql.jdbc.Driver"></property>
</bean>
测试举例:
DataSource dataSource = (DataSource) ioc.getBean("comboPooledDataSource");
②所以我们要引用外部属性文件,体现数据的动态性
首先我们要用到context命名空间
<!--context命名空间,注册外部的properties文件,并设置访问路径,classpath是类目录下 -->
<context:property-placeholder location="classpath:JDBCConfig.properties"/>
<!--举例数据库连接池,用Spring来管理数据库连接池 -->
<bean id="comboPooledDataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
<property name="user" value="${jdbc.user}"></property>
<property name="password" value="${password}"></property>
<property name="jdbcUrl" value="${jdbcUrl}"></property>
<property name="driverClass" value="${driverClass}"></property>
注意点:为了区分配置文件中的值和Spring自己属性的值,一般建议给配置文件中的每个属性都加一个前缀
10)基于XML的自动装配
装配:解释过来就是给属性赋值的过程
①手动装配:在容器启动时,每个组件的属性值都为null,所以要为每个属性手动的在配置文件中配置值
<bean id="bookService" class="com.fgy.service.BookService">
<property name="bookDao" ref="bookDao"></property>
</bean>
<bean id="bookDao" class="com.fgy.dao.BookDao"></bean>
②自动装配:自动装配对基本数据类型无作用
autowire="default"、autowire="no":都是设置为不自动装配
autowire="byName":按照属性名作为bean的id,去容器中查找,并且赋值
注意:bean的id是按什么确定的呢?不是按照类中的属性名,而是按照类中属性的setter方法中set后面的单词,当找到的时候,对容器组件属性赋值,否则赋值为null
<bean id="bookService" class="com.fgy.service.BookService" autowire="byName"></bean>
<bean id="bookDao" class="com.fgy.dao.BookDao"></bean>
autowire="byType":按照属性的类型去容器中找到这个类型对于的bean,然后赋值。如果容器中有多个这个类型的bean,则这种方法就不能进行自动装配了
<bean id="bookService" class="com.fgy.service.BookService" autowire="byType"></bean>
<bean id="bookDao" class="com.fgy.dao.BookDao"></bean>
autowire="constructor":按照构造器进行装配
1.先去看构造器中参数的类型,按照类型进行装配
2.如果这个参数类型有多个组件,参数的变量名就会作为id继续查找
3.都找不到,则装配null
<bean id="bookService" class="com.fgy.service.BookService" autowire="constructor"></bean>
<bean id="bookDao" class="com.fgy.dao.BookDao"></bean>
11)SpEL
<bean id="person" class="com.atguigu.beans.Person">
<!--[SpEL测试I]在SpEL中使用字面量 -->
<property name="lastName" value="#{'张三'}"></property>
<!--[SpEL测试VI]在SpEL中使用运算符 -->
<property name="salary" value="#{9879.98*14}"></property>
<!--[SpEL测试II]在SpEL中引用其他bean #{对象的id}获取这个对象 -->
<property name="myBook" value="#{book01}"></property>
<!--[SpEL测试III]在SpEL中引用其他bean的某个属性值 -->
<property name="email" value="#{book01.author}"></property>
<!--[SpEL测试IV]在SpEL中调用非静态方法 -->
<property name="myMap" value="#{helloMap.getMap('key01')}"></property>
<!--[SpEL测试V]在SpEL中调用静态方法
#{T(全类名).方法名(参数列表)}
-->
<property name="list" value="#{T(com.atguigu.beans.HelloMap).getList(3)}"></property>
</bean>
<bean id="helloMap" class="com.atguigu.beans.HelloMap"></bean>
<bean id="book01" class="com.atguigu.beans.Book">
<property name="bookName" value="西游记"></property>
<property name="author" value="吴承恩@atguigu.com"></property>
</bean>