首页 文章 精选 留言 我的

精选列表

搜索[技巧],共4633篇文章
优秀的个人博客,低调大师

Windows 入侵痕迹清理技巧

为避免入侵行为被发现,攻击者总是会通过各种方式来隐藏自己,比如:隐藏自己的真实IP、清除系统日志、删除上传的工具、隐藏后门文件、擦除入侵过程中所产生的痕迹等。 01、Windows日志清除 windows 日志路径: 系统日志:%SystemRoot%\System32\Winevt\Logs\System.evtx安全日志:%SystemRoot%\System32\Winevt\Logs\Security.evtx应用程序日志:%SystemRoot%\System32\Winevt\Logs\Application.evtx 日志在注册表的键:HKEY_LOCAL_MACHINE\system\CurrentControlSet\Services\Eventlog windows 日志清除方式: (1)最简单粗暴的方式 开始→运行,输入 eventvwr 进入事件查看器,右边栏选择清除日志。 (2)命令行一键清除Windows事件日志 PowerShell -Command "& {Clear-Eventlog -Log Application,System,Security}" Get-WinEvent-ListLogApplication,Setup,Security-Force|%{Wevtutil.execl$_.Logname} (3)利用脚本停止日志的记录 通过该脚本遍历事件日志服务进程(专用svchost.exe)的线程堆栈,并标识事件日志线程以杀死事件日志服务线程。 因此,系统将无法收集日志,同时事件日志服务似乎正在运行。 github项目地址:https://github.com/hlldz/Invoke-Phant0m (4)Windows单条日志清除 该工具主要用于从Windows事件日志中删除指定的记录。 github项目地址:https://github.com/QAX-A-Team/EventCleaner (5)Windows日志伪造 使用eventcreate这个命令行工具来伪造日志或者使用自定义的大量垃圾信息覆盖现有日志。 eventcreate -l system -so administrator -t warning -d "this is a test" -id 500 02、IIS日志 IIS默认日志路径: %SystemDrive%\inetpub\logs\LogFiles\W3SVC1\ 清除WWW日志: 停止服务:net stop w3svc删除日志目录下所有文件:del *.*启用服务:net start w3svc 03、利用Windows自带命令进行安全擦除 (1)Shift+Delete快捷键永久删除 直接删除文件,还是能在回收站找到的,使用Shift+Delete快捷键可以直接永久删除了。但是用数据恢复软件,删除的文件尽快恢复,否则新的文件存入覆盖了原来的文件痕迹就很难恢复了。 (2)Cipher 命令多次覆写 在删除文件后,可以利用Cipher 命令通过 /W 参数可反复写入其他数据覆盖已删除文件的硬盘空间,彻底删除数据防止被恢复。 比如,删除D:\tools目录下的文件,然后执行这条命令: cipher /w:D:\tools 这样一来,D 盘上未使用空间就会被覆盖三次:一次 0x00、一次 0xFF,一次随机数,所有被删除的文件就都不可能被恢复了。 (3)Format命令覆盖格式化 Format 命令加上 /P 参数后,就会把每个扇区先清零,再用随机数覆盖。而且可以覆盖多次。比如: format D: /P:8 这条命令表示把 D 盘用随机数覆盖 8 次。 04、清除远程桌面连接记录 当通过本机远程连接其他客户端或服务器后,会在本机存留远程桌面连接记录。代码保存为clear.bat文件,双击运行即可自动化清除远程桌面连接记录。 @echo offreg delete "HKEY_CURRENT_USER\Software\Microsoft\Terminal Server Client\Default" /va /freg delete "HKEY_CURRENT_USER\Software\Microsoft\Terminal Server Client\Servers" /freg add "HKEY_CURRENT_USER\Software\Microsoft\Terminal Server Client\Servers"cd %userprofile%\documents\attrib Default.rdp -s -hdel Default.rdp 05、Metasploit 痕迹清除 (1)查看事件日志 meterpreter > run event_manager -i [*] Retriving Event Log Configuration Event Logs on System==================== Name Retention Maximum Size Records ---- --------- ------------ ------- Application Disabled 20971520K 2149 HardwareEvents Disabled 20971520K 0 Internet Explorer Disabled K 0 Key Management Service Disabled 20971520K 0 Security Disabled 20971520K 1726 System Disabled 20971520K 3555 Windows PowerShell Disabled 15728640K 138 (2)清除事件日志(包括六种日志类型) meterpreter > run event_manager -c (3)另外,也可以输入clearv命令清除目标系统的事件日志(仅包含三种日志类型) meterpreter > clearev [*] Wiping 4 records from Application...[*] Wiping 8 records from System...[*] Wiping 7 records from Security... 本文分享自微信公众号 - Bypass(Bypass--)。 如有侵权,请联系 support@oschina.cn 删除。 本文参与“OSC源创计划”,欢迎正在阅读的你也加入,一起分享。

优秀的个人博客,低调大师

Intellij IDEA Debug 调试技巧

F9:恢复程序 Alt+F10:显示执行断点 F8:跳到下一步F7:进入到代码Alt+shift+F7:强制进入代码Shift+F8:跳到下一个断点Atl+F9:运行到光标处ctrl+shift+F9:debug运行java类ctrl+shift+F10:正常运行java类Alt+F8:debug时选中查看值 1、这里以一个web工程为例,点击图中按钮开始运行web工程。 2、设置断点 3、使用postman发送http请求 4、请求发送之后会自动跳到断点处,并且在断点之前会有数据结果显示 5、按F8在 Debug 模式下,进入下一步,如果当前行断点是一个方法,则不进入当前方法体内,跳到下一条执行语句。 6、按F7在 Debug 模式下,进入下一步,如果当前行断点是一个方法,则进入当前方法体内,如果该方法体还有方法,则会进入该内嵌的方法中 . 7、继续按F7,则跳到StopWatch() 构造方法中。 8、跳出该方法,可以按Shift+F8,在 Debug 模式下,跳回原来地方。 9、这时我们按F8,会继续执行下一条语句。 10、当我们执行到第二个断点处,如果想直接执行到第三个断点处,可以按F9。 补充:Alt+F8 可以通过在 Debug 的状态下,选中对象,弹出可输入计算表达式调试框,查看该输入内容的调试结果 。 第一个红框是我输入的参数,第二个是我执行之后显示得结果。 原文发布时间为:2018-11-26 本文来自云栖社区合作伙伴“Java技术栈”,了解相关信息可以关注“Java技术栈”。

优秀的个人博客,低调大师

