看到7Yue的blog推荐游戏开发的书籍不由得转载下,最近比较关注flash游戏开发方面的,感觉岁数大了是该好好选择方向的时候了,不能在漫无目的的瞎忙了
现如今基于Flash/ActionScript进行网页游戏制作的需求和团队风声水起,游戏多种多样,有Social Game,休闲游戏,网页游戏,MMORPG游戏,目前参与到其中大大小小的制作团队数量呈直线上升趋势,本文不是为了评判各个制作团队的素质之高低的文 章,只是希望通过介绍一套针对Flash游戏设计开发制作的团队进行参考的书籍。这套书籍是经由国外同行推荐的一套集合,书的名字我就不打字输入了,上 图:

此 4本书籍能够帮助一个Flash设计开发团队快速建立其使用Flash和AS3技术制作游戏的理论基础和实用技术,但是这套书籍无法传授游戏开发共通的理 论和方法。从这4本书中,我们可以看到除了设计和数学是Flash游戏必不可少的基础之外,就是工具实战和AS3 API编程技巧。
感兴趣的可以从amazon.com上购买,请不要写什么外国书比中国书贵,英文书不如中文书好懂的评论,明白人都能算清楚这种学习投资和未来回报之间的一笔账。
[工具]WinToFlash – 从 U 盘安装 Windows
WinToFlash 是一个制作 U 盘系统盘的东西,支持 Windows XP, Windows 2003, Windows Vista, Windows 2008, Windows 7 系统,你可以不用光驱就能安装系统,节省了刻盘的成本,另外,对于没有光驱的小屏幕用户,非常实在;当然,对于向 @scavin 这样光驱坏掉的用户,也很强悍。@Appinn
![WinToFlash 从 U 盘安装 Windows[图] | 小众软件 > system WinToFlash 从 U 盘安装 Windows[图] | 小众软件 > system](http://pic.yupoo.com/jdvip/795557fd97ab/medium.jpg)
还记得你上次打开电脑光驱是什么时候呢?随着网络带宽以及牛逼下载技术的实现,我们越来越少的使用光驱了。电影、电视剧的下载速度早以超过了街边盗 版商贩的速度,而如果遇到重装系统,还是需要拿出压箱底的系统盘,打开可能已经生锈的光驱 (@scavin 的光驱很可能就是长时间不用而造成了故障,总之现在读盘已经很是问题了)。
WinToFlash 将整个 Windows 系统安装盘拷贝进 U 盘中,并且制作成可以开机启动的系统盘,这样就能直接利用 U 盘来安装系统了。当然要想制作系统盘,大容量的 U 盘是必须的。怎么说也得 2GB 以上,而 Vista 和 Win7 最少也要 4GB 了。
![WinToFlash 从 U 盘安装 Windows[图] | 小众软件 > system WinToFlash 从 U 盘安装 Windows[图] | 小众软件 > system](http://pic.yupoo.com/jdvip/923357fd97ac/medium.jpg)
WinToFlash 虽暂无中文版,但使用还是非常简单的。请注意备份 U 盘数据,制作 U 盘启动盘的过程会格式化 U 盘。详细使用步骤请见 重灌狂人 的文章:WinToFlash 自制可开机、可安装系统的“USB重灌随身碟”
[MVC] Oxite分析之Module
Oxite分析之Module
2009-09-08 00:16 by 韩安, 114 visits, 网摘, 收藏, 编辑
change set:42353
download :http://oxite.codeplex.com/SourceControl/ListDownloadableCommits.aspx
约定:在Oxite中,对实现了IOxiteModule接口的类称之为Module或模块。
在 某种角度上,可以将Oxite看成是由一个个Module构成的。Oxite项目Modules目录下的各个Module可以看做是系统模块,如 Oxite.Modules.Core.OxiteModule、Oxite.Modules.Membership.MembershipModule 等;而解决方案目录Modules下的各个Module可以看做是自定义Module,如Oxite.Blogs.BlogsModule、 Oxite.CMS.CMSModule。
一、IOxiteModule接口
每个Module都实现了IOxiteModule接口,下面看看IOxiteModule接口的定义:
1: public interface IOxiteModule
2: {
3: void Initialize();
4: void Unload();
5: void RegisterRoutes(RouteCollection routes);
6: void RegisterCatchAllRoutes(RouteCollection routes);
7: void RegisterFilters(IFilterRegistry filterRegistry);
8: void RegisterModelBinders(ModelBinderDictionary modelBinders);
9: void RegisterWithContainer();
10: }
由于目前Oxite官方文档几乎空白,而且又没正式发布,各个方法的作用分别是猜测如下:
RegisterWithContainer:向依赖注入容器注册Module所需的对象或实例;
Initialize:初始化Module(OxiteModule类和PluginsModule类)
RegisterFilters:注册自定义ActionFilter
RegisterModelBinders:注册自定义ModelBinders
RegisterRoutes:设置路由规则
RegisterCatchAllRoutes:作用未知
在“Oxite分析之初始化”一文中曾提到过,当Oxite初始化时,将会调用各个被加载的Module的除了Unload方法外的所有方法。而Unload方法将在Application_End方法中被间接调用用于清理一些资源。
具体查看LoadModules : IBootStrapperTask类的Execute方法:
1: OxiteConfigurationSection config = container.Resolve<OxiteConfigurationSection>();
2: IModulesLoaded modulesLoaded = this.container.Resolve<IModulesLoaded>();
3: RouteCollection routes = this.container.Resolve<RouteCollection>();
4: IFilterRegistry filterRegistry = this.container.Resolve<FilterRegistry>();
5: ModelBinderDictionary modelBinders = this.container.Resolve<ModelBinderDictionary>();
6: //...
7:
8: filterRegistry.Clear();
9:
10: modelBinders.Clear();
11:
12: //todo: (nheskew) get plugin routes registered on load in the right order instead of just clearing the routes before module init
13: routes.Clear();
14:
15: foreach (OxiteModuleConfigurationElement module in config.Modules)
16: {
17: IOxiteModule moduleInstance = modulesLoaded.Load(config, module);
18:
19: if (moduleInstance != null)
20: {
21: moduleInstance.RegisterWithContainer();
22: moduleInstance.Initialize();
23: moduleInstance.RegisterFilters(filterRegistry);
24: moduleInstance.RegisterModelBinders(modelBinders);
25:
26: this.container.RegisterInstance(modulesLoaded);
27:
28: //...
29: }
30: }
31:
32: routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
33:
34: routes.LoadFromModules(modulesLoaded);
35:
36: routes.LoadCatchAllFromModules(modulesLoaded);
37:
这里需要注意各个方法的调用顺序,这一点可以查看PluginsModule的源码。我们一般会认为Initialize应该被最先调用,但 PluginsModule类的Initialize方法将会用到RegisterWithContainer方法中进行的某些配置,所以 RegisterWithContainer方法应该最新被调用。
二、IOxiteDataProvider
Module不一定要实现IOxiteDataProvider。当需要制定Module内部数据访问方式时,通过外部配置以及让Module实现IOxiteDataProvider可以达到。
另外,Oxite配置的灵活性使得Module也可以不实现IOxiteDataProvider接口,而新建一个类来实现。目前Oxite版本中实现在了Module本身中。
通过这样的配置,使得各个Module可以拥有自己特有的数据访问方式,甚至于每个Module可以放在不同的数据库或不同类型的数据库中。在数据访问上说,去除了与系统本身的耦合。
在“Oxite分析之初始化”一文中曾提到过web.config配置文件的自定义"oxite"配置节点。具体配置已经移至单独的Oxite.config,下面看看它的内容:
1: <oxite>
2: <connectionStrings>
3: <add name="Sql" connectionString="Data Source=.\SQLEXPRESS;Initial Catalog=Oxite.Database;Integrated Security=true;"/>
4: <!--<add name="Sql" connectionString="Data Source=.\SQLEXPRESS;AttachDBFileName=|DataDirectory|Oxite.Database.mdf;Integrated Security=true;User Instance=true;"/>-->
5: </connectionStrings>
6: <dataProviders defaultConnectionString="Sql">
7: <add name="Membership" type="Oxite.Modules.Membership.MembershipModule, Oxite" category="LinqToSql" />
8: <add name="Tags" type="Oxite.Modules.Tags.TagsModule, Oxite" category="LinqToSql" />
9: <add name="Comments" type="Oxite.Modules.Comments.CommentsModule, Oxite" category="LinqToSql" />
10: <add name="Plugins" type="Oxite.Modules.Plugins.PluginsModule, Oxite" category="LinqToSql" />
11: <add name="Blogs" type="Oxite.Modules.Blogs.BlogsModule, Oxite.Blogs" category="LinqToSql" />
12: <add name="CMS" type="Oxite.Modules.CMS.CMSModule, Oxite.CMS" category="LinqToSql" />
13: <add name="Conferences" type="Oxite.Modules.Conferences.ConferencesModule, Oxite.Conferences" category="LinqToSql" />
14: <add name="Search" type="Oxite.Modules.Search.SearchModule, Oxite" category="LinqToSql" />
15: </dataProviders>
16: <modules>
17: <add name="AspNetCache" type="Oxite.Modules.AspNetCache.AspNetCacheModule, Oxite" />
18: <add name="Membership" type="Oxite.Modules.Membership.MembershipModule, Oxite" dataProvider="Membership" />
19: <add name="FormsAuthentication" type="Oxite.Modules.FormsAuthentication.FormsAuthenticationModule, Oxite" />
20: <add name="Core" type="Oxite.Modules.Core.OxiteModule, Oxite" />
21: <add name="Tags" type="Oxite.Modules.Tags.TagsModule, Oxite" dataProvider="Tags" />
22: <add name="Files" type="Oxite.Modules.Files.FilesModule, Oxite" />
23: <add name="Comments" type="Oxite.Modules.Comments.CommentsModule, Oxite" dataProvider="Comments" />
24: <add name="Plugins" type="Oxite.Modules.Plugins.PluginsModule, Oxite" dataProvider="Plugins" />
25: <add name="Blogs" type="Oxite.Modules.Blogs.BlogsModule, Oxite.Blogs" dataProvider="Blogs" />
26: <add name="CMS" type="Oxite.Modules.CMS.CMSModule, Oxite.CMS" dataProvider="CMS" />
27: <add name="Conferences" type="Oxite.Modules.Conferences.ConferencesModule, Oxite.Conferences" dataProvider="Conferences" enabled="false" />
28: <add name="Search" type="Oxite.Modules.Search.SearchModule, Oxite" dataProvider="Search" />
29: <add name="Site" type="OxiteSite.App_Code.Modules.OxiteSite.OxiteSiteModule" />
30: </modules>
31: </oxite>
modules节点下的元素,有的具有dataProvider属性(查看OxiteModuleConfigurationElement类的 DataProvider属性的定义)。比如name为"Search"的元素,dataProvider为"Search":
1: <add name="Search" type="Oxite.Modules.Search.SearchModule, Oxite" dataProvider="Search" />
其对应的是dataProviders节点下的name为"Search"的元素:
1: <add name="Search" type="Oxite.Modules.Search.SearchModule, Oxite" category="LinqToSql" />
接着请看ModulesLoaded类的Load方法:
1: public IOxiteModule Load(OxiteConfigurationSection config, OxiteModuleConfigurationElement module)
2: {
3: if (module == null || !module.Enabled) return null;
4:
5: foreach (OxiteDataProviderConfigurationElement dataProvider in config.Providers)
6: {
7: if (dataProvider.Name == module.DataProvider)
8: {
9: Type dataProviderType = Type.GetType(dataProvider.Type);
10:
11: if (dataProviderType == null)
12: throw new TypeLoadException(string.Format("Could not load type '{0}'.", dataProvider.Type));
13:
14: IOxiteDataProvider dataProviderInstance = container.Resolve(dataProviderType) as IOxiteDataProvider;
15:
16: if (dataProviderInstance != null)
17: dataProviderInstance.ConfigureProvider(config, dataProvider, container);
18:
19: break;
20: }
21: }
22:
23: //…
25: }
OxiteConfigurationSection config为整个oxite配置节点;OxiteModuleConfigurationElement module为oxite配置节点modules下的一个元素(比如name为"Search"的元素)
上 述代码的第5行开始,遍历oxite.config配置中的dataProviders节点下的dataProvider元素,如果元素的name值等于 module的dataProvider属性,通过dataProvider元素的type属性,获取类型。接着判断类型是否真的继承于 IOxiteDataProvier。如果是,调用类型的ConfigureProvider方法。
[MVC] Oxite分析之初始化
change set:42353
download :http://oxite.codeplex.com/SourceControl/ListDownloadableCommits.aspx
和一般的web程序一样,Oxite在第一次被访问时将进行初始化操作,即Oxite.OxiteApplication的 Application_Start方法。这些操作笼统来说包括两部分,一是设置依赖注入容器并将之存入应用程序状态中 (HttpApplicationState),二是通过依赖注入容器中的某些配置加载模块(Oxite分析之Module)。
这两部分的分析入口分别为Application_Start方法中的Application["container"] = setupContainer();和load();
一、设置依赖注入容器
注意力转移至setupContainer方法。该方法直接或间接将大批的对象和类型注册入依赖注入容器。
首先,将几个基础对象注册为单例。
1: parentContainer
2: .RegisterInstance((OxiteConfigurationSection)ConfigurationManager.GetSection("oxite"))
3: .RegisterInstance(new AppSettingsHelper(ConfigurationManager.AppSettings))
4: .RegisterInstance(RouteTable.Routes)
5: .RegisterInstance(System.Web.Mvc.ModelBinders.Binders)
6: .RegisterInstance(ViewEngines.Engines)
7: .RegisterInstance(HostingEnvironment.VirtualPathProvider);
OxiteConfigurationSectio类,自定义配置节点。其下目前有三个属 性:connectionStrings,dataProviders,moduls(可能会增加一个settings属性)。可以查看 OxiteSite项目下的oxite.confing文件直观的了解其结构。该配置基本上是针对Module的(Oxite分析之Module)。
Oxite几乎将所有东西都放置在一个个模块中,包括最核心的部分。比如设置自定义CollectionerFactiory、自定义 ControllerActionInvoker、自定义ViewEngines等在Oxite.Moduls.Core模块中进行。而moduls节点 就定义了Oxite系统包括哪些Module;
dataProviders定义了各个需要数据访问的Module的数据访问方式的分类(category属性),而在Module的具体定义中可以这个分类注入不同的Repository以达到各个模块可以单独使用不同的数据访问方式设置不同的数据库。
connectionStrings节点结构和站点配置web.config中的系统connectionStrings节点完全相同,这里之所以又进行 一次定义,是为了提供给dataProviders使用(dataProviders有个defaultConnectionString属性)。当 dataProviders下的元素未定义connectionString时,可以使用dataProviders节点的 defaultConnectionString属性对应的connectionStrings节点的元素的connectionString属性(真拗 口)。
AppSettingsHelper 类对ConfigurationManager.AppSettings 进行包装, 提供几个读取方法GetInt32、GetString等,用于读取web.config文件中的appSettings节点下的值。其实完全可以将这几 个读取方法放入NameValueCollectionExtensions类(Oxite已定义该类)。appSettings节点下,可以定义名称为 Oxite.InstanceName的值来指定Oxite实例,该值指导程序在数据库读取Oxite_Site表中的相应配置。还可以定义名称为 IsEmail、IsUrl等,值为正则表达式的元素。如果appSettings节点中没有定义这些元素,将会使用程序硬编码中的设置。
RouteTable.Routes,RouteCollection对象。System.Web.Routing中对其定义,稍微熟悉一点ASP.NET MVC应该清楚这是做什么的。
另外ModelBinders.Binders(Oxite分析之ModelBinders)、ViewEngines.Engines(Oxite分析之ViewEngines) 可以参看关于ASP.NET MVC方面的资料。
HostingEnvironment.VirtualPathProvider,HostingEnvironment 类的静态属性,其值是VirtualPathProvider类的对象。个人猜测可能会用在自定义ViewEngine中,不过目前Oxite版本中好像 还没地方用。
然后,将web.config中的connectionStrings和自定义节点”oxite”下的connectionStrings注册为单件。
1: foreach (ConnectionStringSettings connectionString in ConfigurationManager.ConnectionStrings)
2: parentContainer.RegisterInstance(connectionString.Name, connectionString.ConnectionString);
3:
4: foreach (ConnectionStringSettings connectionString in parentContainer.Resolve<OxiteConfigurationSection>().ConnectionStrings)
5: parentContainer.RegisterInstance(connectionString.Name, connectionString.ConnectionString);
将数据库连接字符串注册为单件,在构造数据库访问对象(或ORM,Linq to SQL、Entity Framework等)将会使用。
接着,两行咋看不太起眼的代码,实际上是进入Oxite核心的入口:
1: parentContainer
2: .RegisterInstance<IBootStrapperTask>("LoadModules", new LoadModules(parentContainer));
创建LoadModules对象,并注册为单例。LoadModules实现了IBootStrapperTask接口。
1: public interface IBootStrapperTask
2: {
3: void Execute(IDictionary<string, object> state);
4: void Cleanup(IDictionary<string, object> state);
5: }
在OxiteApplication的Application_Start调用私有方法load,load调用静态方法 Load。Load方法中,将从依赖注入容器中提取(ResolveAll)所有实现了IBootStrapperTask接口的类的对象,并调用 Execute方法(详情查看静态方法Load);在Application_End方法中,类似的将间接执行实现了IBootStrapperTask 接口的类的对象的Cleanup方法(详情查看私有方法unload)。当然,目前Oxite版本中只有一个LoadModules对象。
LoadModules类的主要代码:
1: public void Execute(IDictionary<string, object> state)
2: {
3: OxiteConfigurationSection config = container.Resolve<OxiteConfigurationSection>();
4: IModulesLoaded modulesLoaded = this.container.Resolve<IModulesLoaded>();
5: RouteCollection routes = this.container.Resolve<RouteCollection>();
6: IFilterRegistry filterRegistry = this.container.Resolve<FilterRegistry>();
7: ModelBinderDictionary modelBinders = this.container.Resolve<ModelBinderDictionary>();
8: IBackgroundServiceRegistry backgroundServicesRegistry = this.container.Resolve<IBackgroundServiceRegistry>();
9:
10: filterRegistry.Clear();
11:
12: modelBinders.Clear();
13:
14: //todo: (nheskew) get plugin routes registered on load in the right order instead of just clearing the routes before module init
15: routes.Clear();
16:
17: foreach (OxiteModuleConfigurationElement module in config.Modules)
18: {
19: IOxiteModule moduleInstance = modulesLoaded.Load(config, module);
20:
21: if (moduleInstance != null)
22: {
23: moduleInstance.RegisterWithContainer();
24: moduleInstance.Initialize();
25: moduleInstance.RegisterFilters(filterRegistry);
26: moduleInstance.RegisterModelBinders(modelBinders);
27:
28: this.container.RegisterInstance(modulesLoaded);
29:
30: //TODO: (erikpo) Move this into its own BootStrapper and spin up the appropriate executor classes and start them
31: IOxiteBackgroundService backgroundServices = moduleInstance as IOxiteBackgroundService;
32:
33: if (backgroundServices != null)
34: backgroundServices.RegisterBackgroundServices(backgroundServicesRegistry);
35: }
36: }
37:
38: routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
39:
40: routes.LoadFromModules(modulesLoaded);
41:
42: routes.LoadCatchAllFromModules(modulesLoaded);
43:
44: container.RegisterInstance(filterRegistry);
45:
46: container.RegisterInstance(backgroundServicesRegistry);
47: }
48:
49: public void Cleanup(IDictionary<string, object> state)
50: {
51: container.Resolve<IModulesLoaded>().UnloadModules();
52:
53: //TODO: (erikpo) Loop through all background services running in the background and stop them
54: }
在这里我们暂时不去关心IBackgroundServiceRegistry、IFilterRegistry和FilterRegistry的定义,不过通过接口名称可以看出是和后台服务和ActionFilter相关的。
下面简单说说Oxite怎么实现Module(Oxite分析之Module)的插拔功能的。
Oxite中的Module都实现了IOxiteModule接口。当做好一个Module后(源码文件.cs或.vb等),直接复制到OxiteSet 的App_Code目录下,或者编译成dll复制到bin目录下并在依赖注入容器中进行注入,然后在oxite配置节点中进行配置即可。轻松实现 Module的插拔功能。
IModulesLoaded接口,从字面上看就是”已经加载的Module”,在Oxite中有一个实现:ModulesLoaded类,在依赖注入容 器中已经进行了注册:parentContainer.RegisterType<IModulesLoaded, ModulesLoaded>()
。该类的Load方法正是实现上述Module插拔功能。
Load方法中将找到的Module实例化,并将对象私有变量List<IOxiteModule> modules中,分别提供泛型和非泛型的GetModules方法供外部读取。当然,这种插拔功能如果完全可以自己另外实现一 个:parentContainer.RegisterType<IModulesLoaded, MyModulesLoaded>()。
然后再看看IOxiteModule接口。
IOxiteModule接口定义了7个方法:
1: public interface IOxiteModule
2: {
3: void Initialize();
4: void Unload();
5: void RegisterRoutes(RouteCollection routes);
6: void RegisterCatchAllRoutes(RouteCollection routes);
7: void RegisterFilters(IFilterRegistry filterRegistry);
8: void RegisterModelBinders(ModelBinderDictionary modelBinders);
9: void RegisterWithContainer();
10: }
11:
在LoadModules类的Execute方法中的,查看foreach循环:
foreach (XoohooModuleConfigurationElement module in container.Resolve<XoohooConfigurationSection>().Modules)
对oxite配置节点获取moduls节点的所有元素进行遍历。
IOxiteModule moduleInstance = modulesLoaded.Load(config, module);
ModulesLoaded类实例modulesLoaded的Load方法将Module进行实例化,返回IOxiteModule对象。如果正常返回 IOxiteModule对象,就调用如下四个方法: RegisterWithContainer
Initialize
RegisterFilters
RegisterModelBinders
接着:
this.container.RegisterInstance(modulesLoaded);
作用是将”已经加载的Module”注册为单例。
这句写在循环里,为什么不写在循环外面?
接下来的几句是关于后台服务的,略去。
跳出循环后:
1: routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
2:
3: routes.LoadFromModules(modulesLoaded);
4:
5: routes.LoadCatchAllFromModules(modulesLoaded);
6:
第一句代码比较熟悉,排除axd文件的路由。
第二、三行代码,调用RouteCollection类型的扩展方法LoadFromModules和LoadCatchAllFromModules。 这两个方法实际上分别执行所有”已经加载的Module”的IModulesLoaded.LoadFromModules方法和 IModulesLoaded.LoadCatchAllFromModules。
这里有一个疑问,这两行代码为什么没有像调用ModulesLoaded类的RegisterWithContainer,Initialize,RegisterFilters,RegisterModelBinders这四个方法那样调用:
1: routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
2:
3: foreach (OxiteModuleConfigurationElement module in config.Modules)
4: {
5: IOxiteModule moduleInstance = modulesLoaded.Load(config, module);
6:
7: if (moduleInstance != null)
8: {
9: moduleInstance.RegisterWithContainer();
10: moduleInstance.Initialize();
11: moduleInstance.RegisterFilters(filterRegistry);
12: moduleInstance.RegisterModelBinders(modelBinders);
13: moduleInstance.RegisterRoutes(routes);
14: moduleInstance.RegisterCatchAllRoutes(routes);
15:
16: this.container.RegisterInstance(modulesLoaded);
17: //...
18: }
19: }
20:
setupContainer方法接下来注册的类型,除非了ModulesLoaded,其他的目前还不能直接分析。
二、静态方法Load
静态方法Load在被load方法调用,load方法被Applaction_Start方法调用。也就是说,Load方法在站点第一次被访问时,就会被调用。
1: public static void Load(HttpContextBase context)
2: {
3: IEnumerable<IBootStrapperTask> tasks = ((IUnityContainer)context.Application["container"]).ResolveAll<IBootStrapperTask>();
4: bool bootStrappersLoaded = (bool)context.Application["bootStrappersLoaded"];
5: IDictionary<string, object> state = (IDictionary<string, object>)context.Application["bootStrapperState"];
6:
7: if (state == null)
8: {
9: context.Application["bootStrapperState"] = state = new Dictionary<string, object>();
10: }
11:
12: // If the tasks have been executed previously, call Cleanup on them to rollback any changes
13: // they caused.
14: if (bootStrappersLoaded)
15: {
16: foreach (IBootStrapperTask task in tasks)
17: {
18: task.Cleanup(state);
19: }
20: }
21:
22: foreach (IBootStrapperTask task in tasks)
23: {
24: task.Execute(state);
25: }
26:
27: context.Application["bootStrappersLoaded"] = true;
28: }
29:
Application["bootStrapperState"]保存一个状态值,指示站点时候已经初始化。
Application["bootStrappersLoaded"],可以查看后台服务的实现部分。
Load方法首先获取实现IBootStrapperTask接口并注册入依赖注入容器的所有实例并保存入局部变量 IEnumerable<IBootStrapperTask> tasks。通过之前的分析我们知道,目前tasks集合中也就只有一个对象:LoadModules对象。
state部分暂时略去,看下面的部分:
1: if (bootStrappersLoaded)
2: {
3: foreach (IBootStrapperTask task in tasks)
4: {
5: task.Cleanup(state);
6: }
7: }
8:
9: foreach (IBootStrapperTask task in tasks)
10: {
11: task.Execute(state);
12: }
13:
如果站点已经初始化首先进行清理操作,然后再重新执行。
如果需要在运行时候重新加载模块,在其他地方调用Load也是可以的,从而可以实现真正的Module热插拔。
文未校对。但不成为不被拍砖的的理由。
[JQuery]带闰年的日期校验正则表达式
JavaScript作为客户端脚本可以说无处不在,尤其是作为客户端的数据验证尤为重要。而对于数据有效性、合法性的验证,正则表达式起到了举足轻重的作用。现在我们就从时间校验来开始学习正则表达式的用法。
为什么要从时间验证的例子开始我们的正则表达之旅呢?首先,因为时间校验比较常用,尤其是日期的验证几乎是每个注册程序都要进行校验的;另外,日期函数相 对来说包含的涉及正则表达式的内容较少,其次字符单一除了数字之外,就是几个很有限的连接连接符号,正则逻辑简单。
预备知识:
1.为JavaScript增加函数:熟悉Java的人都是知道,Java所有的方法都是封装在类中的,那么JavaScript实际上类似(注意:我并 没有讨论JavaScript和Java的相关性,只是说道他们的相似性);JavaScript中所有的字符串操作实际上都是在String这个类中, 那么我们要为字符串增加一个日期校验的函数应该来怎么做呢?
类名.prototype.方法名 = function (){
/*语句块*/
}
比如我们为字符创类增加一个日期校验的方法IsDate:
String.prototype.IsDate=function(){
}
那么问题来了,这个字符串本身用什么来代表呢?数字面向对象的朋友很快就会想到this,对了就是它;
2.正则表达式的基本知识:
/ 标识正则表达式的开始位置和基础位置
^ 表示字符串的开始位置
$ 表示字符串的结束位置
| 或者
– 描述该字符串的是由(-)左右连个字符之间的所有字符构成
{n,m}有n到m个字符串组成
下面我们就开始日期校验函数的编写
首先我们分析一个完整日期的构成,比如2005-9-13
·年份是后4位数字构成
·月份是由1-12的数字构成
·日期是由1-31的数字构成
·年份可以被四整除为闰年
·2月的闰年为29天,否则为28天
·1、3、5、7、8、10、12每个月的为31天
·4、6、9、11每个月为30天
·如果日期或月份为各位数,那么十位也可以加上0
以上为我们分析的结果,那么根据我们的分析,我们先来构造自己的算法或者说方法。}
年份:能被四整除的了,那么能被4整除的数字都什么特征呢?这是个数学问题了,如果你想不出来,可以再去翻一翻小学的数学书。这里我可以个告诉你以为十位 数为0、2、4、6、8个位为0、4、8以及十位是1、3、5、7、9各位是26可以被四整除。前两位是由0到9的两位数字组成[0-9]{2},后两位 闰年[02468][048]或者是[13579][26],完整闰年就是将这些都连起来([0-9]{2}([02468][048])| ([13579][26]))那么非闰年呢?所有不能被4整除的就是,也就是把闰年的后两位反过来
月份:接下来我们来从最特殊的入手,也就是2月,而其他的月份则与是否闰年无关先来看一下闰年的2月;2月的写法可以使2也可以是02,在正则里应该是 [0]{0,1}[2]或者你也可以(2|02);还有大月(([0]{0,1}(1|3|5|7|8))|(10|12)、小月(([0]{0,1} (4|6))|11)
日期:2月闰年是1到29天,非闰年是1到28天,从正则来考虑,十位是0到2,各位是0到8或0到9组成,闰年:([0][1-9])|([1-2] [0-9],非闰年([0][1-9])|([1][0-9])|([2][0-8]),大月([0][1-9])|([1-2] [0-9])|30|31小月([0][1-9])|([1-2][0-9])|30
至此,我们的零部件已经制作完成,接下就是我们的组装过程,完整日期的合法性是怎么样子的呢?
闰年-2月-闰2月的的日期
或者
非闰年-2月-非闰2月的的日期
或者
年份-大月-大月日期
或者
年份-小月-小月日期
润2月完整日期:(([0-9]{2}([02468][048])|([13579][26])))(-)(2|02)(-)(([0][1-9])|([1-2][0-9]))
非润2月完整日期:(([0-9]{2}([02468][123579])|([13579][01345789])))(-)(2|02)(-)(([0][1-9])|([1][0-9])([2][0-8]))
大月完整日期:([0-9]{4})(-)((([0]{0,1}(1|3|5|7|8))|(10|12))(-)(([0][1-9])|([1-2][0-9])|30|31)
小月完整日期:([0-9]{4})(-)((([0]{0,1}(4|6))|11))(-)(([0][1-9])|([1-2][0-9])|30)
到这里我们的日期校验函数基本算是完工了,唯一要做的就是收尾工作了,把四个完整日期正则用|(或者)连起来,并且把它写到我们为String新增的函数中就算完工了:
String.prototype.IsDate=function(){
var regexp = /^((([0-9]{2}([02468][048])|([13579][26])))(-)(2|02)(-)(([0][1-9])|([1-2][0-9])))|((([0-9]{2}([02468][123579])|([13579][01345789])))(-)(2|02)(-)(([0][1-9])|([1][0-9])([2][0-8])))|(([0-9]{4})(-)((([0]{0,1}(1|3|5|7|8))|(10|12))(-)(([0][1-9])|([1-2][0-9])|30|31))|(([0-9]{4})(-)((([0]{0,1}(4|6))|11))(-)(([0][1-9])|([1-2][0-9])|30))$/g;
return regexp.test(this);
}
从现在开始我们就是这样来判断一个字符串是否为一个合法的日期了:
var strDate ="2005-2-29";
if(strDate.IsDate())
alert("这是一个正确的日期格式");
else
alert("日期格式错误");
[Report]增加的web打印控件 一次打印多份文档的方法
web打印一直是web管理系统的难点,事实证明,采用轻量级的dll控件来实现web打印,是比较理想的解决方案。
采用vc的ATL技术,结合IE的打印模版技术编写的webprint.dll,压缩打包生成dll后,文件下载只有80k左右,是轻量级的打印控件。最近主要实现了如下功能:
1、dll插件的功能修改
1.1增加打印url的属性
描述:当设置这个属性的值为一个url链接,然后再调用Print()函数做打印,就可以将url链接到的网页打印出来。
语法: webprint.printURL=[StringValue];
Webprint.Print(); 或 webprint.preview();
应用于:webprint对象
使用举例:
Webprint.InitPrint();
Webprint.printURL=”http://www.fcsoft.com.cn”;
Webprint.Preview(); //会查看到链接到的页面
注意:webprint是打印小插件的id.
1.2增加打印完成的状态属性
描述:当调用dll控件做打印文档的时候,文档处理完成,成功发送给打印机处理的时候,这个时候,此状态值会变成”over”;即用户在做打印文档的时候,可以通过回调判断这个属性的值来判断文档是否打印完成。
语法:webprint.state
使用举例:
Webprint.InitPrint();
Webprint.Print();
……此处需要使用回调来判断是否打印完成。
If (webprint.state==”over”){
Alert(“打印完成!”);
}
注意:webprint是打印小插件的id
1.3打印预览界面图片显示
原来:调用windows系统目录中的图片,当系统在vista上运行时,不能正常显示。
修改后:图片修改成独立调用的图片,在vista和windows系统上均可以正常显示。
1.4 页边距的设置
原来:在windows系统中设置页边距正常,在vista操作系统上设置会有问题
修改后:四个页边距在windows和vista上设置都没有问题。
1.5 出现无权限问题 :
原来:点击直接打印,后快速点 打印预览,会出现脚本错误,无权限。
修改后:没有这个现象了。
1.6 点击预览页面窗口的关闭后window.close()失效的问题
原来:在预览 页面直接点窗口 的关闭,然后页面的window.close()脚本运行会失效。
修改后:修改了这个问题。
1.7一次打印多份文档方式
利用在页面隐藏帧的方式,每个帧里面加载不同的页面,做一次点击打印多份文档的方式。
1.8打印预览或打印会出现安全问题提示
原来:提示信息为“该页包含潜在的安全隐患,是否继续”
修改后:不再出现此提示。
2、eprint自定义打印工具的升级
2.1 只首页打印,尾页打印,循环页打印
增加了只首页打印,只尾页打印,每页均打印的功能。例如有的文档标题只需要第一页打印,换页后不需要打印标题;有的只尾页需要打印一些落款或签名信息,其它页均不需要打印;有的需要每页都打印。
Webprint4.2中eprint自定义打印工具实现了此功能
设置界面:
在ep_band的属性中,增加打印设置选择项目:
首页打印
每页都打印
尾页打印
如下图所示
设置完成后,在相应的ep_band上放置的信息,就会按照预设置的方式来打印。
2.2 利用eprint一次打印多个模版的示例
利用webprint.dll的printState属性(打印完成后的状态)来做一次打印多份eprint模版的功能。
在实例页面中加如下代码,
//start 一次打印多份模版,将需要打印的模版名放到arr[]中********************************
var arr = new Array();
arr[0]= "sample001";
arr[1]= "sample002";
arr[2]= "sample003";
arr[3]= "sample004";
arr[4]= "sample005";
var iTime=0;
var iframeName = "_print_iframe";
function PrintAll(){
printNextJob(0);
}
function printNextJob(index){
if(iTime == 0 || window.frames(iframeName).oPrintCtl.printState == "over"){
if(iTime != 0) window.clearInterval(iTime);
if(index>=arr.length) {alert("打印完成!"); return;}
Printer.Print(arr[index],true);
document.getElementById(iframeName).onreadystatechange= function () {
if(document.getElementById(iframeName).readyState != "complete") return;
window.frames(iframeName).oPrintCtl.printState ="start";
iTime = window.setInterval("printNextJob("+(index+1)+")",500);
}
}
}
//end****************************************************************
调用printAll();函数后,就可以点击一次,打印5份模版对应的文档。
修改arr[n]=xxx; 达到打印各种不同模版的文档。
实例代码在:eprint for java"samples"samples.htm 页面 点击 一次打印多个
eprint for dotnet"samples"samples.htm 页面 点击 一次打印多个
[IIS]由一个案例引出SQL注入防范(WebKnight),补救(数据修复)的思考
前言
- 生产服务器的代码没有任何被改动过的迹象,排除服务器被入侵,代码被篡改的可能;
- 有病毒下载代码的部分,在数据库里真实看到了篡改迹象,可以排除ARP欺骗的可能;
- 同上,能确定网站被SQL注入攻击了,因为数据库被篡改了。
由于是被攻击后马上发现的现象,可以判断攻击此刻应该正在继续(后来检查的结果证明的确是这样),如何能尽快阻止攻击呢?我有2个思路(如果各位有更好的思路,期待您的指点):
- 马上找到含有SQL注入漏洞的程序,修复之;
- 马上增加一个应用程序防火墙(Application Firewall),从HTTP请求时就阻断攻击。
第一个思路几乎是不可能马上完成的任务,第二个思路还可行,因为前段时间上海天存信息技术有限公司曾提供给我们试用iWall应用防火墙(正式产品是收费的),还记得当时一位朋友提供给我的开源应用程序防火墙WebKnight,我信赖开源,所以我选择了后者。
如何在IIS6中安装WebKnight?
- 首先下载一份WebKnight,下载地址:http://aqtronix.com/?PageID=99#Download(注,这不是直接下载地址,点开后需要再点击WebKnight 2.2 (Release date: 2008.09.02),以防更新后各位朋友还下载旧的版本)
- 解压后有2个目录Setup、Source,其中Source是源码,我们这里只需要安装,进入Setup
- 进入Setup后还有2个目录:w32代表32位;x64代表64位;按照您服务器的操作系统来选择即可,我这里选x64(由于WebKnight的32位、64位文件结构完全相同,所以下面的内容完全适用与32位操作系统)
- 确保自己的每一个网站都运行在独立的应用程序池中;
- 在WebKnight的配置程序中
- 取消选择“Global Filter Capabilities”下的“Is Installed As Global Filter”
- 选择“Logging”下的“Per Process Logging”,这样每一个应用程序池的实例都会加载一个单独的WebKnight实例
- 确保Windows用户NETWORK SERVICE(或您设定的应用程序池的其他用户)有WebKnight文件夹的修改权限
- 拷贝第3步中x64文件夹中的所有文件到服务器上(如:F:\WebKnight\WebSite1\),注意:每一个网站均需要一个独立完整的WebKnight,不可共用
- 打开IIS Manager
- 在需要安装WebKnight的网站上点击右键 > 属性 > ISAPI filters
- 点击添加 > Filter Name随意,如(WebKnight),Excutable选择WebKnight目录下的WebKnight.dll(注意:要选网站所属的WebKnight目录,不要选错)
- 点击确定,完成安装
- 点击WebKnight目录中的Config.exe,具体配置方法见下一节,配置完成后再进行下一步,切记
- 在以上操作后,重新启动IIS(重启IIS其实可以避免,只需将配置WebKnight的网站的应用程序池停止再启动即可)
如何配置WebKnight
在WebKnight目录中(如:F:\WebKnight\WebSite1\),双击Config.exe开始配置,在弹出的Open Configuration对话框中,选择WebKnight.xml
- Scanning Engine 扫描引擎
- 无需更改默认配置
- Incident Response Handling 已发生攻击的处理
- 如果您希望有人攻击时看到的页面是WebKnight目录中的denied.htm,选择Response Directly即可;
- 如 果您希望有人攻击时看到的页面是您网站下的某个文件(如:http://www.xxx.com/Error/Denied.htm),选择 Response Redirect,并在下面的Response Redirect URL中填写您网站下文件的路径(如:/Error/Denied.htm)
- 如果您只希望记录攻击,但不希望中断用户的访问,您可以选择Response Log Only
- Logging 日志
- 如果日志量特别大,请取消选择Enabled,否则很有可能磁盘可用空间不知不觉就没有了,还有可能有比较严重的磁盘I/O性能问题
- 日志默认是存储在WebKnight目录下的LogFiles文件夹中,如果您想改变该路径,可以修改Log Directory的值
- WebKnight每天的日志是由不同文件存储的,默认保存28天的数据,您可以在Log Retention中修改该值
- Connection 连接
- 无需更改默认配置
- Authentication 安全认证
- 无需更改默认配置
- Request Limits 请求限制
- 取消选择Limit Content Length(Content-Length是header中的一个值,代表所请求元素的尺寸),我个人觉得这项没有必要选择,因为元素尺寸有可能很大
- 取消选择Limit URL(即限制URL的长度),原因同上,URL也可能很长
- 取消选择Limit Query String(即查询字符串的长度),原因同上,查询字符串也可能很长
- 取消选择Limit HTTP Version(即HTTP版本),我感觉没有必要限制HTTP版本,有可能会造成使用过旧版本浏览器的用户无法访问自己的网站
- 取消选择Use Max Headers(即限制Headers中各项的最大长度)。我一开始是选择了该项的,但在我的实践中,由于我们用了网站流量统计、广告合作代码等,导致Headers中的一些项超长,阻止了相当多的正常请求,所以我想干脆一劳永逸,取消选择了该项
- URL Scanning 网址扫描
- 取消选择RFC Compliant URL、RFC Compliant HTTP Url、Deny Url HighBitShellCode,勾选了这三项,很多不太标准的URL格式就会无法访问,比如包含中文的URL
- 取消选择Deny URL Backslash,因为我们网站中,“\”在URL里面也会用到
- 在URL Denied Sequences中,描述了拒绝请求的一些URL字符串,如果其中有您网站中正在使用的,可以删除,方法是选中要删除的项目,右键,点击Remove Selected
- Mapped Path 映射目录
- Use Allowed Paths,这项保持勾选,因为这项可以限制Web程序可以访问的服务器上的物理路径,我们需要做的只是在下面的Allowed Paths中添加上我们自己的网站物理路径,比如F:\WebSite1,添加方法是在任意项上点击右键 > Insert Item > 输入物理路径后,回车即可
- Requested File 被请求的文件
- 在Denied Files(拒绝请求的文件)中,去掉网站允许请求的文件,如:log.htm、logfiles
- 在Denied Extensions(拒绝请求的后缀名)中,去掉网站循序请求的后缀名,如:shtm
- Robots 蜘蛛程序
- 无需更改默认配置
- Headers 头信息
- Server Header中,可以修改Header中的Server字段的值,我觉得这个也可以改改,挺好玩的
- 为了防止组织合法的请求,取消勾选RFC Compliant Host Header、Use Denied Headers
- ContentType 内容类型
- 取消选择Use Allowed Content Types,若选中,则无法上传文件
- Cookie 这个就不需要翻译成中文了吧:)
- 无需更改默认配置
- User Agent 用户代理/客户端
- 取消勾选Deny User Agent Empty、Deny User Agent Non RFC,否则有部分合法访问会被拒绝
- Referrer 访问来路
- 取消选择Use Referrer Scanning,因为我觉得一个访问的来路可能不会有太严重的安全问题,还是为了尽量让合法的请求通过,我选择取消勾选该项
- Methods HTTP请求方法
- 无需更改默认配置
- Querystring 查询字符串
- 无需更改默认配置
- Global Filter Capabilities 全局过滤功能
- 取消勾选Is Installed As Global Filter,切记,该项一定要取消选择,否则WebKnight不能正产工作
- SQL Injection SQL 注入
- 无需更改默认配置
- Web Applications Web应用程序
- 勾选Allow File Uploads,否则上传文件的功能会失效
- 勾选Allow Unicode
- 勾选Allow ASP NET
- 如果您的网站需要支持ASP,勾选Allow ASP
- 同理,您的网站需要支持什么,请您自己选择需要勾选的项
修改后,记得通过菜单栏File > Save 来保存配置(或通过快捷键Ctrl+S),保存配置后,就可以重启IIS或应用程序池来启用WebKnight了
如何恢复被篡改的数据
2、如何恢复被篡改的数据
2 FROM disk='X:\BackupName.BAK' —备份文件的路径
3 WITH
4 file=1, —文件的编号
5 move '备份数据文件的逻辑名' TO 'X:\MSSQL\Data\物理文件名_Data.MDF', —数据库逻辑名 to 还原到的物理文件名
6 move '备份日志文件的逻辑名_Log' TO 'X:\MSSQL\Data\物理文件名_Log.LDF' —日志逻辑名 to 还原到的物理文件名
3、当还原成功后,执行下面的语句对数据进行恢复:
如何找到有漏洞的程序
[JQuery]Fckeditor使用手册
1. 新建一个编辑器
Head:
<script type="text/JavaScript" src="editor_path/fckeditor.js"></script>
Html:
<textarea id="content" name="content" style="width: 100%"></textarea>
JavaScript:
var oFCKeditor = new FCKeditor('content'); // content为textarea的id
oFCKeditor.BasePath = "../FCKeditor/"; // editor跟路径
oFCKeditor.Height = "100%"; // 高度度设置
oFCKeditor.Width = "100%"; // 宽度度度设置
oFCKeditor.ToolbarSet = "None"; // 工具条设置
oFCKeditor.Config["ToolbarStartExpanded"] = false; // 属性配置
oFCKeditor.ReplaceTextarea();
2. 操作编辑器
取得对象:
var oEditor = FCKeditorAPI.GetInstance('content ');
取得名字:
var editorName = oEditor.Name;
取得内容:
var content = oEditor.GetXHTML(true);
设置内容:
oEditor.SetHTML(‘html’);
插入内容到当前光标处:
oEditor.InsertHtml(‘html’);
更新内容到textarea:
oEditor.UpdateLinkedField();
要点:
Fckeditor所有方法和属性的第一个单词的首字母都是大写
3. 有用事件
编辑器加载完毕后会自动调用名字为FCKeditor_OnComplete的函数,并将自身作为参数传递进去,一般在FCKeditor_OnComplete函数初始化编辑器
function FCKeditor_OnComplete(editorInstance)
{
// init code
alert(editorInstance.Name) ;
}
[Agent]Agent编程平台的实现,你会晕吗?
该平台于2005年基于DOTNET V1.1实现,版本号V1.0,本文描述一下实现的方案。目前,我正在升级V2.0,该版本Agent将完全兼容OOP,采用面向上下文编程方法实现。这 玩意比较抽象,估计你会晕,如果有晕的地方,就多看几遍,我一开始自己都被自己搞糊涂了。底层应用的技术有 DynamicProxy,ObjectBuilder,Agent技术和面向上下文编程模型。
Object和Agent
什么是Agent?为社么需要Agent?Agent属于人工智能的范畴,它也被称为“代理人”。Agent能够在一定程序上模拟了人类社会的行为和关系,具有一定的智能并能够自主运行。通俗的将,Agent相当于“人”。先用一个场景来看一下为什么需要Agent。
场景为“人物A让人物B关门”,Object模拟:用对象A模拟人物A,对象B模拟人物B,对象B具有关门的方法。在对象A中调用“B.关门()”方法对该场景进行模拟。
该模拟的缺陷如下:
缺陷一:对象是静态的,并不能贴切模拟人的思维等。
缺陷二:“对象A中调用‘B.关门()’方法”模拟该场景时,实际上是对象A的线程执行“B.关门()”方法的代码(与人类活动相左),而现实中是B来执行的。
缺陷三:在该模拟中,对象A调用“B.关门()”方法后,它必须等待该方法执行完毕后才能执行其它操作,这与现实是不一致的(需要异步支持)。
Object用于模拟只能实体,天生就有一些缺陷,相比Agent,它是静态的、被动的和没有思维的。本文针对利用Object模拟现实的缺陷,提出了改进的方案——将Object包装成Agent,使Object具有Agent的特性,方案如下。
缺陷一改进:将消息队列和消息处理器(作用相当于大脑)与Object绑定,使得对象能够模拟人的思维来接收消息并处理。这个消息处理器是一个线程。
缺陷二改进:设计一个调用拦截体系。当一个对象调用另一个对象的方法时,拦截体系能够拦截当前调用,并将该调用封装成一个消息包,发送给目标对象。目标对象的消息处理器从消息队列中获取该消息并处理。
缺陷三改进:拦截体系拦截调用后会根据调用方法的性质(同步或者异步)决定调用线程是否要等待调用结果或直接返回。
本 文把基于拦截体系的OOP平台称为AOP平台。该平台把基于拦截体系改进后的Object称为Agent。在该平台,Agent的本质是基于拦截体系的与 消息队列和消息处理器绑定的对象。上述的Agent能够模拟人与人之间的合作,但是在现实社会,不仅有单个人之间的合作,还存在团队合作。因此,本文提出 了一个Agent组(AgentGroup)概念。利用AgentGroup来模拟一个团队,一个AgentGroup由多个Agent组成。 AgentGroup的本质是与消息队列、消息处理器和消息路由器绑定的Object,并且由一个或几个Agent组成。
Agent和AgentGroup结构

在 本平台,Agent本质上是基于拦截体系的与消息队列和消息处理器绑定的对象。一个Agent至少包含一个消息队列和一个消息处理器。 AgentGroup的本质是与消息队列、消息处理器和消息路由器绑定的Object。一个AgentGroup至少包含一个消息队列、一个消息处理器、 一个消息路由器以及所包含的Agent。AgentGroup的消息队列用来接收、保存和读取与该AgentGroup相关的消息。当系统给一个 Agent发送消息时,如果该Agent属于一个AgentGroup,则这个消息会发送给该Agent所属的组。AgentGroup消息处理器本质也 是一个线程,它的状态图与Agent不同的是它没有“等待调用结果”状态和与该状态相关的转换。AgentGroup消息处理器主要是用来循环的从消息队 列读取消息,然后将该消息发送给消息路由器。消息路由器的作用是将该消息根据某种路由方式路由给该AgentGroup的一个或几个Agent。
拦截体系的实现
该 改进方案的实现依赖于一个拦截体系。拦截体系由3部分组成:(1)目标对象。(2)拦截机对象。(3)目标对象代理。拦截体系的工作机制如下:(1)利 用动态代理技术建立目标对象的代理,该代理暴露了与目标对象相同的接口;(2)调用对象调用目标对象的代理的接口时,拦截体系首先将当前调用封装成调用消 息包,然后建立一个相应拦截机对象,将当前消息作为参数传递给拦截机对象的Invoke方法,由拦截机对象决定如何调用目标对象的方法。

用 户注册一个Agent时,本平台首先创建该Agent的代理对象以及相应的拦截机对象。用户要调用一个Agent方法时,它首先从本平台获取需要调用的 Agent,本平台将会返回该Agent相应的动态代理对象给用户,此时,用户并非获取了Agent,而是Agent的代理对象;当用户调用代理对象的方 法时,拦截体系会从堆栈中取出当前调用信息,并将当前调用转换成一个调用消息包,传递给拦截机对象,拦截机对象会根据当前调用消息包的信息路由给被调用的 Agent或者被调用Agent所在的AgentGroup。因此,要实现一个拦截体系,必须实现:动态代理技术和拦截机。
动态代理技术 可以利用当前流行的OOP平台现成的反射机制和代码生成来完成,它的实现过程如下:(1)利用反射机制获取目标对象的描述信息,这些描述信息包括目标对象 的所有成员和方法的信息;(2)利用代码生成,动态的构建与目标对象的所有成员和方法信息一致的代理对象,并插入相应的拦截机对象。拦截机的作用是在将拦 截体系发送的调用消息包进行处理和路由。拦截机将根据不同的调用方式进行不同的处理。
基于拦截体系的AOP平台,Agent与 Agent、Agent与AgentGroup的调用将不同于Object间的调用。在AOP平台中,因为有了拦截体系,调用者的线程不再进入被调用者的 方法,而是转到拦截机,执行拦截机代码然后等待结果或者返回。该平台允许Agent、AgentGroup间共4种不同的调用方式:Agent与 Agent的同步调用、Agent与Agent的异步调用、Agent与AgentGroup的同步调用和Agent与AgentGroup的异步调用。
[MVC] Oxite2初探
由于Oxite1在ASP.NET MVC1还未发布的时候就早早地发布了,2009年2月15日发布的Oxite1口碑不好,但是即将发布的经过重新开发的Oxite2已经不再羸弱了,有非常多的值得观察与学习的地方。本文讨论的内容是基于Oxite2的,你可以在这里下载到最新的Oxite源码:http://oxite.codeplex.com/SourceControl/ListDownloadableCommits.aspx
欢迎加入Oxite小组一起学习:博客园Oxite小组
既然决定学习Oxite2,并且是有源代码的,那就让我们直接从源码开始吧。
解决方案文件
按照这里的说明,打开Oxite解决方案,VS显示整个解决方案共有七个项目组成:

注:上图移除了Oxite.Database项目
可以看出,解决方案中的七个项目中有五个是被分组放在Modules解决方案虚拟文件夹下进行管理的。剩下两个项目,其中Oxite项目是整个解决方案的最底层和核心架构,其他项目均对Oxite项目有引用关系,而另一个项目是Web应用程序项目。我们先大致看一下OxiteSite Web项目的结构,然后适当的时候转入Oxite项目做重点研究。
OxiteSite Web项目
首先展开OxiteSite Web应用程序项目,如下图:
有以下信息值得留意:
1. 整个Web应用程序项目中除View之外无任何Controller,ActionResult,Model,Filter等。
2. 打开Global.asax文件发现只有一行指令。其中Oxite.OxiteApplication类被放在了Oxite项目中。
<%@ Application Inherits="Oxite.OxiteApplication" Language="C#" %>
……
我们看到Oxite的Web应用程序项目只有一个职责:表现数据。这一点值得借鉴。
Oxite类库项目
因为Global.asax中的OxiteApplication类是在Oxite项目中的,所以很自然的我们打开Oxite类库项目看个究竟。通过该项目的命名、目录信息和其他项目对该项目的引用关系等可以判断Oxite类库项目就是整个解决方案的核心项目,该项目构建了Oxite的基础架构,为各个Module项目所用(Module项目是指放在解决方案文件夹Modules下的所有项目,各Module项目的相关信息通过IOxiteModule接口在Application_start中导入Unity容器。用于借助理解,可以设想将来Modules文件夹下可能还会出现Oxite.BBS、Oxite.Xuefly等)。在Oxite核心项目中定义了大量的基类、接口、基础服务、通用服务、以及必要的扩展方法等。其中IOxiteModule接口,Controller工厂OxiteControllerFactory : DefaultControllerFactory,缓存基础架构(ICacheEntity,IOxiteCacheModule),视图引擎IOxiteViewEngine : IViewEngine,插件机制IPluginEngine等,都是在该项目中定义和实现的。一些通用和底层的Model、Controller、自定义ActionResult等也是在这里定义和实现的。
东西很多,不可能做到在一篇随笔中全部包含。现在就让我们针对OxiteApplication : HttpApplication和IOxiteModule来考察一番吧。
OxiteApplication类
很明显画有下滑线的那些方法都是具有特定前缀名的方法,也是我们已经很熟悉的Application的编程接口。其他方法是用于画有下划线的方法的辅助方法。下面一一列举各个方法的作用:如果对相关方法的执行时机和顺序不太了解的话可以查看MSDN
Application_Start()方法:
双击上面类图中的Application_Start()方法或者选中该方法后按下F7进入代码,如下
/// <summary>
/// Setup and load application
/// 设置和加载应用程序
/// </summary>
protected void Application_Start()
{
Application["container"] = setupContainer();
Application["bootStrappersLoaded"] = false;
load();
}
作用见注释,注意到Unity容器被放在了Application["container"]中。
setupContainer()方法:
光标移动至Application_Start()中的setupContainer()调用上,按下F12进入setupContainer()的定义。
在该方法中创建Oxite所使用的Unity容器(IOC)并注册相关数据。,如果你对Unity不熟悉可以参考TerryLee的这篇文章,或者干脆下载Unity的源码和文档,通过看单元测试代码或者阅读仅仅200页的文档来学习也是一个不错的途径。
load()方法:
/// <summary>
/// Initialize any needed info for Oxite.
/// </summary>
private void load()
{
Load(new HttpContextWrapper(Context));
}
Load(HttpContextBase context)方法:
执行所有导入任务以导入各个模块(即实现了IOxiteModule接口的*Module)中注册的信息。注意到一个有趣的接口IBootStrapperTask(Boot Strapper是导入彪形大汉之意,我记得Visual Studio中的某个用于安装.NET的文件夹就是叫这么个名字,.NET团队把.NET比作彪形大汉,估计是寓意.NET很强大和很能干之意)。在Load(HttpContextBase context)方法中同样使用Unity容器取得注册数据。
因为Load方法中的主要逻辑是执行IBootStrapperTask接口的方法,所以我们看一下Unity容器中注册的是什么类型的彪形大汉(IBootStrapperTask)?Application_Start() è setupContainer()找到如下代码:
parentContainer.RegisterInstance<IBootStrapperTask>("LoadModules", new LoadModules(parentContainer));
可以看到,频繁传递的唯一的IUnityContainer实例中注册的是LoadModules : IBootStrapperTask类型的彪形大汉(IBootStrapperTask)。是在LoadModules 中IBootStrapperTask接口的Execute方法中对各个模块(即实现了IOxiteModule接口的*Module)进行循环注册信息到Unity容器的。如果观察一下Modules解决方案文件夹下的其他“模块项目”的话,就会发现Oxite是把各个“模块项目”相关的信息(Route、Filter、Binder、I**Repository、服务、等)分别放在各自项目的“模块(*Module)”中注册的。可以这样认为:所谓IOxiteModule接口其实就是抽象出来用于统一注册信息用的。我们明确定义所谓“模块”就是指继承自IOxiteModule接口的类,减少交流时的概念不统一障碍。
IOxiteModule接口:
public interface IOxiteModule
{
void Initialize();
void Unload();
void RegisterRoutes(RouteCollection routes);
void RegisterCatchAllRoutes(RouteCollection routes);
void RegisterFilters(IFilterRegistry filterRegistry);
void RegisterModelBinders(ModelBinderDictionary modelBinders);
void RegisterWithContainer();
}
Application_BeginRequest方法:
该方法中有两个逻辑
1, 如果尚未安装站点,即ISiteService的GetSite()方法返回空,即数据库中没有站点存在的话,导向安装页。
2, 核实进来的Uri,判断域名是否跟我们存在数据库中的Uri域名信息一致,否则尝试跳转。
另外该方法最后保存了Application_BeginRequest之前,即上次请求的请求上下文信息。
hasSameHostAsRequest和makeHostMatchRequest这两个私有方法的用途很明显就不说了。
OxiteApplication_AcquireRequestState方法:
该方法是Application的AcquireRequestState事件方法,当 ASP.NET 获取与当前请求关联的当前状态(如会话状态)时执行。该方法对于交由ASP.NET处理的每次请求都会执行,在该方法的逻辑中对包含当前请求上下文信息HttpContext实例中的IHttpHandler属性值的类型进行了判读,如果当前请求是被MvcHandler处 理程序处理的请求的话,接下来会验证用户状态并保存用户和站点的一些相关信息到当前请求上下文中,否则不做处理(由于IIS7将所有请求交由 ASP.NET处理,所以对于静态图片的请求也是会执行该方法的,但因为默认情况下图片并没有被配置到ASP.NET处理程序,所以不用担心OxiteApplication_AcquireRequestState中的代码会被执行)。
OxiteApplication_AcquireRequestState方法
private void OxiteApplication_AcquireRequestState(object sender, EventArgs e)
{
MvcHandler handler = Context.Handler as MvcHandler;
if (handler != null)
{
RequestContext requestContext = handler.RequestContext;
if (requestContext != null)
{
IUnityContainer container = ((IUnityContainer)Application["container"]);
if (container != null)
{
IModulesLoaded modules = container.Resolve<IModulesLoaded>();
if (modules != null)
{
IUser user = new UserAnonymous();
foreach (IOxiteAuthenticationModule module in modules.GetModules<IOxiteAuthenticationModule>().Reverse())
{
user = module.GetUser(requestContext);
if (user.IsAuthenticated)
break;
}
Context.Items[typeof(Site).FullName] = container.Resolve<ISiteService>().GetSite();
Context.Items[typeof(IUser).FullName] = user;
Context.Items[typeof(RequestContext).FullName] = requestContext;
}
}
}
}
}OxiteApplication类介绍完毕。
建立Oxite小组是 为了通过对Oxite2源码的观察和学习最终得到Oxite开发小组对ASP.NET MVC的一些处理和使用经验。对于ASP.NET MVC我们已经有很多人熟悉了,但可能像我一样仅仅只是做过Demo还没有在实际项目中使用过,我们缺少的正是ASP.NET MVC在实际项目中的使用和处理经验,希望大家通过参与Oxite小组发 现信息、总结经验,甚至形成模式。人与人之间的高效交流需要具备一个统一的知识载体,对于我们各自的ASP.NET MVC知识来说现在正是没有这样一个载体,所以才会交流困难,难以协作,存在误解。怎么办?好办,找个项目作为个体知识的共同载体吧!Oxite2挺好 ^_^,我觉得,你觉得呢?
Mikel
下载(2.10MB): 
