Spring 소스 코드 (4) Context 편의 AbstractApplication Context (상)

50947 단어 spring
이전 편 에 서 는 Spring 의 ContextLoader 류 가 Spring 응용 컨 텍스트 를 초기 화 할 때 refresh () 방법 을 사용 하 였 으 며, 이 방법의 구체 적 인 실현 은 AbstractApplication Context 류 에 있 습 니 다.
새로 고침 방법 이 어떤 일 을 했 는 지 살 펴 보 자.
@Override
public void refresh() throws BeansException, IllegalStateException {
	synchronized (this.startupShutdownMonitor) {
		// Prepare this context for refreshing.
		prepareRefresh();

		// Tell the subclass to refresh the internal bean factory.
		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) {
			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();
		}
	}
}

그 첫 번 째 집행 prepareRefresh() 방법 은 말 그대로 리 셋 을 위 한 준비 입 니 다!
/**
 1. Prepare this context for refreshing, setting its startup date and
 2. active flag as well as performing any initialization of property sources.
 */
protected void prepareRefresh() {
	this.startupDate = System.currentTimeMillis();
	this.closed.set(false);
	this.active.set(true);

	if (logger.isInfoEnabled()) {
		logger.info("Refreshing " + this);
	}

	// Initialize any placeholder property sources in the context environment
	initPropertySources();

	// Validate that all properties marked as required are resolvable
	// see ConfigurablePropertyResolver#setRequiredProperties
	getEnvironment().validateRequiredProperties();

	// Allow for the collection of early ApplicationEvents,
	// to be published once the multicaster is available...
	this.earlyApplicationEvents = new LinkedHashSet<ApplicationEvent>();
}
  • this.startupDate = System.currentTimeMillis(); context 의 시작 시간 을 설정 합 니 다.
  • this.closed.set(false); context 의 닫 기 표 지 를 false 로 설정 합 니 다.
  • this.active.set(true); context 의 활동 표 지 를 true 로 설정 합 니 다.
  • initPropertySources(); context environment (컨 텍스트 환경) 의 자리 차지 문자 속성 원본 을 초기 화 합 니 다.
  • getEnvironment().validateRequiredProperties(); '필수' 로 표 시 된 모든 속성 을 분석 할 수 있 는 지 검증
  • this.earlyApplicationEvents = new LinkedHashSet(); spring event 발표 및 감청 사례
  • 실행 prepareRefresh() 방법, 콘 솔 은 다음 로 그 를 인쇄 합 니 다.
    [2019-01-03 16:49:48  INFO org.springframework.web.context.support.XmlWebApplicationContext:583] Refreshing Root WebApplicationContext: startup date [Thu Jan 03 16:49:40 CST 2019]; root of context hierarchy
    

    두 번 째 실행 ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory(); 은 AbstractApplication Context 의 하위 클래스 를 내부 bean 공장 으로 갱신 합 니 다 (사실은 bean 공장 을 다시 만 드 는 것 입 니 다).
    protected ConfigurableListableBeanFactory obtainFreshBeanFactory() {
    	refreshBeanFactory();
    	ConfigurableListableBeanFactory beanFactory = getBeanFactory();
    	if (logger.isDebugEnabled()) {
    		logger.debug("Bean factory for " + getDisplayName() + ": " + beanFactory);
    	}
    	return beanFactory;
    }
    

    그 중에서 refreshBeanFactory() 방법 은 Abstract Refreshable Application Context 류 의 refreshBean Factory () 방법 을 호출 했 습 니 다. 지난 편의 클래스 관계 도 에서 우 리 는 Abstract Refreshable Application Context 류 가 Abstract Application Context 류 의 하위 클래스 라 는 것 을 알 고 있 습 니 다.
    AbstractRefreshable Application Context 의 refreshBeanFactory() 방법:
    @Override
    protected final void refreshBeanFactory() throws BeansException {
    	if (hasBeanFactory()) {
    		destroyBeans();
    		closeBeanFactory();
    	}
    	try {
    		DefaultListableBeanFactory beanFactory = createBeanFactory();
    		beanFactory.setSerializationId(getId());
    		customizeBeanFactory(beanFactory);
    		loadBeanDefinitions(beanFactory);
    		synchronized (this.beanFactoryMonitor) {
    			this.beanFactory = beanFactory;
    		}
    	}
    	catch (IOException ex) {
    		throw new ApplicationContextException("I/O error parsing bean definition source for " + getDisplayName(), ex);
    	}
    }
    

    이 방법의 논 리 는 위 에서 아래로 새로운 BeanFactory 를 초기 화하 고 정 의 된 Bean 에서 BeanFactory 까지 불 러 오 는 것 입 니 다.
    <bean id="sqlSession" class="org.mybatis.spring.SqlSessionTemplate">
    	<constructor-arg index="0" name="sqlSessionFactory" ref="sqlSessionFactory"/>
    </bean>
    

    Bean 을 BeanFactory 에 불 러 오 려 면 xml 를 옮 겨 다 니 며 해석 합 니 다. 어떤 xml 을 해석 합 니까?
    코드 를 따라 가면 loadBeanDefinitions 방법 이 XmlWebApplication Context 류 의 loadBeanDefinitions 방법 을 호출 하 는 것 을 방금 발견 할 수 있 습 니 다.
    @Override
    protected void loadBeanDefinitions(DefaultListableBeanFactory beanFactory) throws BeansException, IOException {
    	// Create a new XmlBeanDefinitionReader for the given BeanFactory.
    	XmlBeanDefinitionReader beanDefinitionReader = new XmlBeanDefinitionReader(beanFactory);
    
    	// Configure the bean definition reader with this context's
    	// resource loading environment.
    	beanDefinitionReader.setEnvironment(getEnvironment());
    	beanDefinitionReader.setResourceLoader(this);
    	beanDefinitionReader.setEntityResolver(new ResourceEntityResolver(this));
    
    	// Allow a subclass to provide custom initialization of the reader,
    	// then proceed with actually loading the bean definitions.
    	initBeanDefinitionReader(beanDefinitionReader);
    	loadBeanDefinitions(beanDefinitionReader);
    }
    

    그 중에서 재 추적 loadBeanDefinitions() 방법 소스 코드:
    protected void loadBeanDefinitions(XmlBeanDefinitionReader reader) throws IOException {
    	String[] configLocations = getConfigLocations();
    	if (configLocations != null) {
    		for (String configLocation : configLocations) {
    			reader.loadBeanDefinitions(configLocation);
    		}
    	}
    }
    

    여기 getConfigLocations() 방법 은 부모 클래스 Abstract Refreshable ConfigApplication Context 의 configLocations 변 수 를 가 져 왔 습 니 다. 지난 편 에 서 는 contextConfigLocation 의 설정 (웹. xml 에서 설정) 을 말 했 고 설정 한 value 값 이 Abstract Refreshable ConfigApplication Context 류 의 configLocations 변수 에 설정 되 었 다 고 말 했 습 니 다. 이것 이 맞 잖 아 요!!
    <context-param>
    	<param-name>contextConfigLocation</param-name>
    	<param-value>classpath*:/spring/*.xml
    
    

    프로젝트 에 불 러 올 xml 파일 의 위 치 를 설정 합 니 다. 이 xml 파일 들 은 위 loadBeanDefinitions 방법의 for 순환 을 통 해 일일이 불 러 오기 분석 을 하고 Bean 을 초기 화 하 며 Bean 인 스 턴 스 를 BeanFactory 에 두 었 습 니 다. BeanFactory 는 마지막 으로 Context 관리 에 맡 깁 니 다!
    실행 loadBeanDefinitions(XmlBeanDefinitionReader reader) 방법 은 다음 로 그 를 입력 합 니 다 (로그 분석 원 과 결합 하여 더 잘 알 수 있 습 니 다).
    [2019-01-09 15:10:52  INFO org.springframework.beans.factory.xml.XmlBeanDefinitionReader:317] Loading XML bean definitions from file [D:\workspace\.metadata\.plugins\org.eclipse.wst.server.core\tmp2\wtpwebapps\accplatform-job\WEB-INF\classes\spring\applicationContext-msg.xml]
    [2019-01-09 15:10:57  INFO org.springframework.beans.factory.xml.XmlBeanDefinitionReader:317] Loading XML bean definitions from file [D:\workspace\.metadata\.plugins\org.eclipse.wst.server.core\tmp2\wtpwebapps\accplatform-job\WEB-INF\classes\spring\applicationContext-redis.xml]
    [2019-01-09 15:10:57  INFO org.springframework.beans.factory.xml.XmlBeanDefinitionReader:317] Loading XML bean definitions from file [D:\workspace\.metadata\.plugins\org.eclipse.wst.server.core\tmp2\wtpwebapps\accplatform-job\WEB-INF\classes\spring\applicationContext.xml]
    

    세 번 째 refresh () 에서 prepareBeanFactory(beanFactory); 방법 을 실행 합 니 다. 아래 Spring 컨 텍스트 에서 BeanFactory 를 사용 하여 준비 하기 위해 소스 코드 를 확인 합 니 다.
    protected void prepareBeanFactory(ConfigurableListableBeanFactory beanFactory) {
    	// Tell the internal bean factory to use the context's class loader etc.
    	beanFactory.setBeanClassLoader(getClassLoader());
    	beanFactory.setBeanExpressionResolver(new StandardBeanExpressionResolver(beanFactory.getBeanClassLoader()));
    	beanFactory.addPropertyEditorRegistrar(new ResourceEditorRegistrar(this, getEnvironment()));
    
    	// Configure the bean factory with context callbacks.
    	beanFactory.addBeanPostProcessor(new ApplicationContextAwareProcessor(this));
    	beanFactory.ignoreDependencyInterface(EnvironmentAware.class);
    	beanFactory.ignoreDependencyInterface(EmbeddedValueResolverAware.class);
    	beanFactory.ignoreDependencyInterface(ResourceLoaderAware.class);
    	beanFactory.ignoreDependencyInterface(ApplicationEventPublisherAware.class);
    	beanFactory.ignoreDependencyInterface(MessageSourceAware.class);
    	beanFactory.ignoreDependencyInterface(ApplicationContextAware.class);
    
    	// BeanFactory interface not registered as resolvable type in a plain factory.
    	// MessageSource registered (and found for autowiring) as a bean.
    	beanFactory.registerResolvableDependency(BeanFactory.class, beanFactory);
    	beanFactory.registerResolvableDependency(ResourceLoader.class, this);
    	beanFactory.registerResolvableDependency(ApplicationEventPublisher.class, this);
    	beanFactory.registerResolvableDependency(ApplicationContext.class, this);
    
    	// Register early post-processor for detecting inner beans as ApplicationListeners.
    	beanFactory.addBeanPostProcessor(new ApplicationListenerDetector(this));
    
    	// Detect a LoadTimeWeaver and prepare for weaving, if found.
    	if (beanFactory.containsBean(LOAD_TIME_WEAVER_BEAN_NAME)) {
    		beanFactory.addBeanPostProcessor(new LoadTimeWeaverAwareProcessor(beanFactory));
    		// Set a temporary ClassLoader for type matching.
    		beanFactory.setTempClassLoader(new ContextTypeMatchClassLoader(beanFactory.getBeanClassLoader()));
    	}
    
    	// Register default environment beans.
    	if (!beanFactory.containsLocalBean(ENVIRONMENT_BEAN_NAME)) {
    		beanFactory.registerSingleton(ENVIRONMENT_BEAN_NAME, getEnvironment());
    	}
    	if (!beanFactory.containsLocalBean(SYSTEM_PROPERTIES_BEAN_NAME)) {
    		beanFactory.registerSingleton(SYSTEM_PROPERTIES_BEAN_NAME, getEnvironment().getSystemProperties());
    	}
    	if (!beanFactory.containsLocalBean(SYSTEM_ENVIRONMENT_BEAN_NAME)) {
    		beanFactory.registerSingleton(SYSTEM_ENVIRONMENT_BEAN_NAME, getEnvironment().getSystemEnvironment());
    	}
    }
    

    소스 코드 를 통 해 알 수 있 듯 이 이 방법 은 기본적으로 BeanFactory 의 데 이 터 를 초기 화 하 는 것 입 니 다. BeanFactory 의 배치 화 (Configure) 과정 이 라 고도 부 릅 니 다. BeanFctory 는 Spring IOC 의 중점 류 (약간 복잡 합 니 다) 에 속 합 니 다. 그 다음 에 Spring IOC 의 측면 에서 Spring 의 Bean 공장 시 리 즈 를 전면적으로 해석 해 야 합 니 다. 지금 은 Spring Context 의 측면 에서 해석 하기 때문에 간단하게 가 져 가 야 합 니 다.
    그 네 번 째 refresh () 에서 실행 postProcessBeanFactory(beanFactory); 은 사실 Abstract Refreshable Webapplication Context 류 의 방법 을 실 행 했 습 니 다.
    @Override
    protected void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) {
    	beanFactory.addBeanPostProcessor(new ServletContextAwareProcessor(this.servletContext, this.servletConfig));
    	beanFactory.ignoreDependencyInterface(ServletContextAware.class);
    	beanFactory.ignoreDependencyInterface(ServletConfigAware.class);
    
    	WebApplicationContextUtils.registerWebApplicationScopes(beanFactory, this.servletContext);
    	WebApplicationContextUtils.registerEnvironmentBeans(beanFactory, this.servletContext, this.servletConfig);
    }
    

    여기 서 중요 한 것 은 다음 두 줄 의 코드 입 니 다. 웹 의 특정한 역할 영역 ("request", "session", "globalSession", "application") 부터 BeanFactory 까지 웹 응용 프로그램 Context 를 편리 하 게 사용 할 수 있 습 니 다. 역할 영역 은 여기 서 먼저 이렇게 이해 할 수 있 습 니 다 (Bean 의 생 성 측면 에서 이해).
  • request 는 모든 Http 에 Bean 인 스 턴 스 를 만 들 기 를 요청 합 니 다
  • session 은 Http 가 요청 한 세 션 에 Bean 인 스 턴 스 를 만 듭 니 다
  • globalSession 은 전역 Http 세 션 에 Bean 인 스 턴 스 를 만 듭 니 다
  • application 은 웹 응용 프로그램 을 통합 하기 위해 Bean 인 스 턴 스 를 만 듭 니 다
  • 그 5 refresh () 에서 실행 invokeBeanFactoryPostProcessors(beanFactory); 의 소스 코드 는 다음 과 같다.
    /**
     * Instantiate and invoke all registered BeanFactoryPostProcessor beans,
     * respecting explicit order if given.
     * 

    Must be called before singleton instantiation. */

    protected void invokeBeanFactoryPostProcessors(ConfigurableListableBeanFactory beanFactory) { PostProcessorRegistrationDelegate.invokeBeanFactoryPostProcessors(beanFactory, getBeanFactoryPostProcessors()); // Detect a LoadTimeWeaver and prepare for weaving, if found in the meantime // (e.g. through an @Bean method registered by ConfigurationClassPostProcessor) if (beanFactory.getTempClassLoader() == null && beanFactory.containsBean(LOAD_TIME_WEAVER_BEAN_NAME)) { beanFactory.addBeanPostProcessor(new LoadTimeWeaverAwareProcessor(beanFactory)); beanFactory.setTempClassLoader(new ContextTypeMatchClassLoader(beanFactory.getBeanClassLoader())); } }

    소스 코드 주석 을 통 해 알 수 있 듯 이 이 방법의 호출 은 모든 singleton 이 예화 되 기 전에 이 방법의 이름 을 통 해 알 수 있 듯 이 IOC 의 Processor 역할 (사실은 Bean 공장 의 프로세서) 을 도입 했다. Bean 의 프로세서 아래 에서 대량의 복잡 한 작업 을 했 고 구체 적 으로 어떤 작업 을 했 는 지 알 수 있다. Context 편 시스템 은 여기 서 깊이 분석 하지 않 는 다.
    그 6 refresh () 에서 실행 registerBeanPostProcessors(beanFactory); 의 원본 코드 는 다음 과 같다.
    /**
     * Instantiate and invoke all registered BeanPostProcessor beans,
     * respecting explicit order if given.
     * 

    Must be called before any instantiation of application beans. */

    protected void registerBeanPostProcessors(ConfigurableListableBeanFactory beanFactory) { PostProcessorRegistrationDelegate.registerBeanPostProcessors(beanFactory, this); }

    아니면 빈 팩 토리 에 대한 처리!!refresh () 방법 은 두 번 째 부터 여섯 번 째 까지 모두 BeanFactory 에 관 한 처리 입 니 다.
    다음은 AbstractApplication Context 의 refresh() 를 해석 합 니 다.

    좋은 웹페이지 즐겨찾기