[Chrome]用虚拟机实际体验Google Chromium OS (Chrome OS)

mikel阅读(1150)

转载:http://www.cnblogs.com/zhubo/archive/2009/11/20/Experience_Google_Chromium_OS_Pre_Beta_with_SUN_VirtualBox.html

前言

今 天凌晨(20091120)2:05左右,与Chrome技术交流QQ群(75448027)的朋友们一起聆听了远在美国的ChromeOS发布会,经过 一番实验后,终于在自己PC的虚拟机中成功运行了ChromeOS。这篇文章旨在帮助所有对ChromeOS感兴趣的朋友一起实际体验一下 ChromeOS,比看截图要更有趣一些。

环境/准备

安装

  • 将下载的VMDK Image解压,如:E:\ChromiumBuild\ChromiumBuild.vmdk
  • 打开Sun VirtualBox,点击新建(如图1)

Snap1 图1

  • 在“新建虚拟电脑”中点击下一步(如图2)

Snap2 图2

  • 名称随意填写,如Chrome,操作系统选Linux,Version选Ubuntu(如图3),下一步

Snap3 图3

  • 内存大小根据自己的需要选择,我选的是1024M(如图4),下一步

Snap4 图4

  • 虚拟硬盘选择使用现有虚拟硬盘,并点击右侧的向上箭头小图标,(如图5)

Snap5 图5

  • 在弹出的虚拟介质管理器中,点击注册(如图6)

Snap6 图6

  • 选择一个虚拟硬盘中,选择刚才解压的E:\ChromiumBuild\ChromiumBuild.vmdk(如图7)

Snap7 图7

  • 选中ChromiumBuild.vmdk,点击选择,(如图8)

Snap8 图8

  • 下一步,(如图9),再点击完成

Snap9 图9

  • 此时,在VirtualBox左侧就会出现刚才我们新建的这个虚拟机,右键点击开始(如图10)(或直接双击)

Snap10 图10

  • 稍待片刻,即进入了ChromeOS的登录界面,用自己的Gmail帐号登录即可(如图11)

Snap11 图11

  • 登录后,进入ChromeOS(如图12),接下来就尽情的折腾它吧。

Snap12 图12

Q&A

  • Q:Chrome能安装到普通PC里么?
    A:不能,目前Chrome只支持5款硬件[2]
    * Acer Aspire One AOD250-1165 10.1-Inch Blue Netbook
    * ASUS Eee PC 1008HA Seashell 10.1-Inch Pearl Black Netbook
    * HP Mini 5101
    * Lenovo Ideapad S10-1311UW 10.2-Inch White Netbook
    * Dell Mini 10V
  • Q:如何自己编译ChromeOS?
    A:参照:http://www.chromium.org/chromium-os/building-chromium-os

参考

  • [1]http://digiex.net/applications/2900-google-chromium-os-chrome-os-pre-beta-vmdk-download.html
  • [2]http://sites.google.com/a/chromium.org/dev/chromium-os/getting-dev-hardware/dev-hardware-list

[MVC]Oxite分析之初始化(更新版)

mikel阅读(670)

转载:http://www.cnblogs.com/alby/archive/2009/11/20/oxite-initialize-new.html

change set:46759
download:http://oxite.codeplex.com/SourceControl/ListDownloadableCommits.aspx
Web应用程序的初始化我觉得应该分两类,一类是系统级的初始化,另一类是应用程序级的初始化。两者也有交叉的部分,如将会谈到的Application_StartApplication_End ,正是利用其在系统级的特殊性来完成应用程序级的初始化工作。关于系统级的初始化,MSDN上有简要的描述:《ASP.NET应用程序生命周期概述》http://msdn.microsoft.com/zh-cn/library/ms178473(VS.80).aspx 。本篇主要分析Oxite在配置级的初始化工作。
一、对ASP.NET应用程序生命周期的理解

摘要《ASP.NET 应用程序生命周期概述》中的几段话:
A:

第一次在应用程序中请求 ASP.NET 页或进程时,将创建 HttpApplication 的一个新实例。不过,为了尽可能提高性能,可对多个请求重复使用 HttpApplication 实例。
理解:也就是说,在应用程序域中将实例化多个HttpApplication对象。每一次请求都将为该请求分配一个HttpApplication对象。而多个请求可能会重复使用同一个HttpApplication对象。但不会存在并发的情况。

B:

Application_StartApplication_End 方法是不表示 HttpApplication 事件的特殊方法。在应用程序域的生命周期期间,ASP.NET 仅调用这些方法一次,而不是对每个 HttpApplication 实例都调用一次。

请求 ASP.NET 应用程序中第一个资源(如页)时调用。在应用程序的生命周期期间仅调用一次 Application_Start 方法。可以使用此方法执行启动任务,如将数据加载到缓存中以及初始化静态值。

在应用程序启动期间应仅设置静态数据。由于实例数据仅可由创建的 HttpApplication 类的第一个实例使用,所以请勿设置任何实例数据。
理解:Application_Start 是在应用程序发生第一次请求,创建第一个HttpApplication对象时发生。(在创建了所有模块之后,对 HttpApplication 类的每个实例都调用一次Application_Init方法)
C:

在应用程序的生命周期期间,应用程序会引发可处理的事件并调用可重写的特定方法。若要处理应用程序事件或方法,可以在应用程序根目录中创建一个名为 Global.asax 的文件。

如果创建了 Global.asax 文件,ASP.NET 会将其编译为从 HttpApplication 类派生的类,然后使用该派生类表示应用程序。
理解:我们创建Global.asax 目的是“替换”原来的HttpApplication 类,而为每一次请求分配的将是Global.asax类的实例。在Global.asax中,我们可以捕获相关事件或重写如Application_StartApplication_End 之类的特殊方法。
二、OixteSite项目中Global.asax文件中的Application_StartApplication_End方法

查看OxiteSite项目的Global.asax文件,发现其实现代码在Oxite项目的OxiteApplication类中。在Application_Start方法中对OxiteSite进行了初始化工作。
Application_Start方法具体做了哪些事呢?
一是设置依赖注入容器并将之存入应用程序状态中(HttpApplicationState)
二是根据配置加载模块

protected void Application_Start()
{
Application["container"] = setupContainer();
Application["bootStrappersLoaded"] = false;
load();
}

Oxite中使用的依赖注入容器为Unity(详见Enterprise Library 4.0以上版本)。
setupContainer方法设置注入容器并返回一个UnityContainer对象,保存为Application["container"]。稍后将仔细分析该方法。
Application["bootStrappersLoaded"]用于标认初始化是否完成。
load()方法调用静态方法Load(HttpContextBase context)。作用是根据配置加载指定模块(Module)。

 

Application_End方法调用unload()方法。在应用程序结束时,完成某些模块的清理工作。
三、setupContainer方法
预备知识:Unity (IOC/DI)、自定义web.config配置结点
通过setupContainer方法的方法名不难看出是用于设置依赖注入容器的。
在setupContainer方法中,首先定义一个IUnityContainer变量parentContainer:

IUnityContainer parentContainer = new UnityContainer();

首先,将几个基础对象注册为单例:

parentContainer
.RegisterInstance((OxiteConfigurationSection)ConfigurationManager.GetSection("oxite"))
.RegisterInstance(new AppSettingsHelper(ConfigurationManager.AppSettings))
.RegisterInstance(RouteTable.Routes)
.RegisterInstance(System.Web.Mvc.ModelBinders.Binders)
.RegisterInstance(ViewEngines.Engines)
.RegisterInstance(HostingEnvironment.VirtualPathProvider);