Gradle 使用技巧(五) - AAR

1. 什么是AAR 关于aar文件,摘取官方的解释:AAR 文件的文件扩展名为 .aar,Maven 工件类型也应当是 aar。文件本身是一个包含以下强制性条目的 zip 文件: /AndroidManifest.xml /classes.jar /res/ /R.txt 此外,AAR 文件可能包含以下可选条目中的一个或多个: /assets/ /libs/名称.jar /jni/abi 名称/名称.so(其中 abi 名称 是 Android 支持的 ABI 之一) /proguard.txt /lint.jar 2. AAR文件位置 AAP的位置为 【项目名称/模块名称/build/outputs/aar/】 中,如果没有aar文件,可以通过点击 Build > Make Project 的方式重新生成此文件。 需要注意的是:只有library模块才会生成aar文件,application模块是不会生成的。 3. 如何使用AAR文件 创建一个library工程,写一段简单的代码。 TestUtils Build -> Make Project aar文件位置 将aar文件复制到需要引入的模块下的libs文件夹中。 放入aar gradle中配置引用 在android同级节点做以下配置: repositories{ flatDir{ dirs 'libs' } } dependencies中配置依赖: //implementation(name:'xxx',ext:'aar') implementation(name:'core-library',ext:'aar') 然后同步一下,等待编译完成。 验证是否引入成功 尝试调用 4. AAR开发注意事项 资源合并冲突 构建工具会将库模块中的资源与相关应用模块的资源合并。如果在两个模块中均定义了给定资源 ID,将使用应用中的资源。 如果多个 AAR 库之间发生冲突,将使用依赖项列表首先列出(位于 dependencies 块顶部)的库中的资源。 为了避免常用资源 ID 的资源冲突,请使用在模块(或在所有项目模块)中具有唯一性的前缀或其他一致的命名方案。 库模块可以包含 JAR 库 可以开发一个自身包含 JAR 库的库模块;不过,需要手动编辑相关应用模块的构建路径,并添加 JAR 文件的路径。 库模块可以依赖外部 JAR 库 在开发一个依赖于外部库(例如 Maps 外部库)的情况下,相关应用必须针对包含外部库(例如 Google API 插件)的目标构建。另外也要注意,库模块和相关应用都必须在其清单文件的 <uses- library> 元素中声明外部库。 库模块不得包含原始资源 工具不支持在库模块中使用原始资源文件(保存在 assets/ 目录中)。应用使用的任何原始资源都必须存储在应用模块自身的 assets/ 目录中。 应用模块的 minSdkVersion 必须大于或等于库定义的版本 库作为相关应用模块的一部分编译,因此,库模块中使用的 API 必须与应用模块支持的平台版本兼容。 每个库模块都会创建自己的 R 类 在构建相关应用模块时,库模块将先编译到 AAR 文件中,然后再添加到应用模块中。因此,每个库都有其自己的 R 类,并根据库的软件包名称命名。从主模块和库模块生成的 R 类会在所需的所有软件包(包括主模块的软件包和库的软件包)中创建。 5. 参考资料 Create an Android Library

优秀的个人博客,低调大师

Gradle 使用技巧 - 配置签名

使用场景 我们在使用百度地图和微信相关的API的时候,是需要我们填入相关的的app签名进行校验后才能进行操作,而默认我们在调试模式下使用的是AS为我们提供的android.keystore,所幸,Gradle为我们提供了相关的方法,我们可以为debug和release指定密钥文件和前面。我们需要先在app.gradle的android节点做做相关的配置。 配置签名信息 signingConfigs { debug { try { storeFile file("../签名文件.jks") storePassword "密码" keyAlias "别名" keyPassword "密码" v1SigningEnabled true v2SigningEnabled false } catch (ex) { ex.printStackTrace() throw new InvalidUserDataException("秘钥用户名或密码错误${ex.getMessage()}") } } release { try { storeFile file("../签名文件.jks") storePassword "密码" keyAlias "别名" keyPassword "密码" v1SigningEnabled true v2SigningEnabled false } catch (ex) { throw new InvalidUserDataException("秘钥用户名或密码错误") } } } 这里需要注意的是storeFile的路径是指当前app.gradle的相对路径。 关于v1SigningEnabled和v2SigningEnabled APK Signature Scheme v2是Android 7.0 引入一项新的应用签名方案 APK Signature Scheme v2,它能提供更快的应用安装时间和更多针对未授权 APK 文件更改的保护。一般情况下都会选择关闭。 在相应的buildTypes中指定签名 buildTypes{ release{ signingConfig signingConfigs.release ... } debug{ signingConfig signingConfigs.debug ... } } 虽然不指定这一步,我们依然能够正常运行,但是为了出不必要的幺蛾子,我们还是把它加上吧。

优秀的个人博客,低调大师

JS高级技巧(简洁版)

