|
2012年8月20日
在用 extjs editorgridpanel 进行输入编辑的时候, 默认情况下只支持使用 tab 键可以实现焦点切换, 如果想让editorgridpanel 在编辑时通过方向键来实现焦点跳转切换,只需加上以下代码: //让grid支持方向键盘 by liongis@163.com Ext.override(Ext.grid.CellSelectionModel, { onEditorKey : function(field, e) { var smodel = this; var k = e.getKey(), newCell, g = smodel.grid, ed = g.activeEditor; switch(k){ case e.TAB: e.stopEvent(); ed.completeEdit(); if (e.shiftKey) { newCell = g.walkCells(ed.row, ed.col-1, -1, smodel.acceptsNav, smodel); } else { newCell = g.walkCells(ed.row, ed.col+1, 1, smodel.acceptsNav, smodel); } if (ed.col == 1) { if (e.shiftKey) { newCell = g.walkCells(ed.row, ed.col+1, -1, smodel.acceptsNav, smodel); } else { newCell = g.walkCells(ed.row, ed.col+1, 1, smodel.acceptsNav, smodel); } } break; case e.UP: e.stopEvent(); ed.completeEdit(); newCell = g.walkCells(ed.row-1, ed.col, -1, smodel.acceptsNav, smodel); break; case e.DOWN: e.stopEvent(); ed.completeEdit(); newCell = g.walkCells(ed.row+1, ed.col, 1, smodel.acceptsNav, smodel); break; case e.LEFT: e.stopEvent(); ed.completeEdit(); newCell = g.walkCells(ed.row, ed.col-1, -1, smodel.acceptsNav, smodel); break; case e.RIGHT: e.stopEvent(); ed.completeEdit(); newCell = g.walkCells(ed.row, ed.col+1, 1, smodel.acceptsNav, smodel); break; } if (newCell) { g.startEditing(newCell[0], newCell[1]); } } }); 注意:这里重写的是:CellSelectionModel ,而不是RowSelectionMode 原文出自: http://www.cnblogs.com/liongis/p/3284620.html
序言: 1.本文摘自网络,看控件命名像是4.0以前的版本,但控件属性配置仍然可以借鉴(不足之处,以后项目用到时再续完善)。 Ext.form.TimeField: 配置项: maxValue:列表中允许的最大时间 maxText:当时间大于最大值时的错误提示信息 minValue:列表中允许的最小时间 minText:当时间小于最小值时的错误提示信息 increment:两个相邻选项间的时间间隔,默认为15分钟 format:显示格式,默认为“g:i A”。一般使用“H:i:s” H:带前缀0的24小时 i:带前缀0的分钟 s:带前缀0的秒 invalidText:当时间值非法时显示的提示信息 altFormats:多个时间输入格式组成的字符串,不同的格式之间使用“|”进行分割 Ext.form.FieldSet animCollapse:动画折叠,默认为false checkboxToggle:设置是否显示字段集的checkbox选择框,默认为false checkboxName:指定字段集中用于展开或隐藏字段集面板的checkbox的名字,该属性只有在checkboxToggle为true时生效 labelWidth:字段标签的宽度,可以级联到子容器 layout:布局,默认为form Ext.form.DateFied maxValue:允许选择的最大日期 maxText:当日期大于最大值时的错误提示信息 minValue:允许选择的最小时间 minText:当日期小于最小值时的错误提示信息 format:日期显示格式,默认为“m/d/y”,一般使用“Y-m-d” Y:四位年份 m:带前缀0的月份 d:带前缀0的日期 y:两位年份 n:不带前缀0的月份 j:不带前缀0的日期 w:星期的数字,0表示星期日,1代表星期一 showToday:是否显示今天按钮,默认为true altFormats:多个日期输入格式组成的字符串,不同的格式之间使用“|”进行分割,默认值为'm/d/Y|n/j/Y|n/j/y|m/j /y|n/d/y|m/j/Y|n/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d|Y-m-d' disabledDates:禁止选择的日期组成的数组 disabledDatesText:选择禁选日期时显示的提示信息 disabledDays:禁止选择的星期组成的数组,0代表星期日,1代表星期一 disabledDaysText:选择禁选星期时显示的提示信息 invalidText:当日期值非法时显示的提示信息 方法: getValue():取得日期值 Ext.form.ComboBox displayField:被显示在下拉框中的字段名 editable:是否可编辑,默认为true forceSelection:输入值是否严格为待选列表中存在的值。如果输入不存在的值,会自动选择第一个最接近的值。 hiddenName:隐藏字段的名字,如果提供该参数则一个隐藏字段将被创建,用来存储所选值,当表单提交时在服务器端可以通过该名字取得列表中的所选值 listWidth:下拉列表的宽度 minListWidth:下拉列表的最小宽度,默认为70像素 loadingText:当下拉框加载数据时显示的提示信息,只有当mode='remote'时才会生效 maxHeight:下拉列表框的最大高度,默认为300像素 minChars:下拉列表框自动选择前用户需要输入的最小字符数量。mode='remote'默认为4,mode='local'默认为0 mode:下拉列表框的数据读取模式。remote读取远程数据,local读取本地数据 pageSize:下拉列表框的分页大小。该项设置只在mode='remote'时生效 queryParam:查询的名字,默认为'query',将被传递到查询字符串中 allQuery:一个发往服务器用来查询全部信息的查询字符串,默认为空字符串'' selectOnFocus:当获得焦点时立刻选择一个已存在的列表项。默认为false,此项只有在editable=true时才会生效 store:列表框绑定的数据源 transform:将页面中已存在的元素转换为组合框 lazyInit:延时初始化下拉列表,默认为true lazyRender:延时渲染,默认为false triggerAction:设置单击触发按钮时执行的默认操作,有效值包括all和query,默认为query,如果设置为all则会执行allQuery中设置的查询 typeAhead:设置在输入过程中是否自动选择匹配的剩余部分文本(选择第一个满足条件的),默认为false value:初始化组合框中的值 valueField:组合框的值字段 valueNotFoundText:值不存在时的提示信息 tpl:Ext模板字符串或模板对象,可以通过该配置项自定义下拉列表的显示方式 方法: clearValue():清空字段当前值 doQuery( String query, Boolean forceAll ): getValue(): getStore(): setValue( String value ): Ext.from.RadioGroup allowBlank: blankText: Ext.form.Radio; getGroupValue(): setValue( value {String/Boolean} ): Ext.form.CheckboxGroup allowBlank:是否允许不选择,默认为true blankText: columns:显示的列数,可选值包括:固定值auto、数值、数组(整数、小数) items:对象数组 vertical:是否垂直方向显示对象,默认为false Ext.form.Checkbox boxLabel:复选框的文字描述 checked:复选框是否被选择,默认为false handler:当checked值改变时触发的函数,函数包含两个参数:checkbox、checked inputValue: 方法: getValue():返回复选框的checked状态 setValue( Boolean/String checked ): Ext.form.NumberField allowDecimals:是否允许输入小数,默认为true allowNegative:是否允许输入负数,默认为true baseChars:输入的有效数字集合,默认为'0123456789' decimalPrecision:数字的精度,默认保留小数点后2位 decimalSeparator:十进制分隔符,默认为'.' maxValue:允许输入的最大数值 maxText:超过最大值之后的提示信息 minValue:允许输入的最小数值 minText:超过最小值之后的提示信息 nanText:输入非有效数值之后的提示信息 Ext.form.TextArea preventScrollbars:是否禁止出现滚动条,默认为false Ext.form.TextField allowBlank:是否允许为空,默认为true blankText:空验证失败后显示的提示信息 emptyText:在一个空字段中默认显示的信息 grow:字段是否自动伸展和收缩,默认为false growMin:收缩的最小宽度 growMax:伸展的最大宽度 inputType:字段类型:默认为text maskRe:用于过滤不匹配字符输入的正则表达式 maxLength:字段允许输入的最大长度 maxLengthText:最大长度验证失败后显示的提示信息 minLength:字段允许输入的最小长度 minLengthText:最小长度验证失败后显示的提示信息 regex:正则表达式 regexText:正则表达式验证失败后显示的提示信息 vtype:验证类型的名字 alpha:限制只能输入字母 alphanum:限制只能输入字母和数字 email url vtypeText:验证失败时的提示信息 validator:自定义验证函数 selectOnFocus:当字段得到焦点时自动选择已存在的文本,默认为false Ext.form.Field name:字段名 value:字段的初始化值 disabled:字段是否不可用,默认为false fieldLabel:字段标签说明 hideLabel:隐藏字段标签,默认为false labelSeparator:字段标签与字段之间的分隔符,默认为':' labelStyle:字段标签样式 inputType:默认为text invalidClass:默认为x-form-invalid invalidText:字段非法文本提示 msgTarget:错误信息显示的位置,默认为qtip qtip:显示一个浮动的提示信息 title:显示一个浏览器的浮动提示信息 under:在字段下方显示一个提示信息 side:在字段右边显示一个提示信息 readOnly:字段是否只读,默认为false validateOnBlur:字段在失去焦点时被验证,默认为true 方法: clearInvalid(): getRawValue() setRawValue( Mixed value ) getValue() setValue( Mixed value ) isDirty():字段值在装载后是否被修改过 isValid( Boolean preventMark ):当前字段值是否合法 markInvalid( [String msg] ) validate() reset() Ext.form.FormPanel items:一个元素或元素数组 buttons:一个按钮配置对象的数组,按钮将被添加到表单页脚中 buttonAlign:按钮的对齐方式,可选值有left、center、right,默认为center labelWidth:表单标签的宽度 labelAlign:表单标签的对齐方式,可选值有left、top、right,默认为left labelSeparator:字段标签与字段之间的分隔符,默认为':' minButtonWidth:按钮的最小宽度,默认为75 方法: getForm() : Ext.form.BasicForm load( Object options ) startMonitoring() stopMonitoring() Ext.form.BaseicForm baseParams:传递到请求中的参数 method:表单的提交方式,有效值包括GET、POST url:表单默认的提交路径 fileUpload:表单是否进行文件上传 timeout:表单动作的超时时间,默认为30秒 trackResetOnLoad:是否在表单初次创建时清楚数据 方法: doAction( String/Object actionName, [Object options] ):执行一个预订的动作,可用选项包括: url:动作提交的路径 method:表单的提交方式,有效值包括GET、POST params:传递到请求中的参数 headers: success:执行成功后回调的函数,包括两个参数:form和action failure:执行失败后回调的函数,包括两个参数:form和action clientValidation:是否客户端验证 clearInvalid():清除表单中所有的无效验证信息 findField( String id ):查找表单字段 getValues( [Boolean asString] ): isDirty():表单数据是否被更改过 isValid():客户端验证是否成功 load( Object options ):执行表单读取动作 loadRecord( Record record ):从一个数据记录中读取数据到表单中 markInvalid( Array/Object errors ):成批设置表单字段为验证无效 reset():重置表单 setValues( Array/Object values ):成批设置表单字段值 submit( Object options ):执行表单提交动作 updateRecord( Record record ):持久化表单数据到记录集中 Ext.form.Action success:执行成功后回调的函数,包括两个参数:form和action failure:执行失败后回调的函数,包括两个参数:form和action method:表单的提交方式,有效值包括GET、POST params:传递到请求中的参数 url:动作提交的路径 waitMsg:动作执行时显示的等待信息 属性: Action.CLIENT_INVALID:客户端验证错误 Action.CONNECT_FAILURE:通信错误 Action.LOAD_FAILURE:加载数据时,没有包含data属性的字段被返回 Action.SERVER_INVALID:服务端验证错误 failureType:错误类型 result:包含布尔类型的success属性和其他属性,如{success: true, msg: 'ok'} type:动作类型,可选值有submit和load Ext.form.Action.Submit:返回的信息中要包含一个布尔类型的success属性和一个可选的errors属性 Ext.form.Action.Load:返回的信息中要包含一个布尔类型的success属性和一个data属性 Ext.grid.EditorGridPanel clicksToEdit:设置点击单元格进入编辑模式的点击次数,默认为2 autoEncode:是否自动编码/解码HTML内容,默认为false selModel:默认为Ext.grid.CellSelectionModel 主要方法: startEditing( Number rowIndex, Number colIndex ):开始编辑指定单元格 stopEditing( [Boolean cancel] ):结束编辑操作 Ext.grid.GroupinView enableGroupingMenu:是否在表头菜单中进行分组控制,默认为true groupByText:表头菜单中分组控制的菜单文字,默认为'Group By This Field' enableNoGroups:是否允许用户关闭分组功能,默认为true showGroupsText:在表头菜单中启用分组和禁用分组的菜单文字,默认为'Show in Groups' groupTextTpl:用于渲染分组信息的模板,默认为'{text}',常用的可选值有: text:列标题:组字段值 gvalue:组字段的值 startRow:组行索引 enableGrouping:是否对数据分组,默认为true hideGroupedColumn:是否隐藏分组列,默认为false ignoreAdd:在向表格中添加数据时是否刷新表格,默认为false showGroupName:是否在分组行上显示分组字段的名字,默认为true startCollapsed:初次显示时分组是否处于收缩状态,默认为false 主要方法: collapseAllGroups():收缩所有分组行 expandAllGroups():展开所有分组行 getGroupId( String value ):根据分组字段值取得组id toggleAllGroups( [Boolean expanded] ):切换所有分组行的展开或收缩状态 toggleGroup( String groupId, [Boolean expanded] ):切换指定分组行的展开或收缩状态 2、Ext.data.GroupingStore groupField:分组字段 groupOnSort:是否在分组字段上排序,默认为false remoteGroup:是否远程分组数据,默认为false。如果是远程分组数据,则通过groupBy参数发送分组字段名 Ext.grid.GridPanel: store:表格的数据集 columns:表格列模式的配置数组,可自动创建ColumnModel列模式 autoExpandColumn:自动充满表格未用空间的列,参数为列id,该id不能为0 stripeRows:表格是否隔行换色,默认为false cm、colModel:表格的列模式,渲染表格时必须设置该配置项 sm、selModel:表格的选择模式,默认为Ext.grid.RowSelectionModel enableHdMenu:是否显示表头的上下文菜单,默认为true enableColumnHide:是否允许通过标题中的上下文菜单隐藏列,默认为true loadMask:是否在加载数据时显示遮罩效果,默认为false view:表格视图,默认为Ext.grid.GridView viewConfig:表格视图的配置对象 autoExpandMax:自动扩充列的最大宽度,默认为1000 autoExpandMin:自动扩充列的最小宽度,默认为50 columnLines:是否显示列分割线,默认为false disableSelection:是否禁止行选择,默认为false enableColumnMove:是否允许拖放列,默认为true enableColumnResize:是否允许改变列宽,默认为true hideHeaders:是否隐藏表头,默认为false maxHeight:最大高度 minColumnWidth:最小列宽,默认为25 trackMouseOver:是否高亮显示鼠标所在的行,默认为true 主要方法: getColumnModel():取得列模式 getSelectionModel():取得选择模式 getStore():取得数据集 getView():取得视图对象 reconfigure( Ext.data.Store store, Ext.grid.ColumnModel colModel ):使用一个新的数据集和列模式重新配置表格组件
2、Ext.grid.Column 主要配置项: id:列id header:表头文字 dataIndex:设置列与数据集中数据记录的对应关系,值为数据记录中的字段名称。如果没有设置该项则使用列索引与数据记录中字段的索引进行对应 width:列宽 align:列数据的对齐方式 hidden:是否隐藏列,默认为false fixed:是否固定列宽,默认为false menuDisabled:是否禁用列的上下文菜单,默认为false resizable:是否允许改变列宽,默认为true sortable:是否允许排序,默认为true renderer:设置列的自定义单元格渲染函数 传入函数的参数有: value:数据的原始值 metadata:元数据对象,用于设置单元格的样式和属性,该对象包含的属性有: css:应用到单元格TD元素上的样式名称 attr:一个HTML属性定义字符串,例如'style="color:blue"' record:当前数据记录对象 rowIndex:单元格的行索引 colIndex:单元格的列索引 store:数据集对象 xtype:列渲染器类型,默认为gridcolumn,其它可选值有booleancolumn、numbercolumn、datecolumn、templatecolumn等 editable:是否可编辑,默认为true editor:编辑器 groupName: emptyGroupText: groupable:
3、Ext.grid.ColumnModel 主要配置项: columns:字段数组 defaultSortable:是否进行默认排序,默认为false defaultWidth:默认宽度 主要方法: findColumnIndex( String col ):根据给定的dataIndex查找列索引 getColumnById( String id ):取得指定id对应的列 getColumnCount( Boolean visibleOnly ):取得列总数 getColumnHeader( Number col ):取得列的表头 getColumnId( Number index ):取得列id getDataIndex( Number col ):取得列对应的数据字段名 getIndexById( String id ):取得列索引 getTotalWidth( Boolean includeHidden ) isCellEditable( Number colIndex, Number rowIndex ) isFixed() isHidden( Number colIndex ) setColumnHeader( Number col, String header ) setColumnWidth( Number col, Number width, Boolean suppressEvent ) setDataIndex( Number col, String dataIndex ) setEditable( Number col, Boolean editable ) setEditor( Number col, Object editor ) setHidden( Number colIndex, Boolean hidden ) setRenderer( Number col, Function fn ) 4、Ext.grid.AbstractSelectionModel 主要方法: lock():锁定选择区域 unlock():解锁选择区域 isLocked():当前选择区域是否被锁定 5、Ext.grid.CellSelectionModel 主要方法: clearSelections( Boolean preventNotify ):清除选择区域 getSelectedCell():取得当前选择的单元格,返回一数组,其格式:[rowIndex, colIndex] hasSelection():当前是否有选择区域 select( Number rowIndex, Number colIndex, [Boolean preventViewNotify], [Boolean preventFocus], [Ext.data.Record r] ):选择指定单元格 6、Ext.grid.RowSelectionModel 主要配置项: singleSelect:是否单选模式,默认为false,即可以选择多条数据
主要方法: clearSelections( [Boolean fast] ):清除所有选择区域 deselectRange( Number startRow, Number endRow ):取消范围内的行选择 deselectRow( Number row, [Boolean preventViewNotify] ):取消指定行的选择状态 each( Function fn, [Object scope] ):遍历所有选择行,并调用指定函数。当前被选行将传入该函数中 getCount():得到选择的总行数 getSelected():得到第一个被选记录 getSelections():得到所有被选记录的数组 hasNext():判断当前被选行之后是否还有记录可以选择 hasPrevious():判断当前被选行之前是否还有记录可以选择 hasSelection():是否已选择了数据 isIdSelected( String id ):判断指定id的记录是否被选择 isSelected( Number/Record index ):判断指定记录或记录索引的数据是否被选择 selectAll():选择所有行 selectFirstRow():选择第一行 selectLastRow( [Boolean keepExisting] ):选择最后行 keepExisting:是否保持已有的选择 selectNext( [Boolean keepExisting] ):选择当前选择行的下一行 selectPrevious( [Boolean keepExisting] ):选择当前选择行的上一行 selectRange( Number startRow, Number endRow, [Boolean keepExisting] ):选择范围内的所有行 selectRecords( Array records, [Boolean keepExisting] ):选择一组指定记录 selectRow( Number row, [Boolean keepExisting], [Boolean preventViewNotify] ):选择一行 row:行索引 selectRows( Array rows, [Boolean keepExisting] ):选择多行 rows:行索引数组 7、Ext.grid.CheckboxSelectionModel 主要配置项: singleSelect:是否单选模式,默认为false,即可以选择多条数据 checkOnly:是否只能通过点击checkbox列进行选择,默认为false sortable:是否允许checkbox列排序,默认为false width:checkbox列的宽度,默认为20 8、Ext.grid.RowNumberer 主要配置项: header:行号列表头显示的内容 width:列宽,默认为23 9、Ext.grid.GridView 主要配置项: enableRowBody:是否包含行体 sortAscText:表格标题菜单中升序的文字描述 sortDescText:表格标题菜单中降序的文字描述 columnsText:表格标题菜单中列对应的文字描述 autoFill:是否自动扩展列以充满整个表格,默认为false forceFit:是否强制调整表格列宽以适用表格的整体宽度,防止出现水平滚动条,默认为false
主要方法: focusCell( Number row, Number col ):将焦点移到指定单元格 focusRow( Number row ):将焦点移动指定行 getCell( Number row, Number col ):取得指定单元格对应的td元素 getHeaderCell( Number index ):取得指定表头对应的td元素 getRow( Number index ):取得指定行对应的tr元素 getRowClass( Record record, Number index, Object rowParams, Store store ):得到附加到表格行上的样式名 record:当前行的数据记录对象 index:当前行的索引 rowParams:渲染时传入到行模板中的配置对象,通过它可以为行体定制样式,该对象只在enableRowBody为true时才生效,可能的属性如下: body:渲染到行体中的HTML代码片段 bodyStyle:应用到行体tr元素style属性的字符串 cols:应用到行体td元素colspan属性的值,默认为总列数 store:表格数据集 refresh( [Boolean headersToo] ):刷新表格组件 scrollToTop():滚动表格到顶端 Ext.TabPanel: activeTab:初始激活的tab,索引或者id值,默认为none autoTabs:是否自动将带有'x-tab'样式类的div转成tabs添加到TabPanel中,默认为false。 当该配置项设为true时,需要设置deferredRender为false,还必须使用applyTo。 deferredRender:是否延迟渲染,默认为true。 autoTabSelector:默认为'div.x-tab'。 resizeTabs:是否可以改变tab的尺寸,默认为false。 minTabWidth:tab的最小宽度,默认为30。 tabWidth:每个新增加的tab宽度,默认为120。 tabTip:tab的提示信息 tabPosition:tab位置,可选值有top、bottom,默认为top。 enableTabScroll:是否允许Tab溢出时可以滚动,默认为false。 closable:tab是否可关闭,默认为false scrollDuration:每次的滚动时长,默认为0.35毫秒。 scrollIncrement:每次的滚动步长,默认为100像素。 wheelIncrement:每次鼠标滑轮的滚动步长,默认为20像素。 2、主要方法: activate( String/Panel tab ) getActiveTab():获取当前活动的tab get( String/Number key ):根据组件id或者索引获取组件 getItem(String id):根据tab id获取tab setActiveTab( String/Number item ) remove( Component/String component, [Boolean autoDestroy] ) removeAll( [Boolean autoDestroy] ) 在使用TabPanel时需要注意: 1、在创建Ext.TabPanel时deferredRender配置项经常会被忽略。该配置项的默认值是true。true表示只有在用户第一次访问 选项卡时,该选项卡的panel才会被渲染。 所以当我们有可能使用脚本操作选项卡时,谨记将该配置项设置为false。 2、在FormPanel中使用TabPanel,如果在TabPanel中不定义deferredRender的值为false,那么,当你使用 Load方法为Form加载数据,或使用setValue为没有激活过的Panel的控件赋值时,将会发生错误。原因是,在默认设置下 deferredRender为true,TabPanel并不会渲染所有Panel上的控件,只有在该Panel被激活时才渲染控件,所以当你为这些控 件设置数据时,将会找不到这些控件,会出现错误。因而,在FormPanel中使用TabPanel,一定要在TabPanel中设置 deferredRender的值为false,强制TabPanel在Layout渲染时同时渲染所有Panel上的控件。 本文转自: http://www.cnblogs.com/knowledgesea/p/3284404.html
摘要: 前言:最近公司有个Web要发布,但是以前都是由实施到甲方去发布,配置,这几天有点闲,同事让我搞一个一键发布,就和安装软件那样的程序,好让实施直接 配置一下数据库就可以了,然后到网上搜了下,找到一些相关的教程,现在整理了一下,花了一个下午的时间来写笔记,写好了,首先奉献给博客园的小伙伴们,和 大伙儿分享一下,好了,下面进入主题~~~ 1,首先打开VS2010,新建一个项目,如图1-1所示: &nbs... 阅读全文
大家可以参考下这个网站http://eoffice.im.fju.edu.tw/phpbb/viewtopic.php?p=28685
1.先启动项目上的h2/bin下的h2.bat或h2w.bat文件,把h2数据库启动起来 2.SSH2框架和h2数据库整合方法 2.1先在数据库下创建 schema目录(相当于一个数据库实例) create schema fdrkftcode 目的是解决这种异常org.h2.jdbc.JdbcSQLException: Schema "fdrkftcode" not found; ... 2.2在schema目录下创建表,如创建系统用户表admin create table fdrkftcode.admin( id int primary key, adminname varchar(50), username varchar(50), userpwd varchar(50), adminrights varchar(50), createdate datetime, usedtimes int, lastlogin datetime, curstatus int, remark varchar(200) ) 3.为了使用hibernate操作h2,需要作如下设置,在sql编辑窗口输入下面这些脚本 对于实体pojo对象的映射,我是用的annotation,关键是id主键的映射,如下: @Column(name = "ID", nullable = false) @Id @GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "ABC_ID_SEQ") @SequenceGenerator(name = "ABC_ID_SEQ", sequenceName = "ABC_ID_SEQ") protected Long id; 注意这里的GeneratedValue和SequenceGenerator的使用,这属于JPA规范,全部来自javax.persisten 4.配置applicationContext.xml文件,主要有三个地方要注意: 4.1修改连接数据库的JDBC驱动 driverClass的值为org.h2.Driver 4.2修改连接数据库所用的URL字符串 jdbcUrl的值为jdbc:h2:tcp://localhost/~/FDRKFTCODE;MODE=MySQL;AUTO_SERVER=TRUE 4.3修改Hibernate的数据库方言hibernate.dialect为org.hibernate.dialect.H2Dialect 5.h2数据库一些常用操作 5.1帮助命令help 5.2表中某字段重命名 ALTER TABLE fdrkftcode.admin ALTER COLUMN usepwd rename to userpwd 5.3表中新增字段 ALTER TABLE fdrkftcode.admin ADD IF NOT EXISTS abc varchar(50) 5.4表中删除字段 ALTER TABLE fdrkftcode.admin DROP COLUMN IF EXISTS abc 5.5查找表中记录 SELECT * from fdrkftcode.admin 5.6往表中插入记录 INSERT INTO fdrkftcode.admin VALUES (1,'管理员','admin','admin','10000000000000000000','2013-05-1 00:12:34',3,'2013-05-1 15:32:57',1,'超过级管理员') 5.7修改表中某记录 UPDATE fdrkftcode.admin SET fdrkftcode.admin.adminname='超级管理员' where fdrkftcode.admin.id=1 5.8删除表中某记录 DELETE FROM fdrkftcode.admin WHERE fdrkftcode.admin.id=1 6.下面是我项目的applicationContext.xml配置方法,大家可以参考下 <?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:p="http://www.springframework.org/schema/p" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd"> <!-- 定义使用C3P0连接池的数据源 --> <bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource"> <!-- 指定连接数据库的JDBC驱动 --> <property name="driverClass"> <value>org.h2.Driver</value> </property> <!-- 连接数据库所用的URL --> <property name="jdbcUrl"> <value>jdbc:h2:tcp://localhost/~/FDRKFTCODE;MODE=MySQL;AUTO_SERVER=TRUE</value> </property> <!-- 连接数据库的用户名 --> <property name="user"> <value>sa</value> </property> <!-- 连接数据库的密码 --> <property name="password"> <value></value> </property> <!-- 设置数据库连接池的最大连接数 --> <property name="maxPoolSize"> <value>50</value> </property> <!-- 设置数据库连接池的最小连接数 --> <property name="minPoolSize"> <value>5</value> </property> <!-- 设置数据库连接池的初始化连接数 --> <property name="initialPoolSize"> <value>5</value> </property> <!-- 设置数据库连接池的连接的最大空闲时间,单位为秒 --> <property name="maxIdleTime"> <value>20</value> </property> </bean> <!-- 定义Hibernate的SessionFactory --> <bean id="sessionFactory" class="org.springframework.orm.hibernate3.LocalSessionFactoryBean"> <!-- 依赖注入上面定义的数据源dataSource --> <property name="dataSource" ref="dataSource"/> <!-- 注册Hibernate的ORM映射文件 --> <property name="mappingResources"> <list> <value>com/sungoal/ORM/Admin.hbm.xml</value> </list> </property> <!-- 设置Hibernate的相关属性 --> <property name="hibernateProperties"> <props> <!-- 设置Hibernate的数据库方言 --> <prop key="hibernate.dialect">org.hibernate.dialect.H2Dialect</prop> <!-- 设置Hibernate是否在控制台输出SQL语句,开发调试阶段通常设为true --> <prop key="show_sql">true</prop> <!-- 设置Hibernate一个提交批次中的最大SQL语句数 --> <prop key="hibernate.jdbc.batch_size">50</prop> </props> </property> </bean> <!--定义Hibernate的事务管理器HibernateTransactionManager --> <bean id="transactionManager" class="org.springframework.orm.hibernate3.HibernateTransactionManager"> <!-- 依赖注入上面定义的sessionFactory --> <property name="sessionFactory" ref="sessionFactory"/> </bean> <!-- 装配通用数据库访问类BaseDAOImpl --> <bean id="dao" class="com.sungoal.DAO.BaseDAOImpl"> <!-- 依赖注入上面定义的sessionFactory --> <property name="sessionFactory" ref="sessionFactory"/> </bean> <!-- 部署系统用户管理业务控制器AdminAction --> <bean id="adminAction" class="com.sungoal.struts.action.AdminAction" scope="prototype"> <property name="dao" ref="dao"/> </bean> </beans>
hibernate.properties ###################### ### Query Language ### ###################### ## define query language constants / function names hibernate.query.substitutions yes 'Y', no 'N' ## select the classic query parser #hibernate.query.factory_class org.hibernate.hql.classic.ClassicQueryTranslatorFactory ################# ### Platforms ### ################# ## JNDI Datasource #hibernate.connection.datasource jdbc/test #hibernate.connection.username db2 #hibernate.connection.password db2 ## HypersonicSQL hibernate.dialect org.hibernate.dialect.HSQLDialect hibernate.connection.driver_class org.hsqldb.jdbcDriver hibernate.connection.username sa hibernate.connection.password hibernate.connection.url jdbc:hsqldb:./build/db/hsqldb/hibernate #hibernate.connection.url jdbc:hsqldb:hsql://localhost #hibernate.connection.url jdbc:hsqldb:test ## H2 (www.h2database.com) #hibernate.dialect org.hibernate.dialect.H2Dialect #hibernate.connection.driver_class org.h2.Driver #hibernate.connection.username sa #hibernate.connection.password #hibernate.connection.url jdbc:h2:mem:./build/db/h2/hibernate #hibernate.connection.url jdbc:h2:testdb/h2test #hibernate.connection.url jdbc:h2:mem:imdb1 #hibernate.connection.url jdbc:h2:tcp://dbserv:8084/sample; #hibernate.connection.url jdbc:h2:ssl://secureserv:8085/sample; #hibernate.connection.url jdbc:h2:ssl://secureserv/testdb;cipher=AES ## MySQL #hibernate.dialect org.hibernate.dialect.MySQLDialect #hibernate.dialect org.hibernate.dialect.MySQLInnoDBDialect #hibernate.dialect org.hibernate.dialect.MySQLMyISAMDialect #hibernate.connection.driver_class com.mysql.jdbc.Driver #hibernate.connection.url jdbc:mysql:///test #hibernate.connection.username gavin #hibernate.connection.password ## Oracle #hibernate.dialect org.hibernate.dialect.OracleDialect #hibernate.dialect org.hibernate.dialect.Oracle9Dialect #hibernate.connection.driver_class oracle.jdbc.driver.OracleDriver #hibernate.connection.username ora #hibernate.connection.password ora #hibernate.connection.url jdbc:oracle:thin:@localhost:1521:orcl #hibernate.connection.url jdbc:oracle:thin:@localhost:1522:XE ## PostgreSQL #hibernate.dialect org.hibernate.dialect.PostgreSQLDialect #hibernate.connection.driver_class org.postgresql.Driver #hibernate.connection.url jdbc:postgresql:template1 #hibernate.connection.username pg #hibernate.connection.password ## DB2 #hibernate.dialect org.hibernate.dialect.DB2Dialect #hibernate.connection.driver_class com.ibm.db2.jcc.DB2Driver #hibernate.connection.driver_class COM.ibm.db2.jdbc.app.DB2Driver #hibernate.connection.url jdbc:db2://localhost:50000/somename #hibernate.connection.url jdbc:db2:somename #hibernate.connection.username db2 #hibernate.connection.password db2 ## TimesTen #hibernate.dialect org.hibernate.dialect.TimesTenDialect #hibernate.connection.driver_class com.timesten.jdbc.TimesTenDriver #hibernate.connection.url jdbc:timesten:direct:test #hibernate.connection.username #hibernate.connection.password ## DB2/400 #hibernate.dialect org.hibernate.dialect.DB2400Dialect #hibernate.connection.username user #hibernate.connection.password password ## Native driver #hibernate.connection.driver_class COM.ibm.db2.jdbc.app.DB2Driver #hibernate.connection.url jdbc:db2://systemname ## Toolbox driver #hibernate.connection.driver_class com.ibm.as400.access.AS400JDBCDriver #hibernate.connection.url jdbc:as400://systemname ## Derby (not supported!) #hibernate.dialect org.hibernate.dialect.DerbyDialect #hibernate.connection.driver_class org.apache.derby.jdbc.EmbeddedDriver #hibernate.connection.username #hibernate.connection.password #hibernate.connection.url jdbc:derby:build/db/derby/hibernate;create=true ## Sybase #hibernate.dialect org.hibernate.dialect.SybaseDialect #hibernate.connection.driver_class com.sybase.jdbc2.jdbc.SybDriver #hibernate.connection.username sa #hibernate.connection.password sasasa #hibernate.connection.url jdbc:sybase:Tds:co3061835-a:5000/tempdb ## Mckoi SQL #hibernate.dialect org.hibernate.dialect.MckoiDialect #hibernate.connection.driver_class com.mckoi.JDBCDriver #hibernate.connection.url jdbc:mckoi:/// #hibernate.connection.url jdbc:mckoi:local://C:/mckoi1.0.3/db.conf #hibernate.connection.username admin #hibernate.connection.password nimda ## SAP DB #hibernate.dialect org.hibernate.dialect.SAPDBDialect #hibernate.connection.driver_class com.sap.dbtech.jdbc.DriverSapDB #hibernate.connection.url jdbc:sapdb://localhost/TST #hibernate.connection.username TEST #hibernate.connection.password TEST #hibernate.query.substitutions yes 'Y', no 'N' ## MS SQL Server #hibernate.dialect org.hibernate.dialect.SQLServerDialect #hibernate.connection.username sa #hibernate.connection.password sa ## JSQL Driver #hibernate.connection.driver_class com.jnetdirect.jsql.JSQLDriver #hibernate.connection.url jdbc:JSQLConnect://1E1/test ## JTURBO Driver #hibernate.connection.driver_class com.newatlanta.jturbo.driver.Driver #hibernate.connection.url jdbc:JTurbo://1E1:1433/test ## WebLogic Driver #hibernate.connection.driver_class weblogic.jdbc.mssqlserver4.Driver #hibernate.connection.url jdbc:weblogic:mssqlserver4:1E1:1433 ## Microsoft Driver (not recommended!) #hibernate.connection.driver_class com.microsoft.jdbc.sqlserver.SQLServerDriver #hibernate.connection.url jdbc:microsoft:sqlserver://1E1;DatabaseName=test;SelectMethod=cursor ## The New Microsoft Driver #hibernate.connection.driver_class com.microsoft.sqlserver.jdbc.SQLServerDriver #hibernate.connection.url jdbc:sqlserver://localhost ## jTDS (since version 0.9) #hibernate.connection.driver_class net.sourceforge.jtds.jdbc.Driver #hibernate.connection.url jdbc:jtds:sqlserver://1E1/test ## Interbase #hibernate.dialect org.hibernate.dialect.InterbaseDialect #hibernate.connection.username sysdba #hibernate.connection.password masterkey ## DO NOT specify hibernate.connection.sqlDialect ## InterClient #hibernate.connection.driver_class interbase.interclient.Driver #hibernate.connection.url jdbc:interbase://localhost:3060/C:/firebird/test.gdb ## Pure Java #hibernate.connection.driver_class org.firebirdsql.jdbc.FBDriver #hibernate.connection.url jdbc:firebirdsql:localhost/3050:/firebird/test.gdb ## Pointbase #hibernate.dialect org.hibernate.dialect.PointbaseDialect #hibernate.connection.driver_class com.pointbase.jdbc.jdbcUniversalDriver #hibernate.connection.url jdbc:pointbase:embedded:sample #hibernate.connection.username PBPUBLIC #hibernate.connection.password PBPUBLIC ## Ingres ## older versions (before Ingress 2006) #hibernate.dialect org.hibernate.dialect.IngresDialect #hibernate.connection.driver_class ca.edbc.jdbc.EdbcDriver #hibernate.connection.url jdbc:edbc://localhost:II7/database #hibernate.connection.username user #hibernate.connection.password password ## Ingres 2006 or later #hibernate.dialect org.hibernate.dialect.IngresDialect #hibernate.connection.driver_class com.ingres.jdbc.IngresDriver #hibernate.connection.url jdbc:ingres://localhost:II7/database;CURSOR=READONLY;auto=multi #hibernate.connection.username user #hibernate.connection.password password ## Mimer SQL #hibernate.dialect org.hibernate.dialect.MimerSQLDialect #hibernate.connection.driver_class com.mimer.jdbc.Driver #hibernate.connection.url jdbc:mimer:multi1 #hibernate.connection.username hibernate #hibernate.connection.password hibernate ## InterSystems Cache #hibernate.dialect org.hibernate.dialect.Cache71Dialect #hibernate.connection.driver_class com.intersys.jdbc.CacheDriver #hibernate.connection.username _SYSTEM #hibernate.connection.password SYS #hibernate.connection.url jdbc:Cache://127.0.0.1:1972/HIBERNATE ################################# ### Hibernate Connection Pool ### ################################# hibernate.connection.pool_size 1 ########################### ### C3P0 Connection Pool### ########################### #hibernate.c3p0.max_size 2 #hibernate.c3p0.min_size 2 #hibernate.c3p0.timeout 5000 #hibernate.c3p0.max_statements 100 #hibernate.c3p0.idle_test_period 3000 #hibernate.c3p0.acquire_increment 2 #hibernate.c3p0.validate false ############################## ### Proxool Connection Pool### ############################## ## Properties for external configuration of Proxool hibernate.proxool.pool_alias pool1 ## Only need one of the following #hibernate.proxool.existing_pool true #hibernate.proxool.xml proxool.xml #hibernate.proxool.properties proxool.properties ################################# ### Plugin ConnectionProvider ### ################################# ## use a custom ConnectionProvider (if not set, Hibernate will choose a built-in ConnectionProvider using hueristics) #hibernate.connection.provider_class org.hibernate.connection.DriverManagerConnectionProvider #hibernate.connection.provider_class org.hibernate.connection.DatasourceConnectionProvider #hibernate.connection.provider_class org.hibernate.connection.C3P0ConnectionProvider #hibernate.connection.provider_class org.hibernate.connection.ProxoolConnectionProvider ####################### ### Transaction API ### ####################### ## Enable automatic flush during the JTA beforeCompletion() callback ## (This setting is relevant with or without the Transaction API) #hibernate.transaction.flush_before_completion ## Enable automatic session close at the end of transaction ## (This setting is relevant with or without the Transaction API) #hibernate.transaction.auto_close_session ## the Transaction API abstracts application code from the underlying JTA or JDBC transactions #hibernate.transaction.factory_class org.hibernate.transaction.JTATransactionFactory #hibernate.transaction.factory_class org.hibernate.transaction.JDBCTransactionFactory ## to use JTATransactionFactory, Hibernate must be able to locate the UserTransaction in JNDI ## default is java:comp/UserTransaction ## you do NOT need this setting if you specify hibernate.transaction.manager_lookup_class #jta.UserTransaction jta/usertransaction #jta.UserTransaction javax.transaction.UserTransaction #jta.UserTransaction UserTransaction ## to use the second-level cache with JTA, Hibernate must be able to obtain the JTA TransactionManager #hibernate.transaction.manager_lookup_class org.hibernate.transaction.JBossTransactionManagerLookup #hibernate.transaction.manager_lookup_class org.hibernate.transaction.WeblogicTransactionManagerLookup #hibernate.transaction.manager_lookup_class org.hibernate.transaction.WebSphereTransactionManagerLookup #hibernate.transaction.manager_lookup_class org.hibernate.transaction.OrionTransactionManagerLookup #hibernate.transaction.manager_lookup_class org.hibernate.transaction.ResinTransactionManagerLookup ############################## ### Miscellaneous Settings ### ############################## ## print all generated SQL to the console #hibernate.show_sql true ## format SQL in log and console hibernate.format_sql true ## add comments to the generated SQL #hibernate.use_sql_comments true ## generate statistics #hibernate.generate_statistics true ## auto schema export #hibernate.hbm2ddl.auto create-drop #hibernate.hbm2ddl.auto create #hibernate.hbm2ddl.auto update #hibernate.hbm2ddl.auto validate ## specify a default schema and catalog for unqualified tablenames #hibernate.default_schema test #hibernate.default_catalog test ## enable ordering of SQL UPDATEs by primary key #hibernate.order_updates true ## set the maximum depth of the outer join fetch tree hibernate.max_fetch_depth 1 ## set the default batch size for batch fetching #hibernate.default_batch_fetch_size 8 ## rollback generated identifier values of deleted entities to default values #hibernate.use_identifer_rollback true ## enable bytecode reflection optimizer (disabled by default) #hibernate.bytecode.use_reflection_optimizer true ##################### ### JDBC Settings ### ##################### ## specify a JDBC isolation level #hibernate.connection.isolation 4 ## enable JDBC autocommit (not recommended!) #hibernate.connection.autocommit true ## set the JDBC fetch size #hibernate.jdbc.fetch_size 25 ## set the maximum JDBC 2 batch size (a nonzero value enables batching) #hibernate.jdbc.batch_size 5 #hibernate.jdbc.batch_size 0 ## enable batch updates even for versioned data hibernate.jdbc.batch_versioned_data true ## enable use of JDBC 2 scrollable ResultSets (specifying a Dialect will cause Hibernate to use a sensible default) #hibernate.jdbc.use_scrollable_resultset true ## use streams when writing binary types to / from JDBC hibernate.jdbc.use_streams_for_binary true ## use JDBC 3 PreparedStatement.getGeneratedKeys() to get the identifier of an inserted row #hibernate.jdbc.use_get_generated_keys false ## choose a custom JDBC batcher # hibernate.jdbc.factory_class ## enable JDBC result set column alias caching ## (minor performance enhancement for broken JDBC drivers) # hibernate.jdbc.wrap_result_sets ## choose a custom SQL exception converter #hibernate.jdbc.sql_exception_converter ########################## ### Second-level Cache ### ########################## ## optimize chache for minimal "puts" instead of minimal "gets" (good for clustered cache) #hibernate.cache.use_minimal_puts true ## set a prefix for cache region names hibernate.cache.region_prefix hibernate.test ## disable the second-level cache #hibernate.cache.use_second_level_cache false ## enable the query cache #hibernate.cache.use_query_cache true ## store the second-level cache entries in a more human-friendly format #hibernate.cache.use_structured_entries true ## choose a cache implementation #hibernate.cache.provider_class org.hibernate.cache.EhCacheProvider #hibernate.cache.provider_class org.hibernate.cache.EmptyCacheProvider hibernate.cache.provider_class org.hibernate.cache.HashtableCacheProvider #hibernate.cache.provider_class org.hibernate.cache.TreeCacheProvider #hibernate.cache.provider_class org.hibernate.cache.OSCacheProvider #hibernate.cache.provider_class org.hibernate.cache.SwarmCacheProvider ## choose a custom query cache implementation #hibernate.cache.query_cache_factory ############ ### JNDI ### ############ ## specify a JNDI name for the SessionFactory #hibernate.session_factory_name hibernate/session_factory ## Hibernate uses JNDI to bind a name to a SessionFactory and to look up the JTA UserTransaction; ## if hibernate.jndi.* are not specified, Hibernate will use the default InitialContext() which ## is the best approach in an application server #file system #hibernate.jndi.class com.sun.jndi.fscontext.RefFSContextFactory #hibernate.jndi.url file:/ #WebSphere #hibernate.jndi.class com.ibm.websphere.naming.WsnInitialContextFactory #hibernate.jndi.url iiop://localhost:900/
摘要: 这几天学习了一下Spring Security3.1,从官网下载了Spring Security3.1版本进行练习,经过多次尝试才摸清了其中的一些原理。本人不才,希望能帮助大家。还有,这次我第二次写博客啊,文体不是很行。希望 能让观看者不产生疲惫的感觉,我已经心满意足了。 一、数据库结构 先来看一下数据库结构,采用的是基于角色-资源-用户的权限... 阅读全文
我们知道,EXT的全部js是比较大的,一个ext-all-debug.js就达2m多,它的压缩版(去掉js中的换行及空格),也达600多k,这对于在网速不太快的时,下载js就得漫长的等待。 JOffice中的日历任务控件,js多达四五个,每个js大小都达70多k,尽管我们采用了后加载的方式,则当用户点击我的任务功能时,才下载该js,但这样仍然很慢,因为下载的js很慢 ,鉴于此,在互联网上使用类似Joffice类似的程序,速度会使很多开发商不敢选用ext作为开发技术。据本人当时参与移动一个内部采购平台的开发,就是因为其运行程序慢,遭到移动的终端用户的弃骂, 所以,要想用EXT来开发应用,需要解决其运行慢的特点。
我们可以从以下几种方法来提高应用程序的运行速度:
一.前期尽量少加载js.
这点在Joffice中有比较好的运用,采用的是由ScriptMgr.load方法来完成,加载完成后,其会在body中插入一个div,只要当前页面不被刷新,下次再访问该功能时,不需要再加载js function $ImportJs(viewName,callback) { var b = document.getElementById(viewName+'-hiden'); if (b != null) { var view = eval('new ' + viewName + '()'); callback.call(this, view); } else { var jsArr = eval('App.importJs.' + viewName); if(jsArr==undefined){ var view = eval('new ' + viewName + '()'); callback.call(this, view); return ; } ScriptMgr.load({ scripts : jsArr, callback : function() { Ext.DomHelper.append(document.body,"<div id='" + viewName + "-hiden' style='display:none'></div>"); var view = eval('new ' + viewName + '()'); callback.call(this, view); } }); } } package com.htsoft.core.web.filter; import java.io.IOException; import java.util.HashMap; import java.util.Iterator; import java.util.Map; import javax.servlet.Filter; import javax.servlet.FilterChain; import javax.servlet.FilterConfig; import javax.servlet.ServletException; import javax.servlet.ServletRequest; import javax.servlet.ServletResponse; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; public class GzipJsFilter implements Filter { Map headers = new HashMap(); public void destroy() { } public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws IOException, ServletException { if(req instanceof HttpServletRequest) { doFilter((HttpServletRequest)req, (HttpServletResponse)res, chain); }else { chain.doFilter(req, res); } } public void doFilter(HttpServletRequest request, HttpServletResponse response, FilterChain chain) throws IOException, ServletException { request.setCharacterEncoding("UTF-8"); for(Iterator it = headers.entrySet().iterator();it.hasNext();) { Map.Entry entry = (Map.Entry)it.next(); response.addHeader((String)entry.getKey(),(String)entry.getValue()); } chain.doFilter(request, response); } public void init(FilterConfig config) throws ServletException { String headersStr = config.getInitParameter("headers"); String[] headers = headersStr.split(","); for(int i = 0; i < headers.length; i++) { String[] temp = headers[i].split("="); this.headers.put(temp[0].trim(), temp[1].trim()); } } }
3.在WEB.xml 文件中,添加以下配置: <filter> <filter-name>GzipJsFilter</filter-name> <filter-class>com.htsoft.core.web.filter.GzipJsFilter</filter-class> <init-param> <param-name>headers</param-name> <param-value>Content-Encoding=gzip</param-value> </init-param> </filter> <filter-mapping> <filter-name>GzipJsFilter</filter-name> <url-pattern>*.gzjs</url-pattern> lt;/filter-mapping> <servlet-mapping>
4.在index.jsp中引入该压缩文件: <script type="text/javascript" src="<%=request.getContextPath()%>/ext3/ext-all.gzjs"></script> 可以看到浏览器解压后,其代码是一样的: 大家可以看到以上,这块是在外网使用的,其速度是比较快的。当然,浏览器解压这个文件需要一点时间,不过在本地解压是非常快的,可以不用管。 三、通过Js缓存,更加可以提高EXT的加载速度,关于缓存,本文不作讨论。 原文出自: http://man1900.iteye.com/blog/515058
我们常常在代码中读取一些资源文件(比如图片,音乐,文本等等)。在单独运行的时候这些简单的处理当然不会有问题。但是,如果我们把代码打成一个jar包以后,即使将资源文件一并打包,这些东西也找不出来了。看看下面的代码: //源代码1: package edu.hxraid; import java.io.*; public class Resource { public void getResource() throws IOException{ File file=new File("bin/resource/res.txt"); BufferedReader br=new BufferedReader(new FileReader(file)); String s=""; while((s=br.readLine())!=null) System.out.println(s); } }
这段代码写在Eclipse建立的java Project中,其目录为:(其中将资源文件res.txt放在了bin目录下,以便打成jar包) 1、src/ src/edu/hxraid/Resource.java 2、bin/ bin/resource/res.txt bin/edu/hxraid/Resource.class 很显然运行源代码1是能够找到资源文件res.txt。但当我们把整个工程打成jar包以后(ResourceJar.jar),这个jar包内的目录为: edu/hxraid/Resource.class resource/res.txt 而这时jar包中Resource.class字节 码:ldc <String "bin/resource/res.txt"> [20] 将无法定位到jar包中的res.txt位置上。就算把bin/目录去掉:ldc <String "resource/res.txt"> [20] 仍然无法定位到jar包中res.txt上。 这主要是因为jar包是一个单独的文件而非文件夹,绝对不可能通过"file:/e:/.../ResourceJar.jar/resource /res.txt"这种形式的文件URL来定位res.txt。所以即使是相对路径,也无法定位到jar文件内的txt文件(读者也许对这段原因解释有些费解,在下面我们会用一段代码运行的结果来进一步阐述)。 那么把资源打入jar包,无论ResourceJar.jar在系统的什么路径下,jar包中的字节码程序都可以找到该包中的资源。这会是幻想吗? 当然不是,我们可以用类装载器(ClassLoader)来做到这一点: (1) ClassLoader 是类加载器的抽象类。它可以在运行时动态的获取加载类的运行信息。 可以这样说,当我们调用ResourceJar.jar中的Resource类时,JVM加载进Resource类,并记录下Resource运行时信息 (包括Resource所在jar包的路径信息)。而ClassLoader类中的方法可以帮助我们动态的获取这些信息: ● public URL getResource(String name) 查找具有给定名称的资源。资源是可以通过类代码以与代码基无关的方式访问的一些数据(图像、声音、文本等)。并返回资源的URL对象。 ● public InputStream getResourceAsStream(String name); 返回读取指定资源的输入流。这个方法很重要,可以直接获得jar包中文件的内容。 (2) ClassLoader是abstract的,不可能实例化对象,更加不可能通过ClassLoader调用上面两个方法。所以我们真正写代码的时候,是通过Class类中的getResource()和getResourceAsStream()方法,这两个方法会委托ClassLoader中的getResource()和getResourceAsStream()方法 。好了,现在我们重新写一段Resource代码,来看看上面那段费解的话是什么意思了: //源代码2: package edu.hxraid; import java.io.*; import java.net.URL; public class Resource { public void getResource() throws IOException{ //查找指定资源的URL,其中res.txt仍然开始的bin目录下 URL fileURL=this.getClass().getResource("/resource/res.txt"); System.out.println(fileURL.getFile()); } public static void main(String[] args) throws IOException { Resource res=new Resource(); res.getResource(); } } 运行这段源代码结果:/E:/Code_Factory/WANWAN/bin/resource/res.txt (../ Code_Factory/WANWAN/.. 是java project所在的路径) 我们将这段代码打包成ResourceJar.jar ,并将ResourceJar.jar放在其他路径下(比如 c:\ResourceJar.jar)。然后另外创建一个java project并导入ResourceJar.jar,写一段调用jar包中Resource类的测试代码: import java.io.IOException; import edu.hxraid.Resource; public class TEST { public static void main(String[] args) throws IOException { Resource res=new Resource(); res.getResource(); } } 这时的运行结果是:file:/C:/ResourceJar.jar!/resource/res.txt 我们成功的在运行时动态获得了res.txt的位置。然而,问题来了,你是否可以通过下面这样的代码来得到res.txt文件? File f=new File("C:/ResourceJar.jar!/resource/res.txt"); 当然不可能,因为".../ResourceJar.jar!/resource/...."并不是文件资源定位符的格式 (jar中资源有其专门的URL形式: jar:<url>!/{entry} )。所以,如果jar包中的类源代码用File f=new File(相对路径);的形式,是不可能定位到文件资源的。这也是为什么源代码1打包成jar文件后,调用jar包时会报出FileNotFoundException的症结所在了。 (3) 我们不能用常规操作文件的方法来读取ResourceJar.jar中的资源文件res.txt,但可以通过Class类的getResourceAsStream()方法来获取 ,这种方法是如何读取jar中的资源文件的,这一点对于我们来说是透明的。我们将Resource.java改写成: //源代码3: package edu.hxraid; import java.io.*; public class Resource { public void getResource() throws IOException{ //返回读取指定资源的输入流 InputStream is=this.getClass().getResourceAsStream("/resource/res.txt"); BufferedReader br=new BufferedReader(new InputStreamReader(is)); String s=""; while((s=br.readLine())!=null) System.out.println(s); } } 我们将java工程下/bin目录中的edu/hxraid /Resource.class和资源文件resource/res.txt一并打包进ResourceJar.jar中,不管jar包在系统的任何目录 下,调用jar包中的Resource类都可以获得jar包中的res.txt资源,再也不会找不到res.txt文件了。 原文出自: http://www.iteye.com/topic/483115
编辑代码常用快捷键 格式化代码的快捷键 Ctrl + Shift + F 格式化缩进的快捷键是 Ctrl + I,只能对选中的文本进行缩进 删除一行的快捷键是 Ctrl + D 当前窗口最大化最小化切换 Ctrl + M 转到最后进行修改的位置 Ctrl + Q 快速查找选中的字符 Ctrl + K(向下) Ctrl + Shift + K(向上) 光标放到一个括号,切换到另一个成对的括号 Ctrl + Shirt + P 在编辑过的位置进行切换 Alt + 左右方向键 阅读代码常用的快捷键 F3不解释(一些人喜欢用Ctrl + 鼠标左键) 选中方法或者变量 Ctrl + Alt + H,查找在哪些地方调用,快速阅读代码和评估代码修改必须要用到的 继承关系 F4,了解代码的框架 快速查找函数和变量 Ctrl + O,输入函数或变量的名字,比在Outline中一个一个找要快很多,但是要对代码有了解 全工程查找 Ctrl + H,代码巨多的情况下必不可少。 由于水平有限,笔者只用到了这些快捷键 如果想知道其他的快捷键 Ctrl + Shift + L 自定义格式化代码 在Preference中打开Java的Format 内建的模版是不能修改的,点击New...,随便输入一个名字,新建一个自己的模板,弹出自定义Edit窗口 可以定义的项目非常丰富,在右边还可以进行预览,就算对英文不感冒,也可以捉摸出大致的意思。对代码进行格式化的好处是不仅仅是美观,便于阅读,在 进行团队开发的时候,使用统一的格式,在合并代码的时候可以避免许多的冲突。修改完成的模版就是使用Ctrl + Shift + F格式化时的模板 自动去除无用的import,自动补全@Override和@deprecated,eclipse的Clean up 在Code Style中,除了Format还有Clean Up 和Format进行同样的操作,新建一个模板,有几个地方我决定有必要改一下 在Code Organizing标签选择Remove trailing whitespace(移除尾部的空格) 和Organzie imports 选择Organzie imports前效果 选择Organzie imports后效果 切换到Code Style标签 Use blocks in if/while/for/do statements 为if/while/for/do自动添加括号,这个因人而异,我决定即使只用一行,也应该添加括号。 点击菜单里的Source - Clean up,可以对代码进行清理,清理代码最大的好处是——移除没用的import,自动添加@Deprecated和@Override 特别是自动添加@Override,可以很清楚的明白那些函数是继承的。 代码提示 用过visual assistx的一定非常系统它的代码提示功能,我是个没有代码提示就无法Coding的人,点击菜单Windows-Preference,切换到以下窗口 找到Auto Activation,也就是红色方框中的部分,将Auto activation delay(ms): 修改为 50 将Auto activation triggers for Java:修改为 .abcdefghigklmnoprstuvwxyz,这样就能随时提示了。 最后介绍两个工具,Search Everything 根据文件名快速查找文件,ClipX剪贴板历史记录。 原文出自:http://www.cnblogs.com/sw926/p/3209615.html
1 @Entity 2 @Table(name="T_ADM_USER") 3 public class User extends GenericEntity implements Serializable { 4 5 @OneToOne(cascade = CascadeType.PERSIST) 6 @JoinColumn(name="grade_id") 7 public Grade getGrade() { 8 return grade; 9 } 10 public void setGrade(Grade grade) { 11 this.grade = grade; 12 } 13 @Column(name="user_name", insertable = true, updatable = true, nullable = true) 14 public String getUsername() { 15 return username; 16 } 17 public void setUsername(String username) { 18 this.username = username; 19 } 20 21 }
1 public class TableUtil { 2 3 public static void main(String[] args) { 4 try{ 5 6 AnnotationConfiguration cfg = new AnnotationConfiguration(); 7 cfg.addAnnotatedClass(com.mygogo.grade.user.entity.User.class); 9 SchemaExport se = new SchemaExport(cfg); 10 se.setDelimiter(";"); 11 se.drop(true, true); 12 se.create(true, true); 13 }catch(Exception e){ 14 e.printStackTrace(); 15 } 16 17 } 18 }
Servlet和Filter的url匹配以及url-pattern详解 Servlet和filter是J2EE开发中常用的技术,使用方便,配置简单,老少皆宜。估计大多数朋友都是直接配置用,也没有关心过具体的细节,今天 遇到一个问题,上网查了servlet的规范才发现,servlet和filter中的url-pattern还是有一些文章在里面的,总结了一些东西, 放出来供大家参考,以免遇到问题又要浪费时间。一 servlet容器对url的匹配过当一个请求发送到servlet容器的时候,容器先会将请求的url减去当前应用上下文的路径作为servlet的映射url,比如我访问的是http://localhost/test/aaa.html,我的应用上下文是test,容器会将http://localhost/test去掉,剩下的/aaa.html部分拿来做servlet的映射匹配。这个映射匹配过程是有顺序的,而且当有一个servlet匹配成功以后,就不会去理会剩下的servlet了(filter不同,后文会提到)。其匹配规则和顺序如下:<o:p></o:p>
1. 精确路径匹配。例子:比如servletA 的url-pattern为 /test,servletB的url-pattern为 /* ,这个时候,如果我访问的url为http://localhost/test ,这个时候容器就会先进行精确路径匹配,发现/test正好被servletA精确匹配,那么就去调用servletA,也不会去理会其他的servlet了。<o:p></o:p>
2. 最长路径匹配。例子:servletA的url-pattern为/test/*,而servletB的url-pattern为/test/a/*,此时访问http://localhost/test/a时,容器会选择路径最长的servlet来匹配,也就是这里的servletB。<o:p></o:p>
3. 扩展匹配,如果url最后一段包含扩展,容器将会根据扩展选择合适的servlet。例子:servletA的url-pattern:*.action<o:p></o:p>
4. 如果前面三条规则都没有找到一个servlet,容器会根据url选择对应的请求资源。如果应用定义了一个default servlet,则容器会将请求丢给default servlet(什么是default servlet?后面会讲)。
根据这个规则表,就能很清楚的知道servlet的匹配过程,所以定义servlet的时候也要考虑url-pattern的写法,以免出错。 对于filter,不会像servlet那样只匹配一个servlet,因为filter的集合是一个链,所以只会有处理的顺序不同,而不会出现只选择一 个filter。Filter的处理顺序和filter-mapping在web.xml中定义的顺序相同。 二 url-pattern详解- 在web.xml文件中,以下语法用于定义映射:
- 以”/’开头和以”/*”结尾的是用来做路径映射的。
- 以前缀”*.”开头的是用来做扩展映射的。
- “/” 是用来定义default servlet映射的。
- 剩下的都是用来定义详细映射的。比如: /aa/bb/cc.action
所以,为什么定义”/*.action”这样一个看起来很正常的匹配会错?因为这个匹配即属于路径映射,也属于扩展映射,导致容器无法判断 出自: http://foxty.iteye.com/blog/39332
oracle监听不能启动的问题及处理过程! oracle环境如下: SQL> select * from V$version 2 ; BANNER ---------------------------------------------------------------- Oracle Database 10g Enterprise Edition Release 10.2.0.1.0 - Prod PL/SQL Release 10.2.0.1.0 - Production CORE 10.2.0.1.0 Production TNS for 32-bit Windows: Version 10.2.0.1.0 - Production NLSRTL Version 10.2.0.1.0 - Production 出错过程回忆:在此之前用windows优化大师对系统注册表进行了修复! Q:链接oracle时报错:ORA-12541: TNS: 无监听程序 A: 1〉查看监听有没有启动: 一:运行lsnrctl C:\Documents and Settings\Admin>lsnrctl LSNRCTL for 32-bit Windows: Version 10.2.0.1.0 - Production on 20-4月 -2007 09: 1:43 Copyright (c) 1991, 2005, Oracle. All rights reserved. 欢迎来到LSNRCTL, 请键入"help"以获得信息。 二:查看stauts LSNRCTL> status 正在连接到 (DESCRIPTION=(ADDRESS=(PROTOCOL=IPC)(KEY=EXTPROC1))) TNS-12541: TNS: 无监听程序 TNS-12560: TNS: 协议适配器错误 TNS-00511: 无监听程序 32-bit Windows Error: 2: No such file or directory 正在连接到 (DESCRIPTION=(ADDRESS=(PROTOCOL=TCP)(HOST=zxt)(PORT=1521))) TNS-12541: TNS: 无监听程序 TNS-12560: TNS: 协议适配器错误 TNS-00511: 无监听程序 32-bit Windows Error: 61: Unknown error 三:发现监听没有启动,现在启动监听 LSNRCTL> start 启动tnslsnr: 请稍候... Failed to start service, error 3. TNS-12560: TNS: 协议适配器错误 TNS-00530: 协议适配器错误 2〉发现监听启动不起来,估计是注册表有点问题,登录注册表 C:\Documents and Settings\Admin>regedit 进入注册表到HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\OracleOraDb10g_home1TNSListener 发现ImagePath关键值没有了,增加可扩充字符串值,取名为ImagePathImagePath,编辑字符串的数值数据为:G:\oracle\product\10.2.0\db_2\BIN\TNSLSNR(对应oracle的TNSLSNR的位置) ,退出注册表。 3〉启动监听 LSNRCTL> start 启动tnslsnr: 请稍候... TNSLSNR for 32-bit Windows: Version 10.2.0.1.0 - Production 系统参数文件为d:\oracle\product\10.2.0\db_1\network\admin\listener.ora 写入d:\oracle\product\10.2.0\db_1\network\log\listener.log的日志信息 监听: (DESCRIPTION=(ADDRESS=(PROTOCOL=ipc)(PIPENAME=\\.\pipe\EXTPROC1ipc))) 监听: (DESCRIPTION=(ADDRESS=(PROTOCOL=tcp)(HOST=zxt)(PORT=1521))) 正在连接到 (DESCRIPTION=(ADDRESS=(PROTOCOL=IPC)(KEY=EXTPROC1))) LISTENER 的 STATUS ------------------------ 别名 LISTENER 版本 TNSLSNR for 32-bit Windows: Version 10.2.0.1.0 - Produ ction 启动日期 20-4月 -2007 09:41:29 正常运行时间 0 天 0 小时 0 分 3 秒 跟踪级别 off 安全性 ON: Local OS Authentication SNMP OFF 监听程序参数文件 d:\oracle\product\10.2.0\db_1\network\admin\listener.o ra 监听程序日志文件 d:\oracle\product\10.2.0\db_1\network\log\listener.log 监听端点概要... (DESCRIPTION=(ADDRESS=(PROTOCOL=ipc)(PIPENAME=\\.\pipe\EXTPROC1ipc))) (DESCRIPTION=(ADDRESS=(PROTOCOL=tcp)(HOST=zxt)(PORT=1521))) 服务摘要.. 服务 "PLSExtProc" 包含 1 个例程。 例程 "PLSExtProc", 状态 UNKNOWN, 包含此服务的 1 个处理程序... 命令执行成功 4>监听启动成功后尝试登录 C:\Documents and Settings\Admin>sqlplus zxt@orcl SQL*Plus: Release 10.2.0.1.0 - Production on 星期五 4月 20 09:42:33 2007 Copyright (c) 1982, 2005, Oracle. All rights reserved. 输入口令: 连接到: Oracle Database 10g Enterprise Edition Release 10.2.0.1.0 - Production With the Partitioning, OLAP and Data Mining options 登录成功! 总结:估计是windows优化大师或者别的工具在修复注册表时候删掉了ImagePath字段,补上后就可以了! 补充:登录sqlplus时报 ORA-12514: TNS: 监听程序当前无法识别连接描述符中请求的服务错误! 处理办法: 1〉oracle_home\NETWORK\ADMIN\tnsnames.ora中修改(ADDRESS = (PROTOCOL = TCP)(HOST = localhost)(PORT = 1521)) 为(ADDRESS = (PROTOCOL = TCP)(HOST = 本地计算机名)(PORT = 1521)),保存即可。 2〉有的人介绍oracle_home\NETWORK\ADMIN\sqlnet.ora中修改SQLNET.AUTHENTICATION_SERVICES = (NTS)为 SQLNET.AUTHENTICATION_SERVICES = (NONE) 这2种方法,第一个最佳!
Ext GridPanel实现复选框选择框: 1 var selectModel = new Ext.grid.CheckboxSelectionModel({ 2 singleSelect : false 3 }); 4 但是这样每一行都会有复选框,如果需求为:某行数据的某个列满足什么条件我才有复选框选项就不太好实现了,
这样就需要重写Ext.grid.CheckboxSelectionModel的渲染,行点击涵数来实现. 1 var selectModel = new Ext.grid.CheckboxSelectionModel({ 2 singleSelect : false, 3 renderer : function(v, p, record){ 4 if (record.data['结果状态'] == '0'){ 5 return ''; 6 } 7 return '<div class="x-grid3-row-checker"> </div>'; 8 }, 9 onHdMouseDown : function(e, t) { 10 if (t.className == 'x-grid3-hd-checker') { 11 e.stopEvent(); 12 var hd = Ext.fly(t.parentNode); 13 var isChecked = hd.hasClass('x-grid3-hd-checker-on'); 14 if (isChecked){ 15 hd.removeClass('x-grid3-hd-checker-on'); 16 this.clearSelections(); 17 }else { 18 hd.addClass('x-grid3-hd-checker-on'); 19 if (this.locked){ 20 return; 21 } 22 this.selections.clear(); 23 for (var i = 0, len = this.grid.store.getCount(); i < len; i++ ){ 24 if (this.grid.store.getAt(i).data["结果状态"] != '0'){ 25 this.selectRow(i, true); 26 } 27 } 28 } 29 } 30 }, 31 handleMouseDown : function(g, rowIndex, e){ 32 if (e.button !== 0 || this.isLocked()) { 33 return; 34 } 35 var view = this.grid.getView(); 36 if (e.shiftKey && !this.singleSelect && this.last != false ) { 37 var last = this.last; 38 this.selectRange(last, rowIndex, e.ctrlKey); 39 this.last = last; 40 view.focusRow(rowIndex); 41 }else{ 42 var isSelected = this.isSelected(rowIndex); 43 if (e.ctrlKey && isSelected) { 44 this.deselectRow(rowIndex); 45 }else if(!isSelected || this.getCount() > 1){ 46 if(this.grid.store.getAt(rowIndex).data["结果状态"] != '0'){ 47 this.selectRow(rowIndex, e.ctrlKey || e.shiftKey); 48 } 49 view.focusRow(rowIndex); 50 } 51 } 52 } 53 }); 原文: http://fordream.iteye.com/blog/1179252
摘要: 在学习中Ext.grid.EditorGridPanel 的时候碰到一个知识点,如何用复选框来表示真假值,当然我们可以直接这样 Code highlighting produced by Actipro CodeHighlighter (freeware)http://www.CodeHighlighter.com/-->1 {2 heade... 阅读全文
Ext.grid.GridPanel可以设置stripeRows: true的属性来实现隔行换颜色的效果,如果你想自定义每行的颜色,那么你可以按照下边地方法来实现: Ext.ux.GridView=Ext.extend( Ext.grid.GridView, { getRowClass:function(record,index) { if(index%2==0) return 'red'; else return 'green'; } } )
使用自定义的view
var grid = new Ext.grid.GridPanel({ //other code store: store, view:new Ext.ux.GridView(), //other code });
样式定义: .red { background-color:#FF0000; } .green { background-color:#00FF00; }
通过firebug可以看到,给每行的div添加了自定义的样式 原文出自: http://love4j.iteye.com/blog/516007
摘要: 先上图 grid分页: 把grid和page工具绑定在一起 // create the Data Store var store = new Ext.data.Store... 阅读全文
http://yourgame.iteye.com/blog/252853
Ext中的combobox有属性typeAhead:true 可以实现模糊匹配,但是是从开始匹配的,如果需要自定的的匹配,则需要监听beforequery方法,实现自己的匹配查询方法: 代码如下: var gfxmComb = new Ext.form.ComboBox({ id : 'gfxmComb', store : gfxmStore, typeAhead : true, mode : 'local', editable : true, displayField :'xmMc', valueField :'xmBm', triggerAction : 'all', selectOnFocus : true, listeners : { 'beforequery':function(e){ var combo = e.combo; if(!e.forceAll){ var input = e.query; // 检索的正则 var regExp = new RegExp(".*" + input + ".*"); // 执行检索 combo.store.filterBy(function(record,id){ // 得到每个record的项目名称值 var text = record.get(combo.displayField); return regExp.test(text); }); combo.expand(); return false; } } } }); 原文出自:http://weibaojun.iteye.com/blog/1098731
Javascript是一门非常灵活的语言,我们可以随心所欲的书写各种风格的代码,不同风格的代码也必然也会导致执行效率的差异,开发过程中零零散散地接触到许多提高代码性能的方法,整理一下平时比较常见并且容易规避的问题 Javascript自身执行效率 Javascript中的作用域链、闭包、原型继承、eval等特性,在提供各种神奇功能的同时也带来了各种效率问题,用之不慎就会导致执行效率低下。 1、全局导入 我们在编码过程中多多少少会使用到一些全局变量(window,document,自定义全局变量等等),了解javascript作用域链的人都 知道,在局部作用域中访问全局变量需要一层一层遍历整个作用域链直至顶级作用域,而局部变量的访问效率则会更快更高,因此在局部作用域中高频率使用一些全 局对象时可以将其导入到局部作用域中,例如: 1 //1、作为参数传入模块 2 (function(window,$){ 3 var xxx = window.xxx; 4 $("#xxx1").xxx(); 5 $("#xxx2").xxx(); 6 })(window,jQuery); 7 8 //2、暂存到局部变量 9 function(){ 10 var doc = document; 11 var global = window.global; 12 } 2、eval以及类eval问题 我们都知道eval可以将一段字符串当做js代码来执行处理,据说使用eval执行的代码比不使用eval的代码慢100倍以上(具体效率我没有测试,有兴趣同学可以测试一下) JavaScript 代码在执行前会进行类似“预编译”的操作:首先会创建一个当前执行环境下的活动对象,并将那些用 var 申明的变量设置为活动对象的属性,但是此时这些变量的赋值都是 undefined,并将那些以 function 定义的函数也添加为活动对象的属性,而且它们的值正是函数的定义。但是,如果你使用了“eval”,则“eval”中的代码(实际上为字符串)无法预先识 别其上下文,无法被提前解析和优化,即无法进行预编译的操作。所以,其性能也会大幅度降低 其实现在大家一般都很少会用eval了,这里我想说的是两个类eval的场景(new Function{} ,setTimeout ,setInterver ) setTimtout("alert(1)",1000);
setInterver("alert(1)",1000);
(new Function("alert(1)"))(); 上述几种类型代码执行效率都会比较低,因此建议直接传入匿名方法、或者方法的引用给setTimeout方法 3、闭包结束后释放掉不再被引用的变量var f = (function(){ var a = {name:"var3"}; var b = ["var1","var2"]; var c = document.getElementByTagName("li"); //****其它变量 //***一些运算 var res = function(){ alert(a.name); } return res; })() 上述代码中变量f的返回值是由一个立即执行函数构成的闭包中返回的方法res,该变量保留了对于这个闭包中所有变量(a,b,c等)的引用,因此这两个变 量会一直驻留在内存空间中,尤其是对于dom元素的引用对内存的消耗会很大,而我们在res中只使用到了a变量的值,因此,在闭包返回前我们可以将其它变 量释放 var f = (function(){ var a = {name:"var3"}; var b = ["var1","var2"]; var c = document.getElementByTagName("li"); //****其它变量 //***一些运算 //闭包返回前释放掉不再使用的变量 b = c = null; var res = function(){ alert(a.name); } return res; })() 在web开发过程中,前端执行效率的瓶颈往往都是在dom操作上面,dom操作是一件很耗性能的事情,如何才能在dom操作过程中尽量节约性能呢? 1、减少reflow 什么是reflow? 当 DOM 元素的属性发生变化 (如 color) 时, 浏览器会通知 render 重新描绘相应的元素, 此过程称为 repaint。 如果该次变化涉及元素布局 (如 width), 浏览器则抛弃原有属性, 重新计算并把结果传递给 render 以重新描绘页面元素, 此过程称为 reflow。 减少reflow的方法 -
先将元素从document中删除,完成修改后再把元素放回原来的位置(当对某元素及其子元素进行大量reflow操作时,1,2两种方法效果才会比较明显) -
将元素的display设置为”none”,完成修改后再把display修改为原来的值 - 修改多个样式属性时定义class类代替多次修改style属性(for certain同学推荐)
-
大量添加元素到页面时使用documentFragment 例如 for(var i=0;i<100:i++){ var child = docuemnt.createElement("li"); child.innerHtml = "child"; document.getElementById("parent").appendChild(child); } 上述代码会多次操作dom,效率比较低,可以改为下面的形式 创建documentFragment,将所有元素加入到docuemntFragment不会改变dom结构,最后将其添加到页面,只进行了一次reflow var frag = document.createDocumentFragment(); for(var i=0;i<100:i++){ var child = docuemnt.createElement("li"); child.innerHtml = "child"; frag.appendChild(child); } document.getElementById("parent").appendChild(frag); 2、暂存dom状态信息 当代码中需要多次访问元素的状态信息,在状态不变的情况下我们可以将其暂存到变量中,这样可以避免多次访问dom带来内存的开销,典型的例子就是: var lis = document.getElementByTagName("li"); for(var i=1;i<lis.length;i++){ //*** } 上述方式会在每一次循环都去访问dom元素,我们可以简单将代码优化如下 var lis = document.getElementByTagName("li"); for(var i=1,j=lis.length ;i<j;i++){ //*** } 3、缩小选择器的查找范围 查找dom元素时尽量避免大面积遍历页面元素,尽量使用精准选择器,或者指定上下文以缩小查找范围,以jquery为例 - 少用模糊匹配的选择器:例如
$("[name*='_fix']") ,多用诸如id以及逐步缩小范围的复合选择器$("li.active") 等 - 指定上下文:例如
$("#parent .class") ,$(".class",$el) 等 4、使用事件委托 使用场景:一个有大量记录的列表,每条记录都需要绑定点击事件,在鼠标点击后实现某些功能,我们通常的做法是给每条记录都绑定监听事件,这种做法会导致页面会有大量的事件监听器,效率比较低下。 基本原理:我们都知道dom规范中事件是会冒泡的,也就是说在不主动阻止事件冒泡的情况下任何一个元素的事件都 会按照dom树的结构逐级冒泡至顶端。而event对象中也提供了event.target(IE下是srcElement)指向事件源,因此我们即使在 父级元素上监听该事件也可以找到触发该事件的最原始的元素,这就是委托的基本原理。废话不多说,上示例 $("ul li").bind("click",function(){ alert($(this).attr("data")); }) 上述写法其实是给所有的li元素都绑定了click事件来监听鼠标点击每一个元素的事件,这样页面上会有大量的事件监听器。 根据上面介绍的监听事件的原理我们来改写一下 $("ul").bind("click",function(e){ if(e.target.nodeName.toLowerCase() ==="li"){ alert($(e.target).attr("data")); } }) 这样一来,我们就可以只添加一个事件监听器去捕获所有li上触发的事件,并做出相应的操作。 当然,我们不必每次都做事件源的判断工作,可以将其抽象一下交给工具类来完成。jquery中的delegate()方法就实现了该功能 语法是这样的$(selector).delegate(childSelector,event,data,function) ,例如: $("div").delegate("button","click",function(){ $("p").slideToggle(); }); 参数说明(引自w3school) 参数 | 描述 | childSelector | 必需。规定要附加事件处理程序的一个或多个子元素。 | event | 必需。规定附加到元素的一个或多个事件。由空格分隔多个事件值。必须是有效的事件。 | data | 可选。规定传递到函数的额外数据。 | function | 必需。规定当事件发生时运行的函数。 | Tips:事件委托还有一个好处就是,即使在事件绑定之后动态添加的元素上触发的事件同样可以监听到哦,这样就不用在每次动态加入元素到页面后都为其绑定事件了 原文出自: http://www.cnblogs.com/gewei/archive/2013/03/29/2988180.html
摘要: 在Java中,它的内存管理包括两方面:内存分配(创建Java对象的时候)和内存回收,这 两方面工作都是由JVM自动完成的,降低了Java程序员的学习难度,避免了像C/C++直接操作内存的危险。但是,也正因为内存管理完全由JVM负责, 所以也使Java很多程序员不再关心内存分配,导致很多程序低效,耗内存。因此就有了Java程序员到最后应该去了解JVM,才能写出更高效,充分利用有 限的内存的程序。 1... 阅读全文
在设计产品时,要考虑到到很多的因素,其中一个重要的是配色方案。不同的颜色可以使您的产品创造出不同的效果。请务必检查颜色在一般和特定的文化含义,以获得最佳使用效果。 在这篇文章中,我想向大家介绍一些有用的设计工具将帮助您选择合适的调色板为您的设计。这里有15个在线工具分析和收集的色彩组合。
转自:http://www.cnblogs.com/58top/archive/2013/01/15/useful-color-combination-apps-for-designers.html
转: http://www.cnblogs.com/58top/archive/2013/01/11/free-jquery-tooltip-plugins.html
textarea元素已被广泛用于网页Web的IDE。通常网站自带的textarea编辑器不能满足我们的需求,作为一名开发者我们经常需要进行代码的在线编辑,高亮显示代码等,因此,通过其他的开源项目,我们可以添加一些实用的功能, 在这篇文章中,我将使用 JavaScript库 ACE来创建一个输入框效果。这是一个完全开源的脚本。该脚本允许开发人员创建支持语法高亮的输入框。然后你可以代码嵌入到网站中的任何地方 下载库 首先我们需要Github上下载ACE代码。 下载完成后解压缩,在你的header部分引入js文件 <script src="src-min/ace.js" type="text/javascript" charset="utf-8"></script> 添加代码到编辑器 首先设置一个id为editor的div 然后在script里面调用ace.edit()方法,代码如下
var editor = ace.edit("editor"); editor.getSession().setMode("ace/mode/javascript"); 您可以重命名变量,为了方便起见,我定义了var editor作为变量,你也可以定义var demoeditor作为变量 。第二行声明使用哪种类型的语言高亮显示。您可以从 src 目录选择其他语言集合。这里是一些支持支持的语言集合: - SQL
- Ruby
- SASS
- PHP
- Objectivec
- Csharp
- Java
- JSON
使用额外的参数editor.setTheme("ace/theme/dawn"); editor.getSession().setTabSize(2); editor.getSession().setUseWrapMode(true); 这3行代码是关于文本输入效果的,第一行改变代码默认的语法颜色和主题,在src目录下个有几十个新的主题,你可以从中任意选择 另外两个选项是关于用户体验。通常情况下,按一个键盘上的Tab键将输入4个空格,这里我设置成2个空格,此外,该文本在默认情况下将不会自动换行,超了会追加一个水平滚动条向外延伸。但使用这种方法setUseWrapMode(true) ,我们可以修复自动换行的问题。 还有一些其他的命令,你可以参考ACE向导。这里面包含了改变光标的位置,动态添加新内容,或复制的文本的全部内容。 CSS代码 #editor { margin-left: 15px; margin-top: 15px; width: 1000px; height: 400px; } 原文出自: http://www.cnblogs.com/58top/archive/2013/01/28/building-a-syntax-highlighted-input-box-with-javascript.html
Aspect Oriented Programming 面向切面编程。解耦是程序员编码开发过程中一直追求的。AOP也是为了解耦所诞生。 具体思想是:定义一个切面,在切面的纵向定义处理方法,处理完成之后,回到横向业务流。 AOP 在Spring框架中被作为核心组成部分之一,的确Spring将AOP发挥到很强大的功能。最常见的就是事务控制。工作之余,对于使用的工具,不免需要了解其所以然。学习了一下,写了些程序帮助理解。 AOP 主要是利用代理模式的技术来实现的。 1、静态代理:就是设计模式中的proxy模式 a、业务接口 /** * 抽象主题角色:声明了真实主题和代理主题的共同接口。 * * @author yanbin * */ public interface ITalk {
public void talk(String msg);
} b、业务实现 /** * 真实主题角色:定义真实的对象。 * * @author yanbin * */ public class PeopleTalk implements ITalk {
public String username; public String age;
public PeopleTalk(String username, String age) { this.username = username; this.age = age; }
public void talk(String msg) { System.out.println(msg + "!你好,我是" + username + ",我年龄是" + age); }
public String getName() { return username; }
public void setName(String name) { this.username = name; }
public String getAge() { return age; }
public void setAge(String age) { this.age = age; }
} c、代理对象 /** * 代理主题角色:内部包含对真实主题的引用,并且提供和真实主题角色相同的接口。 * * @author yanbin * */ public class TalkProxy implements ITalk {
private ITalk talker;
public TalkProxy(ITalk talker) { // super(); this.talker = talker; }
public void talk(String msg) { talker.talk(msg); }
public void talk(String msg, String singname) { talker.talk(msg); sing(singname); }
private void sing(String singname) { System.out.println("唱歌:" + singname); }
} d、测试类 /** * 代理测试类,使用代理 * * @author yanbin * */ public class ProxyPattern {
public static void main(String[] args) { // 不需要执行额外方法的。 ITalk people = new PeopleTalk("AOP", "18"); people.talk("No ProXY Test"); System.out.println("-----------------------------");
// 需要执行额外方法的(切面) TalkProxy talker = new TalkProxy(people); talker.talk("ProXY Test", "代理"); }
} 从这段代码可以看出来,代理模式其实就是AOP的雏形。 上端代码中talk(String msg, String singname)是一个切面。在代理类中的sing(singname)方法是个后置处理方法。 这样就实现了,其他的辅助方法和业务方法的解耦。业务不需要专门去调用,而是走到talk方法,顺理成章的调用sing方法 再从这段代码看:1、要实现代理方式,必须要定义接口。2、每个业务类,需要一个代理类。 2、动态代理:jdk1.5中提供,利用反射。实现InvocationHandler接口。 业务接口还是必须得,业务接口,业务类同上。 a、代理类: /** * 动态代理类 * * @author yanbin * */ public class DynamicProxy implements InvocationHandler {
/** 需要代理的目标类 */ private Object target;
/** * 写法固定,aop专用:绑定委托对象并返回一个代理类 * * @param delegate * @return */ public Object bind(Object target) { this.target = target; return Proxy.newProxyInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces(), this); }
/** * @param Object * target:指被代理的对象。 * @param Method * method:要调用的方法 * @param Object * [] args:方法调用时所需要的参数 */ @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { Object result = null; // 切面之前执行 System.out.println("切面之前执行"); // 执行业务 result = method.invoke(target, args); // 切面之后执行 System.out.println("切面之后执行"); return result; }
} b、测试类 /** * 测试类 * * @author yanbin * */ public class Test {
public static void main(String[] args) { // 绑定代理,这种方式会在所有的方法都加上切面方法 ITalk iTalk = (ITalk) new DynamicProxy().bind(new PeopleTalk()); iTalk.talk("业务说明"); } } 输出结果会是: 切面之前执行 people talk业务说法 切面之后执行 说明只要在业务调用方法切面之前,是可以动态的加入需要处理的方法。 从代码来看,如果再建立一个业务模块,也只需要一个代理类。ITalk iTalk = (ITalk) new DynamicProxy().bind(new PeopleTalk()); 将业务接口和业务类绑定到动态代理类。 但是这种方式:还是需要定义接口。 3、利用cglib CGLIB是针对类来实现代理的,他的原理是对指定的目标类生成一个子类,并覆盖其中方法实现增强。采用的是继承的方式。不细说,看使用 a、业务类 /** * 业务类 * * @author yanbin * */ public class PeopleTalk {
public void talk(String msg) { System.out.println("people talk" + msg); }
} b、cglib代理类 /** * 使用cglib动态代理 * * @author yanbin * */ public class CglibProxy implements MethodInterceptor {
private Object target;
/** * 创建代理对象 * * @param target * @return */ public Object getInstance(Object target) { this.target = target; Enhancer enhancer = new Enhancer(); enhancer.setSuperclass(this.target.getClass()); // 回调方法 enhancer.setCallback(this); // 创建代理对象 return enhancer.create(); }
@Override public Object intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy) throws Throwable { Object result = null; System.out.println("事物开始"); result = methodProxy.invokeSuper(proxy, args); System.out.println("事物结束"); return result; }
} c.测试类 /** * 测试类 * * @author yanbin * */ public class Test {
public static void main(String[] args) { PeopleTalk peopleTalk = (PeopleTalk) new CglibProxy().getInstance(new PeopleTalk()); peopleTalk.talk("业务方法"); peopleTalk.spreak("业务方法"); }
} 最后输出结果: 事物开始 people talk业务方法 事物结束 事物开始 spreak chinese业务方法 事物结束 由于篇幅有限,这篇主要对AOP的原理简单实现做了演示和阐述,有助自己理解。至于Spring的AOP实现上面无外乎其右,不过实现方面复杂的多。 原文出自: http://www.cnblogs.com/yanbincn/archive/2012/06/01/2530377.html
摘要: 摘要:ComboBox是常用控件之一,但由于其数据来源分两种形式:本地和远程,故写的形式难度并不亚于ExtJS中的TreePanel和 GridPanel。鄙人也经常提醒自己的师弟师妹,ExtJS本身是面向对象写的,不能在应用的时候却不按照面向对象来写,面向对象最起码的好处就是代 码的复用,对于网页来讲,代码复用的好处就是加载的JS会少很多,这样网页渲染时就不会很慢。下面我将分别介绍扩展的四种Co... 阅读全文
首先定义一数据源,一般使用simpleStore,jsonStore。需要注意的是simpleStore用于读取二维数组的数据,而jsonStroe用于读取json数据格式。 Combox使用simpleStore 代码如下所示: var subjectField = new Ext.form.ComboBox({ fieldLabel : '分类名称', hiddenName : 'drug.subjectCode',// 传递到后台的参数 store : new Ext.data.SimpleStore({ autoLoad : true, url :'xxx', fields : ['subjectCode', 'subjectName'] }), valueField : 'subjectCode',// 域的值,对应于store里的fields displayField : 'subjectName',// 显示的域,对应于store里的fields typeAhead : true,// 设置true,完成自动提示 mode : 'local', // 设置local,combox将从本地加载数据 triggerAction : 'all',// 触发此表单域时,查询所有 selectOnFocus : true, anchor : '90%', forceSelection : true });
服务端返回的数据结构如下所示: [ ["00000003","硬膏剂"], ["00000005","滴眼剂"], ["00000016","栓剂"], ["00000017","注射剂"], ["00000018","软膏剂"] ] 当combox使用jsonStore时,一般运用于分页查询。页面效果如下所示: 示例代码如下所示: // 药品商品名 var itemNameField = new Ext.form.ComboBox({ width : 200, fieldLabel : '药品商品名', hiddenName : 'drug.itemName', store : advanceStore, valueField : 'itemName', displayField : 'itemName', typeAhead : true, mode : 'remote',// 分页查询必须设置为 remote,当我们点击下一页的时候是从服务端取数据,而不是本地 triggerAction : 'all', emptyText : '请选择一个分类名', selectOnFocus : true, minChars : 0, // 完成自动提示,当mode为‘local’时,默认为0,当mode为‘remote’时候,默认为4,这里设置为0 pageSize : 10,// 每页显示的记录数字 queryParam :'drug.itemName' // 在combox内敲入字符时候,combox向后台查询传递的参数,这里设置为'drug.itemName'是为了更好的封装,默认传递参数‘query’ });
这里还有一个问题,就是Combox设置初始值。 我是采用如下做法的,不知道各位知不知道其他用法? var subjectField = new Ext.form.ComboBox({ fieldLabel : '分类名称', hiddenName : 'drug.subjectCode', store : new Ext.data.SimpleStore({ autoLoad : true, url : 'xxx', fields : ['subjectCode', 'subjectName'], listeners : { load : function(){ subjectField.setValue(record.get("drug.subjectCode")); } } }), valueField : 'subjectCode', displayField : 'subjectName', typeAhead : true, mode : 'local', triggerAction : 'all', emptyText : '请选择一个分类名', selectOnFocus : true, anchor : '90%', forceSelection : true });
原文出自: http://www.iteye.com/topic/296710
所有的事件回调函数都有两个参数: event和 ui,浏览器自有event对象,和经过封装的ui对象 ui.helper - 表示sortable元素的JQuery对象,通常是当前元素的克隆对象 ui.position - 表示相对当前对象,鼠标的坐标值对象{top,left} ui.offset - 表示相对于当前页面,鼠标的坐标值对象{top,left} ui.item - 表示当前拖拽的元素
ui.placeholder - 占位符(如果有定义的话) ui.sender - 当前拖拽元素的所属sortable对象(仅当元素是从另一个sortable对象传递过来时有用) ·参数(参数名 : 参数类型 : 默认值) appendTo : String : 'parent' Defines where the helper that moves with the mouse is being appended to during the drag (for example, to resolve overlap/zIndex issues). 初始:$('.selector').sortable({ appendTo: 'body' }); 获取:var appendTo = $('.selector').sortable('option', 'appendTo'); 设置:$('.selector').sortable('option', 'appendTo', 'body'); axis : String : false 如果有设置,则元素仅能横向或纵向拖动。可选值:'x', 'y' 初始:$('.selector').sortable({ axis: 'x' }); 获取:var axis = $('.selector').sortable('option', 'axis'); 设置:$('.selector').sortable('option', 'axis', 'x'); cancel : Selector : ':input,button' 阻止排序动作在匹配的元素上发生。 初始:$('.selector').sortable({ cancel: 'button' }); 获取:var cancel = $('.selector').sortable('option', 'cancel'); 设置:$('.selector').sortable('option', 'cancel', 'button'); connectWith : Selector : false 允许sortable对象连接另一个sortable对象,可将item元素拖拽到另一个中。 初始:$('.selector').sortable({ connectWith: '.otherlist' }); 获取:var connectWith = $('.selector').sortable('option', 'connectWith'); 设置:$('.selector').sortable('option', 'connectWith', '.otherlist'); containment : Element, String, Selector : false 约束排序动作只能在一个指定的范围内发生。可选值:DOM对象, 'parent', 'document', 'window', 或jQuery对象 初始:$('.selector').sortable({ containment: 'parent' }); 获取:var containment = $('.selector').sortable('option', 'containment'); 设置:$('.selector').sortable('option', 'containment', 'parent'); cursor : String : 'auto' 定义在开始排序动作时,如果的样式。 初始:$('.selector').sortable({ cursor: 'crosshair' }); 获取:var cursor = $('.selector').sortable('option', 'cursor'); 设置:$('.selector').sortable('option', 'cursor', 'crosshair'); cursorAt : Object : false 当开始移动时,鼠标定位在的某个位置上(最多两个方向)。可选值:{ top, left, right, bottom }. 初始:$('.selector').sortable({ cursorAt: 'top' }); 获取:var cursorAt = $('.selector').sortable('option', 'cursorAt'); 设置:$('.selector').sortable('option', 'cursorAt', 'top'); delay : Integer : 0 以毫秒为单位,设置延迟多久才激活排序动作。此参数可防止误点击。 初始:$('.selector').sortable({ delay: 500 }); 获取:var delay = $('.selector').sortable('option', 'delay'); 设置:$('.selector').sortable('option', 'delay', 500); distance : Integer : 1 决定至少要在元素上面拖动多少像素后,才正式触发排序动作。 初始:$('.selector').sortable({ distance: 30 }); 获取:var distance = $('.selector').sortable('option', 'distance'); 设置:$('.selector').sortable('option', 'distance', 30); dropOnEmpty : Boolean : true 是否允許拖拽到一個空的sortable对象中。 初始:$('.selector').sortable({ dropOnEmpty: false }); 获取:var dropOnEmpty = $('.selector').sortable('option', 'dropOnEmpty'); 设置:$('.selector').sortable('option', 'dropOnEmpty', false); forceHelperSize : Boolean : false If true, forces the helper to have a size. 初始:$('.selector').sortable({ forceHelperSize: true }); 获取:var forceHelperSize = $('.selector').sortable('option', 'forceHelperSize'); 设置:$('.selector').sortable('option', 'forceHelperSize', true); forcePlaceholderSize : Boolean : false If true, forces the placeholder to have a size. 初始:$('.selector').sortable({ forcePlaceholderSize: true }); 获取:var forcePlaceholderSize = $('.selector').sortable('option', 'forcePlaceholderSize'); 设置:$('.selector').sortable('option', 'forcePlaceholderSize', true); grid : Array : false 将排序对象的item元素视为一个格子处理,每次移动都按一个格子大小移动,数组值:[x,y] 初始:$('.selector').sortable({ grid: [50, 20] }); 获取:var grid = $('.selector').sortable('option', 'grid'); 设置:$('.selector').sortable('option', 'grid', [50, 20]); handle : Selector, Element : false 限制排序的动作只能在item元素中的某个元素开始。 初始:$('.selector').sortable({ handle: 'h2' }); 获取:var handle = $('.selector').sortable('option', 'handle'); 设置:$('.selector').sortable('option', 'handle', 'h2'); helper : String, Function : 'original' 设置是否在拖拽元素时,显示一个辅助的元素。可选值:'original', 'clone' 初始:$('.selector').sortable({ helper: 'clone' }); 获取:var helper = $('.selector').sortable('option', 'helper'); 设置:$('.selector').sortable('option', 'helper', 'clone'); items : Selector : '> *' 指定在排序对象中,哪些元素是可以进行拖拽排序的。 初始:$('.selector').sortable({ items: 'li' }); 获取:var items = $('.selector').sortable('option', 'items'); 设置:$('.selector').sortable('option', 'items', 'li'); opacity : Float : false 定义当排序时,辅助元素(helper)显示的透明度。 初始:$('.selector').sortable({ opacity: 0.6 }); 获取:var opacity = $('.selector').sortable('option', 'opacity'); 设置:$('.selector').sortable('option', 'opacity', 0.6); placeholderType: StringDefault: false 设置当排序动作发生时,空白占位符的CSS样式。 初始:$('.selector').sortable({ placeholder: 'ui-state-highlight' }); 获取:var placeholder = $('.selector').sortable('option', 'placeholder'); 设置:$('.selector').sortable('option', 'placeholder', 'ui-state-highlight'); revert : Boolean : false 如果设置成true,则被拖拽的元素在返回新位置时,会有一个动画效果。 初始:$('.selector').sortable({ revert: true }); 获取:var revert = $('.selector').sortable('option', 'revert'); 设置:$('.selector').sortable('option', 'revert', true); scroll : Boolean : true 如果设置成true,则元素被拖动到页面边缘时,会自动滚动。 初始:$('.selector').sortable({ scroll: false }); 获取:var scroll = $('.selector').sortable('option', 'scroll'); 设置:$('.selector').sortable('option', 'scroll', false); scrollSensitivity : Integer : 20 设置当元素移动至边缘多少像素时,便开始滚动页面。 初始:$('.selector').sortable({ scrollSensitivity: 40 }); 获取:var scrollSensitivity = $('.selector').sortable('option', 'scrollSensitivity'); 设置:$('.selector').sortable('option', 'scrollSensitivity', 40); scrollSpeed : Integer : 20 设置页面滚动的速度。 初始:$('.selector').sortable({ scrollSpeed: 40 }); 获取:var scrollSpeed = $('.selector').sortable('option', 'scrollSpeed'); 设置:$('.selector').sortable('option', 'scrollSpeed', 40); tolerance : String : 'intersect' 设置当拖动元素越过其它元素多少时便对元素进行重新排序。可选值:'intersect', 'pointer' intersect:至少重叠50% pointer:鼠标指针重叠元素 初始:$('.selector').sortable({ tolerance: 'pointer' }); 获取:var tolerance = $('.selector').sortable('option', 'tolerance'); 设置:$('.selector').sortable('option', 'tolerance', 'pointer'); zIndex : Integer : 1000 设置在排序动作发生时,元素的z-index值。 初始:$('.selector').sortable({ zIndex: 5 }); 获取:var zIndex = $('.selector').sortable('option', 'zIndex'); 设置:$('.selector').sortable('option', 'zIndex', 5); ·事件
start 当排序动作开始时触发此事件。 定义:$('.selector').sortable({ start: function(event, ui) { ... } }); 绑定:$('.selector').bind('sortstart', function(event, ui) { ... }); sort 当元素发生排序时触发此事件。 定义:$('.selector').sortable({ sort: function(event, ui) { ... } }); 绑定:$('.selector').bind('sort', function(event, ui) { ... }); change 当元素发生排序且坐标已发生改变时触发此事件。 定义:$('.selector').sortable({ change: function(event, ui) { ... } }); 绑定:$('.selector').bind('sortchange', function(event, ui) { ... }); beforeStop 当排序动作结束之前触发此事件。此时占位符元素和辅助元素仍有效。 定义:$('.selector').sortable({ beforeStop: function(event, ui) { ... } }); 绑定:$('.selector').bind('sortbeforeStop', function(event, ui) { ... }); stop 当排序动作结束时触发此事件。 定义:$('.selector').sortable({ stop: function(event, ui) { ... } }); 绑定:$('.selector').bind('sortstop', function(event, ui) { ... }); update 当排序动作结束时且元素坐标已经发生改变时触发此事件。 定义:$('.selector').sortable({ update: function(event, ui) { ... } }); 绑定:$('.selector').bind('sortupdate', function(event, ui) { ... }); receive 当一个已连接的sortable对象接收到另一个sortable对象的元素后触发此事件。 定义:$('.selector').sortable({ receive: function(event, ui) { ... } }); 绑定:$('.selector').bind('sortreceive', function(event, ui) { ... }); over 当一个元素拖拽移入另一个sortable对象后触发此事件。 定义:$('.selector').sortable({ over: function(event, ui) { ... } }); 绑定:$('.selector').bind('sortover', function(event, ui) { ... }); out 当一个元素拖拽移出sortable对象移出并进入另一个sortable对象后触发此事件。 定义:$('.selector').sortable({ out: function(event, ui) { ... } }); 绑定:$('.selector').bind('sortout', function(event, ui) { ... }); activate 当一个有使用连接的sortable对象开始排序动作时,所有允许的sortable触发此事件。 定义:$('.selector').sortable({ activate: function(event, ui) { ... } }); 绑定:$('.selector').bind('sortactivate', function(event, ui) { ... }); deactivate 当一个有使用连接的sortable对象结束排序动作时,所有允许的sortable触发此事件。 定义:$('.selector').sortable({ deactivate: function(event, ui) { ... } }); 绑定:$('.selector').bind('sortdeactivate', function(event, ui) { ... }); ·方法
destory 从元素中移除拖拽功能。 用法:.sortable( 'destroy' ) disable 禁用元素的拖拽功能。 用法:.sortable( 'disable' ) enable 启用元素的拖拽功能。 用法:.sortable( 'enable' ) option 获取或设置元素的参数。 用法:.sortable( 'option' , optionName , [value] ) serialize 获取或设置序列化后的每个item元素的id属性。 用法:.sortable( 'serialize' , [options] ) toArray 获取序列化后的每个item元素的id属性的数组。 用法:.sortable( 'toArray' ) refresh 手动重新刷新当前sortable对象的item元素的排序。 用法:.sortable( 'refresh' ) refreshPositions 手动重新刷新当前sortable对象的item元素的坐标,此方法可能会降低性能。 用法:.sortable( 'refreshPositions' ) cancel 取消当前sortable对象中item元素的排序改变。 用法:.sortable( 'cancel' ) 排序后保存有两种方法, 一是cookie,二是 数据库配置文件等。 这个是cookie 的例子 大家可以参考 http://www.cnblogs.com/tianxiangbing/archive/2010/01/26/jquery_sortable.html 下面是数据库的部分代码 原作: $(function() { var show = $(".loader"); var orderlist = $(".orderlist"); var listleft = $("div[id = 'column_left']"); var listcenter = $("div[id = 'column_center']"); var listright = $("div[id = 'column_right']"); $( ".column" ).sortable({ opacity: 0.5,//拖动的透明度 revert: true, //缓冲效果 cursor: 'move', //拖动的时候鼠标样式 connectWith: ".column", //开始用update 结果执行两次,浪费资源,古改成stop //但是stop在元素没有改变位置的时候也会执行, //用update其他js会有问题,^_^ stop: function(){ var new_order_left = []; //左栏布局 var new_order_center = [];//中栏布局 var new_order_right = [];//右栏布局 listleft.children(".portlet").each(function() { new_order_left.push(this.title); }); listcenter.children(".portlet").each(function() { new_order_center.push(this.title); }); listright.children(".portlet").each(function() { new_order_right.push(this.title); }); var newleftid = new_order_left.join(','); var newcenterid = new_order_center.join(','); var newrightid = new_order_right.join(','); $.ajax({ type: "post", url: jsonUrl, //服务端处理程序 data: { leftid: newleftid, centerid: newcenterid, rightid:newrightid}, //id:新的排列对应的ID,order:原排列顺序 // beforeSend: function() { // show.html(" 正在更新"); // }, success: function(msg) { //alert(msg); show.html(""); } }); } }); 原文出自: http://hb-keepmoving.iteye.com/blog/1154618
图片预加载
1 (function($) { 2 var cache = []; 3 // Arguments are image paths relative to the current page. 4 $.preLoadImages = function() { 5 var args_len = arguments.length; 6 for (var i = args_len; i--;) { 7 var cacheImage = document.createElement('img'); 8 cacheImage.src = arguments[i]; 9 cache.push(cacheImage); 10 } 11 } 12 13 jQuery.preLoadImages("image1.gif", "/path/to/image2.png"); 在新窗口打开链接 (target=”blank”)1 $('a[@rel$='external']').click(function(){ 2 this.target = "_blank"; 3 }); 4 5 /* 6 Usage: 7 <a href="http://www.catswhocode.com" rel="external">catswhocode.com</a> 8 */ 当支持 JavaScript 时为 body 增加 class1 /* 该代码只有1行,但是最简单的用来检测浏览器是否支持 JavaScript 的方法,如果支持 JavaScript 就在 body 元素增加一个 hasJS 的 class */ 2 $('body').addClass('hasJS'); 平滑滚动页面到某个锚点 1 $(document).ready(function() { 2 $("a.topLink").click(function() { 3 $("html, body").animate({ 4 scrollTop: $($(this).attr("href")).offset().top + "px" 5 }, { 6 duration: 500, 7 easing: "swing" 8 }); 9 return false; 10 }); 11 }); 鼠标滑动时的渐入和渐出1 $(document).ready(function(){ 2 $(".thumbs img").fadeTo("slow", 0.6); // This sets the opacity of the thumbs to fade down to 60% when the page loads 3 4 $(".thumbs img").hover(function(){ 5 $(this).fadeTo("slow", 1.0); // This should set the opacity to 100% on hover 6 },function(){ 7 $(this).fadeTo("slow", 0.6); // This should set the opacity back to 60% on mouseout 8 }); 9 }); 制作等高的列1 var max_height = 0; 2 $("div.col").each(function(){ 3 if ($(this).height() > max_height) { max_height = $(this).height(); } 4 }); 5 $("div.col").height(max_height); 在一些老的浏览器上启用 HTML5 的支持 1 (function(){ 2 if(!/*@cc_on!@*/0) 3 return; 4 var e = "abbr,article,aside,audio,bb,canvas,datagrid,datalist,details,dialog,eventsource,figure,footer,header,hgroup,mark,menu,meter,nav,output,progress,section,time,video".split(','),i=e.length;while(i--){document.createElement(e[i])} 5 })() 6 7 //然后在head中引入该js 8 <!--[if lt IE 9]> 9 <script src="http://html5shim.googlecode.com/svn/trunk/html5.js"></script> 10 <![endif]--> 测试浏览器是否支持某些 CSS3 属性 1 var supports = (function() { 2 var div = document.createElement('div'), 3 vendors = 'Khtml Ms O Moz Webkit'.split(' '), 4 len = vendors.length; 5 6 return function(prop) { 7 if ( prop in div.style ) return true; 8 9 prop = prop.replace(/^[a-z]/, function(val) { 10 return val.toUpperCase(); 11 }); 12 13 while(len--) { 14 if ( vendors[len] + prop in div.style ) { 15 // browser supports box-shadow. Do what you need. 16 // Or use a bang (!) to test if the browser doesn't. 17 return true; 18 } 19 } 20 return false; 21 }; 22 })(); 23 24 if ( supports('textShadow') ) { 25 document.documentElement.className += ' textShadow'; 获取 URL 中传递的参数1 $.urlParam = function(name){ 2 var results = new RegExp('[\\?&]' + name + '=([^&#]*)').exec(window.location.href); 3 if (!results) { return 0; } 4 return results[1] || 0; 5 } 禁用表单的回车键提交1 $("#form").keypress(function(e) { 2 if (e.which == 13) { 3 return false; 4 } 5 });
摘要: 为什么需要元数据模型 您已经熟悉使用 Cognos 来创建报表,进行自助式设计分析,然而这些需要的创建的应用都依赖于对应的元数据模型,用户使用元数据模型对他们的数据源进行分析和报告。元数据模型是整 个 Cognos 应用的基础,它是一个或多个数据源中信息的业务演示。基于这个基础,您才能够创建报表,进行自助式设计分析。 Cognos 能支持多种数据源,包括关系型的和多维的数据库。元数据模型能隐藏底... 阅读全文
什么是自助式设计分析 自助式设计分析是指在业务人员在同一工具做即席查询、报表和分析,完全由业务用户自助式制作。并且能和自助式仪表盘、专业报表工具紧密集成。通过业务人员进行自助式设计分析可以提升业务敏捷度、提升客户满意度、快速报表制作、降低 IT 管理成本。 IBM Cognos Business Insight Advanced 是用于创建报表和分析数据的基于 Web 的工具。商业用户可通过此用户界面了解其业务。IBM Cognos Business Insight Advanced 属于一种新型报表使用环境,将为商业用户带来全面的商业智能体验。 在《第一个自助式仪表盘》介绍了使用 IBM Cognos Business Insight 创建复杂的交互式仪表盘并以预定义的方式浏览内容。在 Business Insight 仪表盘中,可处理现有的内容并进行基本分析、数据浏览和协作性决策的制定。如果要执行深入分析并要创建报表,可以切换到 Business Insight Advanced 来执行更高级的数据浏览,例如添加附加度量、条件格式化和高级计算。 在 IBM Cognos BI 10.1 版本以前,IBM Cognos Report Studio 中的“专业”和“快速”两种创建模式,从 IBM Cognos BI 10.1 版本开始,“快速”创建模式现已替换为 IBM Cognos Business Insight Advanced,专业 IT 报表创建者仍使用 Report Studio 工具创建高级报表。 Business Insight Advanced 由业务人员使用,提供比原来“快速”创建模式的功能更为强大,例如完全支持列表报表、图表和关系数据源,而且还提供了专用于数据浏览的全新用户体验。 Business Insight Advanced 用户界面重点是在浏览数据。因此某些默认行为与以前版本的 IBM Cognos Report Studio 中“快速”创建模有所不同。例如,默认情况下,在 Business Insight Advanced 双击项目现将执行向下追溯。 不论是 IBM Cognos BI 的 Report Studio、Query Studio、Analysis Studio 制作的报表,IBM Cognos Business Insight Advanced 都可以打开,并且可以由业务人员进行自助式设计分析。而 Query Studio、Analysis Studio 功能已经被 Business Insight Advanced 取代,所以 Cognos BI 10 的业务用户只需要使用 Business Insight Advanced 就足够了。Report Studio 则留给专业人员使用。 创建第一个自助式设计分析 - 在开始菜单中启动 IBM Cognos BI Developer Edition,运行 Developer Edition Manager,在确保左侧的服务都正常的情况下,点击右上角的启动,选择 Business Insight Advanced。
- 在“Cognos > 公共文件夹 > 示样 > 多维数据集”路径下,选择“Great Outdoors Sales (cube)”数据包,进入 IBM Cognos Business Insight Advanced 后选择“新建”。在选择报表类型的时候,选择“交叉表”,点击确定。
如果载入数据包报错,例如在内容存储库中找不到数据源“great_outdoors_sales_en”,则需要按照《第一次安装》的步骤检查是否正确建立了 PowerCube 数据源。 - 在右侧的“可插入对象”窗格中的“来源”选项卡显示了面向成员的数据视图。您可以看到维度数据源,也就是包括多维数据源或者按维度建模的关系数据源,在本教程中使用的是 PowerCube 多维数据源。
- 数据包是模型的子集,其中包含可以插入到报表中的项目。
- 维度是有关业务主要方面(如产品、日期或市场)的描述性数据的概括分组。
- 级别层级是维度内更具体的分组。例如,对于“年份”维度,可以将数据组织为较小的组,如“年份”、“当前月份”和“上一个月份”。
- 成员文件夹包含可用于层级或级别的成员。例如,“年”级别层级的“成员”文件夹包含“年”、“季度”和“月份”级别中的所有内容。
- 级别是包含相同详细程度信息并具有共同属性的维度层级内的位置。一个级别层级内可以存在多个级别,从根级别开始。例如,“年份”级别层级具有以下相关级别。
- 成员属性是每个成员所具有的属性。例如,性别可以是所有员工成员的属性。
图 1. 可插入对象 - 对于维度模型数据源,您可以通过单击树面板顶部的按钮来交替查看元数据树和面向成员的数据树。在本教程中选择“查看成员树”。然后把 Years 拖动到列,把 Products 拖动到行,把 Revenue 拖动到度量,如图 2 所示。
图 2. 查看成员树 - 接下来您可以了解一下在 Business Insight Advanced 展示数据的功能。使用维度数据源或按维度建模的关系数据源时,您可以向下追溯到较低级别数据集或向上追溯到较高级别数据集的报表。向上追溯和向下追溯允许 您在预定义的维度层级(例如“年 - 年 - 季度 - 月”)内查看有关数据的更加全面或更加详细的信息,而无需创建其他报表。
左键点击交叉表中的产品“Golf Equipment”两次,可以实现向下追溯的功能。双击第一列最后一行汇总标题上的“Golf Equipment”可以实现向上追溯的功能。 其实在交叉表里面的任意数据都可以实现向下追溯的功能,而选择汇总标题上的数据都可以实现向上追溯的功能。 - 在“Golf Equipment”的向下追溯的效果下,单击成员“Golf Accessories”,在数据菜单下选择“浏览”,再单击“替换”,然后单击“使用级别成员”,如图 3 所示。
图 3. 替换成员 这样就把“Golf Accessories”替换为相同级别的成员。为了帮助理解,可以参看图 4,展开面向成员的数据树,和“Golf Accessories”相同级别的成员是标注红色的那些数据项。 图 4. 级别成员 - 等待报表刷新后,右键选择任意产品类型,点击菜单“最高或最低”,选择“最高 5 (基于 Revenue, Years)”,这样就筛选出最高的 5 个产品类型。
图 5. 最高或最低 - 右键选择汇总标题上的“Golf Equipment”,点击“删除”,这样就可以把不需要的汇总行除去,如图 6 所示。
图 6. 删除汇总行 - 按住 Ctrl 键选择“Tents”和“Packs”两个产品类型,右键选择“排除成员 > 从初始集”,如图 7 所示。您会发现仍然是 5 个产品类型,但是已经不是原来的 5 个,“Tents”和“Packs”两个产品类型已经被换成另外两个。
而如果您刚才选择“排除成员 > 从当前集”,则就会剩下 3 个产品类型。 图 7. 排除成员 - 在右侧的“可插入对象”窗格中的“来源”选项卡中选择“Personal Accessories”产品类型下的后面四个成员拖拽到报表的最后一行后面,如图 8 所示。
图 8. 添加成员 - 您会看到报表有 9 个产品类型,它们分别来自两个数据集。其中 Watches 和 Eyewear 这两个产品类型在两个数据集里面都有,属于重复的产品类型。
您按住 Ctrl 键可以选择两个数据集的 Eyewear,这样就等同于选择了两个数据集。然后在数据菜单下选择“浏览”,再单击“合并到一个集中”,选择“删除重复项”,如图 9 所示。 图 9. 合并数据集 - 这样就剩下合并在一个数据集中的 7 个产品类型,Watches 和 Eyewear 也没有重复了。
右键选择任意产品类型,点击菜单“最高或最低”,选择“最低 5 (基于 Revenue, Years)”,这样就筛选出最低的 5 个产品类型。 - 按住 Ctrl 键选择 Packs 和 Navigation 两个产品类型,右键选择“移动成员”,点击 “到顶部”,如图 10 所示。Packs 和 Navigation 两个产品类型就会被放到报表的头两行。
图 10. 移动成员 - 刚才您做了很多数据集的操作,这些操作包括:
- 排除成员
- 将成员移到集的顶部或底部
- 联接多个集
- 应用最高或最低过滤器
- 过滤集
- 展开或折叠集合中的成员
在 IBM Cognos Business Insight Advanced 可以查看集定义,从而了解、更改、添加或删除可对该集执行的操作,集的定义以图形树的形式为您显示了对集执行的所有操作的历史记录。 您可以任意选择数据项,比如 Packs ,然后右键菜单选择“编辑集”。或者您也可以在“数据”菜单下选择“浏览”,找到“编辑集”的菜单项。 图形树显示了对成员集合执行的所有操作,如图 11 所示。您可以执行以下操作,在这里选择“取消”即可。 - 要查看操作详情,请将鼠标悬停在操作节点上。
- 要更改操作的顺序,请单击操作节点,然后单击向右箭头或向左箭头。
- 要编辑操作,请单击操作节点,然后单击“编辑”按钮。
- 要添加新操作,请单击“新建”按钮。
图 11. 编辑集 - 任意选择报表的数据项,右键选择“显示属性”,这样会展开报表的属性页。您可以设置报表的属性。
图 12. 设置属性 - 保存您的分析结果在“我的文件夹”下,命名为“第一个自助式设计分析”。
使用图表 IBM Cognos Business Insight Advanced 包含一种默认图表技术,该技术不同于 10.1 之前版本中使用的原始图表技术。“使用原始图表创建”选项,让您能够使用老版本的原始图表技术的报表,而不是默认的 Business Insight Advanced 图表来创建新的报表。 在《第一张交互式离线报表》,您已经去掉“使用原始图表创建”的选择,这里可以再检查一下。您可以在“工具”菜单中点击“选项”,在“高级选项”标签中,检查是否已经去掉“使用原始图表创建”的选择。 IBM Cognos Business Insight Advanced 包含丰富的图表类型: - 柱形图:对于比较离散数据或显示随时间变化的趋势非常有用。
- 折线图:对于显示随时间变化的趋势和比较多个数据序列非常有用。
- 饼形图:对于突出显示比例非常有用。
- 条形图:对于显示随时间变化的趋势和绘制多个数据序列非常有用。
- 面积图:对于强调随时间变化的更改量非常有用,堆积面积图还用于显示部分与整体的关系。
- 点状图:对于以非群集样式显示定量数据非常有用。
- 组合图:通过在一个图表内使用柱形图、面积图和折线图的组合,绘出多个数据序列。组合图对于突出显示各种数据序列之间的关系非常有用。
- 散点图:使用数据点来沿刻度的任何位置(不仅在常规刻度线处)绘出两个度量,对于浏览不同数据集之间的相互关系非常有用。
- 泡形图:使用数据点和气泡沿某一刻度的任意位置绘出度量,就像散点图一样,气泡的大小表示第三个度量,对于表示财务数据非常有用。
- 项目符号图表:是条形图的一个变体。它们可以将特色度量(项目符号)与目标度量(目标)进行对比,也可以将比对度量与背景中提供其他定性度量(如“非常满意”、“满意”和“不满意”)的彩色区域联系到一起,通常用于代替执行仪表盘中的仪表板图。
- 仪表板图:使用指针将信息显示为表盘上的读数。在有颜色的数据范围或图表轴上读取每个指针的值非常容易。此图表类型常用于执行仪表盘报表,以显示关键业务指示器。
- 排列图:通过标识事件的主要原因来帮助您改善这些过程。排列图按照从最频繁到最不频繁的顺序对类别进行排名。这些图表常用于质量控制数据,以便您确定并减少问题的主要来源。
- 渐进图:类似于堆积图,单个堆积的各分段都从下一分段垂直偏移,对于强调各分段占整体的比例非常有用。
- 象限图:将背景分成四个等区域的泡形图。象限图对于绘出包含三个度量(使用 X 轴、Y 轴和表示第三个度量值的气泡大小)的数据很有用。
- Marimekko 图表:是百分堆积图,其中列的宽度与列值的总和成正比,各个分段高度是各自列总值的百分比。
- 雷达图:将多个轴组合成一个放射状的图形。对于每个数字,均沿着从图表中心开始的单独轴绘出数据。
- 盈亏图表:是一种 Microchart 图,其中每列的值为 1 或 -1,通常表示盈利或亏损。
- 极坐标图:对于显示科学计算数据非常有用,是使用值和角度将信息显示为极坐标的圆形图。
- 在工具栏中找到“页面布局”,为报表选择预定义的页面布局。您选择 1x2 的布局,这样就能在报表旁边放置图表,如图 13 所示。页面刷新后,报表出现在左侧的单元格,而右侧的单元格是空着的。
图 13. 页面布局 - 在右侧的“工具箱”中选择图表,拖拽到右侧的单元格中。当“插入图表”提示出现时,选择饼形图,如图 14 所示。
图 14. 插入图表 - 在右侧的“来源”中选择 Gross Profit 拖拽到默认度量,把 Sales Regions 拖拽到序列(饼形图扇区)。
- 您可以尝试在饼形图上进行向下追溯和向上追溯的功能。右键选择饼形扇区,然后单击“向下追溯”或者“向上追溯”。
- 您可以将饼形扇区拉离剩余的饼形图,以突出显示饼形扇区。右键选择要拉出的饼形扇区,然后单击“分解扇区”。此时扇区会从图表中拉出。要使已拉出的扇区返回到饼形图中,请右键选择饼形图图表对象,然后单击“删除已分解的扇区”。
- 您在“查看”菜单下可以切换“页面设计”和“页面预览”,其中“页面预览”是缺省选择,这是给业务人员进行自助式设计分析用的,所见即所得,可以直接看到数据结果。而“页面设计”功能是给专业人员用的,界面类似即将在后面介绍的 Report Studio。
- 保存并退出 Business Insight Advanced。
在自助式仪表盘中打开 - 在开始菜单中启动 IBM Cognos BI Developer Edition,运行 Developer Edition Manager,在确保左侧的服务都正常的情况下,点击右上角的启动,选择 Business Insight。在 IBM Cognos Business Insight 的启动页上选择“新建”。
- 在右侧的可插入对象中选择下面的“内容”,在“我的文件夹”下,可以找到刚才保存的“第一个自助式设计分析”,展开可以发现有两个对象,分别是“交叉表 1”和“饼形图 1”。右键选择“第一个自助式设计分析”报表,选择“插入”。
- 在自助式仪表盘中,您如果想看除了报表里面 5 个产品类型以外的产品,比如想把 Lanterns 产品类型也添加到报表中。那就不能直接在 Business Insight 里面完成,可以通过选择菜单的齿轮形状的按钮“完成更多”来启动 Business Insight Advanced,如图 15 所示。等待一会以后,Business Insight Advanced 界面就出现了。
图 15. 完成更多 - 默认情况下,当您使用 IBM Cognos Business Insight Advanced 将源目录树中的成员插入到报表中时,成员将与其子项一起以集 [ 创建成员集 ] 的形式插入。您可以更改成员的插入方式。因为现在只想添加 Lanterns 产品类型,所以想要插入不带子项的成员。
为此,在“可插入对象”窗格的“来源”选项卡上,单击“插入带子项的成员”按钮,并选择插入成员的方式,选择“插入单个成员”,如图 16 所示。 图 16. 插入单个成员 - 然后在 Products > Camping Equipment 路径下,把 Lanterns 拖拽到报表的最后一行,注意等到有闪烁行线出现时再松开鼠标。
- 这样就有 6 个产品类型出现在报表中,Lanterns 产品类型出现在报表的最后一行。点击右上角的“完成”回到 Business Insight 的仪表盘窗口。
- 值得注意的是,现在修改的报表仅仅是改变自助式仪表盘的数据,而不影响原先的报表“第一个自助式设计分析”。因此您可以在自助式仪表盘里面引用自助式设计 分析的结果,也可以随时利用自助式设计分析的强大功能来弥补自助式仪表盘的不足。Business Insight 和 Business Insight Advanced 两者无缝集成,不需要切换。
总结 您可以使用两种不同的方式打开 Business Insight Advanced: - 从执行报表高级编辑(“完成更多”)的 Business Insight 仪表盘中打开。这种方式适合业务人员在自助式仪表盘里面想进一步进行自助分析。
- 从 IBM Cognos Connection 的“启动”菜单中打开或从欢迎页面(“创建者业务报表”)中打开。这种方式适合业务人员进行自助设计分析。
Business Insight Advanced 是给业务人员进行自助式设计分析用的。在《第一次安装》中,您曾经用 Report Studio 构建了第一张简单报表,而 Report Studio 是给专业人士使用的报表工具。 您可以用 Report Studio 来打开刚才保存的“第一个自助式设计分析”。 在开始菜单中启动 IBM Cognos BI Developer Edition,运行 Developer Edition Manager,在确保左侧的服务都正常的情况下,点击右上角的启动,选择 Report Studio。在“Cognos > 公共文件夹 > 示样 > 多维数据集”路径下,选择“Great Outdoors Sales (cube)”数据包,进入 IBM Report Studio 后选择“打开现有的”。在“我的文件夹”下,可以找到刚才保存的“第一个自助式设计分析”,点击“打开”。 您可以看到 Report Studio 提供了给专业报表创建者的许多功能,下面举例说明 Business Insight Advanced 不能实现而 Report Studio 可以完成的功能。 - 在 Report Studio 中间的“查询资源管理器”,您可以创建或修改关系报表 [ 使用关系查询 ] 或维度报表 [ 使用维度查询 ] 中的查询,以及执行复杂任务,如定义合并联接和写入 SQL 语句,如图 17 所示。在 Business Insight Advanced,您不能看到也不能操控查询。
图 17. 查询资源管理器 - 点击交叉表的左上方的三个小红点,选择交叉表,您可以看到交叉表的属性,如图 18 所示。这里有一些属性是 Business Insight Advanced 无法编辑的。比如在“其他”类别里面的“名称”,这里的名称就是您刚才在 Business Insight 中进行自助式仪表盘时候看到的对象名称。您可以在 Report Studio 把它修改成更有意义的名字,比如“产品线收入表”。
图 18. 交叉表属性
摘要: 熟悉 Report Studio Report Studio 是用来制作更加精细的专有报表的工具。IBM Cognos Report Studio 是一个基于 Web 的报表创建工具,专业报表创建者和开发人员可使用此工具针对多个数据库创建复杂的、具有多页并且可以进行多项查询的报表。使用 Report Studio,您可以创建公司所需的任何报表,如发票报表、财务报表以及每周销售和库存报表。报表... 阅读全文
什么是自助式仪表盘 IBM Cognos Business Insight 基于网页的界面可以让您建立先进的交互式的仪表盘,来提供见解并使协同决策变得简单。在 IBM Cognos Business Insight 中创建的仪表盘可以让业务用户将集成的商业智能体验与协作决策相结合。用户可以快速而轻松地完成绝大部分任务。例如可以查看并与报表交互、排列数据或者执 行额外的计算、或者与团队中其他成员共享信息。因为用户对报表和数据有不同的需求,所以可以利用自由形式布局也可以重新排列报表或添加新报表。 定制化内容 当启动 IBM Cognos Business Insight 的时候,您可以选择打开一个已存在的仪表盘或是创建一个仪表盘。在打开的工作区域中,您都可以添加或重新排列新小工具,所有仪表盘都是可编辑的。业务用户 可以将有权限访问各种系统内容组合成一个指定的仪表盘,并可以进行分析。高级业务用户或报表作者可以为一组业务用户创建报表和基础仪表盘,来包含该组用户 工作所需的所有信息。这样,业务用户就能定制仪表盘来契合特殊的需求。这些需求可能包括重排布局,更改图形,将数据排序,添加计算,新建过滤等以及搜索一 份补充报表并添加其到工作区域。 添加新内容 您可以从内容或工具箱标签拖拽新小工具到仪表盘来。使用相同的方式,你可以添加报表、报表组件、度量列表或独立度量、TM1 项目、或者任何小工具中描述的项目。您可以使用 IBM Cognos Business Insight 中增强的搜索功能来寻找并添加相关内容到仪表盘。这个功能是一种全文搜索,类似于流行的搜索引擎。 交互分析能力 除了更改报表中数据的显示,你也可以与报表交互,并自定义其排列。此外,你还可以利用报表中的数据添加基础性运算,可以过滤数据。 高级过滤 使用过滤小工具可以过滤报表中所有与其关联的报表。这样,如果你有一个地区选择过滤器,它过滤所有该地区一项的报表。当然,它只过滤那些与这个过滤器关联的报表中的数据。当你在过滤小工具上选择一个值,报表就会重新刷新来显示你选择的过滤好的数据项。 其他内容 除了报表内容之外,您也可以添加其他内容到仪表盘,例如 Web 页面、图像、我的收件箱、文本、RSS 订阅。 创建注释 对于仪表盘上的一份报表中的内容,评论或注释有助于用户与团队中其他成员进行合作。这些注释可以让其他查看相同报表的用户看见。这些用户也可以添加 关于此报表的进一步注释,来提供额外的信息。例如,注释可以提醒在某个地区调查销售较低的结果,也可以是某些异常数据的一个解释,例如一个最近发布且已在 市场上有数月的产品的销售数据较小。您可以注释实时报表并保存输出版本。当打印一个报表的 PDF 版本或是导出报表为 PDF 或 Excel 输出时,注释也会被包括在其中。 创建第一个自助式仪表盘 - 在开始菜单中启动 IBM Cognos BI Developer Edition,运行 Developer Edition Manager,在确保左侧的服务都正常的情况下,点击右上角的启动,选择 Business Insight。在 IBM Cognos Business Insight 的启动页上选择“新建”。
- 在右侧的可插入对象中选择下面的“内容”,在“公共文件夹 > 示样 > 模型 > GO 数据仓库 ( 查询 ) > Business Insight 源报表 > 收入数据”路径下,右键选择“Revenue by Country – bar - chart”报表,选择“插入”,如图 1 所示。再用同样的方式,把另外的 Revenue by Product Type - Combination Chart 和 Revenue by Order method - Pie Chart 两张报表插入到仪表盘中。
图 1. 选择报表 - 在仪表盘空白处点击右键,选择“排列所有小工具以符合内容的显示尺寸”来调整三张报表在仪表盘的大小和位置。您也可以单独调整每个报表的大小和位置,方法是鼠标移动到报表上,等到小工具菜单显示后选择“调整大小以适合内容”,如图 2 所示。
图 2. 调整大小 - 在右侧的可插入对象中选择下面的“工具箱”,可以看到有很多小工具,比如可以添加公司的 Web 页面,添加公司商标图像等。这里我们选择“文本”,输入“公司收入仪表盘”,选择合适的大小,把它添加到仪表盘中,如图 3 所示。
图 3. 添加文本 - 点击上方工具栏的保存按钮,保存到我的文件夹中,命名为“第一个交互式仪表盘”。不要关闭 IBM Cognos Business Insight。
您可以在 Business Insight 中保存的任意仪表盘设置为主页。在保存按钮旁边有“主页”按钮,您可以在旁边单击箭头,找到“将仪表盘设为主页”这个选项。 查看仪表盘的数据 - 选择“Revenue by Country – bar - chart”报表,在上方的小工具操作中选择“更改显示类型”,更改为“列表”,如图 4 所示。在“更改显示类型”旁边的按钮是“更改调色板”,您可以根据喜好来选择不同风格不同类型的显示方式来展现数据。
图 4. 更改显示类型 - 在列表中选择“中国”、“巴西”、“加拿大”和“法国”,然后在上方小工具操作中选择“过滤器”,过滤条件是“包含中国、巴西、加拿大、法国”,如图 5 所示。 。
图 5. 添加本地过滤条件 - 在上方的小工具操作中选择“更改显示类型”,更改为“条形图”。这样新的“Revenue by Country – bar - chart”报表就仅仅包含中国、巴西、加拿大、法国了。
- 点击左上方的展开按钮,可以查看已应用过滤器和已应用排序,如图 6 所示。点击过滤器右边的删除按钮,把刚才建立的“包含中国、巴西、加拿大、法国”过滤器删除。
图 6. 查看本地过滤条件 - 在右侧的可插入对象中选择下面的“工具箱”,右键点击“幻灯片过滤器”选择“插入”,把它添加到仪表盘中。在幻灯片过滤器属性窗口中,选择三张报表都有的数据项“收入”,把过滤器的内容设为值范围,修改描述文本后确定,如图 7 所示。
图 7. 幻灯片过滤器属性 - 您可以试着调整页面布局,使得仪表盘更美观。在页面的右上角有一个“内容”按钮,点击可以隐藏或者显示右侧的可插入对象,隐藏右侧的可插入对象可以让仪变盘区域变得更大一些。
您还可以试着拖拽幻灯片过滤器的滑条杆,仪表盘的三张报表图形都会随着变化。 图 8. 调整全局过滤条件 选择一张报表点击左上方的展开按钮,可以查看已应用过滤器和已应用排序,如图 9 所示。刚才建立的全局过滤条件已经显示在已应用过滤器中了,如果在这里删除过滤器,那么只会影响当前报表,而不会影响仪表盘中的其他报表,这是和本地过滤条件的区别所在。 图 9. 查看全局过滤条件 仪表盘的数据交互 - 在右侧的可插入对象中选择下面的“内容”,在“公共文件夹 > 示样 > 模型 > GO 数据仓库 ( 分析 ) > Report Studio 报表示样”路径下,右键选择“预算与实际”报表,选择“在新仪表盘中打开”。如果提示“是否保存仪表盘”,选择“是”。
- 点击上方工具栏的保存按钮,保存到我的文件夹中,命名为“数据交互”。
- 右键选择您需要评注的数据,比如美洲户外用品商店的差额百分比 69.16%,选择添加注释,如图 10 所示。
图 10. 添加注释 - 注释内容填写“69.16% - 增加预算”,确定以后可以看到在 69.16% 数据项上有红色三角形标记,表明有注释。您要查看注释,可将指针悬停于注释(由红色三角形标记指明)上。具有报表读取权限的所有用户都可以查看注释。 如果同一单元格或报表小工具有多个注释,它们将按逆时序显示。 对于每个注释,您可以看到撰写注释的用户的用户名、撰写日期和时间。
图 11. 查看注释 - 点击上方小工具操作的菜单,可以将包含报表内容的报表小工具导出为多种格式。要保存数据的快照,您可以创建报表小工具的 PDF 版本,刚才添加的注释也会保留在导出的 PDF 文件中。当您导出到 PDF 时,小工具将出现在 Adobe Reader 中,因此您必须在计算机上安装 Adobe Reader。
图 12. 将仪表盘小工具导出为不同格式 - 您可以查看报表数据项的 lineage 信息,以了解该报表数据项所代表的内容。Lineage 信息通过数据包和数据包使用的数据源追溯项目的元数据。Lineage 还显示报表创建者添加的或在数据模型中定义的所有数据项过滤器。
IBM Cognos BI Lineage 工具包括两种视图:业务视图和技术视图。业务视图显示了高级语篇信息,该信息描述了数据项及其所来自的数据包。技术视图是选定数据项的 lineage 的图形表示。Lineage 从数据包中的数据项一直跟踪到数据包使用的数据源。 图 13. 查看报表数据项的传承信息 - 下面按住 CTRL 键选中您关心的地区门店,比如“美洲 > 户外用品商店”、“美洲 > 直销”和“美洲 > 百货商店”,然后点击上方小工具操作的计算按钮,选择“+ > 直销 + 户外用品商店 + 百货商店”。您会发现刷新后的仪表盘把计算结果显示在新行中。默认情况下,将使用计算中所用的表达式作为标题名称。计算结果不会存储在基础数据源中。相 反,IBM Cognos Business Insight 会在每次刷新报表时重新运行计算。计算结果始终以数据源中的最新数据为基础。
图 14. 增加计算 您在上方小工具操作的按钮里面还能找到“分组 / 取消分组”、排序等按钮。如果是灰色,表示该功能不适用您目前的报表和数据。 排序按字母或数字的升序或降序组织数据。例如,您可以对列出产品销售额的列按降序进行排序,以便对产品销售额从高到低进行排序。 如果列表报表中的同一列多次出现同一个值,那么您可以将这些相同的值归为一组。分组操作将对选定报表项目的行进行重新排序,这样相同值会一起显示并抑制重 复显示的情况。由于分组的列会显示在未分组的列之前,因此分组和取消分组操作可能会更改报表项目的顺序。但是您可以对列表中的列重新排序来提高报表的可读 性。 总结 在本系列教程的“第 1 部分,第一次安装”中,您安装了样例数据和样例档案。您可以在“公共文件夹 > 示样 > 模型 > Business Insight 示样”目录下找到六个样例仪表盘。您可以依次打开来进行更深一步地研究和学习。 - 员工满意度仪表盘
该报表显示员工满意度的不同度量,例如培训投资、员工调查结果(按部门和按主题,包括与计划调查结果的比较),以及员工奖金列表(按国家 / 地区排序)。滑块过滤器适用于奖金列表。 - 市场营销仪表盘
该仪表盘显示不同促销活动的结果。活动名称的选择值过滤器适用于前两个图表。产品系列选择值过滤器适用于广告费图表,年份滑块过滤器适用于广告费交叉表。 - 招聘仪表盘
该仪表盘显示针对不同指标(根据组织、部门、分部)的招聘结果(填充职位的平均天数)、年份和有关不同招聘技巧的成功的详细信息。两个选择值过滤器控制其中三个小工具。 - 收入数据仪表盘
该仪表盘按区域、国家 / 地区(由选择值过滤器控制)、产品类型(由选择值过滤器控制)及订购方法显示收入。 - 销售(按年)仪表盘
该仪表盘显示由滑块过滤器控制的一年范围内不同的销售指标:利润率、毛利润、产品成本、销售数量、区域收入以及实际收入与计划收入之间的比较。滑块过滤器控制所有小工具。 - 销售仪表盘(交互式)
该仪表盘显示销售的不同方面:月毛利润、地区毛利润和产品系列毛利润,区域收入以及对该销售做出贡献的销售代表的数目。 该源对象基于“Go 数据仓库 ( 分析 )”数据包和“Go 数据仓库 ( 查询 )”数据包。 该销售仪表盘是交互式的,可以支持向上追溯和向下追溯的功能。 您可以在报表或报表组成部分中向上追溯或向下追溯。在 IBM Cognos Business Insight 中,对于列表和交叉表,将指针悬停在数据项上时,超链接会标识可追溯项目。在图表中,当您将指针悬停在可追溯项目上时,指针将变为手形,并且工具提示将指 明您将追溯的内容。在 Business Insight 中,仅当您使用按维度结构化的数据时,才能执行向上追溯和向下追溯。
什么是交互式离线报表 IBM Cognos Active Report 是可以与用户交互的离线报表,包含了数据和展现内容,它在无法访问企业内部网络和数据库的情况下仍然可以通过此类报表分析数据,获得有价值的信息。 IBM Cognos Active Report 非常适合移动办公的情况,如销售体系。使用者在离线的条件下浏览报表,深入挖掘数据,获取额外的信息。IBM Cognos Active Report 拓展了商务智能的应用场景,并让系统户的更好的性能和支持更大的使用规模。 用户使用 IBM Cognos Report Studio 来创建 Active Report。IBM Cognos Active Report 具有很强的交互性和易用性,报表的设计从用户需求出发,并确保的简洁美观流畅的用户体验。 IBM Cognos Active Report 是 IBM Cognos Report Studio 报表的拓展。数据需要以一种简洁易懂的组织方式呈现给客户。有些用户习惯于数字,而另一些则偏好于图表。为了方便设计人员设计出更简洁的报表,IBM Cognos Report Studio 在保持原有功能的同时加入了一些交互式的控件 , 如选项卡、下拉菜单等,用于定义交互报表,对数据进行排序和过滤。 作为一个高级业务人员,可以将刚刚完成的动态报表下载成为本地文件,并转发给公司其它成员 . 文件最终以 mht 格式保存 , 并可以以邮件附件形式发送给同事。 如果正在使用 Microsoft Internet Explorer 6.0,则无法将 MHT 格式的活动报表作为文件打开,交互式离线报表需要 Microsoft Internet Explorer 7 版本以上。要在 Mozilla Firefox 中查看 MHT 格式的活动报表,必须先下载一个 UnMHT 附加组件。 创建第一张交互式离线报表 - 在开始菜单中启动 IBM Cognos BI Developer Edition,运行 Developer Edition Manager,在确保左侧的服务都正常的情况下,点击右上角的启动,选择 Report Studio。
- 在“Cognos > 公共文件夹 > 示样 > 模型”路径下,选择“ GO 数据仓库 ( 查询 ) ”数据包,进入 IBM Report Studio 后选择“新建”。
图 1. 选择数据包 在选择报表类型的时候,选择“活动报表”,点击确定。 图 2. 选择报表类型 - 在左侧的可插入对象中先插入一个列表,然后展开“销售和市场营销(查询)”目录,再展开“销售(查询)”命名空间,按住 Control 键后选择“产品”下的产品类型以及“销售资料”下的收入和计划收入,拖动到右边报表页中,如图 3 所示。
准备工作 IBM Cognos Business Intelligence 10.1 是最新的商业智能解决方案,用于提供查询、报表、分析、仪表板和记分卡功能,并且可通过规划、方案建模、预测分析等功能进行扩展。它可以在人们尝试了解业绩并使用工具做出决策时,在思考和工作方式方面提供支持,以便人们可以搜索和组合与业务相关的所有方面,并与之进行交互。 - 查询和报表功能为用户提供根据事实做出决策所需的信息。
- 仪表板使任何用户都能够以支持其做出决策的方式来访问内容、与之交互,并对其进行个性化设置。
- 分析功能使您能够从多个角度和方面对信息进行访问,从而可以查看和分析信息,帮助您做出明智的决策。
- 协作功能包括通信工具和社交网络,用于推动决策过程中的意见交流。
- 记分卡功能可实现业务指标的捕获、管理和监控的自动化,使您可将其与自己的战略和运营目标进行比较。
在开始体验 Cognos BI 10.1 之前,您需要到 IBM developerWorks 去下载 Cognos 10.1 的试用版。IBM Cognos BI Developer Edition V10.1.0 下载地址是:http://www.ibm.com/developerworks/cn/downloads/im/cognosbi/。 您需要下载两个文件,IBM Cognos BI Developer Edition 10.1.0 Windows English 的下载文件名是 CZS56EN.tar.gz,IBM Cognos Business Intelligence Samples V10.1.0 for DB2 LUW Windows English 的下载文件名是 CZQ90EN.tar.gz。IBM Cognos BI Developer Edition V10.1.0 有 30 天的使用时间,足够让您完成本教程的学习了。 根据试用版的系统需求,您需要用 Windows 操作系统来进行安装,要求 Windows XP SP3 或者更高的版本。另外您要安装 Windows 的 Internet 信息服务 IIS,并且把浏览器 IE 升级到 7 版本以上。建议内存有 2G 以上,有 1.5 G 以上的临时空间和 2G 以上的磁盘空间来进行安装。 由于本教程需要数据库来存放 Cognos BI 10.1 的样例数据,所以您还需要到 IBM developerWorks 去下载 DB2 的社区版。DB2 Express-C 9.7.4 for Windows 下载地址是:http://www.ibm.com/developerworks/cn/downloads/im/udbexp/。下载文件名是 db2exc_974_WIN_x86.exe。 安装 DB2 双击 db2exc_974_WIN_x86.exe 然后选择任意目录进行解压缩。解压成功后点击 setup.exe 进入 DB2 安装启动板。 图 1. DB2 安装启动板 点击“安装新产品”,进入到安装向导界面,点击“下一步”继续。阅读并接受许可协议,点击“下一步”继续。 图 2. 选择安装类型 本教程选择典型安装,这个选项将安装 DB2 的主要部件和功能。 图 3. 选择创建响应文件 选择不创建响应文件而只进行安装,点击“下一步”继续。 图 4. 选择安装文件夹 一般使用默认的驱动器和目录设置就可以了,确保有 560 MB 可用空间,点击“下一步”继续。 图 5. 设置用户信息 安装 DB2 之后,某些 DB2 进程会作为系统服务运行。为了运行这些服务,需要一个操作系统帐户。在本教程中,使用默认的 db2admin 用户帐户,密码为 cognos,DB2 安装程序会在操作系统中创建它。当然您也可以指定使用一个现有的帐户,但是这个帐户必须具有本地管理员权力。点击“下一步”继续。 图 6. 配置 DB2 实例 可以认为 DB2 实例是数据库的容器。必须有一个实例,然后才能创建数据库。在 Windows 上进行安装时,会自动创建一个称为 DB2 的实例。在默认情况下,DB2 实例监听端口 50000 上的 TCP/IP 连接。可以点击配置来查看,在本教程中一切保持默认配置即可。点击“下一步”继续。 图 7. 开始复制文件 检查前面选择的安装选项。单击“安装”。结束以后,点击“完成”即可。 安装 Cognos 解压文件 CZS56EN.tar.gz,然后点击运行 Cognos 10 BI Server 安装程序 install.exe。 图 8. IBM Cognos BI Developer Edition 安装界面 选择简体中文后,点击“OK”。在简介这一步骤中,阅读许可协议并选择接受协议条款。 图 9. 选择安装文件夹 选择缺省文件夹即可以,要保证有可用空间。快捷方式文件夹是指产品图标的程序组,也可以采用缺省的 IBM Cognos BI Developer Edition。 图 10. 配置 在配置界面中可使用默认的端口。然后给 Cognos 的管理员设定用户名和密码,在本教程中设定管理员为 administrator,密码为 cognos。 图 11. 安装摘要 在安装摘要中可以看到整个安装需要 1.5G 以上的磁盘空间,点击“安装”继续。 图 12. 完成安装 完成安装界面时候,勾选启动 IBM Cognos BI Developer Edition Manager,然后点击“完成”。IBM Cognos BI Developer Edition Manager 是开发版特有的组件,可以用来安装和配置 IBM Cognos BI 和 Framework Manager,也可以用来管理用户和启动停止服务。 图 13. IBM Cognos Developer Edition Manager 第一次启动界面 第一次启动 IBM Cognos BI Developer Edition Manager,点击“完成”让 Manager 进行初始化的安装和配置。 图 14. 安装 IBM Cognos Developer Edition Manager 安装过程可能需要十几分钟,耐心等待直到登陆界面出现。 图 15. 登陆 IBM Cognos Developer Edition Manager 在登陆界面中输入在 图 10所示的用户名和密码,在本教程中是 administrator 和 cognos,然后点击确定。 图 16. 安装 Cogos BI Suite 选择 BI Suite,然后进行安装。IBM Cognos BI Suite 赋予了您完整的自助服务报告和即席查询能力,使其可以访问、修改和创建报表。访问任意类型的数据,包括关系 OLAP、分析 OLAP 或桌面文件。并通过 Web、PDF、Excel、电子邮件或门户发送您的报表。安装过程可能需要半小时,耐心等待直到直到登陆界面出现。在登陆界面中输入在图 10 所示的用户名和密码,在本教程中是 administrator 和 cognos,然后点击确定。 图 17. 安装 Framework Manager 选择 Framework Manager,然后进行安装,这个步骤非常快就完成了。Framework Manager 是元数据建模工具。它使得建模者可以创建和管理业务相关的元数据,以便在所有 Cognos BI 应用程序中使用。Framework Manager 的主要用户为数据仓库开发人员和数据建模者。 配置 Cognos 首先需要把 C:\Program Files\IBM\SQLLIB\java 目录下的 db2jcc.jar 和 db2jcc_license_cu.jar 拷贝到 C:\Program Files\IBM\Cognos Developer\tomcat\lib 目录下,然后在 IBM Cognos Developer Edition Manager 重启 Cognos 服务。 安装样例数据 解压文件 CZQ90EN.tar.gz,然后点击运行 install_DB2_samples.exe 进行安装。在 Instruction 这一步骤中,阅读许可协议并选择接受协议条款。 图 18. 选择安装路径 安装路径采用缺省的,这样会和 IBM Cognos BI Developer Edition 安装在同一目录下。完成安装后,样例数据文件会放在 C:\Program Files\IBM\Cognos Developer\webcontent\samples\datasources\db2,找到数据文件 GS_DB.tar.gz,并进行解压缩。 然后在点击开始菜单,选择运行,输入 db2cmd 再确定,进入到 DB2CLP 的窗口。安装过程可能需要五分钟,具体操作命令和显示参见清单 1。在这个过程中需要输入 DB2 的用户名和密码,在本教程是 db2admin 和 cognos。 清单 1. 创建数据库并导入数据
>CD C:\Program Files\IBM\Cognos Developer\webcontent\samples\datasources\db2\GS_DB\win >GOSalesConfig.bat >setupGSDB.bat ------------------------------------------------------------------- DB2 version 9 or later detected - using DB2 Version 9 syntax ------------------------------------------------------------------- Press Enter at the prompts to accept the default value shown Default values can be specifed in the file GOSalesConfig.sh ------------------------------------------------------------------- Please enter the name of the database ( or the alias ) to be used for the GOSales sample data (default=GS_DB) : ------------------------------------------------------------------- This script can create the GS_DB database. Creating the database will cause any existing databases with the same name to be dropped. If you choose not to recreate the database, existing objects within the database will be dropped. Would you like to create the database GS_DB (Y/N) Default=Y : Please wait ... Starting GOSALES_RUN_SCRIPTS Dropping existing database GS_DB if found Creating database GS_DB Please wait ... Starting GOSALES_RUN_SCRIPTS Connecting to GS_DB 输入 db2admin 的当前密码:cognos 数据库连接信息 数据库服务器 = DB2/NT 9.7.4 SQL 授权标识 = DB2ADMIN 本地数据库别名 = GS_DB Error dropping existing tables Creating tables. Loading data. Creating primary keys Creating indexes Creating constraints. Creating stored procedures Creating views Granting permissions Updating statistics Verifying row counts Table row count validation successful Adding table comments | 创建数据源连接 在开始菜单中启动 IBM Cognos BI Developer Edition,运行 Developer Edition Manager,在确保左侧的服务都正常的情况下,点击右上角的启动,如图 19 所示选择 IBM Cognos Administration。 图 19. 启动 Cognos Administration 在 IBM Cognos Administration 界面中选择配置页,选中“数据源连接”,点击右上角的图标,如图 20 所示新建数据源。 图 20. 在 Cognos Administration 中添加数据源 - 在指定名称和说明 - 新建数据源向导中,名称中输入“great_outdoors_sales”,点击下一步。
- 在指定连接 - 新建数据源向导中,选择类型为“IBM DB2”,点击下一步。
- 在指定 IBM DB2 连接字符串 - 新建数据源向导中,DB2 数据库名称写为“GS_DB”,在登陆勾选“密码”,用户 ID 中写为 “db2admin”,密码和确认密码写为 “cognos”。然后点击“测试连接”如图 21 所示。测试成功后点击下一步。如果测试有问题的时候,可以重启 Cognos 服务或者重启系统。
图 21. 指定 IBM DB2 连接字符串 - 在指定命令 - 新建数据源向导中,保持缺省,点击完成。
- 重复 1 到 4 的步骤,再创建“great_outdoors_warehouse”数据源,即名称为“great_outdoors_warehouse”,其余内容“great_outdoors_sales”一样。
- 在指定名称和说明 - 新建数据源向导中,名称中输入“sales_and_marketing”,点击下一步。
- 在指定连接 - 新建数据源向导中,选择类型为“IBM Cognos PowerCube”,点击下一步。
- 指定 IBM Cognos PowerCube 连接字符串 - 新建数据源向导中,Windows 位置输入“C:\Program Files\IBM\Cognos Developer\webcontent\samples\datasources\cubes\PowerCubes\EN \sales_and_marketing.mdc”。然后点击“测试连接”,测试成功后点击完成。
- 重复 6 到 8 的步骤,再创建“great_outdoors_sales_en”数据源,即名称为 “great_outdoors_sales_en”,Windows 位置输入“C:\Program Files\IBM\Cognos Developer\webcontent\samples\datasources\cubes\PowerCubes\EN \great_outdoors_sales_en.mdc”。
- 重复 6 到 8 的步骤,再创建“employee_expenses”数据源,即名称为“employee_expenses”,Windows 位置输入“C:\Program Files\IBM\Cognos Developer\webcontent\samples\datasources\cubes\PowerCubes\EN\ employee_expenses.mdc”。
- 重复 6 到 8 的步骤,再创建“go_accessories”数据源,即名称为“go_accessories”,Windows 位置输入“C:\Program Files\IBM\Cognos Developer\webcontent\samples\datasources\cubes\PowerCubes\EN\ go_accessories.mdc”。
- 重复 6 到 8 的步骤,再创建“go_americas”数据源,即名称为“go_americas”,Windows 位置输入“C:\Program Files\IBM\Cognos Developer\webcontent\samples\datasources\cubes\PowerCubes\EN\ go_americas.mdc”。
- 重复 6 到 8 的步骤,再创建“go_asia_pacific”数据源,即名称为“go_asia_pacific”,Windows 位置输入“C:\Program Files\IBM\Cognos Developer\webcontent\samples\datasources\cubes\PowerCubes\EN\ go_asia_pacific.mdc”。
回页首 导入示例档案库 接下来要把示例的部署档案库导入到 Cognos 环境中来。这些档案库的位置在 C:\Program Files\IBM\Cognos Developer\webcontent\samples\content\db2 目录下。选择拷贝三个文件到 C:\Program Files\IBM\Cognos Developer\deployment 目录下准备进行部署。 这三个文件是 IBM_Cognos_Samples.zip,本教程主要的 Great Outdoors 公司例子; IBM_Cognos_DrillThroughSamples.zip, Great Outdoors 公司显示钻透功能的例子; IBM_Cognos_PowerCube.zip 多维立方体的例子。 - 在 IBM Cognos Administration 界面中选择配置页,选中“内容管理”,点击右上角的图标,如图 22 所示新建导入。
图 22. 新建导入向导 - 在部署档案库中,首先选择 IBM_Cognos_Samples,点击下一步。
- 在指定名称和说明 - 新建导入向导,按照默认的名字,点击下一步。
- 在公共文件夹内容中,勾选档案库的内容“示样”,点击下一步。
- 在指定常规选项 - 新建导入向导,保留默认选项,点击下一步。
- 复查汇总 - 新建导入向导,点击下一步。
- 在选择操作 - 新建导入向导中,选择“保存并运行一次” ,点击完成。
- 在运行使用选项 - IBM_Cognos_Samples 中,选择“现在”,点击运行。
- 重复 1 到 8 的步骤,选择 IBM_Cognos_PowerCube 进行导入。
10. 重复 1 到 8 的步骤,选择 IBM_Cognos_DrillThroughSamples 进行导入。 第一张简单报表 在开始菜单中启动 IBM Cognos BI Developer Edition,运行 Developer Edition Manager,在确保左侧的服务都正常的情况下,点击右上角的启动,选择 Report Studio,如 图 19所示。 在“Cognos > 公共文件夹 > 示样 > 模型”路径下,选择“ GO 数据仓库 ( 查询 ) ”数据包,进入 IBM Report Studio 后选择“新建”。 图 23. 选择数据包 在选择报表类型的时候,选择“列表”,点击确定。 图 24. 选择报表类型 在左侧的可插入对象中,展开“销售和市场营销(查询)”目录,再展开“销售(查询)”命名空间,选择“产品”下的产品系列、产品类型和产品,选择“销售资料”下的数量和收入,一个一个拖动到右边的列表中。 图 25. 选择项目 剩下的工作就是点击工具栏的运行按钮,来等待您的第一张简单报表出炉了,如图 26 所示。 图 26. 运行报表 报表运行结果如图 27 所示。 图 27. 第一张简单报表 总结 在整个安装过程中,DB2 是用户数据源,我们把本教程的样例数据放在了 DB2 的 GS_DB 数据库中,并在 Cognos Administration 进行了数据源的配置。 Cognos Connection 是 Cognos 门户,提供信息的集成和用户访问的统一入口。管理员可以通过他实现用户、角色管理,服务器配置,权限控制等各种管理功能;最终用户可以通过 Cognos Connection 访问到文件夹、报表、个性化展现、访问 Cognos Viewer、Report Studio、Business Insight 和 Event Studio 的内容。 Report Studio 是专业的报表制作模块。报表制作人员可以通过他制作各种类型的报表,包括中国特色的非平衡报表,地图,动态仪表盘,KPI 报表等。报表制作人员可以分页面设计,每页可以有多个查询,每个查询可以连接多个数据源,甚至异构数据源。报表的内容采用的是化繁为简的方式,可以精确控 制报表中每一个对象的各种属性。 Business Insight 是业务用户的自定义仪表盘工具,用户可以拖拽任意 Cognos BI 内容(包含查询,报表,分析,TM1 数据集等)形成自定义的仪表盘。 Cognos Framework Manager 是一个专门对元数据进行管理的客户端开发工具。他可以连接多个数据源,能够连接 OLAP 和数据库等各种数据源,并提供对元数据的定制和管理以及安全性控制等相关控制。 图 28. IBM Cognos BI Developer Edition 架构和工作原理 当您运行第一张简单报表的时候,IBM Cognos BI Developer Edition 是按照下面步骤来运行的: - 在 IBM Cognos Framework Manager 工具中,建模人员确保元数据是按照业务人员可以理解的方式来进行组织的。建模人员把元数据从一个或多个数据库中导入,并按照业务需求添加到模型中。
- 建模人员把模型数据包发布到 IBM Cognos Connection,这样开发报表人员就可以利用它们来进行创建报表和仪表盘了。比如在本教程中的“ GO 数据仓库 ( 查询 ) ”数据包。
- 业务人员和报表开发人员利用已经发布的数据包来理解业务数据。
- 用户在 IBM Cognos Connection 运行、查看和管理他们的内容。根据不同的权限,他们可以简单运行和查看报表,或者管理计划、门户展示等。
源文出自:http://blog.csdn.net/thy822/article/details/7608305
两种方法一样,只是写法不同 第一种写法 1 listeners:{ 2 beforeedit:function(e){ 3 if(...) e.cancel = true;//true表示不可编辑 4 } 5 } 第二种写法 1 grid.on('beforeedit',function(e)){ 2 var record = e.record; 3 if(...){ 4 e.cancel = true;//true表示不可编辑 5 } 6 }
1.首先看效果 2.需要一个合计插件GridSummary.js GridSummary 插件,下载后将后缀名该为 js 3.页面需要引用的文件 1 <link rel="stylesheet" type="text/css" href="include/ext-3.4.0/resources/css/ext-all.css" /> 2 <link rel="stylesheet" type="text/css" href="include/ext-3.4.0/ux/css/ux-all.css" /> 3 <script type="text/javascript" src="include/ext-3.4.0/adapter/ext/ext-base.js"></script> 4 <script type="text/javascript" src="include/ext-3.4.0/ext-all.js"></script> 5 <script type="text/javascript" src="include/ext-3.4.0/ux/ux-all.js"></script> 6 <script type="text/javascript" src="include/ext-3.4.0/locale/ext-lang-zh_CN.js"></script> 7 <script type="text/javascript" src="include/ext-3.4.0/ux/GridSummary.js"></script> <!-- 合计插件 -->
8 <script type="text/javascript" src="demo.js"></script> <!-- 示例js -->
4.Grid代码 var url = "RequestAction/AT/Req_CT_SHIPMENTS.aspx?Action=GetDetailAll&S_SHIPMENTS_M_GUID=" + guid; //复选框 var sm = new Ext.grid.CheckboxSelectionModel(); var textFileldVehicle = new Ext.form.TextField ({ allowBlank: false, blankText: "请输入车号", maxLength: 50 });
var numField = new Ext.form.NumberField({ allowNegative: false, allowDecimals: true, allowFormat: true, decimalPrecision: 2, allowBlank: false, blankText: '金额必须大于零' }); //字段集合 var fields = [ { name: 'S_VEHICLE_NUMBER' }, { name: 'N_QUANTITY' }, { name: 'N_FREIGHT' }, { name: 'S_GUID' }, { name: 'S_SHIPMENTS_M_GUID' } ]; var proxy = new Ext.data.HttpProxy({ url: url }); //数据读取器 var reader = new Ext.data.JsonReader({ totalProperty: "totalPorperty", //数据总条数 root: "rows", //将要显示数据的数组 id: "S_GUID", //每一行数据的唯一记录 fields: fields });
//列集合 其中 summaryType: 'sum' 为求和
var cm = new Ext.grid.ColumnModel ({ columns: [sm, new Ext.grid.RowNumberer({ header: 'NO', width: 30, align: 'center' }), { header: '车号', dataIndex: 'S_VEHICLE_NUMBER', editor: textFileldVehicle, summaryRenderer: function (v, params, data) { return '合计'; } }, { header: '重量', dataIndex: 'N_QUANTITY', summaryType: 'sum', renderer: formatNumberDefault, align: 'right', editor: numField }, { header: '运费', dataIndex: 'N_FREIGHT', summaryType: 'sum', renderer: formatNumberDefault, align: 'right', editor: numField} ] }); //如果全部列都可排序否则单个设置 cm.defaultSortable = false;
//创建一个store var shipmentsDetailstore = new Ext.data.Store({ proxy: proxy, reader: reader, autoDestroy: true, autoLoad: { params: { start: 0, limit: pageSize} } });
//插入行按钮 var btn_Insert = new Ext.Button({ text: '插入行', iconCls: 'insert', handler: function () { //定义一个recode对象 var initValue = createShipmentDetailRow(); grid.stopEditing(); var maxRowIndex = grid.getStore().getCount(); grid.getStore().insert(maxRowIndex, initValue); //在第一个位置插入 grid.view.refresh(); grid.getSelectionModel().selectLastRow(); grid.getView().focusRow(maxRowIndex); //焦点标记行 grid.startEditing(maxRowIndex, 2); //单元格转换成编辑状态 } }); //删除行按钮 var btn_Remove = new Ext.Button({ text: '删除行', iconCls: 'delete', handler: function () { grid.stopEditing(); var rows = grid.getSelectionModel().getSelections(); if (rows == undefined || rows.length == 0) { setShipmentStatusBarText('error', '请选择需要删除的行!'); return; //判断记录集是否为空,为空返回 } grid.getStore().remove(rows); grid.view.refresh(); } });
//工具栏 var tbar = new Ext.Toolbar({ cls: 'top-toolbar', items: [btn_Insert, '-', btn_Remove] }); var summary = new Ext.ux.grid.GridSummary(); //创建GRID var grid = new Ext.grid.EditorGridPanel ({ id: 'ShipmentsDetailGirdPanel', deferredRender: false, enableColumnHide: false, enableHdMenu: false, columnLines: true, enableColumnMove: false, store: shipmentsDetailstore, sm: sm, cm: cm, loadMask: true, //自适应宽度 参数为列数 // autoExpandColumn: 4, //超过长度带自动滚动条 autoScroll: true, border: false, nocache: false, timeout: 10, clicksToEdit: 1, scripts: true, loadMask: { msg: '正在加载数据,请稍侯……' }, tbar: tbar, view: new Ext.ux.grid.BufferView({ rowHeight: 25, scrollDelay: true, forceFit: true, deferEmptyText: true, emptyText: "无数据" }), plugins: summary }); 5.此时会看到合计行字体偏小 添加样式 .x-grid3-summary-row .x-grid3-cell-inner { FONT: 12.5px tahoma,arial,helvetica,sans-serif } 6.此示例Ext版本为 3.4.0
人们对软件架构存在非常多的误解,其中一个最为普遍的误解就是:将架构(Architecture)和框架(Framework)混为一谈。 框架是一种特殊的软件,它并不能提供完整无缺的解决方案,而是为你构建解决方案提供良好的基础。框架是半成品。典型地,框架是系统或子系统的半成品;框架中的服务可以被最终应用直接调用,而框架中的扩展点是供应用开发人员定制的“可变化点”。 软件架构不是软件,而是关于软件如何设计的重要决策。软件架构决策涉及到如何将软件系统分解成不同的部分、各部分之间的静态结构关系和动态交互关系等。经 过完整的开发过程之后,这些架构决策将体现在最终开发出的软件系统中;当然,引入软件框架之后,整个开发过程变成了“分两步走”,而架构决策往往会体现在 框架之中。或许,人们常把架构和框架混为一谈的原因就在于此吧! 节选自《软件架构设计》书稿 原文出自: http://blog.csdn.net/lovingprince/article/details/3347248
Web集群是由多个同时运行同一个web应用的服务器组成,在外界看来就像一个服务器一样,这多台服务器共同来为客户提供更高性能的服务。集群更标准的定 义是:一组相互独立的服务器在网络中表现为单一的系统,并以单一系统的模式加以管理,此单一系统为客户工作站提供高可靠性的服务。 而负载均衡的任务就是负责多个服务器之 间(集群内)实现合理的任务分配,使这些服务器(集群)不会出现因某一台超负荷、而其他的服务器却没有充分发挥处理能力的情况。负载均衡有两个方面的含 义:首先,把大量的并发访问或数据流量分担到多台节点上分别处理,减少用户等待响应的时间;其次,单个高负载的运算分担到多台节点上做并行处理,每个节点 设备处理结束后,将结果汇总,再返回给用户,使得信息系统处理能力可以得到大幅度提高 因此可以看出,集群和负载均衡有本质上的不同,它们是解决两方面问题的不同方案,不要混淆。 集群技术可以分为三大类: 1、高性能性集群(HPC Cluster) 2、高可用性集群(HA Cluster) 3、高可扩展性集群 一、高性能性集群(HPC Cluster) 指以提高科学计算能力为目标的集群技术。该集群技术主要用于科学计算,这里不打算介绍,如果感兴趣可以参考相关的资料。 二、高可用性集群(HA Cluster) 指为了使群集的整体服务尽可能可用,减少服务宕机时间为目的的集群技术。如果高可用性集群中的某节点发生了故障,那么这段时间内将由其他节点代替它的工作。当然对于其他节点来讲,负载相应的就增加了。 为了提高整个系统的可用性,除了提高计算机各个部件的可靠性以外,一般情况下都会采用该集群的方案。 对于该集群方案,一般会有两种工作方式: ①主-主(Active-Active)工作方式 这是最常用的集群模型,它提供了高可用性,并且在只有一个节点时也能提供可以接受的性能,该模型允许最大程度的利用硬件资源。每个节点都通过网络对客户机 提供资源,每个节点的容量被定义好,使得性能达到最优,并且每个节点都可以在故障转移时临时接管另一个节点的工作。所有的服务在故障转移后仍保持可用,但 是性能通常都会下降。 这是目前运用最为广泛的双节点双应用的Active/Active模式。 支撑用户业务的应用程序在正常状态下分别在两台节点上运行,各自有自己的资源,比如IP地址、磁盘阵列上的卷或者文件系统。当某一方的系统或者资源出现故障时,就会将应用和相关资源切换到对方的节点上。 这种模式的最大优点是不会有服务器的“闲置”,两台服务器在正常情况下都在工作。但如果有故障发生导致切换,应用将放在同一台服务器上运行,由于服务器的处理能力有可能不能同时满足数据库和应用程序的峰值要求,这将会出现处理能力不够的情况,降低业务响应水平。 ②主-从(Active-Standby)工作方式 为了提供最大的可用性,以及对性能最小的影响,主-从工作方式需要一个在正常工作时处于备用状态的节点,主节点处理客户机的请求,而备用节点处于空闲状态,当主节点出现故障时,备用节点会接管主节点的工作,继续为客户机提供服务,并且不会有任何性能上影响。 两节点的Active/Standby模式是HA中最简单的一种,两台服务器通过双心跳线路组成一个集群。应用Application联合各个可选的系统组件如:外置共享的磁盘阵列、文件系统和浮动IP地址等组成业务运行环境。 PCL为此环境提供了完全冗余的服务器配置。这种模式的优缺点: - 缺点:Node2在Node1正常工作时是处于“闲置”状态,造成服务器资源的浪费。
- 优点:当Node1发生故障时,Node2能完全接管应用,并且能保证应用运行时的对处理能力要求。
三、高可扩展性集群 这里指带有负载均衡策略(算法)的服务器群集技术。带负载均衡集群为企业需求提供了更实用的方案,它使负载可以在计算机集群中尽可能平均地分摊处理。而需 要均衡的可能是应用程序处理负载或是网络流量负载。该方案非常适合于运行同一组应用程序的节点。每个节点都可以处理一部分负载,并且可以在节点之间动态分 配负载, 以实现平衡。对于网络流量也是如此。通常,单个节点对于太大的网络流量无法迅速处理,这就需要将流量发送给在其它节点。还可以根据每个节点上不同的可用资 源或网络的特殊环境来进行优化。 负载均衡集群在多节点之间按照一定的策略(算法)分发网络或计算处理负载。负载均衡建立在现有网络结构之上,它提供了一种廉价有效的方法来扩展服务器带宽,增加吞吐量,提高数据处理能力,同时又可以避免单点故障。 前面已经说过负载均衡的作用是在多个节点之间按照一定的策略(算法)分发网络或计算处理负载。负载均衡可以采用软件和硬件来实现。一般的框架结构可以参考下图。 后 台的多个Web节点上面有相同的Web应用,用户的访问请求首先进入负载均衡分配节点(可能是软件或者硬件),由它根据负载均衡策略(算法)合理地分配给 某个Web应用节点。每个Web节点相同的内容做起来不难,所以选择负载均衡策略(算法)是个关键问题。下面会专门介绍均衡算法。 web 负载均衡的作用就是把请求均匀的分配给各个节点,它是一种动态均衡,通过一些工具实时地分析数据包,掌握网络中的数据流量状况,把请求理分配出去。对于不 同的应用环境(如电子商务网站,它的计 算负荷大;再如网络数据库应用,读写频繁,服务器的存储子系统系统面临很大压力;再如视频服务应用,数据传输量大,网络接口负担重压。),使用的均衡策略 (算法)是不同的。 所以均衡策略(算法)也就有了多种多样的形式,广义上的负载均衡既可以设置专门的网关、负载均衡器,也可以通过一些专用软件与协议来实现。在OSI七层协 议模型中的第二(数据链路层)、第三(网络层)、第四(传输层)、第七层(应用层)都有相应的负载均衡策略(算法),在数据链路层上实现负载均衡的原理是 根据数据包的目的MAC地址选择不同的路径;在网络层上可利用基于IP地址的分配方式将数据流疏通到多个节点;而传输层和应用层的交换(Switch), 本身便是一种基于访问流量的控制方式,能够实现负载均衡。 目前,基于负载均衡的算法主要有三种:轮循(Round-Robin)、最小连接数(Least Connections First),和快速响应优先(Faster Response Precedence)。 ①轮循算法,就是将来自网络的请求依次分配给集群中的节点进行处理。 ②最小连接数算法,就是为集群中的每台服务器设置一个记数器,记录每个服务器当前的连接数,负载均衡系统总是选择当前连接数最少的服务器分配任务。 这要比"轮循算法"好很多,因为在有些场合中,简单的轮循不能判断哪个节点的负载更低,也许新的工作又被分配给了一个已经很忙的服务器了。 ③快速响应优先算法,是根据群集中的节点的状态(CPU、内存等主要处理部分)来分配任务。 这一点很难做到,事实上到目前为止,采用这个算法的负载均衡系统还很少。尤其对于硬件负载均衡设备来说,只能在TCP/IP协议方面做工作,几乎不可能深入到服务器的处理系统中进行监测。但是它是未来发展的方向。 上面是负载均衡常用的算法,基于以上负载均衡算法的使用方式上,又分为如下几种: 1、DNS轮询 最早的负载均衡技术是通过DNS来实现的,在DNS中为多个地址配置同一个名字,因而查询这个名字的客户机将得到其中一个地址,从而使得不同的客户访问不同的服务器,达到负载均衡的目的。 DNS负载均衡是一种简单而有效的方法,但是它不能区分服务器的差异,也不能反映服务器的当前运行状态。当使用DNS负载均衡的时候,必须尽量保证不同的 客户计算机能均匀获得不同的地址。由于DNS数据具备刷新时间标志,一旦超过这个时间限制,其他DNS服务器就需要和这个服务器交互,以重新获得地址数 据,就有可能获得不同IP地址。因此为了使地址能随机分配,就应使刷新时间尽量短,不同地方的DNS服务器能更新对应的地址,达到随机获得地址,然而将过 期时间设置得过短,将使DNS流量大增,而造成额外的网络问题。DNS负载均衡的另一个问题是,一旦某个服务器出现故障,即使及时修改了DNS设置,还是 要等待足够的时间(刷新时间)才能发挥作用,在此期间,保存了故障服务器地址的客户计算机将不能正常访问服务器 2、反向代理服务器 使用代理服务器,可以将请求转发给内部的服务器,使用这种加速模式显然可以提升静态网页的访问速度。然而,也可以考虑这样一种技术,使用代理服务器将请求均匀转发给多台服务器,从而达到负载均衡的目的。 这种代理方式与普通的代理方式有所不同,标准代理方式是客户使用代理访问多个外部服务器,而这种代理方式是代理多个客户访问内部服务器,因此也被称为反向代理模式。虽然实现这个任务并不算是特别复杂,然而由于要求特别高的效率,实现起来并不简单。 使用反向代理的好处是,可以将负载均衡和代理服务器的高速缓存技术结合在一起,提供有益的性能。然而它本身也存在一些问题,首先就是必须为每一种服务都专门开发一个反向代理服务器,这就不是一个轻松的任务。 代理服务器本身虽然可以达到很高效率,但是针对每一次代理,代理服务器就必须维护两个连接,一个对外的连接,一个对内的连接,因此对于特别高的连接请求, 代理服务器的负载也就非常之大。反向代理方式下能应用优化的负载均衡策略,每次访问最空闲的内部服务器来提供服务。但是随着并发连接数量的增加,代理服务 器本身的负载也变得非常大,最后反向代理服务器本身会成为服务的瓶颈。 3、地址转换网关 支持负载均衡的地址转换网关,可以将一个外部IP地址映射为多个内部IP地址,对每次TCP连接请求动态使用其中一个内部地址,达到负载均衡的目的。很多 硬件厂商将这种技术集成在他们的交换机中,作为他们第四层交换的一种功能来实现,一般采用随机选择、根据服务器的连接数量或者响应时间进行选择的负载均衡 策略来分配负载。由于地址转换相对来讲比较接近网络的低层,因此就有可能将它集成在硬件设备中,通常这样的硬件设备是局域网交换机。 原文出自: http://blog.csdn.net/lovingprince/article/details/3290871
//修复办法,谷歌浏览器中,table的单元格实际宽度=指定宽度+padding,所以只要重写gridview里的一个方法,如下:
1 //修复办法,谷歌浏览器中,table的单元格实际宽度=指定宽度+padding,所以只要重写gridview里的一个方法,如下: 2 Ext.override(Ext.grid.GridView,{ 3 getColumnStyle : function(colIndex, isHeader) { 4 var colModel = this.cm, 5 colConfig = colModel.config, 6 style = isHeader ? '' : colConfig[colIndex].css || '', 7 align = colConfig[colIndex].align; 8 9 if(Ext.isChrome){ 10 style += String.format("width: {0};", parseInt(this.getColumnWidth(colIndex))-2+'px'); 11 }else{ 12 style += String.format("width: {0};", this.getColumnWidth(colIndex)); 13 } 14 15 if (colModel.isHidden(colIndex)) { 16 style += 'display: none; '; 17 } 18 19 if (align) { 20 style += String.format("text-align: {0};", align); 21 } 22 23 return style; 24 }, 25 }); 26 [代码] [JavaScript]代码//看看修复过后的效果
原文出自:http://www.oschina.net/code/snippet_201314_15163
使用AES加密时,当密钥大于128时,代码会抛出java.security.InvalidKeyException: Illegal key size or default parameters Illegal key size or default parameters是指密钥长度是受限制的,java运行时环境读到的是受限的policy文件。文件位于${java_home}/jre/lib/security 这种限制是因为美国对软件出口的控制。 解决办法: 去掉这种限制需要下载Java Cryptography Extension (JCE) Unlimited Strength Jurisdiction Policy Files.网址如下。 下载包的readme.txt 有安装说明。就是替换${java_home}/jre/lib/security/ 下面的local_policy.jar和US_export_policy.jar jdk 5: http://www.oracle.com/technetwork/java/javasebusiness/downloads/java-archive-downloads-java-plat-419418.html#jce_policy-1.5.0-oth-JPR jdk6: http://www.oracle.com/technetwork/java/javase/downloads/jce-6-download-429243.html 参考http://stackoverflow.com/questions/6481627/java-security-illegal-key-size-or-default-parameters 原文出自:http://blog.csdn.net/shangpusp/article/details/7416603
随笔- 50 文章- 0 评论- 180 jQuery中所支持的异步模型为: - Callbacks,回调函数列队。
- Deferred,延迟执行对象。
- Promise,是Deferred只暴露非状态改变方法的对象。
这些模型都很漂亮,但我想要一种更帅气的异步模型。 Thread? 我们知道链式操作是可以很好的表征运行顺序的(可以参考我的文章《jQuery链式操作》),然而通常基于回调函数或者基于事件监听的异步模型中,代码的执行顺序不清晰。 Callbacks模型实际上类似一个自定义事件的回调函数队列,当触发该事件(调用Callbacks.fire())时,则回调队列中的所有回调函数。 Deferred是个延迟执行对象,可以注册Deferred成功、失败或进行中状态的回调函数,然后通过触发相应的事件来回调函数。 这两种异步模型都类似于事件监听异步模型,实质上顺序依然是分离的。 当然Promise看似能提供我需要的东西,比如Promise.then().then().then()。但是,Promise虽然成功用链式操作明确了异步编程的顺序执行,但是没有循环,成功和失败分支是通过内部代码确定的。 个人认为,Promise是为了规范化后端nodejs中I/O操作异步模型的,因为I/O状态只有成功和失败两种状态,所以他是非常成功的。 但在前端,要么只有成功根本没有失败,要么不止只有两种状态,不应当固定只提供三种状态的方案,我觉得应该提供可表征多状态的异步方案。 这个大家可以在something more看到。 我想要一种类似于线程的模型,我们在这里称为Thread,也就是他能顺序执行、也能循环执行、当然还有分支执行。 顺序执行 线程的顺序执行流程,也就是类似于: 这样就是依次执行do1,do2,do3。因为这是异步模型,所以我们希望能添加wait方法,即类似于: do1(); wait(1000); //等待1000ms do2(); wait(1000); //等待1000ms do3(); wait(1000); //等待1000ms
不使用编译方法的话,使用链式操作来表征顺序,则实现后的样子应当是这样的: 1 Thread(). //获取线程 2 then(do1). //然后执行do1 3 wait(1000). //等待1000ms 4 then(do2). //然后执行do2 5 wait(1000). //等待1000ms 6 then(do3). //然后执行do3 7 wait(1000); //等待1000ms 循环执行 循环这很好理解,比如for循环: 1 for(; true;){ 2 dosomething(); 3 wait(1000); 4 } 进行无限次循环执行do,并且每次都延迟1000ms。则其链式表达应当是这样的: 1 Thread(). //获取线程 2 loop(-1). //循环开始,正数则表示循环正数次,负数则表示循环无限次 3 then(dosomething). //然后执行do 4 wait(1000). //等待1000ms 5 loopEnd(); //循环结束 这个可以参考后面的例子。 分支执行 分支也就是if...else,比如: 1 if(true){ 2 doSccess(); 3 }else{ 4 doFail(); 5 } 6 7 那么其链式实现应当是: 1 Thread(). //获得线程 2 right(true). //如果表达式正确 3 then(doSccess). //执行doSccess 4 left(). //否则 5 then(doFail). //执行doFail 6 leftEnd(). //left分支结束 7 rightEnd(); //right分支结束 声明变量 声明变量也就是: 可被其它函数使用。那么我们的实现是:
1 Thread(). //得到线程 2 define("hello world!"). //将回调函数第一个参数设为hello world! 3 then(function(a){alert(a);}); //获取变量a,alert出来 顺序执行实现方案 Thread实际上是一个打包函数Fn队列。 所谓打包函数就是将回调函数打包后产生的新的函数,举个例子: 1 function package(callback){ 2 return function(){ 3 callback(); 4 // 干其他事情 5 } 6 } 这样我们就将callback函数打包起来了。 Thread提供一个fire方法来触发线程取出一个打包函数然后执行,打包函数执行以后回调Thread的fire方法。 那么我们就可以顺序执行函数了。 现在只要打包的时候设置setTimeout执行,则这个线程就能实现wait方法了。 循环执行实现方案 循环Loop是一个Thread的变形,只不过在执行里面的打包函数的时候使用另外一种方案,通过添加一个指针取出,执行完后触发Loop继续,移动指针取出下一个打包函数。 分支执行实现方案 分支Right和Left也是Thread的一种变形,开启分支的时候,主Thread会创建两个分支Right线程和Left线程,打包一个触发分支Thread的函数推入队列,然后当执行到该函数的时候判断触发哪个分支执行。 其中一个队列执行结束后回调主Thread,通知进行下一步。 例子 由于该方案和wind-asycn非常相似,所以我们拿wind.js中的clock例子进行改造看看其中的差别吧。 wind.js中的例子: 我的例子: Something more? 我们提供了on方法将事件转成分支来执行。 举个例子页面有个按钮“点我”,但是我们希望打开页面5秒内单击没有效,5秒后显示“请点击按钮”后,单击才会出现“你成功点击了”。 使用on分支是这样的:
1 <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN"> 2 <html> 3 <head> 4 <title>on - asThread.js Sample</title> 5 <meta http-equiv="X-UA-Compatible" content="IE=9" /> 6 <script src="asThread.js"></script> 7 </head> 8 <body> 9 <button id = "b">点我</button> 10 <script> 11 var ele = document.getElementById("b"); 12 13 Thread(). // 获得线程 14 then(function(){alert("请点击按钮")}, 5000). //然后等5秒显示"请点击按钮" 15 on(ele, "click"). // 事件分支On开始,如果ele触发了click事件 16 then(function(){alert("你成功点击了")}). //那么执行你成功点击了 17 onEnd(). // 事件分支On结束 18 then(function(){alert("都说可以的了")}). // 然后弹出"都说可以的了" 19 run(); //启动线程 20 </script> 21 </body> 22 </html> 自定义事件也可以哦,只要在.on时候传进去注册监听函数,和删除监听函数就行了。比如: 1 function addEvent(__elem, __type, __handler){ 2 //添加监听 3 } 4 5 function removeEvent(__elem, __type, __handler){ 6 //删除监听 7 } 8 9 Thread(). 10 on(ele, "success", addEvent, removeEvent). 11 then(function(){alert("成功!")}). 12 onEnd(). 13 run(); 当然实际上我们还可以注册多个事件分支。事件分支是并列的,也就是平级的事件分支没有现有顺序,所以我们能这样: 1 <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN"> 2 <html> 3 <head> 4 <title>on - asThread.js Sample</title> 5 <meta http-equiv="X-UA-Compatible" content="IE=9" /> 6 <script src="asThread.js"></script> 7 </head> 8 <body> 9 <button id = "b">点我</button> 10 <button id = "c">点我</button> 11 <script> 12 var ele0 = document.getElementById("b"), 13 ele1 = document.getElementById("c"); 14 15 Thread(). // 获得线程 16 then(function(){alert("请点击按钮")}, 5000). //然后等5秒显示"请点击按钮" 17 on(ele0, "click"). // 事件分支On开始,如果ele0触发了click事件 18 then(function(){alert("你成功点击了")}). //那么执行你成功点击了 19 onEnd(). // 事件分支On结束 20 on(ele1, "click"). // 事件分支On开始,如果ele1触发了click事件 21 then(function(){alert("你成功点击了")}). //那么执行你成功点击了 22 onEnd(). // 事件分支On结束 23 then(function(){alert("都说可以的了")}). // 然后弹出"都说可以的了" 24 run(); //启动线程 25 </script> 26 </body> 27 </html> 一个线程不够用?只要输入名字就能开辟或者得到线程了。 系统会自动初始化一个主线程,当不传参数时就直接返回主线程: 但如果主线程正在用想开辟一个线程时,只要给个名字就行,比如: Thread("hello") //得到名字是hello的线程 那么下次再想用该线程时只要输入相同的名字就行了: Thread("hello") //得到hello线程 默认只最多只提供10个线程,所以用完记得删掉: IE10已经提供了setImmediate方法,而其他现代浏览器也可以模拟该方法,其原理是推倒线程末端,使得浏览器画面能渲染,得到比setTimeout(0)更快的响应。 我们通过接口.imm来提供这一功能。比如: Thread(). imm(function(){alert("hello world")}). run(); 这方法和.then(fn)不太一样,.then(fn)是可能阻塞当前浏览器线程的,但.imm(fn)是将处理推到浏览器引擎列队末端,排到队了在运行。 所以如果你使用多个Thread(伪多线程),而又希望确保线程是并行运行的,那么请使用.imm来替代.then。 当然对于老版IE,只能用setTimeout(0)替代了。 分支Right传的参数如果只是布尔值肯定很不爽,因为这意味着分支是静态的,在初始化时候就决定了,但我们希望分支能在执行到的时候再判断是走 Right还是Left,所以我们提供了传参可以是函数(但是函数返回值需要是布尔值,否则……╮(╯▽╰)╭也会转成布尔值的……哈哈)。比如: 1 fucntion foo(boolean){ 2 return !boolean; 3 } 4 5 Thread(). 6 define(true). 7 right(foo). 8 then(function(){/*这里不会运行到*/}). 9 rightEnd(). 10 run(); Enjoy yourself!! 项目地址 https://github.com/miniflycn/asThread 原问出自: http://www.cnblogs.com/justany/archive/2013/01/25/2874602.html
如何在 WebSphere 中解决 jar 包冲突 郝 爱丽, 高级软件工程师, IBM 中国软件开发中心 简介: Jar 包冲突问题是在大型 Java 软件开发中经常遇到的问题,系统开发人员经常会为解决类似的问题耗费大量的时间进行调试和测试,本文根据各种际情况,结合 WebSphere 中类加载器,讨论了几种解决 jar 包冲突问题的办法,并给出了具体实现的步骤及源代码。 读者定位为具有 Java 和 WebSphere 开发经验的开发人员。 读者可以学习到在 WebSphere 中类加载器的定义以及解决 jar 包冲突问题的几种办法,并可以直接使用文章中提供的 Java 代码,从而节省他们的开发和调试时间,提高效率。 大型的基于 WebSphere 的项目开发中,同一个 WebSphere Application Server(以下简称 WAS)上会部署多个应用程序,而这多个应用程序必然会共用一些 jar 包,包括第三方提供的工具和项目内部的公共 jar 等。把这些共用的 jar 包提取出来在多个应用程序之间共享,不仅可以统一对这些 jar 包进行维护,同时也提高了 WAS 的性能。但是随着应用的不断扩大,新的应用程序的不断增加,新的应用程序会希望使用一些更高版本的共享 jar 包,而由于系统运行维护的需要,老的应用程序仍然希望用老版本的共享 jar 包,这样就必然造成了共享 jar 包的版本冲突。jar 包版本冲突问题是在大型应用项目的开发中经常遇到的问题,本文试图从 WebSphere 的类加载器入手,讨论几种在不同情况下解决 jar 包冲突问题的办法。 WebSphere 中类加载器介绍 Jar 包冲突实际上是应用程序运行时不能找到真正所需要的类,而影响类的查找和加载的是 JVM 以及 WebSphere 中的类加载器(class loader),为此,我们首先介绍一下 WebSphere 中的类加载器以及一些相关的概念。 WebSphere 中类加载器层次结构 Java 应用程序运行时,在 class 执行和被访问之前,它必须通过类加载器加载使之有效,类加载器是 JVM 代码的一部分,负责在 JVM 虚拟机中查找和加载所有的 Java 类和本地的 lib 库。类加载器的不同配置影响到应用程序部署到应用程序服务器上运行时的行为。JVM 和 WebSphere 应用程序服务器提供了多种不同的类加载器配置, 形成一个具有父子关系的分层结构。WebSphere 中类加载器的层次结构图 1 所示: 图 1:WebSphere 中类加载器的层次结构 如上图所示,WebSphere 中类加载器被组织成一个自上而下的层次结构,最上层是系统的运行环境 JVM,最下层是具体的应用程序,上下层之间形成父子关系。 - JVM Class loader:位于整个层次结构的最上层,它是整个类加载器层次结构的根,因此它没有父类加载器。这个类加载器负责加载 JVM 类, JVM 扩展类,以及定义在 classpath 环境变量上的所有的 Java 类。
- WebSphere Extensions Class loader:WebSphere 扩展类加载器 , 它将加载 WebSphere 的一些 runtime 类,资源适配器类等。
- WebSphere lib/app Class loader:WebSphere 服务器类加载器,它将加载 WebSphere 安装目录下 $(WAS_HOME)/lib/app 路径上的类。 在 WAS v4 版本中,WAS 使用这个路径在所有的应用程序之间共享 jar 包。从 WAS v5 开始, 共享库功能提供了一种更好的方式,因此,这个类加载器主要用于一些原有的系统的兼容。
- WebSphere "server" Class loader:WebSphere 应用服务器类加载器。 它定义在这个服务器上的所有的应用程序之间共享的类。WAS v5 中有了共享库的概念之后,可以为应用服务器定义多个与共享库相关联的类加载器,他们按照定义的先后顺序形成父子关系。
- Application Module Class Loader:应用程序类加载器,位于层次结构的最后一层,用于加载 J2EE 应用程序。根据应用程序的类加载策略的不同,还可以为 Web 模块定义自己的类加载器。
关于 WebSphere 的类加载器的层次结构,以下的几点说明可能更有助于进一步的理解类的查找和加载过程: - 每个类加载器负责在自身定义的类路径上进行查找和加载类。
- 一个子类加载器能够委托它的父类加载器查找和加载类,一个加载类的请求会从子类加载器发送到父类加载器,但是从来不会从父类加载器发送到子类加载器。
- 一旦一个类被成功加载,JVM 会缓存这个类直至其生命周期结束,并把它和相应的类加载器关联在一起,这意味着不同的类加载器可以加载相同名字的类。
- 如果一个加载的类依赖于另一个或一些类,那么这些被依赖的类必须存在于这个类的类加载器查找路径上,或者父类加载器查找路径上。
- 如果一个类加载器以及它所有的父类加载器都无法找到所需的类,系统就会抛出 ClassNotFoundExecption 异常或者 NoClassDefFoundError 的错误。
类加载器的委托模式 类加载器有一个重要的属性:委托模式(Delegation Mode,有时也称为加载方式:Classloader mode)。委托模式决定了类加载器在查找一个类的时候, 是先查找类加载器自身指定的类路径还是先查找父类加载器上的类路径。 类加载器的委托模式有两个取值: - Parent_First:在加载类的时候,在从类加载器自身的类路径上查找加载类之前,首先尝试在父类加载器的类路径上查找和加载类。
- Parent_Last:在加载类的时候,首先尝试从自己的类路径上查找加载类,在找不到的情况下,再尝试父类加载器类路径。
有了委托模式的概念,我们可以更加灵活的配置在类加载器的层次结构中类的加载和查找方式。表 1 中给出了在 WebSphere 的类加载器层次结构中各个类加载器的委托模式的定义,并给出了不同的类加载器内类的生命周期。 注意:在上表中,"JVM Class loader" 因为在类加载器的最顶层,它没有父类加载器,因此其委托模式为 N/A,"WebSphere Extensions Class loader"和"WebSphere lib/app Class loader"的委托模式固定为表中的取值,不可配置,其它的类加载器的委托模式都是可以配置的。 WebSphere 中的类加载器策略 WebSphere 中对类加载器有一些相关的配置,称为类加载器策略(class loader policy)。类加载器策略指类加载器的独立策略(class loader isolation policy), 通过类加载器策略设置,我们可以为 WAS 和应用程序的类加载器进行独立定义。 每个 WAS 可以配置自己的应用程序类加载器策略,WAS 中的每个应用程序也可以配置自己的 Web 模块类加载器策略,下面我们对这两种策略分别介绍。 1 .应用服务器(WAS)配置:应用程序类加载器策略 应用服务器对应用程序类加载器策略有两种配置: - Single:整个应用服务器上的所有应用程序使用同一个类加载器。在这种配置下,每个应用程序不再有自己的类加载器。
- Multiple:应用服务器上的每个应用程序使用自己的类加载器。
2 .应用程序配置:Web 模块类加载器策略 应用程序中对 Web 模块类加载器有两种配置: - Application:整个应用程序内的所有的实用程序 jar 包和 Web 模块使用同一个类加载器。
- Module:应用程序内的每个 Web 模块使用自己的类加载器。应用程序的类加载器仍然存在,负责加载应用程序中 Web 模块以外的其它类,包括所有的实用程序 jar 包。
从上面的定义可以看出,不同的类加载器策略的配置下,类加载器的层次结构上的某些类加载器可能不存在。比如在应用程序服务器的应用程序类加载 器策略定义为 single 的情况下,应用程序的类加载器将不存在,同一个应用服务器上的所有应用程序将共用同一个类加载器,这也就意味着不同的应用程序之间的类是共享的,应用程序 间不能存在同名的类。 回页首 在 WebSphere 中解决 jar 包冲突 Jar 包冲突问题实际上就是应用程序希望用某一个确定版本的 jar 包中的类,但是类加载器却找到并加载了另外一个版本的 jar 包中的类。在上一部分介绍了 WebSphere 中类加载器的基本概念和相关配置之后,我们来看如何在 WebSphere 中解决 jar 包冲突。 在 WAS v5 版本之前,使用共享 jar 包的方式是将 jar 包放在 $(WAS_HOME)/lib/app 路径下,从上一部分中,我们可以看到,这个路径正是"WebSphere lib/app Class loader" 类加载器的类查找路径,WebSphere 会查找这个路径以取得相应得 jar 包中的 Java 类,从而做到在 WebSphere ND 上的多个应用程序之间共享 jar 包的目的。但是这样做的一个缺点就是这些共享 jar 包暴露给 WebSphere ND 上所有的应用程序,对于那些希望使用 jar 包其它版本的应用程序,这些 jar 包也同样存在在了它们的类加载器类路径上,因此,就不可避免的会造成版本的冲突。在 WAS v5 版本及之后,增加了共享库(shared library)的概念,推荐的在多个应用程序间共享 jar 包并避免 jar 包冲突的方式是使用共享库。 具体分析引起 jar 包冲突的情况,主要有三种: - 多个应用程序间 jar 包冲突:多个应用程序间由于使用了共享 jar 包的不同版本而造成 jar 包版本冲突。
- 应用程序中多个 Web 模块间 jar 包冲突:同一个应用程序内部,不同的 Web 模块间同时使用一个 jar 包的不同版本而造成 jar 包版本冲突。
- 应用程序中同一个 Web 模块内 jar 包冲突:同一个应用程序内部,同一个 Web 模块内,由于需要同时使用同一个 jar 包的两个版本而造成的 jar 包冲突
本部分根据这三种 jar 包冲突的情况,讨论三种解决 jar 包冲突的办法,并具体讨论三种解决办法的实现步骤和适用情况: - 共享库方式解决 jar 包冲突:主要解决应用程序间的 jar 包冲突问题
- 打包到 Web 模块中解决 jar 包冲突:主要解决应用程序中多个 Web 模块间 jar 包冲突问题
- 命令行运行方式解决 jar 包冲突:主要解决应用程序中同一个 Web 模块内 jar 包冲突问题
共享库方式解决 jar 包冲突 在 WAS v5 中,提供了一种很好的机制,使得 jar 包只存在于需要这个 jar 包的应用程序的类加载器的路径上,而其它的应用程序不受它的任何影响,这就是共享库(Shared library)。共享库可以用在应用服务器级别和应用程序级别,使用应用程序级别的共享库,其好处就是在不同的应用程序之间使用共享 jar 包的不同版本。我们可以为一些通用 jar 包的每个不同版本定义成不同的共享库,应用程序希望使用哪个版本,就把这个版本的共享库放到应用程序的类加载器的类路径上,这种方式有效的解决了应用程序 之间 jar 包冲突的问题。 下面举例介绍定义和使用共享库的具体方法,本例中,假设存在 xerces.jar 包版本冲突。 1 . 定义共享库 系统管理员可以在 WebSphere 的 Admin console 中定义共享库,可以分别在 Cell、Node 以及 server 的级别上定义。 步骤一 : 进入 Admin console,选择 Environment > Shared Library > new。如图 2 所示: 图 2:WebSphere Admin Console 中进入共享库页面 步骤二: 给出共享库的名字,并指定共享的文件和目录。多个不同的文件 / 目录之间通过"Enter"键分隔,且不能有路径分隔符,如":"和";"等。如图 3 所示: 图 3:WebSphere Admin Console 中添加共享库 步骤三:点击 Apply 或者 OK 之后,就添加了一个名字为 Xerces V2.0 的共享库。记住添加完成后一定要在 admin console 保存配置。如图 4 所示: 图 4:WebSphere Admin Console 中共享库列表-
2 .安装应用程序 进入 Admin console,选择 Applications > Install New Application 安装应用程序。请参照 IBM WebSphere 的 Admin console 使用手册进行安装新的应用程序,此处不再详细介绍。 3 .将共享库关联到应用程序 步骤一:进入 Admin console,选择 Applications > Enterprise applications ,并选择需要使用共享库的应用程序。注意:因为要改变应用程序的设置,所以如果应用程序已经运行,需要先停掉应用程序。如图 5 所示: 图 5:WebSphere Admin Console 中选择需要配置的应用程序 步骤二 : 点击应用程序,进入后,选择 Libraries。如图 6 所示: 图 6:WebSphere Admin Console 中选择应用程序库属性步骤三 : 点击 Add,为应用程序添加共享库。如图 7 所示: 图 7:WebSphere Admin Console 中应用程序添加库 步骤四 : 从下拉列表中选择所需要的共享库,点击 OK。如图 8 所示: 图 8:WebSphere Admin Console 中应用程序添加库页面指定所用的共享库 这样,Xerces V2.0 共享库定义的 xerces 版本就存在于了应用程序类加载器的类加载路径上。注意,在添加完成后要保存服务器的设置。 4 .设置应用程序的类加载器的委托模式为 Parent_Last 为了进一步防止共享库定义的 jar 包的其它版本已经存在于 JVM 或者 WebSphere 的类加载器路径上,还需要设置应用程序的类加载器的委托方式为 Parent_Last。 通过上面的配置,即使 xerces 的其它版本已经存在于系统中,应用程序在运行时,其类加载器也会首先查找并加载指定的共享库中的 xerces 版本。这样我们就通过使用共享库的方式,解决了 jar 包版本冲突问题。 打包到 Web 模块中解决 jar 包冲突 共享库的方式,只是在应用程序的层次上,在多个应用程序之间解决了共享 jar 包造成的版本冲突问题,如果一个应用程序的内部,其中一个 Web 模块使用了一个 jar 包的 A 版本,而另一个 Web 模块使用这个 jar 包的 B 版本,在这种情况下造成的 jar 包冲突,共享库的方式是无法解决的,我们可以考虑将其中一个在多个应用程序间共享的 jar 包版本,比如 A 版本,定义成共享库,或者放在"WebSphere lib/app Class loader"类加载器路径上供多个应用程序使用,而将 B 版本的 jar 包打包到使用它的 Web 模块中的方式来解决冲突。 其次,目前很多在线的系统是 WAS v4 的遗留系统,其上运行的应用程序已经使用了"WebSphere lib/app Class loader"类加载器,将 jar 包放在 $(WAS_HOME)/lib/app 目录下进行共享。如果由于其中某个应用程序的升级或者新增加某个应用程序,需要使用某个共享 jar 包的其它版本,在这种情况下,为了减少对系统的影响,也可以考虑将这个共享 jar 包的新版本打包到升级(或新增)的应用程序中的方式来解决 jar 包冲突。 由于 Web 模块的 WebContent/WEB-INFO/lib 目录在应用程序 Web 模块的类加载器查找路径上,因此,我们可以把 jar 包放在这个目录下,Web 模块的类加载器将自动查找并加载这个 jar 包中的类。 步骤一:在 WSAD IE 集成开发环境中,将冲突 jar 包放在 Web 模块的 WebContent/WEB-INFO/lib 目录下。如图 11 所示: 图 11:WSAD IE 中为 Web 模块添加库步骤二:在 Admin console 中,将应用程序部署到 WebSphere server 上之后,进入 Applications > Enterprise Applications, 选择相应的应用程序,并确认应用程序不在运行状态 ( 参见前面章节中选择应用程序的步骤 )。点击进入应用程序,确认应用程序的类加载器的委托模式为 Parent_First, 应用程序的类加载器策略为 Module。如图 12 所示: 图 12:WebSphere Admin Console 中应用程序属性配置页面步骤三:在同一个页面上,选择 Web Modules,点击进入。如图 13 所示: 图 13:WebSphere Admin Console 中选择应用程序 Web 模块属性步骤四:点击相应的包含冲突 jar 包的 Web 模块,设置 Web 模块的类加载器的委托模式为 Parent_Last。注意:在设置完成后要保存服务器配置,并启动应用程序。如图 14 所示: 图 14:WebSphere Admin Console 中为 Web 模块指定类加载器委托模式 将冲突 jar 包打包到 Web 模块中,并设置相应 Web 模块的类加载器的委托模式为 Parent_Last,应用程序在运行过程中加载类的时候,这个 Web 模块的类加载器会首先查找 WebContent/WEB-INFO/lib 目录下的 jar 包进行类的加载;而对于其它的 Web 模块,由于其类加载器的委托模式仍然为缺省的 Parent_First,它们的类加载器仍然首先从应用程序的共享库或者 WebSphere 的共享路径上加载 jar 包中的类,从而解决了 jar 包冲突的问题。 命令行运行方式解决 jar 包冲突 不论是设置共享库,还是将冲突 jar 包打包到应用程序中,其解决的问题都是在应用程序的一个 Web 模块中只使用了冲突 jar 包的一个版本的情况。我们在开发中曾经遇到过这样的情况:应用程序的 Web 模块中已经使用了 1.4 版本的 xerces.jar,由于 Web 功能的扩展,在这个模块中又引入一个新的第三方工具,而这个第三方工具需要使用 2.0 版本的 xerces.jar 才能正常工作,这种情况下的 jar 包冲突如何解决呢? 在前面类加载器的部分已经介绍过,每个应用程序的一个 Web 模块最多只能有一个类加载器,而 Web 模块的类加载器中加载的类的生命周期为整个应用程序的运行期,也就是说,Web 模块加载器不可能同时加载一个类的两个版本,同时,Web 模块的类加载器的委托模式也是在应用程序运行前设置的,在应用程序运行期内无法改变的,因此,上面描述的在一个 Web 模块中同时使用两个版本的 jar 包的问题,象前两种方法那样配置运行在一个 JVM 内的类加载器的设置的方法是无法解决的。 唯一的解决办法就是在应用程序运行的 JVM 外,启动另外一个 JVM 来运行调用冲突 jar 包的代码,因为两个不同的 JVM 可以加载各自的类,从而解决 jar 包冲突问题。 这种情况下,原来使用 jar 包的老版本的方式(包括 jar 包放置路径,共享库设置方式,类加载器的委托模式等)不变,将对 jar 包新版本的调用通过命令行运行方式实现。具体做法是:将对 jar 包新版本内功能的调用,封装到一个可以单独运行的类中,在 Web 模块中以命令行方式运行这个类。同时把这个类以及 jar 包的新版本放在任意一个 was 可访问的路径上(比如 /usr/WebSphere),在命令行的 classpath 参数中包含这个路径(比如 /usr/WebSphere)。 下面通过举例说明命令行运行方式的编程过程,在本例中,假设 TestEar 应用程序的 Web 模块 TestWar 中,已经使用了 conflict_v1.jar,由于新添功能需要使用 conflict_v2.jar 中的 exampleCall() 功能。 冲突 jar 包 conflict_v2.jar 提供的功能: 代码 1:冲突 jar 包 conflict_v2.jar 功能 1 Package com.ibm.conflict; 2 3 Public class ConflictClass{ 4 …… . 5 Public static String exampleCall(string param){ 6 String rs; 7 …… ; 8 Return rs; 9 } 10 …… 不存在冲突问题时的编码举例: 如果没有 jar 包冲突问题,则对这个功能的调用是简单的,只需要将 conflict_v2.jar 放在应用程序自身或者其父类加载器的查找路径上,然后在 Web 模块中直接调用即可,如下: 代码 2:不存在冲突时的调用方式1 Public String methodA(String param){ 2 …… 3 String rs = ConflictClass.exampleCall(param); 4 …… 5 Return rs; 6 }
存在冲突后的命令行运行方式编码举例 针对 jar 包冲突问题,我们需要在 Web 模块中做如下的修改: - 步骤一:将冲突 jar 包放在 was 可访问的路径上,比如 /usr/WebSphere/conflict_v2.jar。
步骤二:将对包含冲突代码的调用封装到一组可单独运行的类中,它们将调用冲突 jar 包的功能,并将结果以系统输出的方式打印到系统标准输出上。 将这些类封装到一个单独的 jar 文件中,比如 workAroundConflict.jar,并将其放在 was 可访问的路径上,比如 /usr/WebSphere/workAroundConflict.jar。
1 Package com.ibm.test; 2 3 Import com.ibm.ConflictClass; 4 5 Public class WorkAround{ 6 Public static void main(String[] args){ 7 String param1=args[0]; 8 String returnStr=ConflictClass.exampleCall (); 9 System.out.println("<RTStr>"+returnStr+"</RTStr>"); 10 Return; 11 } 12 }
代码 3:将对冲突代码的调用写入一个单独的类 WorkAround 步骤三:在 Web 模块中通过命令行方式调用封装的类,通过 classpath 指定所有依赖的 jar 包和类路径。运行封装类,从系统标准输出中取得运行结果。
1 Public static String methodA (String param){ 2 …… 3 String rtStr = ""; 4 String lStr="<RTStr>"; 5 String rStr="<RTStr>"; 6 //put all the dependency jar here 7 String classPath= 8 "/usr/WebSphere/conflict_v2.jar; 9 /usr/WebSphere/workaroundConflict.jar; ……"; 10 String className="com.ibm.test.WorkAround"; 11 String cmdLine="java -classpath " +classPath +" " +className + " "+ param; 12 Try{ 13 Process process = Runtime.getRuntime().exec(cmdLine,null); 14 process.waitFor(); 15 BufferedReader br= new BufferedReader( 16 new InputStreamReader(process.getInputStream())); 17 while ((s = br.readLine())!=null) { 18 if (null == out) 19 out=s; 20 else 21 out+=s; 22 } 23 //get result from out 24 if (null != out){ 25 int lIndex = out.lastIndexOf(lStr); 26 int rIndex = out.lastIndexOf(rStr); 27 rsStr = out.substring(lIndex+lStr.length, rIndex); 28 } 29 30 } catch (Exception e){ 31 e.printStackTrace(); 32 } 33 …… . 34 return rsStr; 35 }
代码 4:在应用程序中通过命令行方式运行 WorkAround -
命令行运行方式通过启动另外一个 JVM 的方式运行冲突代码,在一个不同的 JVM 中加载冲突的类,从而解决了 jar 包冲突问题。 但是命令行运行方式毕竟不是一个很好的方式,它存在以下的弊端: - 命令行运行方式只适用于对冲突 jar 包的使用只是运行一段代码,或者只要求返回简单的字符串结果的情况。对于复杂的交互,命令行方式无法做到。但是如果在别无他法的情况下,可以适当地划分封 装对冲突代码调用的 jar 包的包含范围,尽量将命令行运行的代码接口简单化。
- 命令行运行方式因为启动了另外一个 JVM 来运行,降低了 WebSphere 的性能。
因此,命令行方式只适用于一些极为特殊的情况下解决 jar 包冲突问题。 回页首 结论 本文对基于 WebSphere 的大型项目开发中遇到的 jar 包冲突问题,结合 WebSphere 中类加载器的概念,给出了三种解决办法以及相应的操作步骤和实现代码,并分析了各种方式所适用的具体情况。 项目开发过程中的 jar 包冲突,主要存在以下三种情况: - 多个应用程序间的 jar 包冲突
- 应用程序内多个 Web 模块间的 jar 包冲突
- 应用程序内同一个 Web 模块内部 jar 包冲突
通过对类加载器的分析我们知道,解决 jar 包冲突问题的根本在于合理配置类加载器。在深入理解 WebSphere 中类加载器的层次结构的基础上,我们给出了"共享库解决 jar 包冲突"以及"打包到 Web 模块中解决 jar 包冲突"的办法,通过合理地配置 WebSphere 中类加载器及其委托模式,可以解决大多数的 jar 包冲突问题。但是由于这个层次结构中类加载器只配置到 Web 模块,因此,对于 Web 模块内部的 jar 包冲突问题,类加载器的配置是无法解决的,为此我们给出了"命令行运行方式解决 jar 包冲突"的办法。 表 2 中列出了在不同的 WAS 版本下,三种解决 jar 包冲突问题的办法所适用的 jar 包冲突情况。 表 2:Jar 包冲突解决办法适用的 jar 包冲突情况总结 参考资料 - IBM WebSphere Application Server V5 ClassloaderNamingSpace Guide。
- IBM WebSphere Application Server V5.1 System Management and Configuration。
作者简介 郝爱丽是一位 IBM CSDL 的软件工程师,从事多年 J2EE 开发工作,有丰富的 J2EE 开发经验。目前从事企业电子商务应用的开发和支持。 常红平是一位 IBM CSDL 的软件工程师,IBM certified DB2 DBA 和 IBM certified DB2 developer。他目前正在从事企业电子商务应用的开发。您可以通过 changhp@cn.ibm.com 和他联系。
CXF和Spring结合的非常紧密,默认发布Server端是需要用到Spring的,但是项目中用到的Spring jar包比较老2.0,CXF版本2.3.1,跟Spring不兼容,需要换乘Spring2.5,但是换jar包对原来的项目存在风险,上网搜了一个脱 离Spring运行的方法。 写一个类继承CXFNonSpringServlet,重写loadBus方法。 1 @SuppressWarnings("unchecked") 2 public void loadBus(ServletConfig servletConfig) throws ServletException { 3 super.loadBus(servletConfig); 4 Bus bus = this.getBus(); 5 BusFactory.setDefaultBus(bus); 6 7 Enumeration<String> enums = getInitParameterNames(); 8 while (enums.hasMoreElements()) { 9 String key = enums.nextElement(); 10 String value = getInitParameter(key); 11 try { 12 Class clz = Class.forName(value); 13 try { 14 Endpoint.publish(key, clz.newInstance()); 15 } catch (InstantiationException e) { 16 e.printStackTrace(); 17 } catch (IllegalAccessException e) { 18 e.printStackTrace(); 19 } 20 } catch (ClassNotFoundException e) { 21 e.printStackTrace(); 22 } 23 } 24 } 在web.xml里将要发布的类配置一下,可以配置多个 1 <servlet> 2 <servlet-name>CXFServlet</servlet-name> 3 <servlet-class> 4 com.infodms.ws.util.MyCXFNoSpringServlet 5 </servlet-class> 6 <init-param> 7 <param-name>/TestService</param-name> 8 <param-value>com.infodms.ws.test.TestServiceImpl</param-value> 9 </init-param> 10 <init-param> 11 <param-name>/HelloWorld</param-name> 12 <param-value>com.infodms.ws.test.HelloWorldImpl</param-value> 13 </init-param> 14 </servlet> 15 <servlet-mapping> 16 <servlet-name>CXFServlet</servlet-name> 17 <url-pattern>/ws/*</url-pattern> 18 </servlet-mapping>
配置完成,启动tomcat,报错,还是加载了spring,但是代码确实走了刚刚加的MyCXFNoSpringServlet
1 java.lang.RuntimeException: org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'org.apache.cxf.bus.spring.BusApplicationListener' defined in class path resource [META-INF/cxf/cxf.xml]: Initialization of bean failed; nested exception is java.lang.NoSuchMethodError: org.springframework.context.support.AbstractApplicationContext.addApplicationListener(Lorg/springframework/context/ApplicationListener;)V 2 org.apache.cxf.bus.spring.SpringBusFactory.createBus(SpringBusFactory.java:96)
调试了一下源码,内部需要从环境变量中取得org.apache.cxf.bus.factory的值,如果为空就默认按照spring的方式加载。于是在类的最开始加入一行代码 1 System.setProperty("org.apache.cxf.bus.factory", "org.apache.cxf.bus.CXFBusFactory");
再次启动tomcat,报错,由异常可知WoodstoxValidationImpl类没有默认构造方法,通过反射实例化对象报错,看了一下这个类的源码,确实没有无参构造方法
1 Caused by: java.lang.InstantiationException: org.apache.cxf.wstx_msv_validation.WoodstoxValidationImpl 2 at java.lang.Class.newInstance0(Class.java:340) 3 at java.lang.Class.newInstance(Class.java:308) 4 at org.apache.cxf.bus.extension.Extension.load(Extension.java:110)
原文出自: http://liuqiang5151.iteye.com/blog/840496
摘要: 一、解决基本问题: 在做 RCP 项目的时候经常会遇到一个问题,就是要将一些控制信息输出到 RCP 自身的控制台,那么我们就可以扩展 Eclipse 扩展点 org.eclipse.ui.console.consoleFactories ,来实现我们自己的控制台,解决方法如下: 首先 ,在 plugin.xml 中定义扩展点: plugin.xml: <extension &n... 阅读全文
今天整理系统,发现系统很多页面,只有在IE6下显示正常,其它的都不正常,很是奇怪。所以上网找了一些关于浏览器兼容的问题和解决办法,在此我觉得大牛们总结的比较精彩,分享给网友们! 一、CSS兼容 以下两种方法几乎能解决现今所有兼容. 1, !important (不是很推荐,用下面的一种感觉最安全) 随着IE7对!important的支持, !important 方法现在只针对IE6的兼容.(注意写法.记得该声明位置需要提前.) 代码: <style> #wrapper { width: 100px!important; /* IE7+FF */ width: 80px; /* IE6 */ } </style> 2, IE6/IE77对FireFox <from 针对firefox ie6 ie7的css样式> *+html 与 *html 是IE特有的标签, firefox 暂不支持.而*+html 又为 IE7特有标签. 代码: <style> #wrapper { width: 120px; } /* FireFox */ *html #wrapper { width: 80px;} /* ie6 fixed */ *+html #wrapper { width: 60px;} /* ie7 fixed, 注意顺序 */ </style> 注意: *+html 对IE7的兼容 必须保证HTML顶部有如下声明: 代码: <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://w3.org/TR/html4/loose.dtd"> 二、解决DIV错乱(非常重要) 可以用这个解决多个div对齐时的间距不对, 关于 clear float 的原理可参见 [How To Clear Floats Without Structural Markup] 将以下代码加入Global CSS 中,给需要闭合的div加上 class=”clearfix” 即可,屡试不爽. 代码: <style> /* Clear Fix */ .clearfix:after { content:"."; display:block; height:0; clear:both; visibility:hidden; } .clearfix { display:inline-block; } /* Hide from IE Mac \*/ .clearfix {display:block;} /* End hide from IE Mac */ /* end of clearfix */ </style> 三、其它零碎(不容忽视,细节决定成败) 1. 文字本身的大小不兼容。同样是font-size:14px的宋体文字,在不同浏览器下占的空间是不一样的,ie下实际占高16px,下留白3px,ff 下实际占高17px,上留白1px,下留白3px,opera下就更不一样了。解决方案:给文字设定 line-height 。确保所有文字都有默认的 line-height 值。这点很重要,在高度上我们不能容忍1px 的差异。
2.ff下容器高度限定,即容器定义了height之后,容器边框的外形就确定了,不会被内容撑大,而ie下是会被内容撑大,高度限定失效。所以不要轻易给容器定义height。
3.横向上的撑破容器问题,。如果float 容器未定义宽度,ff下内容会尽可能撑开容器宽度,ie下则会优先考虑内容折行。故,内容可能撑破的浮动容器需要定义width。
小实验:有兴趣大家可以看看这段实验。在不同浏览器下分别测试以下各项代码。
a.<div style=”border:1px solid red;height:10px”></div> b. <div style=”border:1px solid red;width:10px”></div>
c. <div style=”border:1px solid red;float:left”></div> d. <div style=”border:1px solid red;overflow:hidden”></div>
上 面的代码在不同浏览器中是不一样的,实验起源于对小height 值div 的运用,<div style=”height:10px;overflow:hidden”></div>,小height 值要配合overflow:hidden一起使用。实验好玩而已,想说明的是,浏览器对容器的边界解释是大不相同的,容器内容的影响结果各不相同。
4.最被痛恨的,double-margin bug。ie6下给浮动容器定义margin-left 或者margin-right 实际效果是数值的2倍。解决方案,给浮动容器定义display:inline。
5.mirror margin bug,当外层元素内有float元素时,外层元素如定义margin-top:14px,将自动生成margin-bottom:14px。 padding也会出现类似问题,都是ie6下的特产,该类bug 出现的情况较为复杂,远不只这一种出现条件,还没系统整理。解决方案:外层元素设定border 或 设定float。
引申:ff 和ie 下对容器的margin-bottom,padding-bottom的解释有时不一致,似乎与之相关。
6. 吞吃现象。还是ie6,上下两个div,上面的div设置背景,却发现下面没有设置背景的div 也有了背景,这就是吞吃现象。对应上面的背景吞吃现象,还有滚动下边框缺失的现象。解决方案:使用zoom:1。这个zoom好象是专门为解决ie6 bug而生的。
7.注释也能产生bug~~~“多出来的一只猪。”这是前人总结这个bug使用的文案,ie6的这个bug 下,大家会在页面看到猪字出现两遍,重复的内容量因注释的多少而变。解决方案:用“<!–[if !IE]> picRotate start <![endif]–>”方法写注释。
8.img 下的留白,大家看这段代码有啥问题:
<div> < img src=”" mce_src=”" /> < /div>
把div的border打开,你发现图片底部不是紧贴着容器底部的,是img后面的空白字符造成,要消除必须这样写
<div> <img src=”" mce_src=”" /></div>
后面两个标签要紧挨着。ie7下这个bug 依然存在。解决方案:给img设定 display:block。
9. 失去line-height。<div style=”line-height:20px”><img />文字</div>,很遗憾,在ie6下单行文字 line-height 效果消失了。。。,原因是<img />这个inline-block元素和inline元素写在一起了。解决方案:让img 和文字都 float起来。
引申:大家知道img 的align 有 text-top,middle,absmiddle啊什么的,你可以尝试去调整img 和文字让他们在ie和ff下能一致,你会发现怎么调都不会让你满意。索性让img 和文字都 float起来,用margin 调整。
10.clear层应该单独使用。也许你为了节省代码把clear属性直接放到下面的一个内容层,这样有问题,不仅仅是ff和op下失去margin效果,ie下某些margin值也会失效 <div style=”background:red;float:left;”>dd</div> < div style=”clear:both;margin-top:18px;background:green”>ff</div>
11.ie 下overflow:hidden对其下的绝对层position:absolute或者相对层 position:relative无效。解决方案:给overflow:hidden加position:relative或者position: absolute。另,ie6支持overflow-x或者overflow-y的特性,ie7、ff不支持。
12.ie6下严重的bug,float元素如没定义宽度,内部如有div定义了height或zoom:1,这个div就会占满一整行,即使你给了宽度。float元素如果作为布局用或复杂的容器,都要给个宽度的。
13.ie6下的bug,绝对定位的div下包含相对定位的div,如果给内层相对定位的div高度height具体值,内层相对层将具有100%的width值,外层绝对层将被撑大。解决方案给内层相对层float属性。
14.width:100%这个东西在ie里用很方便,会向上逐层搜索width值,忽视浮动层的影响,ff下搜索至浮动层结束,如此,只能给中间的所有浮动层加width:100%才行,累啊。opera这点倒学乖了跟了ie。 四、一句话解决所有兼容性问题(懒人有懒人的办法奥!) 在网站头部加上一句: 1. Google Chrome Frame也可以让IE用上Chrome的引擎: <meta http-equiv=“X-UA-Compatible” content=“chrome=1″/> 2.强制IE8使用IE7模式来解析 <meta http-equiv=“X-UA-Compatible”content=“IE=EmulateIE7″><!– IE7 mode –> //或者 <metahttp-equiv=“X-UA-Compatible”content=“IE=7″><!– IE7 mode –> 3.强制IE8使用IE6或IE5模式来解析 <metahttp-equiv=“X-UA-Compatible”content=“IE=6″><!– IE6 mode –> <metahttp-equiv=“X-UA-Compatible”content=“IE=5″><!– IE5 mode –> 4.如果一个特定版本的IE支持所要求的兼容性模式多于一种,如: <metahttp-equiv=“X-UA-Compatible”content=“IE=5; IE=8″/> 我用的是最后一种,不过还是没有彻底解决问题,我也在寻求方法,恳求大牛们帮忙解决! 原文出自: http://www.cnblogs.com/sybboy/archive/2013/01/19/2868153.html
1. Javascript没有类的概念。一般使用原型链继承(prototypal inheritance)来模拟类。
2. 除了null和undefined之外的任何数据类型都能表现成Object (behave like an object),包括Number类型和Function类型。 var n = 42; function f() { alert("foo"); };
alert("n is " + n.toString()); // "n is 42" alert(f.name + " is a function"); // "f is a function"
注意,是“表现为object”,而不是“是object”。事实上,number, string和boolean是基本类型(primitives),除了这三个之外的都可以算作object,比如一个正则表达式也是一个object。 当需要访问基本类型变量的属性时,那些基本类型变量将被临时转换成object。 例如: "foobar".big(); // is equivalent to new String("foobar").big(); .14.toFixed(); // is equivalent to new Number(3.14).toFixed() 另外,不能强行给基本类型变量(number, string, boolean)加上私有属性。
var a = "mystring", b = new String( "mystring" );
Object.defineProperty( b, 'foo', { value: 42, enumerable: false }); console.log(b.foo); // 42 Object.defineProperty( a, 'foo', { value: 42, enumerable: false }); // TypeError: Object.defineProperty called on non-object
// trying another way: a.foo = 42; // remember, this is equivalent to: // new Number(a).foo = 42; // …so the 'foo' property is defined on the wrapper, not on 'a' console.log(a.foo); // undefined 3. 如果变量名前不加上var,那么这个变量就是全局的。
function setGlobal() { a = 42; }
function setLocal() { var b = 23; }
setGlobal(); alert(a); // 42
setLocal(); alert(b); // ReferenceError: b is not defined
4. this指针是由调用函数赋予的, 而不是由被调函数自身定义的。 例如: var a = {}, b = {};
a.foo = 42; b.foo = 18; a.alertFoo = function() { alert(this.foo); };
a.alertFoo(); // 42 a.alertFoo.call(b); // 18
5. 严格的相等判断应该使用===。例如 : 0 == false 为真, 但是 0 === false 为假。
6. 0, undefined, null, "", NaN 都是假值 (falsy)。 这些值全都等同于false,但他们之间不能相互替换。 7. 变量声明会被提升到当前作用域的顶端。 例如:下述代码中,你认为调用foo函数会返回什么?
var a = 2;
function foo() { return a; var a = 5; }
它将返回undefined。 上述代码等同于下述代码: var a = 2;
function foo() { var a; // 'a' declaration is moved to top return a; a = 5; } 另一个例子: 下述代码中,调用函数foo后变量a的值是什么? var a = 42;
function foo() { a = 12; return a; function a(){} } 答案是42。因为foo函数中变相声明了a变量,因此a成了局部变量了。 function foo() { function a() {} // local 'a' is a function a = 12; // local 'a' is now a number (12) return a; // return the local 'a' (12) } 8. 参数在函数声明中可以省略, 例如: function hello(name, age) { alert("Hello "+name+", you’re "+age+" years old!"); }
hello("Anon", 42); // "hello Anon, you’re 42 years old!" hello("Baptiste"); // "hello Baptiste, you’re undefined years old!" hello("Bulat", 24, 42); // "hello Bulat, you’re 24 years old!" 9. 对于字符串,使用双引号""和单引号''的效果是一样的。
10. Javascript代码可以在浏览器环境之外运行, 比如在Terminal或者HTTP服务器上。(例如 Node.js)
原文出自:http://www.cnblogs.com/newyorker/archive/2013/01/18/2865820.html
本篇介绍可以在C#中使用的1D/2D编码解码器。条形码的应用已经非常普遍,几乎所有超市里面的商品上面都印有条形码;二维码也开始应用到很多场合,如火车票有二维码识别、网易的首页有二维码图标,用户只需要用手机扫描一下就可以看到手机版网易的网址,免去了输入长串字符的麻烦。 条形码的标准: 条形码的标准有ENA条形码、UPC条形码、二五条形码、交叉二五条形码、库德巴条形码、三九条形码和128条形码等,而商品上最常使用的就是EAN商品条形码。EAN商品条形码亦称通用商品条形码,由国际物品编码协会制定,通用于世界各地,是目前国际上使用最广泛的一种商品条形码。我国目前在国内推行使用的也是这种商品条形码。EAN商品条形码分为EAN-13(标准版)和EAN-8(缩短版)两种。 二维码的编码标准: 全球现有的二维码多达200种以上,其中常见的技术标准有PDF417(美系标准),QRCode(日系标准),Code49,Code16K,CodeOne,DM(韩系标准),GM(中国标准),CM(中国标准)等20余种。用得最多的是QRcode。 下面借助google的开源项目zxing来实现1D/2D的编码和解码,测试效果如下: zxing的官方地址是:http://code.google.com/p/zxing/ zxing的功能还是很强大的,最初是用java编写,并支持Android、ios、symbian等手机操作系统。 不过不知是何原因,该官网连一个例子也没有,文档也是字典式的把所有类列出来,一点都没为读者考虑。 下面我把如果使用zxing完成上图所示例子讲解一遍,供初学者参考: 1.我们新建一个Winform测试项目; 2.从官网下载zxing开源项目,大概16m的样子,解压缩后打开zxing-2.1\csharp目录,将该目录拷贝到我们新建的Winform项目下(方便调试和看源码,并非一定要如此); 3.winform项目中添加对zxing项目的引用; 4.按上图所示例子建好控件,“生成条形码”的代码如下:
1 //生成条形码 2 private void button1_Click(object sender, EventArgs e) 3 { 4 lbshow.Text = ""; 5 Regex rg = new Regex("^[0-9]{13}$"); 6 if (!rg.IsMatch(txtMsg.Text)) 7 { 8 MessageBox.Show("本例子采用EAN_13编码,需要输入13位数字"); 9 return; 10 } 11 12 try 13 { 14 MultiFormatWriter mutiWriter = new com.google.zxing.MultiFormatWriter(); 15 ByteMatrix bm = mutiWriter.encode(txtMsg.Text, com.google.zxing.BarcodeFormat.EAN_13, 363, 150); 16 Bitmap img= bm.ToBitmap(); 17 pictureBox1.Image =img; 18 19 //自动保存图片到当前目录 20 string filename = System.Environment.CurrentDirectory + "\\EAN_13" + DateTime.Now.Ticks.ToString() + ".jpg"; 21 img.Save(filename, System.Drawing.Imaging.ImageFormat.Jpeg); 22 lbshow.Text = "图片已保存到:" + filename; 23 } 24 catch(Exception ee) 25 { MessageBox.Show(ee.Message); } 26 } 其中需要注意BarcodeFormat参数,可以打开定义看到具体的编码方式,自己百度每种编码方式对输入的要求。 这里EAN_13编码要求是13位长度的数字,并且满足:把所有偶数序号位上的数相加求和,用求出的和乘3,再把所有奇数序号上的数相加求和,用求出的和加上刚才偶数序号上的数,然后得出和能被10整除。(这个规则校验在UPCEANReader类的checkStandardUPCEANChecksum方法里面,如果不需要,可以去掉) 生成二维码的代码与上面相似:
1 //生成二维码 2 private void button2_Click(object sender, EventArgs e) 3 { 4 lbshow.Text = ""; 5 try 6 { 7 MultiFormatWriter mutiWriter = new com.google.zxing.MultiFormatWriter(); 8 ByteMatrix bm = mutiWriter.encode(txtMsg.Text, com.google.zxing.BarcodeFormat.QR_CODE, 300, 300); 9 Bitmap img = bm.ToBitmap(); 10 pictureBox1.Image = img; 11 12 //自动保存图片到当前目录 13 string filename = System.Environment.CurrentDirectory + "\\QR" + DateTime.Now.Ticks.ToString() + ".jpg"; 14 img.Save(filename, System.Drawing.Imaging.ImageFormat.Jpeg); 15 lbshow.Text = "图片已保存到:" + filename; 16 } 17 catch (Exception ee) 18 { MessageBox.Show(ee.Message); } 19 } 注意编码问题,在com.google.zxing.qrcode.encoder.Encoder类中修改默认编码为utf-8,否则解码出现的是乱码。 System.String DEFAULT_BYTE_MODE_ENCODING = "UTF-8"; 此处之前是"ISO-8859-1",之所以改成UTF-8是因为,在解码的时候程序会猜测可能的编码,如果猜测失败则默认是UTF-8,代码在com.google.zxing.qrcode.decoder.DecodedBitStreamParser类的guessEncoding方法中。 所以此开源项目也缺少全局性思考,连编码和解码的默认编码方式都不一致。 4.实现图片解码,即把条形码或二维码图片解码成其真实内容,当然在pc上应用不大,但可能只是还没发现而已,代码如下: 1 //解码操作 2 private void button3_Click(object sender, EventArgs e) 3 { 4 MultiFormatReader mutiReader = new com.google.zxing.MultiFormatReader(); 5 Bitmap img = (Bitmap)Bitmap.FromFile(opFilePath); 6 if (img == null) 7 return; 8 LuminanceSource ls = new RGBLuminanceSource(img, img.Width, img.Height); 9 BinaryBitmap bb = new BinaryBitmap(new com.google.zxing.common.HybridBinarizer(ls)); 10 11 Result r= mutiReader.decode(bb); 12 txtMsg.Text = r.Text; 13 } opFilePath是图片路径,你可以用openFileDialog控件打开文件来得到路径。 原文出自: http://www.cnblogs.com/tuyile006/archive/2013/01/16/2863367.html
很多人都知道SQL注入,也知道SQL参数化查询可以防止SQL注入,可为什么能防止注入却并不是很多人都知道的。 本文主要讲述的是这个问题,也许你在部分文章中看到过这块内容,当然了看看也无妨。 首先:我们要了解SQL收到一个指令后所做的事情: 具体细节可以查看文章:Sql Server 编译、重编译与执行计划重用原理 在这里,我简单的表示为: 收到指令 -> 编译SQL生成执行计划 ->选择执行计划 ->执行执行计划。 具体可能有点不一样,但大致的步骤如上所示。 接着我们来分析为什么拼接SQL 字符串会导致SQL注入的风险呢? 首先创建一张表Users: CREATE TABLE [dbo].[Users]( [Id] [uniqueidentifier] NOT NULL, [UserId] [int] NOT NULL, [UserName] [varchar](50) NULL, [Password] [varchar](50) NOT NULL, CONSTRAINT [PK_Users] 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] 插入一些数据: INSERT INTO [Test].[dbo].[Users]([Id],[UserId],[UserName],[Password])VALUES (NEWID(),1,'name1','pwd1'); INSERT INTO [Test].[dbo].[Users]([Id],[UserId],[UserName],[Password])VALUES (NEWID(),2,'name2','pwd2'); INSERT INTO [Test].[dbo].[Users]([Id],[UserId],[UserName],[Password])VALUES (NEWID(),3,'name3','pwd3'); INSERT INTO [Test].[dbo].[Users]([Id],[UserId],[UserName],[Password])VALUES (NEWID(),4,'name4','pwd4'); INSERT INTO [Test].[dbo].[Users]([Id],[UserId],[UserName],[Password])VALUES (NEWID(),5,'name5','pwd5'); 假设我们有个用户登录的页面,代码如下: 验证用户登录的sql 如下: select COUNT(*) from Users where Password = 'a' and UserName = 'b' 这段代码返回Password 和UserName都匹配的用户数量,如果大于1的话,那么就代表用户存在。 本文不讨论SQL 中的密码策略,也不讨论代码规范,主要是讲为什么能够防止SQL注入,请一些同学不要纠结与某些代码,或者和SQL注入无关的主题。 可以看到执行结果: 这个是SQL profile 跟踪的SQL 语句。 注入的代码如下: select COUNT(*) from Users where Password = 'a' and UserName = 'b' or 1=1—' 这里有人将UserName设置为了 “b' or 1=1 –”. 实际执行的SQL就变成了如下: 可以很明显的看到SQL注入成功了。 很多人都知道参数化查询可以避免上面出现的注入问题,比如下面的代码: class Program { private static string connectionString = "Data Source=.;Initial Catalog=Test;Integrated Security=True"; static void Main(string[] args) { Login("b", "a"); Login("b' or 1=1--", "a"); } private static void Login(string userName, string password) { using (SqlConnection conn = new SqlConnection(connectionString)) { conn.Open(); SqlCommand comm = new SqlCommand(); comm.Connection = conn; //为每一条数据添加一个参数 comm.CommandText = "select COUNT(*) from Users where Password = @Password and UserName = @UserName"; comm.Parameters.AddRange( new SqlParameter[]{ new SqlParameter("@Password", SqlDbType.VarChar) { Value = password}, new SqlParameter("@UserName", SqlDbType.VarChar) { Value = userName}, }); comm.ExecuteNonQuery(); } } } 实际执行的SQL 如下所示: exec sp_executesql N'select COUNT(*) from Users where Password = @Password and UserName = @UserName',N'@Password varchar(1),@UserName varchar(1)',@Password='a',@UserName='b' exec sp_executesql N'select COUNT(*) from Users where Password = @Password and UserName = @UserName',N'@Password varchar(1),@UserName varchar(11)',@Password='a',@UserName='b'' or 1=1—' 可以看到参数化查询主要做了这些事情: 1:参数过滤,可以看到 @UserName='b'' or 1=1—' 2:执行计划重用 因为执行计划被重用,所以可以防止SQL注入。 首先分析SQL注入的本质, 用户写了一段SQL 用来表示查找密码是a的,用户名是b的所有用户的数量。 通过注入SQL,这段SQL现在表示的含义是查找(密码是a的,并且用户名是b的,) 或者1=1 的所有用户的数量。 可以看到SQL的语意发生了改变,为什么发生了改变呢?,因为没有重用以前的执行计划,因为对注入后的SQL语句重新进行了编译,因为重新执行了语法解析。所以要保证SQL语义不变,即我想要表达SQL就是我想表达的意思,不是别的注入后的意思,就应该重用执行计划。 如果不能够重用执行计划,那么就有SQL注入的风险,因为SQL的语意有可能会变化,所表达的查询就可能变化。 在SQL Server 中查询执行计划可以使用下面的脚本: DBCC FreeProccache select total_elapsed_time / execution_count 平均时间,total_logical_reads/execution_count 逻辑读, usecounts 重用次数,SUBSTRING(d.text, (statement_start_offset/2) + 1, ((CASE statement_end_offset WHEN -1 THEN DATALENGTH(text) ELSE statement_end_offset END - statement_start_offset)/2) + 1) 语句执行 from sys.dm_exec_cached_plans a cross apply sys.dm_exec_query_plan(a.plan_handle) c ,sys.dm_exec_query_stats b cross apply sys.dm_exec_sql_text(b.sql_handle) d --where a.plan_handle=b.plan_handle and total_logical_reads/execution_count>4000 ORDER BY total_elapsed_time / execution_count DESC; 博客园有篇文章: Sql Server参数化查询之where in和like实现详解 在这篇文章中有这么一段: 这里作者有一句话:”不过这种写法和直接拼SQL执行没啥实质性的区别” 任何拼接SQL的方式都有SQL注入的风险,所以如果没有实质性的区别的话,那么使用exec 动态执行SQL是不能防止SQL注入的。 比如下面的代码: private static void TestMethod() { using (SqlConnection conn = new SqlConnection(connectionString)) { conn.Open(); SqlCommand comm = new SqlCommand(); comm.Connection = conn; //使用exec动态执行SQL //实际执行的查询计划为(@UserID varchar(max))select * from Users(nolock) where UserID in (1,2,3,4) //不是预期的(@UserID varchar(max))exec('select * from Users(nolock) where UserID in ('+@UserID+')') comm.CommandText = "exec('select * from Users(nolock) where UserID in ('+@UserID+')')"; comm.Parameters.Add(new SqlParameter("@UserID", SqlDbType.VarChar, -1) { Value = "1,2,3,4" }); //comm.Parameters.Add(new SqlParameter("@UserID", SqlDbType.VarChar, -1) { Value = "1,2,3,4); delete from Users;--" }); comm.ExecuteNonQuery(); } } 执行的SQL 如下: exec sp_executesql N'exec(''select * from Users(nolock) where UserID in (''+@UserID+'')'')',N'@UserID varchar(max) ',@UserID='1,2,3,4' 可以看到SQL语句并没有参数化查询。 如果你将UserID设置为” 1,2,3,4); delete from Users;—- ”,那么执行的SQL就是下面这样: exec sp_executesql N'exec(''select * from Users(nolock) where UserID in (''+@UserID+'')'')',N'@UserID varchar(max) ',@UserID='1,2,3,4); delete from Users;--' 不要以为加了个@UserID 就代表能够防止SQL注入,实际执行的SQL 如下: 任何动态的执行SQL 都有注入的风险,因为动态意味着不重用执行计划,而如果不重用执行计划的话,那么就基本上无法保证你写的SQL所表示的意思就是你要表达的意思。 这就好像小时候的填空题,查找密码是(____) 并且用户名是(____)的用户。 不管你填的是什么值,我所表达的就是这个意思。 最后再总结一句:因为参数化查询可以重用执行计划,并且如果重用执行计划的话,SQL所要表达的语义就不会变化,所以就可以防止SQL注入,如果不能重用执行计划,就有可能出现SQL注入, 存储过程也是一样的道理,因为可以重用执行计划。 原文出自: http://www.cnblogs.com/LoveJenny/archive/2013/01/15/2860553.html
背景 : 在使用搜索引擎和电商的搜索功能时,大家一定遇到过这样的情景:我想搜索电影“十二生肖”,可不小心输成“十二生效”了,不用担心搜不到你想要的结果,因为建立在大数据上的搜索引擎会帮你自动纠错,就这个例子Google和Baidu返回给我的分别是: 显示以下查询字词的结果: 十二生肖 和 您要找的是不是: 十二生肖 ,他们都做到了自动纠错,关于自动纠错我之前也写过一篇陋文,当时是自己实现的N-Gram模型,但是效果不是太好,主要是针对不同的语料库算法的精确度是不一样的,我想换个算法试试看,目前主流的计算串间的距离(相反的,你也可以理解为相似度)是Levenshtein,当要实现时,发现lucene已经做了这个事,那咱就站在巨人的肩膀上成长吧。 引用包: lucene-core-3.1.0.jar + lucene-spellchecker-3.1.0.jar,你可以在这里得到 使用示例: 在类SpellCorrector的main方法中加入以下代码 1 //创建目录 2 File dict = new File(""); 3 Directory directory = FSDirectory.open(dict); 4 5 //实例化拼写检查器 6 SpellChecker sp = new SpellChecker(directory); 7 8 9 //创建词典 10 File dictionary = new File(SpellCorrecter.class.getResource("dictionary.txt").getFile()); 11 12 //对词典进行索引 13 sp.indexDictionary(new PlainTextDictionary(dictionary)); 14 15 16 //有错别字的搜索 17 String search = "非常勿扰"; 18 19 20 //建议个数,这里我只想要最接近的那一个,你可以设置成别的数字,如3 21 int suggestionNumber = 1; 22 23 24 //获取建议的关键字 25 String[] suggestions = sp.suggestSimilar(search, suggestionNumber); 26 27 28 //显示结果 29 System.out.println("搜索:" + search); 30 31 32 for (String word : suggestions) { 33 System.out.println("你要找的是不是:" + word); 34 } 注:这之前你需要有个语料库,我这里是个存放正确视频名称的文件,格式如下:
红颜血泪 冰上火一般的激情 在敌之手 驰风竞艇王第二部 钓金龟 潇湘路一号 戏里戏外第二季 草原狼爵士乐 拯救大兵瑞恩 好了,接下来就直接运行吧,例如我搜索“十二生效”,则提示说是不是要找“十二生肖” 原文出自: http://www.cnblogs.com/wuren/archive/2013/01/16/2862873.html
严重: Exception sending context initialized event to listener instance of class org.springframework.web.context.ContextLoaderListener org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'ResourceService': Invocation of init method failed; nested exception is javax.xml.ws.WebServiceException: java.lang.IllegalArgumentException: An operation with name [####] already exists in this service 可能是因为WebService 存在重载的方法,因此初始化时Spring无法找到对应的方法 使用的插件为: javax.jws-1.0.0_JDK1.5_1.0.0.jar cxf-bundle-2.5.2.jar spring-webmvc-3.1.1.RELEASE.jar
一般我们的前台代码Ext.grid.ColumnModel里会这样写,以便显示日期格式: ////////////////////////////////////////////////////////////// Js代码 .... { header:"birthday", dataIndex:"birthday" .... renderer:new Ext.util.Format.dateRenderer("Y-m-d"), .... } .... ////////////////////////////////////////////////////////////// 如果你前台这样写的话,那恭喜你,你的显示日期那列将不再正确显示时间, 而是显示为"NaN-NaN-NaN",是吧? 呵呵,问我怎么知道的?因为最开始我也是这么错的。 为什么会是错的呢? 让我们来看看Ext.format.dateRenderer的源代码(开源的东西就是好), 它的源代码是这样的: Js代码 ////////////////////////////////////////////////////////////// dateRenderer : function(format) { return function(v) { return Ext.util.Format.date(v, format);、 } } ////////////////////////////////////////////////////////////// 可以看出,我们传会来的值,被当做日期又被格式化了一次,我们传回来的是日期吗? 以前是,经过昨天后台代码的修改,我们传回来的仅仅是个字符串了,至于为什么要这么改, 请看昨天写的“Extjs日期格式问题(一) ”,那咋办?有的朋友应该已经想到了,既然是字符串, 那就直接显示呗,不用renderer了,于是前台代码Ext.grid.ColumnModel里就变成了: Js代码 ////////////////////////////////////////////////////////////// .... {header:"birthday",dataIndex:"birthday".......), .... ////////////////////////////////////////////////////////////// 可以负责任的告诉你,这样写,绝对可以正确显示了,这样是不是感觉更简单了呢? 但是,基于我项目里的要求,这个问题并没有解决完,因为在这里不是一个简单的gridpanel, 而是一个editgridpanel,所以还得定义一个editor,于是有了下面这样一段代码: Js代码 ////////////////////////////////////////////////////////////// .... {header:"birthday",dataIndex:"birthday"......., editor:new Ext.grid.GridEditor(new Ext.form.DateField({format:"Y-m-d"})), ..... ////////////////////////////////////////////////////////////// 加了这个DateField控件后,每次可以正常的选择日期,但是选择完日期后, grid里显示的格式就又不正确了,这次显示的内容成了: "Wed Mar 04 1970......", 这样的格式一看就是个日期,这样显示的原因当然是因为我们没有写renderer进行格式化处理的缘故。。。 说到这里,有人应该已经想到解决办法了,另外有些人可能就抓狂了,这renderer加了不能正常显示, 不加也不能正常显示,这很矛盾啊。。 问题往往到了最矛盾的时候,也是到了解决的时候,现在我把解决代码贴出来,大家一看就明白了。 多的不说,看代码: Js代码 ////////////////////////////////////////////////////////////// renderer:function(value){ if(value instanceof Date){ return new Date(value).format("Y-m-d"); }else{ return value; } } ////////////////////////////////////////////////////////////// 简单吧?自己写renderer就是了。 原文出自:http://ks2144634.blog.163.com/blog/static/133585503201081544950656/
错误”ora-04031 无法分配XXX字节的共享内存(XXX)”的解决办法: oracle 9i: sys用户以sysdba身份登录 先查看当前shared_pool_size值 sql>show parameter shared_pool_size; 然后 sql>alter system set shared_pool_size=’比原先值适当增加’ scope=spfile; 然后 sql>shutdown immediate sql>startup oracle 10g: oracle 10g shared_pool_size默认值为0,也就是系统自动管理shared pool内存,这时可以适当增加shared_pool_reserved_size的值,仍然让系统自动管理这部分内存 sql>alter system set shared_pool_reserved_size=’比原先值适当增加’ scope=spfile; sql>shutdown immediate sql>startup 原文出自:http://openwares.net/database/cant_alloc_share_memory.html
对依赖注入( http://www.oschina.net/code/snippet_871522_15682) 的进一步加强. 作为基本功能的注册,使用语法是:registration.register<Interface,Implementation>(). 可以到了实际的项目中,要注册的接口和类可能不止一个两个,会成百上千。无论是用C#代码来注册,还是用XML文件来配置,都是开发者的噩梦。 这里,让我们来个思维的反转: 变集中配置为分散声明。 1 为要注册的类,加入RegisterInContainer的声明 2 在系统启动的时候,扫描所有的类,对有RegisterInContainer的类,找到它所有实现的接口,把这些接口和该类绑定注册。 用于装饰要注入类的Attribute. 1 using System; 2 using System.Linq; 3 using Skight.eLiteWeb.Domain.BasicExtensions; 4 5 namespace Skight.eLiteWeb.Domain.Containers 6 { 7 [AttributeUsage(AttributeTargets.Class)] 8 public class RegisterInContainerAttribute : Attribute 9 { 10 public RegisterInContainerAttribute(LifeCycle life_cycle) { 11 this.life_cycle = life_cycle; 12 } 13 14 public LifeCycle life_cycle { get; set; } 15 public Type type_to_register_in_container { get; set; } 16 17 public void register_using(Registration registration) { 18 DiscreteItemResolver resolver = new TypeResolver(type_to_register_in_container); 19 if (life_cycle == LifeCycle.singleton) resolver = new SingletonResolver(resolver); 20 registration.register(resolver,type_to_register_in_container.all_interface().ToArray()); 21 } 22 } 23 } 注入声明Attribute参数,指定是否单件方式生成注册类。1 namespace Skight.eLiteWeb.Domain.Containers 2 { 3 public enum LifeCycle 4 { 5 single_call, 6 singleton 7 } 8 } 注册声明Attribute的使用示例 1 //调用Container.get_an<FrontController>()将会得到FrontControllerImpl的对象 2 3 using Skight.eLiteWeb.Domain.Containers; 4 5 namespace Skight.eLiteWeb.Presentation.Web.FrontControllers 6 { 7 [RegisterInContainer(LifeCycle.single_call)] 8 public class FrontControllerImpl : FrontControllers.FrontController 9 { 10 private CommandResolver command_resolver; 11 12 public FrontControllerImpl(CommandResolver commandResolver) 13 { 14 command_resolver = commandResolver; 15 } 16 17 public void process(WebRequest request) 18 { 19 command_resolver.get_command_to_process(request).process(request); 20 } 21 } 22 } Registration添加一个方法:反过来,为一个解析器(实现类)注册多个接口。1 void register(DiscreteItemResolver resolver, params Type[] contracts); 2 扫描注册机,在系统启动时调用一次。 1 using System.Collections.Generic; 2 using System.Reflection; 3 using Skight.eLiteWeb.Domain.BasicExtensions; 4 using Skight.eLiteWeb.Domain.Containers; 5 6 namespace Skight.eLiteWeb.Application.Startup 7 { 8 public class RegistrationScanner:StartupCommand 9 { 10 private readonly Registration registration; 11 private readonly IEnumerable<Assembly> assemblies; 12 13 public RegistrationScanner(Registration registration, params Assembly[] assemblies) 14 { 15 this.registration = registration; 16 this.assemblies = assemblies; 17 } 18 19 public void run() 20 { 21 assemblies 22 .each(assembly => 23 assembly.GetTypes() 24 .each(type => 25 type.run_againste_attribute<RegisterInContainerAttribute>( 26 attribute => 27 { 28 attribute.type_to_register_in_container = type; 29 attribute.register_using(registration); 30 }))); 31 } 32 } 33 } 系统启动:创建注册类,然后传入必要的Assembly,调用注册机注册所有加了Attribute的类。 1 using System; 2 using System.Collections.Generic; 3 using System.Reflection; 4 using Skight.eLiteWeb.Domain.Containers; 5 using Skight.eLiteWeb.Presentation.Web.FrontControllers; 6 7 namespace Skight.eLiteWeb.Application.Startup 8 { 9 public class ApplicationStartup 10 { 11 public void run() 12 { 13 var registration = create_registration(); 14 new RegistrationScanner(registration, 15 Assembly.GetAssembly(typeof (Container)), 16 Assembly.GetAssembly(typeof (FrontController)), 17 Assembly.GetAssembly(typeof (StartupCommand))) 18 .run(); 19 20 } 21 22 private Registration create_registration() 23 { 24 IDictionary<Type, DiscreteItemResolver> item_resolvers = new Dictionary<Type, DiscreteItemResolver>(); 25 Container.initialize_with(new ResolverImpl(item_resolvers)); 26 return new RegistrationImpl(item_resolvers); 27 } 28 } 29 } Global.asax系统入口,把所有的代码串起来。 1 <%@ Application Language="C#" %> 2 <%@ Import Namespace="Skight.eLiteWeb.Application.Startup" %> 3 <script RunAt="server"> 4 5 private void Application_Start(object sender, EventArgs e) 6 { 7 new ApplicationStartup().run(); 8 } 9 10 </script> 原文出自: http://www.oschina.net/code/snippet_871522_16735
摘要: http://www.umlchina.com/Tools/Newindex1.htmUML相关工具一览(截止2012年10月) 整理 本文的PDF版本在此下载>> 以下总结了全世界的各种UML相关工具,按工具名称字母排序。 工具(最新版本) 厂商&地址 版权 ... 阅读全文
1.活动图、用例图、类图 http://yuml.me/diagram/scruffy/class/draw 2.序列图 http://www.websequencediagrams.com/ 3.序列图、协作图 http://www.diagrammr.com/
摘要: Container注入框架的进入点Code highlighting produced by Actipro CodeHighlighter (freeware)http://www.CodeHighlighter.com/--> 1 namespace Skight.LightWeb.Domain 2 { 3 &n... 阅读全文
在这里我们总结了Oracle临时表的集中用法,临时表创建之后基本不占用表空间,如果你没有指定临时表存放的表空的时候,你插入到临时表的数据是存在系统的临时表空间中。 Oracle临时表可以说是提高数据库处理性能的好方法,在没有必要存储时,只存储在Oracle临时表空间中。希望本文能对大家有所帮助。 1 、前言 目前所有使用 Oracle 作为数据库支撑平台的应用,大部分数据量比较庞大的系统,即表的数据量一般情况下都是在百万级以上的数据量。 当然在 Oracle 中创建分区是一种不错的选择,但是当你发现你的应用有多张表关联的时候,并且这些表大部分都是比较庞大,而你关联的时候发现其中的某一张或者某几张表关联 之后得到的结果集非常小并且查询得到这个结果集的速度非常快,那么这个时候我考虑在 Oracle 中创建“临时表”。 我对临时表的理解:在 Oracle 中创建一张表,这个表不用于其他的什么功能,主要用于自己的软件系统一些特有功能才用的,而当你用完之后表中的数据就没用了。 Oracle 的临时表创建之后基本不占用表空间,如果你没有指定临时表(包括临时表的索引)存放的表空的时候,你插入到临时表的数据是存放在 ORACLE 系统的临时表空间中( TEMP )。 2 、临时表的创建 创建Oracle 临时表,可以有两种类型的临时表: 会话级的临时表 事务级的临时表 。 1) 会话级的临时表因为这这个临时表中的数据和你的当前会话有关系,当你当前SESSION 不退出的情况下,临时表中的数据就还存在,而当你退出当前SESSION 的时候,临时表中的数据就全部没有了,当然这个时候你如果以另外一个SESSION 登陆的时候是看不到另外一个SESSION 中插入到临时表中的数据的。即两个不同的SESSION 所插入的数据是互不相干的。当某一个SESSION 退出之后临时表中的数据就被截断(truncate table ,即数据清空)了。会话级的临时表创建方法: 1 Create Global Temporary Table Table_Name 2 (Col1 Type1,Col2 Type2) On Commit Preserve Rows ;
举例: 1 create global temporary table Student 2 (Stu_id Number(5), 3 Class_id Number(5), 4 Stu_Name Varchar2(8), 5 Stu_Memo varchar2(200)) on Commit Preserve Rows ;
2) 事务级临时表是指该临时表与事务相关,当进行事务提交或者事务回滚的时候,临时表中的数据将自行被截断,其他的内容和会话级的临时表的一致(包括退出SESSION 的时候,事务级的临时表也会被自动截断)。事务级临时表的创建方法: 1 Create Global Temporary Table Table_Name 2 (Col1 Type1,Col2 Type2) On Commit Delete Rows ;
举例: 1 create global temporary table Classes 2 (Class_id Number(5), 3 Class_Name Varchar2(8), 4 Class_Memo varchar2(200)) on Commit delete Rows ;
3) 两中类型临时表的区别 会话级临时表采用 on commit preserve rows ;而事务级则采用 on commit delete rows ;用法上,会话级别只有当会话结束临时表中的数据才会被截断,而且事务级临时表则不管是 commit 、 rollback 或者是会话结束,临时表中的数据都将被截断 4 )什么时候使用临时表 1 )、当某一个 SQL 语句关联的表在 2 张及以上,并且和一些小表关联。可以采用将大表进行分拆并且得到比较小的结果集合存放在临时表中 2 )、程序执行过程中可能需要存放一些临时的数据,这些数据在整个程序的会话过程中都需要用的等等。 3 . 例子:略 4 .临时表的不足之处 1 )不支持 lob 对象,这也许是设计者基于运行效率的考虑,但实际应用中确实需要此功能时就无法使用临时表了。 2 )不支持主外键关系 所以,由于以上原因,我们可以自己创建临时表,以弥补 oracle 临时表的不足之处 上面的都是本人经过测试的,但下面是在网上搜索到的方法,本人具体没有测试过,不过觉得可行性很强,有时间测试下 创建方法: 1 、以常规表的形式创建临时数据表的表结构,但要在每一个表的主键中加入一个 SessionID 列以区分不同的会话。(可以有 lob 列和主外键) 2 、写一个用户注销触发器,在用户结束会话的时候删除本次会话所插入的所有记录 (SessionID 等于本次会话 ID 的记录 ) 。 3 、程序写入数据时,要顺便将当前的会话 ID(SessionID) 写入表中。 4 、程序读取数据时,只读取与当前会话 ID 相同的记录即可。 功能增强的扩展设计: 1 、可以在数据表上建立一个视图,视图对记录的筛选条件就是当前会话的SessionID 。 2 、数据表中的SessionID 列可以通过Trigger 实现,以实现对应用层的透明性。 3 、高级用户可以访问全局数据,以实现更加复杂的功能。 扩展临时表的优点: 1 、实现了与Oracle 的基于会话的临时表相同的功能。 2 、支持SDO_GEOMETRY 等lob 数据类型。 3 、支持表间的主外键连接,且主外键连接也是基于会话的。 4 、高级用户可以访问全局数据,以实现更加复杂的功能 原文出自: http://database.51cto.com/art/201001/180851.htm
Heritrix绑定主机IP 关键字:Heritrix 127.0.0.1 IP 主机 Heritrix默认绑定的IP是127.0.0.1。 在org.archive.crawler.Heritrix中 … final private static Collection<String> LOCALHOST_ONLY = Collections.unmodifiableList(Arrays.asList(new String[] { "127.0.0.1" })); … private static Collection<String> guiHosts = LOCALHOST_ONLY; protected static String doCmdLineArgs(final String [] args) throws Exception { … // Now look at options passed. for (int i = 0; i < options.length; i++) { switch(options[i].getId()) { … case 'b': Heritrix.guiHosts = parseHosts(options[i].getValue()); break; … default: assert false: options[i].getId(); } } … } | 首先定义了默认IP:127.0.0.1,然后赋给guiHost主机变量。当指定-b或--bind参数时,才会把指定的IP赋给主机变量。 另外,中间还有一步参数处理,对于--xxxx参数会转为-x的形式统一处理,所以--bind和-b有一样的效果。 Heritrix启动参数 关键字:Heritrix 启动 参数 bind admin properties Heritrix的启动参数,除了--bind外,都可以在heritrix.properties设置,而不用每次都在命令行中输入。 如常用的--port, --admin等。 heritrix.cmdline.admin = admin:admin heritrix.cmdline.port = 8080 heritrix.cmdline.run = false heritrix.cmdline.nowui = false heritrix.cmdline.order = heritrix.cmdline.jmxserver = false heritrix.cmdline.jmxserver.port = 8081 | 关于Heritrix的Extractor中文乱码 关键字:Heritrix 中文 乱码 GB2312 Extractor 继承从org.archive.crawler.extractor.Extractor的子类,在extract方法中可以从参数CrawlURI中取出要解析的内容。 curi.getHttpRecorder().getReplayCharSequence.toString() | 有中文时,不做处理会输出乱码。可以在取到的HttpRecorder后设置编码: HttpRecorder hr = curi.getHttpRecorder(); if ( hr == null ) { throw new IOException( "Why is recorder null here?" ); } hr.setCharacterEncoding( "gb2312" ); cs = hr.getReplayCharSequence(); System.out.println( cs.toString() ); | 原文出自: http://blog.chinaunix.net/uid-8464637-id-2461166.html
原文出自: http://blog.csdn.net/jbxiaozi/article/details/7351768
1.右键项目-》属性-》java bulid path-》jre System Library-》access rules-》resolution选择accessible,下面填上** 点击确定即可!!! 2. 在MyEclipse中编写Java代码时,用到了BASE64Decoder,import sun.misc.BASE64Decoder;可是Eclipse提示: Access restriction: The type BASE64Decoder is not accessible due to restriction on required library C:\Program files\java\jre6\lib\rt.jar Access restriction : The constructor BASE64Decoder() is not accessible due to restriction on required library C:\Program files\java\jre6\lib\rt.jar 解决方案1(推荐): 只需要在project build path中先移除JRE System Library,再添加库JRE System Library,重新编译后就一切正常了。
解决方案2: Windows -> Preferences -> Java -> Compiler -> Errors/Warnings -> Deprecated and trstricted API -> Forbidden reference (access rules): -> change to warning
项目中有时需要用到起始日期和结束日期,要做到起始日期必须小于结束日期。在extjs中已经有现成的函数,摘录如下: 1 Ext.apply(Ext.form.VTypes, { 2 daterange : function(val, field) { 3 var date = field.parseDate(val); 4 if (!date) { 5 return; 6 } 7 if (field.startDateField 8 && (!this.dateRangeMax || (date.getTime() != this.dateRangeMax 9 .getTime()))) { 10 var start = Ext.getCmp(field.startDateField); 11 start.setMaxValue(date); 12 start.validate(); 13 this.dateRangeMax = date; 14 } else if (field.endDateField 15 && (!this.dateRangeMin || (date.getTime() != this.dateRangeMin 16 .getTime()))) { 17 var end = Ext.getCmp(field.endDateField); 18 end.setMinValue(date); 19 end.validate(); 20 this.dateRangeMin = date; 21 } 22 /* 23 * Always return true since we're only using this vtype to set 24 * the min/max allowed values (these are tested for after the 25 * vtype test) 26 */ 27 return true; 28 } 29 }); 然后分别定义起始日期和结束日期控件: 1 var startDate = new Ext.form.DateField({ 2 fieldLabel : '开始日期', 3 emptyText : '请选择', 4 disabledDays : [1, 2, 5],//将星期一,二,五禁止.数值为0-6,0为星期日,6为星期六 5 labelWidth : 100, 6 readOnly : true, 7 allowBlank : false, 8 format : 'Y-m-d',//日期格式 9 name : 'startdt', 10 id : 'startdt', 11 vtype : 'daterange',//daterange类型为上代码定义的类型 12 endDateField : 'endDate'//必须跟endDate的id名相同 13 }) 14 var endDate = new Ext.form.DateField({ 15 fieldLabel : '结束日期', 16 emptyText : '请选择', 17 disabledDays : [1, 2, 5],//将星期一,二,五禁止.数值为0-6,0为星期日,6为星期六 18 readOnly : true, 19 allowBlank : false, 20 format : 'Y-m-d',//日期格式 21 name : 'enddt', 22 id : 'endDate', 23 vtype : 'daterange',//daterange类型为上代码定义的类型 24 startDateField : 'startdt'//必须跟startDate的id名相同 25 }) 效果如下两图: 图1.选择开始日期 图2.选择结束日期 原文出自: http://blog.csdn.net/security08/article/details/5070749
工作流管理联盟: 定义: 创建并完善了工作流的相关标准,开拓了相关市场,是唯一的致力于工作流标准化的专业组织。该组织推出了工作流XML(Wf-XML)和XML过程定义语言(XPDL) ,现在有超过80种有名的解决方案中使用了这两种语言来存储和交换过程模型。发布了用于工作流管理系统之间互操作的工作流参考模型,并且为了实现不同工作流产品之间的互操作,WfMC在工作流管理系统的相关术语、体系结构及应用编程接口等方面制定了一系列标准 工作流: 定义: 工作流是一类能够完全或者部分自动执行的经营过程,根据一系列过程规则,文档、信息或任务能够在不同的执行者之间传递、执行。从工作流的定义可以看出:(1)、有多个参与者:(2)、按照一定的规则进行活动(传递文档、信息、任务等);(3)、活动的推进是自动的或部分自动的。【工作流管理联盟】 工作流管理系统: 定义: 工作流管理系统是一个软件系统,它负责工作流的定义和管理,并按照在计算机中预先定义好的工作流逻辑推进过程实例的执行。工作流管理系统(Workflow Management System,WFMS)是通过对工作流程中涉及各步骤的人员和IT资 源的合理调整,从而起到对工作流的定义、管理和实现的确定性作用。工作流管理系统是支持企业实现业务过程管理和自动化的强有力的软件工具,它能完成工作流 的定义和管理,并按照在计算机中预先定义好的工作流逻辑推进工作流实例的执行。所以工作流是工作流管理系统的最重要的被管理的元素,就像表、试图是数据库 管理系统的管理对象一样。【工作流管理联盟】 工作流参考模型: 定义: 1.通用的工作流系统实现模型 2.把工作流系统中的主要功能组件和这些组件间的接口一起看成抽象的模型 作用: 1.这个模型可以与市场上的大多数产品相匹配,因此为开发协同工作的工作流系统奠定了基础 2.工作流参考模型的引入为人们讨论工作流技术提供了一一个规范的术语表,为在一般意义上讨论工作流系统的体系结构提供了基础:工作流参考模型为工作流管理系统的关键软件部件提供了功能描述,并描述了关键软件部件交互,而 且这个描述是独立于特定产品或技术的实现的:从功能的角度定义五个关键软件部件的交互接口,推动了信息交换的标准化,使得不同产品间的互操作成为可能。 组成: 1.软件组件:为工作流系统的各种功能提供支持。 2.各中类型的系统定义数据和控制数据:系统中的一个或多个软件构件使用的数据。 3.应用程序与应用程序数据库:外部系统或数据,被系统调用来完成整个或部分工作流管理的功能。 五类接口(WorkflowAPI,WAPI) 接口l:工作流执行服务与工作流建模工具间的接口,为实现对工作流过程定义的访问(如建立、修改、删除等)提供了一致的方法。 接口2:工作流服务和用户应用之间的接口,这是最主要的接口规范,它约定所有客户方应用和工作流服务之间的功能访问方式。 接口3:工作流引擎和应用服务间的直接接口,其目标是集成工作流和其它应用服务而无需考虑原有工作流管理系统。 接口4:工作流管理系统之间的互操作接口,用于描述不同工作流产品的互操作性。 一般必须的互操作有两个主要方面: (1)流程定义或子集的公共解释; (2)运行时间对各种控制信息转换,和在不同实施服务之间传递工作流相关数据和应用数据的支持。_个工作流引擎可以选择、实例化和执行其他工作流引擎所约定的流程定义。 接口5:工作流服务和工作流管理工具之间的接口,用于系统管理、应用访问工作流执行服务。 通用工作流系统各部分功能: 1.工作流执行服务 工作流执行服务是指由一个或者多个工作流引擎组成,以创建,管理和执行工作流实例,应用程序可能通过工作流应用程序接口(WAPI))与这个服务进行交互。工作流执行服务的主要功能是:解释流程定义,生成过程实例,并管理其实施过程;依据工作流相关数据实现流程活动导航,包括顺序或并行操作、期限设置等;与外部资源交互,完成各项活动;维护工作流控制数据和工作流相关数据,并向用户传送必要的相关数据。 工作流执行服务使用外部资源的两种途径: 1.用户应用接口:工作流引擎通过任务项列表管理资源,任务项列表管理器负责从任务项列表中选择并监督工作项的完成。任务项列表管理器或用户负责调用应用工具。 2.直接调用应用接口:工作流引擎直接调用相应的应用来完成一项任务。这主要是针对基于服务器的无需用户参与的应用,那些需要用户操作的活动则通过任务列表管理器来调用。 2.工作流引擎 工作流引擎是指为工作流实例提供运行时执行环境的软件服务或“引擎”。 主要提供以下功能:对过程定义进行解释;控制过程实例的生成、激活、挂起、终止等;控制过程活动间的转换,包括串行或并行的操作、工作流相关数据的解释 等;支持用户操作的界面;维护工作流控制数据和工作流相关数据,在应用或用户间传递工作流相关数据;提供用于激活外部应用并提供工作流相关数据的界面;提 供控制、管理和监督的功能。 3.过程定义工具 过程定义工具是管理流程定义的工具,它可以通过图形方式把复杂的流程定义显示出来并加以操作。流程定义工具同工作流执行服务交互,为用户提供一种对实际业务过程进行分析、建模的手段,并生成业务过程的可被计算机处理的形式化描述(过程定义)。这也是工作流系统建立阶段的主要任务。不同的工作流产品,其建模工具输出格式是不同的。接口l不仅使工作流的定义阶段和运行阶段分离,使用户可以分别选择建模工具和执行产品,并且提供了对工作流过程进行协同定义的潜在能力,这些产品提供了分布的运行服务。 4.管理和监控工具 管理和监控工具主要负责对组织机构、角色等数据的维护管理和工作流实例的运行进行监控。管理员可以通过工作流管理工具获得目前各个活动的运行情况报告,并可干预实例的推进。 5.客户端应用 客户端应用是通过请求的方式同工作流执行服务交互的应用,也就是说是客户端应用调用工作流执行服务,客户端应用同工作流执行服务交互。它提供给用户一种手段,以处理实例运行过程中需要人工参与的任务。 6.调用的应用 调用的应用指工作流执行服务在过程实例运行过程中调用的、用以对应用数据进行处理的应用程序和Web服务 原文出自: http://www.cnblogs.com/yuziyan/archive/2012/12/08/2809346.html
类图画法 类之间的几种关系:泛化(Generalization)、实现(Realization)、关联(Association)(又分一般关联、聚合(Aggregation)、组合(Composition))、依赖(Dependency) 一、类图画法 1、 类图的概念 A、显示出类、接口以及它们之间的静态结构和关系 B、用于描述系统的结构化设计 2、 类图的元素 类、接口、协作、关系,我们只简单介绍一下这四种元素。 同其他的图一样,类图也可以包含注解和限制。 类图中也可以包含包和子系统,这两者用来将元素分组。 有时候你也可以将类的实例放到类图中。 3、 类 A、 类是对一组具有相同属性、操作、关系和语义的对象的抽象,它是面向对象系统组织结构的核心,包括名称部分(Name)、属性部分(Attribute)和操作部分(Operation),见下图。 B、 类属性的语法为: [可见性] 属性名 [:类型] [=初始值] [{属性字符串}] 可见性:公有(Public)“+”、私有(Private)“-”、受保护(Protected)“#” 类操作的语法为: [可见性] 操作名 [(参数表)] [:返回类型] [{属性字符串}] 可见性:公有(Public)“+”、私有(Private)“-”、受保护(Protected)“#”、包内公有(Package)“~” 参数表: 定义方式:“名称:类型”;若存在多个参数,将各个参数用逗号隔开;参数可以具有默认值; 属性字符串: 在操作的定义中加入一些除了预定义元素之外的信息。 4、 接口 在没有给出对象的实现和状态的情况下对对象行为的描述。 一个类可以实现一个或多个接口。 使用两层矩形框表示,与类图的区别主要是顶端有<<interface>>显示: 也可以用一个空心圆表示: 5、 协作 协作是指一些类、接口和其他的元素一起工作提供一些合作的行为,这些行为不是简单地将元素 加能得到的。例如:当你为一个分布式的系统中的事务处理过程建模型时,你不可能只通过一个类来明白事务是怎样进行的,事实上这个过程的执行涉及到一系列的 类的协同工作。使用类图来可视化这些类和他们的关系。 6、 关系 这篇文章的重点,详见第二部分。 二、类之间的几种关系 1、 泛化(Generalization) A、 是一种继承关系,表示一般与特殊的关系,它指定了子类如何特化父类的所有特征和行为,描述了一种“is a kind of” 的关系。例如:老虎是动物的一种,即有老虎的特性也有动物的共性。 B、 用带空心箭头的实线表示,箭头指向父类,如下图: 2、 实现(Realization) A、 是一种类与接口的关系,表示类是接口所有特征和行为的实现。 B、 用带空心箭头的虚线表示,箭头指向接口,如下图: 3、 关联(Association) A、 一般关联 a、 关联关系是类与类之间的联结,它使一个类知道另一个类的属性和方法,指明了事物的对象之间的联系,如:老师与学生、丈夫与妻子。关联可以是双向的,也可以是单向的,还有自身关联。 b、 用带普通箭头的实心线表示。双向的关联可以有两个箭头或者没有箭头,单向的关联有一个箭头,如下图: B、 聚合(Aggregation) a、 它是整体与部分(整体 has a 部分)的关系,且部分可以离开整体而单独存在,如车和轮胎是整体和部分的关系,轮胎离开车仍然可以存在。聚合关系是关联关系的一种,是强的关联关系,关联和聚合在语法上无法区分,必须考察具体的逻辑关系。 b、 用带空心菱形的实线表示,菱形指向整体,如下图: C、 组合(Composition) a、 它是整体与部分的关系,但部分不能离开整体而单独存在。如公司和部门是整体和部分的关系,没有公司就不存在部门。组合关系是关联关系的一种,是比聚合关系还要强的关系,它要求普通的聚合关系中代表整体的对象负责代表部分的对象的生命周期。 b、 用带实心菱形的实线表示,菱形指向整体,如下图: 4、 依赖(Dependency) A、 元素A的变化会影响元素B,那么B和A的关系是依赖关系,B依赖A。要避免双向依赖,一般来说,不应该存在双向依赖。关联、实现、泛化都是依赖关系。 B、 用带箭头的虚线表示,箭头指向被依赖元素。 5、 总结 各种关系的强弱顺序如下: 泛化 = 实现 > 组合 > 聚合 > 关联 > 依赖 下面这张UML图,比较形象地展现了各种类图关系: 原文出自: http://www.cnblogs.com/fuhongxue2011/archive/2012/12/09/2810408.html
摘要: 近日来对Ext特别感兴趣,也许是它那种OO的设计思想吸引了我,也可以追溯到第一次见到EXT那种漂亮的界面开始吧.求神拜佛不如自食其力,为了一点小的问题找遍了GOOGLE也没个结果,自己甚少去BBS混,也不熟悉规矩,只能硬着头皮自己干了.翻源代码是一道必不可少的工序,说来惭愧,自己对JS的认识还停留在入门阶段.这里说说自己对于Ext验证这里浅薄的理解:首先看看如下一段代码Code highlight... 阅读全文
由于HTTP协议的无状态特性,导致在ASP.NET编程中,每个请求都会在服务端从头到执行一次管线过程, 对于ASP.NET页面来说,Page对象都会重新创建,所有控件以及内容都会重新生成, 因此,如果希望上一次的页面状态能够在后续页面中保留,则必需引入状态管理功能。 ASP.NET为了实现状态管理功能,提供了8种方法,可帮助我们在页面之间或者整个用户会话期间保留状态数据。 这些方法分为二类:视图状态、控件状态、隐藏域、Cookie 和查询字符串会以不同方式将数据发送到客户端上。 而应用程序状态、会话状态和配置文件属性(Profile)则会将数据存储到服务端。 虽然每种方法都有不同的优点和缺点,对于小的项目来说,可以选择自己认为最容易使用的方法, 然而,对于有着较高要求的程序,尤其是对于性能与扩展性比较关注的程序来说, 选择不同的方法最终导致的差别可能就非常大了。 在这篇博客中,我将谈谈自己对ASP.NET状态管理方面的一些看法。 注意:本文的观点可能并不合适开发小型项目,因为我关注的不是易用性。 hidden-input hidden-input 这个名字我是取的,表示所有type="hidden"的input标签元素。 在中文版的MSDN中,也称之为 隐藏域 。 hidden-input通常存在于HTML表单之内,它不会显示到页面中, 但可以随表单一起提交,因此,经常用于维护当前页面的相关状态,在服务端我们可以使用Request.Form[]来访问这些数据。 一般说来,我通常使用hidden-input来保存一些中间结果,用于在多次提交中维持一系列状态, 或者用它来保存一些固定参数用来提交给其它页面(或网站)。 在这些场景中,我不希望用户看到这些数据,因此,使用hidden-input是比较方便的。 关于表单的更多介绍可参考我的博客:细说 Form (表单) 在ASP.NET WebForm框架中,我们可以使用HiddenField控件来创建一个hidden-input控件,并可以在服务端操作它, 还可以直接以手写的方式使用隐藏域,例如: <input type="hidden" name="hidden-1" value="aaaaaaa" /> <input type="hidden" name="hidden-2" value="bbbbbbb" /> <input type="hidden" name="hidden-3" value="ccccccc" /> 另外,我们还可以调用ClientScript.RegisterHiddenField()方法来创建隐藏域: ClientScript.RegisterHiddenField("hidden-4", "ddddddddd"); 输出结果: <input type="hidden" name="hidden-4" id="hidden-4" value="ddddddddd" /> 这三种方法对于生成的HTML代码来说,主要差别在于它们出现位置不同: 1. HiddenField控件:由HiddenField的出现位置来决定(在form内部)。 2. RegisterHiddenField方法:在form标签的开头位置。 3. hidden-input:你写在哪里就是哪里。 优点: 1. 不需要任何服务器资源:隐藏域随页面一起发送到客户端。 2. 广泛的支持:几乎所有浏览器和客户端设备都支持具有隐藏域的表单。 3. 实现简单:隐藏域是标准的 HTML 控件,不需要复杂的编程逻辑。 缺点: 1. 不能在多页面跳转之间维持状态。 2. 用户可见,保存敏感数据时需要加密。 QueryString 查询字符串是存在于 URL 结尾的一段数据。下面是一个典型的查询字符串示例(红色部分文字): http://www.abc.com/demo.aspx?k1=aaa&k2=bbb&k3=ccc 查询字符串经常用于页面的数据过滤,例如: 1. 给列表页面增加分页参数,list.aspx?page=2 2. 给列表页面增加过虑范围,Product.aspx?categoryId=5 3. 显示特定记录,ProductInfo.aspx?page=3 关于查询字符串的用法,我补充二点: 1. 可以调用HttpUtility.ParseQueryString()来解析查询字符串。 2. 允许参数名重复:list.aspx?page=2&page=3,因此在修改URL参数时,使用替换方式而不是追加。 关于参数重名的读取问题,请参考我的博客:细说 Request[]与Request.Params[] 优点: 1. 不需要任何服务器资源:查询字符串的数据包含在每个URL中。 2. 广泛的支持:几乎所有的浏览器和客户端设备均支持使用查询字符串传递参数值。 3. 实现简单:在服务端直接访问Request.QueryString[]可读取数据。 4. 页面传值简单:<a href="url">或者 Response.Redirect(url) 都可以实现。 缺点: 1. 有长度限制。 2. 用户可见,不能保存敏感数据。 Cookie 由于HTTP协议是无状态的,对于一个浏览器发出的多次请求,WEB服务器无法区分它们是不是来源于同一个浏览器。所以,需要额外的数据用于维护会话。 Cookie 正是这样的一段随HTTP请求一起被传递的额外数据。 Cookie 是一小段文本信息,它的工作方式就是伴随着用户请求和页面在 Web 服务器和浏览器之间传递。Cookie 包含每次用户访问站点时 Web 应用程序都可以读取的信息。 与hidden-input, QueryString相比,Cookie有更多的属性,许多浏览器可以直接查看这些信息: 由于Cookie拥有这些属性,因此在客户端状态管理中可以实现更多的功能,尤其是在实现客户端会话方面具有不可替代的作用。 关于Cookie的更多讲解,请参考我的另一篇博客:细说Cookie 优点: 1. 可配置到期规则:Cookie可以在客户端长期存在,也可以在浏览器关闭时清除。 2. 不需要任何服务器资源:Cookie 存储在客户端。 3. 简单性:Cookie 是一种基于文本的轻量结构,包含简单的键值对。 4. 数据持久性:与其它的客户端状态数据相比,Cookie可以实现长久保存。 5. 良好的扩展性:Cookie的读写要经过ASP.NET管线,拥有无限的扩展性。 这里我要解释一下Cookie 【良好的扩展性】是个什么概念,比如: 1. 我可以实现把Cookie保存到数据库中而不需要修改现有的项目代码。 2. 把SessionId这样由ASP.NET产生的临时Cookie让它变成持久保存。 缺点: 1. 大小受到限制。 2. 增加请求头长度。 3. 用户可见,保存敏感数据时需要加密。 ApplicationState 应用程序状态是指采用HttpApplicationState实现的状态维持方式,使用代码如下: Application.Lock(); Application["PageRequestCount"] = ((int)Application["PageRequestCount"]) + 1; Application.UnLock(); 对于这种方法,我不建议使用,因为: 1. 与使用静态变量差不多,直接使用静态变量可以不需要字典查找。 2. 选择强类型的集合或者变量可以避免装箱拆箱。 ViewState,ControlState 视图状态,控件状态,二者是类似,在页面中表现为一个hidden-input元素: <input type="hidden" name="__VIEWSTATE" id="__VIEWSTATE" value="......................" /> 控件状态是ASP.NET 2.0中引入,与视图状态相比,它不允许关闭。 由于它们使用方式一致,而且视图状态是基于控件状态的实现逻辑,所以我就不区分它们了。 在ASP.NET的早期,微软为了能帮助广大开发人员提高开发效率,引用入一大批的服务端控件,并为了能将事件编程机制引入ASP.NET中,又发明了ViewState。 这种方式虽然可以简化开发工作量,然而却有一些限制和缺点: 1. 视图状态的数据只能用于回发(postback)。 2. 视图状态的【滥用】容易导致生成的HTML较大,这会引起一个恶性循环: a. 过大的ViewState在序列化过程中会消耗较多的服务器CPU资源, b. 过大的ViewState最终生成的HTML输出也会很大,它会浪费服务端网络资源, c. 过大的ViewState输出导致表单在下次提交时,会占用客户端网络资源。 d. 过大的ViewState数据上传到服务端后,反序列化又会消耗较多的服务器CPU资源。 因此,整个交互过程中,用户一直在等待,用户体验极差。 在ASP.NET兴起的年代,ViewState绝对是个了不起的发明。 然而,现在很多关于ASP.NET性能优化的方法中,都会将【关闭ViewState】放在头条位置。 为什么会这样呢,大家可以自己思考一下了。 有些人认为:我现在做的程序只是在局域网内使用,使用ViewState完全没有问题! 然而,那些人或许没有想过: 1. 未来用户可能会把它部署在互联网上运行(对于产品来说就是遇到大客户了)。 2. 项目早期的设计与规划,对后期的开发与维护来说,影响是巨大的,因为许多基础部分通常是在早期开发的。 当这二种情况的任何一种发生时,想再禁用ViewState,可能已经晚了。 对于视图状态,我认为它解决的问题比它引入的问题要多要复杂, 因此,我不想花时间整理它的优缺点,我只想说一句:把它关了,在web.config中关了。 另外,我不排斥使用服务器控件,我认为:你可以使用服务端控件显示数据,但不要用它处理回发。 如果你仍然认为视图状态是不可缺少的,那我还是建议你看看ASP.NET MVC框架,看看没有视图状态是不是照样可以写ASP.NET程序。 Session Session是ASP.NET实现的一种服务端会话技术,它允许我们方便地在服务端保存与用户有关的会话数据。 我认为Session只有一个优点:最简单的服务端会话实现方式。 缺点: 1. 当mode="InProc"时,容易丢失数据,为什么?因为网站会因为各种原因重启。 2. 当mode="InProc"时,Session保存的东西越多,就越占用服务器内存,对于用户在线人数较多的网站,服务器的内存压力会比较大。 3. 当mode="InProc"时,程序的扩展性会受到影响,原因很简单:服务器的内存不能在多台服务器间共享。 4. 当采用进程外模式时,在每次请求中,不管你用不用会话数据,所有的会话数据都为你准备好了(反序列化),这其实很是浪费资源的。 5. 如果你没有关闭Session,SessionStateModule就一直在工作中,尤其是全采用默认设置时,会对每个请求执行一系列的调用,浪费资源。 6. 阻塞同一客户端发起的多次请求(默认方式)。 7. 无Cookie会话可能会丢失数据(重新生成已过期的会话标识符)。 Session的这些缺点也提醒我们: 1. 当网站的在线人数较多时,一定不要用Session保存较大的对象。 2. 在密集型的AJAX型网站或者大量使用iframe的网站中,要关注Session可能引起的服务端阻塞问题。 3. 当采用进程外模式时,不需要访问Session的页面,一定要关闭,否则会浪费服务器资源。 如果想了解更多的Session特点,以及我对Session的看法,可以浏览我的博客:Session,有没有必要使用它? Session的本质有二点: 1. SessionId + 服务端字典:服务端字典保存了某个用户的所有会话数据。 2. 用SessionId识别不同的客户端:SessionId通常以Cookie形式发送到客户端。 我认为了解Sesssion本质非常有用,因为可以借鉴并实现自己的服务端会话方法。 关于Session我还想说一点: 有些新手喜欢用Session来实现身份认证功能,这是一种【不正确】的方法。 如果你的ASP.NET应用程序需要身份认证功能,请使用 Forms身份认证 或者 Windows身份认证 Profile Profile 在中文版的MSDN中被称为 配置文件属性,这个功能是在 ASP.NET 2.0 中引入的。 ASP.NET提供这个功能主要是为了简化与用户相关的个性化信息的读写方式。 简化主要体现在3个方面: 1. 自动与某个用户关联,已登录用户或者未登录都支持。 2. 不需要我们设计用户的个性化信息的保存表结构,只要修改配置文件就够了。 3. 不需要我们实现数据的加载与保存逻辑,ASP.NET框架替我们实现好了。 为了使用Profile,我们首先在web.config中定义所需要的用户个性化信息: <profile> <properties> <add name="Address"/> <add name="Tel"/> </properties> </profile> 然后,就可以在页面中使用了: 为什么会这样呢? 原因是ASP.NET已经根据web.config为我们创建了一个新类型: using System; using System.Web.Profile; public class ProfileCommon : ProfileBase { public ProfileCommon(); public virtual string Address { get; set; } public virtual string Tel { get; set; } public virtual ProfileCommon GetProfile(string username); } 有了这个类型后,当我们访问HttpContext.Profile属性时,ASP.NET会创建一个ProfileCommon的实例。 也正是由于Profile的强类型机制,在使用Profile时才会有智能提示功能。 如果我们希望为未登录的匿名用户也提供这种支持,需要将配置修改成: <profile> <properties> <add name="Address" allowAnonymous="true" /> <add name="Tel" allowAnonymous="true"/> </properties> </profile> <anonymousIdentification enabled="true" /> Profile中的每个属性还允许指定类型和默认值,以及序列化方式,因此,扩展性还是比较好的。 尽管Profile看上去很美,然而,使用Profile的人却很少。 比如我就不用它,我也没见有人有过它。 为什么会这样?
我个人认为:它与MemberShip一样,是个鸡肋。 通常说来,我们会为用户信息创建一张User表,增加用户信息时,会通过增加字段的方式解决。 我认为这样集中的数据才会更好,而不是说,有一部分数据由我维护,另一部分数据由ASP.NET维护。
另一个特例是:我们根本不创建User表,直接使用MemberShip,那么Profile用来保存MemberShip没有信息是有必要的。 还是给Profile做个总结吧: 优点:使用简单。 缺点:不实用。 各种状态管理的对比与总结 前面分别介绍了ASP.NET的8种状态管理技术,这里打算给它们做个总结。
| 客户端 | 服务端 | 数据安全性 | 差 | 好 | 数据长度限制 | 有 | 受硬件限制 | 占用服务器资源 | 否 | 是 | 集群扩展性 | 好 | 差 | 表格中主要考察了数据保存与服务端水平扩展的相关重要指标。 下面我来解释表格的结果。 1. 客户端方式的状态数据(hidden-input, QueryString, Cookie): a. 数据对用户来说,可见可修改,因此数据不安全。 b. QueryString, Cookie 都有长度限制。 c. 数据在客户端,因此不占用服务端资源。这个特性对于在线人数很多的网站非常重要。 d. 数据在客户端,因此和服务端没有耦合关系,WEB服务器可以更容易实现水平扩展。 2. 服务端方式的状态数据(ApplicationState,ViewState,ControlState,Session,Profile): a. 数据对用户不可见,因此安全性好。(ApplicationState,Session,Profile) b. 数所长度只受硬件限制,因此,对于在线人数较多的网站,需谨慎选择。 c. 对于存放在内存中的状态数据,由于不能共享内存,因此会限制水平扩展能力。 d. 如果状态数据保存到一台机器,会有单点失败的可能,也会限制了水平扩展能力。 从这个表格我们还可以得到以下结论: 1. 如果很关注数据的安全性,应该首选服务端的状态管理方法。 2. 如果你关注服务端的水平扩展性,应该首选客户端的状态管理方法。 会话状态的选择 接下来,我们再来看看会话状态,它与状态管理有着一些关系,属于比较类似的概念。 谈到会话状态,首先我要申明一点:会话状态与状态不是一回事。 本文前面所说的状态分为二种: 1. 页面之间的状态。 2. 应用程序范围内的状态。 而会话状态是针对某个用户来说,他(她)在多次操作之间的状态。 在用户的操作期间,有可能状态需要在页面之间持续使用, 也有可能服务端程序做过重启,但数据仍然有效。 因此,这种状态数据更持久。 在ASP.NET中,使用会话状态有二个选择:Session 或者 Cookie 。 前者由ASP.NET实现,并有可能依赖后者。 后者则由浏览器实现,ASP.NET提供读写方法。 那么到底选择哪个呢? 如果你要问我这个问题,我肯定会说:我选 Cookie ! 下面是我选择Cookie实现会话状态的理由: 1. 不会有服务端阻塞问题。 2. 不占用服务端资源。 3. 水平扩展没有限制。 4. 也支持过期设置,而且更灵活。 5. 可以在客户端直接使用会话数据。 6. 可以实现更灵活的会话数据加载策略。 7. 扩展性较好(源于ASP.NET管线的扩展性) 如果选择使用Cookie实现会话状态,有3点需要特别注意: 1. 不建议保存敏感数据,除非已加密。 2. 只适合保存短小简单的数据。 3. 如果会话数据较大,可以在客户端保存用户标识,由服务端实现数据的加载保存逻辑。 或许有些人认为:每种技术都有它们的优缺点,有各自的适用领域。 我表示赞同这句话。 但是,我们要清楚一点:每个项目的规模不一样,性能以及扩展性要求也不同。 对于一个小的项目来说,选择什么方法都不是问题, 但是,对于规模较大的项目,我们一定需要取舍。 取舍的目标是:包装越少越好,因为人家做了过多的包装,就会有较多的限制, 所以,不要只关注现在的调用是否方便,其实只要你愿意包装,你也可以让复杂的调用简单化。 改变开发方式,发现新方法 回想一下:为什么在ASP.NET中需要状态管理? 答:因为与HTTP协议有关,服务端没有保存每个请求的上次页面状态。 为什么Windows计算器(这类)程序不用考虑会话问题呢? 答:因为这类程序的界面不需要重新生成,任何变量都可表示状态。 再来看这样一个场景: 图片左边是一个列表页面,允许调整每条记录的优先级,但是有2个要求: 1. 在移动每条记录时,必须输入一个调整理由。 2. 只要输入理由后,那条记录可以任意调整多次。 显然,完成这个任务必须要有状态才能实现。 面对这个问题,你可以思考一下:选择哪种ASP.NET支持的状态管理方法都很麻烦。 怎么办? 我的解决方法:创建一个JavaScript数组,用每个数组元素保存每条记录的状态, 所有用户交互操作用AJAX方式实现,这样页面不会刷新,JavaScript变量中的状态一直有效。 因此,很容易就能解决这个问题。 这个案例也提醒我们:当发现ASP.NET提供的状态管理功能全部不合适时, 我们需要改变开发方式了。 为什么WEB编程都有【无状态】问题,而桌面程序没有? 我认为与HTTP协议有关,但没有绝对的关系。 只要你能保证页面不刷新,也能像桌面程序那样,用JavaScript变量就能维护页面状态。 原文出自: http://www.cnblogs.com/fish-li/archive/2012/11/21/2780086.html
1.本地库导出
mysqldump -u 用户名 -p 数据库名 > 存放位置 例: mysqldump -uroot -p1234 testdb > /root/db/testdb.sql
2.从远程库导出
mysqldump -h 主机IP -u 用户名 -p 数据库名 > 存放位置 例: mysqldump -h192.168.30.30 -uroot -p1234 testdb > /root/db/testdb.sql
3.导出表结构及数据
mysqldump -u 用户名 -p 数据库名 表名 > 导出的文件名 例: mysqldump -uroot -p1234 testdb tab > /root/db/testdb.tab.sql
4.导入数据库
mysql -u用户名 -p密码 数据库名 < 数据库名 例: mysql -uroot -p1234 testdb < /root/db/back.sql 注意:testdb 要事先创建好.
原文出自: http://www.cnblogs.com/natgeo/archive/2012/12/08/2809027.html
在formPanel里加个Key事件 1 keys : [{ 2 key : Ext.EventObject.ENTER, 3 fn : function(keyCode, e) { 4 var field = Ext.getCmp(e.target.id); 5 if (Ext.isDefined(field) && field != null) { 6 if (Ext.isDefined(field.xtype)) { 7 if (field.isXType('datefield')) { 8 field.setValue(field.getValue()); 9 } 10 } 11 } 12 if (Ext.isIE) { 13 e.browserEvent.keyCode = Ext.EventObject.TAB; 14 } else { 15 var currentfield = Ext.getCmp(e.target.id); 16 var fields = refThis.HusbandView 17 .findByType('field'); 18 var i = 0; 19 for (; i < fields.length; i++) { 20 if (fields[i].id == currentfield.id) 21 break; 22 } 23 while (true) { 24 i++; 25 if (fields.length <= i) 26 break; 27 if (!fields[i].disabled 28 && fields[i].xtype != 'hidden' 29 && !fields[i].hidden) 30 break; 31 } 32 if (fields.length <= i) 33 return; 34 if (!fields[i].disabled) { 35 fields[i].focus(); 36 if (Ext.isDefined(fields[i].selectText)) 37 fields[i].selectText(); 38 } 39 } 40 } 41 }]
原文出自:http://xlong224.blog.163.com/blog/static/601214932011102810201224/
原先的EditGrid无法解决回车控制问题,它的回车控制是向下跑的。而我想让它横着走。搞了半天终于实现了。 1 Ext.override(Ext.grid.RowSelectionModel, { 2 onEditorKey : function(field, e) { 3 // alert('go'); 4 var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor; 5 var shift = e.shiftKey; 6 Ext.log('k:' + k); 7 if (k == e.ENTER) { 8 e.stopEvent(); 9 ed.completeEdit(); 10 if (shift) { 11 newCell = g.walkCells(ed.row, ed.col - 1, -1, 12 this.acceptsNav, this); 13 } else { 14 // alert('go'); 15 newCell = g.walkCells(ed.row, ed.col + 1, 1, 16 this.acceptsNav, this); 17 } 18 } else if (k == e.TAB) { 19 e.stopEvent(); 20 ed.completeEdit(); 21 if (this.moveEditorOnEnter !== false) { 22 if (shift) { 23 newCell = g.walkCells(ed.row - 1, ed.col, -1, 24 this.acceptsNav, this); 25 } else { 26 // alert('go'); 27 newCell = g.walkCells(ed.row + 1, ed.col, 1, 28 this.acceptsNav, this); 29 } 30 } 31 } else if (k == e.ESC) { 32 ed.cancelEdit(); 33 } 34 if (newCell) { 35 g.startEditing(newCell[0], newCell[1]); 36 } 37 } 38 }); 39 var sm2 = new Ext.grid.RowSelectionModel({ 40 moveEditorOnEnter : true, 41 singleSelect : true, 42 listeners : { 43 rowselect : function(sm, row, rec) { 44 centerForm.getForm().loadRecord(rec); 45 } 46 } 47 48 });
原文出自:http://erichua.iteye.com/blog/234698 2. 默认extjs中editorgrid编辑单元格的时候按回车是将焦点向下移动,按照一般的逻辑应该是向右移动。 其实只要将原先rowSelectionModel中onEditorKey方法override一下即可。 代码如下: 1 Ext.override(Ext.grid.RowSelectionModel, { 2 3 onEditorKey : function(field, e) { 4 var k = e.getKey(), newCell, g = this.grid, last = g.lastEdit, ed = g.activeEditor, shift = e.shiftKey, ae, last, r, c; 5 6 if (k == e.TAB) { 7 e.stopEvent(); 8 ed.completeEdit(); 9 if (shift) { 10 newCell = g.walkCells(ed.row, ed.col - 1, -1, this.acceptsNav, 11 this); 12 } else { 13 newCell = g.walkCells(ed.row, ed.col + 1, 1, this.acceptsNav, 14 this); 15 } 16 } else if (k == e.ENTER) { 17 if (this.moveEditorOnEnter !== false) { 18 if (shift) { 19 newCell = g.walkCells(last.row, last.col - 1, -1, 20 this.acceptsNav, this); 21 } else { 22 newCell = g.walkCells(last.row, last.col + 1, 1, 23 this.acceptsNav, this); 24 } 25 } 26 } 27 if (newCell) { 28 r = newCell[0]; 29 c = newCell[1]; 30 31 this.onEditorSelect(r, last.row); 32 33 if (g.isEditor && g.editing) { // *** handle tabbing while 34 // editorgrid is in edit mode 35 ae = g.activeEditor; 36 if (ae && ae.field.triggerBlur) { 37 // *** if activeEditor is a TriggerField, explicitly call 38 // its triggerBlur() method 39 ae.field.triggerBlur(); 40 } 41 } 42 g.startEditing(r, c); 43 } 44 } 45 })
方法1: JMX 很多人询问如何通过 JMX 来管理 Quertz,很奇怪的是 Quartz 的文档完全没有提及这方面的问题,你可以在 quartz.properties 中通过以下配置来启用 JMX 的支持: org.quartz.scheduler.jmx.export = true
然后你就可以使用标准的 JMX 客户端,例如 $JAVA_HOME/bin/jconsole 来连接到 Quartz 并进行远程管理。 方法2: RMI 另外一个远程管理 Quartz 的方法就是启用 RMI。如果你用的是 RMI 的方式,就会自动一个 Quartz 实例来作为 RMI 服务器,然后你可启动第二个 Quartz 实例来作为 RMI 客户端,二者通过 TCP 端口进行通讯。 服务器端调度实例配置方法(quartz.properties): org.quartz.scheduler.rmi.export = true org.quartz.scheduler.rmi.createRegistry = true org.quartz.scheduler.rmi.registryHost = localhost org.quartz.scheduler.rmi.registryPort = 1099 org.quartz.scheduler.rmi.serverPort = 1100
客户端调度器配置(quartz.properties): org.quartz.scheduler.rmi.proxy = true org.quartz.scheduler.rmi.registryHost = localhost org.quartz.scheduler.rmi.registryPort = 1099
关于 Quartz 的 RMI 特性的文档描述请看 这里. Quartz 不提供客户端 API,服务器端和客户端都是使用 org.quartz.Scheduler,只是配置不同而已。通过不同的配置来执行不同的行为。对服务器端来说,调度器用来执行所有的作业;而客户端只 是一个简单的代理,不运行任何作业,在关闭客户端的时候必须小心,因为它允许你也同时关闭服务器端。 这些配置都在我的示例程序 MySchedule 中有着重说明,如果你运行的是 Web 应用,你可以看到 这个演示,你将看到我们提供了很多 quartz 的示例配置用来做远程管理。 如果使用 RMI 方法,你可以用 MySchedule Web UI 做为一个代理来管理 Quartz,你可以查看作业,可停止服务器的运行。 根据我的经验,使用 RMI 方法的弊端就是创建一个新的单点故障,如果你的服务器端口宕掉了,就无法恢复。 原文出自: http://www.oschina.net/question/12_67413
1、Scheduler的配置 <bean class="org.springframework.scheduling.quartz.SchedulerFactoryBean"> <property name="triggers"> <list> <ref bean="testTrigger"/> </list> </property> <property name="autoStartup" value="true"/> </bean>
说明:Scheduler包含一个Trigger列表,每个Trigger表示一个作业。 2、Trigger的配置 <bean id="testTrigger" class="org.springframework.scheduling.quartz.CronTriggerBean"> <property name="jobDetail" ref="testJobDetail"/> <property name="cronExpression" value="*/1 * * * * ?"/> <!-- 每隔1秒钟触发一次 --> </bean>
说明: 1)Cron表达式的格式:秒 分 时 日 月 周 年(可选)。 字段名 允许的值 允许的特殊字符 秒 0-59 , - * / 分 0-59 , - * / 小时 0-23 , - * / 日 1-31 , - * ? / L W C 月 1-12 or JAN-DEC , - * / 周几 1-7 or SUN-SAT , - * ? / L C # 年 (可选字段) empty, 1970-2099 , - * / “?”字符:表示不确定的值 “,”字符:指定数个值 “-”字符:指定一个值的范围 “/”字符:指定一个值的增加幅度。n/m表示从n开始,每次增加m “L”字符:用在日表示一个月中的最后一天,用在周表示该月最后一个星期X “W”字符:指定离给定日期最近的工作日(周一到周五) “#”字符:表示该月第几个周X。6#3表示该月第3个周五 2)Cron表达式范例: 每隔5秒执行一次:*/5 * * * * ? 每隔1分钟执行一次:0 */1 * * * ? 每天23点执行一次:0 0 23 * * ? 每天凌晨1点执行一次:0 0 1 * * ? 每月1号凌晨1点执行一次:0 0 1 1 * ? 每月最后一天23点执行一次:0 0 23 L * ? 每周星期天凌晨1点实行一次:0 0 1 ? * L 在26分、29分、33分执行一次:0 26,29,33 * * * ? 每天的0点、13点、18点、21点都执行一次:0 0 0,13,18,21 * * ? 3、JobDetail的配置 <bean id="testJobDetail" class="org.springframework.scheduling.quartz.MethodInvokingJobDetailFactoryBean"> <property name="targetObject" ref="testJob"/> <property name="targetMethod" value="execute"/> <property name="concurrent" value="false"/> <!-- 是否允许任务并发执行。当值为false时,表示必须等到前一个线程处理完毕后才再启一个新的线程 --> </bean>
4、业务类的配置 <bean id="testJob" class="com.cjm.web.service.quartz.TestJob"/> 5、业务类源代码 public class TestJob { public void execute(){ try{ //. }catch(Exception ex){ ex.printStackTrace(); } } }
说明:业务类不需要继承任何父类,也不需要实现任何接口,只是一个普通的java类。 注意: 在Spring配置和Quartz集成内容时,有两点需要注意 1、在<Beans>中不能够设置default-lazy-init="true",否则定时任务不触发,如果不明确指明default-lazy-init的值,默认是false。 2、在<Beans>中不能够设置default-autowire="byName"的属性,否则后台会报 org.springframework.beans.factory.BeanCreationException错误,这样就不能通过Bean名称自 动注入,必须通过明确引用注入
原文出自: http://www.oschina.net/question/8676_9032
缓存在 Solr 中充当了一个非常重要的角色,Solr 中主要有这三种缓存: - Filter cache(过滤器缓存),用于保存过滤器(fq 参数)和层面搜索的结果
- Document cache(文档缓存),用于保存 lucene 文档存储的字段
- Query result(查询缓存),用于保存查询的结果
还有第四种缓存,lucene 内部的缓存,不过该缓存外部无法控制到。 通过这 3 种缓存,可以对 solr 的搜索实例进行调优。调整这些缓存,需要根据索引库中文档的数量,每次查询结果的条数等。 在调整参数前,需要事先得到 solr 示例中的以下信息: - 索引中文档的数量
- 每秒钟搜索的次数
- 过滤器的数量
- 一次查询返回最大的文档数量
- 不同查询和不同排序的个数
这些数量可以在 solr admin 页面的日志模块找到。假设以上的值分别为: - 索引中文档的数量:1000000
- 每秒钟搜索的次数:100
- 过滤器的数量:200
- 一次查询返回最大的文档数量:100
- 不同查询和不同排序的个数:500
然后可以开始修改 solrconfig.xml 中缓存的配置了,第一个是过滤器缓存: <filterCache class="solr.FastLRUCache" size="200" initialSize="200" autowarmCount="100"/> 第二个是查询结果缓存: <queryResultCache class="solr.FastLRUCache" size="500" initialSize="500" autowarmCount="250"/> 第三个是文档缓存: <documentCache class="solr.FastLRUCache" size="11000" initialSize="11000" /> 这几个配置是基于以上的几个假设的值进行调优的。 原文出自: http://insolr.com/forum.php?mod=viewthread&tid=7&reltid=880&pre_thread_id=19&pre_pos=5&ext=
一.首先准备好solr的dataimport功能需要的东西,在solr的下载包中。分别在: 1》Solr-1.3.0\dist\apache-solr-dataimporthandler-1.3.0.jar 2》E:\education\search\Solr-1.3.0\example\example-DIH\solr\ 3》你是哪种数据库,提供该数据库的jdbc驱动。 二.如果你还不会运行solr,请参考本人的前几篇博客。这里要做的是,先把E:\education\search\Solr-1.3.0 \example\example-DIH\solr\下面的东西拷贝到solr的HOME目录,然后删除rss,这个是另外一个功能是导入rss订阅信 息到solr中,确实很强,这都想到了。将jar文件,实际就两个拷贝到tomcat的webapps下面的solr的WEB-INF的lib文件夹下 面。 三.更改solr Home目录下的conf/solrconfig.xml,其实就是提交一个solrRequestHandler,代码如下: - <requestHandler name="/dataimport" class="org.apache.solr.handler.dataimport.DataImportHandler">
- <lst name="defaults">
- <str name="config">C:\solr-tomcat\solr\db\conf\db-data-config.xml</str>
- </lst>
- </requestHandler>
<requestHandler name="/dataimport" class="org.apache.solr.handler.dataimport.DataImportHandler"> <lst name="defaults"> <str name="config">C:\solr-tomcat\solr\db\conf\db-data-config.xml</str> </lst> </requestHandler> 四.将solr Home目录下面的solrconfig.xml和schema.xml拷贝到db文件夹下面的conf中。 五.修改db\conf\db-data-config.xml - <dataConfig>
- <dataSource type="JdbcDataSource" driver="com.mysql.jdbc.Driver" url="jdbc:mysql://localhost:3306/tuitui" user="root" password="mysql"/>
- <document name="shop">
- <entity name="tuitui_shop" pk="shopId" query="select * from tuitui_shop">
- <field column="shopid" name="shopId" />
- <field column="shopName" name="shopName" />
- <field column="shopUrl" name="shopUrl" />
- <field column="keyword" name="keyword" />
- <field column="synopsis" name="synopsis" />
- <field column="province" name="province" />
- <field column="city" name="city" />
- <field column="domain" name="domain" />
- <field column="address" name="address" />
- <field column="coordinate" name="coordinate" />
- <field column="shopSspn" name="shopSspn" />
- <field column="phone" name="phone" />
- <field column="createTime" name="createTime" />
- </entity>
- </document>
- </dataConfig>
<dataConfig> <dataSource type="JdbcDataSource" driver="com.mysql.jdbc.Driver" url="jdbc:mysql://localhost:3306/tuitui" user="root" password="mysql"/> <document name="shop"> <entity name="tuitui_shop" pk="shopId" query="select * from tuitui_shop"> <field column="shopid" name="shopId" /> <field column="shopName" name="shopName" /> <field column="shopUrl" name="shopUrl" /> <field column="keyword" name="keyword" /> <field column="synopsis" name="synopsis" /> <field column="province" name="province" /> <field column="city" name="city" /> <field column="domain" name="domain" /> <field column="address" name="address" /> <field column="coordinate" name="coordinate" /> <field column="shopSspn" name="shopSspn" /> <field column="phone" name="phone" /> <field column="createTime" name="createTime" /> </entity> </document></dataConfig> 其中的意思我做简单解释,具体大家可以去看看官方wiki。 document:一个文档也就是lucene的document这个没什么解释的; entity:主要针对的是一个数据库表; filed:属性column是数据库的字段,name是filed的名字,即schema中的field name http://wiki.apache.org/solr/DataImportHandler 六.启动TOMCAT,输入地址进行导入,导入分为很多模式:我选用的全部倒入模式。 http://localhost/solr/dataimport?command=full-import 原文出自: http://insolr.com/forum.php?mod=viewthread&tid=128&reltid=880&pre_thread_id=19&pre_pos=4&ext=
先从网址 http://labs.mop.com/apache-mirror/lucene/solr/3.6.1/apache-solr-3.6.1.zip 下载程序文件 1.解压开找到apache-solr-3.6.1\apache-solr-3.6.1\dist目录下的apache-solr-3.6.1.war 直接放到tomcat/webapps下面 2.先别急着启动慢慢来,修改配置文件apache-solr-3.6.1\WEB-INF\web.xml 找到: <!-- People who want to hardcode their "Solr Home" directly into the WAR File can set the JNDI property here... --> <!-- <env-entry> <env-entry-name>solr/home</env-entry-name> <env-entry-value>/put/your/solr/home/here</env-entry-value> <env-entry-type>java.lang.String</env-entry-type> </env-entry> --> 更改这为 <!-- People who want to hardcode their "Solr Home" directly into the WAR File can set the JNDI property here... --> <env-entry> <env-entry-name>solr/home</env-entry-name> <env-entry-value>d:/solr_work/solr</env-entry-value> <env-entry-type>java.lang.String</env-entry-type> </env-entry> 注:红色字体的文件夹为apache-solr-3.6.1\example\solr 该应用(此为单核应用,多核以后会讲到)放大D盘的新建空目录solr_work目录下就可以 3.启动tomcat,输入 http://localhost:8080/solr solr应用搭建成功 原文出自: http://insolr.com/forum.php?mod=viewthread&tid=3&reltid=880&pre_thread_id=19&pre_pos=6&ext=
本文只是Solr 4.0的基础教程,本人不经常写东西,写的不好请见谅,欢迎到群233413850进行讨论学习。 先说一点部署之后肯定会有人用solrj,solr 4.0好像添加了不少东西,其中CommonsHttpSolrServer这个类改 名为HttpSolrServer,我是找了半天才发现,大家以后可以注意。 部署前准备: Solr 4.0 目录: 这里是我的部署方式,Tomcat安装好之后把apache-solr-4.0.0\example\webapps下的solr.war文件拷贝到Tomcat下的 Tomcat7.0\webapps目录下,然后启动Tomcat 报错不用管,solr.war会自动解压,之后打开Tomcat7.0\webapps\solr\WEB- INF\web.xml,把下面代码复制进去放到后面: - <env-entry>
- <env-entry-name>solr/home</env-entry-name>
- <env-entry-value>E:\SolrHome</env-entry-value>
- <env-entry-type>java.lang.String</env-entry-type>
- </env-entry>
复制代码 其中 E:\SolrHome 是存放solr配置文件等,修改为自己文件的位置,为了看着更清晰直观,你可以这样放: E:\Tomcat7.0 E:\apache-solr-4.0.0 E:\SolrHome 现在可以重新启动Tomcat了,没有报错,通过这个地址进入Solr4.0页面:http://localhost:8080/solr 如果进入以上界面说明成功了,没有成功的话页面会有ERROR提示。 如图:左侧core0,core1等是solr 4.0中的示例,core0和core1位于apache-solr-4.0.0\example\multicore所有文件都下拷贝 到E:\SoleHome下,core0和core1可以理解为两个库,都是独立的,用来存放索引以及生成这些索引文件所需要的配置文件,solrtest是我测试建立的目录,如图: 没添加一个库都需要在solr.xml里面进行配置,这个比较简单 - <solr persistent="false">
- <cores adminPath="/admin/cores" host="${host:}" hostPort="${jetty.port:}">
- <core name="core0" instanceDir="core0" />
- <core name="core1" instanceDir="core1" />
- <core name="collection1" instanceDir="collection1" />
- <core name="solrtest" instanceDir="solrtest"/>
- </cores>
- </solr>
复制代码 name="",是库的名字,instanceDir="",是目录 每个目录下包含两个文件夹conf和data,data下有两个文件夹index和tlog,index是存放生成的索引文件,tlog存放log, conf下是必要的配置文件schema.xml和solrconfig.xml,可以参考官方或者core里面的配置文件: - <?xml version="1.0" ?>
- <schema name="example solr test" version="1.1">
- <types>
- <fieldtype name="string" class="solr.StrField" sortMissingLast="true" omitNorms="true"/>
- <fieldType name="long" class="solr.TrieLongField" precisionStep="0" positionIncrementGap="0"/>
- </types>
- <fields>
- <!-- general -->
- <field name="id" type="string" indexed="true" stored="true" multiValued="false" required="true"/>
- <field name="type" type="string" indexed="true" stored="true" multiValued="false" />
- <field name="name" type="string" indexed="true" stored="true" multiValued="false" />
- <field name="_version_" type="long" indexed="true" stored="true"/>
- </fields>
- <!-- field to use to determine and enforce document uniqueness. -->
- <uniqueKey>id</uniqueKey>
- <!-- field for the QueryParser to use when an explicit fieldname is absent -->
- <defaultSearchField>name</defaultSearchField>
- <!-- SolrQueryParser configuration: defaultOperator="AND|OR" -->
- <solrQueryParser defaultOperator="OR"/>
- </schema>
复制代码 solrconfig.xml我还不是很懂,在这里就不讲了,但是必须配置(好像是必须配): - <?xml version="1.0" encoding="UTF-8" ?>
- <!-- 可以从core文件中copy过来 -->
- <config>
- <luceneMatchVersion>LUCENE_40</luceneMatchVersion>
-
- <directoryFactory name="DirectoryFactory" class="${solr.directoryFactory:solr.StandardDirectoryFactory}"/>
- <dataDir>${solr.solrtest.data.dir:}</dataDir> 这里solr.后面名字改掉
- <updateHandler class="solr.DirectUpdateHandler2">
- <updateLog>
- <str name="dir">${solr.solrtest.data.dir:}</str>
- </updateLog>
- </updateHandler>
- <requestHandler name="/get" class="solr.RealTimeGetHandler">
- <lst name="defaults">
- <str name="omitHeader">true</str>
- </lst>
- </requestHandler>
-
- <requestHandler name="/replication" class="solr.ReplicationHandler" startup="lazy" />
- <requestDispatcher handleSelect="true" >
- <requestParsers enableRemoteStreaming="false" multipartUploadLimitInKB="2048" />
- </requestDispatcher>
-
- <requestHandler name="standard" class="solr.StandardRequestHandler" default="true" />
- <requestHandler name="/analysis/field" startup="lazy" class="solr.FieldAnalysisRequestHandler" />
- <requestHandler name="/update" class="solr.UpdateRequestHandler" />
- <requestHandler name="/admin/" class="org.apache.solr.handler.admin.AdminHandlers" />
- <requestHandler name="/admin/ping" class="solr.PingRequestHandler">
- <lst name="invariants">
- <str name="q">solrpingquery</str>
- </lst>
- <lst name="defaults">
- <str name="echoParams">all</str>
- </lst>
- </requestHandler>
- <!-- config for the admin interface -->
- <admin>
- <defaultQuery>solr</defaultQuery>
- </admin>
- </config>
复制代码 之后在exampledocs目录下手动创建一个solr1.xml文件: - <?xml version="1.0" ?>
- <add>
- <doc>
- <field name="id">solr1</field>
- <field name="type">type1</field>
- <field name="name">my solr test</field>
- </doc>
- </add>
复制代码 跟schema.xml中的字段对应,好了现在可以提交数据了,这里在window命令窗口提交数据,把E:\apache-solr- 4.0.0\example\exampledocs下的post.jar复制到 E:\SolrHome\exampledocs下 打开命令窗口CD 到E:\SolrHome\exampledocs下使用命令,Tomcat不要忘了开: java -Durl=http://localhost:8080/solr/solrtest/update -Ddata=files -jar post.jar solr1.xml 如图成功的添加的索引,看下E:\SolrHome\solrtest\data\index下的文件:
进入solr页面: 点击Executu Query查询,右侧生成了地址可以打开查看,到此结束。
Debug视图 认识debug视图,红色部分框为线程堆栈视图,黄色部分框为表达式、断点、变量视图,蓝色部分为代码视图。
线程堆栈视图 分别介绍一下这几个按钮的含义: 1.表示当前实现继续运行直到下一个断点,快捷键为F8。 2.表示打断整个进程 3.表示进入当前方法,快捷键为F5。 4.表示运行下一行代码,快捷键为F6。 5.表示退出当前方法,返回到调用层,快捷键为F7。 6.表示当前线程的堆栈,从中可以看出在运行哪些代码,并且整个调用过程,以及代码行号
变量视图 1.为变量名视图,显示当前代码行中所有可以访问的实例变量和局部变量 2.显示所有的变量值 3.可以通过该窗口来改变变量值
断点视图 1.显示所有断点 2. 将当前窗口1中选中的端口失效,再次点击启用。 3.异常断点
表达式视图 表达式视图:表达式视图是Debug过程中较为常用的一个视图,可以对自己的感兴趣的一些变量进行观察,也可以增加一些自己的表达式,也可以查看一行代码的运行结果。 1.表达式 2. 点击此可以新增一个表达式
代码视图 代码视图:用来显示具体的代码。其中绿色部分是指当前将要执行的代码
场景一:小明辛苦了两天终于将自己的负责的任务完成了,第二天转测后,测试找到了小明说,小明的程序有bug,可以是小明经过仔细调试,发现本地没有任何问题,但是测试的环境上确实有问题,所以小明遇到了难题,测试的环境linux,又不能上去linux去debug,小明这个时候想要是Linux也可以debug就好了. 远程debug 远程debug:远程debug顾名思义,能够将远程操作系统上的任何java进行debug,但是有前提是本地需要有同步的代码。 1.远程debug的步骤是在远程操作系统上启动java进程时增加特殊的 -Xdebug -Xnoagent -Djava.compiler=NONE -Xrunjdwp:transport=dt_socket,address=$DEBUG_PORT,server=y,suspend=n 2.在Eclipse中新建一个Remote Java Application 远程debug 1.打开Debug Configurations视图 2.右击Remote Java Application, New 3.选择源码工程 4.输入远程IP和端口,端口即服务 端的$DEBUG_PORT,点击OK。
场 景一:小明写了一个任务执行者,该执行者不间断的执行一些任务,在现网上运行了一段时间后,发现有概率的出现一些故障,发现运行一段时间后,该任务者异常 退出了,退出的因为是空指针,可以小明想要在本地debug,不知道断点打在哪里,该问题是概率事件,不一定会出现,所以小明debug几遍下来后,头晕 眼花,连代码都看不清楚了,小明想要是能有个断点每当出现空指针异常的时候就停下来让他发现问题,那该多好呀。 异常断点 异常断点:在定位问题的过程中,常常会遇到断点无法打到合适的位置,以至于和问题的触发点千差万别,所以这个时候不妨试试异常断点,顾名思义,异常断点是指抛出某种异常后自动挂起的断点。 点击红色部位,增加一个异常断点
输入想要定位的异常类型,例如NullPointerException,这样系统中抛出任何NullPointerException异常后,都会挂起当前线程,给你机会去定位问题。 场景一:小明写了一个巨大的循环,在调测代码时,小明发现每当循环到第100000次的时候,就是出现问题,没有达到自己的预期,于是小明在循环里打了个断点,想看看到底怎么回事,可小明万万没有想到,想要到达100000次循环是多么的困难,小明这个时候已经开始浮想联翩,如果能有这样的断点: If 循环次数== 100000,线程停下来 条件断点 如右图,循环1000次,如果想要在循环到500 次的时候停下来,可以创建一个条件断点,右 击断点悬着Breakpoint Properties。
选中Enable Condition 在空白处,添加你自己的条件,如果条件返回true,线程会被挂起,如果为false,则忽略该异常 Hit Count为该断点经过多少次后,正式挂起线程,如果设置为500,则表达前499次,经过该断点都不会停下,当第500次,该断点会挂起当前线程。
表达式 表达式可以查看一些在当前代码中没有的命令行,方便定位问题。 场景一:小明最近遇到一个难题,在调用一个第三方插件时总是会有问题,小明怀疑是第三方插件的bug,但小明没有找到源码不能进行debug,小明该怎么办呢? Debug定位第三方插件的问题 1.使用反编译工具将代码反编译 2.将反编译后的源码进行过滤 3.修复源码编译错误 4.进行debug Debug一些经验 1.尽量减少debug,少用debug,优秀的程序员总是花80%的时间来思考如何解决问题,20%的时间来动手完成代码,而糟糕的程序员总是用20%的时间去写代码,80%的时间去调试代码,动手之前尽量想好如何去做,并且已经为你自己的思路做了充分的实验。 2.尽可能的提高debug的效率,设置合适的断点,使用快捷键。 3.debug的F6快捷键经常用到,它与金山词霸的快捷键冲突,所以在debug的时候最好将金山词霸关掉。 4.debug的表达式是可执行代码,将会对代码结果产生永久性影响,在调试时注意,经常将不用的表达式清除掉。 原文出自: http://mgoann.iteye.com/blog/1396637
1 import java.io.File; 2 3 public class FileTest { 4 5 public static void main(String[] args) throws Exception { 6 7 System.out.println(Thread.currentThread().getContextClassLoader() 8 .getResource("")); 9 10 System.out.println(FileTest.class.getClassLoader().getResource("")); 11 12 System.out.println(ClassLoader.getSystemResource("")); 13 14 System.out.println(FileTest.class.getResource("")); 15 16 System.out.println(FileTest.class.getResource("/")); 17 18 // Class文件所在路径 19 20 System.out.println(new File("/").getAbsolutePath()); 21 22 System.out.println(System.getProperty("user.dir")); 23 24 System.out.println(System.getProperty("file.encoding")); 25 26 27 } 28 29 }
当我们启动一个tomcat的服务的时候,jar包和claess文件是是以怎么样的顺序被加载进来的? 加载顺序: 1. $java_home/lib 目录下的java核心api 2. $java_home/lib/ext 目录下的java扩展jar包 3. java -classpath/-Djava.class.path所指的目录下的类与jar包 4. $CATALINA_HOME/common目录下按照文件夹的顺序从上往下依次加载 5. $CATALINA_HOME/server目录下按照文件夹的顺序从上往下依次加载 6. $CATALINA_BASE/shared目录下按照文件夹的顺序从上往下依次加载 7. 我们的项目路径/WEB-INF/classes下的class文件 8. 我们的项目路径/WEB-INF/lib下的jar文件 在同一个文件夹下,jar包是按顺序从上到下依次加载 由ClassLoader的双亲委托模式加载机制我们可以知道,假设两个包名和类名完全相同的class文件不再同一个jar包,如果一个class文件已经被加载java虚拟机里了,那么后面的相同的class文件就不会被加载了。 原文 : http://xiaomogui.iteye.com/blog/847686
多站点共享Session常见的作法有: - 使用.net自动的状态服务(Asp.net State Service);
- 使用.net的Session数据库;
- 使用Memcached。
- 使用Cookie方式实现多个站点间的共享(这种方式只限于几个站点都在同一域名的情况下);
这里我们就 演练一下 以数据库的形来存储Session,来实现多站点共享Session。 首先我们 建好一下站点,如下图: Default.aspx 其中 有二个Button ,SetSession 主要是用于给一个 Session 赋值(如:Session["ShareValue"] = “abcd” ) , GetSession 主要就是获得 一个 Session 值。 具体代码如下: 代码部分就这么多就行了… 下面就是要配置一下 Web.config了 , 其实主要就是在 <system.web> 这个节点中 增加 machineKey 及 sessionState 这两个节点, 1.增加machineKey 主要作用是: “按照MSDN的标准说法:“对密钥进行配置,以便将其用于对 Forms 身份验证 Cookie 数据和视图状态数据进行加密和解密,并将其用于对进程外会话状态标识进行验证。”也就是说Asp.Net的很多加密,都是依赖于machineKey里面 的值,例如Forms 身份验证 Cookie、ViewState的加密。默认情况下,Asp.Net的配置是自己动态生成,如果单台服务器当然没问题,但是如果多台服务器负载均 衡,machineKey还采用动态生成的方式,每台服务器上的machinekey值不一致,就导致加密出来的结果也不一致,不能共享验证和 ViewState,所以对于多台服务器负载均衡的情况,一定要在每台站点配置相同的machineKey。“ ,具体可以查一下其它资料。 2.增加 sessionState 主要是让 Session 保存在数据库中。 具体配置如下: <machineKey validationKey="86B6275BA31D3D713E41388692FCA68F7D20269411345AA1C17A7386DACC9C46E7CE5F97F556F3CF0A07159659E2706B77731779D2DA4B53BC47BFFD4FD48A54" decryptionKey="9421E53E196BB56DB11B9C25197A2AD470638EFBC604AC74CD29DBBCF79D6046" validation="SHA1" decryption="AES"/> <sessionState mode="SQLServer" sqlConnectionString="Data Source=PC-07195;Initial Catalog=AWBUISession;Persist Security Info=True;User ID=jins;Password=js@#$1234" allowCustomSqlDatabase="true" cookieless="false" timeout="100"/> 网站部分 这样就好了。。。 下面就是要配置据库了….. 数据库配置: 使用aspnet_regsql.exe工具 ASP.NET 2.0版本后微软提供了aspnet_regsql.exe工具可以方便的配置Session数据库.该工具位于 Web 服务器上的"系统根目录\Microsoft.NET\Framework\版本号"文件夹中. 使用举例:
aspnet_regsql.exe -S . -U sa -P 123456 -ssadd -sstype p -S参数: 表示数据库实例名称. 可以用"."表示本机. -U和-P参数: 表示用户名和密码. -E参数: 可以再-U –P 与 -E中选择一组. –E表示以当前系统用户通过windows身份验证登录数据库, -U -P则是使用SqlServer用户登录数据库. -ssadd / –ssremove 参数: -ssadd表示是添加Session数据库, -ssremove表示移除Session数据库. sstype 参数说明: t | 将会话数据存储到 SQL Server tempdb 数据库中。这是默认设置。如果将会话数据存储到 tempdb 数据库中,则在重新启动 SQL Server 时将丢失会话数据。 | p | 将会话数据存储到 ASPState 数据库中,而不是存储到 tempdb 数据库中。 | c | 将会话数据存储到自定义数据库中。如果指定 c 选项,则还必须使用 -d 选项包括自定义数据库的名称。 | 我的设置是:aspnet_regsql.exe -S . - E -d AWBUISession -ssadd -sstype c 好了。基本的 我们就已经搞定了。。
现在 我们分别把我们刚建的一个网站 部署 到 IIS 中。不过我们既然要负载。至少也的部署两份 我们把 其中一个 服务器中的 defaut.aspx 中 “服务器 1” 改成 “服务器 2” ,这样做的主要目地是 做一下 区别! 具体如下: 两个网站的 URL分别是: server 1:127.0.0.1:8081; server 2:127.0.0.1:8080; OK。下面我们就是 配置 Nignx了。 首先 在 nginx\conf 配置 文件中找到 nginx.conf 这个文件 ,就记事本打开, 做如上的 设置: OK。 nginx 这样配置 就算OK 了。 我们启动一下 nginx .. 在浏览器中 输入我们 在 nginx 中配置的 URL 如:127.0.0.1:8090
我们会看到 服务器 1 已经开始为我们服务了,我们再点一下 “SetSession”来设置一下一个 会话值, 我们会看到 服务器 2 开始 工作。这时我们再点一下 “GetSesion”看一下 刚才在 服务器 1 设置 的会话值,结果如下 : 出现这种情况 ,主要就是在数据库中存储 一个会话时 没有做到 服务器1 和服务2的Session 共享,主要是 在ASPStateTempSessions 这个表中的 一个SessionID,其中的SessionId包括两个部分:网站生成的24位SessionID及8位AppName对于不同的站点,其AppName不同,在能够在不同站点下使24位SessionID相同的情况下,要保证经过组合加上AppName后的SessionID相同,可以通过修改存储过程TempGetAppID,使其得到的SessionID与AppName无关,修改TempGetAppID如下: ALTER PROCEDURE [dbo].[TempGetAppID] @appName tAppName, @appId int OUTPUT AS SET @appName = 'Test' --LOWER(@appName) 修改这里,使多个站点的APPname ,为一个固定值。 SET @appId = NULL SELECT @appId = AppId FROM [AWBUISession].dbo.ASPStateTempApplications WHERE AppName = @appName IF @appId IS NULL BEGIN BEGIN TRAN SELECT @appId = AppId FROM [AWBUISession].dbo.ASPStateTempApplications WITH (TABLOCKX) WHERE AppName = @appName IF @appId IS NULL BEGIN EXEC GetHashCode @appName, @appId OUTPUT INSERT [AWBUISession].dbo.ASPStateTempApplications VALUES (@appId, @appName) IF @@ERROR = 2627 BEGIN DECLARE @dupApp tAppName SELECT @dupApp = RTRIM(AppName) FROM [AWBUISession].dbo.ASPStateTempApplications WHERE AppId = @appId RAISERROR('SQL session state fatal error: hash-code collision between applications ''%s'' and ''%s''. Please rename the 1st application to resolve the problem.', 18, 1, @appName, @dupApp) END END COMMIT END RETURN 0
经过以上修改之后,下面要实现多个站点共用同一个SessionID. 重启一下各站点。再在浏览一下网站点 “SetSession”, 再点:“GetSession” 这样 我们就看到 服务器2 给出了我们 刚才在 服务器 1 中设置 的会话值了。 我们 再点:“GetSession”,可以看到 服务器1 和服务器 2 返回的是相同的结果,达到了 “多站点共享Session” 附加一点: Session 过期删除,主要是 在 SQL server 代理中的作业完成,具体的可以,查一下其它相关资料. 原文出自: http://www.cnblogs.com/red-fox/archive/2012/11/05/2755271.html
摘要: 1.加载JDBC驱动程序 Code highlighting produced by Actipro CodeHighlighter (freeware)http://www.CodeHighlighter.com/--> 1 import java.sql.DriverManager;  ... 阅读全文
Apache Shiro 架构 ApacheShiro的设计目标是使程序的安全变得简单直观而易于实现,shiro的核心设计参照大多数用户对安全的思考模式--如何对某人(或某事)在与程序交互的环境中的进行安全控制。 程序设计通常都以用户为基础,换句话说,你经常以用户可以(或者应该)如何与软件交互为基础来设计用户接口或者服务API,例如,你可能说,“如果当前与我程序交互的用户已经登录了,我将展示一个按钮给他,他可以点击去查看自己的账户住处,如果他们没有登录,我将显示一个注册按钮。” 这个陈述例子指出我们开发程序很大程度上是为了满足用户的需求,即使“用户(User)”是另外一个软件系统而并非一个人,你仍然要写代码对当前与你软件交互的谁(或者什么)的动作进行回应。 shiro从它的设计中表现了这种理念,为了与软件开发者的直觉相配合,Apache Shiro在几乎所有程序中保留了直观和易用的特性。 概览 在概念层,shiro架构包含三个主要的理念:Subject,SecurityManager和Realm。下面的图展示了这些组件如何相互作用,我们将在下面依次对其进行描述。 subject:就像我们在上一章示例中提到的那样,subject本质上是当前运行用户特定的'view',而单词“user”经常暗指一个人,subject可以是一个人,但也可以是第三方服务、守护进程帐户、时钟守护任务或者其它--当前和软件交互的任何事件。 subject实例都和(也需要)一个SecurityManager绑定,当你和一个subject进行交互,这些交互动作被转换成SecurityManager下subject特定的交互动作。 SecurityManager:SecurityManager是Shiro架构的核心,配合内部安全组件共同组成安全伞。然而,一旦一个程序配置好了SecurityManager和它的内部对象,SecurityManager通常独自留下来,程序开发人员几乎花费的所有时间都集中在Subjet API上。 我们将在以后详细讨论SecurityManager,但当你和一个Subject互动时了解它是很重要的。任何Subject的安全操作中SecurityManager是幕后真正的举重者,这在上面的图表中可以反映出来。 Realms:Reamls是Shiro和你的程序安全数据之间的“桥”或者“连接”,它用来实际和安全相关的数据如用户执行身份认证(登录)的帐号和授权(访问控制)进行交互,shiro从一个或多个程序配置的Realm中查找这些东西。 Realm本质上是一个特定的安全DAO:它封装与数据源连接的细节,得到shiro所需的相关的数据。在配置shiro的时候,你必须指定至少一个Realm来实现认证(authentication)和/或授权(authorization)。SecurityManager可以配置多个复杂的Realm,但是至少有一个是需要的。 Shiro提供out-of-the-box Realms来连接安全数据源(或叫地址)如LDAP、JDBC、文件配置如INI和属性文件等,如果已有的Realm不能满足你的需求你也可以开发自己的Realm实现。 和其它内部组件一样,ShiroSecurityManager管理如何使用Realms获取Subject实例所代表的安全和身份信息。 详细架构 下面的图表展示了Shiro的核心架构思想,下面有简单的解释。 Subject (org.apache.shiro.subject.Subject) 正在与软件交互的一个特定的实体“view”(用户、第三方服务、时钟守护任务等)。 SecurityManager(org.apache.shiro.mgt.SecurityManager) 如同上面提到的,SecurityManager 是Shiro的核心,它基本上就是一把“伞”用来协调它管理的组件使之平稳地一起工作,它也管理着Shiro中每一个程序用户的视图,所以它知道每个用户如何执行安全操作。 Authenticator(org.apache.shiro.authc.Authenticator) Authenticator是一个组件,负责执行和反馈用户的认证(登录),如果一个用户尝试登录,Authenticator就开始执行。Authenticator知道如何协调一个或多个保存有相关用户/帐号信息的Realm,从这些Realm中获取这些数据来验证用户的身份以确保用户确实是其表述的那个人。 Authentication Strategy(org.apache.shiro.authc.pam.AuthenticationStrategy) 如果配置了多个Realm,AuthenticationStrategy将会协调Realm确定在一个身份验证成功或失败的条件(例如,如果在一个方面验证成功了但其他失败了,这次尝试是成功的吗?是不是需要所有方面的验证都成功?还是只需要第一个?) Authorizer(org.apache.shiro.authz.Authorizer) Authorizer是负责程序中用户访问控制的组件,它是最终判断一个用户是否允许做某件事的途径,像Authenticator一样,Authorizer也知道如何通过协调多种后台数据源来访问角色和权限信息,Authorizer利用这些信息来准确判断一个用户是否可以执行给定的动作。 SessionManager(org.apache.shiro.session.mgt.SessionManager) SessionManager知道如何创建并管理用户Session生命周期而在所有环境中为用户提供一个强有力的Session体验。这在安全框架领域是独一无二--Shiro具备管理在任何环境下管理用户Session的能力,即使没有Web/Servlet或者EJB容器。默认情况下,Shiro将使用现有的session(如Servlet Container),但如果环境中没有,比如在一个独立的程序或非web环境中,它将使用它自己建立的session提供相同的作用,sessionDAO用来使用任何数据源使session持久化。 SessionDAO(org.apache.shiro.session.mgt.eis.SessionDAO) SessionDAO代表SessionManager执行Session持久(CRUD)动作,它允许任何存储的数据挂接到session管理基础上。 CacheManager(org.apache.shiro.cache.CacheManager) CacheManager创建并管理其它shiro组件的catch实例生命周期,因为shiro要访问许多后端数据源来实现认证、授权和session管理,caching已经成为提升性能的一流的框架特征,任何一个现在开源的和/或企业级的caching产品都可以插入到shiro中实现一个快速而有效的用户体验。 Cryptography (org.apache.shiro.crypto.*) Cryptography在安全框架中是一个自然的附加产物,shiro的crypto包包含了易用且易懂的加密方式,Hashes(亦即digests)和不同的编码实现。该包里所有的类都亦于理解和使用,曾经用过Java自身的加密支持的人都知道那是一个具有挑战性的工作,而shiro的加密API简化了java复杂的工作方式,将加密变得易用。 Realms (org.apache.shiro.realm.Realm) 如同上面提到的,Realm是shiro和你的应用程序安全数据之间的“桥”或“连接”,当实际要与安全相关的数据进行交互如用户执行身份认证(登录)和授权验证(访问控制)时,shiro从程序配置的一个或多个Realm中查找这些数据,你需要配置多少个Realm便可配置多少个Realm(通常一个数据源一个),shiro将会在认证和授权中协调它们。 SecurityManager 因为shiro API鼓励以Subject为中心的开发方式,大部分开发人员将很少会和SecurityManager直接交互(尽管框架开发人员也许发现它非常有用),尽管如此,知道SecurityManager如何工作,特别是当在一个程序中进行配置的时候,是非常重要的。 设计 如前所述,程序中SecurityManager执行操作并且管理所有程序用户的状态,在shiro基础的SecurityManager实现中,包含以下内容: 认证(Authentication) 授权(Authorization) 会话管理(Session Management) 缓存管理(Cache Management) Realm协调(Realm coordination) 事件传导(Event propagation ) "RememberMe" 服务("Remember Me" Services) 建立Subject(Subject creation) 退出登录(Logout) 及其它。 但这些功能都在一个单独的组件中管理,并且,当所有功能集中在一个类中实现是灵活和可定制是非常困难的。 为了实现配置的简单、灵活、可插拔,shiro在设计时实现了高模块化--尽管模块化,SecurityManager(包括它的继承类)并没有做到,相反地,SecurityManager实现更像一个轻量级的‘容器(container)’,代表几乎所有嵌套/封装组件的行为,这种‘封装(wrapper)’设计在上面的架构图表中已有反映。 当组件执行逻辑的时候,SecurityManager知道如何以及何时去协调组件做出正确的动作。 SecurityManager和JavaBean兼容,这允许你(或者配置途径)通过标准的JavaBean访问/设置方法(get*/set*)很容易地定制插件,这意味着shiro模块可以根据用户行为转化成简易的配置。 简易的配置 因为适合JavaBean,任何支持Javabean配置的组件都有非常简单的途径配置SecurityManager,如Spring、Guice、JBoss,等等。 我们将在下一节讨论配置(Configuration ) 为文档加把手 我们希望这篇文档可以帮助你使用Apache Shiro进行工作,社区一直在不断地完善和扩展文档,如果你希望帮助shiro项目,请在你认为需要的地方考虑更正、扩展或添加文档,你提供的任何点滴帮助都将扩充社区并且提升Shiro。 提供你的文档的最简单的途径是将它发送到用户论坛(http://shiro-user.582556.n2.nabble.com/)或邮件列表(http://shiro.apache.org/mailing-lists.html) 原文地址:http://shiro.apache.org/architecture.html
摘要: 1.上下文Context、面向切面编程AOP模型分析 在本人的“.NET面向上下文、AOP架构模式(概述)” 一文中,我们大概了解了上下文如何辅助对象在运行时的管理。在很多时候我们急需在运行时能把对象控制在一定的逻辑范围内,在必要的时候能让他们体现出集中 化的概念,如人群、车辆、动物等等。而Context与AOP有着密切的联系,Context表示逻辑抽象的范围而AOP描述了... 阅读全文
摘要: 前段时间一直在学习和研究.NET事务处理,慢慢的我发现可以使用事务处理来实现一种可逆的系统框架。这种框架在一些IT社区似乎还没有见过,但是在我们日常开发中确实有这个需求。所以我花了点时间深入的研究了一下事务的原理和使用,实现了以事务为纽带,以资源为操作对象的可逆框架。 这里我假设您对事务有了整体的认识,也对自定义事务管理器有过了解。[王清培版权所有,转载请给出署名] (可以参考本人的:.NET简谈... 阅读全文
1. 上下文概述 上下文:其实就是一个逻辑上的业务、功能区域。在这个逻辑区域里可以有效的进行管理,算是一种制度的约束,也可以理解为某种范围类的数据共享。 其实在很多应用框架中到处可以看见上下文的概念,包括.NET本身的设计就建立在这种思想上的。实例化的对象默认存在于系统中的默认上下文中,我们可以构建自己的上下文将对象在运行时进行合理的管理。 在ASP.NET框架中比较经典的就是HttpContext上下文对象。所有的运行时对象都会逻辑归属到HttpContext上下文中来,如:我们可以使用Request、Response等对象访问HTTP处理的生命周期数据。 在Remoting中跨AppDomin访问也是建立在上下文基础上的,请求的消息通过隧道后序列化到达调用发。王清培版权所有,转载请给出署名 在这些强大的应用框架背后总有着让人难以琢磨的设计秘方,诸多的设计原则、设计模式、丰富的实践经验都将是框架稳定运行的基石。Context算是一个比较完美的逻辑范围设计模式。[王清培版权所有,转载请给出署名] 那么就让我们来领略一下上下文的奥秘吧! 2. 上下文的一般应用 上下文的设计思想绝对的美妙,很多地方一旦进行上下文抽象就能解决很多问题。比如在Remoting中我们可以动态的在上下文中加入很 多扩展对上下文中的所有对象进行强制管理,比如:调用某一个方法我们需要进行安全检查,我们可以编写一个满足自己当前项目需求的安全认证插件动态的注入到 上下文管理器区域中,在这个地方就体现出上下文的设计优势。 在Web编程中,由于它有着与Winfrom编程很大的差异性,需要将同一组对象同时服务于N个客户端进行使用,而在Winfrom中基本上都是属于单线程的,当然可以手动的开启多线程并行操作。对于ASP.NET每当有新的请求处理时,框架会自动开启新的线程去处理当前的调用,然后这个时候就是需要一个相对于之前操作的独立上下文数据环境,而不是在同一个服务器上的所有线程都是共享的。王清培版权所有,转载请给出署名 那么我们就需要将当前的HTTP处理的相关数据纳入到一个逻辑的上下文进行管理和数据共享。 这么多的优势存在,看来我们是有必要尝试一下这中设计模式了。那么就目前系统开发框架而言我们的上下文能用在哪里呢?我想当务之急就是将分层架构中的所有单条线上的对象进行上下文管理。[王清培版权所有,转载请给出署名] 典型的三层架构: 在一般的三层架构开发过程中我们的调用关系基本都是这样的,利用上下文设计模式我们可以将本来鼓励的对象进行合理的管理。上图中User对象线将是属于User上下文的,Order对象线将是属于Order上下文的。大家互不干扰,可以在这个逻辑上下文中共享数据、设置调用安全策略、设计日志记录方式、甚至可以计算每个方法的性能。 BLL的调用代码: - View Code
- /***
- * author:深度训练
- * blog:http://wangqingpei557.blog.51cto.com/
- * **/
- using System;
- using System.Collections.Generic;
- using System.Text;
- using System.Reflection;
-
- namespace ConsoleApplication1.BLL
- {
- [ContextModule.ContextEveningBound(IsEvening = true)]
- public class BLL_Order : ContextModule.ContextModuleBaseObject<BLL_Order>
- {
- DAL.DAL_Order dal_order = new DAL.DAL_Order();
-
- [ContextModule.ContextExceptionHandler(OperationSort = 1)]
- public Model.Model_Order InsertOrderSingle(Model.Model_Order ordermodel)
- {
- return ContextModule.ContextAction.PostMethod<DAL.DAL_Order, Model.Model_Order>(
- dal_order, dal_order.GetMethodInfo("InsertOrderSingle"), ordermodel);
- }
- [ContextModule.ContextExceptionHandler(OperationSort = 1)]
- public void SendOrder(Model.Model_Order ordermodel)
- {
- ContextModule.ContextAction.PostMethod<DAL.DAL_Order, object>(
- dal_order, dal_order.GetMethodInfo("SendOrder"), ordermodel);
- }
- }
- }
DAL的执行代码: - View Code
- /***
- * author:深度训练
- * blog:http://wangqingpei557.blog.51cto.com/
- * **/
- using System;
- using System.Collections.Generic;
- using System.Text;
-
- namespace ConsoleApplication1.DAL
- {
- [ContextModule.ContextEveningBound(IsEvening = true)]
- public class DAL_Order : ContextModule.ContextModuleBaseObject<DAL_Order>
- {
- [ContextModule.ContextLogHandler(OperationSort = 1)]
- [ContextModule.ContextSecurityHanlder(OperationSort = 2)]
- public Model.Model_Order InsertOrderSingle(Model.Model_Order ordermodel)
- {
- return new Model.Model_Order() { OrderGuid = Guid.NewGuid(), OrderTime = DateTime.Now };
- }
- [ContextModule.ContextLogHandler(OperationSort = 1)]
- public void SendOrder(Model.Model_Order ordermodel)
- {
- Console.WriteLine("订单发送成功!");
- }
- }
- }
上述代码是我模拟一个上下文的执行过程。 3. 上下文共享区域 在每个独立的上下文环境中应该有一片共享的数据存储区域,以备多个上下文对象访问。这种方便性多半存在于项目比较紧张的修改需求的时候或者加新业务的时候扩展方法用的。 BLL调用代码: - View Code
- [ContextModule.ContextExceptionHandler(OperationSort = 1)]
- public void UpdateOrderSingle()
- {
- Model.Model_Order ordermodel = new Model.Model_Order() { OrderGuid = Guid.NewGuid(), OrderTime = DateTime.Now };
- //放入上下文共享对象池
- ContextModule.ContextRuntime.CurrentContextRuntime.SetValue("updateorder", ordermodel);
- ContextModule.ContextAction.PostMethod<DAL.DAL_Order, object>(
- dal_order, dal_order.GetMethodInfo("UpdateOrderSingle"), null);
- }
DAL执行代码:DAL执行代码:DAL执行代码:DAL执行代码: - [ContextModule.ContextLogHandler(OperationSort = 1)]
- public void UpdateOrderSingle()
- {
- Model.Model_Order ordermodel =
- ContextModule.ContextRuntime.CurrentContextRuntime.GetValue("updateorder") as Model.Model_Order;
- }
4. 上下文运行时环境 对于上下文运行时环境的构建需要考虑到运行时是共享的上下文对象。对于纳入上下文管理的所有对象都需要共享或者说是受控于上下文运行时。 上下文构建: - /***
- * author:深度训练
- * blog:http://wangqingpei557.blog.51cto.com/
- * **/
- using System;
- using System.Collections.Generic;
- using System.Text;
-
- namespace ContextModule
- {
- /// <summary>
- /// 上下文运行时环境。
- /// 上下文逻辑运行时环境,环境中的功能都是可以通过附加进来的。
- /// </summary>
- public class ContextRuntime : IDisposable
- {
- #region IDisposable成员
- void IDisposable.Dispose()
- {
- _currentContextRuntime = null;
- }
- #endregion
-
- protected ContextRuntime() { }
- private DateTime _initTime = DateTime.Now;
- /// <summary>
- /// 获取运行时创建上下文的时间
- /// </summary>
- public virtual DateTime InitTime { get { return _initTime; } }
- private Dictionary<object, object> _runTimeResource = new Dictionary<object, object>();
- private ContextFilterHandlerMap _filterMap = new ContextFilterHandlerMap();
- /// <summary>
- /// 获取上下文中的方法、类过滤器映射表
- /// </summary>
- public ContextFilterHandlerMap FilterMap { get { return _filterMap; } }
- private Guid _initPrimaryKey = Guid.NewGuid();
- /// <summary>
- /// 获取运行时创建上下文的唯一标识
- /// </summary>
- public virtual Guid InitPrimaryKey { get { return _initPrimaryKey; } }
- /// <summary>
- /// 获取上下文共享区域中的数据
- /// </summary>
- /// <param name="key">数据Key</param>
- /// <returns>object数据对象</returns>
- public virtual object GetValue(object key)
- {
- return _runTimeResource[key];
- }
- /// <summary>
- /// 设置上下文共享区域中的数据
- /// </summary>
- /// <param name="key">数据Key</param>
- /// <param name="value">要设置的数据对象</param>
- public virtual void SetValue(object key, object value)
- {
- _runTimeResource[key] = value;
- }
-
- [ThreadStatic]
- private static ContextRuntime _currentContextRuntime;
- /// <summary>
- /// 获取当前上下文运行时对象.
- /// </summary>
- public static ContextRuntime CurrentContextRuntime { get { return _currentContextRuntime; } }
- /// <summary>
- /// 开始运行时上下文
- /// </summary>
- /// <returns>ContextRuntime</returns>
- public static ContextRuntime BeginContextRuntime()
- {
- //可以通过配置文件配置上下文运行时环境的参数。这里只是实现简单的模拟。
- _currentContextRuntime = new ContextRuntime();
- return _currentContextRuntime;
- }
- }
- }
对于上下文的入口构建: - //开启上下文
- using (ContextModule.ContextRuntime.BeginContextRuntime())
- {
-
- }
通过Using的方式我们开始上下文生命周期。 5. 上下文活动对象 上下文对象的绑定需要延后,不能在对象的构建时就创建上下文。 使用后期绑定动态的切入到执行的上下文中。 调用代码,上下文入口: - /***
- * author:深度训练
- * blog:http://wangqingpei557.blog.51cto.com/
- * **/
- using System;
- using System.Collections.Generic;
- using System.Text;
- using System.Data;
- using ConsoleApplication1.BLL;
- using ConsoleApplication1.Model;
-
- namespace ConsoleApplication1
- {
- public class Program
- {
- public static void Main(string[] args)
- {
- BLL.BLL_Order order = new BLL.BLL_Order();
- using (ContextModule.ContextRuntime.BeginContextRuntime())
- {
- Model.Model_Order ordermodel = new Model_Order() { OrderGuid = Guid.NewGuid(), OrderTime = DateTime.Now };
- Model.Model_Order resultmodel = ContextModule.ContextAction.PostMethod<BLL.BLL_Order, Model.Model_Order>(order, order.GetMethodInfo("InsertOrderSingle"), ordermodel);
- ContextModule.ContextAction.PostMethod<BLL.BLL_Order, object>(order, order.GetMethodInfo("SendOrder"), ordermodel);
- }
-
- }
- }
- }
6. 上下文在分层架构中的运用 有了上下文的核心原型之后我们可以扩展到分层架构中来,对于分层架构的使用其实很有必要,一般的大型业务系统都是混合的使用模式,可能有C/S、B/S、Mobile终端等等。 对于加入Service层之后BLL、DAL将位于服务之后,对于来自客户端的调用需要经过一些列的身份验证及权限授予。有了WCF之后面向SOA的架构开发变的相对容易点,对安全、性能、负载等等都很完美,所以大部分的情况下我们很少需要控制BLL、DAL的执行运行。 那么没有使用WCF构建分布式的系统时或者是没有分布式的需求就是直接的调用,如WEB的一般开发,从UI到BLL到DAL。或者是普通的Winfrom的项目、控制台项目属于内网的使用,可能就需要控制到代码的执行。 下面我通过演示一个具体的实例来看看到底效果如何。 我以控制台的程序作为演示项目类型,也使用简单的三层架构。 这个再简单不过了吧,为了演示越简单越好,关键是突出重点。 需求: 在DAL对象里面加入一个插入Order实体对象的方法: - /***
- * author:深度训练
- * blog:http://wangqingpei557.blog.51cto.com/
- * **/
- using System;
- using System.Collections.Generic;
- using System.Text;
-
- namespace ConsoleApplication1.DAL
- {
- [ContextModule.ContextEveningBound(IsEvening = true)]
- public class DAL_Order : ContextModule.ContextModuleBaseObject<DAL_Order>
- {
- [ContextModule.ContextLogHandler(OperationSort = 1)]
- [ContextModule.ContextSecurityHanlder(OperationSort = 2)]
- public Model.Model_Order InsertOrderSingle(Model.Model_Order ordermodel)
- {
- return new Model.Model_Order() { OrderGuid = Guid.NewGuid(), OrderTime = DateTime.Now };
- }
- }
- }
在这个类的上面有一个特性ContextEveningBound,该是用来表示当前对象属于后期绑定到上下文的对象。同时该类也继承自一个ContextModuleBaseObject<DAL_Order>泛型类,主要作用是将对象强制的绑定到上下文进行管理。 在方法InsertOrderSingle上面有两个特性,ContextLogHandler是用来记录方法的执行日志,ContextSecurityHanlder是用来在方法执行的过程中强制要求管理员认证。 BLL对象代码: - /***
- * author:深度训练
- * blog:http://wangqingpei557.blog.51cto.com/
- * **/
- using System;
- using System.Collections.Generic;
- using System.Text;
- using System.Reflection;
-
- namespace ConsoleApplication1.BLL
- {
- [ContextModule.ContextEveningBound(IsEvening = true)]
- public class BLL_Order : ContextModule.ContextModuleBaseObject<BLL_Order>
- {
- DAL.DAL_Order dal_order = new DAL.DAL_Order();
-
- [ContextModule.ContextExceptionHandler(OperationSort = 1)]
- public Model.Model_Order InsertOrderSingle(Model.Model_Order ordermodel)
- {
- return ContextModule.ContextAction.PostMethod<DAL.DAL_Order, Model.Model_Order>(
- dal_order, dal_order.GetMethodInfo("InsertOrderSingle"), ordermodel);
- }
- }
- }
在BLL对象里面有一个调用DAL对象方法的实例对象,为了演示简单这里没有加入层的依赖注入设计方案,通过直接调 用方式。在BLL方法体中有一个专门用来在上下文中调用方法的接口,这是约束目的是为了能让框架切入到方法的执行之前先执行。具体的设计原理我将在下一篇 文章中详细讲解。 在方法的上面有一个ContextExceptionHandler特性,目的是安全的调用DAL对象的方法,在有异常的情况下能通过上下文的方式人性化的提示错误信息。这样我们就不需要频繁的编写捕获异常的代码,看起来也不爽,我们要的是代码的整洁、美丽。 UI调用: - /***
- * author:深度训练
- * blog:http://wangqingpei557.blog.51cto.com/
- * **/
- using System;
- using System.Collections.Generic;
- using System.Text;
- using System.Data;
- using ConsoleApplication1.BLL;
- using ConsoleApplication1.Model;
-
- namespace ConsoleApplication1
- {
- public class Program
- {
- public static void Main(string[] args)
- {
- BLL.BLL_Order order = new BLL.BLL_Order();
- //开启上下文
- using (ContextModule.ContextRuntime.BeginContextRuntime())
- {
- Model.Model_Order ordermodel = new Model_Order() { OrderGuid = Guid.NewGuid(), OrderTime = DateTime.Now };
-
- Model.Model_Order resultmodel =
- ContextModule.ContextAction.PostMethod<BLL.BLL_Order, Model.Model_Order>(
- order, order.GetMethodInfo("InsertOrderSingle"), ordermodel);
- }
-
- }
- }
- }
执行效果: 会先执行日志的记录,然后要求我们输入用户凭证才能继续执行下面的方法。 我输入YES才能继续执行插入的方法。我们可以通过很简单的实现上下文的管理接口,对方法进行控制。 总结:该篇文章只是介绍上下文的作用、原理、优势。下篇文章:“.NET 面向上下文架构模式(实现)”将详细的介绍上下文框架如何开发。[王清培版权所有,转载请给出署名]
有关SSL的原理和介绍在网上已经有不少,对于Java下使用keytool生成证书,配置SSL通信的教程也非常多。但如果我们不能够亲自动 手做一个SSL Sever和SSL Client,可能就永远也不能深入地理解Java环境下,SSL的通信是如何实现的。对SSL中的各种概念的认识也可能会仅限于可以使用的程度。本文通 过构造一个简单的SSL Server和SSL Client来讲解Java环境下SSL的通信原理。 首先我们先回顾一下常规的Java Socket编程。在Java下写一个Socket服务器和客户端的例子还是比较简单的。以下是服务端的代码: Java代码 1.package org.bluedash.tryssl; 2. 3.import java.io.BufferedReader; 4.import java.io.IOException; 5.import java.io.InputStbreamReader; 6.import java.io.PrintWriter; 7.import java.net.ServerSocket; 8.import java.net.Socket; 9. 10.public class Server extends Thread { 11. private Socket socket; 12. 13. public Server(Socket socket) { 14. this.socket = socket; 15. } 16. 17. public void run() { 18. try { 19. BufferedReader reader = new BufferedReader(new InputStreamReader(socket.getInputStream())); 20. PrintWriter writer = new PrintWriter(socket.getOutputStream()); 21. 22. String data = reader.readLine(); 23. writer.println(data); 24. writer.close(); 25. socket.close(); 26. } catch (IOException e) { 27. 28. } 29. } 30. 31. public static void main(String[] args) throws Exception { 32. while (true) { 33. new Server((new ServerSocket(8080)).accept()).start(); 34. } 35. } 36.} package org.bluedash.tryssl; import java.io.BufferedReader; import java.io.IOException; import java.io.InputStreamReader; import java.io.PrintWriter; import java.net.ServerSocket; import java.net.Socket; public class Server extends Thread { private Socket socket; public Server(Socket socket) { this.socket = socket; } public void run() { try { BufferedReader reader = new BufferedReader(new InputStreamReader(socket.getInputStream())); PrintWriter writer = new PrintWriter(socket.getOutputStream()); String data = reader.readLine(); writer.println(data); writer.close(); socket.close(); } catch (IOException e) { } } public static void main(String[] args) throws Exception { while (true) { new Server((new ServerSocket(8080)).accept()).start(); } } } 服务端很简单:侦听8080端口,并把客户端发来的字符串返回去。下面是客户端的代码: Java代码 1.package org.bluedash.tryssl; 2. 3.import java.io.BufferedReader; 4.import java.io.InputStreamReader; 5.import java.io.PrintWriter; 6.import java.net.Socket; 7. 8.public class Client { 9. 10. public static void main(String[] args) throws Exception { 11. 12. Socket s = new Socket("localhost", 8080); 13. 14. PrintWriter writer = new PrintWriter(s.getOutputStream()); 15. BufferedReader reader = new BufferedReader(new InputStreamReader(s.getInputStream())); 16. writer.println("hello"); 17. writer.flush(); 18. System.out.println(reader.readLine()); 19. s.close(); 20. } 21. 22.} package org.bluedash.tryssl; import java.io.BufferedReader; import java.io.InputStreamReader; import java.io.PrintWriter; import java.net.Socket; public class Client { public static void main(String[] args) throws Exception { Socket s = new Socket("localhost", 8080); PrintWriter writer = new PrintWriter(s.getOutputStream()); BufferedReader reader = new BufferedReader(new InputStreamReader(s.getInputStream())); writer.println("hello"); writer.flush(); System.out.println(reader.readLine()); s.close(); } } 客户端也非常简单:向服务端发起请求,发送一个"hello"字串,然后获得服务端的返回。把服务端运行起来后,执行客户端,我们将得到"hello"的返回。 就是这样一套简单的网络通信的代码,我们来把它改造成使用SSL通信。在SSL通信协议中,我们都知道首先服务端必须有一个数字证书,当客户端连接 到服务端时,会得到这个证书,然后客户端会判断这个证书是否是可信的,如果是,则交换信道加密密钥,进行通信。如果不信任这个证书,则连接失败。 因此,我们首先要为服务端生成一个数字证书。Java环境下,数字证书是用keytool生成的,这些证书被存储在store的概念中,就是证书仓库。我们来调用keytool命令为服务端生成数字证书和保存它使用的证书仓库: Bash代码 1.keytool -genkey -v -alias bluedash-ssl-demo-server -keyalg RSA -keystore ./server_ks -dname "CN=localhost,OU=cn,O=cn,L=cn,ST=cn,C=cn" -storepass server -keypass 123123 keytool -genkey -v -alias bluedash-ssl-demo-server -keyalg RSA -keystore ./server_ks -dname "CN=localhost,OU=cn,O=cn,L=cn,ST=cn,C=cn" -storepass server -keypass 123123 这样,我们就将服务端证书bluedash-ssl-demo-server保存在了server_ksy这个store文件当中。有关keytool的用法在本文中就不再多赘述。执行上面的命令得到如下结果: Bash代码 1.Generating 1,024 bit RSA key pair and self-signed certificate (SHA1withRSA) with a validity of 90 days 2. for: CN=localhost, OU=cn, O=cn, L=cn, ST=cn, C=cn 3.[Storing ./server_ks] Generating 1,024 bit RSA key pair and self-signed certificate (SHA1withRSA) with a validity of 90 days for: CN=localhost, OU=cn, O=cn, L=cn, ST=cn, C=cn [Storing ./server_ks] 然后,改造我们的服务端代码,让服务端使用这个证书,并提供SSL通信: Java代码 1.package org.bluedash.tryssl; 2. 3.import java.io.BufferedReader; 4.import java.io.FileInputStream; 5.import java.io.IOException; 6.import java.io.InputStreamReader; 7.import java.io.PrintWriter; 8.import java.net.ServerSocket; 9.import java.net.Socket; 10.import java.security.KeyStore; 11. 12.import javax.net.ServerSocketFactory; 13.import javax.net.ssl.KeyManagerFactory; 14.import javax.net.ssl.SSLContext; 15.import javax.net.ssl.SSLServerSocket; 16. 17.public class SSLServer extends Thread { 18. private Socket socket; 19. 20. public SSLServer(Socket socket) { 21. this.socket = socket; 22. } 23. 24. public void run() { 25. try { 26. BufferedReader reader = new BufferedReader(new InputStreamReader(socket.getInputStream())); 27. PrintWriter writer = new PrintWriter(socket.getOutputStream()); 28. 29. String data = reader.readLine(); 30. writer.println(data); 31. writer.close(); 32. socket.close(); 33. } catch (IOException e) { 34. 35. } 36. } 37. 38. private static String SERVER_KEY_STORE = "/Users/liweinan/projs/ssl/src/main/resources/META-INF/server_ks"; 39. private static String SERVER_KEY_STORE_PASSWORD = "123123"; 40. 41. public static void main(String[] args) throws Exception { 42. System.setProperty("javax.net.ssl.trustStore", SERVER_KEY_STORE); 43. SSLContext context = SSLContext.getInstance("TLS"); 44. 45. KeyStore ks = KeyStore.getInstance("jceks"); 46. ks.load(new FileInputStream(SERVER_KEY_STORE), null); 47. KeyManagerFactory kf = KeyManagerFactory.getInstance("SunX509"); 48. kf.init(ks, SERVER_KEY_STORE_PASSWORD.toCharArray()); 49. 50. context.init(kf.getKeyManagers(), null, null); 51. 52. ServerSocketFactory factory = context.getServerSocketFactory(); 53. ServerSocket _socket = factory.createServerSocket(8443); 54. ((SSLServerSocket) _socket).setNeedClientAuth(false); 55. 56. while (true) { 57. new SSLServer(_socket.accept()).start(); 58. } 59. } 60.} package org.bluedash.tryssl; import java.io.BufferedReader; import java.io.FileInputStream; import java.io.IOException; import java.io.InputStreamReader; import java.io.PrintWriter; import java.net.ServerSocket; import java.net.Socket; import java.security.KeyStore; import javax.net.ServerSocketFactory; import javax.net.ssl.KeyManagerFactory; import javax.net.ssl.SSLContext; import javax.net.ssl.SSLServerSocket; public class SSLServer extends Thread { private Socket socket; public SSLServer(Socket socket) { this.socket = socket; } public void run() { try { BufferedReader reader = new BufferedReader(new InputStreamReader(socket.getInputStream())); PrintWriter writer = new PrintWriter(socket.getOutputStream()); String data = reader.readLine(); writer.println(data); writer.close(); socket.close(); } catch (IOException e) { } } private static String SERVER_KEY_STORE = "/Users/liweinan/projs/ssl/src/main/resources/META-INF/server_ks"; private static String SERVER_KEY_STORE_PASSWORD = "123123"; public static void main(String[] args) throws Exception { System.setProperty("javax.net.ssl.trustStore", SERVER_KEY_STORE); SSLContext context = SSLContext.getInstance("TLS"); KeyStore ks = KeyStore.getInstance("jceks"); ks.load(new FileInputStream(SERVER_KEY_STORE), null); KeyManagerFactory kf = KeyManagerFactory.getInstance("SunX509"); kf.init(ks, SERVER_KEY_STORE_PASSWORD.toCharArray()); context.init(kf.getKeyManagers(), null, null); ServerSocketFactory factory = context.getServerSocketFactory(); ServerSocket _socket = factory.createServerSocket(8443); ((SSLServerSocket) _socket).setNeedClientAuth(false); while (true) { new SSLServer(_socket.accept()).start(); } } } 可以看到,服务端的Socket准备设置工作大大增加了,增加的代码的作用主要是将证书导入并进行使用。此外,所使用的Socket变成了 SSLServerSocket,另外端口改到了8443(这个不是强制的,仅仅是为了遵守习惯)。另外,最重要的一点,服务端证书里面的CN一定和服务 端的域名统一,我们的证书服务的域名是localhost,那么我们的客户端在连接服务端时一定也要用localhost来连接,否则根据SSL协议标 准,域名与证书的CN不匹配,说明这个证书是不安全的,通信将无法正常运行。 有了服务端,我们原来的客户端就不能使用了,必须要走SSL协议。由于服务端的证书是我们自己生成的,没有任何受信任机构的签名,所以客户端是无法 验证服务端证书的有效性的,通信必然会失败。所以我们需要为客户端创建一个保存所有信任证书的仓库,然后把服务端证书导进这个仓库。这样,当客户端连接服 务端时,会发现服务端的证书在自己的信任列表中,就可以正常通信了。 因此现在我们要做的是生成一个客户端的证书仓库,因为keytool不能仅生成一个空白仓库,所以和服务端一样,我们还是生成一个证书加一个仓库(客户端证书加仓库): Bash代码 1.keytool -genkey -v -alias bluedash-ssl-demo-client -keyalg RSA -keystore ./client_ks -dname "CN=localhost,OU=cn,O=cn,L=cn,ST=cn,C=cn" -storepass client -keypass 456456 keytool -genkey -v -alias bluedash-ssl-demo-client -keyalg RSA -keystore ./client_ks -dname "CN=localhost,OU=cn,O=cn,L=cn,ST=cn,C=cn" -storepass client -keypass 456456 结果如下: Bash代码 1.Generating 1,024 bit RSA key pair and self-signed certificate (SHA1withRSA) with a validity of 90 days 2. for: CN=localhost, OU=cn, O=cn, L=cn, ST=cn, C=cn 3.[Storing ./client_ks] Generating 1,024 bit RSA key pair and self-signed certificate (SHA1withRSA) with a validity of 90 days for: CN=localhost, OU=cn, O=cn, L=cn, ST=cn, C=cn [Storing ./client_ks] 接下来,我们要把服务端的证书导出来,并导入到客户端的仓库。第一步是导出服务端的证书: Bash代码 1.keytool -export -alias bluedash-ssl-demo-server -keystore ./server_ks -file server_key.cer keytool -export -alias bluedash-ssl-demo-server -keystore ./server_ks -file server_key.cer 执行结果如下: Bash代码 1.Enter keystore password: server 2.Certificate stored in file <server_key.cer> Enter keystore password: server Certificate stored in file <server_key.cer> 然后是把导出的证书导入到客户端证书仓库: Bash代码 1.keytool -import -trustcacerts -alias bluedash-ssl-demo-server -file ./server_key.cer -keystore ./client_ks keytool -import -trustcacerts -alias bluedash-ssl-demo-server -file ./server_key.cer -keystore ./client_ks 结果如下: Bash代码 1.Enter keystore password: client 2.Owner: CN=localhost, OU=cn, O=cn, L=cn, ST=cn, C=cn 3.Issuer: CN=localhost, OU=cn, O=cn, L=cn, ST=cn, C=cn 4.Serial number: 4c57c7de 5.Valid from: Tue Aug 03 15:40:14 CST 2010 until: Mon Nov 01 15:40:14 CST 2010 6.Certificate fingerprints: 7. MD5: FC:D4:8B:36:3F:1B:30:EA:6D:63:55:4F:C7:68:3B:0C 8. SHA1: E1:54:2F:7C:1A:50:F5:74:AA:63:1E:F9:CC:B1:1C:73:AA:34:8A:C4 9. Signature algorithm name: SHA1withRSA 10. Version: 3 11.Trust this certificate? [no]: yes 12.Certificate was added to keystore Enter keystore password: client Owner: CN=localhost, OU=cn, O=cn, L=cn, ST=cn, C=cn Issuer: CN=localhost, OU=cn, O=cn, L=cn, ST=cn, C=cn Serial number: 4c57c7de Valid from: Tue Aug 03 15:40:14 CST 2010 until: Mon Nov 01 15:40:14 CST 2010 Certificate fingerprints: MD5: FC:D4:8B:36:3F:1B:30:EA:6D:63:55:4F:C7:68:3B:0C SHA1: E1:54:2F:7C:1A:50:F5:74:AA:63:1E:F9:CC:B1:1C:73:AA:34:8A:C4 Signature algorithm name: SHA1withRSA Version: 3 Trust this certificate? [no]: yes Certificate was added to keystore 好,准备工作做完了,我们来撰写客户端的代码: Java代码 1.package org.bluedash.tryssl; 2. 3.import java.io.BufferedReader; 4.import java.io.InputStreamReader; 5.import java.io.PrintWriter; 6.import java.net.Socket; 7. 8.import javax.net.SocketFactory; 9.import javax.net.ssl.SSLSocketFactory; 10. 11.public class SSLClient { 12. 13. private static String CLIENT_KEY_STORE = "/Users/liweinan/projs/ssl/src/main/resources/META-INF/client_ks"; 14. 15. public static void main(String[] args) throws Exception { 16. // Set the key store to use for validating the server cert. 17. System.setProperty("javax.net.ssl.trustStore", CLIENT_KEY_STORE); 18. 19. System.setProperty("javax.net.debug", "ssl,handshake"); 20. 21. SSLClient client = new SSLClient(); 22. Socket s = client.clientWithoutCert(); 23. 24. PrintWriter writer = new PrintWriter(s.getOutputStream()); 25. BufferedReader reader = new BufferedReader(new InputStreamReader(s 26. .getInputStream())); 27. writer.println("hello"); 28. writer.flush(); 29. System.out.println(reader.readLine()); 30. s.close(); 31. } 32. 33. private Socket clientWithoutCert() throws Exception { 34. SocketFactory sf = SSLSocketFactory.getDefault(); 35. Socket s = sf.createSocket("localhost", 8443); 36. return s; 37. } 38.} package org.bluedash.tryssl; import java.io.BufferedReader; import java.io.InputStreamReader; import java.io.PrintWriter; import java.net.Socket; import javax.net.SocketFactory; import javax.net.ssl.SSLSocketFactory; public class SSLClient { private static String CLIENT_KEY_STORE = "/Users/liweinan/projs/ssl/src/main/resources/META-INF/client_ks"; public static void main(String[] args) throws Exception { // Set the key store to use for validating the server cert. System.setProperty("javax.net.ssl.trustStore", CLIENT_KEY_STORE); System.setProperty("javax.net.debug", "ssl,handshake"); SSLClient client = new SSLClient(); Socket s = client.clientWithoutCert(); PrintWriter writer = new PrintWriter(s.getOutputStream()); BufferedReader reader = new BufferedReader(new InputStreamReader(s .getInputStream())); writer.println("hello"); writer.flush(); System.out.println(reader.readLine()); s.close(); } private Socket clientWithoutCert() throws Exception { SocketFactory sf = SSLSocketFactory.getDefault(); Socket s = sf.createSocket("localhost", 8443); return s; } } 可以看到,除了把一些类变成SSL通信类以外,客户端也多出了使用信任证书仓库的代码。以上,我们便完成了SSL单向握手通信。即:客户端验证服务端的证书,服务端不认证客户端的证书。 以上便是Java环境下SSL单向握手的全过程。因为我们在客户端设置了日志输出级别为DEBUG: Java代码 1.System.setProperty("javax.net.debug", "ssl,handshake"); System.setProperty("javax.net.debug", "ssl,handshake"); 因此我们可以看到SSL通信的全过程,这些日志可以帮助我们更具体地了解通过SSL协议建立网络连接时的全过程。 结合日志,我们来看一下SSL双向认证的全过程: 第一步: 客户端发送ClientHello消息,发起SSL连接请求,告诉服务器自己支持的SSL选项(加密方式等)。 Bash代码 1.*** ClientHello, TLSv1 *** ClientHello, TLSv1 第二步: 服务器响应请求,回复ServerHello消息,和客户端确认SSL加密方式: Bash代码 1.*** ServerHello, TLSv1 *** ServerHello, TLSv1 第三步: 服务端向客户端发布自己的公钥。 第四步: 客户端与服务端的协通沟通完毕,服务端发送ServerHelloDone消息: Bash代码 1.*** ServerHelloDone *** ServerHelloDone 第五步: 客户端使用服务端给予的公钥,创建会话用密钥(SSL证书认证完成后,为了提高性能,所有的信息交互就可能会使用对称加密算法),并通过ClientKeyExchange消息发给服务器: Bash代码 1.*** ClientKeyExchange, RSA PreMasterSecret, TLSv1 *** ClientKeyExchange, RSA PreMasterSecret, TLSv1 第六步: 客户端通知服务器改变加密算法,通过ChangeCipherSpec消息发给服务端: Bash代码 1.main, WRITE: TLSv1 Change Cipher Spec, length = 1 main, WRITE: TLSv1 Change Cipher Spec, length = 1 第七步: 客户端发送Finished消息,告知服务器请检查加密算法的变更请求: Bash代码 1.*** Finished *** Finished 第八步:服务端确认算法变更,返回ChangeCipherSpec消息 Bash代码 1.main, READ: TLSv1 Change Cipher Spec, length = 1 main, READ: TLSv1 Change Cipher Spec, length = 1 第九步:服务端发送Finished消息,加密算法生效: Bash代码 1.*** Finished *** Finished 那么如何让服务端也认证客户端的身份,即双向握手呢?其实很简单,在服务端代码中,把这一行: Java代码 1.((SSLServerSocket) _socket).setNeedClientAuth(false); ((SSLServerSocket) _socket).setNeedClientAuth(false); 改成: Java代码 1.((SSLServerSocket) _socket).setNeedClientAuth(true); ((SSLServerSocket) _socket).setNeedClientAuth(true); 就可以了。但是,同样的道理,现在服务端并没有信任客户端的证书,因为客户端的证书也是自己生成的。所以,对于服务端,需要做同样的工作:把客户端的证书导出来,并导入到服务端的证书仓库: Bash代码 1.keytool -export -alias bluedash-ssl-demo-client -keystore ./client_ks -file client_key.cer 2.Enter keystore password: client 3.Certificate stored in file <client_key.cer> keytool -export -alias bluedash-ssl-demo-client -keystore ./client_ks -file client_key.cer Enter keystore password: client Certificate stored in file <client_key.cer> Bash代码 1.keytool -import -trustcacerts -alias bluedash-ssl-demo-client -file ./client_key.cer -keystore ./server_ks 2.Enter keystore password: server 3.Owner: CN=localhost, OU=cn, O=cn, L=cn, ST=cn, C=cn 4.Issuer: CN=localhost, OU=cn, O=cn, L=cn, ST=cn, C=cn 5.Serial number: 4c57c80b 6.Valid from: Tue Aug 03 15:40:59 CST 2010 until: Mon Nov 01 15:40:59 CST 2010 7.Certificate fingerprints: 8. MD5: DB:91:F4:1E:65:D1:81:F2:1E:A6:A3:55:3F:E8:12:79 9. SHA1: BF:77:56:61:04:DD:95:FC:E5:84:48:5C:BE:60:AF:02:96:A2:E1:E2 10. Signature algorithm name: SHA1withRSA 11. Version: 3 12.Trust this certificate? [no]: yes 13.Certificate was added to keystore keytool -import -trustcacerts -alias bluedash-ssl-demo-client -file ./client_key.cer -keystore ./server_ks Enter keystore password: server Owner: CN=localhost, OU=cn, O=cn, L=cn, ST=cn, C=cn Issuer: CN=localhost, OU=cn, O=cn, L=cn, ST=cn, C=cn Serial number: 4c57c80b Valid from: Tue Aug 03 15:40:59 CST 2010 until: Mon Nov 01 15:40:59 CST 2010 Certificate fingerprints: MD5: DB:91:F4:1E:65:D1:81:F2:1E:A6:A3:55:3F:E8:12:79 SHA1: BF:77:56:61:04:DD:95:FC:E5:84:48:5C:BE:60:AF:02:96:A2:E1:E2 Signature algorithm name: SHA1withRSA Version: 3 Trust this certificate? [no]: yes Certificate was added to keystore 完成了证书的导入,还要在客户端需要加入一段代码,用于在连接时,客户端向服务端出示自己的证书: Java代码 1.package org.bluedash.tryssl; 2. 3.import java.io.BufferedReader; 4.import java.io.FileInputStream; 5.import java.io.InputStreamReader; 6.import java.io.PrintWriter; 7.import java.net.Socket; 8.import java.security.KeyStore; 9.import javax.net.SocketFactory; 10.import javax.net.ssl.KeyManagerFactory; 11.import javax.net.ssl.SSLContext; 12.import javax.net.ssl.SSLSocketFactory; 13. 14.public class SSLClient { 15. private static String CLIENT_KEY_STORE = "/Users/liweinan/projs/ssl/src/main/resources/META-INF/client_ks"; 16. private static String CLIENT_KEY_STORE_PASSWORD = "456456"; 17. 18. public static void main(String[] args) throws Exception { 19. // Set the key store to use for validating the server cert. 20. System.setProperty("javax.net.ssl.trustStore", CLIENT_KEY_STORE); 21. System.setProperty("javax.net.debug", "ssl,handshake"); 22. SSLClient client = new SSLClient(); 23. Socket s = client.clientWithCert(); 24. 25. PrintWriter writer = new PrintWriter(s.getOutputStream()); 26. BufferedReader reader = new BufferedReader(new InputStreamReader(s.getInputStream())); 27. writer.println("hello"); 28. writer.flush(); 29. System.out.println(reader.readLine()); 30. s.close(); 31. } 32. 33. private Socket clientWithoutCert() throws Exception { 34. SocketFactory sf = SSLSocketFactory.getDefault(); 35. Socket s = sf.createSocket("localhost", 8443); 36. return s; 37. } 38. 39. private Socket clientWithCert() throws Exception { 40. SSLContext context = SSLContext.getInstance("TLS"); 41. KeyStore ks = KeyStore.getInstance("jceks"); 42. 43. ks.load(new FileInputStream(CLIENT_KEY_STORE), null); 44. KeyManagerFactory kf = KeyManagerFactory.getInstance("SunX509"); 45. kf.init(ks, CLIENT_KEY_STORE_PASSWORD.toCharArray()); 46. context.init(kf.getKeyManagers(), null, null); 47. 48. SocketFactory factory = context.getSocketFactory(); 49. Socket s = factory.createSocket("localhost", 8443); 50. return s; 51. } 52.} package org.bluedash.tryssl; import java.io.BufferedReader; import java.io.FileInputStream; import java.io.InputStreamReader; import java.io.PrintWriter; import java.net.Socket; import java.security.KeyStore; import javax.net.SocketFactory; import javax.net.ssl.KeyManagerFactory; import javax.net.ssl.SSLContext; import javax.net.ssl.SSLSocketFactory; public class SSLClient { private static String CLIENT_KEY_STORE = "/Users/liweinan/projs/ssl/src/main/resources/META-INF/client_ks"; private static String CLIENT_KEY_STORE_PASSWORD = "456456"; public static void main(String[] args) throws Exception { // Set the key store to use for validating the server cert. System.setProperty("javax.net.ssl.trustStore", CLIENT_KEY_STORE); System.setProperty("javax.net.debug", "ssl,handshake"); SSLClient client = new SSLClient(); Socket s = client.clientWithCert(); PrintWriter writer = new PrintWriter(s.getOutputStream()); BufferedReader reader = new BufferedReader(new InputStreamReader(s.getInputStream())); writer.println("hello"); writer.flush(); System.out.println(reader.readLine()); s.close(); } private Socket clientWithoutCert() tbhrows Exception { SocketFactory sf = SSLSocketFactory.getDefault(); Socket s = sf.createSocket("localhost", 8443); return s; } private Socket clientWithCert() throws Exception { SSLContext context = SSLContext.getInstance("TLS"); KeyStore ks = KeyStore.getInstance("jceks"); ks.load(new FileInputStream(CLIENT_KEY_STORE), null); KeyManagerFactory kf = KeyManagerFactory.getInstance("SunX509"); kf.init(ks, CLIENT_KEY_STORE_PASSWORD.toCharArray()); context.init(kf.getKeyManagers(), null, null); SocketFactory factory = context.getSocketFactory(); Socket s = factory.createSocket("localhost", 8443); return s; } } 通过比对单向认证的日志输出,我们可以发现双向认证时,多出了服务端认证客户端证书的步骤: Bash代码 1.*** CertificateRequest 2.Cert Types: RSA, DSS 3.Cert Authorities: 4.<CN=localhost, OU=cn, O=cn, L=cn, ST=cn, C=cn> 5.<CN=localhost, OU=cn, O=cn, L=cn, ST=cn, C=cn> 6.*** ServerHelloDone *** CertificateRequest Cert Types: RSA, DSS Cert Authorities: <CN=localhost, OU=cn, O=cn, L=cn, ST=cn, C=cn> <CN=localhost, OU=cn, O=cn, L=cn, ST=cn, C=cn> *** ServerHelloDone Bash代码 1.*** CertificateVerify 2.main, WRITE: TLSv1 Handshake, length = 134 3.main, WRITE: TLSv1 Change Cipher Spec, length = 1 *** CertificateVerify main, WRITE: TLSv1 Handshake, length = 134 main, WRITE: TLSv1 Change Cipher Spec, length = 1 在 @*** ServerHelloDone@ 之前,服务端向客户端发起了需要证书的请求 @*** CertificateRequest@ 。 在客户端向服务端发出 @Change Cipher Spec@ 请求之前,多了一步客户端证书认证的过程 @*** CertificateVerify@ 。 客户端与服务端互相认证证书的情景
Java代码 - 在项目中经常遇见这样的问题:修改应用的配置文件web.xml后,无论重启应用还是重启WebSphere服务器,都不能重新加载web.xml,导致修改的内容无效。
-
- 这 个问题困扰了我好久,即使删除了${was安装目录}\IBM\WebSphere\AppServer\profiles\AppSrv01\下的 temp和wstemp两个缓存文件夹下的临时文件,重启后还是无效。几经折腾,后来终于找到了问题所在——还是由于was的缓存机制导致的。
-
- 找 到${was安装目录}\AppServer\profiles\AppSrv01\config\cells\xxxNode01Cell \applications\${应用名}.ear\deployments\目录下,有一个与应用相同名称的缓存文件夹,删除或修改该文件夹的 web.xml,重启was即可。
Java代码 - 在websphere中修改了jsp后,有时会出现修改的jsp没有起作用,特别是改变了某jsp的样式后,在页面中没看到效果,这主要就是由于websphere中缓存的缘故,这就要清除WebSphere中jsp缓存,如我的应用部署的目录为:
- E:\IBM\WebSphere\AppServer\profiles\AppSrv01\installedApps\nbxzfwNode01Cell\项目名_war.ear\项目名.war
- 在这个目录下更新了某个jsp页面,后在浏览器里面查看的时候,发现页面没有改变。基于此,我查看了一下目录,存放应用临时文件的地方:
- E:\IBM\WebSphere\AppServer\profiles\AppSrv01\temp\nbxzfwNode01\server1\项目名_war\项目名.war
- 在这目录下,可以看到有很多class文件,都是jsp编译过来的,对应我们应用目录下的jsp文件,于是找到对应jsp的class文件删除,再到浏览器中查看,发现已经改变了。。
- 还有一种办法,就是把这个jsp从项目中删除或重命名,再到浏览器里面查看那个页面,这时可能会报错,之后,再把对应的jsp添加上或名字改过来,再次到浏览器里面查看应用的时候,就发现这jsp的更新效果出来了,呵呵…
Java代码 - 前两天去客户那里给系统做升级,同时协助解决几个使用中的问题。到了现场第一件事情是把以前的应用导出做备份, 结果居然遇到返回null。查看日志发现系统报告空间不足,以前遇到这个问题是因为WAS出现oom(Out of Memory)之后,会生成javacore和dump文件供分析内存,这两个文件通常都比较大,30多M。如果多次出现oom,生成的文件就会占用大量空间。难道最近经常内存溢出?我心抽搐。
- 快马赶到Profile目录,没有发现导出文件,询问客户也没有出现系统宕机的情况,大石落地。仔细查看之后发现profile下的wstemp目录体积巨大,接近1.7G。这是个临时目录,每当管理员通过console登录之后,wstemp会生成一个新文件夹,保存管理员的所有操作记录,在管理员登出之后会删除该目录。但是wstemp下一堆的临时文件夹都没有被删掉,看来是was 5的bug又遗留到was 6了,真是搞不明白,was的这一堆补丁怎么都没解决掉这么明显的一个问题。N多RMB的was啊,越来越觉得还不如免费的jboss。
- 回公司之后查看了自己的was版本,wstemp目录2.4G,不过分区够大才没出问题。直接删之了事
转: http://dsr-22.iteye.com/blog/1258831
简介: Gang of Four 的解释器设计模式 (Interpreter design pattern) 鼓励在一个语言的基础上构建一个新的语言来实现扩展。大多数函数式语言都能够让您以多种方式(如操作符重载和模式匹配)对语言进行扩展。尽管 Java™ 不支持这些技术,下一代 JVM 语言均支持这些技术,但其具体实现细则有所不同。在本文中,Neal Ford 将探讨 Groovy、Scala 和 Clojure 如何通过以 Java 无法支持的方式来实现函数式扩展,从而实现解释器设计模式的目的。 在本期 函数式思维 的文章中,我将继续研究 Gang of Four (GoF) 设计模式(参阅 参考资料)的函数式替代解决方案。在本文中,我将研究最少人了解,但却是最强大的模式之一:解释器 (Interpreter)。 解释器的定义是: 给定一个语言,定义其语法表示,以及一个使用该表示来解释语言中的句子的解释器。 换句话说,如果您正在使用的语言不适用于解决问题,那么用它来构建一个适用的语言。关于该方法的一个很好的示例出现在 Web 框架中,如 Grails 和 Ruby on Rails(参阅 参考资料),它们扩展了自己的基础语言(分别是 Groovy 和 Ruby),使编写 Web 应用程序变得更容易。 这种模式最少人了解,因为构建一种新的语言并不常见,需要专业的技能和惯用语法。它是最强大的 设计模式,因为它鼓励您针对正在解决的问题扩展自己的编程语言。这在 Lisp(因此 Clojure 也同样)世界是一个普遍的特质,但在主流语言中不太常见。 当使用禁止对语言本身进行扩展的语言(如 Java)时,开发人员往往将自己的思维塑造成该语言的语法;这是您的惟一选择。然而,当您渐渐习惯使用允许轻松扩展的语言时,您就会开始将语言折向解决问题的方向,而不是其他折衷的方式。 Java 缺乏直观的语言扩展机制,除非您求助于面向方面的编程。然而,下一代的 JVM 语言(Groovy、Scala 和 Clojure)(参阅 参考资料)均支持以多种方式进行扩展。通过这样做,它们可以达到解释器设计模式的目的。首先,我将展示如何使用这三种语言实现操作符重载,然后演示 Groovy 和 Scala 如何让您扩展现有的类。 操作符重载(operator overloading) 操作符重载 是函数式语言的一个常见特性,能够重定义操作符(如 + 、- 或 * )配合新的类型工作,并表现出新的行为。操作符重载的缺失是 Java 形成时期的一个有意识的决定,但现在几乎每一个现代语言都具备这个特性,包括在 JVM 上 Java 的天然接班人。 Groovy Groovy 尝试更新 Java 的语法,使其跟上潮流,同时保留其自然语义。因此,Groovy 通过将操作符自动映射到方法名称实现操作符重载。例如,如果您想重载 Integer 的 + 操作符,那么您要重写 Integer 类的 plus() 方法。完整的映射列表已在线提供(参阅 参考资料);表 1 显示了列表的一部分: 表 1. Groovy 的操作符/方法映射列表的一部分 操作符 | 方法 |
---|
x + y | x.plus(y) | x * y | x.multiply(y) | x / y | x.div(y) | x ** y | x.power(y) | 作为一个操作符重载的示例,我将在 Groovy 和 Scala 中都创建一个 ComplexNumber 类。复数 是一个数学概念,由一个实数 和虚数 部分组成,一般写法是,例如 3 + 4i 。复数在许多科学领域中都很常用,包括工程学、物理学、电磁学和混沌理论。开发人员在编写这些领域的应用程序时,大大受益于能够创建反映其问题域的操作符。(有关复数的更多信息,请参阅 参考资料。) 清单 1 中显示了一个 Groovy ComplexNumber 类: 清单 1. Groovy 中的 ComplexNumber package complexnums class ComplexNumber { def real, imaginary public ComplexNumber(real, imaginary) { this.real = real this.imaginary = imaginary } def plus(rhs) { new ComplexNumber(this.real + rhs.real, this.imaginary + rhs.imaginary) } def multiply(rhs) { new ComplexNumber( real * rhs.real - imaginary * rhs.imaginary, real * rhs.imaginary + imaginary * rhs.real) } String toString() { real.toString() + ((imaginary < 0 ? "" : "+") + imaginary + "i").toString() } } | 在 清单 1 中,我创建一个类,保存实数和虚数部分,并且我创建重载的 plus() 和 multiply() 操作符。两个复数的相加是非常直观的:plus() 操作符将两个数各自的实数和虚数分别进行相加,并产生结果。两个复数的相乘需要以下公式: (x + yi)(u + vi) = (xu - yv) + (xv + yu)i | 在 清单 1 中的 multiply() 操作符复制该公式。它将两个数字的实数部分相乘,然后减去虚数部分相乘的积,再加上实数和虚数分别彼此相乘的积。 清单 2 测试复数运算符: 清单 2. 测试复数运算符 package complexnums import org.junit.Test import static org.junit.Assert.assertTrue import org.junit.Before class ComplexNumberTest { def x, y @Before void setup() { x = new ComplexNumber(3, 2) y = new ComplexNumber(1, 4) } @Test void plus_test() { def z = x + y; assertTrue 3 + 1 == z.real assertTrue 2 + 4 == z.imaginary } @Test void multiply_test() { def z = x * y assertTrue(-5 == z.real) assertTrue 14 == z.imaginary } } | 在 清单 2 中,plus_test() 和 multiply_test() 方法对重载操作符的使用(两者都以该领域专家使用的相同符号代表)与类似的内置类型用法没什么区别。 Scala(和 Clojure) Scala 通过放弃操作符和方法之间的区别来实现操作符重载:操作符仅仅是具有特殊名称的方法。因此,要使用 Scala 重写乘法运算,您要重写 * 方法。在清单 3 中,我用 Scala 创建复数。 清单 3. Scala 中的复数 class ComplexNumber(val real:Int, val imaginary:Int) { def +(operand:ComplexNumber):ComplexNumber = { new ComplexNumber(real + operand.real, imaginary + operand.imaginary) } def *(operand:ComplexNumber):ComplexNumber = { new ComplexNumber(real * operand.real - imaginary * operand.imaginary, real * operand.imaginary + imaginary * operand.real) } override def toString() = { real + (if (imaginary < 0) "" else "+") + imaginary + "i" } } | 清单 3 中的类包括熟悉的 real 和 imaginary 成员,以及 + 和 * 操作符/方法。如清单 4 所示,我可以自然地使用 ComplexNumber : 清单 4. 在 Scala 中使用复数 val c1 = new ComplexNumber(3, 2) val c2 = new ComplexNumber(1, 4) val c3 = c1 + c2 assert(c3.real == 4) assert(c3.imaginary == 6) val res = c1 + c2 * c3 printf("(%s) + (%s) * (%s) = %s\n", c1, c2, c3, res) assert(res.real == -17) assert(res.imaginary == 24) | 通过统一操作符和方法,Scala 使操作符重载变成一件小事。Clojure 使用相同的机制来重载操作符。例如,以下 Clojure 代码定义了一个重载的 ** 操作符: (defn ** [x y] (Math/pow x y)) | 回页首 扩展类 类似于操作符重载,下一代的 JVM 语言允许您扩展类(包括核心 Java 类),扩展的方式在 Java 语言本身是不可能实现的。这些设施通常用于构建领域特定的语言 (DSL)。虽然 GOF 从来没有考虑过 DSL(因为它们与当时流行的语言没有共同点),DSL 却体现了解释器设计模式的初衷。 通过将计量单位和其他修饰符添加给 Integer 等核心类,您可以(就像添加操作符一样)更紧密地对现实问题进行建模。Groovy 和 Scala 都支持这样做,但它们使用不同的机制。 Groovy 的 Expando 和类别类 Groovy 包括两种对现有类添加方法的机制:ExpandoMetaClass 和类别。(在 函数式思维:函数设计模式,第 2 部分 中,我在适配器模式的上下文中详细介绍过 ExpandoMetaClass 。) 比方说,您的公司由于离奇的遗留原因,需要以浪(furlongs,英国的计量单位)/每两周而不是以英里/每小时 (MPH) 的方法来表达速度,开发人员发现自己经常要执行这种转换。使用 Groovy 的 ExpandoMetaClass ,您可以添加一个 FF 属性给处理转换的 Integer ,如清单 5 所示: 清单 5. 使用 ExpandoMetaClass 添加一个浪/两周的计量单位给 Integer static { Integer.metaClass.getFF { -> delegate * 2688 } } @Test void test_conversion_with_expando() { assertTrue 1.FF == 2688 } | ExpandoMetaClass 的替代方法是,创建一个类别 包装器类,这是从 Objective-C 借来的概念。在清单 6 中,我添加了一个(小写) ff 属性给 Integer : 清单 6. 通过一个类别类添加计量单位 class FFCategory { static Integer getFf(Integer self) { self * 2688 } } @Test void test_conversion_with_category() { use(FFCategory) { assertTrue 1.ff == 2688 } } | 一个类别类是一个带有一组静态方法集合的普通类。每个方法接受至少一个参数;第一个参数是这种方法增强的类型。例如,在 清单 6 中, FFCategory 类拥有一个 getFf() 方法,它接受一个 Integer 参数。当这个类别类与 use 关键字一起使用时,代码块内所有相应类型都被增强。在单元测试中,我可以在代码块内引用 ff 属性(记住,Groovy 自动将 get 方法转换为属性引用),如在 清单 6 的底部所示。 有两种机制可供选择,让您可以更准确地控制增强的范围。例如,如果整个系统使用 MPH 作为速度的默认单位,但也需要频繁转换为浪/每两周,那么使用 ExpandoMetaClass 进行全局修改将是适当的。 您可能对重新开放核心 JVM 类的有效性持怀疑态度,担心会产生广泛深远的影响。类别类让您限制潜在危险性增强的范围。以下是一个来自真实世界的开源项目示例,它极好地利用了这一机制。 easyb 项目(参阅 参考资料)让您可以编写测试,以验证正接受测试的类的各个方面。请研究清单 7 所示的 easyb 测试代码片段: 清单 7. easyb 测试一个 queue 类 it "should dequeue items in same order enqueued", { [1..5].each {val -> queue.enqueue(val) } [1..5].each {val -> queue.dequeue().shouldBe(val) } } | queue 类不包括 shouldBe() 方法,这是我在测试的验证阶段所调用的方法。easyb 框架已为我添加了该方法;清单 8 中所显示的在 easyb 源代码中的 it() 方法定义,演示了该过程: 清单 8. easyb 的 it() 方法定义 def it(spec, closure) { stepStack.startStep(BehaviorStepType.IT, spec) closure.delegate = new EnsuringDelegate() try { if (beforeIt != null) { beforeIt() } listener.gotResult(new Result(Result.SUCCEEDED)) use(categories) { closure() } if (afterIt != null) { afterIt() } } catch (Throwable ex) { listener.gotResult(new Result(ex)) } finally { stepStack.stopStep() } } class BehaviorCategory { // ... static void shouldBe(Object self, value) { shouldBe(self, value, null) } //... } | 在 清单 8中,it() 方法接受了一个 spec (描述测试的一个字符串)和一个代表测试的主体的闭包块。在方法的中间,闭包会在 BehaviorCategory 块内执行,该块出现在清单的底部。BehaviorCategory 增强 Object ,允许 Java 世界中的任何 实例验证其值。 通过允许选择性增强驻留在层次结构顶层的 Object ,Groovy 的开放类机制可以轻松地实现为任何实例验证结果,但它限制了对 use 块主体的修改。 Scala 的隐式转换 Scala 使用隐式转换 来模拟现有类的增强。隐式转换不会对类添加方法,但允许语言自动将一个对象转换成拥有所需方法的相应类型。例如,我不能将 isBlank() 方法添加到 String 类中,但我可以创建一个隐式转换,将 String 自动转换为拥有这种方法的类。 作为一个示例,我想将 append() 方法添加到 Array ,这让我可以轻松地将 Person 实例添加到适当类型的数组,如清单 9 所示: 清单 9.将一个方法添加到 Array 中,以增加人员 case class Person (firstName: String, lastName: String) {} class PersonWrapper(a: Array[Person]) { def append(other: Person) = { a ++ Array(other) } def +(other: Person) = { a ++ Array(other) } } implicit def listWrapper(a: Array[Person]) = new PersonWrapper(a) | 在 清单 9中,我创建一个简单的 Person 类,它带有若干个属性。为了使 Array[Person] (在 Scala 中,一般使用 [ ] 而不是 < > 作为分隔符)Person 可知,我创建一个 PersonWrapper 类,它包括所需的 append() 方法。在清单的底部,我创建一个隐式转换,当我在数组上调用 append() 方法时,隐式转换会自动将一个 Array[Person] 转换为 PersonWrapper 。清单 10 测试该转换: 清单 10. 测试对现有类的自然扩展 val p1 = new Person("John", "Doe") var people = Array[Person]() people = people.append(p1) | 在 清单 9中,我也为 PersonWrapper 类添加了一个 + 方法。清单 11 显示了我如何使用操作符的这个漂亮直观的版本: 清单 11. 修改语言以增强可读性 people = people + new Person("Fred", "Smith") for (p <- people) printf("%s, %s\n", p.lastName, p.firstName) | Scala 实际上并未对原始的类添加一个方法,但它通过自动转换成一个合适的类型,提供了这样做的外观。使用 Groovy 等语言进行元编程所需要的相同工作在 Scala 中也需要,以避免过多使用隐式转换而产生由相互关联的类所组成的令人费解的网。但是,在正确使用时,隐式转换可以帮助您编写表达非常清晰的代码。 回页首 结束语 来自 GoF 的原始解释器设计模式建议创建一个新语言,但其基础语言并不支持我们今天所掌握的良好扩展机制。下一代 Java 语言都通过使用多种技术来支持语言级别的可扩展性。在本期文章中,我演示了操作符重载如何在 Groovy、Scala 和 Clojure 中工作,并研究了在 Groovy 和 Scala 中的类扩展。 在下期文章中,我将展示 Scala 风格的模式匹配和泛型的组合如何取代一些传统的设计模式。该讨论的中心是一个在函数式错误处理中也起着作用的概念,这一概念将是我们下期文章的主题。 参考资料 学习 获得产品和技术 讨论 关于作者 Neal Ford 是一家全球性 IT 咨询公司 ThoughtWorks 的软件架构师和 Meme Wrangler。他的工作还包括设计和开发应用程序、教材、杂志文章、课件和视频/DVD 演示,而且他是各种技术书籍的作者或编辑,包括最近的新书 The Productive Programmer 。他主要的工作重心是设计和构建大型企业应用程序。他还是全球开发人员会议上的国际知名演说家。请访问他的 Web 站点。
http://www.ibm.com/developerworks/cn/views/java/libraryview.jsp?view_by=search&sort_by=Date&sort_order=desc&view_by=Search&search_by=%E5%87%BD%E6%95%B0%E5%BC%8F%E6%80%9D%E7%BB%B4&dwsearch.x=18&dwsearch.y=11
|