OxiteConfigurationSection类,自定义配置节点。其定义位与Oxite.Configuration命名空间下。是Oxite中实现模块化的配置文件。配置的结点单独放在OxiteSite项目下的oxite.config文件中。
AppSettingsHelper 类对ConfigurationManager.AppSettings 进行包装, 提供几个读取方法GetInt32、GetString等,用于读取web.config文件中的appSettings节点下的值。其实完全可以将这几 个读取方法放入NameValueCollectionExtensions类(Oxite.Extensions命名空间下)。不过后来想了想,这里用 AppSettingsHelper命名其实也可以明确该类的目的就是为了操作AppSettings结点。
RouteTable.Routes静态属性返回一个RouteCollection静态对象。RouteCollection类在System.Web.Routing程序集中定义。用于保存URL路由设置。 注入容器的目的是为了单元测试。
ModelBinders.Binders静态属性返回一个ModelBinderDictionary静态对象。用于处理数据绑定相关操作(获取表单、查询数据并转换;生成URL路径)。
ViewEngines.Engines静态属性返回一个ViewEngineCollection静态对象。用于视图引擎方面。
HostingEnvironment.VirtualPathProvider静态属性返回一个VirtualPathProvider静态对象。个人猜测可能会用在自定义ViewEngine中,不过目前Oxite版本中好像还没地方用,注释掉也没地方报错。
RouteTable.Routes、ModelBinders.Binders和ViewEngines.Engines是ASP.NET MVC底层比较基础性的属性或对象。值得花时间单独去学习。
接着,将web.config中的connectionStrings和自定义节点“oxite”(oxite.config文件)下的connectionStrings注册为单件。

foreach (ConnectionStringSettings connectionString in ConfigurationManager.ConnectionStrings)
parentContainer.RegisterInstance(connectionString.Name, connectionString.ConnectionString);
foreach (ConnectionStringSettings connectionString in parentContainer.Resolve<OxiteConfigurationSection>().ConnectionStrings)
parentContainer.RegisterInstance(connectionString.Name, connectionString.ConnectionString);

疑问1:在运行时,站点不重启的情况下,如果oxite结点下的connectionStrings改变后,要怎样才能更新到依赖注入容器?这里的 处理似乎欠妥。 后来我到Oxite.codeplex.com去问了,Oxite项目组的ErikPorter说目前得重启站点才行。希望他们尽快修正,不然所谓的模块 热插拔会大打折扣。
接着看setupContainer方法:

parentContainer
.RegisterInstance<IBootStrapperTask>("LoadModules", new LoadModules(parentContainer))
.RegisterInstance<IBootStrapperTask>("LoadBackgroundServices", new LoadBackgroundServices(parentContainer));

LoadModules类和LoadBackgroundServices类位于Oxite.BootStrapperTasks命名空间。从类命 名上看,一个是和加载模块相关的,另一个是和加载后台服务相关的,具体是什么得往后细看了。两者都实现了Oxite.Infrastructure命名空 间下的IBootStrapperTask接口。IBootStrapperTask就两个方法:Execute和Clearup。 IBootStrapperTask我觉得可以直译为引导程序接口,其实例可以称为引导程序。
在这里我们只需要知道,Modules实例和LoadBackgroundSercies实例分别注册为单件。
接着看setupContainer方法中将一些类型也注册到依赖注入容器中,除了几个自定义生命周期的类型外,其他的都只是简单的映射,这里就不多说了。
在 setupContainer结束返回值之前,会将web.config文件中Unity配置结点注册入容器中。如果配置结点和我们硬编码中的设置重复, 则会覆盖硬编码中的配置。这一特性非常有用,它允许我们在使用程序的默认配置的同时,又提供了一个接口以供我们替换。详情可以查看相关Unity方面的资 料。
四、静态方法Load

Application_Start中调用私有load方法,load将请求上下包装成HttpContextBase对象作为参数,调用静态方法Load。
Load方法虽为静态,但其接受一个HttpContextBase型参数,从而保证了其线程安全。

在静态方法Load中将会从依赖注入容器中将实现了IBootStrapperTask接口的类的实例(引导程序)从依赖注入容器中提取出来,并执行其Execute方法,具体过程如下分析。
通过对setupContainer的分析,我们知道在Load方法中,tasks集合中会有两个对象:LoadModules类和LoadBackgroundServices类的实例。
另外,Application["bootStrapperState"]可能是预留下来(也有可能是遗留下来的),暂时没有明确其具体的目的(可以自己想想)。
如果Application["bootStrappersLoaded"]为true,表示曾经加载过,则先逐个进行清理工作(Cleanup)。
然后在逐个执行其引导程序的Execute方法。完成后将Application["bootStrappersLoaded"]设为true表示加载完毕。

public static void Load(HttpContextBase context)
{
IEnumerable<IBootStrapperTask> tasks = ((IUnityContainer)context.Application["container"]).ResolveAll<IBootStrapperTask>();
bool bootStrappersLoaded = (bool)context.Application["bootStrappersLoaded"];
IDictionary<string, object> state = (IDictionary<string, object>)context.Application["bootStrapperState"];
if (state == null)
{
context.Application["bootStrapperState"] = state = new Dictionary<string, object>();
}
// If the tasks have been executed previously, call Cleanup on them to rollback any changes
// they caused.
if (bootStrappersLoaded)
{
foreach (IBootStrapperTask task in tasks)
{
task.Cleanup(state);
}
}
foreach (IBootStrapperTask task in tasks)
{
task.Execute(state);
}
context.Application["bootStrappersLoaded"] = true;
}

 
Oxite的初始化有很大一部分在IBootStrapperTask接口的两个实现类中,通过Execute方法来完成的。
五、LoadModules类
打 一个不十分恰当的比喻,Oxite就象一个Windows操作系统,在系统启动时会执行一些引导程序。有一个引导程序根据注册表的配置,运行一些应用程 序,比如杀毒软件、防火墙等等;另一个引导程序根据“启动”菜单中保存的链接,运行另外一些应用程序,如QQ。(Windows引导程序可能不用两个来完 成这两步操作)
在这里,LoadModules可以看成是通过注册表获取开机启动程序并启动的引导程序;LoadBackgroundServices可以看成是通过“启动”菜单中保存的链接来启动程序的引导程序。
Windows平台下的程序能够实现开机启动,前提条件它得是Windows程序。
我们换成Oxite模块的角度上考虑,他们都应该实现了某个或某些接口。的确,Oxite中的模块都实现了IOxiteModule接口。
而Oxite中模块,可以看成需要开机运行的程序。

Oxite可以看成是由一个个模块(Module)组成的,而模块指实现了IOxiteModule接口的类。
Oxite001

(图1:从模块的角度分析Oxite解决方案结构)
AspNetCache、Core、Membership、Oxite.Blogs、Oxite.CMS等都称之为模块。
LoadModules类实现于接口IBootStrapperTask,其目的正是用于加载模块(Module)。

public void Execute(IDictionary<string, object> state)
{
OxiteConfigurationSection config = container.Resolve<OxiteConfigurationSection>();
IModulesLoaded modulesLoaded = this.container.Resolve<IModulesLoaded>();
RouteCollection routes = this.container.Resolve<RouteCollection>();
IFilterRegistry filterRegistry = this.container.Resolve<FilterRegistry>();
ModelBinderDictionary modelBinders = this.container.Resolve<ModelBinderDictionary>();
filterRegistry.Clear();
modelBinders.Clear();
//todo: (nheskew) get plugin routes registered on load in the right order instead of just clearing the routes before module init
routes.Clear();
foreach (OxiteModuleConfigurationElement module in config.Modules)
{
IOxiteModule moduleInstance = modulesLoaded.Load(config, module);
if (moduleInstance != null)
{
moduleInstance.RegisterWithContainer();
moduleInstance.Initialize();
moduleInstance.RegisterFilters(filterRegistry);
moduleInstance.RegisterModelBinders(modelBinders);
this.container.RegisterInstance(modulesLoaded);
}
}
routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
routes.LoadFromModules(modulesLoaded);
routes.LoadCatchAllFromModules(modulesLoaded);
container.RegisterInstance(filterRegistry);
}