高级函数 由于在JS中,所有的函数都是对象,所以使用函数指针十分简单,也是这些东西使JS函数有趣且强大 安全的类型检测 JS内置的类型检测机制并不是完全可靠的 typeof 操作符返回一个字符串,表示未经计算的操作数的类型,在大多数情况下很靠谱,但是当然还有例外 正则表达式 typeof /s/ === 'function'; // Chrome 1-12 , 不符合 ECMAScript 5.1 typeof /s/ === 'object'; // Firefox 5+ , 符合 ECMAScript 5.1 复制代码 NULL typeof null === 'object'; // 从一开始出现JavaScript就是这样的 复制代码 在 JavaScript 最初的实现中,JavaScript 中的值是由一个表示类型的标签和实际数据值表示的。对象的类型标签是 0。由于 null 代表的是空指针(大多数平台下值为 0x00),因此,null的类型标签也成为了 0,typeof null就错误的返回了object instanceof 运算符用来测试一个对象在其原型链中是否存在一个构造函数的 prototype 属性 语法 object instanceof constructor(要检测的对象 instanceof 构造函数) 但是在浏览器中,我们的脚本可能需要在多个窗口之间进行交互。多个窗口意味着多个全局环境,不同的全局环境拥有不同的全局对象,从而拥有不同的内置类型构造函数。这可能会引发一些问题。 [] instanceof window.frames[0].Array //false 复制代码 因为 Array.prototype !== window.frames[0].Array.prototype,因此你必须使用 Array.isArray(myObj) 或者 Object.prototype.toString.call(myObj) === "[object Array]"来判断myObj是否是数组 解决以上两个问题的方案就是Object.prototype.toString Object.prototype.toString 方法返回一个表示该对象的字符串 可以通过toString() 来获取每个对象的类型。为了每个对象都能通过 Object.prototype.toString() 来检测,需要以 Function.prototype.call() 或者 Function.prototype.apply()的形式来调用,传递要检查的对象作为第一个参数,称为thisArg var toString = Object.prototype.toString; toString.call(new Date); // [object Date] toString.call(new String); // [object String] toString.call(Math); // [object Math] toString.call(/s/); // [object RegExp] toString.call([]); // [object Array] //Since JavaScript 1.8.5 toString.call(undefined); // [object Undefined] toString.call(null); // [object Null] 复制代码 作用域安全的构造函数 构造函数其实就是一个使用new操作符调用的函数。当使用new调用时,构造函数内用到的this对象会指向新创建的对象实例 function Person(name, age){ this.name = name; this.age = age; } let person = new Person("addone", 20); person.name // addone 复制代码 当你使用new操作符的时候,就会创建一个新的Person对象,同时分配这些属性,但是如果你没有使用new let person = Person("addone", 20); person1.name // Cannot read property 'name' of undefined window.name // addone 复制代码 这是因为this是在执行时确认的,当你没有使用new,那么this在当前情况下就被解析成了window,属性就被分配到window上了 作用域安全的构造函数在进行更改前,首先确认this对象是正确类型的实例,如果不是,就创建新的对象并且返回 function Person(name, age){ if(this instanceof Person){ this.name = name; this.age = age; }else{ return new Person(name, age); } } let person1 = new Person("addone", 20); person1.name // addone let person2 = Person("addone", 20); person2.name // addone 复制代码 this instanceof Person检查了this对象是不是Person的实例,如果是则继续,不是则调用new 惰性载入函数 假如你要写一个函数,里面有一些判断语句 function foo(){ if(a != b){ console.log('aaa') }else{ console.log('bbb') } } 复制代码 如果你的a和b是不变的,那么这个函数不论执行多少次,结果都是不变的,但是每次执行还要进行if判断,这就造成了不必要的浪费。 惰性载入表示函数执行的分支只会发生一次,这里有两种解决方式。 在函数被调用时再处理函数 function foo(){ if(a != b){ foo = function(){ console.log('aaa') } }else{ foo = function(){ console.log('bbb') } } return foo(); } 复制代码 这样进入每个分支后都会对foo进行赋值,覆盖了之前的函数,之后每次调用foo就不会再执行if判断 在声明函数时就指定适当的函数 var foo = (function foo(){ if(a != b){ return function(){ console.log('aaa') } }else{ return function(){ console.log('bbb') } } })(); 复制代码 这里创建一个匿名,自执行的函数,用来确定应该使用哪一个函数来实现。 惰性函数的优点就是只在第一次执行分支时牺牲一点点性能 函数绑定 请使用fun.bind(thisArg[, arg1[, arg2[, ...]]]) thisArg 当绑定函数被调用时,该参数会作为原函数运行时的 this 指向。当使用new 操作符调用绑定函数时,该参数无效 arg1,arg2,... 当绑定函数被调用时,这些参数将置于实参之前传递给被绑定的方法 返回 由指定的this值和初始化参数改造的原函数拷贝 一个例子 let person = { name: 'addone', click: function(e){ console.log(this.name) } } let btn = document.getElementById('btn'); EventUtil.addHandle(btn, 'click', person.click); 复制代码 这里创建了一个person对象,然后将person.click方法分配给DOM按钮的事件处理程序,当你点击按按钮时,会打印出undefiend,原因是执行时this指向了DOM按钮而不是person 解决方案: 将this强行指向person EventUtil.addHandle(btn, 'click', person.click.bind(person)); 复制代码 函数柯里化 函数柯里化是把接受多个参数的函数转变成接受单一参数的函数 function add(num1, num2){ return num1 + num2; } function curryAdd(num2){ return add(1, num2); } add(2, 3) // 5 curryAdd(2) // 3 复制代码 这个例子用来方便理解柯里化的概念 下面是创建函数柯里化的通用方式 function curry(fn){ var args = Array.prototype.slice.call(arguments, 1); return function(){ let innerArgs = Array.prototype.slice.call(arguments); let finalArgs = args.concat(innerArgs); return fn.apply(null, finalArgs); } } 复制代码 第一个参数是要进行柯里化的函数,其他参数是要传入的值。这里使用Array.prototype.slice.call(arguments, 1)来获取第一个参数后的所有参数(外部)。在返回的函数中,同样调用Array.prototype.slice.call(arguments)让innerArgs来存放所有的参数(内部),然后用concat将内部外部参数组合,用apply传递给函数 function add(num1, num2){ return num1 + num2; } let curryAdd1 = curry(add, 1); curryAdd1(2); // 3 let curryAdd2 = curry(add, 1, 2); curryAdd2(); // 3 复制代码 防篡改对象 Javascript中任何对象都可以被同一环境中运行的代码修改,所以开发人员有时候需要定义防篡改对象(tamper-proof object) 来保护自己 不可扩展对象 默认情况下所有对象都是可以扩展的(添加属性和方法) let person = { name: 'addone' }; person.age = 20; 复制代码 第二行为person对象扩展了age属性,当然你可以阻止这一行为,使用Object.preventExtensions() let person = { name: 'addone' }; Object.preventExtensions(person); person.age = 20; person.age // undefined 复制代码 你还可以用Object.isExtensible()来判断对象是不是可扩展的 let person = { name: 'addone' }; Object.isExtensible(person); // true Object.preventExtensions(person); Object.isExtensible(person); // false 复制代码 请记住这是不可扩展!!,即不能添加属性或方法 密封的对象 密封对象不可扩展,且不能删除属性和方法 let person = { name: 'addone' }; Object.seal(person); person.age = 20; delete person.name; person.age // undefined person.name // addone 复制代码 相对的也有Object.isSealed()来判断是否密封 let person = { name: 'addone' }; Object.isExtensible(person); // true Object.isSealed(person); // false Object.seal(person); Object.isExtensible(person); // false Object.isSealed(person); // true 复制代码 冻结的对象 这是最严格的防篡改级别,冻结的对象即不可扩展,又密封,且不能修改 let person = { name: 'addone' }; Object.freeze(person); person.age = 20; delete person.name; person.name = 'addtwo' person.age // undefined person.name // addone 复制代码 同样也有Object.isFrozen来检测 let person = { name: 'addone' }; Object.isExtensible(person); // true Object.isSealed(person); // false Object.isFrozen(person); // false Object.freeze(person); Object.isExtensible(person); // false Object.isSealed(person); // true Object.isFrozen(person); // true 复制代码 以上三种方法在严格模式下进行错误操作均会导致抛出错误 高级定时器 阅读前提 大概理解setTimeout的基本执行机制和js事件机制 重复的定时器 当你使用setInterval重复定义多个定时器的时候,可能会出现某个定时器代码在代码再次被添加到执行队列之前还没有完成执行,导致定时器代码连续执行多次。 机智Javascript引擎解决了这个问题,使用setInterval()的时候,仅当没有该定时器的其他代码实例时,才会将定时器代码添加到队列中。但这还会导致一些问题: 某些间隔被跳过 间隔可能比预期的小 为了避免这个两个问题,你可以使用链式setTimeout()调用 setTimeout(function(){ TODO(); setTimeout(arguments.callee, interval); }, interval) 复制代码 arguments.callee获取了当前执行函数的引用,然后为其设置另外一个定时器,这样就确保在下一次定时器代码执行前,必须等待指定的间隔。 Yielding Processes 浏览器对长时间运行的脚本进行了制约,如果代码运行超过特定的时间或者特定语句数量就不会继续执行。 如果你发现某个循环占用了大量的时间,那么对于下面这两个问题 该处理是否必须同步完成? 数据是否必须按顺序完成? 如果你的两个答案都是"否",那么你可以使用一种叫做数组分块(array chunking) 的技术。基本思路是为要处理的项目创建一个队列,然后使用定时器取出下一个要出处理的项目进行处理,然后再设置另一个定时器。 function chunk(array, process, context){ setTimeout(function(){ // 取出下一个项目进行处理 let item = array.shift(); process.call(item); if(array.length > 0){ setTimeout(arguments.callee, 100); } }, 100) } 复制代码 这里接受三个参数,要处理的数组,处理的函数,运行该函数的环境(可选),这里设置间隔100ms是个效果不错的选择 如果你一个函数需要50ms以上时间完成,那么最好看看能否将任务分割成一系列可以使用定时器的小任务 函数节流(Throttle) 节流的目的是防止某些操作执行的太快。比如在调整浏览器大小的时候会出发onresize事件,如果在其内部进行一些DOM操作,这种高频率的更爱可能会使浏览器崩溃。为了避免这种情况,可以采取函数节流的方式。 function throttle(method, context){ clearTimeout(method.tId); method.tId = setTimeout(function(){ method.call(context); }, 100) } 复制代码 这里接受两个参数,要执行的函数,执行的环境。执行时先清除之前的定时器,然后将当前定时器赋值给方法的tId,之后调用call来确定函数的执行环境。 一个应用的例子 function resizeDiv(){ let div = document.getElementById('div'); div.style.height = div.offsetWidth + "px"; } window.onresize = function(){ throttle(resizeDiv); } 复制代码 这个就不用讲了吧2333 文章参考于《JavaScript高级程序设计(第三版)》 如果你觉得我的理解有问题或者整理的太简略,那么我强烈安利你自己去读一下这本书~ 原文发布时间:2018-05-14 原文作者:AddOneG 本文来源掘金如需转载请紧急联系作者

