如果是《spring-beans》树,《spring-aop》是树枝,那spring-context就是森林。
ApplicationContext context=new ClassPathXmlApplicationContext("spring-context.xml");
AdminUser user = (AdminUser) context.getBean("admin");
System.out.println(user.getUsername());
通过以上例子,就通很容易的运行ApplicationContext,其代码量对比BeanFactory更简易。先来分析下
ApplicationContext继承多接口,分别是
MessageSource
封装JDK国际化和包含参数的信息的功能。
//resource_en_US.properties,资源文件内容如下
greeting.common=How are you!{0},today is {1}
greeting.morning = Good morning!{0},now is {1 time short}
greeting.afternoon = Good Afternoon!{0} now is {1 date long}
//resource_zh_ CN.properties,资源文件内容如下
greeting.common=你好!{0},今天是 {1}
greeting.morning = 早上好!{0},現在是{1 time short}
greeting.afternoon = 下午好!{0} 現在是 {1 date long}
ResourceBundle rb1 = ResourceBundle.getBundle("com/baobaotao/i18n/fmt_ resource", Locale.US);
ResourceBundle rb2 = ResourceBundle.getBundle("com/baobaotao/i18n/fmt_ resource", Locale.CHINA);
//格式化參數
Object[] params = {"John", new GregorianCalendar().getTime()};
//How are you!John,today is 1/9/07 4:11 PM
String str1 = new MessageFormat(rb1.getString("greeting.common"), Locale.US).format(params);
//早上好!John,现在是下午4:11
String str2 = new MessageFormat(rb2.getString("greeting.morning"), Locale.CHINA).format(params);
//下午好!John,现在是2007年1月9日
String str3 = new MessageFormat(rb2.getString("greeting.afternoon"), Locale.CHINA).format(params);
spring的用法及MessageSource接口的封裝如下
com/baobaotao/i18n/fmt_resource
//①获取MessageSource的Bean
MessageSource ms = (MessageSource)ctx.getBean("myResource");
Object[] params = {"John", new GregorianCalendar(). getTime()};
//②获取格式化的国际化信息
String str1 = ms.getMessage("greeting.common",params,Locale.US);
String str2 = ms.getMessage("greeting.morning",params,Locale.CHINA);
String str3 = ms.getMessage("greeting.afternoon",params,Locale.CHINA);
public interface MessageSource {
/**
* 解析code对应的信息进行返回,如果对应的code不能被解析则返回默认信息defaultMessage。
*
* @param 需要进行解析的code,对应资源文件中的一个属性名
* @param 需要用来替换code对应的信息中包含参数的内容,如:{0},{1,date},{2,time}
* @param defaultMessage 当对应code对应的信息不存在时需要返回的默认值
* @param locale 对应的Locale
* @return
*/
String getMessage(String code, Object[] args, String defaultMessage, Locale locale);
/**
* 解析code对应的信息进行返回,如果对应的code不能被解析则抛出异常NoSuchMessageException
*
* @param code 需要进行解析的code,对应资源文件中的一个属性名
* @param args 需要用来替换code对应的信息中包含参数的内容,如:{0},{1,date},{2,time}
* @param locale 对应的Locale
* @return
* @throws NoSuchMessageException 如果对应的code不能被解析则抛出该异常
*/
String getMessage(String code, Object[] args, Locale locale) throws NoSuchMessageException;
/**
* 通过传递的MessageSourceResolvable对应来解析对应的信息
*
* @param resolvable
* @param locale 对应的Locale
* @return
* @throws NoSuchMessageException 如不能解析则抛出该异常
*/
String getMessage(MessageSourceResolvable resolvable, Locale locale) throws NoSuchMessageException;
}
提供运行时环境变量
public interface EnvironmentCapable {
//提供context运行环境组件
//1. Jvm 运行环境
//2. java命令启动环境, 例如 -Dspring.profiles.active
//3. 提供加入更多profiles文件的接口
//@see StandardEnvironment
Environment getEnvironment();
}
Spring事件的监听
public interface ApplicationEventPublisher {
//向spring上下文广播一事件
void publishEvent(Object event);
}
用于配置文件的匹配查找加载(包括ResourceLoader)
public interface ResourcePatternResolver {
//根据路径查找资源
//1. 绝对路径:file:C:/test.dat
//2. 工作目录相对路径:WEB-INF/test.dat
//3. class路径: classpath:test.dat
//4. 根据提供的ProtocolResolver 查找资源
Resource getResource(String location);
//classpath*:开头的,在class路径使用ant表达式(默认)匹配后面资源文件
Resource[] getResources(String locationPattern) throws IOException;
}
提供容器标识,容器关系
public interface ApplicationContext {
//应用容器ID,自动生成
String getId();
//定义应用名称,默认为空。可继承扩展
String getApplicationName();
//应用容器名字,自动生成
String getDisplayName();
//应用容器启动时间
long getStartupDate();
//获取父级应用容器
ApplicationContext getParent();
//获取本容器的BeanFactory
AutowireCapableBeanFactory getAutowireCapableBeanFactory() throws IllegalStateException;
}
再来看下我们的主角ClassPathXmlApplicationContext,以下图缺失少许
为spring生命周期组件DefaultLifecycleProcessor提供服务
//spring通过DefaultLifecycleProcessor进行管理实现Lifecycle接口的bean
//1. 自动装载,通个context.finishRefresh 自动装载 SmartLifecycle中 isAutoStartup = true
//2. 手动装载,通过context.start 还没装载Lifecycle
//3. 装载顺序,根据SmartLifecycle中getPhase方法排序,如果是普通的Lifecycle则为0
//@see DefaultLifecycleProcessor
public interface Lifecycle {
//手动装载,还没装载Lifecycle
void start();
//手动关闭,还没关闭的还没卸载Lifecycle
//spring 也通 AutoCloseable 实现自动调用
void stop();
//是否启动
boolean isRunning();
}
提供一些原始组件的配置,为系统refresh提供服务
public interface ConfigurableApplicationContext {
//设置父级上下父
//让web,spring 做父子。 父容器中访问不到子对象
void setParent(@Nullable ApplicationContext parent);
//BeanFactoryPostProcessor可理解为利用BeanFactory做一些特殊操作
//context在refresh阶段的invokeBeanFactoryPostProcessors方法中使用
//意在执行:注解的解析
void addBeanFactoryPostProcessor(BeanFactoryPostProcessor postProcessor);
//增加context事件监听器
//@see ApplicationEventMulticaster 监听器管理者
//@see ApplicationEventPublisher conext事件推送者
void addApplicationListener(ApplicationListener> listener);
//加载配置文件路径解析器
void addProtocolResolver(ProtocolResolver resolver);
//加载完配置文件,之后Context开始做一些初始操作,简单刷新。相当于window的启动
//做刷新完操作后,开始正常代码调用,相当于启动完后正常的操作
void refresh() throws BeansException, IllegalStateException;
//向JVM注册勾子,让JVM关闭的时候可以关闭spring容器
void registerShutdownHook();
//应用容器的状态
//加载完配置文件之后,容器设设置为活跃状态 prepareRefresh
//close 不活跃状态 doClose
boolean isActive();
}
默认加载资源实现ResourceLoader类
//可以自定义ProtocolResolver,来解析过来的资源请求
//@see getResource
private final Set protocolResolvers = new LinkedHashSet<>(4);
//缓存 将X资源解析出不同type的类型对象,并根据type索引
//如果查找一type的在所有资源的内存对象,则可在resourceCaches根据获取。
private final Map, Map> resourceCaches = new ConcurrentHashMap<>(4);
声明Context核心组件,并定义context刷新流程
// "refresh" and "destroy" 操作的锁
private final Object startupShutdownMonitor = new Object();
//JVM勾子线程,默认JVM关机的时,触发spring context销毁
private Thread shutdownHook;
//提供路径资源查找器,默认支持 classpath* + ant表达式
private ResourcePatternResolver resourcePatternResolver;
//生命周期管理组件
//@DefaultLifecycleProcessor
private LifecycleProcessor lifecycleProcessor;
//国际化文件组件
//@see DelegatingMessageSource
private MessageSource messageSource;
//事件处理广播组件
//@see SimpleApplicationEventMulticaster
private ApplicationEventMulticaster applicationEventMulticaster;
//事件监听器缓存队列, 在ApplicationEventMulticaster启动后会加载进行
//@see registerListeners
private final Set> applicationListeners = new LinkedHashSet<>();
//refresh之前早期监听器, 把refresh之前的applicationListeners存入其中,不对外爆露
//@see prepareRefresh
private Set> earlyApplicationListeners;
//spring事件, 在ApplicationEventMulticaster启动后会推送
//@see registerListeners
//@see publishEvent
private Set earlyApplicationEvents;
对context中的refreshBeanFactory等环节重写,并提供BeanFactory两项重要配置
/beanFactory里的BeanDefinition是否可覆盖
//@see refreshBeanFactory
private Boolean allowBeanDefinitionOverriding;
//beanFactory允许循环依赖
//@see refreshBeanFactory
private Boolean allowCircularReferences;
//内置的beanFactory
private DefaultListableBeanFactory beanFactory;
对context的配置文件路径进行配置,提供路径解析等功能
//配置文件
private String[] configLocations;
配置文件的解析及较验功能
//是否配置文件开启dtd较验
private boolean validating = true;
提供根据xml的context入口类,它结合上面所有。以下是具体分析流程
public ClassPathXmlApplicationContext(String[] configLocations, boolean refresh, ApplicationContext parent)
throws BeansException {
//设置父亲容器
//并设置路径解析器 @see PathMatchingResourcePatternResolver
super(parent);
//设置配置文件路径
setConfigLocations(configLocations);
//开始刷新操作,相当于windows的系统重启
if (refresh) {
refresh();
}
}
refresh函数中描述了context各组件启动顺序。
public void refresh() throws BeansException, IllegalStateException {
synchronized (this.startupShutdownMonitor) {
//刷新前的准备工作
//1. 设置容器状态
//2. initPropertySources 自定义 Environment 中的 property
//3. Environment中property属性的必设值检查
//4. 把早期加入的applicationListeners 复制到 earlyApplicationListeners。
//5. 初始化earlyApplicationEvents,防止刷新期间的事件丢失,EventMulticaster组件初始化完后转发
prepareRefresh();
//刷新beanFactory
//1. 创建刷新beanFactory
//2. 配置beanFactory BeanDefinition加载参数, 是否允许覆盖,循环依赖
//3. 根据 Environment,解析配置文件路径,加载解析配置文件
//4. 将配置解析 BeanDefinition 加入取 beanFactory
ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
//beanFactory使用前的前置工作
//1. 配置beanFactory的classLoader
//2. 配置beanFactory的表达式解析器,StandardBeanExpressionResolver
//3. 配置beanFactory的注册属性编辑器, 用于搭建xml中 property,和JAVA-bean的真实类型转换
//4. 设置ApplicationContextAwareProcessor @see invokeAwareInterfaces,提供context组件注入方式
//5. 设置ApplicationListenerDetector 自动把加载实现的ApplicationListener的Bean 加入 Context的 applicationEventMulticaster 中
//6. 如果发现容器内有loadTimeWeaver的bean,设置LoadTimeWeaverAwareProcessor协助代理
//7. 注册environment组件到环境中
prepareBeanFactory(beanFactory);
try {
//提供给继承类个性化定义BeanFactory,类似prepareBeanFactory
postProcessBeanFactory(beanFactory);
//通过beanFactoryPostProcessor,执行一些BeanFactory注册与加载操作,例如
//1. 通spring注解的加载为BeanDefinition
//2. 自定义注解装配操作
invokeBeanFactoryPostProcessors(beanFactory);
//对beanFactory的BeanPostProcessor的Bean进行加载
registerBeanPostProcessors(beanFactory);
//初始化国际化组件
initMessageSource();
//初始化广播组件
initApplicationEventMulticaster();
//提供给继承类扩展其它组件,例如 web容器组件的创建
onRefresh();
//把ApplicationListeners注册入EventMulticaster, 并开始发送earlyApplicationEvents
registerListeners();
//结束BeanFactory的初始化
//1. 配置beanFactory的类型换换器ConversionService
//2. 配置beanFactory的内置字符bean属性解析器EmbeddedValueResolver
//3. 提前加载LoadTimeWeaverAware的bean,为其它被代理类提供transformers
//4. 冻结beanFactory的BeanDefinition配置功能,配置改动不实时生效
//5. 加载所有没有配置lazy-init的bean
finishBeanFactoryInitialization(beanFactory);
//结束容器的刷新
//1. 清除配置文件加载时的缓存
//2. 初始化生命周期组件DefaultLifecycleProcessor
//3. 给生命周期组件发送onRefresh事件
//4. 向广播组件Multicaster推送ContextRefreshedEvent事件
finishRefresh();
}
catch (BeansException ex) {
if (logger.isWarnEnabled()) {
logger.warn("Exception encountered during context initialization - " +
"cancelling refresh attempt: " + ex);
}
// Destroy already created singletons to avoid dangling resources.
destroyBeans();
// Reset 'active' flag.
cancelRefresh(ex);
// Propagate exception to caller.
throw ex;
}
finally {
// Reset common introspection caches in Spring's core, since we
// might not ever need metadata for singleton beans anymore...
resetCommonCaches();
}
}
}
spring接入context之后,再接入annotation就可以舍弃xml,用注解的方式,更加简化程序。
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(SpringConfig.class);
AdminUser user = (AdminUser) context.getBean("admin");
System.out.println(user.getUsername());
//用注解的方式替代xml
@Configuration
public class SpringConfig {
@Bean(name = "admin")
public AdminUser getAdminUser(){
AdminUser user = new AdminUser();
user.setUsername("gg");
return user;
}
}
下面通过一个示例分析AnnotationConfigApplicationContext的初始化过程:
public AnnotationConfigApplicationContext(Class>... annotatedClasses) {
//注解的条件判断器ConditionEvaluator该方法在初始化的时调用,当配置的类上有@Conditional注解并且返回false的时候,容器就不处理该类
//AnnotationConfigUtils.registerAnnotationConfigProcessors(this.registry)这个是关键,注册AnnotationConfigProcessor(ConfigurationClassPostProcessor)
this.reader = new AnnotatedBeanDefinitionReader(this);
//按类路径扫描注解bean的
this.scanner = new ClassPathBeanDefinitionScanner(this);
//相当于上面的configLocations
//完成了bean配置类本身的解析和注册
register(annotatedClasses);
//同上
refresh();
}
refresh方法在AbstractApplicationContext容器中实现,AnnotationConfigApplicationContext与ClassPathXmlApplicationContext容器都是通过调用其父类AbstractApplicationContext的refresh()函数启动整个IoC容器完成对Bean定义的载入。
ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
protected ConfigurableListableBeanFactory obtainFreshBeanFactory() {
refreshBeanFactory();
//获取beanfactory
ConfigurableListableBeanFactory beanFactory = getBeanFactory();
if (logger.isDebugEnabled()) {
logger.debug("Bean factory for " + getDisplayName() + ": " + beanFactory);
}
return beanFactory;
}
//与Xml不同:
//beanfactory已经在GenericApplicationContext构造函数中初始化了,refreshBeanFactory的逻辑
//在AbstractApplicationContext的实现类GenericApplicationContext中
protected final void refreshBeanFactory() throws IllegalStateException {
if (!this.refreshed.compareAndSet(false, true)) {
throw new IllegalStateException(
"GenericApplicationContext does not support multiple refresh attempts: just call 'refresh' once");
}
//生成一个序列化id
this.beanFactory.setSerializationId(getId());
}
这里使用AbstractApplicationContext. refreshBeanFactory()在不同实现容器中有点区别,如果是以xml方式配置bean,会使用AbstractRefreshableApplicationContext容器中的实现,该容器中实现xml配置文件定位,并通过BeanDefinition载入和解析xml配置文件。
而如果是注解的方式,则并没有解析项目包下的注解,而是通过在refresh()方法中执行ConfigurationClassPostProcessor后置处理器完成对bean的加载(AnnotationConfigUtils.registerAnnotationConfigProcessors中注册).
一个简单的例子:
public static void main(String[] args) {
AnnotationConfigApplicationContext annotationConfigApplicationContext = new AnnotationConfigApplicationContext(ImportConfig.class);
String[] beanDefinitionNames = annotationConfigApplicationContext.getBeanDefinitionNames();
for (int i = 0; i < beanDefinitionNames.length; i++) {
System.out.println("匹配的类"+beanDefinitionNames[i]);
}
}
@Import(User.class)
public class ImportConfig {
}
public class User {
}
对注解的处理都发生在AbstractApplicationContext -> refresh() -> invokeBeanFactoryPostProcessors(beanFactory) -> ConfigurationClassPostProcessor -> postProcessBeanDefinitionRegistry()方法中
public void processConfigBeanDefinitions(BeanDefinitionRegistry registry) {
List configCandidates = new ArrayList();
//拿出所有注册的类【现在应该只有ImportConfig】
String[] candidateNames = registry.getBeanDefinitionNames();
for (String beanName : candidateNames) {
BeanDefinition beanDef = registry.getBeanDefinition(beanName);
//如果拿出来的类已做过相应的处理则不做处理
if (ConfigurationClassUtils.isFullConfigurationClass(beanDef) ||
ConfigurationClassUtils.isLiteConfigurationClass(beanDef)) {
if (logger.isDebugEnabled()) {
logger.debug("Bean definition has already been processed as a configuration class: " + beanDef);
}
}
//从Bean工厂找出所有Configuratio类加入configCandidates列表中,所谓Configuratio类就是被@Configuration注解的类或者包含@Bean、@Component、@ComponentScan、@Import、@ImportResource注解的类。
else if (ConfigurationClassUtils.checkConfigurationClassCandidate(beanDef, this.metadataReaderFactory)) {
configCandidates.add(new BeanDefinitionHolder(beanDef, beanName));
}
}
// Return immediately if no @Configuration classes were found
//没有配置类则直接返回
if (configCandidates.isEmpty()) {
return;
}
// Sort by previously determined @Order value, if applicable
//根据@Order进行排序
Collections.sort(configCandidates, new Comparator() {
@Override
public int compare(BeanDefinitionHolder bd1, BeanDefinitionHolder bd2) {
int i1 = ConfigurationClassUtils.getOrder(bd1.getBeanDefinition());
int i2 = ConfigurationClassUtils.getOrder(bd2.getBeanDefinition());
return (i1 < i2) ? -1 : (i1 > i2) ? 1 : 0;
}
});
// Detect any custom bean name generation strategy supplied through the enclosing application context
SingletonBeanRegistry sbr = null;
if (registry instanceof SingletonBeanRegistry) {
sbr = (SingletonBeanRegistry) registry;
if (!this.localBeanNameGeneratorSet && sbr.containsSingleton(CONFIGURATION_BEAN_NAME_GENERATOR)) {
BeanNameGenerator generator = (BeanNameGenerator) sbr.getSingleton(CONFIGURATION_BEAN_NAME_GENERATOR);
this.componentScanBeanNameGenerator = generator;
this.importBeanNameGenerator = generator;
}
}
// Parse each @Configuration class
//创建解析器
ConfigurationClassParser parser = new ConfigurationClassParser(
this.metadataReaderFactory, this.problemReporter, this.environment,
this.resourceLoader, this.componentScanBeanNameGenerator, registry);
Set candidates = new LinkedHashSet(configCandidates);
Set alreadyParsed = new HashSet(configCandidates.size());
do {
parser.parse(candidates);
parser.validate();
//解析中发现新的要解析的类【User类被发现】
Set configClasses = new LinkedHashSet(parser.getConfigurationClasses());
//移动【ImportConfig】
configClasses.removeAll(alreadyParsed);
// Read the model and create bean definitions based on its content
if (this.reader == null) {
this.reader = new ConfigurationClassBeanDefinitionReader(
registry, this.sourceExtractor, this.resourceLoader, this.environment,
this.importBeanNameGenerator, parser.getImportRegistry());
}
//把新发现的【User】解析进容器
this.reader.loadBeanDefinitions(configClasses);
alreadyParsed.addAll(configClasses);
candidates.clear();
if (registry.getBeanDefinitionCount() > candidateNames.length) {
String[] newCandidateNames = registry.getBeanDefinitionNames();
Set oldCandidateNames = new HashSet(Arrays.asList(candidateNames));
Set alreadyParsedClasses = new HashSet();
for (ConfigurationClass configurationClass : alreadyParsed) {
alreadyParsedClasses.add(configurationClass.getMetadata().getClassName());
}
for (String candidateName : newCandidateNames) {
if (!oldCandidateNames.contains(candidateName)) {
BeanDefinition bd = registry.getBeanDefinition(candidateName);
if (ConfigurationClassUtils.checkConfigurationClassCandidate(bd, this.metadataReaderFactory) &&
!alreadyParsedClasses.contains(bd.getBeanClassName())) {
candidates.add(new BeanDefinitionHolder(bd, candidateName));
}
}
}
candidateNames = newCandidateNames;
}
}
while (!candidates.isEmpty());
//全都解析完成退出
// Register the ImportRegistry as a bean in order to support ImportAware @Configuration classes
if (sbr != null) {
if (!sbr.containsSingleton(IMPORT_REGISTRY_BEAN_NAME)) {
sbr.registerSingleton(IMPORT_REGISTRY_BEAN_NAME, parser.getImportRegistry());
}
}
if (this.metadataReaderFactory instanceof CachingMetadataReaderFactory) {
((CachingMetadataReaderFactory) this.metadataReaderFactory).clearCache();
}
}
结合spring-context 与 spring-beans可概括其生命周期如下图所示:
《Spring源码分析-MessageSource》
《Spring源码解析之PropertyPlaceholderHelper(占位符解析器)》
《Spring Bean的生命周期(非常详细)》