首先获取oxite.config配置文件。Oxite配置结点在setupContainer方法的分析中提到过,他是Oxite模块化的配置。它相当与注册表,用于保存需要开机启动的程序列表。
OxiteConfigurationSection config = container.Resolve<OxiteConfigurationSection>();
接着获取IModulesLoaded对象(在setupContainer方法中,IModulesLoaded映射为ModulesLoaded类)。
IModulesLoaded modulesLoaded = this.container.Resolve<IModulesLoaded>();
ModulesLoaded的作用是保存“已经加载的模块”。之后会注册为单件。

接着获取RouteCollection对象,即RouteTable.Routes。在setupContainer方法被注册为单件。
RouteCollection routes = this.container.Resolve<RouteCollection>();
接着获取IFilterRegistry实例。FilterRegistry是和Filter相关的(ActionFilter、ResultFilter等),可能需要单独的篇幅来分析。
IFilterRegistry filterRegistry = this.container.Resolve<FilterRegistry>();
这里只是简单的实例化,因为IFilterRegistry 并没有在依赖注如容器中注册过。不过在最后,会将IFilterRegistry实例注册为单件。
接着获取ModelBinderDictionary实例。即System.Web.Mvc.ModelBinders.Binders,在setupContainer方法被注册为单件。
ModelBinderDictionary modelBinders = this.container.Resolve<ModelBinderDictionary>();
本质上FilterRegistry、ModelBinderDictionary、RouteCollection都是集合类。接下来将filterRegistry、modelBinders、routes清空。

接下来是一个foreach循环。ModulesLoaded类实例modulesLoaded的Load方法将Module进行实例化,返回IOxiteModule对象。

foreach (OxiteModuleConfigurationElement module in config.Modules)
{
IOxiteModule moduleInstance = modulesLoaded.Load(config, module);
//...
}

如果正常返回IOxiteModule对象,就调用如下四个方法:
      RegisterWithContainer
      Initialize
      RegisterFilters
      RegisterModelBinders

      接着:

      this.container.RegisterInstance(modulesLoaded);

疑问2:上面这句写在循环里,为什么不写在循环外面? 
 

跳出循环后,对路由规则进行设置:

routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
routes.LoadFromModules(modulesLoaded);
routes.LoadCatchAllFromModules(modulesLoaded);

LoadFromModules和LoadCatchAllFromModules方法是扩展方法。
方法内部会遍历 ModulesLoaded对象中保存的IOxiteModuel对象并分别调用对象的RegisterRoutes和 RegisterCatchAllRoutes方法。详情请看Oxite.Extensions.RouteCollectionExtensions 类。
疑问3,这两行代码为什么没有像调用ModulesLoaded类的RegisterWithContainer,Initialize,RegisterFilters,RegisterModelBinders这四个方法那样调用。
我觉得完全可以这样:

routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
foreach (OxiteModuleConfigurationElement module in config.Modules)
{
IOxiteModule moduleInstance = modulesLoaded.Load(config, module);
if (moduleInstance != null)
{
moduleInstance.RegisterWithContainer();
moduleInstance.Initialize();
moduleInstance.RegisterFilters(filterRegistry);
moduleInstance.RegisterModelBinders(modelBinders);
moduleInstance.RegisterRoutes(routes);
moduleInstance.RegisterCatchAllRoutes(routes);
}
}
this.container.RegisterInstance(modulesLoaded);
this.container.RegisterInstance(filterRegistry);

(疑问3的解释:大家都知道,Routing规则设置的顺序非常重要,RegisterRoutes方法中先对Modules进行倒序再注册,目的 是使排在后面的Module的Routing先注册。我觉得这也带来一点麻烦,也许你会覆盖(说成隐藏比较合适)了原本不想覆盖的,比如新加的模块有可能 会覆盖系统模块的Routing规则。也说明了两个方面问题,一方面模块之间也有依赖关系,比如其他模块对系统模块的依赖,当然我们要尽量普通模块之间的 依赖;另一方面模块在oxite.config中的顺序也很重要。)
此文为《Oxite分析之初始化》的更新版,老版有些错误的认识,也留着被拍吧。