优秀的个人博客,低调大师

Confluence 6 CSS 编辑技巧

开始编辑空间样式表 一个空间的样式表是你开始对 CSS 进行自定义编辑的好的开始。在空间样式表中,包含了你所有可以进行修改的元素。当你对空间样式表进行编辑的时候,空间样式表的修改只会对你修改的空间有效,并且会应用到所有的空间页面中。在空间样式表中创建一些样式,然后进行测试,当没有问题后,你可以将这些样式应用到整个站点上。然后你可以对Confluence 的 CSS 进行更多的自定义包括调整搜索页,主面板和其他的一些整体页面。 使用正确的工具 Confluence 的 CSS 也具有相当的复杂性,Web 开发工具将会帮助你理解页面的样式表是如何被创建的的。针对一些具体的情况,你可能需要查看页面已经存在的源代码。下面的一些免费工具将会帮助你对源代码进行调试和查看。h as the following free applications will allow you to do this. 1. Firebug Firebug,是一个 Firefox 浏览器的插件。这个插件允许你对你页面中元素的样式表进行查看。这个工具对你对当前元素的样式表进行分析非常有效,例如仅仅应用在头部的样式表。 2. Web Developer Web Developer 是一个针对 Firefox 的插件,这个插件允许你在页面中直接对 CSS 进行编辑和创建一个新的设计。 3. CSS Edit CSS 编辑器是一个为 Macintosh 系统编辑使用 CSS 的独立编辑器。这个编辑器能够获取当前所有的 CSS 样式表,然后允许你对这些样式表重新进行编辑。 从简单的元素开始 你可以从一些简单的原始开始编辑,通过这些编辑来看这些修改是如何工作的。当对 CSS 进行修改后,对每一个修改你都应该查看下效果,这样有助于更好的分析和解决问题。请注意一些页面的元素可能比其他页面更适合进行编辑。例如,为页面添加一个 gradient 到工具栏相对于修改整个页面的宽度能够让 CSS 更好的工作,而不会导致页面损坏。编辑合适的静态元素,例如背景图片相对于设计整个节目或者使用 JavaScript 来进行下拉菜单展示就更加精确的表现页面情况(其实也不建议在使用 JavaScript 做过多修改) 。 https://www.cwiki.us/display/CONFLUENCEWIKI/Basic+Styling+Tutorial

优秀的个人博客,低调大师

kafka的topic命名技巧

