Enterprise Library深入解析与灵活应用(5):创建一个简易版的批处理执行器,认识Enterprise Library典型的配置方式和对象创建方式
2008-11-17 11:07
786 查看
最近负责一个框架性项目的升级,主要是从.NET Framework 3.0建议到.NET .NET Framework 3.5,开发工具也从VS2005迁移到VS2008。但是最让我头疼的是,原来Team Foundation Server 2005不能正常工作,公司暂时还没有购买VSTS 2008的打算。基于TFS 2005的Team Build功能不能使用了,导致原本通过Team Build实现的功能需要手工来做,涉及到的包括:Source Code的编译、文档的生成、VS项目类型的模板的创建、脚本的合并、安装包的生成等等。由于绝大部分的功能分为两类:文件系统的管理(目录/文件的创建、移动、拷贝和删除)和可执行文件的执行,所以我本打算写一个bat文件搞定就可以了,在操作过程中觉得可扩展性太差了,于是花了半天的时间写了一个GUI的工具。
这个工具执行一组批处理,也可以看成是一个Sequential Workflow的执行器,我把它成为Batch Job Executor。在使用Batch Job Executor过程中,通过配置可以对批处理的每个步骤、或者是Workflow的每个Activity进行自由地定义。从功能上将,这个小工具仅仅是个小玩意儿,不登大雅之堂。 不过考虑到Batch Job Executor的涉及和实现是基于Enterprise Library典型的实现方式,比如基于EL的配置和对象创建方式,对于那些希望进一步了解EL的读者,或许可以通过这个小小的例子一窥EL的设计原理。对于那些EL的设计不时很了解的读者,对于以下的内容,可能在理解上可能比较困难。最好是下载源代码,结合下面的介绍,希望会帮助了更好的理解EL。(Source Code 下载:http://files.cnblogs.com/artech/Artech.BatchJobExecutor.zip)
一、Batch Job Executor使用
使用Batch Job Executor最重要的步骤就是通过配置配处理的每一个步骤进行设置,在这里我们组成Batch的步骤成为Job Step。我们可以先来看看下面的配置示例:
这个配置包含两个部分:变量的定义和Batch Job的定义。前者定义在<variables>配置节中,一个常用的变量,比如基地址,可以通过name-value的方式在这里定义。在本例中,我们定义两个变量(RootLocation和OutputLocation),对变量的引用通过$variable name$的方式实现。而后者呢,则通过<batchJobs>配置节进行定义,我们可以定义一个活着多个Batch Job,在本例中我一共定义了两个批处理:Batch Job 1和Batch Job 2。
第一个批处理由5个步骤组成,它们分别是:
Step 1:创建临时输出目录,路经通过变量定义
Step 2:通过Notepad打开一个.txt文件,文件路径为E:\readme.txt
Step 3:将原目录移到Step1创建了输出目录
Step 4:执行Step 3移到输出目录下的可执行文件,参数通过<arguments>配置节指定
Step 5:移出Step 1创建的临时目录
有了上面的配置,运行我们Batch Job Executor,将会得到下面的界面。两个批处理名称在下拉框中列出,对于选中的当前批处理,5个Job Step在下面的Grid中列出来。点击“Start”按钮,批处理便开始执行,下面的进度条现实当前的进度。
二、Batch Job Executor的设计
1、Job Step
构成一个批处理的步骤通过抽象类JobStep表示,除了定义了Name和Description属性外,定义一个抽象的Execute()方法,Job Step的所有逻辑通过该方法实现。
由于大部分Job Step用于基于文件系统的操作,我创建了另一个抽象类DirectoryFileJobStep,暂时还没有想到需要定义什么具体的操作,鼓且定义创建出来,以备以后不时之需:
创建了4个具体的JobStep,分别用于进行目录的创建、移动和删除,以及.exe文件的执行(ExecutableJobStep),它们的关系通过下面的类型表示。
2、Job Step Configuration
由于所有Job Step都需要通过配置进行设置,所以配置的定义显得尤为重要。在这里我们采用Enterprise Library的Xxx-XxxData-XxxAssembler的结构(比如Exception Handler的定义就采用这样的结构)。其中Xxx代表具体使用某种功能的类型(比如WrapHandler),XxxData(比如WrapHandlerData)表示Xxx对应的配置,而XxxAssembler(WrapHandlerAssembler)则实现通过XxxData对Xxx的创建。
我们Job Step的结构大体也由上面3个部分构成,我们以ExecutableJobStep为例,它的结构大体可以通过下面的类图表示:
先来看看ExecutableJobStep的定义(只列出重要部分)。三个字段分别表示可执行文件的路径、参数和是否需要等待进程结束才能开始下一步骤。Execute()中通过开启进程的方式执行可执行文件。
需要特别注意的是在ExecutableJobStep 上,通过ConfigurationElementTypeAttribute指定了与之相匹配的配置类型(ExecutableJobStepData)。ExecutableJobStep 的三个属性(executableFile、arguments和waitForProcessExit)都定义在ExecutableJobStepData。ExecutableJobStepData集成我们自定义的基类:JobStepData,下面是JobStepData的定义。JobStepData继承自NameTypeConfigurationElement(定了两个Configuration Property:Name和Type的ConfigurationElement),这是一个在Enterprise Library广泛使用的配置类型,因为分别自定义的类型都是通过它的Type属性进行配置的。
ExecutableJobStepData直接继承自JobStepData ,定了3个配置属性分别于ExecutableJobStep的三个属性:executableFile、arguments和waitForProcessExit,以及参数列表。而以name-value形式定义的参数又定义在ArgumentEntry中。
在ExecutableJobStepData上应用了AssemblerAttribute,并指明了Assembler的类型:ExecutableFileJobStepAssmbler。而通过ExecutableFileJobStepAssmbler,则可以通过配置创建具体的ExecutableFileJobStep对象。ExecutableFileJobStepAssmbler实现了接口:IAssembler<JobStep, JobStepData>,在Assemble方法中,通过配置对象(objectConfiguration)创建ExecutableFileJob对象。由于可执行文件的路径(ExecutableFile属性)可能通过定义的变量定义,所以BatchJobExecutorSettings.ApplyVariable对变量进行解析。
从上面的类图,我们会发现我们漏掉了一个对象CustomJobStepData,它继承自JobStepData类型,并实现了两个重要的接口:IHelperAssistedCustomConfigurationData<CustomJobStepData>和ICustomProviderData。要说到具体的作用和实现,可能需要很多的文字才能阐述清楚,在这里我们可以把CustomJobStepData看成是能够实现配置文件中的配置内容和具体配置类型的适配。
我们有了配置相关的辅助类型,最终需要通过配置来创建与之匹配的对象,在EL中显得相对简单,我们只需要调用AssemblerBasedObjectFactory<TObject, TConfiguration>类型的Create方法就可以了。为此我创建了一个特殊的工厂类:JobStepCustomFactory ,用于创建具体的JobStep。
3、整个配置
在一开始,我们就介绍了如果进行批处理的配置,我们现在来看看,该配置类如何来定义:BatchJobExecutorSettings。
整个Batch Job Executor的配置大体由以下两个部分组成:
变量列表:这是一个NamedElementCollection<VariableEntry>类型,VariableEntry定义如下,
Batch Job列表:NamedElementCollection<BatchJobEntry>类型,BatchJobEntry定义如下:
表示Job Step序列的单个步骤的类型是NameTypeConfigurationElementCollection<JobStepData, CustomJobStepData>。
除了以上两个主要成员之外,在根节点上还定义了默认的Batch Job的名称,以及辅助方法ApplyVariable用于解析包含变量的表达式。
4、Batch Job的Batch Job Factory
我们最后还看看Batch Job的定义和创建,下面的类图列出来整个BatchJob创建体系的结构:通过BatchJobFactory创建BatchJob对象,BatchJobFactory最终通过EL的EnterpriseLibraryFactory实现对象的创建,而BatchJobFactory在进行对象创建工程中,会根据BatchJob类型指定的实现了ICustomFacotory的具体类型来创建对象,而我们定义的BatchJobCustomFactory实现了该接口,以及实现真正的对象创建过程。由于在配置中每个BatchJob都具有一个具体的、唯一的名称,一般地,我们通过传入具体的名称创建对应的BatchJob。但是如果我们在创建过程中,不曾传入BatchJob的名称,我们希望的是创建默认的BatchJob。EL中通过一个特殊的接口IConfigurationNameMapper实现了Default Name和具体的Batch Jon Name的匹配。BatchJobMapper实现了该接口,实现了我们需要的名称匹配关系。在这里我就不一一介绍了,有兴趣的朋友可以下载代码自行研究。
实际上,关于对象的创建一直是EL关注的问题,也是EL的核心所在。EL的ObjectBuild和ObjectBuild2就是专门为对象创建而设计的。ObjectBuild和ObjectBuild2是整个EL的基石,也是Unity、Software Factory的根基所在,涉及的类型比较复杂,非三言两语就能概括,有机会的话,我会写一些关于此方面的内容。
这个工具执行一组批处理,也可以看成是一个Sequential Workflow的执行器,我把它成为Batch Job Executor。在使用Batch Job Executor过程中,通过配置可以对批处理的每个步骤、或者是Workflow的每个Activity进行自由地定义。从功能上将,这个小工具仅仅是个小玩意儿,不登大雅之堂。 不过考虑到Batch Job Executor的涉及和实现是基于Enterprise Library典型的实现方式,比如基于EL的配置和对象创建方式,对于那些希望进一步了解EL的读者,或许可以通过这个小小的例子一窥EL的设计原理。对于那些EL的设计不时很了解的读者,对于以下的内容,可能在理解上可能比较困难。最好是下载源代码,结合下面的介绍,希望会帮助了更好的理解EL。(Source Code 下载:http://files.cnblogs.com/artech/Artech.BatchJobExecutor.zip)
一、Batch Job Executor使用
使用Batch Job Executor最重要的步骤就是通过配置配处理的每一个步骤进行设置,在这里我们组成Batch的步骤成为Job Step。我们可以先来看看下面的配置示例:
<?xml version="1.0" encoding="utf-8" ?> <configuration> <configSections> <section name="batchJobExecutor" type="Artech.BatchJobExecutor.Configuration.BatchJobExecutorSettings,Artech.BatchJobExecutor"/> </configSections> <batchJobExecutor defaultBatchJob="Batch Job 1"> <variables> <add name="RootLocation" value="E:\Other Projects\Artech.BatchJobExecutor\"/> <add name="OutputLocation" value="E:\Output\"/> </variables> <batchJobs> <add name="Batch Job 1" description="The first batch job"> <steps> <!--step 1--> <add name="Create Temp Directory" type="Artech.BatchJobExecutor.DirectoryCreationJobStep,Artech.BatchJobExecutor" directoryToCreate="$OutputLocation$" /> <!--step 2--> <add name="Notepad" type="Artech.BatchJobExecutor.ExecutableJobStep,Artech.BatchJobExecutor" executableFile="Notepad" waitForProcessExit="false"> <arguments> <add name="param1" value="E:\readme.txt"/> </arguments> </add> <!--step 3--> <add name="Copy to Output Location" type="Artech.BatchJobExecutor.DirectoryMoveJobStep,Artech.BatchJobExecutor" source="$RootLocation$ConsoleApplication2\Bin" destination="$OutputLocation$" /> <!--step 4--> <add name="Execute Command 1" type="Artech.BatchJobExecutor.ExecutableJobStep,Artech.BatchJobExecutor" executableFile="$OutputLocation$debug\ConsoleApplication1.exe"> <arguments> <add name="param1" value="1st Patameter"/> <add name="param2" value="2nd Patameter"/> </arguments> </add> <!--step 5--> <add name="Delete Temp Directory" type="Artech.BatchJobExecutor.DirectoryDeletionJobStep,Artech.BatchJobExecutor" directoryToDelete="$OutputLocation$" /> </steps> </add> <add name="Batch Job 2" description="2nd batch job"> <steps> … … </steps> </add> </batchJobs> </batchJobExecutor> </configuration>
这个配置包含两个部分:变量的定义和Batch Job的定义。前者定义在<variables>配置节中,一个常用的变量,比如基地址,可以通过name-value的方式在这里定义。在本例中,我们定义两个变量(RootLocation和OutputLocation),对变量的引用通过$variable name$的方式实现。而后者呢,则通过<batchJobs>配置节进行定义,我们可以定义一个活着多个Batch Job,在本例中我一共定义了两个批处理:Batch Job 1和Batch Job 2。
第一个批处理由5个步骤组成,它们分别是:
Step 1:创建临时输出目录,路经通过变量定义
Step 2:通过Notepad打开一个.txt文件,文件路径为E:\readme.txt
Step 3:将原目录移到Step1创建了输出目录
Step 4:执行Step 3移到输出目录下的可执行文件,参数通过<arguments>配置节指定
Step 5:移出Step 1创建的临时目录
有了上面的配置,运行我们Batch Job Executor,将会得到下面的界面。两个批处理名称在下拉框中列出,对于选中的当前批处理,5个Job Step在下面的Grid中列出来。点击“Start”按钮,批处理便开始执行,下面的进度条现实当前的进度。
二、Batch Job Executor的设计
1、Job Step
构成一个批处理的步骤通过抽象类JobStep表示,除了定义了Name和Description属性外,定义一个抽象的Execute()方法,Job Step的所有逻辑通过该方法实现。
namespace Artech.BatchJobExecutor { public abstract class JobStep { public string Name { get; set; } public string Description { get; set; } public abstract void Execute(); } }
由于大部分Job Step用于基于文件系统的操作,我创建了另一个抽象类DirectoryFileJobStep,暂时还没有想到需要定义什么具体的操作,鼓且定义创建出来,以备以后不时之需:
namespace Artech.BatchJobExecutor { public abstract class DirectoryFileJobStep : JobStep { } }
创建了4个具体的JobStep,分别用于进行目录的创建、移动和删除,以及.exe文件的执行(ExecutableJobStep),它们的关系通过下面的类型表示。
2、Job Step Configuration
由于所有Job Step都需要通过配置进行设置,所以配置的定义显得尤为重要。在这里我们采用Enterprise Library的Xxx-XxxData-XxxAssembler的结构(比如Exception Handler的定义就采用这样的结构)。其中Xxx代表具体使用某种功能的类型(比如WrapHandler),XxxData(比如WrapHandlerData)表示Xxx对应的配置,而XxxAssembler(WrapHandlerAssembler)则实现通过XxxData对Xxx的创建。
我们Job Step的结构大体也由上面3个部分构成,我们以ExecutableJobStep为例,它的结构大体可以通过下面的类图表示:
先来看看ExecutableJobStep的定义(只列出重要部分)。三个字段分别表示可执行文件的路径、参数和是否需要等待进程结束才能开始下一步骤。Execute()中通过开启进程的方式执行可执行文件。
namespace Artech.BatchJobExecutor { [ConfigurationElementType(typeof(ExecutableJobStepData))] public class ExecutableJobStep : JobStep { private string _executableFile; private string _arguments; private bool _waitForProcessExit; … … public ExecutableJobStep(string executableFile, string arguments, bool waitForProcessExit) { if (string.IsNullOrEmpty(executableFile)) { throw new ArgumentNullException("executableFile"); } this._executableFile = executableFile; this._arguments = arguments; this._waitForProcessExit = waitForProcessExit; } public override void Execute() { Process process = null; if (string.IsNullOrEmpty(this.Arguments)) { process = Process.Start(this.ExecutableFile); } else { process = Process.Start(this.ExecutableFile, this.Arguments); } if (this._waitForProcessExit) { process.WaitForExit(); } } } }
需要特别注意的是在ExecutableJobStep 上,通过ConfigurationElementTypeAttribute指定了与之相匹配的配置类型(ExecutableJobStepData)。ExecutableJobStep 的三个属性(executableFile、arguments和waitForProcessExit)都定义在ExecutableJobStepData。ExecutableJobStepData集成我们自定义的基类:JobStepData,下面是JobStepData的定义。JobStepData继承自NameTypeConfigurationElement(定了两个Configuration Property:Name和Type的ConfigurationElement),这是一个在Enterprise Library广泛使用的配置类型,因为分别自定义的类型都是通过它的Type属性进行配置的。
namespace Artech.BatchJobExecutor.Configuration { public class JobStepData : NameTypeConfigurationElement { [ConfigurationProperty("description", IsRequired = false, DefaultValue = "")] public string Description { get { return this["description"] as string; } } public JobStepData() { } public JobStepData(string name, Type type) : base(name, type) { } } }
ExecutableJobStepData直接继承自JobStepData ,定了3个配置属性分别于ExecutableJobStep的三个属性:executableFile、arguments和waitForProcessExit,以及参数列表。而以name-value形式定义的参数又定义在ArgumentEntry中。
namespace Artech.BatchJobExecutor.Configuration { [Assembler(typeof(ExecutableFileJobStepAssmbler))] public class ExecutableJobStepData : JobStepData { [ConfigurationProperty("executableFile", IsRequired = true)] public string ExecutableFile { get { return this["executableFile"] as string; } } [ConfigurationProperty("arguments", IsRequired = false)] public NamedElementCollection<ArgumentEntry> Arguments { get { return this["arguments"] as NamedElementCollection<ArgumentEntry>; } } [ConfigurationProperty("waitForProcessExit", IsRequired = false, DefaultValue = true)] public bool WaitForProcessExit { get { return (bool)this["waitForProcessExit"]; } } } public class ArgumentEntry : NamedConfigurationElement { [ConfigurationProperty("value", IsRequired = true)] public string Value { get { return this["value"] as string; } } public ArgumentEntry() { } public ArgumentEntry(string name) : base(name) { } } }
在ExecutableJobStepData上应用了AssemblerAttribute,并指明了Assembler的类型:ExecutableFileJobStepAssmbler。而通过ExecutableFileJobStepAssmbler,则可以通过配置创建具体的ExecutableFileJobStep对象。ExecutableFileJobStepAssmbler实现了接口:IAssembler<JobStep, JobStepData>,在Assemble方法中,通过配置对象(objectConfiguration)创建ExecutableFileJob对象。由于可执行文件的路径(ExecutableFile属性)可能通过定义的变量定义,所以BatchJobExecutorSettings.ApplyVariable对变量进行解析。
namespace Artech.BatchJobExecutor.Configuration { public class ExecutableFileJobStepAssmbler : IAssembler<JobStep, JobStepData> { #region IAssembler<JobStep,JobStepData> Members public JobStep Assemble(IBuilderContext context, JobStepData objectConfiguration, IConfigurationSource configurationSource, ConfigurationReflectionCache reflectionCache) { ExecutableJobStepData jobStepData = objectConfiguration as ExecutableJobStepData; string spliter = " "; StringBuilder arguments = new StringBuilder(); foreach (ArgumentEntry argument in jobStepData.Arguments) { arguments.Append(BatchJobExecutorSettings.ApplyVariable(argument.Value) + spliter); } JobStep jobStep = new ExecutableJobStep(BatchJobExecutorSettings.ApplyVariable(jobStepData.ExecutableFile), arguments.ToString().Trim(spliter.ToCharArray()), jobStepData.WaitForProcessExit); jobStep.Name = jobStepData.Name; jobStep.Description = jobStepData.Description; return jobStep; } #endregion } }
从上面的类图,我们会发现我们漏掉了一个对象CustomJobStepData,它继承自JobStepData类型,并实现了两个重要的接口:IHelperAssistedCustomConfigurationData<CustomJobStepData>和ICustomProviderData。要说到具体的作用和实现,可能需要很多的文字才能阐述清楚,在这里我们可以把CustomJobStepData看成是能够实现配置文件中的配置内容和具体配置类型的适配。
我们有了配置相关的辅助类型,最终需要通过配置来创建与之匹配的对象,在EL中显得相对简单,我们只需要调用AssemblerBasedObjectFactory<TObject, TConfiguration>类型的Create方法就可以了。为此我创建了一个特殊的工厂类:JobStepCustomFactory ,用于创建具体的JobStep。
namespace Artech.BatchJobExecutor { public class JobStepCustomFactory : AssemblerBasedObjectFactory<JobStep, JobStepData> { public static JobStepCustomFactory Instance = new JobStepCustomFactory(); } }
3、整个配置
在一开始,我们就介绍了如果进行批处理的配置,我们现在来看看,该配置类如何来定义:BatchJobExecutorSettings。
namespace Artech.BatchJobExecutor.Configuration { public class BatchJobExecutorSettings : SerializableConfigurationSection { [ConfigurationProperty("variables", IsRequired = true)] public NamedElementCollection<VariableEntry> Variables { get { return this["variables"] as NamedElementCollection<VariableEntry>; } } [ConfigurationProperty("batchJobs", IsRequired = true)] public NamedElementCollection<BatchJobEntry> BatchJobs { get { return this["batchJobs"] as NamedElementCollection<BatchJobEntry>; } } [ConfigurationProperty("defaultBatchJob", IsRequired = true)] public string DefaultBatchJob { get { return this["defaultBatchJob"] as string; } } public static BatchJobExecutorSettings GetConfigurationSection() { return ConfigurationSourceFactory.Create().GetSection("batchJobExecutor") as BatchJobExecutorSettings; } private static NamedElementCollection<VariableEntry> variables; public static string ApplyVariable(string statement) { if (variables == null) { variables = GetConfigurationSection().Variables; } foreach (VariableEntry variable in variables) { statement = statement.Replace("$" + variable.Name + "$", variable.Value); } return statement; } } }
整个Batch Job Executor的配置大体由以下两个部分组成:
变量列表:这是一个NamedElementCollection<VariableEntry>类型,VariableEntry定义如下,
namespace Artech.BatchJobExecutor.Configuration { public class VariableEntry : NamedConfigurationElement { [ConfigurationProperty("value", IsRequired = true)] public string Value { get { return this["value"] as string; } } public VariableEntry() { } public VariableEntry(string name) : base(name) { } } }
Batch Job列表:NamedElementCollection<BatchJobEntry>类型,BatchJobEntry定义如下:
namespace Artech.BatchJobExecutor.Configuration { public class BatchJobEntry : NamedConfigurationElement { [ConfigurationProperty("description", IsRequired = false)] public string Description { get { return this["description"] as string; } } [ConfigurationProperty("steps", IsRequired = true)] public NameTypeConfigurationElementCollection<JobStepData, CustomJobStepData> Activities { get { return this["steps"] as NameTypeConfigurationElementCollection<JobStepData, CustomJobStepData>; } } } }
表示Job Step序列的单个步骤的类型是NameTypeConfigurationElementCollection<JobStepData, CustomJobStepData>。
除了以上两个主要成员之外,在根节点上还定义了默认的Batch Job的名称,以及辅助方法ApplyVariable用于解析包含变量的表达式。
4、Batch Job的Batch Job Factory
我们最后还看看Batch Job的定义和创建,下面的类图列出来整个BatchJob创建体系的结构:通过BatchJobFactory创建BatchJob对象,BatchJobFactory最终通过EL的EnterpriseLibraryFactory实现对象的创建,而BatchJobFactory在进行对象创建工程中,会根据BatchJob类型指定的实现了ICustomFacotory的具体类型来创建对象,而我们定义的BatchJobCustomFactory实现了该接口,以及实现真正的对象创建过程。由于在配置中每个BatchJob都具有一个具体的、唯一的名称,一般地,我们通过传入具体的名称创建对应的BatchJob。但是如果我们在创建过程中,不曾传入BatchJob的名称,我们希望的是创建默认的BatchJob。EL中通过一个特殊的接口IConfigurationNameMapper实现了Default Name和具体的Batch Jon Name的匹配。BatchJobMapper实现了该接口,实现了我们需要的名称匹配关系。在这里我就不一一介绍了,有兴趣的朋友可以下载代码自行研究。
实际上,关于对象的创建一直是EL关注的问题,也是EL的核心所在。EL的ObjectBuild和ObjectBuild2就是专门为对象创建而设计的。ObjectBuild和ObjectBuild2是整个EL的基石,也是Unity、Software Factory的根基所在,涉及的类型比较复杂,非三言两语就能概括,有机会的话,我会写一些关于此方面的内容。
相关文章推荐
- [原创]Enterprise Library深入解析与灵活应用(5):创建一个简易版的批处理执行器,认识Enterprise Library典型的配置方式和对象创建方式
- [原创]Enterprise Library深入解析与灵活应用(4):创建一个自定义Exception Handler改变ELAB的异常处理机制
- Enterprise Library深入解析与灵活应用(4):创建一个自定义Exception Handler改变ELAB的异常处理机制
- Enterprise Library深入解析与灵活应用(4):创建一个自定义Exception Handler改变ELAB的异常处理机制
- [原创] Enterprise Library深入解析与灵活应用(6):自己动手创建迷你版AOP框架
- Enterprise Library深入解析与灵活应用(6):自己动手创建迷你版AOP框架
- spring学习教程7-spring容器创建bean对象的方式以及如何处理多个spring配置文件
- 深入解析Java对象的hashCode和hashCode在HashMap的底层数据结构的应用
- 一个深入理解JAVA传统线程对象创建的例子
- Spring4.3.x 浅析xml配置的解析过程(2)——使用ResourceLoader创建Resource对象
- 反射第一步 : 创建Class 对象的三种方式,表示一个字节码对象。
- 创建一个book.xml文档,PHP通过DOM方式解析获得每本书的作者和书名
- 深入分析虚拟机创建对象的两种方式以及如何在并发情况下实现线程安全
- Enterprise Library深入解析与灵活应用
- 克隆一个对象——原型模式深入解析
- IOS 应用创建一个视图对象分析
- ETL利器Kettle实战应用解析系列三 【ETL后台进程执行配置方式】
- 无废话Android之listview入门,自定义的数据适配器、采用layoutInflater打气筒创建一个view对象、常用数据适配器ArrayAdapter、SimpleAdapter、使用ContentProvider(内容提供者)共享数据、短信的备份、插入一条记录到系统短信应用(3)
- console.log的一个应用 -----用new方法生成一个img对象和document.createElement方法创建一个img对象的区别
- 深入解析Java对象的hashCode和hashCode在HashMap的底层数据结构的应用