[C#]ESBasic 可复用的.NET类库(16)定时刷新缓存管理器

mikel阅读(804)

转载:http://www.cnblogs.com/zhuweisky/archive/2009/11/21/1607429.html

1.缘起:

    为了提升系统的性能或减轻数据库的压力等原因,我们经常在系统中使用缓存来把那些经常使用的数据保留在内存中。如果因为某些原因,缓存中这些经常使用的数据不能及时与数据源进行同步更新,那么采用定时刷新缓存中的数据有可能就是一种合适的选择。

    如果你的缓存是定时刷新,那么你就需要自己为其维护一个定时器或循环引擎。如果你的系统中像这样定时刷新的缓存有多个,而且每个缓存定时刷新的时间间隔又要求不一样,那么,使这些缓存按照你预想的情况进行运转,你就需要花费一些气力。

    我设计了定时刷新缓存管理器IRefreshableCacheManager来帮助你管理你系统中所有需要进行定时刷新的缓存。它可以根据每个缓存的刷新时间间隔要求,在正确的时刻,刷新其所管理的缓存。你不用再自己手动去处理定时器或循环引擎,IRefreshableCacheManager自动为你完成这一切。

 

2.适用场合:

(1)系统中需要使用一个或多个需要进行定时刷新的缓存。

(2)每个缓存所要求的刷新时间间隔可能都不一样。

(3)刷新的时间间隔并不要求精确。

 

3.设计思想与实现

    本节所讲述的是定时刷新缓存管理器IRefreshableCacheManager,其重点在于“管理器”,而不是在于“缓存”,这点是必须清楚的。缓存只是被管理器管理的对象。

能够被IRefreshableCacheManager管理的缓存必须是可定时刷新的缓存,也就是说,这些缓存必须实现IRefreshableCache接口以表明自己可以接受管理器的管理。IRefreshableCache定义如下:

    public interface IRefreshableCache
    {
        
/// <summary>
        
/// RefreshSpanInSecs 定时刷新的时间间隔(秒)。如果设置为0,则表示与IRefreshableCacheManager的刷新时间统一。
        
/// </summary>
        int RefreshSpanInSecs { get; }
        
/// <summary>
        
/// LastRefreshTime 最后一次刷新时间。
        
/// </summary>
        DateTime LastRefreshTime { getset; }        
        
        
void Refresh();       
    }

这个接口相当简单,它只要求缓存提供Refresh方法进行刷新,并通过RefreshSpanInSecs属性表示出自己希望进行定时刷新的时间间隔。如果这个属性设置为0(默认值),则表示该缓存接受管理器的统一调度。

LastRefreshTime属性用于记录最后一次刷新结束的时间,该属性的值由管理器进行设置。

我们从IRefreshableCache接口的定义看到,管理器不用关心与缓存中的数据相关的任何信息,这是由具体的应用在实现IRefreshableCache接口时才指定的。

接下来,我们看看IRefreshableCacheManager接口的定义:

    public interface IRefreshableCacheManager
    {
        
/// <summary>
        
/// RefreshSpanInSecs 定时刷新缓存的时间间隔。
        
/// </summary>
        int RefreshSpanInSecs { set; }
        
IList<IRefreshableCache> CacheList { set; }
        
void Initialize();
        
/// <summary>
        
/// RefreshNow 手动刷新被管理的所有缓存。
        
/// </summary>
        void RefreshNow();
        
/// <summary>
        
/// AddCache 动态添加缓存。
        
/// </summary>       
        void AddCache(IRefreshableCache cache);
        
/// <summary>
        
/// RemoveCache 动态移除缓存。
        
/// </summary>       
        void RemoveCache(IRefreshableCache cache);      
        
/// <summary>
        
/// CacheRefreshFailed 当某个缓存刷新抛出异常时,将触发该事件。
        
/// </summary>
        event CbCacheException CacheRefreshFailed;
    }

这个接口也有一个RefreshSpanInSecs属性,如果被管理的缓存的RefreshSpanInSecs属性设置的是0,那么管理器将用自身的这个属性对其进行调度。IRefreshableCacheManager接口的RefreshSpanInSecs属性是为了简化那种被管理的所有缓存都采用统一刷新时间间隔的情况而存在的。

      RefreshableCacheManager在实现时仍然是借助前面介绍的循环引擎AgileCycleEngine来进行定时控制的。

IRefreshableCacheManager提供了AddCacheRemoveCache方法用于在运行中动态的添加或移除缓存。

当某个缓存在刷新时,抛出异常,则IRefreshableCacheManager会触发CacheRefreshFailed事件,事件参数包含了出现异常的缓存和异常对象。

 

    关于RefreshableCacheManager的实现,要注意以下几点:

(1)管理器的实现是线程安全的,可以使用于多线程的环境中。我们对其内部的缓存列表进行了加锁控制。

(2)管理器在初始化(Initialize)时,启动了循环引擎(其DetectSpanInSecs必须设置为1秒),而且,将所有被管理的缓存的LastRefreshTime都设置为当前时间。

(3)在实现EngineAction方法时,必须在foreach块中使用try来捕捉引擎刷新时抛出的异常,而不是在try块中进行foreach。这个顺序是重要的,如果在try块中进行foreach,那么当一个缓存在刷新时抛出异常而导致foreach中断后,后续缓存的刷新方法都将不会被检测和调用。

(4)使用锁不仅仅是为了同步对内部缓存列表集合的修改,手动调用刷新方法RefreshNow也需要被同步,否则就可能出现两个线程同时进行检测和刷新缓存的情况(一个是循环引擎的线程,另一个是手动调用RefreshNow方法的线程)。

 

4. 使用时的注意事项

(1)由于管理器内部实现采用了循环引擎,所以定时刷新的时间间隔不可能很精确,而且,针对每个缓存的刷新方法的顺序调用也是导致这种不精确的另一个原因。

(2)也由于管理器内部实现采用了循环引擎,循环引擎能设置的最小检测时间间隔为1秒,所以缓存的刷新时间间隔也不可能小于1秒。

(3)如果某个缓存在刷新时抛出异常,那么其LastRefreshTime属性还是记录的上一次成功刷新的时间。

 

5.扩展

     定时刷新缓存管理器通过事件暴露缓存刷新失败的通知,当缓存刷新发生异常时,我们可能需要将异常记录到日志中。如果是这样,那就可以直接使用ESBasic提供的RefreshableCacheExceptionLogBridge来完成。

       RefreshableCacheExceptionLogBridge借助ESBasic.Logger.IAgileLogger组件来将异常的详细信息记录到目标日志中。日志可以是文本文件,也可以是数据库等其他存储。ESBasic提供了IAgileLogger接口的实现FileAgileLogger,用于将日志写入文本文件中。

 

注:ESBasic源码可到http://esbasic.codeplex.com/下载。
    ESBasic讨论QQ群:37677395
    
ESBasic开源前言

[工具]支持jQuery版本vsdoc.js的智能提示的VS2008 SP1补丁下载

mikel阅读(577)

Jeff King发布了一个JQuery智能提示Visual Studio 2008 SP1 补丁,安装这个补丁后,Visual Studio 2008可以自动找到vsdoc.js文件,我们不需要在脚本文件中定义<%if (false)%>代码段就可以看到JQuery方法说明信息了,这个补丁支持English, Français, Deutsch, Italiano, 한국어, 繁體中文, 简体中文, 日本語, Русский, Português (Brasil) 各种语言。

下载地址:http://code.msdn.microsoft.com/KB958502/Release/ProjectReleases.aspx?ReleaseId=1736

安装界面如下:

安装界面

安装完之后,我新建一个Web程序测试一下,引入jQuery库文件jQuery-1.2.6.js和jQueryVS智能文件jQuery-1.2.6-vsdoc.js。

解决方案

你可以到jQuery官方网站上下载jQuery。地址为:http://docs.jQuery.com/Downloading_jQuery#Download_jQuery

1.使用ScriptManager ScriptReference方式:

ScriptReference

2.使用脚本标记方式:

脚本标记

3.使用引入XML标记方式:

引入XML标记

相关帖子:

http://blogs.msdn.com/webdevtools/archive/2008/11/07/hotfix-to-enable-vsdoc-js-intellisense-doc-files-is-now-available.aspx

http://blogs.msdn.com/webdevtools/archive/2008/10/28/rich-intellisense-for-jquery.aspx

作者:李永京YJingLee's Blog
出处:http://lyj.cnblogs.com
本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。

 原文地址 http://lyj.cnblogs.com

[工具]让代码看起来更舒服(1):选择适合的配色方案

mikel阅读(571)

转载:http://www.cnblogs.com/xiaoshatian/archive/2009/11/20/1606440.html

“让代码看起来更舒服”,看到这个标题,也许你会条件反射地以为我要讲“重构”或者“编码规范”等等。噢,可爱的开发人员,我们暂且不谈技术,只谈体验。让我们来装扮一下每天都要面对的Visual Studio,让代码看起来更舒服。

下图展示了Visual Studio 2008默认的编辑器,为了让代码更加容易辨识,Visual Studio用不同的颜色将各种关键词区分开来。

image

默认的配色方案固然无可厚非,每当夜深人静的时候,它总是能让我热泪盈眶。我当然没有矫情到被自己的代码感动,而是我脆弱的眼睛实在受不了它那惨白的背景色。

于是我决定要做出一些改变。

好在Visual Studio可以方便地设置代码配色方案,选择菜单【工具】【选项】,在弹出的“选项”对话框中依次选中“环境”、“字体和颜色”,在“显示其设置”中选中“文本编辑器”,然后我们就可以随意地更改代码编辑器的字体和配色方案了。

image

好吧,我承认自己动手调整配色方案是一件机械式的体力活,开发人员最痛恨机械式的体力活。有这时间还不如Google一下,看看有没有前人栽好的树供我们乘凉,于是我找到了一些配色方案,只要导入到Visual Studio中,就可以马上看到效果!

导入配色方案的方法很简单,选择菜单【工具】【导入和导出设置】,在弹出的“导入和导出设置向导”对话框中选择“导入选定的环境设置”,然后根据自己的实际情况选择“是,保存当前设置”或“否,仅导入新设置,覆盖我的当前设置”,然后点击“浏览”来打开一个Visual Studio Settings File(扩展名为.vssettings)。

需要注意的是,Visual Studio Settings File包含了Visual Studio的各种设置,所以如果你导入的设置文件除了下图所示的“字体和颜色”之外,还有其他设置,请小心勾选。

image

下面是一些我找到的配色方案,以及它们显示同一段代码的效果图,大家挑选喜欢的下载吧。

Jeff-atwood,猛击这里下载

image

HumaneStudio,猛击这里下载

image

2008-DarkGrey,猛击这里下载

image 

2008-Ragnarok_Grey,猛击这里下载

image

2008-Moria_Alt,猛击这里下载

image

2008-Nightingale,猛击这里下载

image

2008-DesertNights,猛击这里下载

image 

尾注:

  • 由于年代久远,我已经记不清这些配色方案的出处了,所以没有注明来源。
  • 这些配置文件均为Visual Studio 2008配置文件,我没有在Visual Studio其他版本做测试,不保证兼容性。

[JQuery]jQuery事件总结

mikel阅读(640)

转载:http://www.cnblogs.com/Clingingboy/archive/2008/02/14/1068908.html
    很少写这些,看了1.2.3版本的改进,确实佩服,很方便.
1.绑定事件
(1)

$("p").bind("click", function(e){});

(2)

$("p").click(function() {})

2.删除事件
(1)删除特定事件

$("div").unbind("click");

(2)删除所有事件

$("div").unbind();

3.触发事件
(1)trigger方法 触发特定元素事件

$("div").trigger('click');

(2)triggerHandler方法 与trigger方法相似,但不触发浏览器默认事件,如focus事件,使用此方法,将会阻止焦点到元素上

$("input").triggerHandler("focus");

4.特殊事件
(1)one(string event,function data)
此事件只执行一次则被删除

$("p").one("click", function(){
  alert(
"test");
}
);

(2)hover(over, out)
切换mouSEOver与mouSEOut事件

$("td").hover(
  function () 
{
    $(
this).addClass("hover");
  }
,
  function () 
{
    $(
this).removeClass("hover");
  }

);

可用unbind mouSEOver与mouseout方法来删除此事件
(3)toggle(oldclick,newclick)
切换执行click事件

    $("li").toggle(
      function () 
{
        $(
this).css("list-style-type""disc")
               .css(
"color""blue");
      }
,
      function () 
{
        $(
this).css({"list-style-type":"""color":""});
      }

    );

可用unbind click方法来删除此事件
5.     1.2.3版本新增功能
(1)事件命名空间(便于管理)
实际使用方面:
1.当不需要全部事件,删除特定2个以上的事件.
示例:

  $("div").bind("click.plugin",function() {} );
  $(
"div").bind("mouseover.plugin", function(){});
  $(
"div").bind("dblclick", function(){});
  $(
"button").click(function() {$("div").unbind(".plugin");  })

在事件名称后面加命名空间,在删除事件时,只需要指定命名空间即可.以上代码执行以后,dbclick仍然存在.
(2)相同事件名称,不同命名的事件执行方法
示例:

$("div").bind("click", function(){ alert("hello"); });
  $(
"div").bind("click.plugin", function(){ alert("goodbye"); });
  $(
"div").trigger("click!"); // alert("hello") only

以上trigger方法则根据事件名称来执行事件.
简单的写几句.以上的几个方法还是非常实用方便的

[MVC]ASP.NET MVC实践系列8-对查询后分页处理的解决方案

mikel阅读(573)

转载:http://www.cnblogs.com/nuaalfm/archive/2009/11/18/1605364.html

一、前言:

GridView中的分页是用post做的,所以将查询表单中的内容可以存到ViewState中,翻页的时候可以利用,实现起来就比较容易些,而 在mvc中这些就要我们自己来做了,Contrib中的分页只能应付简单应用,对于查询后结果的分页没做处理,下面我们来改造一下这个分页程序。

二、准备工作

首先准备一个数据源


 1 public class News
 2 {
 3     public int ID { getset; }
 4     public string Author { getset; }
 5     public string Title { getset; }
 6     public DateTime CreateTime { getset; }
 7 }
 8 public class ListNews
 9 {
10     public static List<News> GetList()
11     {
12         List<News> list = new List<News>();
13         list.Add(new News { ID = 1, Author = "lfm1", Title = "中华人民共和国60周年", CreateTime = DateTime.Now });
14         list.Add(new News { ID = 2, Author = "lfm2", Title = "中华人民共和国61周年", CreateTime = DateTime.Now.AddHours(1) });
15         list.Add(new News { ID = 3, Author = "lfm3", Title = "中华人民共和国62周年", CreateTime = DateTime.Now.AddHours(2) });
16         list.Add(new News { ID = 4, Author = "lfm3", Title = "中华人民共和国63周年", CreateTime = DateTime.Now.AddHours(3) });
17         list.Add(new News { ID = 5, Author = "lfm2", Title = "中华人民共和国64周年", CreateTime = DateTime.Now.AddHours(4) });
18         list.Add(new News { ID = 6, Author = "lfm1", Title = "中华人民共和国60周年", CreateTime = DateTime.Now });
19         list.Add(new News { ID = 7, Author = "lfm2", Title = "中华人民共和国61周年", CreateTime = DateTime.Now.AddHours(1) });
20         list.Add(new News { ID = 8, Author = "lfm3", Title = "中华人民共和国62周年", CreateTime = DateTime.Now.AddHours(2) });
21         list.Add(new News { ID = 9, Author = "lfm3", Title = "中华人民共和国63周年", CreateTime = DateTime.Now.AddHours(3) });
22         list.Add(new News { ID = 10, Author = "lfm2", Title = "中华人民共和国64周年", CreateTime = DateTime.Now.AddHours(4) });
23         list.Add(new News { ID = 11, Author = "lfm2", Title = "中华人民共和国64周年", CreateTime = DateTime.Now.AddHours(4) });
24         list.Add(new News { ID = 12, Author = "lfm1", Title = "中华人民共和国60周年", CreateTime = DateTime.Now });
25         list.Add(new News { ID = 13, Author = "lfm2", Title = "中华人民共和国64周年", CreateTime = DateTime.Now.AddHours(4) });
26         list.Add(new News { ID = 14, Author = "lfm1", Title = "中华人民共和国60周年", CreateTime = DateTime.Now });
27         list.Add(new News { ID = 15, Author = "lfm2", Title = "中华人民共和国64周年", CreateTime = DateTime.Now.AddHours(4) });
28         list.Add(new News { ID = 16, Author = "lfm1", Title = "中华人民共和国60周年", CreateTime = DateTime.Now });
29 
30         return list;
31     }
32 }

然后添加一个View:


 1  <%Html.BeginForm();%>
 2     Title:<%=Html.TextBox("title",Request.Form["title"]??Request.QueryString["title"]) %>
 3     Author:<%=Html.TextBox("author",Request.Form["author"]??Request.QueryString["author"]) %>
 4     <input type="submit" value="查询" />
 5     <%Html.EndForm(); %>
 6     <%=Html.Grid(Model).Columns(column=>{
 7     column.For(x=>x.ID).Named("News ID");
 8     column.For(x => Html.ActionLink(x.Title, "NewsDetils"new { newsId = x.ID })).DoNotEncode();
 9     column.For(x => x.Author).Header("<th>"+Html.ActionLink("作者","CustomPager",new{desc = Convert.ToBoolean(ViewData["desc"]),sortName="Author"})+"</th>");
10     column.For(x=>x.CreateTime);
11 })
12     %>
13 
14     <%= Html.Pager(Model,ViewData["search"])%>

这里的分页代码和Contrib中略有不同,一会儿我们来讲解这个不同的原因。

添加一个Controller:


 1  public ActionResult CustomPager(int? page)
 2         {
 3             int pageSize = 3;
 4             int pageNumber = page ?? 1;
 5             var list = ListNews.GetList()
 6                 .Where(p => p.Title.Contains(Request.QueryString["title"?? ""))
 7                 .Where(p => p.Author.Contains(Request.QueryString["author"?? ""));
 8                 
 9             var pageList=list.Skip((pageNumber  1* pageSize).Take(pageSize);
10             int total = list.Count();
11             CustomPagination<News> customes = new CustomPagination<News>(pageList, pageNumber, pageSize, total);
12 
13             return View(customes);
14         }
15         [AcceptVerbs(HttpVerbs.Post)]
16         public ActionResult CustomPager(FormCollection formCollection)
17         {
18             int pageSize = 3;
19             int pageNumber =  1;
20             var list = ListNews.GetList().Where(p => p.Title.Contains(Request.Form["title"]))
21                 .Where(p => p.Author.Contains(Request.Form["author"]));
22             int total = list.Count();
23             var pageList = list.Skip((pageNumber  1* pageSize).Take(pageSize);
24             CustomPagination<News> customes = new CustomPagination<News>(pageList, pageNumber, pageSize, total);
25             Dictionary<stringstring> d = new Dictionary<stringstring>();
26             d.Add("title", Request.Form["title"]);
27             d.Add("author", Request.Form["author"]);
28             ViewData["Search"= d;
29             return View(customes);
30         }

注:这部分内容的详细讲解可以参见:ASP.NET MVC实践系列7-Grid实现(下-利用Contrib实现)

三、Contrib的分页源码分析

我们先把Pagination和Pager两个文件夹中的源码copy出来,经过分析我们知道CustomPagination是实现了 IPagination接口的集合,我们把数据整合到CustomPagination中就可以使用Pager进行分页 了,PaginationExtensions是辅助HtmlHelper使用扩展方法的静态类。

四、Pager类改造

经过分析我们发现Pager是通过ToString()方法将分页字符串输出到前台的,所以我们这里需要先改造Pager的ToString()方法。我们常常希望分页是这样显示的:

 上一页  1  2 3 4 5 6 下一页,所以先将ToString()方法改造如下:


 1 public override string ToString()
 2         {
 3             if (_pagination.TotalItems == 0)
 4             {
 5                 return null;
 6             }
 7 
 8             var builder = new StringBuilder();
 9 
10             builder.Append("<div class=" + _pageStyle + ">");
11 
12             if (_pagination.PageNumber > 1)
13                 builder.Append(CreatePageLink(_pagination.PageNumber  1, _pagePrev));
14             else
15                 builder.Append(CreatePageText(_pagePrev));
16             for (int i = 0; i < _pagination.TotalPages; i++)
17             {
18                 var current = i + 1;
19                 if (current == _pagination.PageNumber)
20                 {
21                     builder.Append(CreatePageText(current.ToString()));
22                 }
23                 else
24                 {
25                     builder.Append(CreatePageLink(current, current.ToString()));
26                 }
27                 builder.Append("  ");
28             }
29 
30             if (_pagination.PageNumber < _pagination.TotalPages)
31                 builder.Append(CreatePageLink(_pagination.PageNumber + 1, _pageNext));
32             else
33                 builder.Append(CreatePageText(_pageNext));
34             builder.Append(@"</div>");
35 
36             return builder.ToString();
37         }

这里需要交代一下将要实现查询分页的原理,这个方案中我们将会把查询的信息附加到分页的Url上,首先我们会把需要附加的条件添加到一个Dictionary<string, string>类中,然后传给Pager类进行处理。

下面我们来改造一下CreateQueryString方法:

 


 1  private string CreateQueryString(NameValueCollection values)
 2         {
 3             var builder = new StringBuilder();
 4             if (_searchValues != null)
 5             {
 6                 builder = GetSearchPage(values);
 7             }
 8             else
 9             {
10                 foreach (string key in values.Keys)
11                 {
12                     if (key == _pageQueryName)
13                     //Don't re-add any existing 'page' variable to the querystring – this will be handled in CreatePageLink.
14                     {
15                         continue;
16                     }
17 
18                     foreach (var value in values.GetValues(key))
19                     {
20                         builder.AppendFormat("&amp;{0}={1}", key, HttpUtility.UrlEncode(value));
21                     }
22                 }
23             }
24             
25 
26             return builder.ToString();
27         }
28         /// <summary>
29         /// 根据传入的_searchValues来组织分页字符串
30         /// </summary>
31         /// <param name="values"></param>
32         /// <returns></returns>
33         private StringBuilder GetSearchPage(NameValueCollection values)
34         {
35             var builder = new StringBuilder();
36             Dictionary<stringstring> dictionary = new Dictionary<stringstring>();
37             foreach (var item in _searchValues)
38             {
39                 dictionary.Add(item.Key, item.Value);
40             }
41             foreach (string key in values.Keys)
42             {
43                 if (key == _pageQueryName)
44                 //Don't re-add any existing 'page' variable to the querystring – this will be handled in CreatePageLink.
45                 {
46                     continue;
47                 }
48 
49                 foreach (var value in values.GetValues(key))
50                 {
51                     if (_searchValues.Keys.Contains(key))
52                     {
53                         builder.AppendFormat("&amp;{0}={1}", key, HttpUtility.UrlEncode(dictionary[key]));
54                         dictionary.Remove(key);
55                     }
56                     else
57                     {
58                         builder.AppendFormat("&amp;{0}={1}", key, HttpUtility.UrlEncode(value));
59                     }
60 
61                 }
62 
63             }
64             foreach (var item in dictionary)
65             {
66                 builder.AppendFormat("&amp;{0}={1}", item.Key, HttpUtility.UrlEncode(item.Value));
67 
68             }
69             return builder;
70         }

 

这里边主要添加的就是这个GetSearchPage方法,这个方法就是根据客户端传入的查询条件来组织显示分页字符串的。

五、缺点:

因为时使用附加url的方式实现的,所以对于查询条件过多的时候可能有问题,有空我会再实现一个post方案供大家参考。另外这里大家还需要注意的就是url中的查询字符串的名字不能重复,如果重复会直接被替换,详见源码。

六、源码下载

 

我的ASP.NET MVC实践系列

ASP.NET MVC实践系列1-UrlRouting

ASP.NET MVC实践系列2-简单应用

ASP.NET MVC实践系列3-服务器端数据验证

ASP.NET MVC实践系列4-Ajax应用

ASP.NET MVC实践系列5-结合JQuery

ASP.NET MVC实践系列6-Grid实现(上)

ASP.NET MVC实践系列7-Grid实现(下-利用Contrib实现)

其他:

在ASP.NET MVC中对表进行通用的增删改

[C#]清晰的图片缩略方案

mikel阅读(933)

购吧网 :独步全球的BAC电子商务模式基于一批有着传统营销领域丰富经验的专家及对电子商务运作深刻了解的网络专家整合,由广州丽联网络科技有限公司架构购吧网实现。http://www.365goba.com/

希望这篇文章能给对于需要经常生成缩略图的朋友提供帮助!

购吧网目前拥有4000余种商品,在售商品超过2000万,其中图片量截至目前已有8G。
目前我们的方案是用单独的文件服务器存放商品的图片,通过file.365goba.com访问。
文件服务器上架一个用于上传图片的WCF服务对上传的图片进行缩略并保存。

购吧网前期的缩略算法用的是网略上广泛流传的三线性插值算法(效果并不是很好),代码如下:

using System;
using System.Drawing;
using System.IO;
using System.Drawing.Imaging;
using System.Drawing.Drawing2D;
namespace Ants.Tools
{
public class Image
{
public int Width { get; set; }
public int Height { get; set; }
private Image() { }
public Image(int width, int height)
{
this.Width = width;
this.Height = height;
}
public MemoryStream getHightThumb(Stream imgData, string Mode_HW_W_H_Cut)
{
MemoryStream result = new MemoryStream();
System.Drawing.Image.GetThumbnailImageAbort myCallBack = new System.Drawing.Image.GetThumbnailImageAbort(ThumbnailCallBack);
try
{
System.Drawing.Image originalImage = System.Drawing.Image.FromStream(imgData);
int X, Y;
X = Width;
Y = Height;
int towidth = X;
int toheight = Y;
int x = 0;
int y = 0;
int ow = originalImage.Width;
int oh = originalImage.Height;
switch (Mode_HW_W_H_Cut)
{
case "HW": //指定高宽缩放(可能变形)
  break;
case "W"://指定宽,高按比例
toheight = originalImage.Height * X / originalImage.Width;
break;
case "H//指定高,宽按比例
                        towidth = originalImage.Width * Y / originalImage.Height;
break;
case "Cut":
if ((double)originalImage.Width / (double)originalImage.Height > (double)towidth / (double)toheight)
{
oh = originalImage.Height;
ow = originalImage.Height * towidth / toheight;
y = 0;
x = (originalImage.Width - ow) / 2;
}
else
{
ow = originalImage.Width;
oh = originalImage.Width * Y / towidth;
x = 0;
y = (originalImage.Height - oh) / 2;
}
break;
default:
break;
}
System.Drawing.Image bitmap = new System.Drawing.Bitmap(towidth, toheight);
System.Drawing.Graphics g = System.Drawing.Graphics.FromImage(bitmap);
g.InterpolationMode = System.Drawing.Drawing2D.InterpolationMode.HighQualityBicubic;
g.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.HighQuality;
g.Clear(System.Drawing.Color.Transparent);
g.DrawImage(originalImage, new System.Drawing.Rectangle(0, 0, towidth, toheight),
new System.Drawing.Rectangle(x, y, ow, oh),
System.Drawing.GraphicsUnit.Pixel);
bitmap.Save(result, System.Drawing.Imaging.ImageFormat.Jpeg);
}
catch
{
//do something
}
return result;
} 
    }
}
此算法可以满足一般的网站的需求,但是作为一个电子商务网站,商品的图片的清晰度直接影响到消费都对此商品的购买欲望。
为了找到更好的方案,终于让我们找到了一个好的组件:MagickNet 这个组件是用C++写的,不过网络上已经有可用的C#调用版,文章的
后我也会给出这个DLL文件,值得一提的是这个组件是开源的。大家可以去研究下。
MagickNet 的功能很多,我这里就贴出一下他的缩略方法的用法(MagickNet 的资料在网上很难早)
using System;
using System.Collections.Generic;
using System.Text;
namespace KeChenDLL
{
public class UploadFile
{
private string _path;//要处理的图片(原图)地址
public UploadFile(string path)
{
this._path = path;
}
private UploadFile()
{
}
public void ReSize(int width, int height,string SaveToPath)
{
MagickNet.Image img = new MagickNet.Image(_path);
img.Quality = 100;
System.Drawing.Bitmap bitmap = new System.Drawing.Bitmap(_path);
int x = bitmap.Width;
int y = bitmap.Height;
float rank = ((float) x)/y;
if (x > y)
{
height =Convert.ToInt32(height / rank);
}
else
{
width =Convert.ToInt32(width * rank);
}
img.Resize(new System.Drawing.Size(width, height));
img.Write(SaveToPath);
img.Dispose();
}
}
}
希望这篇文章能帮到需要的人。
附MagickNet组件地址

原创文字只代表本人某一时间内的观点或结论,本人不对涉及到的任何代码担保。转载请标明出处!

[OSGi]插件平台的类加载机制

mikel阅读(679)

作为一个插件平台,除了要解决在运行时插件的交互问题外,还需要解决一个非常重要的问题——类加载。原因在于:(1)类加载机制可以绕过默认类型加 载器按需自动加载每一个插件的所有类型;(2)插件具备独立性,即一个插件的运行不能对其它插件和插件内核平台产生影响,这使得类加载机制必须维护每一个 插件的类型;(3)每一个插件可能会引用同一个Class的不同版本,这要求类加载机制必须能够加载多个版本的同一个类并为每一个插件维护其特定版本的类 型。本文将讨论一下基于Java和.NET的插件平台的类加载机制。

 

在Java平台,每一个类型是由ClassLoader和类型全称唯一标识。ClassLoader具有父子关系的层次结构。来自于不同 ClassLoader的相同全名的类被视为不同类型。ClassLoader提供了简单、灵活且优雅的类型加载机制,在默认的ClassLoader 中,利用其进行类型加载时,它会先尝试使用父ClassLoader进行加载,如果在父ClassLoader没有加载成功,才考虑用子 ClassLoader中加载。如果请求子ClassLoader加载的类型,若在父ClassLoader中加载成功,则子ClassLoader不会 再加载,会直接从父ClassLoader中获取。这意味着,一个在父ClassLoader中加载的类型可以和所有的子ClassLoader共享,不 过,子ClassLoader间是不能够共享类型的(这种父ClassLoader向子ClassLoader的共享行为可以通过自定义 ClassLoader来改变)。考虑到Java的默认ClassLoader机制,在我看来理想状态下的插件平台的类加载机制的设计应该如下图(虽然我 不是专业的Java开发人员,但是对其ClassLoader非常感兴趣,于是好好研究了一下,个人觉得该图的ClassLoader层次设计要比 JavaPluginFramework优雅)。这个类加载机制由3层ClassLoader组成,类加载管理器必须严格控制线程执行过程中的 ClassLoader,从而实现插件独立行、多版本类型支持、插件与平台无关性。(附:Flex的类型加载类似于Java的ClassLoader,只 不过它被命名为AppDomain,估计是吸收了.NET和Java的优点。不过,在开发插件化的Flex平台时,如果采用的是分层的AppDomain 的话,很容易碰到TypeA转换成TypeA异常的问题,我把它称为Singleton Hell Issue,因为Flex底层类库使用了不少的单件类,如果一个AppDomain注册一个实例,另一个AppDomain获取并使用则出现异常,这类异 常在Flex SDK 3.0中大量存在。因此,我们在使用Java的ClassLoader和Flex的AppDomain的时候,需要谨慎并精细。)

 

 

(鉴于本文有点抽象,于是举个例子说明一下 )假设在当前线程运行到Plugin B的代码,插件管理器在执行Plugin B的代码之前已经将其类加载器设置为PluginClassLoader了,因此,线程在执行代码的过程中,如果碰到新的类型,则将从 PluginClassLoader B中加载所需的类型;同理,当执行Plugin A的代码时,其加载器使用的是PluginClassLoader A。这为插件类型空间提供了独立性,从而使得在A和B插件可以使用同一个类型的不同版本。 

 

在.NET平台中,每一个类型是由AppDomain + 程序集全名 + 类全名来唯一标识的。微软在程序集中直接声明了版本号。因此,CLR Loader允许在一个AppDomain存在多个版本的相同程序集,这意味着在同一AppDomain可能存在不同版本的类型。.NET的程序集相对于 Java的Package而言,它天生就对版本进行了标识,也就直接解决了插件平台类型加载的多版本问题。不过,遗憾的是,加载到一个AppDomain 的程序集是无法被卸载的,除非卸载整个AppDomain。为了实现插件的动态卸载,这就要求为每一个插件创建一个独立的程序集,但这又引入了 Remoting Call,极大降低了性能。我在这里提到的类加载机制,为了性能和系统简单性考虑,忽略了插件的动态卸载,仅为一个插件平台使用一个AppDomain, 并引入了一套新的类型加载机制,该类型加载机制能够按需加载程序集,其设计,部分借鉴了Java ClassLoader的思路,如下图。在此机制的TypeLoader由其加载的Assembly组成,也具备有一个层次结构,每一个插件的类型加载都 使用自身的BundleTypeLoader进行加载。BundleTypeLoader会从CoreTypeLoader、 SharedLibTypeLoader和依赖的BundleTypeLoader加载所需的类型。

 

 

本文基于.NET的TypeLoader将基于Assembly.LoadFile实现多版本程序集的加载,因此,我们需要为每一个插件管理好其相 应的程序集,在Bundle B执行过程中,它将使用BundleTypeLoader B来加载所需的类型,而这个Loader会根据需要将对应的程序集加载到当前应用域。从而实现,在插件A和B,其类型加载仅限于从插件本身依赖的程序集加 载,虽然所有的程序集都在同一个应用域,但已经被分层管理起来了。(其实原理就只有一句话“确保在插件平台上线程执行过程中能够加载到正确的类型”,比如 执行插件A的代码时,插件A依赖的类型必须在其类加载器存在。)

 

Creative Commons License

本文基于Creative Commons Attribution 2.5 China Mainland License发布,欢迎转载,演绎或用于商业目的,但是必须保留本文的署名道法自然(包含链接)。如您有任何疑问或者授权方面的协商,请给我留言。

[下载]很不错的后台界面收集

mikel阅读(480)

刚刚在百度上看到了,很多都蛮好看的。呵呵… …
最近打算重整下后台界面,给用户操一个舒适,易作的空间,收集到以下模板,挺不错,供大家下载,转载请注明文章的原出处,谢谢
很漂亮实用的后台模板,深蓝色风格,带登陆页面及后台操作页面,功能页面以选项卡形式打开,左边菜单效果也挺动感的,确实是个不错的后台模板。
点击在新窗口中浏览此图片
 

请下载附件中的源码,不要直接复制 (已下载 419 次)

//PHP开源代码
办公自动化系统OA简单后台模板,深蓝色风格,带登录页面及后台操作页面,操作页以选项卡形式打开,操作还是挺简单的,是不久前开发的一款OA系统所改编而来。
点击在新窗口中浏览此图片
 

请下载附件中的源码,不要直接复制 (已下载 266 次)

 

绿色清新简单的后台模板,绿色风格,含登陆页面及后台操作页面,界面简单,操作方便,且后台还是DIV+CSS实现

//OSPHP.com.CN

 

请下载附件中的源码,不要直接复制 (已下载 97 次)

点击在新窗口中浏览此图片
仿126邮箱系统后台界面,绿色风格,看上去挺简单的,功能页面都在新选项卡下打开,IE6、IE7下显示正常,其他浏览器未测试
点击在新窗口中浏览此图片

//OSPHP.COm.CN

 

请下载附件中的源码,不要直接复制 (已下载 102 次)

Ext 做的清爽简单后台模板,清爽淡蓝色风格,功能较简单,以选项卡方式打开功能,前台中要做用户后台的时候可以考虑下
点击在新窗口中浏览此图片
 

请下载附件中的源码,不要直接复制 (已下载 113 次)

//PHP开源代码

绿色经典后台模板,适合开发CMS、HR、OA、CRM等,绿色风格,是一套结构稍微复杂的模板,适合开发各类中小型系统,并包含配套登陆页面模板,
点击在新窗口中浏览此图片
 

请下载附件中的源码,不要直接复制 (已下载 144 次)

仿outlook邮件系统管理后台模板,蓝色风格,分为左中右(左边菜单、中间内容、右边辅助功能)三栏,对于需要做邮件系统的朋友可以下啦,不过左边有两张图片如果需要的话还要自己手工切割一下。

//开源OSPhP.COM.CN

点击在新窗口中浏览此图片
 

请下载附件中的源码,不要直接复制 (已下载 91 次)

管理信息系统后台模板,简单的蓝黑风格,包含登陆页面,适合简单、快速的后台功能开发
 

请下载附件中的源码,不要直接复制 (已下载 93 次)

//OSPHP.com.CN
点击在新窗口中浏览此图片
学校管理系统后台模板,浅蓝色风格,登陆页面虽然跟里面的功能页有点不搭,但挺时尚哦,后台功能菜单也是半透明效果
 

请下载附件中的源码,不要直接复制 (已下载 103 次)

点击在新窗口中浏览此图片

//OSPHP.COm.CN

学校管理系统后台模板,浅蓝色风格,登陆页面虽然跟里面的功能页有点不搭,但挺时尚哦,后台功能菜单也是半透明效果
点击在新窗口中浏览此图片
 

请下载附件中的源码,不要直接复制 (已下载 92 次)

企业邮件处理系统后台模板,深蓝色风格,包含登陆页面,顶部菜单已经实现,整个后台看上去挺动感的,只是左边的菜单还需要自己找一段脚本去完善
点击在新窗口中浏览此图片

//开源OSPhP.COM.CN

 

请下载附件中的源码,不要直接复制 (已下载 96 次)

网站后台管理系统模板,蓝色风格,菜单较复杂,带横向菜单和纵向菜单,功能交错处理,权限不好控制,有需要的下吧。
点击在新窗口中浏览此图片
 

请下载附件中的源码,不要直接复制 (已下载 92 次)
//开源代码OSPHP.COM.Cn

仿163简约风格邮件系统后台模板,蓝色风格
 

请下载附件中的源码,不要直接复制 (已下载 79 次)

点击在新窗口中浏览此图片
一款超炫的后台,Ext模拟Windows桌面,Ext经典浅蓝风格,功能非常强大,包括最大化、最小化、状态栏、桌面图标等,不过需要非常懂Ext脚本的才可驾驭它。
点击在新窗口中浏览此图片 //OsPHP.COM.CN
 

请下载附件中的源码,不要直接复制 (已下载 99 次)

国外简单后台模板[1],白色风格,相对于上面那套模板来说,不提供风格切换功能,但是在用户操作方面设计的超简单的,所谓后台越简单越好嘛,所以前台有会员功能要做的时候可以借鉴一下哦。含PSD文件,懂设计的朋友可以自己修改。
点击在新窗口中浏览此图片
 

请下载附件中的源码,不要直接复制 (已下载 107 次)
//开源代码OSPhP.COm.CN

国外简单后台模板[1],白色风格,相对于上面那套模板来说,不提供风格切换功能,但是在用户操作方面设计的超简单的,所谓后台越简单越好嘛,所以前台有会员功能要做的时候可以借鉴一下哦。含PSD文件,懂设计的朋友可以自己修改。
 

请下载附件中的源码,不要直接复制 (已下载 83 次)

点击在新窗口中浏览此图片
蓝色动网风格后台模板,蓝色风格,经典的动网蓝色风格后台,好像又回到几年前了,结构简单,开发起来速度快。 //开源代码OSPhP.COm.CN
 

请下载附件中的源码,不要直接复制 (已下载 71 次)

 

请下载附件中的源码,不要直接复制 (已下载 54 次)

国外简单后台模板[2],多种风格,蓝色、橙色、黑色等,可相互切换,如果开发网站的时候有会员机制的话用这套就再简单不过了。含PSD文件,懂设计的朋友可以自己修改。
 

请下载附件中的源码,不要直接复制 (已下载 74 次)

//oSPHP.COM.CN
点击在新窗口中浏览此图片
乡镇农户评级系统后台模板,淡绿色风格,带登陆页面及后台操作页面html文件,菜单有点复杂,横向纵向结合,不过本人觉得还行,适合开发各类系统后台,
点击在新窗口中浏览此图片
 

请下载附件中的源码,不要直接复制 (已下载 81 次)

//OSPHP.COm.CN
一款简单黑色后台管理模板,黑色风格,有登陆页面及操作页面,含PSD文件,会设计的朋友自己切割会好很多,因为此模板的HTML文件切割的不是很好
 

请下载附件中的源码,不要直接复制 (已下载 61 次)

点击在新窗口中浏览此图片
人力资源管理系统(HR)HTML模板,蓝色风格,带登陆页面及后台操作页面html文件
点击在新窗口中浏览此图片 //开源代码OSPHP.COM.Cn
 

请下载附件中的源码,不要直接复制 (已下载 81 次)

TaskMenu3.0仿WindowsXP后台模板,WindowsXP蓝色、银色、经典三种风格,因为全是js控制,所以自带了风格切换功能以及菜单控制功能,这款后台出来应该有几年了,经典值得收藏
点击在新窗口中浏览此图片
 

请下载附件中的源码,不要直接复制 (已下载 73 次)
//OSPHP.COM.Cn开源

淡蓝色网站后台管理模板,淡蓝色风格,带登陆页及后台操作页面,全部HTML文件,后台采用框架集页面完成,适合各种网站开发后台管理或CMS后台也可以
点击在新窗口中浏览此图片
 

请下载附件中的源码,不要直接复制 (已下载 97 次)

操作简单的蓝色设计后台模板,蓝色风格,含登陆页面及后台操作页面,设计上体现了对用户操作的考虑,是您开发一般后台的首选。 //OSPHP.COM.Cn开源
 

请下载附件中的源码,不要直接复制 (已下载 61 次)

点击在新窗口中浏览此图片
一款非常漂亮的后台管理模板,深蓝色风格,带登陆页面及后台操作页面,含PSD文件,懂设计的可以自己动手。
 

请下载附件中的源码,不要直接复制 (已下载 84 次)

//开源代码OSPHP.COM.Cn
点击在新窗口中浏览此图片
一款不错的物业管理系统全套html模板,绿色风格,带登陆页面及后台操作页面,看上去跟前几款的设计模式差不多,不过风格比较清新,个人觉得挺喜欢的
点击在新窗口中浏览此图片
 

请下载附件中的源码,不要直接复制 (已下载 84 次)
//OsPHP.COM.CN

国外简单后台模板[3],蓝白风格,国外的模板没的说,简单,后台就更不用说了,设计超简单的,但功能又介于其内,真是不多得的好模板。
 

请下载附件中的源码,不要直接复制 (已下载 80 次)

点击在新窗口中浏览此图片
另附:http://www.uimaker.com/,经常去的设计网站.

 

原出处:http://www.osphp.com.cn/index.php