
暂无个人介绍
function fun() { return 1; } var a = fun; var b = fun(); JavaScript中我们把下面的代码叫做函数: function fun() { return 1; } 函数是一种叫做function引用类型的实例,因此函数是一个对象。对象是保存在内存中的,函数名则是指向这个对象的指针。 var a = fun 表示把函数名fun这个指针拷贝一份给变量a,但是这不是指函数本身被复制了一份。 即:a是fun函数,b是1 如果函数名后面加上圆括号就表示立即调用(执行)这个函数里面的代码(花括号部分的代码)。 不加括号的,都是把函数名称作为函数的指针,一个函数的名称就是这个函数的指针,此时不是得到函数的结果,因为不会运行函数体代码。它只是传递了函数体所在的地址位置,在需要的时候好找到函数体去执行。 例如: window.onload=init; init函数并不会在这行代码时就执行,浏览器加载文档时这句话会被加载,会被告知文档加载完要执行哪个函数,但实际上没有当时就执行,等到整个文档加载完成之后才会通过init这个指针去执行init()。 小注:本文部分内容参考: https://www.zhihu.com/question/31044040 http://blog.csdn.net/yyx19941129/article/details/49642515 作者:jiankunking 出处:http://blog.csdn.net/jiankunking
与当前系统的用户用户组集成,可以使用视图。 用sql组织现有系统的用户组织等信息,只需要保证与之前activiti物理表名称结构一致即可。 通过视图过渡实现与现有系统中用户组织等的集成(这样就不需要同步用户数据了)。 图片摘自《Activiti实战》 Activiti实战下载地址:这里写链接内容 作者:jiankunking 出处:http://blog.csdn.net/jiankunking
什么是 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/
jQuery是目前使用最广泛的javascript函数库。 据统计,全世界排名前100万的网站,有46%使用jQuery,远远超过其他库。微软公司甚至把jQuery作为他们的官方库。 对于网页开发者来说,学会jQuery是必要的。因为它让你了解业界最通用的技术,为将来学习更高级的库打下基础,并且确实可以很轻松地做出许多复杂的效果。 虽然jQuery上手简单,比其他库容易学会,但是要全面掌握,却不轻松。因为它涉及到网页开发的方方面面,提供的各种方法和内部变化有上千种之多。初学者常常感到,入门很方便,提高很困难。 目前,互联网上最好的jQuery入门教材,是Rebecca Murphey写的《jQuery基础》(jQuery Fundamentals)。在Google里搜索"jQuery 培训",此书排在第一位。jQuery官方团队已经同意,把此书作为官方教程的基础。 这本书虽然是入门教材,但也足足有100多页。我对它做了一个详细的笔记,试图理清jQuery的设计思想,找出学习的脉络。我的目标是全面掌握jQuery,遇到问题的时候,心里有底,基本知道使用它的哪一个功能,然后可以迅速从手册中找到具体的写法。 下面就是我的笔记,它应该是目前网上不多的jQuery中文教程之一。你只需要一点javascript语言的基本知识,就能看懂它,在最短的时间里,掌握jQuery的所有主要方面(除了ajax和插件开发)。 =========================================== jQuery设计思想 原文网址:http://jqfundamentals.com/book/ 阮一峰 翻译整理 【目录】 一、选择网页元素 二、改变结果集 三、链式操作 四、元素的操作:取值和赋值 五、元素的操作:移动 六、元素的操作:复制、删除和创建 七、工具方法 八、事件操作 九、特殊效果 【正文】 一、选择网页元素 jQuery的基本设计思想和主要用法,就是"选择某个网页元素,然后对其进行某种操作"。这是它区别于其他Javascript库的根本特点。 使用jQuery的第一步,往往就是将一个选择表达式,放进构造函数jQuery()(简写为$),然后得到被选中的元素。 选择表达式可以是CSS选择器: $(document) //选择整个文档对象 $('#myId') //选择ID为myId的网页元素 $('div.myClass') // 选择class为myClass的div元素 $('input[name=first]') // 选择name属性等于first的input元素 也可以是jQuery特有的表达式: $('a:first') //选择网页中第一个a元素 $('tr:odd') //选择表格的奇数行 $('#myForm :input') // 选择表单中的input元素 $('div:visible') //选择可见的div元素 $('div:gt(2)') // 选择所有的div元素,除了前三个 $('div:animated') // 选择当前处于动画状态的div元素 二、改变结果集 jQuery设计思想之二,就是提供各种强大的过滤器,对结果集进行筛选,缩小选择结果。 $('div').has('p'); // 选择包含p元素的div元素 $('div').not('.myClass'); //选择class不等于myClass的div元素 $('div').filter('.myClass'); //选择class等于myClass的div元素 $('div').first(); //选择第1个div元素 $('div').eq(5); //选择第6个div元素 有时候,我们需要从结果集出发,移动到附近的相关元素,jQuery也提供了在DOM树上的移动方法: $('div').next('p'); //选择div元素后面的第一个p元素 $('div').parent(); //选择div元素的父元素 $('div').closest('form'); //选择离div最近的那个form父元素 $('div').children(); //选择div的所有子元素 $('div').siblings(); //选择div的同级元素 三、链式操作 jQuery设计思想之三,就是最终选中网页元素以后,可以对它进行一系列操作,并且所有操作可以连接在一起,以链条的形式写出来,比如: $('div').find('h3').eq(2).html('Hello'); 分解开来,就是下面这样: $('div') //找到div元素 .find('h3') //选择其中的h3元素 .eq(2) //选择第3个h3元素 .html('Hello'); //将它的内容改为Hello 这是jQuery最令人称道、最方便的特点。它的原理在于每一步的jQuery操作,返回的都是一个jQuery对象,所以不同操作可以连在一起。 jQuery还提供了.end()方法,使得结果集可以后退一步: $('div') .find('h3') .eq(2) .html('Hello') .end() //退回到选中所有的h3元素的那一步 .eq(0) //选中第一个h3元素 .html('World'); //将它的内容改为World 四、元素的操作:取值和赋值 操作网页元素,最常见的需求是取得它们的值,或者对它们进行赋值。 jQuery设计思想之四,就是使用同一个函数,来完成取值(getter)和赋值(setter),即"取值器"与"赋值器"合一。到底是取值还是赋值,由函数的参数决定。 $('h1').html(); //html()没有参数,表示取出h1的值 $('h1').html('Hello'); //html()有参数Hello,表示对h1进行赋值 常见的取值和赋值函数如下: .html() 取出或设置html内容 .text() 取出或设置text内容 .attr() 取出或设置某个属性的值 .width() 取出或设置某个元素的宽度 .height() 取出或设置某个元素的高度 .val() 取出某个表单元素的值 需要注意的是,如果结果集包含多个元素,那么赋值的时候,将对其中所有的元素赋值;取值的时候,则是只取出第一个元素的值(.text()例外,它取出所有元素的text内容)。 五、元素的操作:移动 jQuery设计思想之五,就是提供两组方法,来操作元素在网页中的位置移动。一组方法是直接移动该元素,另一组方法是移动其他元素,使得目标元素达到我们想要的位置。 假定我们选中了一个div元素,需要把它移动到p元素后面。 第一种方法是使用.insertAfter(),把div元素移动p元素后面: $('div').insertAfter($('p')); 第二种方法是使用.after(),把p元素加到div元素前面: $('p').after($('div')); 表面上看,这两种方法的效果是一样的,唯一的不同似乎只是操作视角的不同。但是实际上,它们有一个重大差别,那就是返回的元素不一样。第一种方法返回div元素,第二种方法返回p元素。你可以根据需要,选择到底使用哪一种方法。 使用这种模式的操作方法,一共有四对: .insertAfter()和.after():在现存元素的外部,从后面插入元素 .insertBefore()和.before():在现存元素的外部,从前面插入元素 .appendTo()和.append():在现存元素的内部,从后面插入元素 .prependTo()和.prepend():在现存元素的内部,从前面插入元素 六、元素的操作:复制、删除和创建 除了元素的位置移动之外,jQuery还提供其他几种操作元素的重要方法。 复制元素使用.clone()。 删除元素使用.remove()和.detach()。两者的区别在于,前者不保留被删除元素的事件,后者保留,有利于重新插入文档时使用。 清空元素内容(但是不删除该元素)使用.empty()。 创建新元素的方法非常简单,只要把新元素直接传入jQuery的构造函数就行了: $('<p>Hello</p>'); $('<li class="new">new list item</li>'); $('ul').append('<li>list item</li>'); 七、工具方法 jQuery设计思想之六:除了对选中的元素进行操作以外,还提供一些与元素无关的工具方法(utility)。不必选中元素,就可以直接使用这些方法。 如果你懂得Javascript语言的继承原理,那么就能理解工具方法的实质。它是定义在jQuery构造函数上的方法,即jQuery.method(),所以可以直接使用。而那些操作元素的方法,是定义在构造函数的prototype对象上的方法,即jQuery.prototype.method(),所以必须生成实例(即选中元素)后使用。如果不理解这种区别,问题也不大,只要把工具方法理解成,是像javascript原生函数那样,可以直接使用的方法就行了。 常用的工具方法有以下几种: $.trim() 去除字符串两端的空格。 $.each() 遍历一个数组或对象。 $.inArray() 返回一个值在数组中的索引位置。如果该值不在数组中,则返回-1。 $.grep() 返回数组中符合某种标准的元素。 $.extend() 将多个对象,合并到第一个对象。 $.makeArray() 将对象转化为数组。 $.type() 判断对象的类别(函数对象、日期对象、数组对象、正则对象等等)。 $.isArray() 判断某个参数是否为数组。 $.isEmptyObject() 判断某个对象是否为空(不含有任何属性)。 $.isFunction() 判断某个参数是否为函数。 $.isPlainObject() 判断某个参数是否为用"{}"或"new Object"建立的对象。 $.support() 判断浏览器是否支持某个特性。 八、事件操作 jQuery设计思想之七,就是把事件直接绑定在网页元素之上。 $('p').click(function(){ alert('Hello'); }); 目前,jQuery主要支持以下事件: .blur() 表单元素失去焦点。 .change() 表单元素的值发生变化 .click() 鼠标单击 .dblclick() 鼠标双击 .focus() 表单元素获得焦点 .focusin() 子元素获得焦点 .focusout() 子元素失去焦点 .hover() 同时为mouseenter和mouseleave事件指定处理函数 .keydown() 按下键盘(长时间按键,只返回一个事件) .keypress() 按下键盘(长时间按键,将返回多个事件) .keyup() 松开键盘 .load() 元素加载完毕 .mousedown() 按下鼠标 .mouseenter() 鼠标进入(进入子元素不触发) .mouseleave() 鼠标离开(离开子元素不触发) .mousemove() 鼠标在元素内部移动 .mouseout() 鼠标离开(离开子元素也触发) .mouseover() 鼠标进入(进入子元素也触发) .mouseup() 松开鼠标 .ready() DOM加载完成 .resize() 浏览器窗口的大小发生改变 .scroll() 滚动条的位置发生变化 .select() 用户选中文本框中的内容 .submit() 用户递交表单 .toggle() 根据鼠标点击的次数,依次运行多个函数 .unload() 用户离开页面 以上这些事件在jQuery内部,都是.bind()的便捷方式。使用.bind()可以更灵活地控制事件,比如为多个事件绑定同一个函数: $('input').bind( 'click change', //同时绑定click和change事件 function() { alert('Hello'); } ); 有时,你只想让事件运行一次,这时可以使用.one()方法。 $("p").one("click", function() { alert("Hello"); //只运行一次,以后的点击不会运行 }); .unbind()用来解除事件绑定。 $('p').unbind('click'); 所有的事件处理函数,都可以接受一个事件对象(event object)作为参数,比如下面例子中的e: $("p").click(function(e) { alert(e.type); // "click" }); 这个事件对象有一些很有用的属性和方法: event.pageX 事件发生时,鼠标距离网页左上角的水平距离 event.pageY 事件发生时,鼠标距离网页左上角的垂直距离 event.type 事件的类型(比如click) event.which 按下了哪一个键 event.data 在事件对象上绑定数据,然后传入事件处理函数 event.target 事件针对的网页元素 event.preventDefault() 阻止事件的默认行为(比如点击链接,会自动打开新页面) event.stopPropagation() 停止事件向上层元素冒泡 在事件处理函数中,可以用this关键字,返回事件针对的DOM元素: $('a').click(function(e) { if ($(this).attr('href').match('evil')) { //如果确认为有害链接 e.preventDefault(); //阻止打开 $(this).addClass('evil'); //加上表示有害的class } }); 有两种方法,可以自动触发一个事件。一种是直接使用事件函数,另一种是使用.trigger()或.triggerHandler()。 $('a').click(); $('a').trigger('click'); 九、特殊效果 最后,jQuery允许对象呈现某些特殊效果。 $('h1').show(); //展现一个h1标题 常用的特殊效果如下: .fadeIn() 淡入 .fadeOut() 淡出 .fadeTo() 调整透明度 .hide() 隐藏元素 .show() 显示元素 .slideDown() 向下展开 .slideUp() 向上卷起 .slideToggle() 依次展开或卷起某个元素 .toggle() 依次展示或隐藏某个元素 除了.show()和.hide(),所有其他特效的默认执行时间都是400ms(毫秒),但是你可以改变这个设置。 $('h1').fadeIn(300); // 300毫秒内淡入 $('h1').fadeOut('slow'); // 缓慢地淡出 在特效结束后,可以指定执行某个函数。 $('p').fadeOut(300, function() { $(this).remove(); }); 更复杂的特效,可以用.animate()自定义。 $('div').animate( { left : "+=50", //不断右移 opacity : 0.25 //指定透明度 }, 300, // 持续时间 function() { alert('done!'); } //回调函数 ); .stop()和.delay()用来停止或延缓特效的执行。 $.fx.off如果设置为true,则关闭所有网页特效。 (完) 原文地址:http://www.ruanyifeng.com/blog/2011/07/jquery_fundamentals.html
学习过Spring框架的人一定都会听过Spring的IoC(控制反转) 、DI(依赖注入)这两个概念,对于初学Spring的人来说,总觉得IoC 、DI这两个概念是模糊不清的,是很难理解的,今天和大家分享网上的一些技术大牛们对Spring框架的IOC的理解以及谈谈我对Spring Ioc的理解。 一、分享Iteye的开涛对Ioc的精彩讲解 首先要分享的是Iteye的开涛这位技术牛人对Spring框架的IOC的理解,写得非常通俗易懂,以下内容全部来自原文,原文地址:http://jinnianshilongnian.iteye.com/blog/1413846 1.1、IoC是什么 Ioc—Inversion of Control,即“控制反转”,不是什么技术,而是一种设计思想。在Java开发中,Ioc意味着将你设计好的对象交给容器控制,而不是传统的在你的对象内部直接控制。如何理解好Ioc呢?理解好Ioc的关键是要明确“谁控制谁,控制什么,为何是反转(有反转就应该有正转了),哪些方面反转了”,那我们来深入分析一下: ●谁控制谁,控制什么:传统Java SE程序设计,我们直接在对象内部通过new进行创建对象,是程序主动去创建依赖对象;而IoC是有专门一个容器来创建这些对象,即由Ioc容器来控制对 象的创建;谁控制谁?当然是IoC 容器控制了对象;控制什么?那就是主要控制了外部资源获取(不只是对象包括比如文件等)。 ●为何是反转,哪些方面反转了:有反转就有正转,传统应用程序是由我们自己在对象中主动控制去直接获取依赖对象,也就是正转;而反转则是由容器来帮忙创建及注入依赖对象;为何是反转?因为由容器帮我们查找及注入依赖对象,对象只是被动的接受依赖对象,所以是反转;哪些方面反转了?依赖对象的获取被反转了。 用图例说明一下,传统程序设计如图2-1,都是主动去创建相关对象然后再组合起来: 图1-1 传统应用程序示意图 当有了IoC/DI的容器后,在客户端类中不再主动去创建这些对象了,如图2-2所示: 图1-2有IoC/DI容器后程序结构示意图 1.2、IoC能做什么 IoC 不是一种技术,只是一种思想,一个重要的面向对象编程的法则,它能指导我们如何设计出松耦合、更优良的程序。传统应用程序都是由我们在类内部主动创建依赖对象,从而导致类与类之间高耦合,难于测试;有了IoC容器后,把创建和查找依赖对象的控制权交给了容器,由容器进行注入组合对象,所以对象与对象之间是 松散耦合,这样也方便测试,利于功能复用,更重要的是使得程序的整个体系结构变得非常灵活。 其实IoC对编程带来的最大改变不是从代码上,而是从思想上,发生了“主从换位”的变化。应用程序原本是老大,要获取什么资源都是主动出击,但是在IoC/DI思想中,应用程序就变成被动的了,被动的等待IoC容器来创建并注入它所需要的资源了。 IoC很好的体现了面向对象设计法则之一—— 好莱坞法则:“别找我们,我们找你”;即由IoC容器帮对象找相应的依赖对象并注入,而不是由对象主动去找。 1.3、IoC和DI DI—Dependency Injection,即“依赖注入”:组件之间依赖关系由容器在运行期决定,形象的说,即由容器动态的将某个依赖关系注入到组件之中。依赖注入的目的并非为软件系统带来更多功能,而是为了提升组件重用的频率,并为系统搭建一个灵活、可扩展的平台。通过依赖注入机制,我们只需要通过简单的配置,而无需任何代码就可指定目标需要的资源,完成自身的业务逻辑,而不需要关心具体的资源来自何处,由谁实现。 理解DI的关键是:“谁依赖谁,为什么需要依赖,谁注入谁,注入了什么”,那我们来深入分析一下: ●谁依赖于谁:当然是应用程序依赖于IoC容器; ●为什么需要依赖:应用程序需要IoC容器来提供对象需要的外部资源; ●谁注入谁:很明显是IoC容器注入应用程序某个对象,应用程序依赖的对象; ●注入了什么:就是注入某个对象所需要的外部资源(包括对象、资源、常量数据)。 IoC和DI由什么关系呢?其实它们是同一个概念的不同角度描述,由于控制反转概念比较含糊(可能只是理解为容器控制对象这一个层面,很难让人想到谁来维护对象关系),所以2004年大师级人物Martin Fowler又给出了一个新的名字:“依赖注入”,相对IoC 而言,“依赖注入”明确描述了“被注入对象依赖IoC容器配置依赖对象”。 看过很多对Spring的Ioc理解的文章,好多人对Ioc和DI的解释都晦涩难懂,反正就是一种说不清,道不明的感觉,读完之后依然是一头雾水,感觉就是开涛这位技术牛人写得特别通俗易懂,他清楚地解释了IoC(控制反转) 和DI(依赖注入)中的每一个字,读完之后给人一种豁然开朗的感觉。我相信对于初学Spring框架的人对Ioc的理解应该是有很大帮助的。 二、分享Bromon的blog上对IoC与DI浅显易懂的讲解 2.1、IoC(控制反转) 首先想说说IoC(Inversion of Control,控制反转)。这是spring的核心,贯穿始终。所谓IoC,对于spring框架来说,就是由spring来负责控制对象的生命周期和对象间的关系。这是什么意思呢,举个简单的例子,我们是如何找女朋友的?常见的情况是,我们到处去看哪里有长得漂亮身材又好的mm,然后打听她们的兴趣爱好、qq号、电话号、ip号、iq号………,想办法认识她们,投其所好送其所要,然后嘿嘿……这个过程是复杂深奥的,我们必须自己设计和面对每个环节。传统的程序开发也是如此,在一个对象中,如果要使用另外的对象,就必须得到它(自己new一个,或者从JNDI中查询一个),使用完之后还要将对象销毁(比如Connection等),对象始终会和其他的接口或类藕合起来。 那么IoC是如何做的呢?有点像通过婚介找女朋友,在我和女朋友之间引入了一个第三者:婚姻介绍所。婚介管理了很多男男女女的资料,我可以向婚介提出一个列表,告诉它我想找个什么样的女朋友,比如长得像李嘉欣,身材像林熙雷,唱歌像周杰伦,速度像卡洛斯,技术像齐达内之类的,然后婚介就会按照我们的要求,提供一个mm,我们只需要去和她谈恋爱、结婚就行了。简单明了,如果婚介给我们的人选不符合要求,我们就会抛出异常。整个过程不再由我自己控制,而是有婚介这样一个类似容器的机构来控制。Spring所倡导的开发方式就是如此,所有的类都会在spring容器中登记,告诉spring你是个什么东西,你需要什么东西,然后spring会在系统运行到适当的时候,把你要的东西主动给你,同时也把你交给其他需要你的东西。所有的类的创建、销毁都由 spring来控制,也就是说控制对象生存周期的不再是引用它的对象,而是spring。对于某个具体的对象而言,以前是它控制其他对象,现在是所有对象都被spring控制,所以这叫控制反转。 2.2、DI(依赖注入) IoC的一个重点是在系统运行中,动态的向某个对象提供它所需要的其他对象。这一点是通过DI(Dependency Injection,依赖注入)来实现的。比如对象A需要操作数据库,以前我们总是要在A中自己编写代码来获得一个Connection对象,有了 spring我们就只需要告诉spring,A中需要一个Connection,至于这个Connection怎么构造,何时构造,A不需要知道。在系统运行时,spring会在适当的时候制造一个Connection,然后像打针一样,注射到A当中,这样就完成了对各个对象之间关系的控制。A需要依赖 Connection才能正常运行,而这个Connection是由spring注入到A中的,依赖注入的名字就这么来的。那么DI是如何实现的呢? Java 1.3之后一个重要特征是反射(reflection),它允许程序在运行的时候动态的生成对象、执行对象的方法、改变对象的属性,spring就是通过反射来实现注入的。 理解了IoC和DI的概念后,一切都将变得简单明了,剩下的工作只是在spring的框架中堆积木而已。 三、我对IoC(控制反转)和DI(依赖注入)的理解 在平时的java应用开发中,我们要实现某一个功能或者说是完成某个业务逻辑时至少需要两个或以上的对象来协作完成,在没有使用Spring的时候,每个对象在需要使用他的合作对象时,自己均要使用像new object() 这样的语法来将合作对象创建出来,这个合作对象是由自己主动创建出来的,创建合作对象的主动权在自己手上,自己需要哪个合作对象,就主动去创建,创建合作对象的主动权和创建时机是由自己把控的,而这样就会使得对象间的耦合度高了,A对象需要使用合作对象B来共同完成一件事,A要使用B,那么A就对B产生了依赖,也就是A和B之间存在一种耦合关系,并且是紧密耦合在一起,而使用了Spring之后就不一样了,创建合作对象B的工作是由Spring来做的,Spring创建好B对象,然后存储到一个容器里面,当A对象需要使用B对象时,Spring就从存放对象的那个容器里面取出A要使用的那个B对象,然后交给A对象使用,至于Spring是如何创建那个对象,以及什么时候创建好对象的,A对象不需要关心这些细节问题(你是什么时候生的,怎么生出来的我可不关心,能帮我干活就行),A得到Spring给我们的对象之后,两个人一起协作完成要完成的工作即可。 所以控制反转IoC(Inversion of Control)是说创建对象的控制权进行转移,以前创建对象的主动权和创建时机是由自己把控的,而现在这种权力转移到第三方,比如转移交给了IoC容器,它就是一个专门用来创建对象的工厂,你要什么对象,它就给你什么对象,有了 IoC容器,依赖关系就变了,原先的依赖关系就没了,它们都依赖IoC容器了,通过IoC容器来建立它们之间的关系。 这是我对Spring的IoC(控制反转)的理解。DI(依赖注入)其实就是IOC的另外一种说法,DI是由Martin Fowler 在2004年初的一篇论文中首次提出的。他总结:控制的什么被反转了?就是:获得依赖对象的方式反转了。 四、小结 对于Spring Ioc这个核心概念,我相信每一个学习Spring的人都会有自己的理解。这种概念上的理解没有绝对的标准答案,仁者见仁智者见智。如果有理解不到位或者理解错的地方,欢迎广大园友指正!
原文地址 本文由 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再进来,会自动启动新的进程。
电影中个人很喜欢的几句话: 此心安处是吾乡 有人去国怀乡,满目萧然。有人竹杖芒鞋轻胜马,一蓑烟雨任平生。而我是黄沙百战穿金甲,不破楼兰终不还 暗透了才看得见星光。要有向死而生的勇气。 有时等一封信,漫长得如同一生,但是慢一点又有什么不可以呢,慢一点才能写出优雅浪漫的话语;慢一点,才能仔细寻觅盼望的爱情。 作者: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
局域网中共享文件夹时,一直用bat命令连接,很长时间一直正常,今天突然不行了,报出以下错误信息: --------------------------- 正在还原网络连接 --------------------------- 将 Y: 重新连接到 \\110.110.110.110\安装程序 时出错 Microsoft Windows Network: 不允许一个用户使用一个以上用户名与服务器或共享资源的多重连接。中断与此服务器或共享资源的所有连接,然后再试一次。 此连接尚未恢复。 --------------------------- 确定 --------------------------- 解决方案:在本机cmd中输入net use * /del /y 搞定
原文地址:这里写链接内容
一、命令模式: 将一个请求封装为一个对象,从而使你可用不同的请求对客户进行参数化;对请求排队或记录请求日志,以及支持可撤销的操作。 ——《设计模式》 二、模式结构: 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下有可能系统为安全,会自动取消你所做的权限设置。先管理员取得所有权,然后在添加。 作者:jiankunking 出处:http://blog.csdn.net/jiankunking
作者:jiankunking 出处:http://blog.csdn.net/jiankunking 今天在本地安装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
具体错误信息如下: “/TestService”应用程序中的服务器错误。 配置错误 说明: 在处理向该请求提供服务所需的配置文件时出错。请检查下面的特定错误详细信息并适当地修改配置文件。 分析器错误消息: 无法识别的属性“targetFramework”。请注意属性名称区分大小写。 源错误: 行 31: </connectionStrings> 行 32: <system.web> 行 33: <compilation debug="true" targetFramework="4.0" /> 行 34: <httpRuntime maxRequestLength="2097151"/> 行 35: “/TestService”应用程序中的服务器错误。 编译错误 说明: 在编译向该请求提供服务所需资源的过程中出现错误。请检查下列特定错误详细信息并适当地修改源代码。 编译器错误消息: CS0016: 未能写入输出文件“c:\Windows\Microsoft.NET\Framework64\v4.0.30319\Temporary ASP.NET Files\Testservice\13766dd4\68cdbe1a\App_global.asax.xg2wm_nf.dll”--“拒绝访问。 ” 源错误: [没有相关的源行] 源文件: 行: 0 显示详细的编译器输出: 版本信息: Microsoft .NET Framework 版本:4.0.30319; ASP.NET 版本:4.0.30319.34280 错误原因: 部署网站时,使用的应用程序池版本不对! 解决方案: 我的应用程序是在.NET 4.0下开发的,所以应用程序池应使用.NET4.0版本。如果您的电脑上没有安装相应版本的框架,下载一个安装上即可。 我的配置文件中是这样写的,<compilation debug="true" targetFramework="4.0">发布在iis上出现了 “无法识别的属性“targetFramework”。请注意属性名称区分大小写。”首先要确定的是大小写不可能出错,如果出错的话 vs 一定会帮我们检查出来的,那么就可能是版本不兼容的问题了 ,一般如果你的iis 是framework2.0的话,就非常的有可能1、你需要将2.0手动改成4.0 : 2、设置应用程序的默认设置,也就是你在iis上发布网站的默认设置 其实以上两步可以省略,直接进行第三步: 3、找到自己的网站: 作者:jiankunking 出处:http://blog.csdn.net/jiankunking
网上关于安装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补丁
Tools(工具栏)--customsize toolbars(自定义工具栏) palette(调色板)勾选:
错误信息: --------------------------- --------------------------- NHibernate.Exceptions.GenericADOException: could not get or update next value[SQL: ] ---> System.Data.SqlClient.SqlException: 对象名 'hibernate_unique_key' 无效。 在 System.Data.SqlClient.SqlConnection.OnError(SqlException exception, Boolean breakConnection, Action`1 wrapCloseInAction) 在 System.Data.SqlClient.SqlInternalConnection.OnError(SqlException exception, Boolean breakConnection, Action`1 wrapCloseInAction) 在 System.Data.SqlClient.TdsParser.ThrowExceptionAndWarning(TdsParserStateObject stateObj, Boolean callerHasConnectionLock, Boolean asyncClose) 在 System.Data.SqlClient.TdsParser.TryRun(RunBehavior runBehavior, SqlCommand cmdHandler, SqlDataReader dataStream, BulkCopySimpleResultSet bulkCopyHandler, TdsParserStateObject stateObj, Boolean& dataReady) 在 System.Data.SqlClient.SqlDataReader.TryConsumeMetaData() 在 System.Data.SqlClient.SqlDataReader.get_MetaData() 在 System.Data.SqlClient.SqlCommand.FinishExecuteReader(SqlDataReader ds, RunBehavior runBehavior, String resetOptionsString) 在 System.Data.SqlClient.SqlCommand.RunExecuteReaderTds(CommandBehavior cmdBehavior, RunBehavior runBehavior, Boolean returnStream, Boolean async, Int32 timeout, Task& task, Boolean asyncWrite, SqlDataReader ds, Boolean describeParameterEncryptionRequest) 在 System.Data.SqlClient.SqlCommand.RunExecuteReader(CommandBehavior cmdBehavior, RunBehavior runBehavior, Boolean returnStream, String method, TaskCompletionSource`1 completion, Int32 timeout, Task& task, Boolean asyncWrite) 在 System.Data.SqlClient.SqlCommand.RunExecuteReader(CommandBehavior cmdBehavior, RunBehavior runBehavior, Boolean returnStream, String method) 在 System.Data.SqlClient.SqlCommand.ExecuteReader(CommandBehavior behavior, String method) 在 System.Data.SqlClient.SqlCommand.ExecuteDbDataReader(CommandBehavior behavior) 在 System.Data.Common.DbCommand.System.Data.IDbCommand.ExecuteReader() 在 NHibernate.Id.TableGenerator.DoWorkInCurrentTransaction(ISessionImplementor session, IDbConnection conn, IDbTransaction transaction) 在 NHibernate.Engine.TransactionHelper.Work.DoWork(IDbConnection connection, IDbTransaction transaction) --- 内部异常堆栈跟踪的结尾 --- 在 NHibernate.Engine.TransactionHelper.Work.DoWork(IDbConnection connection, IDbTransaction transaction) 在 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) 在 WindowsFormsApplication1.Form1.Form1_Load(Object sender, EventArgs e) 位置 C:\Users\JianKunKing\Desktop\NHibernate\NHibernate03\WindowsFormsApplication1\Form1.cs:行号 41 --------------------------- 确定 --------------------------- 解决方法: I’ve genereated a schema for my (SQL 2005) db using SchemaExport, and it’s created a table CREATE TABLE [dbo].[hibernate_unique_key]( [next_hi][int] NULL ) ON [PRIMARY] When I try to add an entity, I get the error: --------------------------- --------------------------- NHibernate.Id.IdentifierGenerationException: could not read a hi value - you need to populate the table: hibernate_unique_key 在 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.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) 在 WindowsFormsApplication1.Form1.Form1_Load(Object sender, EventArgs e) 位置 C:\Users\JianKunKing\Desktop\NHibernate\NHibernate03\WindowsFormsApplication1\Form1.cs:行号 42 --------------------------- 确定 --------------------------- Solution: NHibernate expects to find a value that stores the current hi value in that table, ie it first runs something like: current_hi =[SELECT max(next_hi) FROM hibernate_unique_key]. So all you need to do is seed that table with an initial number, ie: INSERT INTO hibernate_unique_key(next_hi) VALUES (0) 参考文章: 在部署OLAT到myeclipse,数据库配置正确的情况下,出现异常:Error with hilo in NHibernate - “could not read a hi value - you
环境: &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
原文地址 一. 写在前面的 这么多的设计模式,我觉得职责链是我第一次看上去最简单,可是回想起来却又最复杂的一个模式。 因此,这个文章我酝酿了很久,一直也没有胆量发出来,例子也是改了又改,可是仍然觉得不够合理。所以希望各位多多指教。 二. 什么是链 文章伊始,先让我们了解这个最基本的概念,什么是链。 我给链下了这样的定义: 链是一系列节点的集合。 链的各节点可灵活拆分再重组。 三. 何为职责链 职责链模式:使多个对象都有机会处理请求,从而避免请求的发送者和接受者之间的耦合关系。将这个对象连成一条链,并沿着这条链传递该请求,直到有一个对象处理他为止。 图如下: 四. 职责链模式应用之请假管理 请假这个事情,相信每个人都不陌生。 我们公司是个相对很宽松的公司。 在公司里,如果你的请假时间小于0.5天,那么只需要向项目经理打声招呼就OK了。 如果超过了0.5天,但是还小于2天,那么就要去找人事部处理,当然,这就要扣工资了。 如果超过了2天,你就需要去找总经理了,工资当然也玩完了。 那么,对于我们来说,这个流程就是这样的。 也就是这样一个过程,你需要和你的直接上级——项目经理去打交道,最终可能是项目经理给你回邮件,可能是人事部给你回邮件,也可能是总经理给你回邮件。内部的过程其实应该是个黑盒子,你并不知道内部的消息是如何处理的。你需要找到的,只是你想要第一个交付的对象而已。 那么我们的代码应该是这样的。 首先我们要写一个请求的类。 class Request { private int day; private string reason; public int Day { get { return day; } set { day = value; } } public string Reason { get { return reason; } set { reason = value; } } public Request(int day, string reason) { this.day = day; this.reason = reason; } } 接下来看下请求相应者,他们有两个核心方法,一个是相应操作,一个是选择继任者。 abstract class Boss { private string name; public string Name { get { return name; } set { name = value; } } private Boss successor; public Boss Successor { get { return successor; } set { successor = value; } } public Boss(string name) { this.name = name; } public abstract bool PassRequest(Request request); } class PM:Boss { public PM(string name) : base(name) { } public override bool PassRequest(Request request) { int day = request.Day; string reason = request.Reason; if (day <= 0.5) { return true; } return Successor.PassRequest(request); } } class HR:Boss { public HR(string name) : base(name) { } public override bool PassRequest(Request request) { int day = request.Day; string reason = request.Reason; if (day > 0.5&&day<=2) { return true; } return Successor.PassRequest(request); } } class Manager : Boss { public Manager(string name) : base(name) { } public override bool PassRequest(Request request) { int day = request.Day; string reason = request.Reason; if (reason.Equals("正当理由")) { return true; } return false; } } 那么我们调用的时候就很简单了! static void Main(string[] args) { Request request = new Request(3, "非正当理由"); Boss pm = new PM("pm"); Boss hr = new HR("hr"); Boss manager = new Manager("manager"); pm.Successor = hr; hr.Successor = manager; bool pass = pm.PassRequest(request); Console.Write(pass); } 五. 灵活在哪? 让我们来看下职责链究竟灵活在哪? 改变内部的传递规则。 在内部,项目经理完全可以跳过人事部到那一关直接找到总经理。 每个人都可以去动态地指定他的继任者。 2.可以从职责链任何一关开始。 如果项目经理不在,那么完全可以写这样的代码: static void Main(string[] args) { Request request = new Request(3, "非正当理由"); Boss pm = new PM("pm"); Boss hr = new HR("hr"); Boss manager = new Manager("manager"); pm.Successor = hr; hr.Successor = manager; //bool pass = pm.PassRequest(request); bool pass = hr.PassRequest(request); Console.Write(pass); } 3、我们来比较一下,用职责链和不用职责链的区别: 这是不用职责链我们的结构,我们需要和公司中的每一个层级都发生耦合关系。 如果反映在代码上即使我们需要在一个类中去写上很多丑陋的if….else语句。 如果用了职责链,相当于我们面对的是一个黑箱,我们只需要认识其中的一个部门,然后让黑箱内部去负责传递就好了。 六. 职责链 != 链表 很多人都愿意把职责链和链表混为一谈,确实,从字面意思上理解,链,链表,很像。可是他们一样么? 他们区别在哪里: 让我们看一个链表的典型结构: 让我们来看一下链表的典型特征: 链表是一个链状结构,每个节点有一个next属性去指向他的下一节点。 链表有一个Header节点,然后用户每次必须通过头节点,然后去遍历寻找每一个节点。 链表遍历操作的复杂度是O(n),但是插入和删除指定节点的复杂度是常数级。 让我们来着重看这第二点: 我们来想想在文章开始时我们画出的那个链,一个链,我们可以从头将他拿起,也可以从中间将他拿起: 也就是说我们用户可以去访问节点中的任何一个节点作为开始节点,这就是链表与职责链不同的地方。 七. 职责链的扩展——树状链结构 职责链中,我们之前看到的都是一些单链结构,但是其实在很多情况下,每一个节点都对应着很多其他的部分。 那么这样,我们的每一个节点都可以使用一个List来维护他节点的下一节点,甚至可以用组合模式来分别设计每一节点。 八. 由法律想到——职责链的兜底条款 仔细想想法律条文,尤其是刑法,经常可以看到这样的条文: 如果***,则处以拘役处分。 如果***,则处以有期徒刑一年到十年。 如果***,则处以有期徒刑十年以上。 如果*,则**。 如果以上条件皆不满足,则*******。 其实最后一条就叫做法律的兜底条款。这给了法官很大的自由裁量权,在一定程度上也降低了犯罪分子钻法律空子的可能性。 在我们的职责链中,如果不存在这样的兜底条款,那么用户如果不从首节点开始访问,那么就很可能出现异常的情况。于是我们应该为职责链设置一个默认的条款: 这样的话,任何一个处理无论如何访问,都能得到一个正常的处理。 九. 职责链的缺点 让我们继续回到上面的例子,我们发现,其实当请假时间超过2天的时候,PM和HR其实没有做任何的事情,而只是做了一个传递工作。 而传递工作之后,他们就成了垃圾对象。 也就是说,他们在实际的处理中,并没有发挥任何的作用。 那么当这个链结构比较长,比较复杂的话,会产生很多的内存垃圾对象。 这也就是职责链的最大缺点之所在。 十. 职责链的乱用 在和其他的人的讨论中,我发现他们的观点是: 只要一者传一者,那么就要用职责链。在我们的项目中,他们这样去用: abstract class DBHelper { } interface IRequestHandler { IDBHelper ReturnHelper(string dbName); } class RequestHandler:IRequestHandler { private RequestHandler successor; public RequestHandler Successor { get { return successor; } set { successor = value; } } public abstract IDBHelper ReturnHelper(string dbName); } class SQLHelper : DBHelper { } class OracleHelper : DBHelper { } class DB2Helper : DBHelper { } class SQL : RequestHandler { public override IDBHelper ReturnHelper(string dbName) { if (dbName.Equals("SQL Server")) { return new SQLHelper(); } return Successor.ReturnHelper(dbName); } } class Oracle : RequestHandler { public override IDBHelper ReturnHelper(string dbName) { if (dbName.Equals("Oracle")) { return new OracleHelper(); } return Successor.ReturnHelper(dbName); } } class DB2 : RequestHandler { public override IDBHelper ReturnHelper(string dbName) { if (dbName.Equals("DB2")) { return new DB2Helper(); } return new SQLHelper(); } } 这样的话,每个类相当于只负责一个操作。 那么我们如何改进呢?第一,我们可以用一个工厂来实现。另外,我们可以用表驱动的方式来解决问题。 十一. 表驱动改进职责链 表驱动(Table driven),其实就是指用查表的方式来获取值。 那么我们用标驱动法来改进上面的例子: class HelperRequest { private Dictionary<String, DBHelper> dic = new Dictionary<string, DBHelper>(); public void Add(string name,DBHelper helper) { dic.Add(name, helper); } public DBHelper GetHelper(string name) { DBHelper helper; bool temp = dic.TryGetValue(name, out helper); if (temp) { return helper; } return null; } } 我想一个没有学过设计模式的人都会这样写的。一个学过设计模式很多年的人也会这样写的。 而怕的就是为了模式而模式,为了职责链而职责链了。 十二. 职责链在java script中的应用 我们想象这样一种情况: 我们都知道,在ASP.NET 的 Webform模型中页面是以控件树的形式去组织的。那么我们用右键点击其中的一个页面,那么这个事件就会找离他最近的控件,如果不存在,那么就去找他的父控件,如此递归下去,直到找到为止。 这其实就是一种职责链的体现! 十三. 深析职责链的使用 职责链模式不能乱用,否则非常容易变成因为模式而模式的反例。 下面是我归纳出来的一些关于职责链方面的使用规则,只是个人的意见,还希望大家指教。 1, 如果存在N对N,或者是一般的常规线性关系,那么我们完全可以用表驱动来取代职责链。 2, 对象本身要经过什么处理是通过每个链上元素通过运行态来决定的,决定的因素是取决于对象的属性或者一些其他方面的策略。 3, 用户无论是从哪一个节点作为他的请求头节点,最终用户都可以得到一个请求的反馈。 4, 应怪怪建议,补充同级的处理!职责链并非是严格的上下级的传递,其中也包括同级的传递,职责链一样可以在同级之间做传递。 例如,继续用我们上面请假的那个做例子,也许我们公司有两个HR,事实上也是这样的,我们把前台“MM”也美称为人力资源部: static void Main(string[] args) { Request request = new Request(3, "非正当理由"); Boss pm = new PM("pm"); Boss hr1 = new HR("Real HR"); Boss hr2 = new HR("QiantaiMM"); Boss manager = new Manager("manager"); pm.Successor = hr1; hr1.Successor = hr2; hr2.Successor = manager; bool pass = pm.PassRequest(request); Console.Write(pass); } 其实这样也未尝不可。有人也许会说,那么这样的同样一个类的两个对象又有什么意义呢? 那么我们不妨去试着这样改造这个HR的类。 enum HRType { RealHR, Qiantai } class HR:Boss { private HRType type; public HR(string name,HRType type) : base(name) { this.type = type; } public override bool PassRequest(Request request) { int day = request.Day; if (day>=0.5&&day<2) { switch (type) { case HRType.RealHR: //扣工资 return true; break; case HRType.Qiantai: //不扣工资 return true; break; } } return Successor.PassRequest(request); } } 这样,因为前台MM容易说话,很可能他就不去扣你的工资,如果你去先找的HR,那么你这天的工资就报销了。 同理,我们一样可以让他们的职责细化,比如说Real Hr负责0.5天到1天的,而Qiantai去负责1天到2天的,也未尝不可。 总之,职责链并非是单一的上下级的传递,一样可以实现同级的传递。 十四. 职责链总结 1、Chain of Responsibility 模式的应用场合在于“一个请求可能有多个接受者,但是最后真正的接受者只有一个”,只有这时候请求发送者与接受者的耦合才有可能出现“变化脆弱”的症状,职责链的目的就是将二者解耦,从而更好地应对变化。 2、应用了Chain of Responsibility 模式后,对象的职责分派将更具灵活性。我们可以在运行时动态添加/修改请求的处理职责。 3、如果请求传递到职责链的末尾仍得不到处理,应该有一个合理的缺省机制。这也是每一个接受对象的责任,而不是发出请求的对象的责任。
原文地址 开篇 从’严格’意义上说,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的场景,这么做更加严谨。
2020年06月