比如,我们给kafka的topic命名为user_r2p10 表示user这个topic的副本因子(r)是2,分区数(p)是10。 这样后期在写消费者代码的时候,根据topic名称就知道分区有多少个,可以很方便的设置多少个消费者线程。 比如,如下 前期博客,见 Kafka的3节点集群详细启动步骤(Zookeeper是外装) 然后, 创建topic [hadoop@master kafka_2.10-0.9.0.1]$ pwd /home/hadoop/app/kafka_2.10-0.9.0.1 [hadoop@master kafka_2.10-0.9.0.1]$ ll total 128 drwxr-xr-x 3 hadoop hadoop 4096 Jul 27 17:56 bin drwxr-xr-x 2 hadoop hadoop 4096 Jul 27 19:16 config -rw-rw-r-- 1 hadoop hadoop 19520 Jul 28 19:57 kafka.log drwxr-xr-x 2 hadoop hadoop 4096 Jul 27 17:26 libs -rw-r--r-- 1 hadoop hadoop 11358 Feb 12 2016 LICENSE drwxrwxr-x 2 hadoop hadoop 4096 Jul 28 19:56 logs -rw------- 1 hadoop hadoop 67054 Jul 27 23:28 nohup.out -rw-r--r-- 1 hadoop hadoop 162 Feb 12 2016 NOTICE drwxr-xr-x 2 hadoop hadoop 4096 Feb 12 2016 site-docs [hadoop@master kafka_2.10-0.9.0.1]$ bin/kafka-topics.sh --zookeeper master:2181,slave1:2181,slave2:2181 --create --topic gold_log_r2p5 --replication-factor 2 --partitions 5 WARNING: Due to limitations in metric names, topics with a period ('.') or underscore ('_') could collide. To avoid issues it is best to use either, but not both. Created topic "gold_log_r2p5". [hadoop@master kafka_2.10-0.9.0.1]$ [hadoop@master kafka_2.10-0.9.0.1]$

优秀的个人博客,低调大师

在线升级glance镜像技巧

