spring-context 容器上下文

前言

如果是《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更简易。先来分析下

spring-context 容器上下文_第1张图片

ApplicationContext继承多接口,分别是

  1. ​ListableBeanFactory, HierarchicalBeanFactory,BeanFactory 提供容器功能
  2.  EnvironmentCapable 提供运行时环境变量
  3.  MessageSource 用于支持信息的国际化和包含参数的信息的替换
  4.  ApplicationEventPublisher 有于Spring事件的监听
  5.  ResourcePatternResolver, ResourceLoader 用于配置文件的匹配查找加载

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;
    }

EnvironmentCapable

提供运行时环境变量

public interface EnvironmentCapable {

	//提供context运行环境组件
	//1. Jvm 运行环境
	//2. java命令启动环境, 例如 -Dspring.profiles.active
    //3. 提供加入更多profiles文件的接口
    //@see StandardEnvironment
	Environment getEnvironment();

}

ApplicationEventPublisher

Spring事件的监听

public interface ApplicationEventPublisher {
	//向spring上下文广播一事件  
	void publishEvent(Object event);
}

ResourcePatternResolver

用于配置文件的匹配查找加载(包括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;

}

ApplicationContext

   提供容器标识,容器关系

public interface ApplicationContext  {

	//应用容器ID,自动生成
	String getId();

	//定义应用名称,默认为空。可继承扩展
	String getApplicationName();

	//应用容器名字,自动生成
	String getDisplayName();

    //应用容器启动时间
	long getStartupDate();

	//获取父级应用容器
	ApplicationContext getParent();

	//获取本容器的BeanFactory
	AutowireCapableBeanFactory getAutowireCapableBeanFactory() throws IllegalStateException;

}

再来看下我们的主角ClassPathXmlApplicationContext,以下图缺失少许

spring-context 容器上下文_第2张图片

Lifecycle

为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();

}

ConfigurableApplicationContext

提供一些原始组件的配置,为系统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();

}

DefaultResourceLoader

默认加载资源实现ResourceLoader类

//可以自定义ProtocolResolver,来解析过来的资源请求
//@see getResource
private final Set protocolResolvers = new LinkedHashSet<>(4);

//缓存 将X资源解析出不同type的类型对象,并根据type索引
//如果查找一type的在所有资源的内存对象,则可在resourceCaches根据获取。
private final Map, Map> resourceCaches = new ConcurrentHashMap<>(4);

AbstractApplicationContext

声明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;

AbstractRefreshableApplicationContext

对context中的refreshBeanFactory等环节重写,并提供BeanFactory两项重要配置

/beanFactory里的BeanDefinition是否可覆盖
//@see refreshBeanFactory
private Boolean allowBeanDefinitionOverriding;

//beanFactory允许循环依赖
//@see refreshBeanFactory
private Boolean allowCircularReferences;

//内置的beanFactory
private DefaultListableBeanFactory beanFactory;

AbstractRefreshableConfigApplicationContext

对context的配置文件路径进行配置,提供路径解析等功能

//配置文件
private String[] configLocations;

AbstractXmlApplicationContext

配置文件的解析及较验功能

//是否配置文件开启dtd较验
private boolean validating = true;

ClassPathXmlApplicationContext

提供根据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();

              }

         }

     }

AnnotationConfigApplicationContext

    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-context 容器上下文_第3张图片

主要参考

《Spring源码分析-MessageSource》

《Spring源码解析之PropertyPlaceholderHelper(占位符解析器)》

《Spring Bean的生命周期(非常详细)》

你可能感兴趣的:(#,spring,spring-context,spring上下文)