首页 文章 精选 留言 我的

精选列表

搜索[AI编程],共10000篇文章
优秀的个人博客,低调大师

【Android进阶学习】Http编程之HttpClient

在Android开发中,Android SDK附带了Apache的HttpClient,它是一个完善的客户端。它提供了对HTTP协议的全面支持,可以使用HttpClient的对象来执行HTTP GET和HTTP POST调用。 HTTP工作原理: 1.客户端(一般是指浏览器,这里是指自己写的程序)与服务器建立连接 2.建立连接后,客户端向服务器发送请求 3.服务器接收到请求后,向客户端发送响应信息 4.客户端与服务器断开连接 HttpClient的一般使用步骤: 1.使用DefaultHttpClient类实例化HttpClient对象 2.创建HttpGet或HttpPost对象,将要请求的URL通过构造方法传入HttpGet或HttpPost对象。 3.调用execute方法发送HTTP GET或HTTP POST请求,并返回HttpResponse对象。 4.通过HttpResponse接口的getEntity方法返回响应信息,并进行相应的处理。 最后记得要在AndroidManifest.xml文件添加网络权限 <uses-permission android:name="android.permission.INTERNET" /> 下面是具体的例子: 1.使用HttpClient来执行GET调用 在LogCat窗口就能看到输出的信息 packagecom.lingdududu.http; importjava.io.InputStream; importorg.apache.http.HttpResponse; importorg.apache.http.HttpStatus; importorg.apache.http.client.HttpClient; importorg.apache.http.client.methods.HttpGet; importorg.apache.http.impl.client.DefaultHttpClient; importandroid.app.Activity; importandroid.os.Bundle; importandroid.util.Log; publicclassHttpGetActivityextendsActivity{ Stringuri="http://developer.android.com/"; finalStringTAG_STRING="TAG"; @Override publicvoidonCreate(BundlesavedInstanceState){ super.onCreate(savedInstanceState); setContentView(R.layout.main); try{ //得到HttpClient对象 HttpClientgetClient=newDefaultHttpClient(); //得到HttpGet对象 HttpGetrequest=newHttpGet(uri); //客户端使用GET方式执行请教,获得服务器端的回应response HttpResponseresponse=getClient.execute(request); //判断请求是否成功 if(response.getStatusLine().getStatusCode()==HttpStatus.SC_OK){ Log.i(TAG_STRING,"请求服务器端成功"); //获得输入流 InputStreaminStrem=response.getEntity().getContent(); intresult=inStrem.read(); while(result!=-1){ System.out.print((char)result); result=inStrem.read(); } //关闭输入流 inStrem.close(); }else{ Log.i(TAG_STRING,"请求服务器端失败"); } }catch(Exceptione){ //TODOAuto-generatedcatchblock e.printStackTrace(); } } } 使用HTTP GET调用有一个缺点就是,请求的参数作为URL一部分来传递,以这种方式传递的时候,URL的长度应该在2048个字符之内。如果超出这个这范围,就要使用到HTTP POST调用。 2.使用HttpClient来执行POST调用 使用POST调用进行参数传递时,需要使用NameValuePair来保存要传递的参数。NameValuePair封装了一个键/值组合。另外,还需要设置所使用的字符集。 packagecom.androidbook.services.httppost; importjava.io.BufferedReader; importjava.io.IOException; importjava.io.InputStreamReader; importjava.util.ArrayList; importjava.util.List; importorg.apache.http.HttpResponse; importorg.apache.http.NameValuePair; importorg.apache.http.client.HttpClient; importorg.apache.http.client.entity.UrlEncodedFormEntity; importorg.apache.http.client.methods.HttpPost; importorg.apache.http.impl.client.DefaultHttpClient; importorg.apache.http.message.BasicNameValuePair; importandroid.app.Activity; importandroid.os.Bundle; publicclassHttpPostActivityextendsActivity{ Stringuri="http://developer.android.com/"; @Override publicvoidonCreate(BundlesavedInstanceState){ super.onCreate(savedInstanceState); setContentView(R.layout.main); BufferedReaderin=null; try{ HttpClientclient=newDefaultHttpClient(); HttpPostrequest=newHttpPost("http://code.google.com/android/"); //使用NameValuePair来保存要传递的Post参数 List<NameValuePair>postParameters=newArrayList<NameValuePair>(); //添加要传递的参数 postParameters.add(newBasicNameValuePair("id","12345")); postParameters.add(newBasicNameValuePair("username","dave")); //实例化UrlEncodedFormEntity对象 UrlEncodedFormEntityformEntity=newUrlEncodedFormEntity( postParameters); //使用HttpPost对象来设置UrlEncodedFormEntity的Entity request.setEntity(formEntity); HttpResponseresponse=client.execute(request); in=newBufferedReader( newInputStreamReader( response.getEntity().getContent())); StringBufferstring=newStringBuffer(""); StringlineStr=""; while((lineStr=in.readLine())!=null){ string.append(lineStr+"\n"); } in.close(); StringresultStr=string.toString(); System.out.println(resultStr); }catch(Exceptione){ //Dosomethingaboutexceptions }finally{ if(in!=null){ try{ in.close(); }catch(IOExceptione){ e.printStackTrace(); } } } } } 本文转自 lingdududu 51CTO博客,原文链接: http://blog.51cto.com/liangruijun/803097

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

【Android进阶学习】Http编程之HttpConnection

HttpURLConnection是继承于URLConnection类,二者都是抽象类。其对象主要通过URL的openConnection方法获得的。 下面是具体的代码: packagecom.llingdududu.url; importjava.io.BufferedReader; importjava.io.InputStreamReader; importjava.net.HttpURLConnection; importjava.net.URL; importandroid.app.Activity; importandroid.os.Bundle; publicclassURLConnectionActivityextendsActivity{ StringurlStr="http://developer.android.com/"; /**Calledwhentheactivityisfirstcreated.*/ @Override publicvoidonCreate(BundlesavedInstanceState){ super.onCreate(savedInstanceState); setContentView(R.layout.main); try{ //实例化URL URLurl=newURL(urlStr); //使用HttpURLConnection打开连接 HttpURLConnectionhttpConnection=(HttpURLConnection)url.openConnection(); //得到输入流对象 InputStreamReaderin=newInputStreamReader(httpConnection.getInputStream()); BufferedReaderbufReader=newBufferedReader(in); StringlineStr=""; StringresultStr=""; while((lineStr=bufReader.readLine())!=null){ resultStr+=lineStr+"\n"; } System.out.println(resultStr); //关闭输入流 in.close(); //关闭连接 httpConnection.disconnect(); }catch(Exceptione){ //TODOAuto-generatedcatchblock e.printStackTrace(); } } } 本文转自 lingdududu 51CTO博客,原文链接:http://blog.51cto.com/liangruijun/803159

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

Android编程规范不完全指南

命名规则 1). 类名,接口名: 以大写开头,如果一个类的类名由多个单词组成,所有单词的首字母必须大写,单词尽量写全称,不要简写,除非约定俗成的名字,例如:URL,RTMP,RTSP 这些广泛使用的专有名词,可以全部大写,也可以首字母大写。 例如 HttpRequest,CourseActivity 2). 局部变量,类的成员变量,类的成员函数,函数参数: 以小写字母开头其他的单词首字母大写,变量名不建议使用下划线分隔单词,建议使用驼峰命名法,Android的系统类都采用此方法。 例如 toString() onCreateView(Bundle savedInstanceState) 3). 静态常量:全部大写,单词之间使用下划线分开,常量单词全部大写,所以单词之间使用下划线分隔。 例如 WHAT_EMPTY_CONTENT 4). 控件变量的命名,控件的ID命名: 建议:xml布局文件中的控件的id的命名与*.java的代码文件中的控件对象的命名一致。 classMyActivityextendsActivity{ TextViewtxtUserName; … protectedvoidonCreate(BundlesavedInstanceState){ txtUserName=(TextView)findViewById(R.id.txtUserName); } } 5). 常用控件以及类对象命名的规范说明(红色部分为建议的前缀或者后缀): 类名 变量名 类名 变量名 TextView txtDescription ProgressBar progressDescription Button btnDescription SeekBar seekBarDescription ImageButton imgBtnDescription VideoView vvDescription ImageView imgDescription Spinner spinDescription RadioButton rbDescription WebView webViewDescription EditText editDescription ListView listViewDescription ScrollView scrollDescription GridView gridDescription Handler descriptionHandler RatingBar ratingBarDescription PullToRefreshListView pullRefreshViewDescription Adapter descriptionAdapter Fragment descriptionFragment Activity descriptionActivity List<T> descriptionList Map<> mapDescription SlidingMenu slidMenuDescription ViewPager viewPagerDescription CheckBox chBoxDescription View viewDescription RadioGroup rgDescription ExpandableListView expDescription FrameLayout frameLayDescription SharedPreferences spDescription LinearLayout lineLayDescription RelativeLayout relativeLayDescription startActivityForResult(requestCode) REQUEST_CODE_DESCRIPTION msg.what WHAT_DESCRIPTION 6). 资源命名: layout资源文件的命名(全部小写,下划线分隔): activity的资源文件:activity_description1_description2.xml fragment的资源文件:fragment_description1_description2.xml listview列表项的资源文件:list_item_description1_description2.xml 可复用(被include)的组件资源文件: control_description1_description2.xml drawable资源: controlName_description1_description2_selector.xml controlName表示该资源要用在什么类型的控件上面,例如如果是按钮的图片切换则 应该这么定义 button_bg_sendmessage_selector.xml selector表示该资源的形式,例如还有shape等 图片资源的名字:同上 颜色值的命名: color_description 以color为前缀,全部小写,下划线分隔。description既可以是该颜色值使用的功能描述,也可以是该颜色值的英文描述,也可以是具体的颜色值,例如: <colorname="color_white">#ffffff</color> <colorname="color_grey_ccc">#cccccc</color> <colorname="color_grey_ddd">#dddddd</color> 因为grey可能有很多等级,有时候需要不同等级的灰色,没有那么多英文名可以区分,所以名字中可以直接使用颜色值 <color name=”color_button_pressed”>#4c4c4c</color> 根据功能定义description,表示该颜色用于按钮被按下 注:不允许出现毫无意义的命名,例如textview1,textview2 关于字面常量 代码中不允许出现直接硬编码的字面常量,如果是控件上面显示的文本,必须放在strings.xml资源文件中。 如果是代码中用到常量字符串,必须定义成 public static final String类型的常量值,在代码中使用该定义的常量值。这样做的好处是以后需要修改该常量值,只需要修改一个地方。如果是硬编码在代码中则要修改所有使 用它的地方,而且拷贝容易出错。在Activity之间传递参数的时候,intent.putExtra 的key值也要命名规范,并且统一定义为静态常量,不能直接硬编码在代码中,否则想要修改的时候很麻烦。某一个Activity在被启动的时候需要接受参 数,那么这些参数的key定义就应该放在该Activity中。 JSON解析 Android中调用服务端的接口一般返回的是json数据,在解析json的时候,无论是使用原始的手工解析方式,还是使用javabean的解 析方式,解析出来的结果在使用的时候必须都进行判空处理。不允许因为服务端的json出问题,导致app在解析json的时候出现崩溃。 类成员初始化 所有类的成员变量一定要赋初始值,不允许只定义,不赋值。 Int类型常量 函数返回的时候,如果返回的int类型的数据并不是真实的实用的数据值(例如表示高度,宽度,大小等值),仅仅表示函数执行成功、失败、异常的状态值,并且这些值是有限的几个值,必须要将这些值使用静态常量描述,或者使用枚举,例如: intGetJsonString() 该函数返回-1表示获取解析json数据异常,返回0表示成功,返回1表示网络连接异常,返回2表示json内容中的数据部分为空。那么在函数内部的代码里不要直接使用这些字面值,这些字面值对于程序员来说是毫无意义的,代码可阅读性很差,建议做成下面的模式: publicstaticfinalintRESULT_PARSE_JSON_EXCEPTION=-1; publicstaticfinalintRESULT_SUCCESS=0; publicstaticfinalintRESULT_NETWORK_EXCEPTION=1; publicstaticfinalintRESULT_NO_DATA=2; 使用这些符号常量值代替字面值的好处是,符号常量值是由大写的英文单词组成,是有意义的,可以帮助程序员更好的理解函数返回值的意义,而且符号常量值对应的具体的赋值在后期是很方便修改的。 Activity接受参数与模块化 如果一个Activity可能在多个地方被打开,或者一个Fragment可能在多个地方被用到。那么在设计该Activity和Fragment 的时候一定要考虑低耦合,对外提供统一的参数接口,启 动Activity的过程封装在该Activity类的静态成员方法里面,类似如下: classMyActivityextendsActivity{ ... publicstaticvoidstartActivity(Contextcontext,Paramsparam){ Intentintent=newIntent(context,MyActivity.class); intent.putExtra("param",param); startActivity(intent); } publicstaticvoidstartActivityForResult(Contextcontext,Paramsparam){ Intentintent=newIntent(context,MyActivity.class); intent.putExtra("param",param); startActivityForResult(intent,REQUEST_CODE); } } 参数的传递最好是封装在一个Model实体类中,避免使用Map这种方式进行参数传递。建议该实体类实现为对应的Activity的静态可序列化的内部类。 AndroidStudio工程目录组织 AndroidStudio中的项目的包结构应该根据工程各个部分的功能来组织。 Handler的封装 每一个Activity里面几乎都会定义一个Handler内部类,但是很多Activity里面的Handler都使用了重复的消息类型,这里面 是有冗余代码的,所以应该把这些Activity都使用到的Handler类的消息部分,提取成一个公用的Handler类。然后在各个Activity 里面使用继承的方式,来提供该Activity特有的Handler消息类型的Handler类实现。 另外Handler发送消息应该使用Handler类的成员函数,不应该直接使用handler.obtainMessage(xxx).sendToTarget();这 种原始的发送消息的方式,这样不利于降低耦合,这种细节应该隐藏在Handler内的里面。Handler的消息类型应该定义为Handler类里面的静 态常量,而该常量不应是public的,对外部不可见。也就是说使用handler对象发送消息的细节不应该暴露给外部。 List的数据更新 封装ListView的数据更新,在handlerMessage中更新数据,避免出现 java.lang.IllegalStateException 问题 Activity与Fragment之间传递参数 Activity与Fragment的数据传递采用interface的方式,这样可以降低耦合,有利于Fragment的复用: 网络请求数据模块化 一般在Activity中我们通过网络请求服务端的接口获得数据,这个过程一般是在一个线程中做的,获取到数据之后,再通过Activity中的 handler发送消息来通知Activity更新数据。该负责获取数据的线程类,我们一般都实现为一个Activity的内部类,该类可以直接访问 Activity的成员变量,例如handler,数据列表对象等。但是这样不利于该数据获取线程的复用。如果另一个Activity里面也需要获取相同 的数据,那么这个功能是不能复用的,所以这个负责数据请求的线程类,不应该与具体的Handler和Activity联系过于紧密。应该定义为一个静态 类,handler应该作为参数传递进来,而不是直接访问外部类的成员变量。 封装Log功能 Log功能应该封装成为自动将当前所在类的类名变成log输出的TAG参数,发布的app最好是能循环写日志文件到系统存储中,并且日志文件应该使用反复覆盖的方式重复利用。下面仅仅是一个不完善的例子: publicclassMyLog{ publicstaticfinalStringTAG="myapp"; publicstaticvoidv(Objecto,Stringmessage) { Log.v(TAG+o.getClass().getSimpleName(),message); } } 使用 MyLog.v(this,"hellolog"); 打印结果 V/myappMainActivity﹕hellolog 版本控制 使用自动化版本管理,自动生成版本号,使应用程序的版本与版本库上保持一致。使用hg替换工程目录下的app目录下的build.gradle文件 即可,如果manifest里面也有版本号的设置,AndroidStudio还是以build.gradle为准。不应该在每次发布的时候,在 AndroidStudio的工程设置里面手工修改版本号。 为程序添加全局异常捕获 应该为app添加全局异常捕获,app中总会有一些我们未捕获的异常,一旦用户使用过程中遇到这样的异常,程序就会崩溃,我们应该检测该类未捕获的异常信息,程序崩溃的时候通过写文件日志,或者发送邮件的方式获得异常信息,以便解决bug。 来源:51CTO

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

