
http://blog.csdn.net/jiankunking
与当前系统的用户用户组集成,可以使用视图。 用sql组织现有系统的用户组织等信息,只需要保证与之前activiti物理表名称结构一致即可。 通过视图过渡实现与现有系统中用户组织等的集成(这样就不需要同步用户数据了)。 图片摘自《Activiti实战》 Activiti实战下载地址:这里写链接内容
什么是 Spring 视图和视图解析器? Spring MVC(Model View Controller)是 Spring 中一个重要的组成部分,而 Spring 视图和视图解析器则是 Spring MVC 中的组成部分。在介绍 Spring 视图和视图解析器前,我们先了解下在 Spring MVC 框架中,一个 Web 请求所需经历的六个阶段: 请求会首先被 Spring MVC 的前端请求分发器(Dispatcher)拦截。该拦截器是一个 Servlet, 需要在 web.xml 中配置,所有符合所配置的 URL 样式的访问请求,将都会被该拦截器拦截。Spring 提供了默认的分发器 org.springframework.web.servlet.DispatcherServlet,您可以根据需要,决定是否需要定制自己的分发器。 在接收到访问请求后,分发器会根据开发人员在 Spring 配置文件或代码中的注解(Annotation),来查找合适的控制器。 分发器在查找到合适的控制器后,将请求转交给该控制器处理。 通常,控制器会调用相应服务类来处理业务逻辑,在将请求处理后,控制器需返回处理后的结果数据模型(Model)以及下一个需要显示的视图名。 在控制器处理结束并返回模型和视图名之后,Spring 会依次调用 Spring 容器中所注册的视图解析器,来查找符合条件的视图。 在获得 Spring 视图后,Spring 会根据该视图的配置信息,显示该视图。 图 1.Spring MVC 处理流程 通过以上 Spring MVC 的介绍,我们可以发现,视图和视图解析器将出现在整个请求处理流程中的最后部分。那么到底什么是视图和视图解析器?简而言之,视图是指 Spring MVC 中的 V(View),而视图解析器的功能则是依据指定的规则来查找相应的视图。 常用视图和视图解析器简介 在开发中,视图通常就是 JSP、Velocity 等。Spring 默认提供了多种视图解析器,比如,我们可以使用最常用解析器 InternalResourceViewResolver 来查找 JSP 视图(与之相对应的视图类为 InternalResourceView)。通常,一个视图解析器只能查找一个或多个特定类型的视图,在遇到 Spring 不支持的视图或者我们要自定义视图查找规则的情况下,我们就可以通过扩展 Spring 来自定义自己所需的视图解析器。目前,视图解析器都需要实现接口 org.springframework.web.servlet.ViewResolver, 它包含方法 resolveViewName,该方法会通过视图名查找并返回 Spring 视图对象。表 1 列出了常用的 Spring 视图解析器。 表 1.Spring 常用视图解析器列表 视图解析器 描述 XmlViewResolver 接口 ViewResolver 的实现,从 XML 配置文件中查找视图实现(默认 XML 配置文件为 /WEB-INF/views.xml) ResourceBundleViewResolver 接口 ViewResolver 的实现,用于从 properties 文件中查找视图。 UrlBasedViewResolver 接口 ViewResolver 的实现,用于根据请求的 URL 路径返回相应的视图,该视图需为抽象类 AbstractUrlBasedView 的实现,它还有些子类,如 InternalResourceView 和 JstlView 等 . InternalResourceViewResolver UrlBasedViewResolver 的子类,通常用于查找 JSP(类 InternalResourceView)和 JSTL(类 JstlView,InternalResourceView 的子类)等视图。 VelocityViewResolver /FreeMarkerViewResolver UrlBasedViewResolver 的子类分别用于支持 Velocity(类 VelocityView)和 FreeMark 视图(类 FreeMarkerView)。 ContentNegotiatingViewResolver 接口 ViewResolver 的实现,用于根据请求文件的后缀名或请求的 header 中的 accept 字段查找视图。 在多数项目中,InternalResourceViewResolver 是最常用的,该解析器可以返回指定目录下指定后缀的文件,它支持 JSP 及 JSTL 等视图技术,但是用该视图解析器时,需要注意设置好正确的优先级,因为该视图解析器即使没有找到正确的文件,也会返回一个视图,而不是返回 null,这样优先级比该视图解析器低的解析器,将不会被执行。 在 Web 开发中,我们的前端显示可以是 JSP、Excel、Velocity 等,在 Spring 中,不同的前端显示技术都有其对应的 Java 视图类,正如表 1 所提到的,InternalResourceView 可以代表 JSP 视图,FreeMarkerView 代表 FreeMarker 视图。目前,Spring 支持多种技术开发的视图,包括 JSP、JSTL、Excel,Velocity 等,在多数项目中,用户并不需要自定义自己的视图。 本文整理自:开发 Spring 自定义视图和视图解析器 整理人:jiankunking 出处:http://blog.csdn.net/jiankunking
疑问: 在软件系统中,由于应用环境的变化,常常需要将“一些现存的对象”放在新的环境中应用,但是新环境要求的接口是这些现存对象所不满足的。 如何应对这种“迁移的变化”? 如何既能利用现有对象的良好实现,同时又能满足新的应用环境所要求的接口? 定义: 将一个类的接口转换成客户希望的另一个接口。Adapter模式使得原本由于接口不兼容而不能一起工作的那些类可以一起工作。 ——《设计模式》GoF 一、对象适配器 对象适配器采用对象组合,通过引用一个类与另一个类接口 在对象适配器中通过组合获得Adaptee对象 通过调用Adaptee对象的方法,转换后返回Target结果。 二、类适配器 类适配器通过多继承对一个接口与另一个接口进行匹配。 Target定义了Client使用的与特定领域相关的接口,Client通过调用Target实现某一个特定的操作。Adaptee是一个已经存在的类,需要与Target协同工作,这个接口需要适配。Adapter适配器适配Adaptee和Target接口。在类适配器中,通过继承获得Adaptee中的方法。 .NET不支持多重继承,因此当Target是一个类,而不是一个接口时无法实现类适配器,这时需要使用对象适配器。 三、生活中的例子 在国内使用的电源供电电压为220V,美国为380V,当你出差到美国,你的电器需要220V的电压,但旅馆里不提供220V,只提供380,所以,你到市场买了一个电源适配器,在接上适配器后,旅馆里的电源就可以使用在你的电器上了。 Target:标准电源 Adaptee:美国电源 Adapter:适配器 1、实现-Target /// <summary> /// 目标:Target /// </summary> public class StandardPower { /// <summary> /// 供电 /// </summary> /// <returns></returns> public virtual int SupplyPower() { Console.Write("正常SupplyPower,电压:"); return 220; } } 2、实现-Adaptee /// <summary> /// 要适配的对象:Adaptee /// </summary> public class AmericanPower { public int SupplyPower() { Console.Write("在美国供给380V电压"); return 380; } } 3、实现-Adapter /// <summary> /// 适配器:Adapter /// </summary> public class PowerAdapter : StandardPower { /// <summary> /// 当地电源 /// </summary> private AmericanPower localPower = new AmericanPower(); public override int SupplyPower() { int v = localPower.SupplyPower(); if (v != 220) { //这里做一些转换工作 v = 220; } Console.WriteLine("转换后的电压:{0}", v.ToString()); return v;//转换后,即适配后 } } 4、实现-使用 StandardPower power = null; //在中国的时候 power = new StandardPower(); Console.WriteLine(power.SupplyPower()); //到美国以后,买一个适配器 power = new PowerAdapter(); Console.WriteLine("适配后提SupplyPower源电压为:{0}", power.SupplyPower()); //让控制台 等待 Console.ReadLine(); 四、.NET中的Adapter模式 1.适配器模式在.NET Framework中的一个最大的应用就是COM Interop。 COM Interop就好像是COM和.NET之间的一座桥梁(关于COM互操作更多内容可以参考我的互操作系列)。COM组件对象与.NET类对象是完全不同的,但为了使.NET程序 象使用.NET对象一样使用COM组件,微软在处理方式上采用了Adapter模式,对COM对象进行包装,这个包装类就是RCW(Runtime Callable Wrapper)。RCW实际上是runtime生成的一个.NET类,它包装了COM组件的方法,并内部实现对COM组件的调用。如下图所示: 2..NET中的另外一个适配器模式的应用就是DataAdapter。 ADO.NET为统一的数据访问提供了多个接口和基类,其中最重要的接口之一是IdataAdapter。DataAdpter起到了数据库到DataSet桥接器的作用,使应用程序的数据操作统一到DataSet上,而与具体的数据库类型无关。甚至可以针对特殊的数据源编制自己的DataAdpter,从而使我们的应用程序与这些特殊的数据源相兼容。 五、实现要点 适配器模式重在转换接口,它能够使原本不能在一起工作的两个类一起工作,所以经常用在类库复用,代码迁移等方面,有一种亡羊补牢的味道 类适配器和对象适配器可以根据具体实际情况来选用,但一般情况建议使用对象适配器模式 六、效果 通过类的继承或者对象的组合转换已有的接口为目标接口 七、适用性 需要使用一个已经存在的类,但接口与设计要求不符。 希望创建一个可以复用的类,该类可以与其他不相关的类或者是将来不可预见的类协同工作。 八、总结 Adapter模式主要应用于“希望复用一些现存的类,但是接口又与复用环境要求不一致的情况” ,在遗留代码复用、类库迁移等方面非常有用。 GoF 23 定义了两种Adapter模式的实现结构:对象适配器和类适配器。但类适配器采用“多继承”的实现方式,带来了不良的高耦合,所以一般不推荐使用。对象适配器采用“对象组合”的方式,更符合松耦合精神。 Adapter模式可以实现的非常灵活,不必拘泥于Gof23中定义的两种结构。例如,完全可以将Adapter模式中的“现存对象”作为新的接口方法参数,来达到适配的目的。 Adapter模式本身要求我们尽可能地使用“面向接口的编程”风格,这样才能在后期很方便地适配。 作者:jiankunking 出处:http://blog.csdn.net/jiankunking 源码下载:http://download.csdn.net/detail/xunzaosiyecao/9543274 小注: 本文部分资料整理自网络,在此表示感谢。 1、 C#设计模式(7)——适配器模式(Adapter Pattern) 2、本杰.NET 张波的PPT资料
最近搞坏了一次TFS,在修复的过程中发现TFS的安装复杂程度(与其他源码管理工具对比))令人发指啊。 此处以在windows server 2008上的安装Team Foundation Server 2010为例: 一、搭建IIS 此处安装默认的勾选项即可: 二、新建Windows 账户 a) TFSADMIN – 用于安装SQL Server,TFS等,该账户要求管理员权限,也就是将其加入到Administrators组中。 b) TFSSERVICE – 这个账户用于所有服务账户,不要加入到Administrators组中。 三、安装Sql Server 1、设置角色选择:SQL Server功能安装 2、必须安装报表服务 3、实例配置:默认实例 4、将空白账户选择到我们之前创建的账户,正确输入密码(Windwos 用户账户密码),将SQL Server Browser选择到自动启动: 5、这里身份验证可以选择只使用Windows验证,也可以选择混合模式,但建议选择只使用Windows验证,安全性高,另外将Admin组和之前创建的账户加入到管理员中: 6、Analysis账户设置同样: 接下来配置Reporting Service,这里可以选择默认配置或不配置,如果选择不配置那么等一下需要手动配置报表服务: 等待Sql Server安装完成。 四、安装TFS 1、如果单服务器的话建议选择所有角色: 2、安装完成后进入配置界面,这里我们选择高级: 3、配置数据库: 4、配置账户: 5、配置应用层(使用默认设置即可): 6、配置报告 Reporting Services(使用默认设置即可): Analysis Services (使用默认设置即可): 报表读者账户: 7、配置SharePoint服务: 8、项目集合(使用默认设置即可): 9、检测通过后开始安装 安装完成搞定! 本文参考:http://www.cnblogs.com/WilsonWu/archive/2011/11/24/2261674.html 至于为什么已经有一篇了,还要重写一篇?难道你没发现网上的资料不是木有图片就是图片超级小,根本看不清楚吗? 作者:jiankunking 出处:http://blog.csdn.net/jiankunking
本文由 伯乐在线 - kmokidd 翻译。未经许可,禁止转载!英文出处:smashingmagazine。欢迎加入翻译组。 你是否曾经见过像 $(".cta").click(function(){})这样的JavaScrip代码?或许你还会思考下 $('#X') 是什么,如果看到这些你都觉得摸不着头脑,那请一定要读完这篇文章。如果你觉得上述的代码片段是不能正常工作的,那请先看一些jQuery的代码范例,你会发现链接中的代码都是这样的结构。 这篇文章将会分析下面代码片段(动画化一个方形)中出现的一些关键知识点。你可能不会经常接触这样的代码,但了解一下这段代码的机制有助于你理解jQuery: JavaScript 1 2 3 4 5 $(document).ready(function(){ $("button").click(function(){ $("div").animate({height:"toggle"}).append("hi"); }); }); 我们将会逐字逐句地解释上述的代码,告诉你JavaScript函数、jQuery对象还有事件驱动编程的具体细节。希望看完这篇文章以后,再遇到神秘的jQuery代码时你不会再头疼。 $是什么? 在你第一眼看到$的时候,有一种高大上的猜测在你心中盘旋:这一定是个很特别很复杂的JS方法。事实上,它很普通,也没有什么特殊的含义。$就是一个函数,是jQuery函数的另一个名字罢了。 jQuery是一个比较简单的JavaScript库,它在浏览器兼容方面做得很好,而且还提供了许多很有用的特性用来操作网页或者做些动画效果。你可以先引入jQuery库的地址,然后就能使用jQuery函数(比如$)了: JavaScript 1 <script src="http://code.jquery.com/jquery-1.11.1.min.js"></script> 或者你也可以直接在jQuery官网上下载它。 jQuery函数通常只需要一个参数,这个参数可以是一个选择器也可以是JS引用页面上的内容(比如document)。 选择器就是CSS的一个片段,写在{…}之前的内容就是选择器了。所以,$(“div”)和jQuery(“div”)是一个意思,就是简单粗暴地把页面上所有的<div>标签都选中,和在CSS中使用.div获得的是同一个结果。 CSS 1 2 3 <style> div {…} </style> 还记得在代码的最开头有一个$(document)吗?这一步是要把JS变量document传入jQuery方法当中。document是由浏览器来定义的,可以直接使用,它指的是文档对象模型(DOM)的最顶层。DOM指的是各个浏览器是如何来解释页面的整个HTML结构。用jQuery写的程序是基于DOM的。jQuery中的$(‘div’)和document.getElementsByTagNae(“div”)得到的结果大致上是一样的。 关键点 $只是一个方法,它是jQuery方法的简写也是它另一个名字。 点 在$(document)之后的点(’.’)预示着有许多方法可以被调用。一定要是一个JS对象才能使用这个点。说个最简单的,一个JS对象就是一堆属性的集合: JavaScript 1 2 3 4 var digger = new Object(); digger.species = "gerbil"; digger.name = "Digger"; digger.color = "white"; 上面代码中,变量digger是一个对象,我们赋值了三个子对象给它:species,name和color。在面向对象编程中,这三个变量被称为成员变量。你可能更简洁地写成这样: JavaScript 1 var digger = {species:"gerbil", name:"Digger", color:"white"}; 你也可以把方法当做属性赋值给一个变量。沙鼠(Gerbil)大部分时候都很安静,但他们偶尔也会发出高频meeping sort of noise。在JS中,可以这么来表示: JavaScript 1 2 3 function meepMeep(){ alert("meep meep"); } 在JS中,变量、方法和对象之间的界限是很模糊的,所以一个方法也可以被赋值到一个(成员)变量上: JavaScript 1 digger.speak = meepMeep; 现在你可一个调用这个方法来让沙鼠发出叫声来: JavaScript 1 digger.speak(); 在面向对象语法中,digger.speak();是一个成员方法或者函数。在同一个对象中的方法可以互相引用,它们还能引用其他的成员变量。想象一下Digger学会了说英语,这得是多牛X的一件事啊: JavaScript 1 2 3 4 5 6 7 function myNameIs(){ alert("Meep! I am a " + this.species); } //assign the function digger.sayMyName = myNameIs; //call the function digger.sayMyName(); 在myNameIs函数中,this指的是包含它的对象,this.species就是digger.species,它的值就是’gerbil’。如果你想要不通过对象直接调用myNameIs(),那么this指的就是JS的window对象,this.species就是window.species,,在这里它的值是undefined。页面的弹框中的文字会变成”Meep! I am a undefined”。 对象也可以作为函数的返回值,我个人也推荐这么使用: JavaScript 1 2 3 function giveMeTheGerbil(){ return digger; } 这么写的话,返回的就是(全局)变量/对象digger的引用,它和最初的digger是完全一样的,所以操作它的时候你不需要有什么顾虑。 JavaScript 1 2 3 var digger2 = giveMeTheGerbil(); //alerts "Meep! I am a gerbil" digger2.sayMyName(); 你也可以不通过digger2这个中间值,而是直接在giveMeTheGerbil的返回值上调用sayMayName: JavaScript 1 giveMeTheGerbil().sayMyName(); 先不考虑内部代码,这种程序结构和例子里第一行代码是一样的: JavaScript 1 $(document).ready(…); 下一节你将知道ready的作用是什么。 关键点将对象简写:{name:”Digger”, species:”gerbil”},在方法中使用到的this是依附于一个对象(或者一个方法)的,它指向包含它的对象。 匿名函数 在JS中,创建函数的方法多种多样。只要你有一点编程经验那对下面的函数声明就不会陌生: JavaScript 1 2 3 function meepMeep(){ alert("meep meep"); } 在上文里我们已经知道了函数是可以被赋值到变量上的。我们创建了meepMeep函数,并将它赋值到digger.speak上。事实上,函数还可以被匿名地创建出来(我们称呼这样的函数为:函数表达式),它们在声明时是没有任何名字的,声明后再被赋值到一个变量上: JavaScript 1 2 3 var meepMeep = function(){ alert("meep meep"); }; 在JS中,函数可以被赋值到变量上,还能像变量一样到处传递。让我们看看下面这个例子: JavaScript 1 2 3 function runMe(f){ f(); } runMe函数有一个传入参数f,它将这个传入参数视作一个函数,还调用的这个函数。所以你可以这么使用runMe: JavaScript 1 2 3 runMe(function(){ alert("meep meep"); }); 这样meepMeep函数就会被成功调用。如果在这个方法里,你连meepMeep的名字都不需要了,那事情就会更有趣些了。你可以直接创建它,当需要的时候再把它传入runMe来调用这个函数: JavaScript 1 meepMeep(); 事实上,哪里都会出现meepMeep,等同于它的匿名函数也是这样的。这么调用: JavaScript 1 2 3 (function(){ alert("meep meep"); })(); 不像上面那样,你可以用匿名函数替换掉meepMeep,虽然使用匿名函数的时候你需要在最外层添加一组括号: 在JS中,这种写法常常是用在制造变量作用域上。你能不能猜到下面这段代码的输出是什么呢? JavaScript 1 2 3 4 5 var x=3; (function(){ var x=4; console.log("x is " + x); })(); console.log ("x is " + x); 在匿名函数里的var是解题的关键点。通过var,我们在函数内定义了一个局部变量x,它的值是4,然后通过console.log输出这个值。因为var这个关键词,函数内的x和函数外的值为3的x就是互相独立的。因此这段代码会将4和3先后打印出来。 现在我们的沙鼠已经不会发出尖锐的声音了,所以在代码中我们不再使用alert改用console.log来打印它的结果。在现代浏览器中console.log*是可以使用的(换言之,IE浏览器低版本中无法使用它),使用console.log就能安静地在浏览器控制台中输出信息。 我们接着就要讲匿名函数了。jQuery的ready方法可以说是上文中的runMe函数的延时版。ready方法中的内容会等到DOM完全加载完会后在运行。所以等到document加载完成了,下面的匿名函数才会运行: JavaScript 1 2 3 function(){ $("button").click (…) } 如果需要在HTML文档加载完后再执行一些动作的话,程序员们通常会使用$(document).ready(…)。 关键点 匿名函数就是没有名字的函数,像function(){alert(1);}这样。它们可以被赋值到变量上、被传递到其他函数中也可以立即执行以创建出一个作用域来。 方法链 在更详细地分析代码之前,我们要先介绍JS中一个常见的内容:方法链。方法链指的是在一行代码中执行多个函数。这真的只是上述giveMeTheGerbil()的一个扩展: JavaScript 1 giveMeTheGerbil().sayMyName(); 现在让我们要重新定义一下gerbil相关的方法来返回他们的引用。 JavaScript 1 2 3 4 5 6 digger.speak = function(){ alert("meep meep"); return this; } digger.sayMyName = function(){ alert("Meep! I am a " + this.species); return this; } 这两个函数都是对digger做了一些处理后返回digger对象。代码没有做什么改动,但是将digger对象返回以后,就可以把函数串在一起使用: JavaScript 1 giveMeTheGerbil().speak().sayMyName().speak(); giveMeTheGerbil先运行,返回了digger对象的引用。所以上面那行代码等价于: JavaScript 1 digger.speak().sayMyName().speak(); 下一步,digger对象的speak方法运行后弹窗出’meep meep’。这也能返回digger的引用,然后这行代码就变成: JavaScript 1 digger.sayMyName().speak(); 在这之后,sayMyName运行后返回digger的引用……运行后会出现三个警告框:‘meep meep. Meep! I am a gerbil, meep meep’。这样的链式效果常常出现在JS中,你可能在字符串(string)对象中见到这个: 1 2 var s = &quot;I have a dagger.&quot;; console.log(s.substring(9, 15).replace(&quot;a&quot;, &quot;i&quot;).toUpperCase()); 上面的代码是获取字符串s中的子字符串,再将子字符串中的字母’a’用’i’代替,替换后的结果(也就是’digger’)被转为大写,然后返回打印到控制台上。 当然,jQuery中到处都是方法链,在我们的例子中也能看到: JavaScript 1 $("div").animate({height:"toggle"}).append("hi"); $(“div”)将页面上所有的div元素获取到然后作为jQuery对象的一部分返回。基于jQuery对象调用animate方法,然后再在每个jQuery对象上执行append。这样的作用链可以很长很长,下面这个是典型的长jQuery方法链: 总的来说,使用这样的长方法链会造成debug和维护代码的困难。所以尽量避免使用这样的长链,不过在压缩时它们还是常常被使用。 关键点 对象(比如对象中的方法)的方法会返回对象的引用,然后就能基于引用使用方法链,而不需要在执行多个方法的时候还要储存中间值。 jQuery对象 我们的例子里用了好几个jQuery方法:ready、click、animate和append。这些方法都是与jQuery对象结合使用的,和上文中digger对象的speak方法和myNameIs方法类似的机制,也和string对象的substr方法、replace方法和toUpperCase方法类似。 这些函数都是jQuery对象的方法,它们也都会返回一个jQuery对象。不过比起我们例子里的digger对象和string对象,jQuery对象相对而言要复杂许多。就像早前提过的,JS中各个概念之前的界限其实比较模糊。你可以在使用方法链的时候把它视作一个对象,但是你也可以把它当做一个数组来对待: JavaScript 1 2 var mydivs = $("div"); for (var i = 0; i < mydivs.length; i++) {console.log(mydivs[i].innerHTML);} 在这里例子中,$(“div”)将页面上所有的div元素都存储一个jQuery对象中,然后赋值到变量mydivs中。这个jQuery对象会被当做一个数组(其实是一个NodeList)进入迭代。每次迭代都会对DOM中选出的节点做一些操作,这些节点在迭代里也是当做对象的,所以它们也有自己的属性,比如outerHTML和innerHTML。 也可以先把这些节点转成jQuery对象,也就是在取得节点后将它们用$()包起来(你可以把任何代码传入$中,都能将它们转成jQuery对象),再之后通过jQuery方法html()也可以得到相同的结果。 JavaScript 1 2 var mydivs = $("div"); for (var i = 0; i < mydivs.length; i++) {console.log($(mydivs[i]).html());} 上面两个方法都可以将页面上的div元素中的HTML内容打印到控制台中。 当你在运行像$(“div”).animate(…).append(…);这样的代码的时候,动画是会发生在所有的div元素上的,然后这些div元素会被作为jQuery对象的一部分传到方法链中的下一个函数中(在大部分jQuery函数中都是这么实现的,具体请看文档)。 关键点 jQuery的$函数还有像click、animate这样会返回jQuery对象的方法,它们都是对象或者数组的一部分。类似数组的这部分会包含DOM中节点的引用。 总的来看 现在我们可以全局地来看这个例子了,$(document)返回的是页面本身的jQuery对象。将一个方法传入.ready(…)中,等到页面已经解析完了DOM也已经加载完成,ready(…)中的方法就会运行。 JavaScript 1 2 3 function(){ $("button").click(…); } 这个方法将页面中的button元素都获取到了,然后返回一个绑定了click方法的jQuery对象。click方法中还有一个匿名函数: JavaScript 1 2 3 function(){ $("div").animate ({height:"toggle"}).append("hi"); } 上述的函数获取了所有的div元素,然后返回一个jQuery对象,在这个对象上显示调用了它的animate方法。传入jQuery的animate方法中的参数是animate的一系列属性,这些属性是对象的简写形式,{height:”toggle”}这句是告诉jQuery对页面上所有的div元素的高度都使用toggle效果:一开始div的高度会变成0,接着它们的高度又会动画地变回原来的值。 animate方法也会返回一个jQuery对象,执行完animate方法后执行append方法:每当button被点击了,就在每个div元素中添加”hi”字符串。运行下面的HTML代码来看看我们说的效果是什么样的,在线demo在此: XHTML 1 2 3 4 5 6 7 8 9 10 <button>Click me</button> <div style="width:100px;height:100px;background:green;"></div> <script src="http://code.jquery.com/jquery-1.8.3.js"></script> <script> $(document).ready(function(){ $("button").click(function(){ $("div").animate({height:"toggle"}).append("hi"); }); }); </script> 每次button被点击了,绿色的div就会收起或者展开,然后添加一个新的“hi”到div中。 事件驱动造成的问题 下面这段代码看起来够简单的吧: JavaScript 1 2 3 4 5 6 7 8 //set h to 200 var h = 200; $(document).ready(function(){ $("button").click(function(){ //animate up to h, 200 pixels high $("div").animate({height:h}); }); }); 你可能只是希望div的高度到200px,但是事实上从*h*被赋值为200到动画真正发生之间还可能发生了很多事情导致最终的结果和你所期望的不一样。在一个复杂的jQuery应用中,变量*h*可能会被反复使用或者它的值被改写。你可能会发现div的高度只会达到50px而不是期望中的200px。这时候你需要去看看是不是别的代码改写了h的值,当执行*for (h=1; h<50; h++) {…}*来改变h的值时,你可能会有所发现。 坦白来说,这个问题并不是由jQuery或者匿名函数造成的,而是事件驱动编程本身就会遇到的问题。上述的代码的片段其实是在不同的时间点被执行的: 首次执行时($(document).ready(…)) 页面加载完成后($(“button”).click(…)) button被点击后($(“div”).animate(…)) 服务端的代码(比如PHP的程序)运行是有按照从头到尾的顺序的, 从开始到结束,输入HTML以显示页面。JS也可以做到这一点,但是它如果和事件结合起来才能发挥最大作用,比如button点击事件。这就是事件驱动编程,可不仅仅只有JS是这样的编程哦。手机应用背后的程序很多也都是事件驱动的,比如Objective-C、Java或者C++在处理用户与屏幕互动这块也是使用事件驱动编程的。 如果上面的代码转成Java后再Android手机中运行,那么在最里层的函数中的h的引用就会出现错误。这是因为h并没有被声明为全局(或者是Java中的static)变量,所以里层的代码不知道h的值应该是什么。虽然了解这点也解决不了事件驱动造成的问题,不过至少以后你会想清楚要怎么使用变量。 避免上述问题的一个解决办法就是将你的变量放在适当的作用域中。在第一个匿名函数中声明var h变量来解决这个问题,这样局部变量h的优先级高于其他任何的全局变量h: JavaScript 1 2 3 4 5 6 7 8 $(document).ready (function(){ //set h to 200 var h = 200; $("button").click (function(){ //animate up to h, 200 pixels high $("div").animate ({height:h}); }); }); 如果你一定要使用全局变量,那就将这些全局变量命名、组合好,并在你的代码中加上适当的comment: JavaScript 1 2 3 4 5 6 7 8 9 10 //properties of the animation var animationConfig = {upToHeight:200}; //when document is loaded $(document).ready(function(){ //when any <button> element is clicked $("button").click(function(){ //change the height of all <div>s $("div").animate({height:animationConfig.upToHeight}); }); }); 结论 这篇文章是一篇针对初学者的介绍JS语法和如何使用jQuery使用的指南。jQuery只是一个JS库,它有一个很看起来很特别的函数:$,推荐在jQuery中使用对象的简写形式、匿名函数还有方法链。类似的库还有YUI(Yahoo User Interface)。 现在再看jQuery的代码时,你是不是不会再抱有过去的疑问和不确定了呢?你已经知道它要做什么了。虽然由于事件驱动编程的复杂性,你可能不确定什么时候使用它,但是你会知道怎么做。 原文地址:http://blog.jobbole.com/71529/
原文地址 本文由 ImportNew - 黄小非 翻译自 winterbe。欢迎加入翻译小组。转载请见文末要求。 ImportNew注:有兴趣第一时间学习Java 8的Java开发者,欢迎围观《征集参与Java 8原创系列文章作者》。 以下是《Java 8简明教程》的正文。 “Java并没有没落,人们很快就会发现这一点” 欢迎阅读我编写的Java 8介绍。本教程将带领你一步一步地认识这门语言的新特性。通过简单明了的代码示例,你将会学习到如何使用默认接口方法,Lambda表达式,方法引用和重复注解。看完这篇教程后,你还将对最新推出的API有一定的了解,例如:流控制,函数式接口,map扩展和新的时间日期API等等。 允许在接口中有默认方法实现 Java 8 允许我们使用default关键字,为接口声明添加非抽象的方法实现。这个特性又被称为扩展方法。下面是我们的第一个例子: interface Formula { double calculate(int a); default double sqrt(int a) { return Math.sqrt(a); } } 在接口Formula中,除了抽象方法caculate以外,还定义了一个默认方法sqrt。Formula的实现类只需要实现抽象方法caculate就可以了。默认方法sqrt可以直接使用。 Formula formula = new Formula() { @Override public double calculate(int a) { return sqrt(a * 100); } }; formula.calculate(100); // 100.0 formula.sqrt(16); // 4.0 formula对象以匿名对象的形式实现了Formula接口。代码很啰嗦:用了6行代码才实现了一个简单的计算功能:a*100开平方根。我们在下一节会看到,Java 8 还有一种更加优美的方法,能够实现包含单个函数的对象。 Lambda表达式 让我们从最简单的例子开始,来学习如何对一个string列表进行排序。我们首先使用Java 8之前的方法来实现: List<String> names = Arrays.asList("peter", "anna", "mike", "xenia"); Collections.sort(names, new Comparator<String>() { @Override public int compare(String a, String b) { return b.compareTo(a); } }); 静态工具方法Collections.sort接受一个list,和一个Comparator接口作为输入参数,Comparator的实现类可以对输入的list中的元素进行比较。通常情况下,你可以直接用创建匿名Comparator对象,并把它作为参数传递给sort方法。 除了创建匿名对象以外,Java 8 还提供了一种更简洁的方式,Lambda表达式。 Collections.sort(names, (String a, String b) -> { return b.compareTo(a); }); 你可以看到,这段代码就比之前的更加简短和易读。但是,它还可以更加简短: Collections.sort(names, (String a, String b) -> b.compareTo(a)); 只要一行代码,包含了方法体。你甚至可以连大括号对{}和return关键字都省略不要。不过这还不是最短的写法: Collections.sort(names, (a, b) -> b.compareTo(a)); Java编译器能够自动识别参数的类型,所以你就可以省略掉类型不写。让我们再深入地研究一下lambda表达式的威力吧。 函数式接口 Lambda表达式如何匹配Java的类型系统?每一个lambda都能够通过一个特定的接口,与一个给定的类型进行匹配。一个所谓的函数式接口必须要有且仅有一个抽象方法声明。每个与之对应的lambda表达式必须要与抽象方法的声明相匹配。由于默认方法不是抽象的,因此你可以在你的函数式接口里任意添加默认方法。 任意只包含一个抽象方法的接口,我们都可以用来做成lambda表达式。为了让你定义的接口满足要求,你应当在接口前加上@FunctionalInterface 标注。编译器会注意到这个标注,如果你的接口中定义了第二个抽象方法的话,编译器会抛出异常。 举例: @FunctionalInterface interface Converter<F, T> { T convert(F from); } Converter<String, Integer> converter = (from) -> Integer.valueOf(from); Integer converted = converter.convert("123"); System.out.println(converted); // 123 注意,如果你不写@FunctionalInterface 标注,程序也是正确的。 方法和构造函数引用 上面的代码实例可以通过静态方法引用,使之更加简洁: Converter<String, Integer> converter = Integer::valueOf; Integer converted = converter.convert("123"); System.out.println(converted); // 123 Java 8 允许你通过::关键字获取方法或者构造函数的的引用。上面的例子就演示了如何引用一个静态方法。而且,我们还可以对一个对象的方法进行引用: class Something { String startsWith(String s) { return String.valueOf(s.charAt(0)); } } Something something = new Something(); Converter<String, String> converter = something::startsWith; String converted = converter.convert("Java"); System.out.println(converted); // "J" 让我们看看如何使用::关键字引用构造函数。首先我们定义一个示例bean,包含不同的构造方法: class Person { String firstName; String lastName; Person() {} Person(String firstName, String lastName) { this.firstName = firstName; this.lastName = lastName; } } 接下来,我们定义一个person工厂接口,用来创建新的person对象: interface PersonFactory<P extends Person> { P create(String firstName, String lastName); } 然后我们通过构造函数引用来把所有东西拼到一起,而不是像以前一样,通过手动实现一个工厂来这么做。 PersonFactory<Person> personFactory = Person::new; Person person = personFactory.create("Peter", "Parker"); 我们通过Person::new来创建一个Person类构造函数的引用。Java编译器会自动地选择合适的构造函数来匹配PersonFactory.create函数的签名,并选择正确的构造函数形式。 Lambda的范围 对于lambdab表达式外部的变量,其访问权限的粒度与匿名对象的方式非常类似。你能够访问局部对应的外部区域的局部final变量,以及成员变量和静态变量。 访问局部变量 我们可以访问lambda表达式外部的final局部变量: final int num = 1; Converter<Integer, String> stringConverter = (from) -> String.valueOf(from + num); stringConverter.convert(2); // 3 但是与匿名对象不同的是,变量num并不需要一定是final。下面的代码依然是合法的: int num = 1; Converter<Integer, String> stringConverter = (from) -> String.valueOf(from + num); stringConverter.convert(2); // 3 然而,num在编译的时候被隐式地当做final变量来处理。下面的代码就不合法: int num = 1; Converter<Integer, String> stringConverter = (from) -> String.valueOf(from + num); num = 3; 在lambda表达式内部企图改变num的值也是不允许的。 访问成员变量和静态变量 与局部变量不同,我们在lambda表达式的内部能获取到对成员变量或静态变量的读写权。这种访问行为在匿名对象里是非常典型的。 class Lambda4 { static int outerStaticNum; int outerNum; void testScopes() { Converter<Integer, String> stringConverter1 = (from) -> { outerNum = 23; return String.valueOf(from); }; Converter<Integer, String> stringConverter2 = (from) -> { outerStaticNum = 72; return String.valueOf(from); }; } } 访问默认接口方法 还记得第一节里面formula的那个例子么? 接口Formula定义了一个默认的方法sqrt,该方法能够访问formula所有的对象实例,包括匿名对象。这个对lambda表达式来讲则无效。 默认方法无法在lambda表达式内部被访问。因此下面的代码是无法通过编译的: Formula formula = (a) -> sqrt( a * 100); 内置函数式接口 JDK 1.8 API中包含了很多内置的函数式接口。有些是在以前版本的Java中大家耳熟能详的,例如Comparator接口,或者Runnable接口。对这些现成的接口进行实现,可以通过@FunctionalInterface 标注来启用Lambda功能支持。 此外,Java 8 API 还提供了很多新的函数式接口,来降低程序员的工作负担。有些新的接口已经在Google Guava库中很有名了。如果你对这些库很熟的话,你甚至闭上眼睛都能够想到,这些接口在类库的实现过程中起了多么大的作用。 Predicates Predicate是一个布尔类型的函数,该函数只有一个输入参数。Predicate接口包含了多种默认方法,用于处理复杂的逻辑动词(and, or,negate) Predicate<String> predicate = (s) -> s.length() > 0; predicate.test("foo"); // true predicate.negate().test("foo"); // false Predicate<Boolean> nonNull = Objects::nonNull; Predicate<Boolean> isNull = Objects::isNull; Predicate<String> isEmpty = String::isEmpty; Predicate<String> isNotEmpty = isEmpty.negate(); Functions Function接口接收一个参数,并返回单一的结果。默认方法可以将多个函数串在一起(compse, andThen) Function<String, Integer> toInteger = Integer::valueOf; Function<String, String> backToString = toInteger.andThen(String::valueOf); backToString.apply("123"); // "123" Suppliers Supplier接口产生一个给定类型的结果。与Function不同的是,Supplier没有输入参数。 Supplier<Person> personSupplier = Person::new; personSupplier.get(); // new Person Consumers Consumer代表了在一个输入参数上需要进行的操作。 Consumer<Person> greeter = (p) -> System.out.println("Hello, " + p.firstName); greeter.accept(new Person("Luke", "Skywalker")); Comparators Comparator接口在早期的Java版本中非常著名。Java 8 为这个接口添加了不同的默认方法。 Comparator<Person> comparator = (p1, p2) -> p1.firstName.compareTo(p2.firstName); Person p1 = new Person("John", "Doe"); Person p2 = new Person("Alice", "Wonderland"); comparator.compare(p1, p2); // > 0 comparator.reversed().compare(p1, p2); // < 0 Optionals Optional不是一个函数式接口,而是一个精巧的工具接口,用来防止NullPointerEception产生。这个概念在下一节会显得很重要,所以我们在这里快速地浏览一下Optional的工作原理。 Optional是一个简单的值容器,这个值可以是null,也可以是non-null。考虑到一个方法可能会返回一个non-null的值,也可能返回一个空值。为了不直接返回null,我们在Java 8中就返回一个Optional. Optional<String> optional = Optional.of("bam"); optional.isPresent(); // true optional.get(); // "bam" optional.orElse("fallback"); // "bam" optional.ifPresent((s) -> System.out.println(s.charAt(0))); // "b" Streams java.util.Stream表示了某一种元素的序列,在这些元素上可以进行各种操作。Stream操作可以是中间操作,也可以是完结操作。完结操作会返回一个某种类型的值,而中间操作会返回流对象本身,并且你可以通过多次调用同一个流操作方法来将操作结果串起来(就像StringBuffer的append方法一样————译者注)。Stream是在一个源的基础上创建出来的,例如java.util.Collection中的list或者set(map不能作为Stream的源)。Stream操作往往可以通过顺序或者并行两种方式来执行。 我们先了解一下序列流。首先,我们通过string类型的list的形式创建示例数据: List<String> stringCollection = new ArrayList<>(); stringCollection.add("ddd2"); stringCollection.add("aaa2"); stringCollection.add("bbb1"); stringCollection.add("aaa1"); stringCollection.add("bbb3"); stringCollection.add("ccc"); stringCollection.add("bbb2"); stringCollection.add("ddd1"); Java 8中的Collections类的功能已经有所增强,你可以之直接通过调用Collections.stream()或者Collection.parallelStream()方法来创建一个流对象。下面的章节会解释这个最常用的操作。 Filter Filter接受一个predicate接口类型的变量,并将所有流对象中的元素进行过滤。该操作是一个中间操作,因此它允许我们在返回结果的基础上再进行其他的流操作(forEach)。ForEach接受一个function接口类型的变量,用来执行对每一个元素的操作。ForEach是一个中止操作。它不返回流,所以我们不能再调用其他的流操作。 stringCollection .stream() .filter((s) -> s.startsWith("a")) .forEach(System.out::println); // "aaa2", "aaa1" Sorted Sorted是一个中间操作,能够返回一个排过序的流对象的视图。流对象中的元素会默认按照自然顺序进行排序,除非你自己指定一个Comparator接口来改变排序规则。 stringCollection .stream() .sorted() .filter((s) -> s.startsWith("a")) .forEach(System.out::println); // "aaa1", "aaa2" 一定要记住,sorted只是创建一个流对象排序的视图,而不会改变原来集合中元素的顺序。原来string集合中的元素顺序是没有改变的。 System.out.println(stringCollection); // ddd2, aaa2, bbb1, aaa1, bbb3, ccc, bbb2, ddd1 Map map是一个对于流对象的中间操作,通过给定的方法,它能够把流对象中的每一个元素对应到另外一个对象上。下面的例子就演示了如何把每个string都转换成大写的string. 不但如此,你还可以把每一种对象映射成为其他类型。对于带泛型结果的流对象,具体的类型还要由传递给map的泛型方法来决定。 stringCollection .stream() .map(String::toUpperCase) .sorted((a, b) -> b.compareTo(a)) .forEach(System.out::println); // "DDD2", "DDD1", "CCC", "BBB3", "BBB2", "AAA2", "AAA1" Match 匹配操作有多种不同的类型,都是用来判断某一种规则是否与流对象相互吻合的。所有的匹配操作都是终结操作,只返回一个boolean类型的结果。 boolean anyStartsWithA = stringCollection .stream() .anyMatch((s) -> s.startsWith("a")); System.out.println(anyStartsWithA); // true boolean allStartsWithA = stringCollection .stream() .allMatch((s) -> s.startsWith("a")); System.out.println(allStartsWithA); // false boolean noneStartsWithZ = stringCollection .stream() .noneMatch((s) -> s.startsWith("z")); System.out.println(noneStartsWithZ); // true Count Count是一个终结操作,它的作用是返回一个数值,用来标识当前流对象中包含的元素数量。 long startsWithB = stringCollection .stream() .filter((s) -> s.startsWith("b")) .count(); System.out.println(startsWithB); // 3 Reduce 该操作是一个终结操作,它能够通过某一个方法,对元素进行削减操作。该操作的结果会放在一个Optional变量里返回。 Optional<String> reduced = stringCollection .stream() .sorted() .reduce((s1, s2) -> s1 + "#" + s2); reduced.ifPresent(System.out::println); // "aaa1#aaa2#bbb1#bbb2#bbb3#ccc#ddd1#ddd2" Parallel Streams 像上面所说的,流操作可以是顺序的,也可以是并行的。顺序操作通过单线程执行,而并行操作则通过多线程执行。 下面的例子就演示了如何使用并行流进行操作来提高运行效率,代码非常简单。 首先我们创建一个大的list,里面的元素都是唯一的: int max = 1000000; List<String> values = new ArrayList<>(max); for (int i = 0; i < max; i++) { UUID uuid = UUID.randomUUID(); values.add(uuid.toString()); } 现在,我们测量一下对这个集合进行排序所使用的时间。 顺序排序 long t0 = System.nanoTime(); long count = values.stream().sorted().count(); System.out.println(count); long t1 = System.nanoTime(); long millis = TimeUnit.NANOSECONDS.toMillis(t1 - t0); System.out.println(String.format("sequential sort took: %d ms", millis)); // sequential sort took: 899 ms 并行排序 long t0 = System.nanoTime(); long count = values.parallelStream().sorted().count(); System.out.println(count); long t1 = System.nanoTime(); long millis = TimeUnit.NANOSECONDS.toMillis(t1 - t0); System.out.println(String.format("parallel sort took: %d ms", millis)); // parallel sort took: 472 ms 如你所见,所有的代码段几乎都相同,唯一的不同就是把stream()改成了parallelStream(), 结果并行排序快了50%。 Map 正如前面已经提到的那样,map是不支持流操作的。而更新后的map现在则支持多种实用的新方法,来完成常规的任务。 Map<Integer, String> map = new HashMap<>(); for (int i = 0; i < 10; i++) { map.putIfAbsent(i, "val" + i); } map.forEach((id, val) -> System.out.println(val)); 上面的代码风格是完全自解释的:putIfAbsent避免我们将null写入;forEach接受一个消费者对象,从而将操作实施到每一个map中的值上。 下面的这个例子展示了如何使用函数来计算map的编码 map.computeIfPresent(3, (num, val) -> val + num); map.get(3); // val33 map.computeIfPresent(9, (num, val) -> null); map.containsKey(9); // false map.computeIfAbsent(23, num -> "val" + num); map.containsKey(23); // true map.computeIfAbsent(3, num -> "bam"); map.get(3); // val33 接下来,我们将学习,当给定一个key值时,如何把一个实例从对应的key中移除: map.remove(3, "val3"); map.get(3); // val33 map.remove(3, "val33"); map.get(3); // null 另一个有用的方法: map.getOrDefault(42, "not found"); // not found 将map中的实例合并也是非常容易的: map.merge(9, "val9", (value, newValue) -> value.concat(newValue)); map.get(9); // val9 map.merge(9, "concat", (value, newValue) -> value.concat(newValue)); map.get(9); // val9concat 合并操作先看map中是否没有特定的key/value存在,如果是,则把key/value存入map,否则merging函数就会被调用,对现有的数值进行修改。 时间日期API Java 8 包含了全新的时间日期API,这些功能都放在了java.time包下。新的时间日期API是基于Joda-Time库开发的,但是也不尽相同。下面的例子就涵盖了大多数新的API的重要部分。 Clock Clock提供了对当前时间和日期的访问功能。Clock是对当前时区敏感的,并可用于替代System.currentTimeMillis()方法来获取当前的毫秒时间。当前时间线上的时刻可以用Instance类来表示。Instance也能够用于创建原先的java.util.Date对象。 Clock clock = Clock.systemDefaultZone(); long millis = clock.millis(); Instant instant = clock.instant(); Date legacyDate = Date.from(instant); // legacy java.util.Date Timezones 时区类可以用一个ZoneId来表示。时区类的对象可以通过静态工厂方法方便地获取。时区类还定义了一个偏移量,用来在当前时刻或某时间与目标时区时间之间进行转换。 System.out.println(ZoneId.getAvailableZoneIds()); // prints all available timezone ids ZoneId zone1 = ZoneId.of("Europe/Berlin"); ZoneId zone2 = ZoneId.of("Brazil/East"); System.out.println(zone1.getRules()); System.out.println(zone2.getRules()); // ZoneRules[currentStandardOffset=+01:00] // ZoneRules[currentStandardOffset=-03:00] LocalTime 本地时间类表示一个没有指定时区的时间,例如,10 p.m.或者17:30:15,下面的例子会用上面的例子定义的时区创建两个本地时间对象。然后我们会比较两个时间,并计算它们之间的小时和分钟的不同。 LocalTime now1 = LocalTime.now(zone1); LocalTime now2 = LocalTime.now(zone2); System.out.println(now1.isBefore(now2)); // false long hoursBetween = ChronoUnit.HOURS.between(now1, now2); long minutesBetween = ChronoUnit.MINUTES.between(now1, now2); System.out.println(hoursBetween); // -3 System.out.println(minutesBetween); // -239 LocalTime是由多个工厂方法组成,其目的是为了简化对时间对象实例的创建和操作,包括对时间字符串进行解析的操作。 LocalTime late = LocalTime.of(23, 59, 59); System.out.println(late); // 23:59:59 DateTimeFormatter germanFormatter = DateTimeFormatter .ofLocalizedTime(FormatStyle.SHORT) .withLocale(Locale.GERMAN); LocalTime leetTime = LocalTime.parse("13:37", germanFormatter); System.out.println(leetTime); // 13:37 LocalDate 本地时间表示了一个独一无二的时间,例如:2014-03-11。这个时间是不可变的,与LocalTime是同源的。下面的例子演示了如何通过加减日,月,年等指标来计算新的日期。记住,每一次操作都会返回一个新的时间对象。 LocalDate today = LocalDate.now(); LocalDate tomorrow = today.plus(1, ChronoUnit.DAYS); LocalDate yesterday = tomorrow.minusDays(2); LocalDate independenceDay = LocalDate.of(2014, Month.JULY, 4); DayOfWeek dayOfWeek = independenceDay.getDayOfWeek(); System.out.println(dayOfWeek); // FRIDAY 解析字符串并形成LocalDate对象,这个操作和解析LocalTime一样简单。 DateTimeFormatter germanFormatter = DateTimeFormatter .ofLocalizedDate(FormatStyle.MEDIUM) .withLocale(Locale.GERMAN); LocalDate xmas = LocalDate.parse("24.12.2014", germanFormatter); System.out.println(xmas); // 2014-12-24 LocalDateTime LocalDateTime表示的是日期-时间。它将刚才介绍的日期对象和时间对象结合起来,形成了一个对象实例。LocalDateTime是不可变的,与LocalTime和LocalDate的工作原理相同。我们可以通过调用方法来获取日期时间对象中特定的数据域。 LocalDateTime sylvester = LocalDateTime.of(2014, Month.DECEMBER, 31, 23, 59, 59); DayOfWeek dayOfWeek = sylvester.getDayOfWeek(); System.out.println(dayOfWeek); // WEDNESDAY Month month = sylvester.getMonth(); System.out.println(month); // DECEMBER long minuteOfDay = sylvester.getLong(ChronoField.MINUTE_OF_DAY); System.out.println(minuteOfDay); // 1439 如果再加上的时区信息,LocalDateTime能够被转换成Instance实例。Instance能够被转换成以前的java.util.Date对象。 Instant instant = sylvester .atZone(ZoneId.systemDefault()) .toInstant(); Date legacyDate = Date.from(instant); System.out.println(legacyDate); // Wed Dec 31 23:59:59 CET 2014 格式化日期-时间对象就和格式化日期对象或者时间对象一样。除了使用预定义的格式以外,我们还可以创建自定义的格式化对象,然后匹配我们自定义的格式。 DateTimeFormatter formatter = DateTimeFormatter .ofPattern("MMM dd, yyyy - HH:mm"); LocalDateTime parsed = LocalDateTime.parse("Nov 03, 2014 - 07:13", formatter); String string = formatter.format(parsed); System.out.println(string); // Nov 03, 2014 - 07:13 不同于java.text.NumberFormat,新的DateTimeFormatter类是不可变的,也是线程安全的。 更多的细节,请看这里 Annotations Java 8中的注解是可重复的。让我们直接深入看看例子,弄明白它是什么意思。 首先,我们定义一个包装注解,它包括了一个实际注解的数组 @interface Hints { Hint[] value(); } @Repeatable(Hints.class) @interface Hint { String value(); } 只要在前面加上注解名:@Repeatable,Java 8 允许我们对同一类型使用多重注解, 变体1:使用注解容器(老方法) @Hints({@Hint("hint1"), @Hint("hint2")}) class Person {} 变体2:使用可重复注解(新方法) @Hint("hint1") @Hint("hint2") class Person {} 使用变体2,Java编译器能够在内部自动对@Hint进行设置。这对于通过反射来读取注解信息来说,是非常重要的。 Hint hint = Person.class.getAnnotation(Hint.class); System.out.println(hint); // null Hints hints1 = Person.class.getAnnotation(Hints.class); System.out.println(hints1.value().length); // 2 Hint[] hints2 = Person.class.getAnnotationsByType(Hint.class); System.out.println(hints2.length); // 2 尽管我们绝对不会在Person类上声明@Hints注解,但是它的信息仍然可以通过getAnnotation(Hints.class)来读取。并且,getAnnotationsByType方法会更方便,因为它赋予了所有@Hints注解标注的方法直接的访问权限。 @Target({ElementType.TYPE_PARAMETER, ElementType.TYPE_USE}) @interface MyAnnotation {} 先到这里 我的Java 8编程指南就到此告一段落。当然,还有很多内容需要进一步研究和说明。这就需要靠读者您来对JDK 8进行探究了,例如:Arrays.parallelSort, StampedLock和CompletableFuture等等 ———— 我这里只是举几个例子而已。 我希望这个博文能够对您有所帮助,也希望您阅读愉快。完整的教程源代码放在了GitHub上。您可以尽情地fork,并请通过Twitter告诉我您的反馈。 原文链接: winterbe 翻译: ImportNew.com - 黄小非 译文链接: http://www.importnew.com/10360.html [ 转载请保留原文出处、译者和译文链接。]
公司项目切换服务器,之前服务器共享的盘符没法继续使用了,于是想把网络驱动器盘符图标给删掉: 删除方法: 单击「开始」,然后单击“运行”。 在“打开”框中,键入 cmd。 键入 net use Y: /delete 注意:其中 Y:是共享资源的驱动器号 命令执行完成后重启机器,搞定! 作者:jiankunking 出处:http://blog.csdn.net/jiankunking
信息: The APR based Apache Tomcat Native library which allows optimal performance in production environments was not found on the java.library.path:XXXX 出现原因分析: Tomcat建议使用apache的apr,来更好的运行Tomcat; ——————–apache的apr 的介绍 ————————— APR(Apache portable Run-time libraries,Apache可移植运行库)的目的如其名称一样,主要为上层的应用程序提供一个可以跨越多操作系统平台使用的底层支持接口库。在早期 的Apache版本中,应用程序本身必须能够处理各种具体操作系统平台的细节,并针对不同的平台调用不同的处理函数。 随着Apache的进一步开发,Apache组织决定将这些通用的函数独立出来并发展成为一个新的项目。这样,APR的开发就从Apache中独立出来,Apache仅仅是使用 APR而已。 APR使得平台细节的处理进行下移。对于应用程序而言,它们根本就不需要考虑具体的平台,不管是Unix、Linux还是Window,应用程序执行的接口基本都是统一一致的。因此对于APR而言,可移植性和统一的上层接口是其考虑的一个重点。而APR最早的目的并不是如此,它最早只是希望将Apache中用到的所有代码合并为一个通用的代码库,然而这不是一个正确的策略,因此后来APR改变了其目标。有的时候使用公共代码并不是一件好事,比如如何将一个请求映射到线程或者进程是平台相关的,因此仅仅一个公共的代码库并不能完成这种区分。APR的目标则是希望安全合并所有的能够合并的代码而不需要牺牲性能。 APR的最早的一个目标就是为所有的平台(不是部分)提供一个公共的统一操作函数接口,这是一个非常了不起的目的,当然也是不现实的一个目标。我们不可能支持所有平台的所有特征,因此APR目前只能为大多数平台提供所有的APR特性支持,包括Win32、OS/2、BeOS、Darwin、Linux等等。为了能够实现这个目标,APR开发者必须为那些不能运行于所有平台的特性创建了一系列的特征宏(FEATURE MACROS)以在各个平台之间区分这些特征。这些特征宏定义非常简单,通常用APR_HAS_FEATURE参数设置: 如果某个平台具有这个特性,则该宏必须设置为true,比如Linux和window都具有内存映射文件,同时APR提供了内存映射文件的操作接口,因此在这两个平台上,APR_HAS_MMAP宏必须设置,同时ap_mmap_*函数应该将磁盘文件映射为内存并返回适当的状态码。如果你的操作系统并不支持内存映射,那么APR_HAS_MMAP必须设置为0,而且所有的ap_mmap_*函数也可以不需要定义。第二步就是对于那些在程序中使用了不支持的函数必须提出警告。 解决方法: http://archive.apache.org/dist/tomcat/tomcat-connectors/native/ 下载与你Tomcat对应版本的 tcnative-1.dll,放到apache-tomcat-N(版本号)\bin 目录下面,重启tomcat ; 如果你不清楚版本,你随便下载一个版本,放进目录里面,在重启tomcat 的时候,会有提示你合适的 tcnative-1.dll 版本; 一、 二、 三、 四、 原文地址:http://www.cnblogs.com/java-class/p/4280037.html
采用360安全卫士的软件卸载工具,记得将有关的注册表信息全部删除。 在“运行”中输入Regedit,打开注册表编辑器,找到HKEY_LOCAL_MACHINE/SOFTWARE/JavaSoft ,将JavaSoft文件夹及其子目录全部删除。 作者:jiankunking 出处:http://blog.csdn.net/jiankunking
译者:jiankunking 出处:http://blog.csdn.net/jiankunking 原文地址 本文讲解ASP.NET Web API如何将HTTP请求路由至控制器。 如果你熟悉ASP.NET MVC,Web API路由与MVC路由非常相似。主要差别是Web API使用HTTP方法,而不是URI路径来选择Action。你也可以按照之前配置MVC路由的方式来配置Web API路由。本文不需要任何ASP.NET MVC知识。 Routing Tables路由表 在Asp.Net Web API中,一个控制器就是一个处理HTTP请求的类,控制器的public 方法被叫做action方法或者简单的Aciton。当Web API接收到一个请求的时候,它将这个请求路由到一个Action。 为了确定那个Action被调用,这个框架使用了一个路由表。Visual Studio中Web API的项目模板会创建一个默认路由: routes.MapHttpRoute( name: "API Default", routeTemplate: "api/{controller}/{id}", defaults: new { id = RouteParameter.Optional } ); 这个路由是在WebApiConfig.cs文件中定义的,该文件位于App_Start目录。 关于WebApiConfig类的更多信息参阅“配置ASP.NET Web API” 如果你要自己托管(self-host )Web API,你必须直接在HttpSelfHostConfiguration对象上设置路由表。更多信息参阅“自托管Web API“。 路由表中的每一个条目都包含一个路由模板。这个Web API默认的路由模版是”api/{controller}/{id}”。在这个模版中,“api”是一个文字式路径片段(literal path segment),而{controller}和{id}则是占位符变量。 当Web API框架接收一个HTTP请求时,它会试图根据路由表中的一个路由模板来匹配其URI。如果无路由匹配,客户端会接收到一个404(未找到)错误。例如,以下URI与这个默认路由的匹配: /api/contacts /api/contacts/1 /api/products/gizmo1 然而,以下URI不匹配,因为它缺少“api”片段: /contacts/1 小注: 在路由中使用“api”的原因是为了避免与ASP.NET MVC的路由冲突。通过这种方式,可以用“/contacts”进入一个MVC控制器,而“/api/contacts”进入一个Web API控制器。当然,如果你不喜欢这种约定,你也可以修改这个默认路由表。 一旦一个匹配的路由被发现,Web API便会选择相应的Controller和Action: 为了找到Controller,Web API会把“控制器”加到{controller}变量的值。 为了找到Action,Web API会查找HTTP方法,然后寻找一个名称以HTTP方法名开头的方法。例如,对于一个Get请求,Web API会查找一个以“Get…”开头的动作,如“GetContact”或“GetAllContacts”等。这种约定只应用于GET、POST、PUT和DELETE方法。通过在你的Controller上使用attributes,你可以启用其他的HTTP方法。稍后我们就会看到一个例子。 路由模版中其他的占位变量,例如{id},将被映射成Action的参数。 让我们来看一个简单的例子,假设你定义了以下控制器: public class ProductsController : ApiController { public void GetAllProducts() { } public IEnumerable<Product> GetProductById(int id) { } public HttpResponseMessage DeleteProduct(int id){ } } 以下是一些可能的HTTP请求,以及要被调用的每个动作: HTTP Method URI Path Action Parameter GET api/products GetAllProducts (none) GET api/products/4 GetProductById 4 DELETE api/products/4 DeleteProduct 4 POST api/products (no match) 注意,URI中的{id}片段如果出现,会被映射成Action的id参数。在这个例子中,这个控制器定义了两个GET方法,一个带有id参数的和一个不带有id参数的。 另外要注意,POST请求是失败的,因为该控制器未定义“Post…”方法。 Routing Variations路由变化 上一节描述了ASP.NET Web API基本的路由机制。本小节描述一些变化。 HTTP方法 替代使用HTTP方法的命名约定,你可以明确的为一个Action指定HTTP方法,通过以HttpGet、HttpPost、HttpPut或者HttpDelete属性来对Action方法进行修饰。 在下列示例中,FindProduct方法被映射到GET请求: public class ProductsController : ApiController { [HttpGet] public Product FindProduct(id) {} } 允许一个Action对应多个HTTP方法,或者允许除了Get、Put、Post、Delete方法之外的HTTP方法,需要使用AcceptVerbs注解属性,它以HTTP方法列表作为参数。 public class ProductsController : ApiController { [AcceptVerbs("GET", "HEAD")] public Product FindProduct(id) { } // WebDAV method [AcceptVerbs("MKCOL")] public void MakeCollection() { } } 通过Action名称路由 在默认的路由模版中,这个Web API使用HTTP方法去选择Action。然而,你也可以在URI中创建包含动作名的路由: routes.MapHttpRoute( name: "ActionApi", routeTemplate: "api/{controller}/{action}/{id}", defaults: new { id = RouteParameter.Optional } ); 在这个路由模板中,{action}参数命名了控制器上的动作方法。采用这种风格的路由,需要使用注解属性来指明所允许的HTTP方法。例如,假设你的控制器已有如下方法: public class ProductsController : ApiController { [HttpGet] public string Details(int id); } 在这种情况下,一个Get请求”api/Products/Details/1”将会映射到这个这个Details方法。这种风格的路由类似于Asp.Net MVC,而且可能与RPC式的API相接近。 你也可以通过使用ActionName注解属性来覆盖动作名。在以下例子中,有两个动作映射到“api/products/thumbnail/id”。一个支持GET,而另一个支持POST: public class ProductsController : ApiController { [HttpGet] [ActionName("Thumbnail")] public HttpResponseMessage GetThumbnailImage(int id); [HttpPost] [ActionName("Thumbnail")] public void AddThumbnailImage(int id); } Non-Actions 为了防止一个方法被作为一个动作所请求,可以使用NonAction注解属性。它对框架发出信号:这个方法不是一个动作,,即使它可能与路由规则匹配。 // Not an action method. [NonAction] public string GetPrivateData() { ... } 初次接触Web API,首次翻译外文文章,如有不对的地方,麻烦斧正。 译者:jiankunking 出处:http://blog.csdn.net/jiankunking 小注:本文部分内容参考网络资料。
译者:jiankunking 出处:http://blog.csdn.net/jiankunking 源码下载 HTTP不仅提供web页面服务,在构建公开服务和数据api方面,它也是一个强大的平台。HTTP简单、灵活、无处不在。几乎你能想到的所有的平台,都有一个HTTP库,因此HTTP服务可以影响到广泛的客户端,包括浏览器、移动设备,和传统的桌面应用程序。 ASP.NET Web API是一个基于.NET框架用于构建Web API的框架。在本教程中,您将使用ASP.NET Web API创建一个Web API并返回一个产品列表。 本教程中开发工具及环境版本: Visual Studio 2015 Update 2 Web API 2 创建一个Web API项目 在本教程中,将使用 ASP.NET Web API 创建一个web API项目返回一个产品列表。前端Web页面使用jQuery来显示结果。 启动Visual Studio,在开始界面选择新建新项目或者从文件菜单选择:新建-项目。 在模板页选择:已安装-模板-Visual C#-Web。 在项目模板的列表中,选择ASP.NETWeb应用程序。 项目名称:“productsapp”并单击“确定”。 在新建New ASP.NET 项目对话框,选择空模板。为以下相添加文件夹和核心引用,选择:Web API。点击确定。 您也可以使用“Web API”模板创建一个Web API项目。 Web API模板使用ASP.NET MVC提供API帮助页面。在本教程中我用空模板,因为我不想使用MVC来演示Web API。一般来说,你使用MVC Web API不需要知道ASP.NET MVC。 添加Model 模型是一个表示应用程序中数据的对象。ASP.NET Web API可以自动序列化你的模型为JSON,XML或其他格式,然后将序列化后的数据填充到HTTP响应消息体。 只要客户端可以读取序列化格式,它可以对对象进行反序列化。大多数客户端都能够解析XML或JSON。此外,客户端可以声明它希望通过在HTTP请求的Accept报头格式(即Web API支持格式协商,客户端可以通过Accept header通知服务器期望的格式)。 让我们从创建一个代表产品的简单模型。 如果“解决方案资源管理器”已不可见,请单击“视图”菜单并选择“解决方案资源管理器”。 在“解决方案资源管理器”中,右键单击“Models ”文件夹。从上下文菜单中,选择“添加”然后选择“类”: 将类命名为:Product,添加以下属性到Product类中: namespace ProductsApp.Models { public class Product { public int Id { get; set; } public string Name { get; set; } public string Category { get; set; } public decimal Price { get; set; } } } 添加Controller 在Web API,控制器(Controller)是一个处理HTTP请求的对象。我们将添加一个可以返回产品列表或指定ID的单个产品的控制器。 如果你之前用过ASP.NET MVC,那么你应该已经熟悉了控制器。Web API控制器类似MVC控制器,但是继承ApiController类而不是Controller类。 在解决方案资源管理器中,右键单击Controllers 文件夹。选择Add然后选择控制器。 在添加基架对话框中,选择 Web API Controller - Empty。单击添加。 再添加控制器界面,输入:ProductsController,点击 添加: 你不需要把你的控制器添加到一个命名为控制器文件夹。文件夹的名字仅仅是为了方便组织你的源文件。 如果这个文件没有打开,双击该文件以打开它。用以下代码替换该文件中的代码: using productsapp.Models; using System; using System.Collections.Generic; using System.Linq; using System.Net; using System.Net.Http; using System.Web.Http; namespace productsapp.Controllers { public class ProductsController : ApiController { Product[] products = new Product[] { new Product { Id = 1, Name = "Tomato Soup", Category = "Groceries", Price = 1 }, new Product { Id = 2, Name = "Yo-yo", Category = "Toys", Price = 3.75M }, new Product { Id = 3, Name = "Hammer", Category = "Hardware", Price = 16.99M } }; // GET api/products public IEnumerable<Product> GetAllProducts() { return products; } // GET api/products/id public IHttpActionResult GetProduct(int id) { var product = products.FirstOrDefault((p) => p.Id == id); if (product == null) { return NotFound(); } return Ok(product); } } } 为了使示例尽量简单,产品存储在控制器类的一个固定的数组内。当然,在真实的应用程序中,您将查询数据库或使用其他一些外部数据源。 控制器定义了两个方法,该方法返回产品: GetAllProducts方法返回 IEnumerable类型的整个产品 。 GetProduct方法根据ID返回单个产品。 控制器上的每个方法对应于一个或多个uri: Controller Method URI GetAllProducts /api/products GetProduct /api/products/id 有关如何使用Web API的HTTP请求路由到控制器方法的更多信息,参见ASP.NET Web API路由。 拓展: HTTP 的四个主要方法 (GET, PUT, POST, DELETE) 按照下列方式映射为 CURD 操作: GET 用于获取 URI 资源的进行展示, GET 操作不应对服务端有任何影响; PUT 用于更新 URI 上的一个资源, 如果服务端允许, PUT 也可以用于新建一个资源; POST 用于新建 资源, 服务端在指定的 URI 上创建一个新的对象, 将新资源的地址作为响应消息的一部分返回; DELETE 用于删除指定的 URI 资源。 通过JavaScript和jQuery调用Web API 在这一部分中,我们将添加一个HTML页面,使用AJAX调用Web API。我们将使用jQuery的Ajax调用进行和更新结果页面。 在“解决方案资源管理器”中,右键单击该项目并选择“添加”,然后选择“新建项”。 在添加新项对话框中,选择Visual c#节点下的Web节点,然后选择HTML页面项。新建名字为“index . html”的页面。 将index . html文件中的内容用一下代码替换: <!DOCTYPE html> <html xmlns="http://www.w3.org/1999/xhtml"> <head> <title>Product App</title> </head> <body> <div> <h2>All Products</h2> <ul id="products" /> </div> <div> <h2>Search by ID</h2> <input type="text" id="prodId" size="5" /> <input type="button" value="Search" onclick="find();" /> <p id="product" /> </div> <script src="http://ajax.aspnetcdn.com/ajax/jQuery/jquery-2.0.3.min.js"></script> <script> var uri = 'api/products'; $(document).ready(function () { // Send an AJAX request $.getJSON(uri) .done(function (data) { // On success, 'data' contains a list of products. $.each(data, function (key, item) { // Add a list item for the product. $('<li>', { text: formatItem(item) }).appendTo($('#products')); }); }); }); function formatItem(item) { return item.Name + ': $' + item.Price; } function find() { var id = $('#prodId').val(); $.getJSON(uri + '/' + id) .done(function (data) { $('#product').text(formatItem(data)); }) .fail(function (jqXHR, textStatus, err) { $('#product').text('Error: ' + err); }); } </script> </body> </html> 有几种方法可以得到jQuery。在这个例子中,我使用了微软的Ajax CDN。你也可以从http://jquery.com/下载它,ASP.NET “Web API”项目模板中也包含jQuery。 获取产品列表 发送一个HTTP get请求到“/ api /products”,得到一个产品列表。 jQuery getJSON函数发送一个AJAX请求。为响应包含JSON对象的数组。done函数挂了一个请求成功时的回调。在回调函数中通过产品信息更新DOM。 $(document).ready(function () { // Send an AJAX request $.getJSON(apiUrl) .done(function (data) { // On success, 'data' contains a list of products. $.each(data, function (key, item) { // Add a list item for the product. $('<li>', { text: formatItem(item) }).appendTo($('#products')); }); }); }); 通过ID获取产品 通过发送一个HTTP get请求到“/api/products/id”获取产品(id是产品id)。 function find() { var id = $('#prodId').val(); $.getJSON(apiUrl + '/' + id) .done(function (data) { $('#product').text(formatItem(data)); }) .fail(function (jqXHR, textStatus, err) { $('#product').text('Error: ' + err); }); } 我们仍然调用getJSON发送AJAX请求,但这一次我们把ID拼接到请求URI里面了。该请求的响应是一个用JSON表示的产品。 运行应用程序 按F5启动应用调试。网页应该看起来如下: 通过产品ID获取产品,输入ID点击Search: 如果你输入无效的ID,服务端将返回一个HTTP error: 拓展: 对于每一个 Http 消息, ASP.NET Web API 框架通过路由表决定由哪个控制器处理请求。 当你创建一个新的 Web API 项目时, 将会包含一个类似这样的一个默认的路由: /api/{controller}/{id} {controller} 和 {id} 是两个占位符, 当遇到一个符合这种样式的 URI , 将将会开始寻找合适的控制器方法进行调用, 规则如下: {controller} 用来与控制器名称像匹配; HTTP 请求的方法用来与方法名称匹配; (本规则只适用于 GET, POST, PUT 和 DELETE) {id} , 如果有, 将会用于和方法的 id 参数进行匹配; 查看HTTP请求和响应 原文是以IE为例讲解的,此处以Mozilla Firefox浏览器讲解。 现在回到网页并按F5键刷新网页。Mozilla Firefox将捕获浏览器和Web服务器之间的HTTP流量。概要视图显示一个页面的所有网络流量: 如果你点击“JSON”选项卡,你可以看到产品列表是如何被序列化成JSON。 原文地址 初次接触Web API,首次翻译外文文章,如有不对的地方,麻烦斧正。 译者:jiankunking 出处:http://blog.csdn.net/jiankunking ASP.NET Web API官方文档
作者:jiankunking 出处:http://blog.csdn.net/jiankunking 在Internet 信息服务(IIS)管理器中,右键浏览了一下XXXX.svc搞定。 IIS6的默认设置是如果20分钟没有request进来会回收w3wp进程。 新的request再进来,会自动启动新的进程。
最近公司升级内存,由于我用的还是win7 32位系统所以需要重装一下系统(安装的是win10 64位)。 系统重装方式主要有以下几种: 1、本机硬盘直接安装 适用于:32位升级到32位系统,64位系统升级到64位系统。 2、U盘 没有限制,不过需要设置BOSS中的启动顺序 小注: 如果BOSS有密码,可以将主板电池卸下几分钟,再装上即可。 3、光盘安装 没有限制,不过需要设置BOSS中的启动顺序 4、WinPE 硬盘版 本机安装WinPE 程序后,在进入系统的时候,会有一个WinPE 的选项,选择该选项,进入系统,在本机找到系统镜像或者安装包进行安装。 But由于我本机Usb口、光驱都已经损坏,所以我尝试过:方法3、方法4 但是系统装好了之后,开机后过不了多长时间,系统就会报出以下等等错误(变着花样的出错蓝屏,我也是醉了): 然后,我又重装几遍系统,但是问题依旧啊,于是百度等等各种方式,最终怀疑到内存上了,把内存拔出来用橡皮擦拭了金手指一下,再插上,搞定。 作者:jiankunking 出处:http://blog.csdn.net/jiankunking
电影中个人很喜欢的几句话: 此心安处是吾乡 有人去国怀乡,满目萧然。有人竹杖芒鞋轻胜马,一蓑烟雨任平生。而我是黄沙百战穿金甲,不破楼兰终不还 暗透了才看得见星光。要有向死而生的勇气。 有时等一封信,漫长得如同一生,但是慢一点又有什么不可以呢,慢一点才能写出优雅浪漫的话语;慢一点,才能仔细寻觅盼望的爱情。 作者:jiankunking 出处:http://blog.csdn.net/jiankunking
具体转换过程如下图: 作者:jiankunking 出处:http://blog.csdn.net/jiankunking
SELECT a.NAME ,b.rows FROM sysobjects AS a INNER JOIN sysindexes AS b ON a.id = b.id WHERE (a.type = 'u') AND ( b.indid IN ( 0 ,1 ) ) ORDER BY a.NAME ,b.rows DESC 作者:jiankunking 出处:http://blog.csdn.net/jiankunking
会更新ACT_RE_MODEL和ACT_GE_BYTEARRAY表, ACT_GE_BYTEARRAY表保存了PNG图片和xml文件信息(其实是json格式的字符串),并关联到ACT_RE_MODEL表。 ACT_GE_BYTEARRAY表中NAME_列值source-extra行中存储的是:PNG图片 ACT_GE_BYTEARRAY表中NAME_列值source行中存储的是:xml文件信息 流程设计器设计流程后,保存数据到ACT_RE_MODEL 表。 EDITOR_SOURCE_VALUE_ID(流程文件放在ACT_GE_BYTEARRAY中的ID); EDITOR_SOURCE_EXTRA_VALUE_ID(流程文件图像放在ACT_GE_BYTEARRAY中的ID)。 作者:jiankunking 出处:http://blog.csdn.net/jiankunking
今天想要用一下虚拟机,谁知道开启虚拟机时,提示“内部错误”,如下图: 经过上网查找发现问题处理VMware的相关服务上: 关于vm的5个服务都已停止,这个可以设置成开机自启,也可以右键“开始”。 开启这五个服务,问题搞定。 作者:jiankunking 出处:http://blog.csdn.net/jiankunking
账号 密码 角色 kermit kermit admin gonzo gonzo manager fozzie fozzie user 作者:jiankunking 出处:http://blog.csdn.net/jiankunking
从官网下载的Activiti-explorer的war文件内部默认是使用h2内存数据库的,如果想改用其他的数据库来做持久化,比如sql server,需要做如下配置: 1、修改db.properties文件 找到Tomcat安装目录下webapps文件夹。找到该文件夹下的 webapps\activiti-explorer\WEB-INF\classes\db.properties文件 内容如下: db=mssql jdbc.driver=com.microsoft.sqlserver.jdbc.SQLServerDriver jdbc.url=jdbc:sqlserver://localhost:1433;databaseName=activiti jdbc.username=sa jdbc.password=sa的密码2、从微软官网下载sql server的jdbc驱动; 只要在搜索引擎上搜索关键词:sql server jdbc driver 找到微软官网的连接,我使用的是: 点击打开链接 下载sqlserver的jdbc驱动压缩包:sqljdbc_4.0.2206.100_enu.tar.gz 然后将其中的sqljdbc4.jar 拷贝到Activiti-explorer的libs路径: Tomcat安装目录\webapps\activiti-explorer\WEB-INF\lib3、确认sql server的tcp/ip连接方式已经打开使用 sql server configuration manager来打开该实例的TCP/IP连接,然后重启sql server服务即可。 4、确认windows 防火墙已经关闭,或者已经把sql server的端口添加到特例中 5、使用sql server manangement studio来验证当前实例的TCP/IP方式是否已经开启。 使用如下方式如果可以正常连接,那么就说明sql server 的TCP/IP连接方式配置成功了。 6、创建名为activiti的数据库 7、都ok之后,就启动tomcat(或者重启服务器),可以正常启动,并且可以访问http://localhost:8080/activiti-explorer 连接。 8、tomcat正常启动之后, activiti-explorer会自动给activiti数据库创建相关的表,并对表的内容做初始化,如下: 本文整理自:点击打开链接
1、从https://github.com/henryyan/activiti-study下载: 将activiti-study的压缩包,解压 2、导入activiti-study项目 选择Maven项目 搞定 3、导入后效果: 作者:jiankunking 出处:http://blog.csdn.net/jiankunking
1、安装Java Jdk并配置环境变量 2、安装tomcat 3、下载Activiti包 解压后,将activiti-explorer.war文件放置到tomcat安装目录的webapps文件夹 重启tomcat 会自动解压activiti-explorer.war文件 浏览以下网址:http://localhost:8080/activiti-explorer 成功后,界面如下: 作者:jiankunking 出处:http://blog.csdn.net/jiankunking
GridView有RowCellClick事件,即单元格点击事件,但是针对列标题行以及列标题单元格却没有相应的事件。 在这里使用GridView的MouseDown事件。这里同样使用的是GridHitInfo来获取点击位置的信息,来判断是否在列标题上。GridHitInfo根据鼠标点击的x、y坐标获取该点的相关信息,判断是否点击在列标题行内。 private void gridView_MouseDown(object sender, MouseEventArgs e) { //鼠标左键点击 if (e.Button == MouseButtons.Left) { GridHitInfo info = gridView.CalcHitInfo(e.X, e.Y); //在列标题栏内且列标题name是"colName" if (info.InColumnPanel && info.Column.Name =="colName") { XtraMessageBox.Show("点击Name列标题!"); } } } 以上代码很简单,但是有个小问题,就是在该列右边线拖动这一列的列宽时,也会弹出对话框,因为这里拖动列宽也被视为点击鼠标。这显然不合适。解决这个问题的办法也很简单,就是判断鼠标点击位置不在右边线向左移动一点距离(3像素)范围内。下面对以上代码稍加修改,就不会再有这个问题了。 private void gridView_MouseDown(object sender, MouseEventArgs e) { //鼠标左键点击 if (e.Button == MouseButtons.Left) { GridHitInfo gridHitInfo = gridView.CalcHitInfo(e.X, e.Y); //在列标题栏内且列标题name是"colName" if (gridHitInfo.InColumnPanel && gridHitInfo.Column.Name =="colName") { //获取该列右边线的x坐标 GridViewInfo gridViewInfo = (GridViewInfo)this.gridView.GetViewInfo(); int x = gridViewInfo.GetColumnLeftCoord(gridHitInfo.Column) + gridHitInfo.Column.Width; //右边线向左移动3个像素位置不弹出对话框(实验证明3个像素是正好的) if (e.X < x - 3) { XtraMessageBox.Show("点击Name列标题!"); } } } } 上面的方法转载自:DevExpress GridView使用技巧之列标题点击事件 方法二(同事浩哥想出来的): if (info.InColumnPanel) { if (gridControl1.Cursor != Cursors.Default) { XtraMessageBox.Show("当鼠标在标题栏两列之间的时候,鼠标的样式会发生变化"); } else { //这里面应该是点击的列标题而不是两列之间的那部分 } } 作者:jiankunking 出处:http://blog.csdn.net/jiankunking
在研究activiti的过程中,有时想清空整个库中的数据,但用delete或者truncate命令清空表的时候,经常会遇到各种约束的提示,操作木有办法清除数据, 于是上网找了一个存储过程,分享一下: CREATE PROCEDURE [dbo].[PROC_DeleteAllData] AS --关闭约束 EXEC sp_MSForEachTable 'ALTER TABLE ? NOCHECK CONSTRAINT ALL' --关闭触发器 EXEC sp_MSForEachTable 'ALTER TABLE ? DISABLE TRIGGER ALL' --清空表 EXEC sp_MSForEachTable 'DELETE FROM ?' --启用约束 EXEC sp_MSForEachTable 'ALTER TABLE ? CHECK CONSTRAINT ALL' --启用触发器 EXEC sp_MSForEachTable 'ALTER TABLE ? ENABLE TRIGGER ALL' --查询库中数据 EXEC sp_MSFOREACHTABLE 'SELECT * FROM ?' GO 作者:jiankunking 出处:http://blog.csdn.net/jiankunking
作者:jiankunking 出处:http://blog.csdn.net/jiankunking 本文主要是以activiti-study中的xiaomage.xml流程图为例进行跟踪分析 具体的流程图如下: 流程图对应的XML文件如下: <?xml version="1.0" encoding="UTF-8"?> <definitions xmlns="http://www.omg.org/spec/BPMN/20100524/MODEL" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:activiti="http://activiti.org/bpmn" xmlns:bpmndi="http://www.omg.org/spec/BPMN/20100524/DI" xmlns:omgdc="http://www.omg.org/spec/DD/20100524/DC" xmlns:omgdi="http://www.omg.org/spec/DD/20100524/DI" typeLanguage="http://www.w3.org/2001/XMLSchema" expressionLanguage="http://www.w3.org/1999/XPath" targetNamespace="http://www.activiti.org/test"> <process id="MyProcess" name="MyProcess"> <documentation>Place documentation for the 'MyProcess' process here.</documentation> <startEvent id="startevent1" name="Start"/> <userTask id="sss" name="ddd" activiti:assignee="fq"/> <sequenceFlow id="flow1" name="" sourceRef="startevent1" targetRef="sss"/> <endEvent id="endevent1" name="End"/> <sequenceFlow id="flow2" name="" sourceRef="sss" targetRef="endevent1"/> </process> <bpmndi:BPMNDiagram id="BPMNDiagram_MyProcess"> <bpmndi:BPMNPlane bpmnElement="MyProcess" id="BPMNPlane_MyProcess"> <bpmndi:BPMNShape bpmnElement="startevent1" id="BPMNShape_startevent1"> <omgdc:Bounds height="35" width="35" x="340" y="150"/> </bpmndi:BPMNShape> <bpmndi:BPMNShape bpmnElement="sss" id="BPMNShape_sss"> <omgdc:Bounds height="55" width="105" x="305" y="250"/> </bpmndi:BPMNShape> <bpmndi:BPMNShape bpmnElement="endevent1" id="BPMNShape_endevent1"> <omgdc:Bounds height="35" width="35" x="340" y="370"/> </bpmndi:BPMNShape> <bpmndi:BPMNEdge bpmnElement="flow1" id="BPMNEdge_flow1"> <omgdi:waypoint x="357" y="185"/> <omgdi:waypoint x="357" y="250"/> </bpmndi:BPMNEdge> <bpmndi:BPMNEdge bpmnElement="flow2" id="BPMNEdge_flow2"> <omgdi:waypoint x="357" y="305"/> <omgdi:waypoint x="357" y="370"/> </bpmndi:BPMNEdge> </bpmndi:BPMNPlane> </bpmndi:BPMNDiagram> </definitions> 流程实例创建过程如下(下图转载自:activiti 源码笔记之startProcess): 流程启动跟踪分析: 图一: 图二: 图三: 图四: 以上主要是跟踪分析了,三个节点之间的流转情况。 在流转的时候需要注意以下两个接口: 原子操作(AtomicOperation)接口: public interface AtomicOperation { AtomicOperation PROCESS_START = new AtomicOperationProcessStart(); AtomicOperation PROCESS_START_INITIAL = new AtomicOperationProcessStartInitial(); AtomicOperation PROCESS_END = new AtomicOperationProcessEnd(); AtomicOperation ACTIVITY_START = new AtomicOperationActivityStart(); AtomicOperation ACTIVITY_EXECUTE = new AtomicOperationActivityExecute(); AtomicOperation ACTIVITY_END = new AtomicOperationActivityEnd(); AtomicOperation TRANSITION_NOTIFY_LISTENER_END = new AtomicOperationTransitionNotifyListenerEnd(); AtomicOperation TRANSITION_DESTROY_SCOPE = new AtomicOperationTransitionDestroyScope(); AtomicOperation TRANSITION_NOTIFY_LISTENER_TAKE = new AtomicOperationTransitionNotifyListenerTake(); AtomicOperation TRANSITION_CREATE_SCOPE = new AtomicOperationTransitionCreateScope(); AtomicOperation TRANSITION_NOTIFY_LISTENER_START = new AtomicOperationTransitionNotifyListenerStart(); AtomicOperation DELETE_CASCADE = new AtomicOperationDeleteCascade(); AtomicOperation DELETE_CASCADE_FIRE_ACTIVITY_END = new AtomicOperationDeleteCascadeFireActivityEnd(); void execute(InterpretableExecution execution); boolean isAsync(InterpretableExecution execution); } 注意: void execute(InterpretableExecution execution); InterpretableExecution接口: public interface InterpretableExecution extends ActivityExecution, ExecutionListenerExecution, PvmProcessInstance { void take(PvmTransition transition); void take(PvmTransition transition, boolean fireActivityCompletedEvent); void setEventName(String eventName); void setEventSource(PvmProcessElement element); Integer getExecutionListenerIndex(); void setExecutionListenerIndex(Integer executionListenerIndex); ProcessDefinitionImpl getProcessDefinition(); void setActivity(ActivityImpl activity); void performOperation(AtomicOperation etomicOperation); boolean isScope(); void destroy(); void remove(); InterpretableExecution getReplacedBy(); void setReplacedBy(InterpretableExecution replacedBy); InterpretableExecution getSubProcessInstance(); void setSubProcessInstance(InterpretableExecution subProcessInstance); InterpretableExecution getSuperExecution(); void deleteCascade(String deleteReason); boolean isDeleteRoot(); TransitionImpl getTransition(); void setTransition(TransitionImpl object); void initialize(); void setParent(InterpretableExecution parent); void setProcessDefinition(ProcessDefinitionImpl processDefinitionImpl); void setProcessInstance(InterpretableExecution processInstance); boolean isEventScope(); void setEventScope(boolean isEventScope); StartingExecution getStartingExecution(); void disposeStartingExecution(); } 注意: void performOperation(AtomicOperation etomicOperation); 单独摘出来的两个方法是图一中: 上下文、原子操作、执行器实体三者相互调用的关键。 上图的具体调用情况如下: ExecutionEntity类中的: public void performOperation(AtomicOperation executionOperation) { if (executionOperation.isAsync(this)) { scheduleAtomicOperationAsync(executionOperation); } else { performOperationSync(executionOperation); } } protected void performOperationSync(AtomicOperation executionOperation) { Context .getCommandContext() .performOperation(executionOperation, this); } performOperation函数中调用上下文CommandContext类中的: public void performOperation(AtomicOperation executionOperation, InterpretableExecution execution) { nextOperations.add(executionOperation); if (nextOperations.size()==1) { try { Context.setExecutionContext(execution); while (!nextOperations.isEmpty()) { AtomicOperation currentOperation = nextOperations.removeFirst(); if (log.isTraceEnabled()) { log.trace("AtomicOperation: {} on {}", currentOperation, this); } if (execution.getReplacedBy() == null) { currentOperation.execute(execution); } else { currentOperation.execute(execution.getReplacedBy()); } } } finally { Context.removeExecutionContext(); } } } performOperation函数调用原子操作(AtomicOperation)接口中的void execute(InterpretableExecution execution)来处理。 该处的处理分为两种情况: 1、根据AtomicOperation接口标识来继续进行流转 (再次调用ExecutionEntity类中的performOperation(AtomicOperation executionOperation)方法) 比如: PROCESS_START=》PROCESS_START_INITIAL=》ACTIVITY_EXECUTE。。。。。。 具体可以参考本文图一到图四的代码跟踪中的标识。 2、根据节点上的ActivityBehavior类进行不同的处理 Activiti节点(开始、结束、任务、网关等等)都是Activity类型的,只是其挂的ActivityBehavior不同,通过不同的ActivityBehavior来实现相应的操作。 作者:jiankunking 出处:http://blog.csdn.net/jiankunking
ExecutionEntity内部含有parent,是一个执行树或执行路径,应该是一个流程实例的执行过程,一个实例对应一个ExecutionEntity,通过getActivity得到的是当前正在执行的activity. Activiti之流程部署: 流程文件部署主要涉及到3个表,分别是:ACT_GE_BYTEARRAY、ACT_RE_DEPLOYMENT、ACT_RE_PROCDEF。主要完成“部署包”–>“流程定义文件”–>“所有包内文件”的解析部署关系 流程定义的部署需要完成如下操作: 1、初始化到数据库,完成三张表的插入,一个部署包里可以有N个流程定义,所以 PROCDEF表内对应N条数据,BYTEARRAY表内对应N+条记录,每个xml文件一条记录,图片也会另外存放一条记录。DEPLOYMENT内会存放一条记录 2、解析后的流程定义存入DeploymentCache 流程部署的序列图: 流程部署已后,启动流程时,会调用StartProcessInstanceCmd,来启动流程。 StartProcessInstanceCmd在查找ProcessDefinitionEntity时,会从deploymentCache中查找,当cache中不存在时,会执行deploymentCache.deploy() 。 Activiti之Query查询: 缓存查询: Activiti之manager: 创建流程实例: 创建流程实例: runtimeService.startProcessInstanceByKey("financialReport") 执行步骤: 1、 首先根据” financialReport”在数据库中查找流程定义 2、 查找到流程定义后,再从processDefinitionCache中获取已经缓存的、部署完成的(xml 文件被解析的)流程定义,如果processDefinitionCache中不存在,那么执行流程的解析过程 3、 创建流程实例 代码执行过程: ①AtomicOperation: org.activiti.engine.impl.pvm.runtime.AtomicOperationProcessStart ②AtomicOperation: org.activiti.engine.impl.pvm.runtime.AtomicOperationProcessStartInitial ③AtomicOperation: org.activiti.engine.impl.pvm.runtime.AtomicOperationActivityExecute ④ProcessInstance[805] executes Activity(theStart): org.activiti.engine.impl.bpmn.behavior.NoneStartEventActivityBehavior ⑤Leaving activity ‘theStart’ 流程启动: 流程启动执行: ExecutionEntity.performOperation(AtomicOperation.PROCESS_START) 接着执行:execution.performOperation(PROCESS_START_INITIAL); 接着执行:execution.performOperation(ACTIVITY_EXECUTE); 其内部执行逻辑为: ActivityBehavior activityBehavior = activity.getActivityBehavior(); activityBehavior.execute(execution); activityBehavior有很多的实现类,比如:当流程启动时,启动节点的activityBehavior实际上是NoneStartEventActivityBehavior的实例。给每个节点装配ActivityBehavior应该是在流程解析时完成的,一个类型的节点装配特定类型的Behavior,固定写死的。 Activiti之数据操作 Insert、update、delete三种操作,先更新cache,在一个cmd中,把所有需要insert的数据放入一个map,执行完cmd后,统一进行insert、update、delete操作 每个Service的“命令执行者”都是由: “LogInterceptor–>CommandContextInterceptor–>CommandExecutorImpl”组成的执行链状结构。 日志—>执行前后操作(比如:transaction的开启及commit)执行命令 Activiti之任务分配 任务分配时,根据流程定义,首先根据人工活动定义的Assignee,直接进行分配,如果 Assignee有值,此时该任务处于已认领状态。然后继续执行任务的候选人分配,候选人分两种:候选组表达式及候选人表达式,解析表达式,进行公共任务分配。任务应该没有状态,标记是已经被认领时,是根据该任务的属性:task.getAssignee()是否有值进行判断。 Activiti之任务认领 1、没有找到判断是否有权限进行认领操作 2、 如果该任务属性:task.getAssignee()有值,说明已经被认领,如果和当前认领人不同, 抛出异常,is already claimed by someone else 执行认领操作,数据库操作步骤(分别对应三张表): ⑴、update HistoricActivityInstanceEntity ⑵、update HistoricTaskInstanceEntity ⑶、update TaskEntity Activiti之任务完成 在创建一个新人工任务时,在数据表IdentityLinkEntity中删除当前已完成的这条数据,当流程实例结束时,删除execution表中的当前流程数据。 完成一个人工任务时,完成的数据库表操作: 1. insert HistoricActivityInstanceEntity 2. insert TaskEntity 3. insert HistoricTaskInstanceEntity 4. insert IdentityLinkEntity 5. update ExecutionEntity 6. update HistoricActivityInstanceEntity 7. update HistoricTaskInstanceEntity 8. delete IdentityLinkEntity 9. delete TaskEntity 完成任务时,走的过程: 1.Leaving activity ‘writeReportTask’ [org.activiti.engine.impl.bpmn.behavior.BpmnActivityBehavior] 2.AtomicOperation: org.activiti.engine.impl.pvm.runtime.AtomicOperationTransitionNotifyListenerEnd 3.AtomicOperation: org.activiti.engine.impl.pvm.runtime.AtomicOperationTransitionNotifyListenerEnd@1c09624 4.AtomicOperation: org.activiti.engine.impl.pvm.runtime.AtomicOperationTransitionDestroyScope 5.AtomicOperation: org.activiti.engine.impl.pvm.runtime.AtomicOperationTransitionNotifyListenerTake 6.ProcessInstance[605] takes transition (writeReportTask)–flow2–>(verifyReportTask) [org.activiti.engine.impl.pvm.runtime.AtomicOperationTransitionNotifyListenerTake] 7.AtomicOperation: org.activiti.engine.impl.pvm.runtime.AtomicOperationTransitionCreateScope 8. AtomicOperation: org.activiti.engine.impl.pvm.runtime.AtomicOperationTransitionNotifyListenerStart 9.AtomicOperation: org.activiti.engine.impl.pvm.runtime.AtomicOperationTransitionNotifyListenerStart 10. AtomicOperation: org.activiti.engine.impl.pvm.runtime.AtomicOperationActivityExecute@1478a2don org.activiti.engine.impl.interceptor.CommandContext 11. ProcessInstance[605] executes Activity(verifyReportTask): org.activiti.engine.impl.bpmn.behavior.UserTaskActivityBehavior Activiti其它 Activiti之executionEntity: Activiti之定义模型: Activiti之优缺点: 1、 一直没能理解它的executionEntity的模型,它提供了三个接口:pvmexecution、execution、 activityExecution,是它的名字起的不好还是有其它的思想? 2、 当流程结束时,删除对应的记录,这种操作很巧妙的支持了集群环境 3、 见识了把一个流程从开始到结束的抽象:分解成各种cmd和AtomicOperation 4、 通过一个执行链完成transaction的编程式事物控制 5、 不支持组织机构的扩展,与业务系统进行集成时,要写很多的event来完成任务分配 6、 可以记录冗余业务数据来组合查询任务 7、 把已完成的流程做数据清理并备份 原文链接
1、ProcessInstance 与ProcessDefinition 流程实例(ProcessInstance)和流程定义(ProcessDefinition)的关系,与类和实例对象的关系有点像,ProcessDefinition是整个流程步骤的说明而ProcessInstance就是指流程定义从开始到结束的那个最大的执行路线。 2、Execution Execution是按照ProcessDefinition的规则执行的当前的路线,如果ProcessDefinition只有一个执行路线的话,那么Execution和ProcessInstance就是完全一样了如果ProcessDefinition中有多个执行路线的话,Execution和ProcessInstance可能是同一个也可能不是同一个。 所以得出结论:一个流程中ProcessInstance有且只能有一个,而Execution可以存在多个。 3、Task 任务(Task)就是当流程执行到某步骤或某环节时生产的任务信息。 4、数据库表设计 5、基础框架 6、BPMN2.0 BPMN2.0对流程执行语义定义了三类基本要素,它们是日常业务流程的“三板斧”: Activities(活动)——在工作流中所有具备生命周期状态的都可以称之为“活动”,如原子级的任务(Task)、流向(Sequence Flow),以及子流程(Sub-Process)等 Gateways(网关)——顾名思义,所谓“网关”就是用来决定流程流转指向的,可能会被用作条件分支或聚合,也可以被用作并行执行或基于事件的排它性条件判断 Events(事件)——在BPMN2.0执行语义中也是一个非常重要的概念,像启动、结束、边界条件以及每个活动的创建、开始、流转等都是流程事件,利用事件机制,可以通过事件控制器为系统增加辅助功能,如其它业务系统集成、活动预警等 这三类执行语义的定义涵盖了业务流程常用的Sequence Flow(流程转向)、Task(任务)、Sub-Process(子流程)、Parallel Gateway(并行执行网关)、ExclusiveGateway(排它型网关)、InclusiveGateway(包容型网关)等常用图元,如下图:
作者:jiankunking 出处:http://blog.csdn.net/jiankunking 1、定义 简单工厂模式(Simple Factory Pattern)属于类的创建型模式,又叫静态工厂方法模式(Static FactoryMethod Pattern),但不属于23种GOF设计模式之一,是通过专门定义一个类来负责创建其他类的实例,被创建的实例通常都具有共同的父类。 2、基本简介 简单工厂模式的实质是由一个工厂类根据传入的参数,动态决定应该创建哪一个产品类(这些产品类继承自一个父类或接口)的实例。 该模式中包含的角色及其职责: 工厂(Creator)角色 简单工厂模式的核心,它负责实现创建所有实例的内部逻辑。工厂类的创建产品类的方法可以被外界直接调用,创建所需的产品对象。 抽象产品(Product)角色 简单工厂模式所创建的所有对象的父类,它负责描述所有实例所共有的公共接口。 具体产品(Concrete Product)角色 是简单工厂模式的创建目标,所有创建的对象都是充当这个角色的某个具体类的实例。 UML图(来自百度百科) 特点、: 1、只有一个工厂(具体的,没有抽象) 2、只生产一种产品(抽象的产品) 3、这种产品可以有多种具体产品类型(派生) 工厂模式就相当于创建实例对象的new,我们经常要根据类Class生成实例对象,如A a=new A() 工厂模式也是用来创建实例对象的,所以以后new时就要多个心眼,是否可以考虑实用工厂模式,虽然这样做,可能多做一些工作,但会给你系统带来更大的可扩展性和尽量少的修改量。 比如: 在控制台输入两个数还有要进行的是:+、-、*、/ 中的哪一种运算,那么应该怎么写呢? class Program { static void Main(string[] args) { Console.WriteLine("请输入第一个数字"); int a = int.Parse(Console.ReadLine()); Console.WriteLine("请输入第二个数字"); int b = int.Parse(Console.ReadLine()); Console.WriteLine("请输入运算符号"); string op = Console.ReadLine(); int result; ////////////////////////////// Computer com = null; com = SimpleFactory.LoadComputer(op); com.NumberA = a; com.NumberB = b; result = com.Result; ////////////////////////////// Console.WriteLine("{0}{1}{2}={3}", a, op, b, result); } } public static class SimpleFactory { /// <summary> /// 简单的工厂的方法 /// *封装了对象实例创建的复杂度 /// </summary> /// <param name="op">用来区分具体产品的标识</param> /// <returns>产品的抽象</returns> public static Computer LoadComputer(string op) { Computer com = null; switch (op) { case "+": com = new AddComputer(); break; case "-": com = new JianComputer(); break; case "*": com = new ChenComputer(); break; case "/": com = new ChuComputer(); break; case "^": com = new PowComputer(); break; default: com = new AddComputer(); break; } return com; } } /// <summary> /// 运算 /// </summary> public abstract class Computer { protected int _NumberA; public int NumberA { get { return _NumberA; } set { _NumberA = value; } } protected int _NumberB; public int NumberB { get { return _NumberB; } set { _NumberB = value; } } /// <summary> /// 子类必须完成的功能:计算结果并返回结果 /// </summary> public abstract int Result { get; } } public class AddComputer : Computer { public override int Result { get { return base._NumberA + base._NumberB; } } } public class JianComputer : Computer { public override int Result { get { return base._NumberA - base._NumberB; } } } public class ChenComputer : Computer { public override int Result { get { return base._NumberA * base._NumberB; } } } public class ChuComputer : Computer { public override int Result { get { if (base._NumberB == 0) { throw new ArgumentException("被除数不能为零"); } return base._NumberA / base._NumberB; } } } public class PowComputer : Computer { public override int Result { get { return (int)Math.Pow(base._NumberA, base._NumberB); } } } 3、优缺点: 优点 工厂类是整个模式的关键.包含了必要的逻辑判断,根据外界给定的信息,决定究竟应该创建哪个具体类的对象.通过使用工厂类,外界可以从直接创建具体产品对象的尴尬局面摆脱出来,仅仅需要负责“消费”对象就可以了。而不必管这些对象究竟如何创建及如何组织的.明确了各自的职责和权利,有利于整个软件体系结构的优化。 缺点 由于工厂类集中了所有实例的创建逻辑,违反了高内聚责任分配原则,将全部创建逻辑集中到了一个工厂类中;它所能创建的类只能是事先考虑到的,如果需要添加新的类,则就需要改变工厂类了。 当系统中的具体产品类不断增多时候,可能会出现要求工厂类根据不同条件创建不同实例的需求.这种对条件的判断和对具体产品类型的判断交错在一起,很难避免模块功能的蔓延,对系统的维护和扩展非常不利; 这些缺点在工厂方法模式中得到了一定的克服。 使用场景 工厂类负责创建的对象比较少; 客户只知道传入工厂类的参数,对于如何创建对象(逻辑)不关心; 由于简单工厂很容易违反高内聚责任分配原则,因此一般只在很简单的情况下应用。 小注:本文部分资料整理自网络,在此表示感谢。
保证一个类仅有一个实例,并提供一个该实例的全局访问点。 ——《设计模式》 单例模式的概念很简单,下面以C#语言为例子,列出常见单例写法的优缺点。 1、简单实现 public sealed class Singleton { static Singleton instance = null; public void Show() { Console.WriteLine( "instance function"); } private Singleton() { } public static Singleton Instance { get { if (instance == null) { instance = new Singleton(); } return instance; } } }评注: 对于线程来说不安全 单线程中已满足要求优点: 由于实例是在 Instance 属性方法内部创建的,因此类可以使用附加功能 直到对象要求产生一个实例才执行实例化;这种方法称为“惰性实例化”。惰性实例化避免了在应用程序启动时实例化不必要的 singleton。 2、线程的安全 public sealed class Singleton { static Singleton instance = null; private static readonly object padlock = new object(); private Singleton() { } public static Singleton Instance { get { lock (padlock) { if (instance == null) { instance = new Singleton(); } } return instance; } } } 评注: 同一个时刻加了锁的那部分程序只有一个线程可以进入 对象实例由最先进入的那个线程创建 后来的线程在进入时(instence == null)为假,不会再去创建对象实例 增加了额外的开销,损失了性能 3、双重锁定 public sealed class Singleton { static Singleton instance = null; private static readonly object padlock = new object(); private Singleton() { } public static Singleton Instance { get { if (instance == null) { lock (padlock) { if (instance == null) { instance = new Singleton(); } } } return instance; } } }评注: 多线程安全 线程不是每次都加锁 允许实例化延迟到第一次访问对象时发生 4、静态初始化 public sealed class Singleton { private static readonly Singleton instance = null; static Singleton() { instance = new Singleton(); } private Singleton() { } public static Singleton Instance { get { return instance; } } }评注: 依赖公共语言运行库负责处理变量初始化 公共静态属性为访问实例提供了一个全局访问点 对实例化机制的控制权较少(.NET代为实现) 静态初始化是在 .NET 中实现 Singleton 的首选方法 小注: 静态构造函数既没有访问修饰符,C#会自动把他们标记为private,之所以必须标记为private, 是为了阻止开发人员写的代码调用它,对它的调用总是由CLR负责的。 5、延迟初始化 public sealed class Singleton { private Singleton() { } public static Singleton Instance { get { return Nested.instance; } } public static void Hello() { } private class Nested { internal static readonly Singleton instance = null; static Nested() { instance = new Singleton(); } } } 评注: 初始化工作由Nested类的一个静态成员来完成,这样就实现了延迟初始化。 由于静态函数的调用时机,是在类被实例化或者静态成员被调用的时候进行调用,并且是由.net框架来调用静态构造函数来初始化静态成员变量, 所以,如果按照写法四来写,再调用Hello方法的时候,就会实例化出来Singleton实例,这不是我们想看到的,因为我们有可能只是想用Hello方法,而不是别的。 注意事项: 1、Singleton模式中的实例构造器可以设置为protected以允许子类派生。 2、Singleton模式一般不要支持ICloneable接口,因为这可能会导致多个对象实例,与Singleton模式的初衷违背。 3、Singleton模式一般不要支持序列化,因为这也有可能导致多个对象实例,同样与Singleton模式的初衷违背。 4、Singletom模式只考虑到了对象创建的管理,没有考虑对象销毁的管理。就支持垃圾回收的平台和对象的开销来讲,我们一般没有必要对其销毁进行特殊的管理。 总结: 1、Singleton模式是限制而不是改进类的创建。 2、理解和扩展Singleton模式的核心是“如何控制用户使用new对一个类的构造器的任意调用”。 3、可以很简单的修改一个Singleton,使它有少数几个实例,这样做是允许的而且是有意义的。 作者:jiankunking 出处:http://blog.csdn.net/jiankunking 本文部分内容来自网络,截图部分来自《CLR.via.C#第三版》
一、针对接口编程,而不是针对实现编程 – 客户无需知道所使用对象的特定类型,只需要知道对象拥有客户所期望的接口。 小注: 接口是定义行为,只是定义我们要做什么事情,至于如何做这些事情是由接口的实现来做的,当我们定义接口的时候无需关心这个行为如何实现,只要知道有这个接口就可以。 别人在调用你的代码的时候,都是调用你的接口对象,至于如何实现,对别人是透明的。 二、优先使用对象组合,而不是类继承 – 类继承通常为“白箱复用”,对象组合通常为“黑箱复用”。继承在某种程度上破坏了封装性,子类父类耦合度高;而对象组合则只要求被组合的对象具有良好定义的接口,耦合度低。 小注: 因为继承在编译时刻就定义了,所以无法在运行时刻改变从父类继承的实现。更糟的是,父类通常至少定义了部分子类的具体表示。因为继承对子类揭示了其父类的实现细节,所以继承常被认为“破坏了封装性” 。子类中的实现与它的父类有如此紧密的依赖关系,以至于父类实现中的任何变化必然会导致子类发生变化。当你需要复用子类时,实现上的依赖性就会产生一些问题。如果继承下来的实现不适合解决新的问题,则父类必须重写或被其他更适合的类替换。这种依赖关系限制了灵活性并最终限制了复用性。一个可用的解决方法就是只继承抽象类,因为抽象类通常提供较少的实现。 对象组合是通过获得对其他对象的引用而在运行时刻动态定义的。组合要求对象遵守彼此的接口约定,进而要求更仔细地定义接口,而这些接口并不妨碍你将一个对象和其他对象一起使用。这还会产生良好的结果:因为对象只能通过接口访问,所以我们并不破坏封装性;只要类型一致,运行时刻还可以用一个对象来替代另一个对象;更进一步,因为对象的实现是基于接口写的,所以实现上存在较少的依赖关系。 对象组合对系统设计还有另一个作用,即优先使用对象组合有助于你保持每个类被封装,并被集中在单个任务上。这样类和类继承层次会保持较小规模,并且不太可能增长为不可控制的庞然大物。另一方面,基于对象组合的设计会有更多的对象 (而有较少的类),且系统的行为将依赖于对象间的关系而不是被定义在某个类中。 三、封装变化点 – 使用封装来创建对象之间的分界层,让设计者可以在分界层的一侧进行修改,而不会对另一侧产生不良的影响,从而实现层次间的松耦合。 小注: 考虑你的设计中哪些地方可能变化,这种方式与关注会导致重新设计的原因相反。它不是考虑什么时候会迫使你的设计改变,而是考虑你怎样才能够在不重新设计的情况下进行改变。这里的关键在于封装发生变化的概念,这是许多设计模式的主题。---《设计模式》 四、使用重构得到模式 - 设计模式的应用不宜先入为主,一上来就使用设计模式是对设计模式的最大误用。没有一步到位的设计模式。敏捷软件开发实践提倡的“Refactoring to Patterns ”是目前普遍公认的最好的使用设计模式的方法。 五、单一职责原则(SRP Single Responsibility Principle) – 一个类应该仅有一个引起它变化的原因。 小注: 所谓职责是指类变化的原因。如果一个类有多于一个的动机被改变,那么这个类就具有多于一个的职责。而单一职责原则就是指一个类或者模块应该有且只有一个改变的原因。 六、开放封闭原则(OCP Open Closed Principle) – 类模块应该是可扩展的,但是不可修改(对扩展开放,对更改封闭) 小注: 1、对扩展开放,意味着有新的需求或变化时,可以对现有代码进行扩展,以适应新的情况。 2、对修改封闭,意味着类一旦设计完成,就可以独立完成其工作,而不要对类进行任何修改。 比如: 将业务功能抽象为接口,当业务员依赖于固定的抽象时,对于修改就是封闭的;而通过继承和多态机制,从抽象体派生出新的扩展实现,就是对扩展的开放。 七、Liskov 替换原则(LSP Liskov Substitution Pinciple) – 子类必须能够替换它们的基类。 八、依赖倒置原则(DIP Dependency Inversion Principle) – 高层模块不应该依赖于低层模块,二者都应该依赖于抽象。 – 抽象不应该依赖于实现细节,实现细节应该依赖于抽象。 九、接口隔离原则(ISP Interface Segregation Principle) – 不应该强迫客户程序依赖于它们不用的方法。 尽量应用专门的接口,而不是单一的总接口,接口应该面向用户,将依赖建立在最小的接口上。 十、合成/聚合复用原则(CARP Composite/Aggregate Reuse Principle ) -在新对象中聚合已有对象,使之成为新对象的成员,从而通过操作这些对象达到复用的目的。 合成方式较继承方式耦合更松散,所以应该少继承、多聚合。 小注: 如果两个类之间是“Has-A”的关系应使用组合或聚合,如果是“Is-A”关系可使用继承。 十一、迪米特法则(LoD Law of Demeter ) 又叫最小知识原则,指软件实体应该尽可能少的和其他软件实体发生相互作用 小注: 迪米特法则的初衷在于降低类之间的耦合。由于每个类尽量减少对其他类的依赖,因此,很容易使得系统的功能模块功能独立,相互之间不存在(或很少有)依赖关系。 迪米特法则不希望类之间建立直接的联系。如果真的有需要建立联系,也希望能通过它的友元类来转达。因此,应用迪米特法则有可能造成的一个后果就是:系统中存在大量的中介类,这些类之所以存在完全是为了传递类之间的相互调用关系——这在一定程度上增加了系统的复杂度。 本文作者:jiankunking 出处:http://blog.csdn.net/jiankunking
原文地址:这里写链接内容
一、命令模式: 将一个请求封装为一个对象,从而使你可用不同的请求对客户进行参数化;对请求排队或记录请求日志,以及支持可撤销的操作。 ——《设计模式》 二、模式结构: Command: 定义命令的接口,声明执行的方法。 ConcreteCommand: 命令接口实现对象,是“虚”的实现;通常会持有接收者,并调用接收者的功能来完成命令要执行的操作。 Receiver: 接收者,真正执行命令的对象。任何类都可能成为一个接收者,只要它能够实现命令要求实现的相应功能。 Invoker: 要求命令对象执行请求,通常会持有命令对象,可以持有很多的命令对象。这个是客户端真正触发命令并要求命令执行相应操作的地方,也就是说相当于使用命令对象的入口。 Client: 创建具体的命令对象,并且设置命令对象的接收者。注意这个不是我们常规意义上的客户端,而是在组装命令对象和接收者,或许,把这个Client称为装配者会更好理解,因为真正使用命令的客户端是从Invoker来触发执行。 三、模式协作: 1、 Client创建一个ConcreteCommand对象并指定他的Receiver对象。 2、 某个Invoker对象存储该ConcreteCommand对象。 3、该Invoker通过调用Command对象的Execute操作来提交一个请求。若该命令是可撤销的,ConcreteCommand就在执行Execute操作之前存储当前状态以用于取消该命令。 4、 ConcreteCommand对象对调用它的Receiver的一些操作以执行该请求。 四、模式分析: 1、命令模式的本质是对命令进行封装,将发出命令的责任和执行命令的责任分割开。 2、每一个命令都是一个操作:请求的一方发出请求,要求执行一个操作;接收的一方收到请求,并执行操作。 3、命令模式允许请求的一方和接收的一方独立开来,使得请求的一方不必知道接收请求的一方的接口,更不必知道请求是怎么被接收,以及操作是否被执行、何时被执行,以及是怎么被执行的。 4、命令模式使请求本身成为一个对象,这个对象和其他对象一样可以被存储和传递。 5、命令模式的关键在于引入了抽象命令接口,且发送者针对抽象命令接口编程,只有实现了抽象命令接口的具体命令才能与接收者相关联。 五、模式优点: 1、降低对象之间的耦合度。 2、新的命令可以很容易地加入到系统中。 3、可以比较容易地设计一个组合命令。 4、调用同一方法实现不同的功能。 六、模式缺点: 使用命令模式可能会导致某些系统有过多的具体命令类。 因为针对每一个命令都需要设计一个具体命令类,因此某些系统可能需要大量具体命令类,这将影响命令模式的使用。 七、适用环境: 1、使用命令模式作为“CallBack”在面向对象系统中的替代。“CallBack”讲的便是先将一个函数登记上,然后在以后调用此函数。 2、需要在不同的时间指定请求、将请求排队。一个命令对象和原先的请求发出者可以有不同的生命期。换言之,原先的请求发出者可能已经不在了,而命令对象本身仍然是活动的。这时命令的接收者可以是在本地,也可以在网络的另外一个地址。命令对象可以在串形化之后传送到另外一台机器上去。3、系统需要支持命令的撤消(undo)。命令对象可以把状态存储起来,等到客户端需要撤销命令所产生的效果时,可以调用undo()方法,把命令所产生的效果撤销掉。命令对象还可以提供redo()方法,以供客户端在需要时,再重新实施命令效果。 4、如果一个系统要将系统中所有的数据更新到日志里,以便在系统崩溃时,可以根据日志里读回所有的数据更新命令,重新调用Execute()方法一条一条执行这些命令,从而恢复系统在崩溃前所做的数据更新。 八、实例解析: 电视机遥控器: 电视机是请求的接收者,遥控器是请求的发送者,遥控器上有一些按钮,不同的按钮对应电视机的不同操作。抽象命令角色由一个命令接口来扮演,有三个具体的命令类实现了抽象命令接口,这三个具体命令类分别代表三种操作:打开电视机、关闭电视机和切换频道。显然,电视机遥控器就是一个典型的命令模式应用实例。 在面向对象程式设计的范畴中,命令模式(Command Pattern)是一种设计模式,它尝试以物件来代表实际行动。 /// <summary> /// 执行命令的接口 /// </summary> public interface ICommand { //命令执行方法 void Execute(); } /// <summary> /// 频道切换命令 /// </summary> public class CommandChange : ICommand { private Tv myTv; private int channel; public CommandChange(Tv tv, int channel) { myTv = tv; this.channel = channel; } public void Execute() { myTv.changeChannel(channel); } } /// <summary> /// 关机命令 /// </summary> public class CommandOff : ICommand { private Tv myTv; public CommandOff(Tv tv) { myTv = tv; } public void Execute() { myTv.turnOff(); } } /// <summary> /// 开机命令 /// </summary> public class CommandOn : ICommand { private Tv myTv; public CommandOn(Tv tv) { myTv = tv; } public void Execute() { myTv.turnOn(); } } /// <summary> /// 可以看作是遥控器 /// Invoker对象:要求命令对象执行请求,通常会持有命令对象,可以持有很多的命令对象。 /// 这个是客户端真正触发命令并要求命令执行相应操作的地方,也就是说相当于使用命令对象的入口。 /// </summary> public class Control { private ICommand onCommand, offCommand, changeChannel; public Control(ICommand on, ICommand off, ICommand channel) { onCommand = on; offCommand = off; changeChannel = channel; } public void TurnOn() { onCommand.Execute(); } public void TurnOff() { offCommand.Execute(); } public void ChangeChannel() { changeChannel.Execute(); } } /// <summary> /// 命令接收者 /// </summary> public class Tv { public int currentChannel = 0; public void turnOn() { MessageBox.Show("The televisino is on."); } public void turnOff() { MessageBox.Show("The television is off."); } public void changeChannel(int channel) { this.currentChannel = channel; MessageBox.Show("Now TV channel is " + channel); } } 本文参考: http://www.cnblogs.com/zhili/p/CommandPattern.html http://baike.baidu.com/link?url=Cl2lla1YA9SELV-b1FsijUA1n6MMT_ePuOIBl2OgZe4UqpR81TLbQ40qI5DAQ3kICkZ1GLZkOUq8oUu8mLcQj_ demo代码:http://download.csdn.net/detail/xunzaosiyecao/9448285 作者:jiankunking 出处:http://blog.csdn.net/jiankunking
1、Jquery中$(document).ready()和window.onload的区别 2、多个库之间冲突 3、简单选择器 在使用 jQuery 选择器时,我们首先必须使用“$()”函数来包装我们的 CSS 规则。而CSS 规则作为参数传递到 jQuery 对象内部后,再返回包含页面中对应元素的 jQuery 对象。随后,我们就可以对这个获取到的 DOM 节点进行行为操作了。 #box { //使用 ID 选择器的 CSS 规则 color:red; //将 ID 为 box 的元素字体颜色变红 } 在 jQuery 选择器里,我们使用如下的方式获取同样的结果: $('#box').css('color', 'red'); //获取 DOM 节点对象,并添加行为 除了 ID 选择器之外,还有两种基本的选择器,分别为:元素标签名和类(class): CSS 找到元素后添加的是单一的样式,而 jQuery 则添加的是动作行为。最重要的一点是:CSS 在添加样式的时候,高级选择器会对部分浏览器不兼容,而 jQuery 选择器在添加 CSS 样式的时候却不必为此烦恼。 4、进阶选择器 //群组选择器 span, em, .box { //多种选择器添加红色字体 color:red; } //群组选择器 jQuery 方式 $('span, em, .box').css('color', 'red'); //后代选择器 ul li a { //层层追溯到的元素添加红色字体 color:red; } //群组选择器 jQuery 方式 $('ul li a').css('color', 'red'); //通配选择器 * { //页面所有元素都添加红色字体 color:red; } $('*').css('color', 'red');//通配选择器 小注: 在实际使用上,通配选择器一般用的并不多,尤其是在大通配上,比如:$(‘*’),这种使用方法效率很低,影响性能,建议竟可能少用。 组合了多种选择器 $('#box p, ul li *').css('color', 'red'); <!DOCTYPE html> <html> <head> <meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> <title></title> <script src="../Src/jquery.js"></script> <meta charset="utf-8" /> <script type="text/javascript"> $(document).ready($(function () { $('#box p, ul li *').css('color', 'red'); })) </script> </head> <body> <div id="box"> <p>pppp1</p> div <p>pppp2</p> </div> <p> pppp3 </p> <ul> <li> <a>aaaa</a></li> <li> 2</li> <li> 3</li> </ul> </body> </html> 效果:
1、右键“我的电脑”图标,在弹出菜单中依次选择“属性”-“高级”-“环境变量”。 2、在“环境变量”的“系统变量”选项新建系统变量(如果该变量已有,就修改该变量): JAVA_HOME,值为:C:\Program Files\Java\jdk1.7.0(填写你的JDK路径即可)。 同样方法再建一个系统变量(如果该变量已有,就修改该变量): CLASSPATH,值为:;%JAVA_HOME%\lib\dt.jar;%JAVA_HOME%\lib\tools.jar; 3、在系统变量中找到变量“PATH”,双击打开,把以下代码加到变量值末尾:“;%JAVA_HOME%\bin;%JAVA_HOME%\jre\bin;”(注意最前的”;”不能少) 配置完成后启动eclipse,它会自动完成对JAVA环境的配置。 小注: 这里已经新建过上面提到的三个变量,所以出现的是编辑界面。 下面检验是否配置成功,运行cmd命令,在出现的对话框输入”java“命令,如果出现以下结果,则表明配置成功: 具体的新建详细步骤也可以参考:这里写链接内容 作者:jiankunking 出处:http://blog.csdn.net/jiankunking
作者:jiankunking 出处:http://blog.csdn.net/jiankunking 1、首先,在安装jdk的时候要安装源码,或许你没注意到,其实源码就在我们的jdk安装目录下面。(安装的时候是可以选择源码安装路径的,如果选择了其他路径就不会在jdk下面了,默认是在jdk下面的。) 2、打开eclipse,点 “window”-> “Preferences” -> “Java” -> “Installed JRES” 此时”Installed JRES”右边是列表窗格,列出了系统中的 JRE 环境,选择你的JRE,然后点边上的 “Edit…”, 会出现一个窗口(Edit JRE) 选中rt.jar文件的这一项:“c:\program files\java\jre_1.5.0_06\lib\rt.jar” 点 左边的“+” 号展开它 展开后,可以看到“Source Attachment:(none)”,点这一项,点右边的按钮“Source Attachment…”, 选择你的JDK目录下的 “src.zip”文件 搞定! 方法二: 或者在提示找不到源码的时候,添加源码路径:
1、Ctrl+Shift+t 查找 2、Ctrl+t 查找实现实现(比如:查找继承该类的所有类) 快速显示当前类的继承结构 3、Ctrl+o 查看类中方法 4、f3 查看定义 5、Ctrl+Shift+r:打开资源 6、Ctrk+Shift +G 查找函数的所有调用 7、Ctrk+Shift+O:移除无用import包快捷键 调试部分: F8是调到下一个断点(没有的话,直接运行到结束)。 F5是单步调试进入函数内部。 F6是单步调试不进入函数内部。 F7是由函数内部返回到调用处。
错误信息如下: 解决方案: 将windows/temp属性-安全-高级 添加IIS_USERS用户,同时编辑权限为完全控制(写入和编辑)即可。 注意(未遇到过): 要确保权限添加上了,win7下有可能系统为安全,会自动取消你所做的权限设置。先管理员取得所有权,然后在添加。
今天在本地安装iis,搭建网站,应用程序的时候报错下面的错误: 服务器错误 Internet Information Services 7.5 错误摘要 HTTP 错误 500.19 - Internal Server Error 无法访问请求的页面,因为该页的相关配置数据无效。 详细错误信息 模块 IIS Web Core 通知 BeginRequest 处理程序 尚未确定 错误代码 0x80070032 配置错误 无法读取配置节“system.web.extensions”,因为它缺少节声明 配置文件 \\?\E:\12.service\AMACService\web.config 请求的 URL http://localhost:8088/AMACService/AMACService.svc 物理路径 E:\12.service\AMACService\AMACService.svc 登录方法 尚未确定 登录用户 尚未确定 配置源 25: </system.web> 26: <system.web.extensions> 27: <scripting> 链接和更多信息 当读取 Web 服务器或 Web 应用程序的配置文件出现问题时,就会发生此错误。在某些情况下,事件日志会包含有关导致此错误的原因的更多信息。 查看更多信息 » 运行环境先检查下:1.已经安装.net 4.0运行库2.设置网站基于.net4.0 3.修改“托管管道模式”,Set the Manage Pipeline mode from Integrated to Classic (由集成到经典) 然并软,问题还是没有解决。 最后在微软的论坛找到了解决的办法: This is because config section hasn't declared. in 4.0 webconfig you have to add it manually. <configSections> <sectionGroup name="system.web.extensions" type="System.Web.Configuration.SystemWebExtensionsSectionGroup, System.Web.Extensions, Version=4.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35"> <sectionGroup name="scripting" type="System.Web.Configuration.ScriptingSectionGroup, System.Web.Extensions, Version=4.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35"> <section name="scriptResourceHandler" type="System.Web.Configuration.ScriptingScriptResourceHandlerSection, System.Web.Extensions, Version=4.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35" requirePermission="false" allowDefinition="MachineToApplication"/> <sectionGroup name="webServices" type="System.Web.Configuration.ScriptingWebServicesSectionGroup, System.Web.Extensions, Version=4.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35"> <section name="jsonSerialization" type="System.Web.Configuration.ScriptingJsonSerializationSection, System.Web.Extensions, Version=4.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35" requirePermission="false" allowDefinition="Everywhere"/> <section name="profileService" type="System.Web.Configuration.ScriptingProfileServiceSection, System.Web.Extensions, Version=4.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35" requirePermission="false" allowDefinition="MachineToApplication"/> <section name="authenticationService" type="System.Web.Configuration.ScriptingAuthenticationServiceSection, System.Web.Extensions, Version=4.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35" requirePermission="false" allowDefinition="MachineToApplication"/> <section name="roleService" type="System.Web.Configuration.ScriptingRoleServiceSection, System.Web.Extensions, Version=4.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35" requirePermission="false" allowDefinition="MachineToApplication"/> </sectionGroup> </sectionGroup> </sectionGroup> </configSections> 如上添加后,在iis7的管理介面不再报错! 本文参考:http://blog.csdn.net/maxwoods/article/details/8723221
Tools(工具栏)--customsize toolbars(自定义工具栏) palette(调色板)勾选:
网上关于安装MongoDB的中文介绍坑太多,请参考下面的官方文档,原文地址 Overview Use this tutorial to install MongoDB Community Edition on Windows systems. PLATFORM SUPPORT Starting in version 2.2, MongoDB does not support Windows XP. Please use a more recent version of Windows to use more recent releases of MongoDB. IMPORTANT If you are running any edition of Windows Server 2008 R2 or Windows 7, please install a hotfix to resolve an issue with memory mapped files on Windows. Requirements MongoDB Community Edition requires Windows Server 2008 R2, Windows Vista, or later. The .msi installer includes all other software dependencies and will automatically upgrade any older version of MongoDB installed using an .msi file. Get MongoDB Community Edition 1 Determine which MongoDB build you need. There are three builds of MongoDB for Windows: MongoDB for Windows 64-bit runs only on Windows Server 2008 R2, Windows 7 64-bit, and newer versions of Windows. This build takes advantage of recent enhancements to the Windows Platform and cannot operate on older versions of Windows. MongoDB for Windows 32-bit runs on any 32-bit version of Windows newer than Windows Vista. 32-bit versions of MongoDB are only intended for older systems and for use in testing and development systems. 32-bit versions of MongoDB only support databases smaller than 2GB. NOTE Starting in MongoDB 3.2, 32-bit binaries are deprecated and will be unavailable in future releases. MongoDB for Windows 64-bit Legacy runs on Windows Vista, Windows Server 2003, and Windows Server 2008 and does not include recent performance enhancements. To find which version of Windows you are running, enter the following commands in the Command Prompt or Powershell: wmic os get caption wmic os get osarchitecture 2 Download MongoDB for Windows. Download the latest production release of MongoDB from the MongoDB downloads page. Ensure you download the correct version of MongoDB for your Windows system. The 64-bit versions of MongoDB do not work with 32-bit Windows. Install MongoDB Community Edition Interactive Installation 1 Install MongoDB for Windows. In Windows Explorer, locate the downloaded MongoDB .msi file, which typically is located in the default Downloads folder. Double-click the .msi file. A set of screens will appear to guide you through the installation process. You may specify an installation directory if you choose the “Custom” installation option. NOTE These instructions assume that you have installed MongoDB to C:\mongodb. MongoDB is self-contained and does not have any other system dependencies. You can run MongoDB from any folder you choose. You may install MongoDB in any folder (e.g. D:\test\mongodb). Unattended Installation You may install MongoDB Community unattended on Windows from the command line using msiexec.exe. 1 Open an Administrator command prompt. Press the Win key, type cmd.exe, and press Ctrl + Shift + Enter to run the Command Prompt as Administrator. Execute the remaining steps from the Administrator command prompt. 2 Install MongoDB for Windows. Change to the directory containing the .msi installation binary of your choice and invoke: msiexec.exe /q /i mongodb-win32-x86_64-2008plus-ssl-3.2.1-signed.msi ^ INSTALLLOCATION="C:\mongodb" ^ ADDLOCAL="all" You can specify the installation location for the executable by modifying the INSTALLLOCATION value. By default, this method installs all MongoDB binaries. To install specific MongoDB component sets, you can specify them in the ADDLOCAL argument using a comma-separated list including one or more of the following component sets: Component Set Binaries Server mongod.exe Router mongos.exe Client mongo.exe MonitoringTools mongostat.exe, mongotop.exe ImportExportTools mongodump.exe, mongorestore.exe, mongoexport.exe, mongoimport.exe MiscellaneousTools bsondump.exe, mongofiles.exe, mongooplog.exe, mongoperf.exe For instance, to install only the MongoDB utilities, invoke: msiexec.exe /q /i mongodb-win32-x86_64-2008plus-ssl-3.2.1-signed.msi ^ INSTALLLOCATION="C:\mongodb" ^ ADDLOCAL="MonitoringTools,ImportExportTools,MiscellaneousTools" Run MongoDB Community Edition WARNING Do not make mongod.exe visible on public networks without running in “Secure Mode” with the authsetting. MongoDB is designed to be run in trusted environments, and the database does not enable “Secure Mode” by default. 1 Set up the MongoDB environment. MongoDB requires a data directory to store all data. MongoDB’s default data directory path is\data\db. Create this folder using the following commands from a Command Prompt: md \data\db You can specify an alternate path for data files using the --dbpath option to mongod.exe, for example: C:\mongodb\bin\mongod.exe --dbpath d:\test\mongodb\data If your path includes spaces, enclose the entire path in double quotes, for example: C:\mongodb\bin\mongod.exe --dbpath "d:\test\mongo db data" You may also specify the dbpath in a configuration file. 2 Start MongoDB. To start MongoDB, run mongod.exe. For example, from the Command Prompt: C:\mongodb\bin\mongod.exe This starts the main MongoDB database process. The waiting for connections message in the console output indicates that the mongod.exe process is running successfully. Depending on the security level of your system, Windows may pop up a Security Alert dialog box about blocking “some features” of C:\mongodb\bin\mongod.exe from communicating on networks. All users should select Private Networks, such as my home or worknetwork and click Allow access. For additional information on security and MongoDB, please see the Security Documentation. 3 Connect to MongoDB. To connect to MongoDB through the mongo.exe shell, open another Command Prompt. C:\mongodb\bin\mongo.exe If you want to develop applications using .NET, see the documentation of C# and MongoDB for more information. 4 Begin using MongoDB. To help you start using MongoDB, MongoDB provides Getting Started Guides in various driver editions. See Getting Started for the available editions. Before deploying MongoDB in a production environment, consider the Production Notes document. Later, to stop MongoDB, press Control+C in the terminal where the mongod instance is running. Configure a Windows Service for MongoDB Community Edition 1 Open an Administrator command prompt. Press the Win key, type cmd.exe, and press Ctrl + Shift + Enter to run the Command Prompt as Administrator. Execute the remaining steps from the Administrator command prompt. 2 Create directories. Create directories for your database and log files: mkdir c:\data\db mkdir c:\data\log 3 Create a configuration file. Create a configuration file. The file must set systemLog.path. Include additional configuration options as appropriate. For example, create a file at C:\mongodb\mongod.cfg that specifies both systemLog.path andstorage.dbPath: systemLog: destination: file path: c:\data\log\mongod.log storage: dbPath: c:\data\db 4 Install the MongoDB service. IMPORTANT Run all of the following commands in Command Prompt with “Administrative Privileges”. Install the MongoDB service by starting mongod.exe with the --install option and the -configoption to specify the previously created configuration file. "C:\mongodb\bin\mongod.exe" --config "C:\mongodb\mongod.cfg" --install To use an alternate dbpath, specify the path in the configuration file (e.g.C:\mongodb\mongod.cfg) or on the command line with the --dbpath option. If needed, you can install services for multiple instances of mongod.exe or mongos.exe. Install each service with a unique --serviceName and --serviceDisplayName. Use multiple instances only when sufficient system resources exist and your system design requires it. 5 Start the MongoDB service. net start MongoDB 6 Stop or remove the MongoDB service as needed. To stop the MongoDB service use the following command: net stop MongoDB To remove the MongoDB service use the following command: "C:\mongodb\bin\mongod.exe" --remove Manually Create a Windows Service for MongoDB Community Edition You can set up the MongoDB server as a Windows Service that starts automatically at boot time. The following procedure assumes you have installed MongoDB Community using the .msi installer with the path C:\mongodb\. If you have installed in an alternative directory, you will need to adjust the paths as appropriate. 1 Open an Administrator command prompt. Press the Win key, type cmd.exe, and press Ctrl + Shift + Enter to run the Command Prompt as Administrator. Execute the remaining steps from the Administrator command prompt. 2 Create directories. Create directories for your database and log files: mkdir c:\data\db mkdir c:\data\log 3 Create a configuration file. Create a configuration file. The file must set systemLog.path. Include additional configuration options as appropriate. For example, create a file at C:\mongodb\mongod.cfg that specifies both systemLog.path andstorage.dbPath: systemLog: destination: file path: c:\data\log\mongod.log storage: dbPath: c:\data\db 4 Create the MongoDB service. Create the MongoDB service. sc.exe create MongoDB binPath= "C:\mongodb\bin\mongod.exe --service --config=\"C:\mongodb\mongod.cfg\"" DisplayName= "MongoDB" start= "auto" sc.exe requires a space between “=” and the configuration values (eg “binPath= ”), and a “\” to escape double quotes. If successfully created, the following log message will display: [SC] CreateService SUCCESS 5 Start the MongoDB service. net start MongoDB 6 Stop or remove the MongoDB service as needed. To stop the MongoDB service, use the following command: net stop MongoDB To remove the MongoDB service, first stop the service and then run the following command: sc.exe delete MongoDB Additional Resources¶ MongoDB for Developers Free Course MongoDB for .NET Developers Free Online Course MongoDB Architecture Guide Windows KB2731284补丁
环境: &nbsp&nbspVisual Studio 2010 一、Mindscape.NhibernateModelDesigner安装 &nbsp&nbsp在打开VS2010之后,我们可以在“工具”菜单下找到“扩展管理器,搜索:Mindscape NHibernate Model Designer 下载安装即可。安装完成后,在向项目中添加新项时如果我们拉到最下方我们会看到如下界面: 更加具体的操作可以参考:用好VS2010扩展管理器-NHibernate生成 二、根据数据库表结构生成实体并通过实体进行操作 1、添加nhmodel实体 2、打开nhmodel实体,根据数据库表生成实体 &nbsp&nbsp左侧工具栏部分切换到【服务器资源管理器】,连接上你想要获取数据结构的数据库,就会看到展示出来的数据库内容: &nbsp&nbsp拖动你想要的表到设计器主界面,如下图: 即可获取到数据库表对应的实体。 3、生成配置文件 小注: &nbsp&nbsp如果不生成配置文件直接运行第4步中代码,会报出下面的错误: 未处理 NHibernate.Cfg.HibernateConfigException HResult=-2146232832 Message=An exception occurred during configuration of persistence layer. Source=NHibernate StackTrace: 在 NHibernate.Cfg.ConfigurationSchema.HibernateConfiguration..ctor(XmlReader hbConfigurationReader, Boolean fromAppSetting) 在 NHibernate.Cfg.ConfigurationSchema.HibernateConfiguration..ctor(XmlReader hbConfigurationReader) 在 NHibernate.Cfg.Configuration.Configure(XmlReader textReader) 在 NHibernate.Cfg.Configuration.Configure(String fileName, Boolean ignoreSessionFactoryConfig) 在 NHibernate.Cfg.Configuration.Configure(String fileName) 在 NHibernate.Cfg.Configuration.Configure() 在 DataBaseToEntity.ConfigurationHelper.CreateConfiguration() 位置 C:\Users\JianKunKing\Desktop\NHibernateStudy\NHibernateStudy\DataBaseToEntity\NHibernateModel1.cs:行号 20 在 DataBaseToEntity.DataBaseToEntityForm1..ctor() 位置 C:\Users\JianKunKing\Desktop\NHibernateStudy\NHibernateStudy\DataBaseToEntity\DataBaseToEntityForm1.cs:行号 20 在 DataBaseToEntity.Program.Main() 位置 C:\Users\JianKunKing\Desktop\NHibernateStudy\NHibernateStudy\DataBaseToEntity\Program.cs:行号 18 在 System.AppDomain._nExecuteAssembly(RuntimeAssembly assembly, String[] args) 在 System.AppDomain.ExecuteAssembly(String assemblyFile, Evidence assemblySecurity, String[] args) 在 Microsoft.VisualStudio.HostingProcess.HostProc.RunUsersAssembly() 在 System.Threading.ThreadHelper.ThreadStart_Context(Object state) 在 System.Threading.ExecutionContext.RunInternal(ExecutionContext executionContext, ContextCallback callback, Object state, Boolean preserveSyncCtx) 在 System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state, Boolean preserveSyncCtx) 在 System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state) 在 System.Threading.ThreadHelper.ThreadStart() InnerException: System.IO.FileNotFoundException HResult=-2147024894 Message=未能找到文件“C:\Users\JianKunKing\Desktop\NHibernateStudy\NHibernateStudy\DataBaseToEntity\bin\Debug\hibernate.cfg.xml”。 Source=mscorlib FileName=C:\Users\JianKunKing\Desktop\NHibernateStudy\NHibernateStudy\DataBaseToEntity\bin\Debug\hibernate.cfg.xml StackTrace: 在 System.IO.__Error.WinIOError(Int32 errorCode, String maybeFullPath) 在 System.IO.FileStream.Init(String path, FileMode mode, FileAccess access, Int32 rights, Boolean useRights, FileShare share, Int32 bufferSize, FileOptions options, SECURITY_ATTRIBUTES secAttrs, String msgPath, Boolean bFromProxy, Boolean useLongPath, Boolean checkHost) 在 System.IO.FileStream..ctor(String path, FileMode mode, FileAccess access, FileShare share, Int32 bufferSize) 在 System.Xml.XmlDownloadManager.GetStream(Uri uri, ICredentials credentials, IWebProxy proxy, RequestCachePolicy cachePolicy) 在 System.Xml.XmlUrlResolver.GetEntity(Uri absoluteUri, String role, Type ofObjectToReturn) 在 System.Xml.XmlTextReaderImpl.OpenUrlDelegate(Object xmlResolver) 在 System.Threading.CompressedStack.runTryCode(Object userData) 在 System.Runtime.CompilerServices.RuntimeHelpers.ExecuteCodeWithGuaranteedCleanup(TryCode code, CleanupCode backoutCode, Object userData) 在 System.Threading.CompressedStack.Run(CompressedStack compressedStack, ContextCallback callback, Object state) 在 System.Xml.XmlTextReaderImpl.OpenUrl() 在 System.Xml.XmlTextReaderImpl.Read() 在 System.Xml.XmlTextReader.Read() 在 System.Xml.XmlCharCheckingReader.Read() 在 System.Xml.XsdValidatingReader.Read() 在 System.Xml.XPath.XPathDocument.LoadFromReader(XmlReader reader, XmlSpace space) 在 System.Xml.XPath.XPathDocument..ctor(XmlReader reader, XmlSpace space) 在 System.Xml.XPath.XPathDocument..ctor(XmlReader reader) 在 NHibernate.Cfg.ConfigurationSchema.HibernateConfiguration..ctor(XmlReader hbConfigurationReader, Boolean fromAppSetting) InnerException: 4、通过实体来操作数据库: //获取映射关系及配置 ISessionFactory sessionFactory = ConfigurationHelper.CreateConfiguration().Configure().BuildSessionFactory(); //此处新增 TbNHibernate entity = new TbNHibernate(); entity.UserName = "UserName1"; entity.UserPwd = "UserPwd1"; using (ISession session = sessionFactory.OpenSession()) { try { var a = session.Save(entity); session.Flush(); } catch (Exception ee) { MessageBox.Show(ee.ToString()); } } //部分字段更新 using (ISession session = sessionFactory.OpenSession()) { ITransaction trans = session.BeginTransaction(); try { string sql = " update tb_NHibernate set userPWD=" + value + " where id='" + id + "'"; ISQLQuery Query = session.CreateSQLQuery(sql).AddEntity(typeof(TbNHibernate)); Query.ExecuteUpdate(); session.Flush(); trans.Commit(); } catch (Exception ex) { MessageBox.Show(ex.ToString()); IsSuccess = false; trans.Rollback(); } finally { if (session != null) { session.Clear(); } } } 三、根据实体生成数据库表结构并通过实体进行操作 两者之间的操作与之前一样 小注: &nbsp&nbsp 1、如果在选择主键生成方式的时候选择了HiLo选项 ,那么生成表的主键字段是uniqueidentifier类型的: CREATE TABLE [dbo].[DataBaseToEntity1]( [Id] [uniqueidentifier] NOT NULL, [Name] [nvarchar](max) NOT NULL, [Code] [nvarchar](max) NOT NULL, PRIMARY KEY CLUSTERED ( [Id] ASC )WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY] ) ON [PRIMARY] GO &nbsp&nbsp如果在主键类型你选择的是Guid &nbsp&nbsp那么此时,你实体类中的主键字段是Guid类型的,如果你通过Guid.NewGuid()给你主键字段赋值会报出如下错误: --------------------------- --------------------------- NHibernate.HibernateException: error performing isolated work ---> System.FormatException: GUID 应包含带 4 个短划线的 32 位数(xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx)。 在 System.Guid.TryParseGuidWithNoStyle(String guidString, GuidResult& result) 在 System.Guid.TryParseGuid(String g, GuidStyles flags, GuidResult& result) 在 System.Guid..ctor(String g) 在 NHibernate.Type.GuidType.Get(IDataReader rs, Int32 index) 在 NHibernate.Id.TableGenerator.DoWorkInCurrentTransaction(ISessionImplementor session, IDbConnection conn, IDbTransaction transaction 在 NHibernate.Engine.TransactionHelper.Work.DoWork(IDbConnection connection, IDbTransaction transaction) 在 NHibernate.Transaction.AdoNetTransactionFactory.ExecuteWorkInIsolation(ISessionImplementor session, IIsolatedWork work, Boolean transacted) --- 内部异常堆栈跟踪的结尾 --- 在 NHibernate.Transaction.AdoNetTransactionFactory.ExecuteWorkInIsolation(ISessionImplementor session, IIsolatedWork work, Boolean transacted) 在 NHibernate.Transaction.AdoNetWithDistributedTransactionFactory.ExecuteWorkInIsolation(ISessionImplementor session, IIsolatedWork work, Boolean transacted) 在 NHibernate.Engine.Transaction.Isolater.DoIsolatedWork(IIsolatedWork work, ISessionImplementor session) 在 NHibernate.Engine.TransactionHelper.DoWorkInNewTransaction(ISessionImplementor session) 在 NHibernate.Id.TableGenerator.Generate(ISessionImplementor session, Object obj) 在 NHibernate.Id.TableHiLoGenerator.Generate(ISessionImplementor session, Object obj) 在 NHibernate.Event.Default.AbstractSaveEventListener.SaveWithGeneratedId(Object entity, String entityName, Object anything, IEventSource source, Boolean requiresImmediateIdAccess) 在 NHibernate.Event.Default.DefaultSaveOrUpdateEventListener.SaveWithGeneratedOrRequestedId(SaveOrUpdateEvent event) 在 NHibernate.Event.Default.DefaultSaveEventListener.SaveWithGeneratedOrRequestedId(SaveOrUpdateEvent event) 在 NHibernate.Event.Default.DefaultSaveOrUpdateEventListener.EntityIsTransient(SaveOrUpdateEvent event) 在 NHibernate.Event.Default.DefaultSaveEventListener.PerformSaveOrUpdate(SaveOrUpdateEvent event) 在 NHibernate.Event.Default.DefaultSaveOrUpdateEventListener.OnSaveOrUpdate(SaveOrUpdateEvent event) 在 NHibernate.Impl.SessionImpl.FireSave(SaveOrUpdateEvent event) 在 NHibernate.Impl.SessionImpl.Save(Object obj) 在 DataBaseToEntity.DataBaseToEntityForm1.button1_Click(Object sender, EventArgs e) 位置 C:\Users\JianKunKing\Desktop\NHibernateStudy\NHibernateStudy\DataBaseToEntity\DataBaseToEntityForm1.cs:行号 31 --------------------------- 确定 --------------------------- &nbsp&nbsp那么这种情况应该处理呢?毕竟大多数的主键都是Guid类型的啊,此时需要修改你模型主键的生成规则: 在这里修改为guid类型的就可以了 2、选择主键的类型选择int类型的时候: &nbsp&nbsp此时通过实体操作数据是不需要填充主键字段的,你填充了也更新不进去。 &nbsp&nbsp本文中有什么不对的地方欢迎支出,谢谢 四、[NHibernate操作文档及demo] (http://download.csdn.net/detail/xunzaosiyecao/9398186) 作者:jiankunking 出处:http://blog.csdn.net/jiankunking
原文地址 开篇 从’严格’意义上说,javascript并不是一门真正的面向对象语言。这种说法原因一般都是觉得javascript作为一门弱类型语言与类似java或c#之类的强型语言的继承方式有很大的区别,因而默认它就是非主流的面向对象方式,甚至竟有很多书将其描述为’非完全面向对象’语言。其实个人觉得,什么方式并不重要,重要的是是否具有面向对象的思想,说javascript不是面向对象语言的,往往都可能没有深入研究过javascript的继承方式,故特撰此文以供交流。 为何需要利用javascript实现继承? 早期pc机器的性能确实不敢恭维,所有的压力全在服务器端,客户端浏览器纯属摆设。再加上那时流行的table布局以及电话线的上网方式导致浏览一个网页十分的卡;而今互联网时代飞速发展,个人电脑硬件得到了极大提升,客户端浏览器的性能也十分的酸爽,web开发的模式也在悄悄改变:服务端不再像以前那样“辛苦”,取而代之的是尽可能的让浏览器承担更多的任务,如此一来,压力分摊到每个客户端上,企业不但节省成本,随之也让web前端开发变的更加有趣--越来越多的前端框架层出不穷,甚至出现了许多前端的MVC框架。在这种背景下,javascript的角色已经绝对不是只做一些简单的验证,发送一些请求或者操作一些DOM,更多的需要担任类似前端路由和业务层的角色,并且javascript需要做大量的逻辑性任务,这里面就包括前台数据的抽离(即model),而只有运用面向对象的思维才能很好的对抽离数据进行处理,因此继承就在这里显得举足轻重。 从一个简单的需求开始 现从前台抽离一个model名为Person,其有基本属性name和age,默认每个人都会说话,因此将说话的功能say放在了原型对象上,以供每个实例享用。现在对于Man来说,它需要继承Person的基本属性,并且在此基础上添加自己特有的属性。 function Person(name, age) { this.name = name; this.age = age; } Person.prototype.say = function () { alert('hello, my name is ' + this.name); }; function Man() { } Man.prototype = new Person('pursue'); var man1 = new Man(); man1.say(); //hello, my name is pursue var man2 = new Man(); alert(man1.say === man2.say);//true alert(man1.name === man2.name);//true 下面介绍几种主流的继承方式。 1.原型链继承 function Person(name, age) { this.name = name; this.age = age; } Person.prototype.say = function () { alert('hello, my name is ' + this.name); }; function Man() { } Man.prototype = new Person('pursue'); var man1 = new Man(); man1.say(); //hello, my name is pursue var man2 = new Man(); alert(man1.say === man2.say);//true alert(man1.name === man2.name);//true 这种继承方式很直接,为了获取Person的所有属性方法(实例上的和原型上的),直接将父类的实例new Person(‘pursue’)赋给了子类的原型,其实子类的实例man1,man2本身是一个完全空的对象,所有的属性和方法都得去原型链上去找,因而找到的属性方法都是同一个。 所以直接利用原型链继承是不现实的。 2.利用构造函数继承 function Person (name, age) { this.name = name; this.age = age; } Person.prototype.say = function(){ alert('hello, my name is ' + this.name); }; function Man(name, age) { Person.apply(this, arguments); } //Man.prototype = new Person('pursue'); var man1 = new Man('joe'); var man2 = new Man('david'); alert(man1.name === man2.name);//false man1.say(); //say is not a function 这里子类的在构造函数里利用了apply去调用父类的构造函数,从而达到继承父类属性的效果,比直接利用原型链要好的多,至少每个实例都有自己那一份资源,但是这种办法只能继承父类的实例属性,因而找不到say方法,为了继承父类所有的属性和方法,则就要修改原型链,从而引入了组合继承方式。 3.组合继承 function Person(name, age) { this.name = name; this.age = age; } Person.prototype.say = function () { alert('hello, my name is ' + this.name); }; function Man(name, age) { Person.apply(this, arguments); } Man.prototype = new Person(); var man1 = new Man('joe'); var man2 = new Man('david'); alert(man1.name === man2.name);//false alert(man1.say === man2.say);//true man1.say(); //hello, my name is joe 需要注意的是man1和man2的实例属性其实是覆盖了原型属性,但是并没要覆盖掉原型上的say方法(因为它们没有),所以这里man1.say === man2.say依然返回true,因而需要十分小心没有覆盖掉的原型属性,因为它是所有实例共有的。 4.寄生组合继承 说实话我真不知道下面的这种形式叫这名字,但是它确实是最流行,最经典的javascript的继承方式。其实,只需要明白原型对象的结构即可: function Person (name, age) { this.name = name; this.age = age; } Person.prototype.say = function(){ alert('hello, my name is ' + this.name); }; function Man(name, age) { Person.apply(this, arguments); } Man.prototype = Object.create(Person.prototype);//a. Man.prototype.constructor = Man;//b. var man1 = new Man('pursue'); var man2 = new Man('joe'); alert(man1.say == man2.say); alert(man1.name == man2.name); 其实寄生组合继承和上面的组合继承区别仅在于构造子类原型对象的方式上(a.和b.),这里用到了Object.creat(obj)方法,该方法会对传入的obj对象进行浅拷贝,类似于: function create(obj){ function T(){}; T.prototype = obj; return new T(); } 因此,a.会将子类的原型对象与父类的原型对象进行很好的连接,而并不像一般的组合继承那样直接对子类的原型进行复制(如Man.prototype = new Person();),这样只是很暴力的在对属性进行覆盖。而寄生组合继承方式则对实例属性和原型属性分别进行了继承,在实现上更加合理。 注意:代码b.并不会改变instanceof的结果,但是对于需要用到construcor的场景,这么做更加严谨。
.NET中的枚举我们一般有两种用法,一是表示唯一的元素序列,例如一周里的各天;还有就是用来表示多种复合的状态。这个时候一般需要为枚举加上[Flags]特性标记为位域,例如: [Flags] enum Styles{ ShowBorder = 1, //是否显示边框 ShowCaption = 2, //是否显示标题 ShowToolbox = 4 //是否显示工具箱 } 这样我们就可以用”或”运算符组合多个状态,例如: myControl.Style = Styles.ShowBorder | Styles.ShowCaption; 这时myControl.Style枚举的值将变成 1+2=3,它的ToString()将变成"Styles.ShowBorder , Styles.ShowCaption" 这里我们可以解释为什么第三个值ShowToolbox可以为4,5..而不能为3。也就是说它的值不应该是前几项值的复合值。有一个比较简单的方法就是用2的n次方来依次为每一项赋值,例如 1,2,4,8,16,32,64….. 现在举个常见的Flags应用例子。例如一个简单的权限系统,有”Admin”和”User”两种角色,我们可以在表中放一个 varchar()字段,以文本形式存放权限字”Admin,User”。但是用Flags型枚举的话,我们就可以直接将 Roles.Admin | Roles.User 的值放在一个int字段里。 以下是关于枚举的一些常见操作: 将枚举的值变回枚举对象: Styles style = (Styles) Enum.Parse(typeof(Styles), 4 ); // -> style = Styles.Toolbox; 检查枚举是否包含某个元素: bool hasFlag = ((style & Styles.ShowBorder) != 0); 其实我们还会碰到一种情况,就是需要从组合状态中去掉一个元素。用”^”运算符可以做到: Styles style = Styles.ShowBorder | Styles.ShowCaption; style = style ^ Styles.ShowBorder; 这个时候style的值就会变成 Styles.ShowCaption 但这里有一个很严重的问题(偶现在才发现) 我们这个时候再执行一次 style = style ^ Styles.ShowBorder; 按照我们的设想,这个时候 style 的值是 Styles.ShowCaption,不包含 Styles.ShowBorder,所以我们就算去掉这个元素,style应该还是不会变。但实际的 style 的值却又变成了 Styles.ShowBorder | Styles.ShowCaption !! 再执行一遍,又会去掉这个元素,周而复始。 当然我们可以在去掉某个元素前做一番检查,如果枚举包含这个元素,再去掉它: if ((style & Styles.ShowBorder) != 0){ style = style ^ Styles.ShowBorder; } 下面这种方式可以方便的去掉一个元素: style = style & (~Styles.ShowBorder) 原文地址
在不同浏览器或者同一浏览器的不同版本中,CSS 都会对小数进行取整(直接去除小数还是会根据小数的大小进行判断就不一定了) 测试代码: <!DOCTYPE html> <html> <head> <meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> <title></title> <meta charset="utf-8" /> <style type="text/css"> .class1 { width: 10.1px; height: 10.8px; border: 0px; background-color: Black; } </style> <script type="text/javascript"> window.onload = function () { var div1 = document.getElementById("div1"); var clientWidth = div1.clientWidth; var offsetWidth = div1.offsetWidth; var scrollWidth = div1.scrollWidth; var ul = document.createElement('ul'); var oLi1 = document.createElement('li'); oLi1.innerHTML = "div.clientWidth=" + clientWidth; ul.appendChild(oLi1); var oLi2 = document.createElement('li'); oLi2.innerHTML = "div.offsetWidth=" + offsetWidth; ul.appendChild(oLi2); var oLi3 = document.createElement('li'); oLi3.innerHTML = "div.scrollWidth=" + scrollWidth; ul.appendChild(oLi3); var clientHeight = div1.clientHeight; var offsetHeight = div1.offsetHeight; var scrollHeight = div1.scrollHeight; var oLi4 = document.createElement('li'); oLi4.innerHTML = "div.clientHeight=" + clientHeight; ul.appendChild(oLi4); var oLi5 = document.createElement('li'); oLi5.innerHTML = "div.offsetHeight=" + offsetHeight; ul.appendChild(oLi5); var oLi6 = document.createElement('li'); oLi6.innerHTML = "div.scrollHeight=" + scrollHeight; ul.appendChild(oLi6); div1.appendChild(ul); } </script> </head> <body> <div id="div1" class="class1"> </div> </body> </html> 测试结果: 作者:jiankunking 出处:http://blog.csdn.net/jiankunking
1、javascript 面向对象 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <html xmlns="http://www.w3.org/1999/xhtml"> <head> <meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> <title>无标题文档</title> <script type="text/javascript"> //oDiv.onclick = function () { // alert(this); //}; window.onload = function () { var arr = [12, 65, 87]; //this:当前的方法,属于谁 //arr.show = function () { // alert(this); //}; arr.sssss = function () { alert('123'); }; arr.sssss(); } </script> </head> <body> </body> </html> 我擦,代码竟然可以这么写 2、构造函数与原型 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <html xmlns="http://www.w3.org/1999/xhtml"> <head> <meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> <title>无标题文档</title> <script type="text/javascript"> //构造函数 function Person(name, sex) { this.name = name; this.sex = sex; } //原型 Person.prototype.showName = function () { alert(this.name); }; Person.prototype.showSex = function () { alert(this.sex); }; var p = new Person('blue', '男'); p.showName(); p.showSex(); </script> </head> <body> </body> </html> 小注: 每个对象有差异的东东,可以放到构造函数中,通用的可以使用原型 3、原型的优先级 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <html xmlns="http://www.w3.org/1999/xhtml"> <head> <meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> <title>无标题文档</title> <script type="text/javascript"> Array.prototype.a = 12; var arr = [1, 2, 3]; alert(arr.a); //12 arr.a = 5; alert(arr.a); //5 delete arr.a; alert(arr.a); //12 </script> </head> <body> </body> </html> 4、[Javascript中this关键字详解] (http://blog.csdn.net/jiankunking/article/details/50413767) 5、事件绑定 IE方式 attachEvent(事件名称, 函数),绑定事件处理函数 detachEvent(事件名称, 函数),解除绑定 DOM方式 addEventListener(事件名称,函数, 捕获) removeEventListener(事件名称, 函数, 捕获) //1.谁 //2.事件 //3.函数 function AddEvent(obj, sEvent, fn) { //IE if(obj.attachEvent) { obj.attachEvent('on'+sEvent, fn); } else { obj.addEventListener(sEvent, fn, false); } } 6、绑定和this <!DOCTYPE html> <html> <head> <meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> <title></title> <meta charset="utf-8" /> <script type="text/javascript"> window.onload = function () { var oBtn = document.getElementById('btn1'); /*oBtn.onclick=function () { alert(this); };*/ //IE 事件绑定 this->window /*oBtn.attachEvent('onclick', function (){ alert(this==window); });*/ //FF oBtn.addEventListener('click', function () { alert(this); }, false); }; </script> </head> <body> <input id="btn1" type="button" value="aaa" /> </body> </html> 7、匿名函数 匿名函数绑定事件无法解除绑定 与C #一样嘛 作者:jiankunking 出处:http://blog.csdn.net/jiankunking
原文地址 Quiz 请看下面的代码,最后alert出来的是什么呢? var name = "Bob"; var nameObj ={ name : "Tom", showName : function(){ alert(this.name); }, waitShowName : function(){ setTimeout(this.showName, 1000); } }; nameObj.waitShowName(); 要解决这个问题我们需要了解Javascript的this关键字的用法。 this指向哪里? 一般而言,在Javascript中,this指向函数执行时的当前对象。 In JavaScript, as in most object-oriented programming languages, this is a special keyword that is used within methods to refer to the object on which a method is being invoked. ——jQuery Fundamentals (Chapter 2), by Rebecca Murphey 值得注意,该关键字在Javascript中和执行环境,而非声明环境有关。 The this keyword is relative to the execution context, not the declaration context. 我们举个例子来说明这个问题: var someone = { name: "Bob", showName: function(){ alert(this.name); } }; var other = { name: "Tom", showName: someone.showName } other.showName(); //Tom this关键字虽然是在someone.showName中声明的,但运行的时候是other.showName,所以this指向other.showName函数的当前对象,即other,故最后alert出来的是other.name。 没有明确的当前对象时 当没有明确的执行时的当前对象时,this指向全局对象window。 By default, this refers to the global object. 为什么说是全局对象(the global object),因为非浏览器情况下(例如:nodejs)中全局变量并非window对象,而就是叫“全局变量”(the global object)。不过由于我们这片文章主要讨论的是前端开发知识,所以nodejs就被我们忽略了。 例如对于全局变量引用的函数上我们有: var name = "Tom"; var Bob = { name: "Bob", show: function(){ alert(this.name); } } var show = Bob.show; show(); //Tom 你可能也能理解成show是window对象下的方法,所以执行时的当前对象时window。但局部变量引用的函数上,却无法这么解释: var name = "window"; var Bob = { name: "Bob", showName: function(){ alert(this.name); } }; var Tom = { name: "Tom", showName: function(){ var fun = Bob.showName; fun(); } }; Tom.showName(); //window setTimeout、setInterval和匿名函数 文章开头的问题的答案是Bob。 在浏览器中setTimeout、setInterval和匿名函数执行时的当前对象是全局对象window,这条我们可以看成是上一条的一个特殊情况。 所以在运行this.showName的时候,this指向了window,所以最后显示了window.name。 浏览器中全局变量可以当成是window对象下的变量,例如全局变量a,可以用window.a来引用。 我们将代码改成匿名函数可能更好理解一些: var name = "Bob"; var nameObj ={ name : "Tom", showName : function(){ alert(this.name); }, waitShowName : function(){ !function(__callback){ __callback(); }(this.showName); } }; nameObj.waitShowName(); //Bob **在调用nameObj.waitShowName时候,我们运行了一个匿名函数,将nameObj.showName作为回调函数传进这个匿名函数,然后匿名函数运行时,运行这个回调函数。由于匿名函数的当前对象是window,所以当在该匿名函数中运行回调函数时,回调函数的this指向了window,所以alert出来window.name。 由此看来setTimeout可以看做是一个延迟执行的: function(__callback){ __callback(); } setInterval也如此类比。 但如果我们的确想得到的回答是Tom呢?通过一些技巧,我们能够得到想要的答案: var name = "Bob"; var nameObj ={ name : "Tom", showName : function(){ alert(this.name); }, waitShowName : function(){ var that = this; setTimeout(function(){ that.showName(); }, 1000); } }; nameObj.waitShowName(); //Tom 在执行nameObj.waitShowName函数时,我们先对其this赋给变量that(这是为了避免setTimeout中的匿名函数运行时,匿名函数中的this指向window),然后延迟运行匿名函数,执行that.showName,即nameObj.showName,所以alert出正确结果Tom。 eval 对于eval函数,其执行时候似乎没有指定当前对象,但实际上其this并非指向window,因为该函数执行时的作用域是当前作用域,即等同于在该行将里面的代码填进去。下面的例子说明了这个问题: var name = "window"; var Bob = { name: "Bob", showName: function(){ eval("alert(this.name)"); } }; Bob.showName(); //Bob apply和call apply和call能够强制改变函数执行时的当前对象,让this指向其他对象。因为apply和call较为类似,所以我们以apply为例: var name = "window"; var someone = { name: "Bob", showName: function(){ alert(this.name); } }; var other = { name: "Tom" }; someone.showName.apply(); //window someone.showName.apply(other); //Tom apply用于改变函数执行时的当前对象,当无参数时,当前对象为window,有参数时当前对象为该参数。于是这个例子Bob成功偷走了Tom的名字。 new关键字 new关键字后的构造函数中的this指向用该构造函数构造出来的新对象: function Person(__name){ this.name = __name; //这个this指向用该构造函数构造的新对象,这个例子是Bob对象 } Person.prototype.show = function(){ alert(this.name); } var Bob = new Person("Bob"); Bob.show(); //Bob
原文地址 使用ASP.NET/PHP/JSP 或者javascript都会用到http的不同状态,一些常见的状态码为: 200 – 服务器成功返回网页 404 – 请求的网页不存在 503 – 服务不可用 1xx(临时响应) 表示临时响应并需要请求者继续执行操作的状态代码。 代码 说明 100 (继续) 请求者应当继续提出请求。 服务器返回此代码表示已收到请求的第一部分,正在等待其余部分。 101 (切换协议) 请求者已要求服务器切换协议,服务器已确认并准备切换。 2xx (成功) 表示成功处理了请求的状态代码。 代码 说明 200 (成功) 服务器已成功处理了请求。 通常,这表示服务器提供了请求的网页。 201 (已创建) 请求成功并且服务器创建了新的资源。 202 (已接受) 服务器已接受请求,但尚未处理。 203 (非授权信息) 服务器已成功处理了请求,但返回的信息可能来自另一来源。 204 (无内容) 服务器成功处理了请求,但没有返回任何内容。 205 (重置内容) 服务器成功处理了请求,但没有返回任何内容。 206 (部分内容) 服务器成功处理了部分 GET 请求。 3xx (重定向) 表示要完成请求,需要进一步操作。 通常,这些状态代码用来重定向。 代码 说明 300 (多种选择) 针对请求,服务器可执行多种操作。 服务器可根据请求者 (user agent) 选择一项操作,或提供操作列表供请求者选择。 301 (永久移动) 请求的网页已永久移动到新位置。 服务器返回此响应(对 GET 或 HEAD 请求的响应)时,会自动将请求者转到新位置。 302 (临时移动) 服务器目前从不同位置的网页响应请求,但请求者应继续使用原有位置来进行以后的请求。 303 (查看其他位置) 请求者应当对不同的位置使用单独的 GET 请求来检索响应时,服务器返回此代码。 304 (未修改) 自从上次请求后,请求的网页未修改过。 服务器返回此响应时,不会返回网页内容。 305 (使用代理) 请求者只能使用代理访问请求的网页。 如果服务器返回此响应,还表示请求者应使用代理。 307 (临时重定向) 服务器目前从不同位置的网页响应请求,但请求者应继续使用原有位置来进行以后的请求。 4xx(请求错误) 这些状态代码表示请求可能出错,妨碍了服务器的处理。 代码 说明 400 (错误请求) 服务器不理解请求的语法。 401 (未授权) 请求要求身份验证。 对于需要登录的网页,服务器可能返回此响应。 403 (禁止) 服务器拒绝请求。 404 (未找到) 服务器找不到请求的网页。 405 (方法禁用) 禁用请求中指定的方法。 406 (不接受) 无法使用请求的内容特性响应请求的网页。 407 (需要代理授权) 此状态代码与 401(未授权)类似,但指定请求者应当授权使用代理。 408 (请求超时) 服务器等候请求时发生超时。 409 (冲突) 服务器在完成请求时发生冲突。 服务器必须在响应中包含有关冲突的信息。 410 (已删除) 如果请求的资源已永久删除,服务器就会返回此响应。 411 (需要有效长度) 服务器不接受不含有效内容长度标头字段的请求。 412 (未满足前提条件) 服务器未满足请求者在请求中设置的其中一个前提条件。 413 (请求实体过大) 服务器无法处理请求,因为请求实体过大,超出服务器的处理能力。 414 (请求的 URI 过长) 请求的 URI(通常为网址)过长,服务器无法处理。 415 (不支持的媒体类型) 请求的格式不受请求页面的支持。 416 (请求范围不符合要求) 如果页面无法提供请求的范围,则服务器会返回此状态代码。 417 (未满足期望值) 服务器未满足”期望”请求标头字段的要求。 5xx(服务器错误) 这些状态代码表示服务器在尝试处理请求时发生内部错误。 这些错误可能是服务器本身的错误,而不是请求出错。 代码 说明 500 (服务器内部错误) 服务器遇到错误,无法完成请求。 501 (尚未实施) 服务器不具备完成请求的功能。 例如,服务器无法识别请求方法时可能会返回此代码。 502 (错误网关) 服务器作为网关或代理,从上游服务器收到无效响应。 503 (服务不可用) 服务器目前无法使用(由于超载或停机维护)。 通常,这只是暂时状态。 504 (网关超时) 服务器作为网关或代理,但是没有及时从上游服务器收到请求。 505 (HTTP 版本不受支持) 服务器不支持请求中所用的 HTTP 协议版本。 RFC 6585 最近刚刚发布,该文档描述了 4 个新的 HTTP 状态码。 HTTP 协议还在变化?是的,HTTP 协议一直在演变,新的状态码对于开发 REST 服务或者说是基于 HTTP 的服务非常有用,下面我们为你详细介绍这四个新的状态码以及是否应该使用。 428 Precondition Required (要求先决条件) 先决条件是客户端发送 HTTP 请求时,如果想要请求能成功必须满足一些预设的条件。 一个好的例子就是 If-None-Match 头,经常在 GET 请求中使用,如果指定了 If-None-Match ,那么客户端只在响应中的 ETag 改变后才会重新接收回应。 先决条件的另外一个例子就是 If-Match 头,这个一般用在 PUT 请求上用于指示只更新没被改变的资源,这在多个客户端使用 HTTP 服务时用来防止彼此间不会覆盖相同内容。 当服务器端使用 428 Precondition Required 状态码时,表示客户端必须发送上述的请求头才能执行请求,这个方法为服务器提供一种有效的方法来阻止 ‘lost update’ 问题。 429 Too Many Requests (太多请求) 当你需要限制客户端请求某个服务数量时,该状态码就很有用,也就是请求速度限制。 在此之前,有一些类似的状态码,例如 ‘509 Bandwidth Limit Exceeded’. Twitter 使用 420 (这不是HTTP定义的状态码) 如果你希望限制客户端对服务的请求数,可使用 429 状态码,同时包含一个 Retry-After 响应头用于告诉客户端多长时间后可以再次请求服务。 431 Request Header Fields Too Large (请求头字段太大) 某些情况下,客户端发送 HTTP 请求头会变得很大,那么服务器可发送 431 Request Header Fields Too Large 来指明该问题。 我不太清楚为什么没有 430 状态码,而是直接从 429 跳到 431,我尝试搜索但没有结果。唯一的猜测是 430 Forbidden 跟 403 Forbidden 太像了,为了避免混淆才这么做的,天知道! 511 Network Authentication Required (要求网络认证) 对我来说这个状态码很有趣,如果你在开发一个 HTTP 服务器,你不一定需要处理该状态码,但如果你在编写 HTTP 客户端,那这个状态码就非常重要。 如果你频繁使用笔记本和智能手机,你可能会注意到大量的公用 WIFI 服务要求你必须接受一些协议或者必须登录后才能使用。 这是通过拦截HTTP流量,当用户试图访问网络返回一个重定向和登录,这很讨厌,但是实际情况就是这样的。 使用这些“拦截”客户端,会有一些讨厌的副作用。在 RFC 中有提到这两个的例子: 如果你在登录WIFI前访问某个网站,网络设备将会拦截首个请求,这些设备往往也有自己的网站图标 ‘favicon.ico’。登录后您会发现,有一段时间内你访问的网站图标一直是WIFI登录网站的图标。 如果客户端使用HTTP请求来查找文档(可能是JSON),网络将会响应一个登录页,这样你的客户端就会解析错误并导致客户端运行异常,在现实中这种问题非常常见。 因此 511 状态码的提出就是为了解决这个问题。 如果你正在编写 HTTP 的客户端,你最好还是检查 511 状态码以确认是否需要认证后才能访问。 英文原文
C# 单例模式的五种写法及优劣分析,见下文: [单例模式及常见写法](http://blog.csdn.net/jiankunking/article/details/50867050)
原文地址:点击打开链接 #region 清除事件绑定的函数 /// <summary> /// 清除事件绑定的函数 /// </summary> /// <param name="objectHasEvents">拥有事件的实例</param> /// <param name="eventName">事件名称</param> public static void ClearAllEvents(object objectHasEvents, string eventName) { if (objectHasEvents == null) { return; } try { EventInfo[] events = objectHasEvents.GetType().GetEvents(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance); if (events == null || events.Length < 1) { return; } for (int i = 0; i < events.Length; i++) { EventInfo ei = events[i]; if (ei.Name == eventName) { FieldInfo fi = ei.DeclaringType.GetField(eventName, BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance); if (fi != null) { fi.SetValue(objectHasEvents, null); } break; } } } catch { } } #endregion