0.背景 我们经常需要更新glance镜像,比如上传的镜像可能有配置问题,或者我们需要对已有的镜像进行软件包升级或者预装一些新的软件包等。当时着手这件事是由于我们对cloud-init进行了大量的patch,需要同步到glance镜像中,如果重新制作镜像然后传输到生产环境再上传,不仅工作非常繁琐,还非常消耗时间,我们希望能够在原来的镜像基础之上直接升级cloud-init。 1.通过快照方式更新镜像 最简单的方式是通过创建一个云主机,然后更新cloud-init后创建快照形成新的镜像(注意删除/var/lib/cloud下的文件,否则cloud-init下次启动时不会重新拉取metadata)。可是我们遇到的问题是,老版本的cloud-init在L版中注入密码和密钥都失败,并且安装的qemu-guest-agent不支持修改密码,因此完全没有办法登录云主机。因此必须另辟蹊径。 2.glance存储后端使用本地文件系统 于是我们想到把镜像挂载到本地文件系统中,然后chroot到镜像文件系统环境中进行升级配置。如果glance镜像使用本地文件系统作为存储后端,镜像格式为raw,挂载镜像就非常简单了: losetup/dev/loop0image.img kpartx-a/dev/loop0 mount/dev/mapper/loop0p1/mnt/image 如果是qcow2格式,需要安装qemu-nbd工具包,并且加载nbd内核模块: modprobenbdmax_part=63 qemu-nbd-c/dev/nbd0image.img mount/dev/nbd0p1/mnt/image 如果镜像文件系统中使用LVM,可以使用以下方法初始化: vgscan vgchange-ay mount/dev/VolGroupName/LogVolName/mnt/image chroot进行后执行相关操作后,卸载镜像文件系统: umount/mnt/image vgchange-anVolGroupName killallqemu-nbd kpartx-d/dev/loop0 losetup-d/dev/loop0 以上方法参考Mounting-raw-and-qcow2-vm-disk-images。 本文重点不在于基于本地文件系统作为存储后端的情况,重点介绍基于Ceph存储后端的操作方法,下文将详细介绍过程。 3.基于Ceph存储后端的更新方法 我们知道使用ceph作为存储后端时,镜像首先会生成一个以snap命名的快照,比如glance有一个镜像id为35fcb79c-43a1-4b59-83d7-f4e46a524419,则在rbd中有对应的image,命名与glance镜像id一致,我们查看其快照: rbdsnapls--imageopenstack-images/35fcb79c-43a1-4b59-83d7-f4e46a5244192>/dev/null 输出: SNAPIDNAMESIZE 94604snap20480MB 我们不能直接修改快照snap,因为它是protect的,只能读不能写。直接修改镜像的话不能同步到快照中,并且出错也不好回滚。 因此我们的做法是先复制一个rbd image副本. 首先使用glance命令行工具获取需要更新的镜像的id: IMAGE_ID=$(glanceimage-list--name"CentOS7.164bit2>/dev/null|awk--re-interval'/\w{8}-.*/{print$2}') 使用rbd命令拷贝一份镜像副本,假设POOL变量为glance使用的ceph池: rbdcp$POOL/${IMAGE_ID}$POOL/${IMAGE_ID}_copy 挂载镜像到本地文件系统中: rbdmap$POOL/${IMAGE_ID}_copy #/dev/rbd0 mount/dev/rbd0p1/mnt 注意由于镜像是Linux文件系统,因此通常只有一个根分区,如果是windows镜像,第一个分区通常是隐藏分区,C盘是第二个分区,因此挂载时注意选择rbd0p2,其中p表示partition。 挂载到本地后,可以拷贝软件包、配置模板等到镜像文件系统中,比如/mnt/root/,然后chroot到镜像文件系统中: cd/mnt#/mnt是我们需要chroot的根目录 #mount-tprocprocproc/#挂载proc #TouseaninternetconnectioninthechrootenvironmentcopyovertheDNSdetails: cp/etc/resolv.confetc/resolv.conf#覆盖即可,生成云主机会自动覆盖 #Tochangerootintoabashshell: chroot/mntbash#chroot到/mnt中,如果提示bash命令找不到,使用绝对路径试试chroot/mnt/bin/bash #初始化环境变量 source/etc/profile source~/.bashrc #如果提示找不到ls等命令,需要手动设置PATH变量:PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/root/bin:/bin #Tip:Optionally,createauniqueprompttobeabletodifferentiateyourchrootenvironment: exportPS1="(chroot)$PS1" 接下来就可以执行命令对镜像进行更新了,比如更新cloud-init: yumremovecloud-init#Removetheoldversionpackage rpm-icloud-init-0.7.6-bzr1.el7.centos.noarch.rpm#Installthepacakge 升级qemu-guest-agent: yuminstall-yqemu-guest-agent rpm-aq|grepqemu-guest-agent #qemu-guest-agent-2.3.0-4.el7.x86_64 执行完所有的更新操作后,完成以下清理工作: yumcleanall rm-rf/root/*#危险!!!删除根目录下拷贝的临时文件,执行前确认无有用文件! exit#退出chroot环境 cd#切换到家目录,工作目录不能位于/mnt,否则卸载不了 umount--recursive/mnt/#如果busy怎么办?lsof查看哪个进程占用,如果还是不行试试使用--lazy参数 rbdunmap/dev/rbd0#从rbd中卸载 最后需要更新glance,我们的方法是先创建一个glance空镜像实例,不需要指定镜像文件,只是占个坑,拿到新镜像id: NEW_IMAGE_ID=`glanceimage-create|grepid|awk'{print$4}'`||{echo'Error:glanceimage-createfailed!';exit1;} 接下来更新我们的rbd image,命名为新创建镜像实例id并设置快照: rbdmv$POOL/${IMAGE_ID}_copy$POOL/${NEW_IMAGE_ID} rbd--pool=$POOL--image=${NEW_IMAGE_ID}--snap=snapsnapcreate rbd--pool=$POOL--image=${NEW_IMAGE_ID}--snap=snapsnapprotect 最后更新glance镜像元数据: glanceimage-update--name="$DISPLAY_NAME"--disk-format=raw--container-format=bare--is-public=True${NEW_IMAGE_ID} glanceimage-update--propertyimage_meta="$image_meta"${NEW_IMAGE_ID} glanceimage-update--propertyhw_qemu_guest_agent=yes$IMAGE_ID #... #updateimagelocation FSID=`ceph-s|grepcluster|awk'{print$2}'` glanceimage-update--locationrbd://$FSID/$POOL/$IMAGE_ID/snap${NEW_IMAGE_ID} if[[$IMAGE_LABEL=="Windows"||$IMAGE_LABEL=="windows"]];then glanceimage-update--propertyhw_video_model=qxl--propertyhw_video_ram=64--propertyos_admin_user=Administrator$IMAGE_ID fi 最后对新镜像进行功能验证,如果没有问题,就可以安全的删除老的镜像了。 作者:付广平 来源:51CTO

优秀的个人博客,低调大师

Android全套动画使用技巧

【引自MrXI的博客】 一、Android View 动画框架 Animation框架定义了透明度、旋转、缩放和位移几种常见的动画,控制的整个View,实现原理是每次绘制视图时View所在ViewGroup中的drawChild函数获取该View的Animation的Transformation值,然后调用canvas.concat(transformToApply.getMatrix()),通过矩阵运算完成动画帧。如果没有完成就继续调用invalidate()函数,启动下次绘制来驱动动画,从而完成整个动画的绘制。 视图动画使用简单,效果丰富,它提供了AlphaAnimation、RotateAnimation、TranslateAnimation、ScaleAnimation四种动画方式,并提供动画集合AnimationSet,混合使用多种动画。在Android3.0之前,视图动画一家独大,但随着Android3.0之后属性动画框架的推出,它的风光就大不如从前。相比属性动画,视图动画的一个非常大的缺陷就是不具备交互性,当某个元素发生视图动画后,其响应事件的位置还依然在动画前的地方,所以视图动画只能做普通的动画效果,避免交互的发生。但是它的优点也非常明显,即效率比较高且使用方便。 视图动画使用非常简单,不仅可以通过XML文件来描述一个动画过程,同样也可以使用代码来控制整个动画过程。 (1)、透明度动画 为视图增加透明度的变换动画。 AlphaAnimationaa=newAlphaAnimation(0,1); aa.setDuration(1000); view.startAnimation(aa); (2)、旋转动画 为视图增加旋转的变换动画。 RotateAnimationra=newRotateAnimation(0,360,100,100); ra.setDuration(1000); view.startAnimation(ra); 其参数分别为旋转的起始角度和旋转中心点的坐标,当然,可以通过设置参数来控制旋转动画的参考系,这里设置旋转动画的参考系为中心。 RotateAnimationra1=newRotateAnimation(0,360,RotateAnimation.RELATIVE_TO_SELF,0.5F,RotateAnimation.RELATIVE_TO_SELF,0.5F); (3)、位移动画 为视图移动时增加位移动画。 TranslateAnimationta=newTranslateAnimation(0,200,0,300); ta.setDuration(1000); view.startAnimation(ta); (4)、缩放动画 为视图的缩放增加动画效果 ScaleAnimationsa=newScaleAnimation(0,2,0,2); sa.setDuration(1000); view.startAnimation(sa); 与旋转动画一样,缩放动画也可以设置罗芳的中心点,设置中心为自身中心效果 ScaleAnimationsa1=newScaleAnimation(0,1,0,1,Animation.RELATIVE_TO_SELF,0.5F,Animation.RELATIVE_TO_SELF,0.5F); sa1.setDuration(1000); view.startAnimation(sa1); (5)、动画集合 通过AnimationSet,可以将动画以组合的形式展现出来: AnimationSetas=newAnimationSet(true); as.setDuration(1000); AlphaAnimationaa=newAlphaAnimation(0,1); aa.setDuration(1000); as.addAnimation(aa); RotateAnimationra=newRotateAnimation(0,360,100,100); ra.setDuration(1000); as.addAnimation(ra); TranslateAnimationta=newTranslateAnimation(0,200,0,300); ta.setDuration(1000); as.addAnimation(ta); ScaleAnimationsa=newScaleAnimation(0,2,0,2); sa.setDuration(1000); as.addAnimation(sa); view.startAnimation(as); 可以直接拷贝运行代码看效果! 对于动画事件,Android也提供了对应的监听回调,代码: as.setAnimationListener(newAnimation.AnimationListener(){ @Override publicvoidonAnimationStart(Animationanimation){ //动画开始 } @Override publicvoidonAnimationEnd(Animationanimation){ //动画结束 } @Override publicvoidonAnimationRepeat(Animationanimation){ //动画重复 } }); 二、属性动画 由于Android3.0之前已有的动画框架Animation存在一些局限性——动画改变的只是显示,并不能响应事件。因此在Android3.0之后,Google就提出了属性动画这样一个新的动画框架,实现更丰富的效果。 而在Animator框架中使用最多的就是AnimatorSet和ObjectAnimator配合,使用ObjectAnimator进行更精细化控制,只控制一个对象的一个属性值,而使用多个ObjectAnimator组合到AnimatorSet形成一个动画。而且ObjectAnimator能够自动驱动,可以调用setFrameDelay(long frameDelay)设置动画帧之间的间隙时间。最重要的是,属性动画通过调用属性的get、set方法来真实地控制了一个View的属性值,因此强大的属性动画框架,基本可以实现所有的动画效果。 (1)、ObjectAnimator ObjectAnimator是属性动画框架中最重要的实行类,创建一个ObjectAnimator只需要通过他的静态工厂类直接返回一个ObjectAnimator对象。参数包括一个对象和对象的属性名字,但这个属性也必须有get和set函数,内部会通过Java反射机制来调用set函数修改对象属性值。同样,你也可以调用setInterpolator设置相应的差值器。 接下来试想一下对一个Button添加一个平移动画,使用以前的动画框架平移后将不能触发点击事件,点击的有效区域仍然是原来的地方,点击移动后的地方是不会有点击事件发生的。而属性动画则不同,它真实地改变了一个View的属性,所以事件响应的区域也同样发生了改变,这时候点击移动后的按钮,就会响应点击事件了。 属性动画平移代码如下: ObjectAnimatoranimator=ObjectAnimator.ofFloat( imageView, "translationX", 200F); animator.setDuration(300); animator.start(); 在使用ObjectAnimator的时候,有一点非常重要,那就是要操纵的属性必须具有get、set方法,不然ObjectAnimator就无法生效。下面是常用的属性: translationX和translationY:这两个属性作为一种增量控制着View对象从它布局容器左上角坐标开始的位置。 rotation、rotationX和rotationY:这个三个属性控制View对象围绕支点进行2D和3D旋转。 scaleX和scaleY:这两个属性控制着View对象围绕他的支点进行2D缩放。 pivotX和pivotY:这两个属性控制着View对象的支点位置,围绕这个支点进行旋转和缩放变换处理。默认情况下,该支点的位置就是View对象的中心点。 x和y:这两个简单实用的属性,描述了View对象在它的容器中的最终位置,它是最初的左上角坐标和translationX、translationY值的累积和。 alpha:表示View对象的alpha透明度。默认值是1(不透明),0代表完全透明(不可见)。 根据以上得知视图动画所实现的动画效果,这里基本都已经包含了。 那么如果一个属性没有get、set方法,属性动画是不是就束手无策了呢?答案是否定的,Google在应用层提供了两种方案来解决这个问题,一个是通过自定义一个属性类或者包装类,来间接地给这个属性增加get、set方法;或者通过ValueAnimator来实现,ValueAnimator在后面的内容中讲到,这个先看看使用包装类的方法给一个属性增加get、set方法,代码如下: privatestaticclassWrapperView{ privateViewmTarget; publicWrapperView(ViewmTarget){ this.mTarget=mTarget; } publicintgetWidth(){ returnmTarget.getLayoutParams().width; } publicvoidsetWidth(intwidth){ mTarget.getLayoutParams().width=width; mTarget.requestLayout(); } } 通过以上代码,就跟一个属性包装了一层,并给它提供了get、set方法。使用时只需要操纵包装类就可以间接调用到get、set方法了,代码如下所示: WrapperViewwrapperView=newWrapperView(view); ObjectAnimator.ofInt(wrapperView,"width",500).setDuration(5000).start(); (2)、PropertyValuesHolder 类似视图动画中的AnimationSet,在属性动画中,如果针对同一个对象的多个属性,要同时作用多种动画,可以使用PropertyValuesHolder来实现。比如平移动画,如果在平移的过程中同时改变X、Y轴的缩放,可以这样实现,代码: PropertyValuesHolderpvh1=PropertyValuesHolder.ofFloat("translationX",300); PropertyValuesHolderpvh2=PropertyValuesHolder.ofFloat("scaleX",1f,0,1f); PropertyValuesHolderpvh3=PropertyValuesHolder.ofFloat("scaleY",1f,0,1f); ObjectAnimator.ofPropertyValuesHolder(pvh1,pvh2,pvh3).setDuration(1000).start(); 在代码中,分别使用PropertyValuesHolder 对象控制translationX、scaleX、scaleY这三个属性,最屌调用ObjectAnimator.ofPropertyValuesHolder方法实现多属性动画的共同作用,整个实现方法非常类似AnimatorSet使用。 (3)、ValueAnimator ValueAnimator在属性动画中占用非常重要的地位,虽然不ObjectAnimator那样耀眼,但它却是属性动画的核心所在,ObjectAnimator也是继承自ValueAnimator。 publicfinalclassObjectAnimatorextendsValueAnimator ValueAnimator本身不提供任何动画效果,它更像一个数值发生器,用来产生具有一定规律的数字,从而让调用者来控制动画的实现过程,ValueAnimator的一般使用方法:通常在ValueAnimator的AnimatorUpdateListener中监听数值的变换,完成动画的变换。 ValueAnimatorvalueAnimator=ValueAnimator.ofFloat(0,100); valueAnimator.setTarget(imageView); valueAnimator.setDuration(1000).start(); valueAnimator.addUpdateListener(newValueAnimator.AnimatorUpdateListener(){ @Override publicvoidonAnimationUpdate(ValueAnimatoranimation){ Floatvalue=(Float)animation.getAnimatedValue(); } }); (4)、动画事件的监听 一个完整的动画具有Start、Repeat、End、Cancel四个过程,通过Android提供了接口,很方便地监听到这四个事件: ObjectAnimatoranim=ObjectAnimator.ofFloat(imageView,"alpha",0.5F); anim.addListener(newAnimator.AnimatorListener(){ @Override publicvoidonAnimationStart(Animatoranimation){ } @Override publicvoidonAnimationEnd(Animatoranimation){ } @Override publicvoidonAnimationCancel(Animatoranimation){ } @Override publicvoidonAnimationRepeat(Animatoranimation){ } }); anim.start(); 大部分的时候只关心onAnimationEnd事件,所以Android也提供了一个AnimatorListenerAdapter来让我们选择必要的事件进行监听: anim.addListener(newAnimatorListenerAdapter(){ @Override publicvoidonAnimationEnd(Animatoranimation){ super.onAnimationEnd(animation); } }); (5)、AnimatorSet 对于一个属性同时作用多个属性动画效果,前面已经使用PropertyValuesHolder实现了这样的效果。而AnimatorSet不仅能实现这样的效果,同时也能实现更为精确的顺序控制。同样是实现上面使用PropertyValuesHolder演示的那个动画效果,如果使用AnimatorSet来实现,那么代码如下: ObjectAnimatorobjectAnimator=ObjectAnimator.ofFloat(imageView,"translationX",300f); ObjectAnimatorobjectAnimator1=ObjectAnimator.ofFloat(imageView,"scaleX",1f,0f,1f); ObjectAnimatorobjectAnimator2=ObjectAnimator.ofFloat(imageView,"scaleY",1f,0f,1f); AnimatorSetanimatorSet=newAnimatorSet(); animatorSet.setDuration(1000); animatorSet.playTogether(objectAnimator,objectAnimator1,objectAnimator2); animatorSet.start(); 在属性动画中,AnimatorSet正是通过playTogether()、playSquentially()、animSet.play().width()、defore()、after()这些方法来控制多个动画的协同工作方式,从而做到对动画播放顺序的精确控制。 (6)、在XML中使用属性动画 属性动画同视图动画一样,也可以直接写在XML文件中,代码: <?xmlversion="1.0"encoding="utf-8"?> <objectAnimatorxmlns:android="http://schemas.android.com/apk/res/android" android:duration="1000" android:propertyName="scaleX" android:valueFrom="1.0" android:valueTo="2.0" android:valueType="floatType"> </objectAnimator> 前提使用XML定义属性动画XML文件一定要放在res/animator/filename.xml文件夹下面才能识别,否则不能识别。发现属性动画与视图动画在XML文件中的写法很相似。在程序中使用: Animatoranim=AnimatorInflater.loadAnimator(this,R.animator.filename); anim.setTarget(view); anim.start(); (7)、View的animate方法 在Android3.0之后,Google给View增加了animate方法来直接驱动属性动画,代码如下: imageView.animate() .alpha(0) .y(300) .setDuration(300) .withStartAction(newRunnable(){ @Override publicvoidrun(){ } }) .withEndAction(newRunnable(){ @Override publicvoidrun(){ runOnUiThread(newRunnable(){ @Override publicvoidrun(){ } }); } }).start(); 三、Android布局动画 布局动画是指作用在ViewGroup上,给ViewGroup增加View时添加一个动画过渡效果。最简单的布局动画是在ViewGroup的XML中,使用如下代码打开布局动画: android:animateLayoutChanges="true" 通过以上设置,当ViewGroup添加到View时,子View会呈现逐渐显示的过渡效果,不过这个效果是Android默认的显示的过渡效果,无法使用自定义动画来替换这个效果。 还可以通过使用LayoutAnimatorController类自定义一个子View的过渡效果,添加一个视图动画,使得子View出现的时候有一个缩放的动画效果,代码: LinearLayoutll=(LinearLayout)findViewById(R.id.ll); ScaleAnimationsa=newScaleAnimation(0,1,0,1); sa.setDuration(2000); //设置布局动画的显示 LayoutAnimationControllerlac=newLayoutAnimationController(sa,0.5f); //设置布局动画 ll.setLayoutAnimation(lac); LayoutAnimationController 的第一个参数,是需要作用的动画,而第二个参数,则是每个子View显示的delay时间。当delay时间不为0时,可以设置子View显示的顺序。 //顺序 publicstaticfinalintORDER_NORMAL=0; //随机 publicstaticfinalintORDER_REVERSE=1; //反序 publicstaticfinalintORDER_RANDOM=2; 四、Interpolators——插值器 插值器是动画一个非常重要的概念,通过插值器Interpolators,可以定义动画变换速率,这一点非常类似物理中的加速度,起作用主要是控制目标变量的变化值进行对应的变化。 AccelerateDecelerateInterpolator 在动画开始与介绍的地方速率改变比较慢,在中间的时候加速 AccelerateInterpolator 在动画开始的地方速率改变比较慢,然后开始加速 AnticipateInterpolator 开始的时候向后然后向前甩 AnticipateOvershootInterpolator 开始的时候向后然后向前甩一定值后返回最后的值 BounceInterpolator 动画结束的时候弹起 CycleInterpolator 动画循环播放特定的次数,速率改变沿着正弦曲线 DecelerateInterpolator 在动画开始的地方快然后慢 LinearInterpolator 以常量速率改变 OvershootInterpolator 向前甩一定值后再回到原来位置 PathInterpolator 路径插值器 未完,待续。。。 五、开源代码库 最后再分享一个自己积攒很久的代码库,只有你想不到,没有用不到的,欢迎star https://github.com/xijiufu 由于github服务器在美国,有时访问很慢,还提供了开源中国地址库,2个仓库代码均同步更新: http://git.oschina.net/xijiufu 作者:MrXI 来源:51CTO

优秀的个人博客,低调大师

Android 开发实用小技巧

转载请标明出处:http://blog.csdn.net/zhaoyanjun6/article/details/75647437 本文出自【赵彦军的博客】 Android Studio 实用插件传送门:http://blog.csdn.net/zhaoyanjun6/article/details/75646540 1、查看依赖包的方法数 网址: http://www.methodscount.com/ 2、在线把 layout 布局文件转为Java代码 网址:http://android.lineten.net/layout.php 3、在线生成 shapes 代码 网址: http://shapes.softartstudio.com/ 4、在线生成 Button 的样式 网址:http://angrytools.com/android/button/ 5、根据 packname 下载 GooglePlay 上的应用 网址: https://apps.evozi.com/apk-downloader/ 个人微信号:zhaoyanjun125 , 欢迎关注

资源下载

更多资源
腾讯云软件源

腾讯云软件源

为解决软件依赖安装时官方源访问速度慢的问题,腾讯云为一些软件搭建了缓存服务。您可以通过使用腾讯云软件源站来提升依赖包的安装速度。为了方便用户自由搭建服务架构,目前腾讯云软件源站支持公网访问和内网访问。

Nacos

Nacos

Nacos /nɑ:kəʊs/ 是 Dynamic Naming and Configuration Service 的首字母简称,一个易于构建 AI Agent 应用的动态服务发现、配置管理和AI智能体管理平台。Nacos 致力于帮助您发现、配置和管理微服务及AI智能体应用。Nacos 提供了一组简单易用的特性集,帮助您快速实现动态服务发现、服务配置、服务元数据、流量管理。Nacos 帮助您更敏捷和容易地构建、交付和管理微服务平台。

Spring

Spring

Spring框架(Spring Framework)是由Rod Johnson于2002年提出的开源Java企业级应用框架,旨在通过使用JavaBean替代传统EJB实现方式降低企业级编程开发的复杂性。该框架基于简单性、可测试性和松耦合性设计理念,提供核心容器、应用上下文、数据访问集成等模块,支持整合Hibernate、Struts等第三方框架,其适用范围不仅限于服务器端开发,绝大多数Java应用均可从中受益。

WebStorm

WebStorm

WebStorm 是jetbrains公司旗下一款JavaScript 开发工具。目前已经被广大中国JS开发者誉为“Web前端开发神器”、“最强大的HTML5编辑器”、“最智能的JavaScript IDE”等。与IntelliJ IDEA同源,继承了IntelliJ IDEA强大的JS部分的功能。

用户登录
用户注册