iOS编程中throttle的那些事

不知道大家对throttle这个单词是否看着眼熟,还是说对这个计算机基础概念有很清晰的了解了。今天就来聊聊和throttle相关的一些技术场景。 定义 我经常有一种感觉,对于英语这门语言的语感,会影响我们对于一些关键技术概念的理解。有时候在学习新技术知识的时候,我会先花一些时间去了解术语英文单词的各种语义,在形成强烈清晰的语感之后,再去深入具体的技术语境。throttle也算是个生僻的单词,至少在口语中毕竟少用到,先来看看词义: a device controlling the flow of fuel or power to an engine. 中文翻译是节流器,一种控制流量的设备。对应到我们计算机世界,可以理解成,一种控制数据或者事件流量大小的机制。这么说可能还是有些抽象,再来看看一些具体的技术场景加深理解。 场景一:GCD Background Queue 话说GCD几乎是iOS面试的必问题,也是个送分题:)。 我一般会机械式的先问:GCD有哪几种Queue?回答:串行Queue和并行Queue。 我继续问:Global Queue有哪几种优先级?回答:有几种吧,大概记得Default,Low,High吧。 我双眉一挑,进一步试探:不知道少侠有没有研究过DISPATCH_QUEUE_PRIORITY_BACKGROUND作何用?问完立即竖起耳朵,殷殷期盼萦绕于心的关键字。如果能听到「I/O Throttle 呀!」,我会瞬间觉得面试气氛被点亮了。 当然啦,答不出I/O Throttle并不能说明技术不扎实,但能答出来,至少表明对待技术是有好奇心的,加分! 官方文档如是说: Items dispatched to the queue run at background priority; the queue is scheduled for execution after all high priority queues have been scheduled and the system runs items on a thread whose priority is set for background status. Such a thread has the lowest priority and any disk I/O is throttled to minimize the impact on the system. 那Disk I/O Throttle做什么用呢?按照上面这段描述,Disk I/O会impact system performance。 理解Disk I/O的影响需要补充一些大学课本上的知识。一次磁盘读写操作涉及到的硬件资源主要有两个,CPU和磁盘。任务本身由CPU触发和调度,读操作发生时,CPU告知Disk去获取某个地址的数据,此时由于Disk的读操作存在寻址延迟,CPU是处于I/O wait状态,一直维持到Disk返回数据为止。处于I/O wait状态的CPU,此时并不能把这部分等待的时间用来处理其他任务,也就是说这一段等待的CPU时间被“浪费”了。而CPU是公共的系统资源,这部分资源的损耗自然会对系统的整体表现产生负面影响。即使Global Queue使用的是子线程,也会造成CPU资源的消耗。 如果把任务的Priority调整为DISPATCH_QUEUE_PRIORITY_BACKGROUND,那么这些任务中的I/O操作就被被控制,虽然具体的控制策略并没有官方文档描述(一种可能的策略是并发的Disk I/O变为串行的),但我们能确认的是,部分I/O操作的启动时间很有可能被适当延迟,把更多的CPU资源腾出来处理其他任务(比如说一些系统资源的调度任务),这样可以让我们的系统更加稳定高效。简而言之,对于重度磁盘I/O依赖的后台任务,如果对实时性要求不高,放到DISPATCH_QUEUE_PRIORITY_BACKGROUND Queue中是个好习惯,对系统更友好。 实际上I/O Throttle还分为好几种,有Disk I/O Throttle,Memory I/O Throttle,和Network I/O Throttle。语义类似只不过场景不同,继续往下看。 场景二:ASIHttpRequest Network Throttle 早几年读ASIHttpRequest源码的时候,读到过一段有意思的代码: - (void)handleNetworkEvent:(CFStreamEventType)type { //... [self performThrottling]; //... } 在AFNetworking中也有类似的代码: /** Throttles request bandwidth by limiting the packet size and adding a delay for each chunk read from the upload stream. When uploading over a 3G or EDGE connection, requests may fail with "request body stream exhausted". Setting a maximum packet size and delay according to the recommended values (`kAFUploadStream3GSuggestedPacketSize` and `kAFUploadStream3GSuggestedDelay`) lowers the risk of the input stream exceeding its allocated bandwidth. Unfortunately, there is no definite way to distinguish between a 3G, EDGE, or LTE connection over `NSURLConnection`. As such, it is not recommended that you throttle bandwidth based solely on network reachability. Instead, you should consider checking for the "request body stream exhausted" in a failure block, and then retrying the request with throttled bandwidth. @param numberOfBytes Maximum packet size, in number of bytes. The default packet size for an input stream is 16kb. @param delay Duration of delay each time a packet is read. By default, no delay is set. */ - (void)throttleBandwidthWithPacketSize:(NSUInteger)numberOfBytes delay:(NSTimeInterval)delay; 原谅我贴了一大段注释,这段英文描述对于加深我们对于一些网络行为的理解很有帮助。 这些知名的第三方网络框架都有对Newtork Throttle的支持,你可能会好奇,我们为什么要对自己发出的网络请求做流量控制,难道不应该尽可能最大限度的利用带宽吗? 此处需要科普一点TCP协议相关的知识。我们通过HTTP请求发送数据的时候,实际上数据是以Packet的形式存在于一个Send Buffer中的,应用层平时感知不到这个Buffer的存在。TCP提供可靠的传输,在弱网环境下,一个Packet一次传输失败的概率会升高,即使一次失败,TCP并不会马上认为请求失败了,而是会继续重试一段时间,同时TCP还保证Packet的有序传输,意味着前面的Packet如果不被ack,后面的Packet就会继续等待,如果我们一次往Send Buffer中写入大量的数据,那么在弱网环境下,排在后面的Packet失败的概率会变高,也就意味着我们HTTP请求失败的几率会变大,类似这样: 大部分时候在应用层写代码的时候,估计不少同学都意识不到Newtork Throttle这种机制的存在,在弱网环境下(丢包率高,带宽低,延迟高)一些HTTP请求(比如上传图片或者日志文件)失败率会激增,有些朋友会觉得这个我们也没办法,毕竟网络辣么差。其实,作为有追求的工程师,我们可以多做一点点,而且弱网下请求的成功率其实是个很值得深入研究的方向。针对弱网场景,我们可以启用Newtork Throttle机制,减小我们一次往Send Buffer中写入的数据量,或者延迟某些请求的发送时间,这样所有的请求在弱网环境下,都能「耐心一点,多等一会」,请求成功率自然也就适当提高啦。 那么,再看AFNetworking中的这个函数,是不是更能理解了呢? - (void)throttleBandwidthWithPacketSize:(NSUInteger)numberOfBytes delay:(NSTimeInterval)delay; Network Throttle体现了一句至理名言「慢即是快」。 场景三:Event Frequency Control 不知道大家在写UI的时候,有没有遇到过用户快速连续点击UIButton,产生多次Touch事件回调的场景。以前机器还没那么快的时候,我在用一些App的时候,时不时会遇到偶尔卡顿,多次点击一个Button,重复Push同一个Controller。有些工程师会在Button的点击事件里记录一个timestamp,然后判断每次点击的时间间隔,间隔过短就忽略,这也不失为一种解决办法。 再后来学习RxSwift的时候,看到: button.rx_tap .throttle(0.5, MainScheduler.instance) .subscribeNext { _ in print("Hello World") } .addDisposableTo(disposeBag) 终于有了优雅的书写方式。发现没有,throttle又出现了,这里throttle控制的是什么呢?不是disk读写,也不是network buffer,而是事件,把事件本身抽象成了一种Data,控制这种数据的流量或者产生频率,就解决了上面我们所说重复点击按钮的问题,so easy。 总结 当然还会有更多的场景,throttle其实是个基础的计算机知识。理解throttle相关的技术概念,需要在不同场景下去抽象出一个flow被节流的画面。现在,如果让你来解释一些具体的技术场景下,throttle是怎么回事,是不是可以信手拈来了: ) 作者:MrPeak杂货铺 来源:51CTO

资源下载

更多资源
Mario

Mario

马里奥是站在游戏界顶峰的超人气多面角色。马里奥靠吃蘑菇成长,特征是大鼻子、头戴帽子、身穿背带裤,还留着胡子。与他的双胞胎兄弟路易基一起,长年担任任天堂的招牌角色。

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部分的功能。

用户登录
用户注册