Spring源码阅读(一)浅析ApplicationContext
一、IOC与DI
学习Spring框架基本都是从IOC入手的, IOC(Inversion of Control)译为“控制反转”,基于这概念,可以衍生以下3个问题:
-
谁控制了谁?
-
控制了什么?
-
为什么是反转?
首先,回答第一个问题:传统模式下,我们通常使用new来创建对象。而使用Spring,我们调用getBean(String name, Class<?> type)就可以直接获得对象。看下面的例子,可以看出IOC容器控制了对象。
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class Client {
/**
* 获取spring的ioc容器,根据id获取对象
* ApplicationContext接口的三个实现类
* 1、ClassPathXmlApplicationContext:它可以加载类路径下的配置文件,要求配置文件必须在类路径下面。不在的话,无法加载
* 2、FileSystemXmlApplicationContext:它可以加载磁盘任意路径下的配置文件(必须有访问权限)
* 3、AnnotationConfigApplicationContext:它是读取注解创建容器的方法
* @param args
*/
public static void main(String[] args) {
//1、获取核心容器对象
ApplicationContext ac = new ClassPathXmlApplicationContext("bean.xml");
//2、根据id获取bean对象
IAccountService as=(IAccountService)ac.getBean("accountService");
IAccountDao ad = ac.getBean("accountDao",IAccountDao.class);
System.out.println(as);
System.out.println(ad);
}
}
那么,Spring容器控制了对象的什么呢?要回答这个问题,我们可以直接把IOC的定义搬过来:
所谓 IOC ,就是由 Spring IOC 容器来负责对象的生命周期和对象之间的关系。
为什么是反转?个人理解:通过new来创建对象,对象的生命周期以及对象间的依赖都由程序员自己控制,这是正转。而反转刚好倒过来,由容器来对对象进行管理,我们可以直接从容器中获得对象及其依赖。
DI(Dependency Injection),即“依赖注入”,是指容器在运行时决定组件之间的依赖,这是容器管理对象的另一个说法。它并非是为系统添加新的功能,而是提升了组件的重用性。
二、ApplicationContext
ApplicationContext是一个spring初始化的BeanFactory,在BeanFactory的基础上扩展了很多功能。
从ApplicationContext的类图中可以看到,它除了继承了BeanFactory之外,还继承了MessageSource、ResourceLoader、ApplicationEventPublisher和EnvironmentCapable接口。
2.1 MessageSource
MessageSource是一个解析消息的策略接口,它支持参数化与国际化。
/**
* 用于解析消息的策略接口,支持此类消息的参数化和国际化
*/
public interface MessageSource {
/**
* 解析消息,如果没找到code对应的消息就返回defaultMessage
*/
@Nullable
String getMessage(String code, @Nullable Object[] args, @Nullable String defaultMessage, Locale locale);
/**
* 解析消息,如果没找到code对应的消息就抛异常
*/
String getMessage(String code, @Nullable Object[] args, Locale locale) throws NoSuchMessageException;
/**
* 使用MessageSourceResolvable中的所有属性解析消息
* MessageSourceResolvable用code[]是啥意思呢???
*/
String getMessage(MessageSourceResolvable resolvable, Locale locale) throws NoSuchMessageException;
}
从MessageSource的源码可以看到,它定义了三个getMessage方法,根据Locale指定的地区解析code对应的消息,并用args参数替换消息中的占位符,最终返回解析后的消息。
为了帮助大家理解,我举个简单的例子:
- 在Spring配置文件spring.xml中定义properties文件的前缀
<bean id="messageSource"
class="org.springframework.context.support.ResourceBundleMessageSource">
<property name="basenames">
<list>
<value>messages</value>
</list>
</property>
</bean>
- 新建几个以messages为前缀的properties文件,内容和文件名如下:
message=我只是个{0} #messages_zh_CN.properties
message=I am just a {0} #messages_en.properties
- 解析并获取解析后的消息
ApplicationContext context=new ClassPathXmlApplicationContext("spring.xml");
String message1=context.getMessage("message", new String[]{"小孩"}, Locale.CHINA);
String message2=context.getMessage("message", new String[]{"child"}, Locale.ENGLISH);
System.out.println("message1=" + message1);
System.out.println("message2=" + message2);
- 打印输出
message1=我只是个小孩
message2=I am just a child
说明它具有解析参数化和国际化消息的功能。
2.2 BeanFactory
BeanFactory负责bean的创建和管理,从ApplicationContext的类图中可以看出,ApplicationContext并不是直接继承BeanFactory接口,而是继承了BeanFactory的子接口:ListableBeanFactory和HierarchicalBeanFactory。
2.2.1 BeanFactory
/**
* 创建、获取bean以及一些bean相关的其他操作
*/
public interface BeanFactory {
/**
* FactoryBean的前缀
*/
String FACTORY_BEAN_PREFIX = "&";
/**
* 返回name对应的bean对象
*/
Object getBean(String name) throws BeansException;
/**
* 返回指定type和name的bean对象
*/
<T> T getBean(String name, Class<T> requiredType) throws BeansException;
/**
* 返回现有对象或者用传入的args创建对象,非原型模式传入args会报错
*/
Object getBean(String name, Object... args) throws BeansException;
/**
* 只能返回指定类型的唯一对象,大于小于一个都报错
*/
<T> T getBean(Class<T> requiredType) throws BeansException;
/**
* 返回requiredType对应的bean对象或者用传入的args创建对象,非原型模式传入args会报错
*/
<T> T getBean(Class<T> requiredType, Object... args) throws BeansException;
/**
* 获取requiredType类型的ObjectProvider
*/
<T> ObjectProvider<T> getBeanProvider(Class<T> requiredType);
/**
* 获取requiredType类型的ObjectProvider
*/
<T> ObjectProvider<T> getBeanProvider(ResolvableType requiredType);
/**
* 是否存在名字为name的bean
*/
boolean containsBean(String name);
/**
* 名字为name的bean是否单例bean
*/
boolean isSingleton(String name) throws NoSuchBeanDefinitionException;
/**
* 是否原型bean
*/
boolean isPrototype(String name) throws NoSuchBeanDefinitionException;
/**
* 指定bean是否该类型
*/
boolean isTypeMatch(String name, ResolvableType typeToMatch) throws NoSuchBeanDefinitionException;
/**
* 指定bean是否该类型
*/
boolean isTypeMatch(String name, Class<?> typeToMatch) throws NoSuchBeanDefinitionException;
/**
* 获取指定bean的类型
*/
@Nullable
Class<?> getType(String name) throws NoSuchBeanDefinitionException;
/**
* 获取指定bean的别名
*/
String[] getAliases(String name);
}
,BeanFactory定义了对bean的基本操作。
2.2.2 ListableBeanFactory
BeanFactory定义的是对单个bean进行操作,而ListableBeanFactory对多个bean或者bean相关元素进行操作。
/**
* 可以枚举所有bean实例,而不是按客户端的请求逐个尝试按名称查找bean
*/
public interface ListableBeanFactory extends BeanFactory {
/**
* 是否存在指定name的BeanDefinition
*/
boolean containsBeanDefinition(String beanName);
/**
* 获取BeanFactory中BeanDefinition的数量
*/
int getBeanDefinitionCount();
/**
* 获取所有BeanDefinition的名字
*/
String[] getBeanDefinitionNames();
/**
* 获取指定类型的beanName数组
*/
String[] getBeanNamesForType(ResolvableType type);
/**
* 获取指定类型的beanName数组
*/
String[] getBeanNamesForType(@Nullable Class<?> type);
/**
* 获取指定类型的beanName数组,加了是否包含单例和非懒加载这几个限制
*/
String[] getBeanNamesForType(@Nullable Class<?> type, boolean includeNonSingletons, boolean allowEagerInit);
/**
* 获取指定类型的所有bean
*/
<T> Map<String, T> getBeansOfType(@Nullable Class<T> type) throws BeansException;
/**
* 获取指定类型的所有bean,加了是否包含单例和非懒加载这几个限制
*/
<T> Map<String, T> getBeansOfType(@Nullable Class<T> type, boolean includeNonSingletons, boolean allowEagerInit)
throws BeansException;
/**
* 获取指定注解类型的beanNames
*/
String[] getBeanNamesForAnnotation(Class<? extends Annotation> annotationType);
/**
* 获取指定类型的bean对象
*/
Map<String, Object> getBeansWithAnnotation(Class<? extends Annotation> annotationType) throws BeansException;
/**
* 根据beanName和指定注解类型获取注解bean对象
*/
@Nullable
<A extends Annotation> A findAnnotationOnBean(String beanName, Class<A> annotationType)
throws NoSuchBeanDefinitionException;
}
2.2.3 HierarchicalBeanFactory
HierarchicalBeanFactory定义了对BeanFactory分级的操作,比如返回父BeanFactory。
/**
* 定义了对BeanFactory层次结构的操作
*/
public interface HierarchicalBeanFactory extends BeanFactory {
/**
* Return the parent bean factory, or {@code null} if there is none.
*/
@Nullable
BeanFactory getParentBeanFactory();
/**
* 当前context(不包含祖先context)是否含有指定name的bean
*/
boolean containsLocalBean(String name);
}
2.3 ResourceLoader
ResourceLoader是一个资源加载的策略接口,说明有资源加载的功能,这是Spring容器的第一步操作:加载配置文件(applicationContext.xml)。因为配置文件定义了bean以及bean之间的关系,只有把配置文件加载进来才能创建、管理bean。
/**
* 资源加载的策略接口
*/
public interface ResourceLoader {
/** Pseudo URL prefix for loading from the class path: "classpath:". */
String CLASSPATH_URL_PREFIX = ResourceUtils.CLASSPATH_URL_PREFIX;
/**
* 返回指定路径的资源,这里只返回一个,说明不支持模式匹配
*/
Resource getResource(String location);
/**
* 返回统一的ClassLoader,而不是依赖于线程上下文ClassLoader
*/
@Nullable
ClassLoader getClassLoader();
}
2.3.1 ResourcePatternResolver
从名字就可以看出来,ResourcePatternResolver对ResourceLoader进行了扩展,它支持解析模式匹配的路径。
/**
* 解析模式匹配的路径,加载资源
*/
public interface ResourcePatternResolver extends ResourceLoader {
/**
* classpath url的前缀
*/
String CLASSPATH_ALL_URL_PREFIX = "classpath*:";
/**
* 解析模式匹配的路径,返回多个Resource
*/
Resource[] getResources(String locationPattern) throws IOException;
}
2.4 ApplicationEventPublisher
ApplicationEventPublisher定义了事件发布的功能,可以将事件发布给注册了此应用所有匹配的监听器。
/**
* 定义了事件发布功能
*/
@FunctionalInterface
public interface ApplicationEventPublisher {
default void publishEvent(ApplicationEvent event) {
publishEvent((Object) event);
}
/**
* 通知在此应用程序中注册的所有匹配的listeners
*/
void publishEvent(Object event);
}
2.5 EnvironmentCapable
EnvironmentCapable中只定义了getEnvironment的方法,向外界暴露了Environment接口。Environment是Spring运行时的环境,它包含了profiles和properties。
在一个软件的开发过程中,往往要将项目部署在不同的环境,比如测试环境、生产环境。profile正是起到了区分环境的作用,容器只会加载当前active的profile环境所定义的bean及其他配置。properties是指当前应用的属性,它可以在System环境变量、properties文件等多个地方进行配置。
三、小结
ApplicationContext是BeanFactory的子类,它包含了创建bean以及其他管理bean的功能,除此之外,还有下列功能:
- 解析参数化和国际化消息;
- 资源加载;
- 事件发布;
- 暴露环境。
推荐阅读
-
基于Spring注解的上下文初始化过程源码解析(一)
-
spring源码分析系列5:ApplicationContext的初始化与Bean生命周期
-
spring源码分析6: ApplicationContext的初始化与BeanDefinition的搜集入库
-
从一个简单的例子看spring ApplicationContext上下文隔离
-
Mybaits 源码解析 (十二)----- Mybatis的事务如何被Spring管理?Mybatis和Spring事务中用的Connection是同一个吗?
-
spring-boot-2.0.3不一样系列之源码篇 - run方法(三)之createApplicationContext,绝对有值得你看的地方
-
spring5 源码深度解析-----ApplicationContext容器refresh过程
-
Spring MVC源码(一) ----- 启动过程与组件初始化
-
Mybaits 源码解析 (十)----- 全网最详细,没有之一:Spring-Mybatis框架使用与源码解析
-
九、Spring之BeanFactory源码分析(一)