`

spring源码学习系列2.1-从xml到document

 
阅读更多
本章探讨从xml到document的过程-xml文件生成document类

从xml到document

从document到beanDefinition

从beanDefiniton到instance


在<spring源码学习系列2.2-从document到beanDefinition>中,

"
三是注册到beanDefinition容器
// Register the final decorated instance.  
BeanDefinitionReaderUtils.registerBeanDefinition(bdHolder, getReaderContext().getRegistry());  

"

对这里getReaderContext().getRegistry()有些疑惑,getReaderContext()是什么?getRegistry()是什么?限于篇幅,这里进行释疑。

在堆栈从xml到document的过程中,可以得到上面疑惑的解答。

对于从document到beanDefinition以及beanDefinition到instance的过程,请参见:
<spring源码学习系列2.2-从document到beanDefinition>
<spring源码学习系列2-从beanDefinition到instance>


涉及的源码类:
org.springframework.web.context.ContextLoaderListener

org.springframework.web.context.ContextLoader

org.springframework.web.context.ConfigurableWebApplicationContext



xml到document

web项目启动spring容器的入口在web.xml中,
<listener>
		<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
	</listener>


ContextLoaderListener源码:
http://grepcode.com/file/repository.springsource.com/org.springframework/org.springframework.web/3.2.2/org/springframework/web/context/ContextLoaderListener.java

该类的部分信息为:
ContextLoaderListener extends ContextLoader implements ServletContextListener
执行初始化方法的入口:
/**
	 * Initialize the root web application context.
	 */
	public void contextInitialized(ServletContextEvent event) {
		this.contextLoader = createContextLoader();
		if (this.contextLoader == null) {
			this.contextLoader = this;
		}
		this.contextLoader.initWebApplicationContext(event.getServletContext());
	}


contextLoader源码:
http://grepcode.com/file/repository.springsource.com/org.springframework/org.springframework.web/3.2.2/org/springframework/web/context/ContextLoader.java

其将初始化委托给contextLoader.initWebApplicationContext,
// Store context in local instance variable, to guarantee that
			// it is available on ServletContext shutdown.
			if (this.context == null) {
                                // 创建webApplicationContext上下文环境
				this.context = createWebApplicationContext(servletContext);
			}
			if (this.context instanceof ConfigurableWebApplicationContext) {
				ConfigurableWebApplicationContext cwac = (ConfigurableWebApplicationContext) this.context;
				if (!cwac.isActive()) {
					// The context has not yet been refreshed -> provide services such as
					// setting the parent context, setting the application context id, etc
					if (cwac.getParent() == null) {
						// The context instance was injected without an explicit parent ->
						// determine parent for root web application context, if any.
                                                // 加载父上下文
						ApplicationContext parent = loadParentContext(servletContext);
						cwac.setParent(parent);
					}
                                    // 初始化webApplicationContext上下文环境
                                    configureAndRefreshWebApplicationContext(cwac, servletContext);
				}
			}



创建XmlWebApplicationContext类容器

首先实例化contextClass:XmlWebApplicationContext-创建webApplicationContext的上下文环境
protected WebApplicationContext createWebApplicationContext(ServletContext sc) {
		Class<?> contextClass = determineContextClass(sc);
		if (!ConfigurableWebApplicationContext.class.isAssignableFrom(contextClass)) {
			throw new ApplicationContextException("Custom context class [" + contextClass.getName() +
					"] is not of type [" + ConfigurableWebApplicationContext.class.getName() + "]");
		}
		return (ConfigurableWebApplicationContext) BeanUtils.instantiateClass(contextClass);
	}


protected Class<?> determineContextClass(ServletContext servletContext) {
		String contextClassName = servletContext.getInitParameter(CONTEXT_CLASS_PARAM);
		if (contextClassName != null) {
			try {
				return ClassUtils.forName(contextClassName, ClassUtils.getDefaultClassLoader());
			}
			catch (ClassNotFoundException ex) {
				throw new ApplicationContextException(
						"Failed to load custom context class [" + contextClassName + "]", ex);
			}
		}
		else {
			contextClassName = defaultStrategies.getProperty(WebApplicationContext.class.getName());
			try {
				return ClassUtils.forName(contextClassName, ContextLoader.class.getClassLoader());
			}
			catch (ClassNotFoundException ex) {
				throw new ApplicationContextException(
						"Failed to load default context class [" + contextClassName + "]", ex);
			}
		}
	}

在determineContextClass方法中,
String contextClassName = servletContext.getInitParameter(CONTEXT_CLASS_PARAM);

开发人员可以自定义配置CONTEXT_CLASS_PARAM=contextClass参数,定义自己扩展的applicationContext作为the root WebApplicationContext-此处可以看做spring的一种扩展

如果没有配置,则从默认配置spring-web.jar/org.springframework.web.context/ContextLoader.properties中读取,
org.springframework.web.context.WebApplicationContext=org.springframework.web.context.support.XmlWebApplicationContext


然后调用contextLoader.configureAndRefreshWebApplicationContext-初始化上下文环境
该方法里面准备上面创建的上下文环境类(ConfigurableWebApplicationContext)的环境,并委托ConfigurableWebApplicationContext做进一步初始化操作
wac.refresh();



contextLoader
-------------------------------------------------------承上启下的分割线----------------------------------------------
applicationContext
1.这里主要分析生成beanFactory部分


这里开始进入加载spring的实质性入口

abstractApplicationContext源码:
http://grepcode.com/file/repository.springsource.com/org.springframework/org.springframework.context/3.2.2/org/springframework/context/support/AbstractApplicationContext.java

AbstractRefreshableApplicationContext源码:
http://grepcode.com/file/repository.springsource.com/org.springframework/org.springframework.context/3.2.2/org/springframework/context/support/AbstractRefreshableApplicationContext.java/

abstractApplicationContext.refresh:(abstractApplicationContext implements ConfigurableWebApplicationContext)
public void refresh() throws BeansException, IllegalStateException {
		synchronized (this.startupShutdownMonitor) {
			// Prepare this context for refreshing.
			prepareRefresh();
//解析xml生成beanDefinition并注册
			// 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;
			}
		}
	}


文章主要分析xml到document的部分(实例化beanFactory的一部分),所以主要关注
// Tell the subclass to refresh the internal bean factory.
			ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();


这行包括主要有2个操作:
从xml到document

从document到beanDefinition
见:<spring源码学习系列2.2-从document到beanDefinition>


该方法里面,调用子类AbstractRefreshableApplicationContext.refreshBeanFactory()初始化beanFactory:
/**
	 * This implementation performs an actual refresh of this context's underlying
	 * bean factory, shutting down the previous bean factory (if any) and
	 * initializing a fresh bean factory for the next phase of the context's lifecycle.
	 */
	@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);
		}
	}

该方法中,可分为2部分:

创建DefaultListableBeanFactory(parentBeanFactory)

首先创建beanFactory:
DefaultListableBeanFactory beanFactory = createBeanFactory();


AbstractRefreshableApplicationContext.createBeanFactory()
protected DefaultListableBeanFactory createBeanFactory() {
		return new DefaultListableBeanFactory(getInternalParentBeanFactory());
	}


abstractApplicationContext.getInternalParentBeanFactory()
protected BeanFactory getInternalParentBeanFactory() {
		return (getParent() instanceof ConfigurableApplicationContext) ?
				((ConfigurableApplicationContext) getParent()).getBeanFactory() : getParent();
	}





其次就是进入到初始化doc的代码-从这里开始可以解答开头提出的问题
loadBeanDefinitions(beanFactory);


创建XmlBeanDefinitionReader(beanFactory)

具体调用子类xmlWebApplicationContext.loadBeanDefinitions
/**
	 * Loads the bean definitions via an XmlBeanDefinitionReader.
	 * @see org.springframework.beans.factory.xml.XmlBeanDefinitionReader
	 * @see #initBeanDefinitionReader
	 * @see #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);
	}



初始化XmlBeanDefinitionReader,由此类读取xml文件。将beanFactory传入XmlBeanDefinitionReader,beanFactory实现了BeanDefinitionRegistry,开头的问题getRegistry()实际得到的就是beanFactory,注册beanDefinition实际上就是注册到beanFactory中。beanFactory为所有ioc容器的顶级接口。
public XmlBeanDefinitionReader(BeanDefinitionRegistry registry) {
		super(registry);
	}


xmlWebApplicationContext#loadBeanDefinitions
protected void loadBeanDefinitions(XmlBeanDefinitionReader reader) throws IOException {
		String[] configLocations = getConfigLocations();
		if (configLocations != null) {
			for (String configLocation : configLocations) {
				reader.loadBeanDefinitions(configLocation);
			}
		}
	}



applicationContext
-------------------------------------------------------承上启下的分割线----------------------------------------------
xmlBeanDefinitionReader
1.调用documentLoader生成doc
2.调用BeanDefinitionDocumentReader解析doc并生成beanDefinition

委托xmlBeanDefinitionReader读取xml,解析成beanDefinition,并注册到传入的beanFactory中

最终通过xmlBeanDefinitionReader.doLoadBeanDefinitions加载xml(Actually load bean definitions from the specified XML file)
xmlBeanDefinitionReader#doLoadBeanDefinitions
/**
	 * Actually load bean definitions from the specified XML file.
	 * @param inputSource the SAX InputSource to read from
	 * @param resource the resource descriptor for the XML file
	 * @return the number of bean definitions found
	 * @throws BeanDefinitionStoreException in case of loading or parsing errors
	 */
	protected int doLoadBeanDefinitions(InputSource inputSource, Resource resource)
			throws BeanDefinitionStoreException {
		try {
			int validationMode = getValidationModeForResource(resource);
			Document doc = this.documentLoader.loadDocument(
					inputSource, getEntityResolver(), this.errorHandler, validationMode, isNamespaceAware());
			return registerBeanDefinitions(doc, resource);
		}
		catch (BeanDefinitionStoreException ex) {
			throw ex;
		}
		catch (SAXParseException ex) {
			throw new XmlBeanDefinitionStoreException(resource.getDescription(),
					"Line " + ex.getLineNumber() + " in XML document from " + resource + " is invalid", ex);
		}
		catch (SAXException ex) {
			throw new XmlBeanDefinitionStoreException(resource.getDescription(),
					"XML document from " + resource + " is invalid", ex);
		}
		catch (ParserConfigurationException ex) {
			throw new BeanDefinitionStoreException(resource.getDescription(),
					"Parser configuration exception parsing XML from " + resource, ex);
		}
		catch (IOException ex) {
			throw new BeanDefinitionStoreException(resource.getDescription(),
					"IOException parsing XML document from " + resource, ex);
		}
		catch (Throwable ex) {
			throw new BeanDefinitionStoreException(resource.getDescription(),
					"Unexpected exception parsing XML document from " + resource, ex);
		}
	}

首先调用this.documentLoader.loadDocument解析xml生成document类
Document doc = this.documentLoader.loadDocument(
					inputSource, getEntityResolver(), this.errorHandler, validationMode, isNamespaceAware());

spring是通过默认的jdk工具类javax.xml.parsers.DocumentBuilder来解析xml文件的

引用
javax.xml.parsers.DocumentBuilder


Defines the API to obtain DOM Document instances from an XML document. Using this class, an application programmer can obtain a Document from XML.

An instance of this class can be obtained from the DocumentBuilderFactory.newDocumentBuilder() method. Once an instance of this class is obtained, XML can be parsed from a variety of input sources. These input sources are InputStreams, Files, URLs, and SAX InputSources.

Note that this class reuses several classes from the SAX API. This does not require that the implementor of the underlying DOM implementation use a SAX parser to parse XML document into a Document. It merely requires that the implementation communicate with the application using these existing APIs.
Author:Jeff Suttor



其次调用XmlBeanDefinitionReader#registerBeanDefinitions解析doc生成beanDefinition并注册
public int registerBeanDefinitions(Document doc, Resource resource) throws BeanDefinitionStoreException {
		BeanDefinitionDocumentReader documentReader = createBeanDefinitionDocumentReader();
		documentReader.setEnvironment(this.getEnvironment());
		int countBefore = getRegistry().getBeanDefinitionCount();
		documentReader.registerBeanDefinitions(doc, createReaderContext(resource));
		return getRegistry().getBeanDefinitionCount() - countBefore;
	}


委托BeanDefinitionDocumentReader解析doc并注册
documentReader.registerBeanDefinitions(doc, createReaderContext(resource));

这里创建了读取xml的上下文环境XmlReaderContext

创建XmlReaderContext

XmlBeanDefinitionReader#createReaderContext
protected XmlReaderContext createReaderContext(Resource resource) {
		if (this.namespaceHandlerResolver == null) {
			this.namespaceHandlerResolver = createDefaultNamespaceHandlerResolver();
		}
		return new XmlReaderContext(resource, this.problemReporter, this.eventListener,
				this.sourceExtractor, this, this.namespaceHandlerResolver);
	}


public XmlReaderContext(
			Resource resource, ProblemReporter problemReporter,
			ReaderEventListener eventListener, SourceExtractor sourceExtractor,
			XmlBeanDefinitionReader reader, NamespaceHandlerResolver namespaceHandlerResolver) {

		super(resource, problemReporter, eventListener, sourceExtractor);
		this.reader = reader;
		this.namespaceHandlerResolver = namespaceHandlerResolver;
	}


引用
回到开头提出的问题:
getReaderContext() 获取的就是xmlReaderContext

xmlReaderContext.getRegistry() -得到的就是xmlBeanDefinitionReader.getRegistry(),即beanFactory
public final BeanDefinitionRegistry getRegistry() {
		return this.reader.getRegistry();
	}


至此,xml到document解析完成,然后就是document到beanDefintion的过程,请参考:
<spring源码学习系列2.2-从document到beanDefinition>

DefaultBeanDefinitionDocumentReader#registerBeanDefinitions
/**
	 * {@inheritDoc}
	 * <p>This implementation parses bean definitions according to the "spring-beans" XSD
	 * (or DTD, historically).
	 * <p>Opens a DOM Document; then initializes the default settings
	 * specified at the {@code <beans/>} level; then parses the contained bean definitions.
	 */
	public void registerBeanDefinitions(Document doc, XmlReaderContext readerContext) {
		this.readerContext = readerContext;
		logger.debug("Loading bean definitions");
		Element root = doc.getDocumentElement();
		doRegisterBeanDefinitions(root);
	}


DefaultBeanDefinitionDocumentReader#doRegisterBeanDefinitions
protected void doRegisterBeanDefinitions(Element root) {
		String profileSpec = root.getAttribute(PROFILE_ATTRIBUTE);
		if (StringUtils.hasText(profileSpec)) {
			Assert.state(this.environment != null, "Environment must be set for evaluating profiles");
			String[] specifiedProfiles = StringUtils.tokenizeToStringArray(
					profileSpec, BeanDefinitionParserDelegate.MULTI_VALUE_ATTRIBUTE_DELIMITERS);
			if (!this.environment.acceptsProfiles(specifiedProfiles)) {
				return;
			}
		}

		// Any nested <beans> elements will cause recursion in this method. In
		// order to propagate and preserve <beans> default-* attributes correctly,
		// keep track of the current (parent) delegate, which may be null. Create
		// the new (child) delegate with a reference to the parent for fallback purposes,
		// then ultimately reset this.delegate back to its original (parent) reference.
		// this behavior emulates a stack of delegates without actually necessitating one.
		BeanDefinitionParserDelegate parent = this.delegate;
		this.delegate = createDelegate(this.readerContext, root, parent);

		preProcessXml(root);
		parseBeanDefinitions(root, this.delegate);
		postProcessXml(root);

		this.delegate = parent;
	}



涉及源码职责:
contextLoader.initWebApplicationContext(event.getServletContext())

contextLoader.createWebApplicationContext(servletContext)

contextLoader.configureAndRefreshWebApplicationContext(cwac, servletContext)

abstractApplicationContext.refresh()

abstractRefreshableApplicationContext.refreshBeanFactory

abstractRefreshableApplicationContext.createBeanFactory

xmlWebApplicationContext.loadBeanDefinitions



参考网站:
ConfigurableApplicationContext vs ApplicationContext
http://stackoverflow.com/questions/30861709/configurableapplicationcontext-vs-applicationcontext
分享到:
评论

相关推荐

    spring-cglib-repack-3.2.0.jar和spring-objenesis-repack-2.1.jar

    解决这个问题的方法通常是将`spring-cglib-repack-3.2.0.jar`和`spring-objenesis-repack-2.1.jar`添加到你的项目构建路径中,如果是Maven或Gradle项目,可以在pom.xml或build.gradle文件中声明对应的依赖。...

    spring-framework-2.1-m1

    2.1 版本在此基础上优化了注解支持,如 @Autowired 和 @Qualifier,使得无需XML配置也能实现依赖注入。 此外,Spring 2.1 引入了AOP(Aspect-Oriented Programming,面向切面编程)的增强,提供了一种更强大的方式...

    spring-cglib-repack-3.1.jar,spring-objenesis-repack-2.1.jar

    当我们在研究或编译Spring 4.0的源码时,可能会遇到一些依赖问题,其中"spring-cglib-repack-3.1.jar"和"spring-objenesis-repack-2.1.jar"是两个关键的jar包,它们在源码编译过程中起着至关重要的作用。本文将详细...

    hibernate-jpa-2.1-api-1.0.0.Final.jar

    本文将深入探讨`hibernate-jpa-2.1-api-1.0.0.Final.jar`这个库,了解其源码中的关键概念和API,并结合具体应用,阐述如何在实际项目中有效利用这些知识。 首先,Hibernate-JPA 2.1 API 是Hibernate对Java ...

    Spring In Action-2.1-01-@Component注解

    @RunWith(SpringJUnit4ClassRunner.class)//Spring的Junit测试,会在测试开始时,创建Spring的应用上下文 @ContextConfiguration(classes=CDPlayerSpringConfig.class)//表明配置类 public class SpringTest1 { //...

    spring4源码所需要的spring-objenesis-repack-2.1.jar,spring-cglib-repack-3.1.jar

    在Spring框架中,`spring-objenesis-repack-2.1.jar`和`spring-cglib-repack-3.1.jar`是两个关键的依赖库,它们在Spring的内部工作原理和功能扩展中扮演着重要角色。让我们深入探讨这两个库以及它们与Spring 4的关系...

    struts2-spring-plugin-2.1.8.1.jar

    struts2-spring-plugin-2.1.8.1.jar

    spring-cglib-repack-3.2.0.jar和spring-objenesis-repack-2.1.jar.zip

    Spring源码深度解析中的两个jar包,文章课件需要的小伙伴可以下载,spring-cglib-repack-3.2.0.jar和spring-objenesis-repack-2.1.jar.zip

    mybatis-spring-boot-starter-2.1.3.jar

    mybatis-spring-boot-starter-2.1.3.jarmybatis-spring-boot-starter-2.1.3.jarmybatis-spring-boot-starter-2.1.3.jar

    spring-cglib-repack-3.2.0.jar

    下载源码,地址https://github.com/spring-projects/spring-framework/tags 下载自己想要的版本,我用的版本是 v5.0.x git clone https://github.com/spring-projects/spring-framework.git 进入目录 下载 ...

    spring-framework-1.0-m1.zip源码

    2. 控制反转:IoC是指应用程序的控制权从应用程序本身转移到框架,Spring容器根据配置文件动态地创建、装配和管理对象。这种设计模式使得组件之间解耦,降低了系统复杂度。 三、核心组件分析 1. ...

    jasypt-spring-boot-starter-2.1.1.jar

    java运行依赖jar包

    spring-cglib-repack-3.2.6.jar,spring-objenesis-repack-2.6.jar

    2018-02-08从git_hub上拉取的Spring源码打成的jar,构建Spring-beans5.0以上源码所缺失的两个jar包spring-cglib-repack-3.2.6.jar,spring-objenesis-repack-2.6.jar

    spring_mybatis_spring-mybatis_

    - **spring-context.xml**:Spring的配置文件,用于定义Bean,包括SqlSessionFactoryBean、MapperScannerConfigurer(扫描Mapper接口)和事务管理器。 5. **编程模式**: - **注解驱动**:通过在Service和Mapper...

    Spring MVC step-by-step 源码

    - `src/main/resources`:存放配置文件,如Spring的XML配置文件,可能包括`dispatcher-servlet.xml`(Spring MVC的配置)和`applicationContext.xml`(Spring容器的配置)。 - `WebContent`:MyEclipse的Web项目...

    thymeleaf-extras-eclipse-plugin-2.1-master.zip

    The content assist features are driven by metadata about a dialect, currently done using XML files, conforming to a schema that lives at http://www.thymeleaf.org/xsd/thymeleaf-extras-dialect-2.1.xsd. ...

    spring1.2.6源码

    通过阅读和分析Spring 1.2.6的源码,不仅可以学习到Spring的核心设计原则,还能了解到设计模式的运用,例如工厂模式、单例模式、观察者模式等。同时,这也是提升Java编程技巧和理解框架底层运作的好机会。在实际的...

    spring-data-commons-1.7.2.RELEASEspring-data-jpa-1.5.2.RELEASE-java datajpa

    这两个模块的 jar 文件,`spring-data-commons-1.7.2.RELEASE.jar` 和 `spring-data-jpa-1.5.2.RELEASE.jar`,包含了它们各自的功能实现和依赖。在实际项目中,将这些 jar 文件添加到类路径,就可以利用 Spring Data...

    官方原版源码spring-framework-5.1.4.RELEASE.zip

    首先,源码分析从`spring-framework-5.1.4.RELEASE-dist.zip`开始,这是Spring框架的基础组件包,包含了所有核心模块的类库和配置文件。主要模块有Core Container(核心容器)、Data Access/Integration(数据访问与...

    基于SpringCloud2.1+spring-security-oauth2+nacos+feign的微服务开发脚手架.zip

    这是一个基于SpringCloud 2.1版本的微服务开发框架,结合了spring-security-oauth2用于权限管理和认证,Nacos作为服务注册与配置中心,以及Feign进行服务间的调用。让我们详细了解一下这些技术栈的核心概念和作用。 ...

Global site tag (gtag.js) - Google Analytics