




已阅读5页,还剩19页未读, 继续免费阅读
版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领
文档简介
对于Java开发者来说,经常会用到一个框架Spring,但是从用的角度来讲,很多人已经认为比较“复杂”了,但是我认为这个和思维方式有关,当人看到一个电视机,然后看完说明书(相当于Spring的用户文档),最后在磕磕碰碰的点击各种按钮后电视机开始工作了,但是他某一天想让电视机能干点说明书描述的东西以外的事情的时候,现在的电视机也就无能为力了,比如他想让电视机能够具备播放N种格式的视频,并且可以录制电视节目,他买的电视机并不能满足他的要求,必须换新的了,但是恰好他的这个老电视机的电路板上留出了各种插口,可以帮助用户拓展功能,这个说明书没有写,只有维修人员知道,属于内部秘密,因为他们公司所有的电视都用一个电路板的设计做的,只是板子上有不同的插件罢了,这能减少很多设计成本、制作成本、维护成本、人员成本。那么这个用户根本不知道他的电视机还能增强,结果他又买了一个新的,这个新的电视机结果也是用着同样的电路板只是多了几个插件!对于软件这件东西,其实也是一样,spring也是一个软件,我们就属于那些使用者,如果我们不知道其中的原理,就没有办法有效地在合适的场景下使用Spring的一般特性和高级特性,也不知道如何在面对不同的应用环境中如何拓展好它以便更加好的让它为我们的应用服务。以上是一种观点,估计您能理解,我还有一个补充观点,那就是这些出了名的框架包装了很多设计思想,把这些设计思想分解开,你会发现对你自己设计软件时很有帮助,而且不仅仅如此,更加学会了一种阅读代码的方式、学习其设计思想、建立创新型思维的入口的捷径,反倒比看那些什么Spring整合Hibeernate、*指南等等而学得彻底,但是这些捷径的前提是,我们必须破除一个观念就是“权威的东西一定很复杂或者深奥”的想法,或者会用就行的观念,或者直接去源代码而不去先去了解其理论模型的含义,这些不好的观念是可以把你带到书呆子或者工具狂的恶意帮手。从这篇文章开始,我将细细的分析spring源代码,我大可以去分析什么Hibernate、Struts等等这些东西,但是我要说我只是拿Spring作为特例来做分析,并不是最终目的,最终目的是为了和大家探讨如下几个问题:1) 如何做设计前的分析2) 如何利用好现有的知识系统3) 如何对待各种设计理念4) 如果学好和用好工具软件5) 如何学好技术等等,而且在此中最大可能性的解释共通理念、技术的重要性,如何能做到触类旁通等等。所以这系列的文章绝对不是一个什么指南,或者就是为了分析源代码而分析的文章。开始之前,我先要照顾一下初学者,什么是Spring? (你可以直接到官方网站上去查,不过这里还是给个定义,作为Spring的整体概念,这个整体概念将被各种子概念解释,各种子概念将被实际的实现解释)Spring提供对象关系解耦的能力,又能使各个松散耦合的对象关联起来实现一个系统的功能,我这里值的系统也是一个整体观念,具体的可能是一个应用层组件系统,也可能就是一个组件,它并且提供对一个对象本身声明周期的管理。好了,这好像是一个很学究的定义,其实上面这个定义还可以翻译成:把一个系统的关注点分离(对象或其他什么东西的关系解耦),然后再让他们协调起来干一个明确目标的事情.这样来讲可能是看起来有些矛盾和抽象了,其实我们可以观察一下周围的物件,比如电视机,电视机每个零件都是独立的,比如电子管,这个电子管没有被组装到这个电视机中之前谁也不知道它的作用,还可能装到一个手表里也说不定,所以它的独立的,高内聚低耦合的,然而在设计师的“设计“下,把这些互相独立的零件组装起来,来干电视机应该干的事情,而这些独立的零件,又是可以替换的,坏了或者升级都要替换掉,所以Spring这个框架解释了软件设计中最最朴素的也是最最本质的问题那就是对一个概念的关注点的分离或者分层,然后还要使其结合起来实现一个整体的目标!以上看起来很抽象,那么我们先用一个如何使用Spring的简单代码示例来说明问题:设计一个功能(略)1) 实现各个独立的类2)写配置文件3)部署再看看客户程序如何使用这个功能:New FileSystemXmlApplicationContext(“C:beans.xml).getBean();看到了吧,一个功能(整体概念)被分解了,每一个被装配的对象都是可以通过外部配置文件替换的,但是又是一个整体,对立而统一,你会发现每个对象(类的实例)都是可以独立存在的,也可以被装备到其他的应用中,并且可以脱离整体而进行单独测试,这一切带来的好处都是基于上面那么多论述的理由而来的!如此简单,如此优美。接下来我们来看看Spring的核心实现:IOC和AOP.(切忌不可被名词或者什么概念吓倒,很多名词都是在使用隐喻的办法说明一个抽象的概念,是为了离你近一些,明白一些,比如IOC,控制反转,控制是隐喻,反转也是隐喻)一, IOC就是A类依赖另外B类的时候,不需要A管理B的生命周期,也就是不用再在A中来new B,也就是说A不必知道B是否存在,而只是有个接口引用就可以了,那么谁来管理它们之间的依赖(也就是把A&B建立一种实际的关系),那就是IOC容器干的事情,你可以把它想象成一个中间者,那么具体实现一个控制反转是靠的什么,一个类能和其他类发生依赖的地方基本上是构造函数参数和属性或者方法参数,所以就有了DI(依赖注入)的具体实现IOC的方式:构造函数注入和属性注入,下面是一个简单的伪代码例子:A a=new A();/IOC容器来创建B b=new B();/IOC容器来创建a.bRef=b;/IOC容器来关联或者B b=new B();/IOC容器来创建A a=new A(b);/IOC容器来创建发现所有的new和关联工作都“可以“交给IOC容器,那么对于Spring的设计目的来说,IOC容器的实现就是其核心部分之一。下面我们来看看Spring IOC容器(容器也是一个隐喻-一个容器里面肯定能装东西才叫容器,用来说明作用,其实具体实现很简单,后面有讲)的实现(分析的基础是了解其使用、概念或者具体使用过):/#ToDO 源代码文件夹结构从上面这个源代码文件夹结构中(幸亏他们的名字起的好),我第一眼就看到了org.springframework.context这个子项目(Spring所有的大的独立部分都是以子项目存在的,这样很好独立维护和升级,这也符合分解和结合的观点),因为我们经常使用的容器就是ApplicationContext,我们以此为入口点进行分析(最好的习惯就是从你最常用或者大家最常用或者框架、软件的说明书上直接入口使用点开始顺藤摸瓜),我在Eclipse中看到了ApplicationContext的源代码(注释都被我去掉了,是为了更加清晰):public interface ApplicationContext extends EnvironmentCapable, ListableBeanFactory, HierarchicalBeanFactory,MessageSource, ApplicationEventPublisher, ResourcePatternResolver String getId();String getDisplayName();long getStartupDate();ApplicationContext getParent();AutowireCapableBeanFactory getAutowireCapableBeanFactory() throws IllegalStateException;由于我们之前总是在使用Spring,所以一眼就看出来getParent能得到“父“容器,getAutowireCapableBeanFactory与自动装配有关.我们再看看它有哪些实现,我猜测我可以看到一个我们非常熟悉的ApplicationContext,然后我就可以按这个最熟悉ApplicationContext开始分析其原理了,幸好Eclipse提供便捷的工具让你知道一个接口有多少实现!请按F4!哈哈,可以看到ClassPathXmlApplicationContext和FileSystemXmlApplicationContext,这两个都是我们经常用到的ApplicationContext实现,我挑FileSystemXmlApplicationContext进行分析,切忌在分析一个东西的实现时没有理论基础没有主线或者就直接看源代码,会非常容易进到代码丛林中的,最终迷失在茫茫的代码中(但是实际代码并不多,只是我们自己把自己搞晕了,阅读代码时保持一个方向进行,抓清主干然后分析分支是有效的办法,FileSystemXmlApplicationContext的实现是:public class FileSystemXmlApplicationContext extends AbstractXmlApplicationContext public FileSystemXmlApplicationContext() public FileSystemXmlApplicationContext(ApplicationContext parent) super(parent);public FileSystemXmlApplicationContext(String configLocation) throws BeansException this(new String configLocation, true, null);public FileSystemXmlApplicationContext(String. configLocations) throws BeansException this(configLocations, true, null);public FileSystemXmlApplicationContext(String configLocations, ApplicationContext parent) throws BeansException this(configLocations, true, parent);public FileSystemXmlApplicationContext(String configLocations, boolean refresh) throws BeansException this(configLocations, refresh, null);public FileSystemXmlApplicationContext(String configLocations, boolean refresh, ApplicationContext parent)throws BeansException super(parent);setConfigLocations(configLocations);if (refresh) refresh();Overrideprotected Resource getResourceByPath(String path) if (path != null & path.startsWith(/) path = path.substring(1);return new FileSystemResource(path);啊?怎么这么多方法?看清楚了很多都是构造函数和一些重载的方法实现(Override注释的),对于可能在前进路上看到的一大坨代码,不要惊呼,先看看整体这些属性和方法的类别(构造函数,方法等等),先分个类,再找关键的部分,找关键的部分也需要技巧,那就是从调用方找,我们客户程序在使用ApplicationContext时,不就是先在构造函数中设置一下XML配置文件的位置(这个配置文件就是声明了对象之间依赖关系的地方),然后通过getBean方法取的容器中的Bean(装配好的)实例吗?所以我主动在上面的代码寻找构造函数的细节和getBean方法,等找到这些关键部分之后,我们再看支持它们实现的类变量,属性和其他方法即可,可但是我在上面的代码中没有看到什么getBean方法,只看到构造函数中的细节了,而且很多地方用super调用父类的构造函数之实现,因为总和OO这类的语言打交道,直接会就想到getBean可能在父类中!但是稍等一下,从调用方使用它的角度看,构造函数应该是解析XML配置的地方,而XML配置文件中讲述了如何装配Bean的信息,看来getBean方法依赖于这个解析过程,这是一个基础!好了!先不要马上进入找getBean的过程吧,先把它的基础搞明白,开始找XML解析的过程!对!你应该看到了,在public FileSystemXmlApplicationContext(String configLocations, boolean refresh, ApplicationContext parent)throws BeansException super(parent);setConfigLocations(configLocations);if (refresh) refresh();这个构造函数中(其他构造函数都是调用这个构造函数的),貌似看到了一些端倪,第一关联了parent,估计AppliactionContext父子关系建立是通过这方式的!但是我现在先不关心这个的实现,我是想知道XML如何被解析的,setConfigLocation方法的调用也不是,从名字上猜出八九不离十(你看,写程序的时候,名字越贴切越好,给各位推荐一本书代码整洁之道),只能是refresh方法了,从上面的代码分析我大概知道XML解析就在这里面实现的,但是这个方法干了些其他什么事情我还不知道(分析代码时,必须有点想象力,但是这种想象力必须建立在“证据“上,不要瞎猜),我迫不及待地按F3进入refresh方法中看个究竟(这时你发现refresh方法是在AbstractApplicationContext类中实现的,为什么在这里实现?除了OO继承的观念外,还有一种设计思想在里面,这个思想我在后面的分析中会写到):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) / Destroy already created singletons to avoid dangling resources.destroyBeans();/ Reset active flag.cancelRefresh(ex);/ Propagate exception to caller.throw ex;呵呵,无论从调用关系、方法名称和注释都能敏感的感觉到找对地方了,prepareRefresh的注释写“refreshing”,真搞不清这个概念,先进去看看:protected void prepareRefresh() this.startupDate = System.currentTimeMillis();synchronized (this.activeMonitor) this.active = true;if (logger.isInfoEnabled() (Refreshing + this);发现,就是重置一个标志位(先不要急于对看见的东西着迷,我说的是看到和目标暂时无关的东西),我发现貌似和我找的东西无关,先退到到主程序再说(这只是一个小例子,如果遇到一个大的和查找目的无关的代码,你先大体看看,然后再决定是不是马上退回去,不要在这里浪费时间,迟早还会回来的,这可以帮助你减少陷入代码丛林中的几率),接下来看ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();发现obtainFreshBeanFactory()方法的实现是:protected ConfigurableListableBeanFactory obtainFreshBeanFactory() refreshBeanFactory();ConfigurableListableBeanFactory beanFactory = getBeanFactory();if (logger.isDebugEnabled() logger.debug(Bean factory for + getDisplayName() + : + beanFactory);return beanFactory;对于refreshBeanFactory方法:protected abstract void refreshBeanFactory() throws BeansException, IllegalStateException;唉?一个抽象方法!谁实现它,反正不是AbstractApplicationContext!想退回去?且慢,当你不明确知道一个方法调用在干什么,尤其是抽象方法,你最好先找找它的实现,再想退回去也不迟,因为只要你忽略掉一个部分,就会对后面的代码的理解产生偏差,但是不要太深入了,这样既可以保证不会掉到实现的代码丛林中,也不会失掉什么关键点!幸好Eclipse有查看一个类、方法实现关系的工具,请按F4,看来有个好工具对学习是有好的帮助的,所以千万不要低估工具的力量!另外你看ConfigurableListableBeanFactory beanFactory = getBeanFactory();只是取一个对象工厂!也是抽象方法,你可以也按我上面和下面介绍的办法去分析,结果就是一个创建对象工厂实例的逻辑,没有加载XML的逻辑!这里就不累述了,发现这样一个实现关系,父类中有DefaultResourceLoader,有Resource加载的能力吧?XML配置文件不就是一种资源吗?看来靠近了想要到达的地方了,至少不会太远了,于是你想先进去看看,不过我还是建议你看看这个实现关系中的其他类,做到心里有数,至少知道这个地图,不然你一个劲往里钻,没有记不起来如何出来了,尤其是类多了时候,于是我先看了一下上面这个结构,知道下面还有两个ApplicationContext实现,Ok,先看看这个资源加载器,因为从名字来看更接近我想找的东西!public class DefaultResourceLoader implements ResourceLoader private ClassLoader classLoader;public DefaultResourceLoader() this.classLoader = ClassUtils.getDefaultClassLoader();public DefaultResourceLoader(ClassLoader classLoader) this.classLoader = classLoader;public void setClassLoader(ClassLoader classLoader) this.classLoader = classLoader;public ClassLoader getClassLoader() return (this.classLoader != null ? this.classLoader : ClassUtils.getDefaultClassLoader();public Resource getResource(String location) Assert.notNull(location, Location must not be null);if (location.startsWith(CLASSPATH_URL_PREFIX) return new ClassPathResource(location.substring(CLASSPATH_URL_PREFIX.length(), getClassLoader();else try / Try to parse the location as a URL.URL url = new URL(location);return new UrlResource(url);catch (MalformedURLException ex) / No URL - resolve as resource path.return getResourceByPath(location);protected Resource getResourceByPath(String path) return new ClassPathContextResource(path, getClassLoader();private static class ClassPathContextResource extends ClassPathResource implements ContextResource public ClassPathContextResource(String path, ClassLoader classLoader) super(path, classLoader);public String getPathWithinContext() return getPath();Overridepublic Resource createRelative(String relativePath) String pathToUse = StringUtils.applyRelativePath(getPath(), relativePath);return new ClassPathContextResource(pathToUse, getClassLoader();好了,我可以肯定的一点是这里是加载XML文件的地方,但且不是解析的地方,为什么?精通Java的家伙都知道资源加载这回事,也知道Resource接口这样的东西干什么用的,从代码上看只是加载资源而已,好了!到此为止,我必须退回去一步了,幸好我之前浏览了一下类实现的结构图,AbstractApplicationContext下面有两个ApplicationContext实现,当你每次看结构时最好做个笔记,在ApplicationContext继承体系中,你也能看到这两个类,这样你就清楚这些类和FileSystemXmlApplicationContext相对关系了,也知道和AbtractApplicationContext之间是个什么关系了,因为我们在FileSystemXmlApplicationContext中发现入口的驱动方法是Refresh方法,而Refresh方法是在AbstractApplicationContext中实现的,而Refresh方法调用一个方法来实现加载和解析,而这个解析过程被推迟到子类实现,而FileSystemXmlApplicationContext并没有实现,只能是处于它们之间的一个ApplicationContext来实现的(有关为什么会有这样一个“复杂”的继承体系-ApllicationContext下又有一个ApplicationContext,我会慢慢给大家讲清楚,这也和软件的设计思想有关,而且是最朴素的理念!),基于这种分析,我就在AbstractRefreshableApplicationContext和GenericApplicationContext之间做个抉择!因为refresh这个方法名让我联想到可能需要仔细看看AbstractRefreshableApplicationContext!另外FileSystemXmlAppliactionContext的父类中有AbstractRefreshableApplicationContext,从地图中分析,从名称中分析(一般像这样著名开源框架都能遵守很好命名规范,否则就不敢拿出来开源了,会被大家骂死)得到最大的可能性是AbstractRefreshableApplicationContext!我们就试着打开看看吧!终于发现refreshBeanFactory方法实现的地方了:Overrideprotected 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);Very good!逻辑简单并且明了!前面是如果一个BeanFactory存在就摧毁之,然后再创建一个,哈哈,也就是说是实例化ApplicationContext时实际上一个重启动作,也就是清空AppliactionContext缓存的对象(容器倒出所有的水或者东西),而且你发现重新创建的DefaultListableBeanFactory,我猜这个也许是ApplicationContext其他行为的核心,比如对象创建,依赖注入和AOP等等!再往下看,一下子盯住一个方法那就是loadBeanDefinitions(beanFactory),好了,凡是经常用Spring的人,都知道BeanDefinition是干什么的,就是里面存有对象依赖关系的元数据,而这些元数据都是在XML中定义的,我兴高采烈地进入这个方法来看一看:protected abstract void loadBeanDefinitions(DefaultListableBeanFactory beanFactory)throws BeansException, IOException;哈哈,又一次触雷,你可能会问,为什么会这么设计,非得子类去实现吗?这个问题是个设计理念的问题,等我把IOC的原理都给你展示清楚,你在解释为什么这么设计,会更有说服力的。现在看来loadBeanDefinitions的实现极大可能是在AbstractRefreshableApplicationContext和FileSystemXmlAppliactionContext之间的ApplicationContext!看来记录一下类继承结构很有用啊!而这个之间,就两个类了:AbstractRefreshableConfigApplicationContext和AbstractXmlApplicationContext,范围已近很小了,我们这时大可以仔细看看这两个类了看看哪个才是真实现loadBeanDefinitions方法的!检视一下,发现,是在AbstractXmlApplicationContext中,Overrideprotected 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 contexts/ resource loading environment.beanDefinitionReader.setEnvironment(this.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);.protected void loadBeanDefinitions(XmlBeanDefinitionReader reader) throws BeansException, IOException Resource configResources = getConfigResources();if (configResources != null) reader.loadBeanDefinitions(configResources);String configLocations = getConfigLocations();if (configLocations != null) reader.loadBeanDefinitions(configLocations); 从上上面的代码中,你可以肯定的是XML是在XmlBeanDefinitionReader类中解析的,解析到BeanDefinition中!进入XmlBeanDefinitionReader中,看一看:public int loadBeanDefinitions(EncodedResource encodedResource) throws BeanDefinitionStoreException Assert.notNull(encodedResource, EncodedResource must not be null);if (logger.isInfoEnabled() (Loading XML bean definitions from + encodedResource.getResource();Set currentResources = this.resourcesCurrentlyBeingLoaded.get();if (currentResources = null) currentResources = new HashSet(4);this.resourcesCurrentlyBeingLoaded.set(currentResources);if (!currentResources.add(encodedResource) throw new BeanDefinitionStoreException(Detected cyclic loading of + encodedResource + - check your import definitions!);try InputStream inputStream = encodedResource.getResource().getInputStream();try InputSource inputSource = new InputSource(inputStream);if (encodedResource.getEncoding() != null) inputSource.setEncoding(encodedResource.getEncoding();/这里是具体加载的实现逻辑return doLoadBeanDefinitions(inputSource, encodedResource.getResource();finally inputStream.close();catch (IOException ex) throw new BeanDefinitionStoreException(IOException parsing XML document from + encodedResource.getResource(), ex);finally currentResources.remove(encodedResource);if (currentResources.isEmpty() this.resourcesCurrentlyBeingLoaded.remove();哈哈,你也可以看到ReaourceLoader确实是加载XML的!进入doLoadBeanDefinitions方法!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) 看到了吧?就是一个大家经常用到SAX的Document来取得XML文档的,在进入registerBeanDefinitions看看!public int registerBeanDefinitions(Document doc, Resource resource) throws BeanDefinitionStoreException / Read document based on new BeanDefinitionDocumentReader SPI. / TODO SPR-7508: polish - remove commentBeanDefinitionDocumentReader documentReader = createBeanDefinitionDocumentReader();/ TODO SPR-7508: remove ugly castif (documentReader instanceof DefaultBeanDefinitionDocumentReader) (DefaultBeanDefinitionDocumentReader)documentReader).setEnvironment(this.getEnvironment();int countBefore = getRegistry().getBeanDefinitionCount();documentReader.registerBeanDefinitions(doc, createReaderContext(resource);return getRegistry().getBeanDefinitionCount() - countBefore;在进入documentReader.registerBeanDefinitions看看!protected void doRegisterBeanDef
温馨提示
- 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
- 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
- 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
- 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
- 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
- 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
- 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。
最新文档
- 浙江省诸暨市荣怀小学2024-2025学年六年级上学期期末考试语文试题
- 汉字对话课件
- 网红经济现状与未来趋势分析
- 2025年农村土地转让协议模板
- 2024年秋新北师大版数学一年级上册教学课件 第二单元 5以内数加与减 第5课时 可爱的小猫
- 水表井安全知识培训课件记录
- 燃气设备紧急故障应急方案
- 混凝土施工过程中的温差控制技术方案
- 建筑工程项目施工现场物流与仓储方案
- 消防疏散指示标志设置方案
- 留疆战士考试题库及答案
- 2023年安徽师范大学医院高校医学专业毕业生招聘考试历年高频考点试题含答案解析
- YY 0271.2-2009牙科水基水门汀第2部分:光固化水门汀
- GB/T 18341-2021地质矿产勘查测量规范
- 三查四定(含详细内容介绍)课件
- 企业生产安全隐患排查奖励台账
- oh卡牌理论-课件
- 合同工期管理台账
- 赏识你的学生
- 心衰病患者护理查房课件
- TSG11-2020 锅炉安全技术规程
评论
0/150
提交评论