首页 文章 精选 留言 我的

精选列表

搜索[后台会话],共10006篇文章
优秀的个人博客,低调大师

后台(24)——Filter(1)

探索Android软键盘的疑难杂症 深入探讨Android异步精髓Handler 详解Android主流框架不可或缺的基石 站在源码的肩膀上全解Scroller工作机制 Android多分辨率适配框架(1)— 核心基础 Android多分辨率适配框架(2)— 原理剖析 Android多分辨率适配框架(3)— 使用指南 自定义View系列教程00–推翻自己和过往,重学自定义View 自定义View系列教程01–常用工具介绍 自定义View系列教程02–onMeasure源码详尽分析 自定义View系列教程03–onLayout源码详尽分析 自定义View系列教程04–Draw源码分析及其实践 自定义View系列教程05–示例分析 自定义View系列教程06–详解View的Touch事件处理 自定义View系列教程07–详解ViewGroup分发Touch事件 自定义View系列教程08–滑动冲突的产生及其处理 版权声明 本文原创作者:谷哥的小弟 作者博客地址:http://blog.csdn.net/lfdfhl Filter简介 JavaWeb中的过滤器Filter可以拦截所有访问Web资源(Servlet、JSP、HTML等等)的请求及其响应。Filter本身并不会产生请求和响应对象,它只提供过滤作用;比如:在Servlet被调用之前检查或者修改Request、Request Header、Request中的内容;在Servlet被调用之后检查或者修改Response、Response Header和Response中的内容。通过Filter我们可以实现URL级别的权限访问控制、过滤敏感词汇、压缩响应信息、验证、加密等一些非常实用的功能。 Filter入门示例 Filter的实现步骤如下: 1、创建一个类实现Filter接口 2、实现接口中方法,在doFilter( )中实现过滤 3、在web.xml文件中配置Filter 请看如下示例 先写一个Filter /** * 本文作者:谷哥的小弟 * 博客地址:http://blog.csdn.net/lfdfhl */ package cn.com; import java.io.IOException; import javax.servlet.Filter; import javax.servlet.FilterChain; import javax.servlet.FilterConfig; import javax.servlet.ServletException; import javax.servlet.ServletRequest; import javax.servlet.ServletResponse; public class FilterTest implements Filter{ public FilterTest(){ System.out.println("调用Filter构造方法"); } @Override public void init(FilterConfig filterConfig) throws ServletException { System.out.println("调用Filter的init()方法"); } @Override public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { System.out.println("doFilter()开始过滤"); chain.doFilter(request, response); System.out.println("doFilter()过滤结束"); } @Override public void destroy() { System.out.println("调用Filter的destroy()方法"); } } 再写一个Servlet /** * 本文作者:谷哥的小弟 * 博客地址:http://blog.csdn.net/lfdfhl */ package cn.com; import java.io.IOException; import javax.servlet.ServletException; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; public class HttpServletTest extends HttpServlet { @Override protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { System.out.println("调用HttpServlet的doGet()方法"); } @Override protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { System.out.println("调用HttpServlet的doPost()方法"); } } 最后在web.xml中配置Filter和Servlet <?xml version="1.0" encoding="UTF-8"?> <web-app version="2.5" xmlns="http://java.sun.com/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"> <filter> <filter-name>TestFilter</filter-name> <filter-class>cn.com.FilterTest</filter-class> </filter> <filter-mapping> <filter-name>TestFilter</filter-name> <url-pattern>/*</url-pattern> </filter-mapping> <servlet> <servlet-name>TestServlet</servlet-name> <servlet-class>cn.com.HttpServletTest</servlet-class> </servlet> <servlet-mapping> <servlet-name>TestServlet</servlet-name> <url-pattern>/servlet</url-pattern> </servlet-mapping> <welcome-file-list> <welcome-file>index.jsp</welcome-file> </welcome-file-list> </web-app> 在这里也可看出:Filter的配置方式和Servlet非常类似!不仅如此,Filter的生命周期也与Servlet基本一致: 服务器启动时创建Filter对象并调用init(),该方法只调用一次. 访问的资源路径与Filter的拦截路径匹配时执行Filter中的doFilter()执行过滤(拦截) 当服务器关闭时调用Filter的destroy() 运行后输出结果: 调用Filter构造方法 调用Filter的init()方法 doFilter()开始过滤 调用HttpServlet的doGet()方法 doFilter()过滤结束 嗯哼,看到了吧:浏览器发起的请求会首先进入到Filter中后才进入到Servlet里;当Servlet中的方法执行完成后再次回到Filter。 在该实例中,我们只使用了一个Filter。其实,我们也可以编写多个Filter实现不同的功能,这些Filter将形成一个链条即FilterChain;当浏览器发起请求时,请求会先通过这个链条再到达资源。请注意一个小问题:链条中Filter的调用顺序是由其在web.xml中对应的<filter-mapping>的先后顺序决定的。 FilterConfig 我们知道Servlet中有一个ServletConfig;其实,在Filter也存在一个类似于ServletConfig的东西,它就是FilterConfig。嗯哼,想起来没有?——我们刚才的示例FilterTest中的init( )方法参数是不是一个FilterConfig呢? FilterConfig的作用: 1.获取Filter的名称 2.获取初始化参数 3.获取ServletContext对象 FilterConfig的常用方法: getFilterName( ) getInitParameter(String name) getInitParameterNames( ) getServletContext() Filter配置详解 Filter基本配置介绍 <filter> <filter-name>filter名称</filter-name> <filter-class>filter类全名</filter-class> </filter> <filter-mapping> <filter-name>filter名称</filter-name> <url-pattern>映射路径</url-pattern> </filter-mapping> 关于<url-pattern>的配置 1、完全匹配 必须以”/”开始. 2、目录匹配 必须以”/”开始,以*结束. 3、扩展名匹配 以*.xxx结束. 关于<servlet-name>的配置 可在<filter-mapping>中利用<servlet-name>对servlet进行过滤配置。即用<servlet-name>设置当前Filter拦截哪一个servlet 关于<dispatcher>的配置 可在<filter-mapping>中利用<dispatcher>指明:Filter在资源以什么方式被Servlet容器调用时才进行拦截。<dispatcher>的取值有:REQUEST、FORWARD、ERROR、INCLUDE 可在<filter-mapping>中利用<dispatcher>设置Web资源以什么方式被Servlet容器调用时Filter才进行拦截。<dispatcher>的取值有:REQUEST、FORWARD、ERROR、INCLUDE 1、REQUEST 目标资源被访问或者系统重定向后指向该目标资源时Filter才拦截。这也是默认的值 2、FORWARD 目标资源通过RequestDispatcher.forward(request, response)被调用时Filter才拦截 3、ERROR 目标资源是在系统处理异常时被调用 4、INCLUDE 目标资源通过RequestDispatcher.include(request, response)被调用时Filter才拦截

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

后台(19)——事务Transaction

探索Android软键盘的疑难杂症 深入探讨Android异步精髓Handler 详解Android主流框架不可或缺的基石 站在源码的肩膀上全解Scroller工作机制 Android多分辨率适配框架(1)— 核心基础 Android多分辨率适配框架(2)— 原理剖析 Android多分辨率适配框架(3)— 使用指南 自定义View系列教程00–推翻自己和过往,重学自定义View 自定义View系列教程01–常用工具介绍 自定义View系列教程02–onMeasure源码详尽分析 自定义View系列教程03–onLayout源码详尽分析 自定义View系列教程04–Draw源码分析及其实践 自定义View系列教程05–示例分析 自定义View系列教程06–详解View的Touch事件处理 自定义View系列教程07–详解ViewGroup分发Touch事件 自定义View系列教程08–滑动冲突的产生及其处理 版权声明 本文原创作者:谷哥的小弟 作者博客地址:http://blog.csdn.net/lfdfhl 事务简介 事务(Transaction)是数据库操作中并发控制的基本单位。事务是一个操作序列,这个序列中的所有数据库操作要么都成功执行,要么都不执行,它是一个不可分割的工作单位。 在数据库中与事务相关的操作有: 开启事务:start transaction 提交事务:commit 回滚事务:rollback 事务的特性 原子性 事务是一个不可分割的工作单位,事务中的操作要么都发生,要么都不发生。 一致性 事务必须使数据库从一个一致性状态变换到另外一个一致性状态。比如:两个账户相互转账,那么转账前和转账后的总金额不变。 隔离性 多个用户并发访问数据库时,数据库为每一个用户开启的事务不能被其他事务的操作数据所干扰;即多个并发事务之间要相互隔离。 持久性 事务一旦被提交,它对数据库中数据的改变就是永久性的,即使数据库发生故障也不应该对其有任何影响 事务的四大特性中原子性,一致性,持久性很好理解,在此不再赘述。 下面重点介绍一下事务的隔离级别。 在多个线程中各自开启事务操作数据库中数据时,数据库系统要负责隔离操作,以保证各个线程在获取数据时的准确性。假如,事务不考虑隔离性可能会引发如下问题: 赃读 在一个事务中读取了另一个事务未提交的数据。 不可重复读 在一个事务内读取表中的某些数据,多次读取结果不同;即一个事务读取到了另一个事务提交后的数据。比如:开启事务后利用SQL语句查询数据,得到结果;立即再次查询得到的结果却与刚才不一样。通俗地说,不可重复读就是:自己的事务还没有提交,又读到了其它事务中执行了upate并提交的数据。 虚读 是指在一个事务内读取到了别的事务中执行insert并提交的数据,导致前后读取不一致。 数据库通过设置事务的隔离级别防止以上情况的发生,常用的事务级别有如下几种: 1、READ UNCOMMITTED 将事务级别设置为该级别后,赃读、不可重复读、虚读都有可能发生。 2、READ COMMITTED 将事务级别设置为该级别后,可避免赃读;但是不可重复读、虚读都有可能发生 3、REPEATABLE READ 将事务级别设置为该级别后,避免赃读、不可重复读;但是虚读有可能发生。 4、SERIALIZABLE 将事务级别设置为该级别后,可避免赃读、不可重复读以及虚读。 关于数据库的事务级别,请注意: 事务级别越高那么数据操作越安全但是操作性能越低 在MySQL中可利用SELECT @@TX_ISOLATION查看当前的事务隔离级别 在MySQL中可利用SET TRANSACTION ISOLATION LEVEL XXXXX 更改事务隔离级别 应在开启事务之前设置隔离级别 JDBC中控制事务以及操作事务 利用connection.setTransactionIsolation(int level)设置隔离级别 利用connection.setAutoCommit(false)开启事务 利用connection.commit( )提交事务 利用connection.rollback( )回滚事务

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

后台(17)——JSP(3)

探索Android软键盘的疑难杂症 深入探讨Android异步精髓Handler 详解Android主流框架不可或缺的基石 站在源码的肩膀上全解Scroller工作机制 Android多分辨率适配框架(1)— 核心基础 Android多分辨率适配框架(2)— 原理剖析 Android多分辨率适配框架(3)— 使用指南 自定义View系列教程00–推翻自己和过往,重学自定义View 自定义View系列教程01–常用工具介绍 自定义View系列教程02–onMeasure源码详尽分析 自定义View系列教程03–onLayout源码详尽分析 自定义View系列教程04–Draw源码分析及其实践 自定义View系列教程05–示例分析 自定义View系列教程06–详解View的Touch事件处理 自定义View系列教程07–详解ViewGroup分发Touch事件 自定义View系列教程08–滑动冲突的产生及其处理 版权声明 本文原创作者:谷哥的小弟 作者博客地址:http://blog.csdn.net/lfdfhl 今天我们继续学习JSP中的EL和JSTL。 EL表达式 EL(expression language)表达式是JSP中获取数据的一种规范,它主要用于简化jsp中java代码开发。 利用EL表达式获取数据 我们利用EL表达式获取数据,但是请务必注意它只可以获取存在4个作用域中的数据。 语法格式:${ } 比如,我们在session中存了数据: session.setAttribute(“name”, “波少野结衣”); 那么我们可以利用EL表达式获取存的数据 ${name} 嗯哼,这里使用EL表达式就非常简单了,究其根本是因为${}背后执行的是pageContext.findAttribute( )。 与此类似,如果我们有一个User类,其中有name属性,user是该类的对象。现在将该对象保存到域中后,如果要获取该对象的name可以这么写: ${user.name} 该方式就等效于user.getName()。也就是说在EL表达式中.运算符相当于调了getter方法。同理如果User中有一个Address属性,Address中又有一个city,我们也可以通过该方式获取city的值 ${user.address.city} 还有一个功能与.运算符非常类似的运算符[],但它比.运算符要强大许多,在此不再赘述。 EL表达式的简单运算 我们还可以利用EL表达式做一些简单的运算。 利用empty判断null 它会将空字符串和没有元素的集合都判断为null即返回true,示例如下: ${empty object} EL的三元运算符 请看如下示例: ${empty user ? “未登录”:”已经登录”} EL表达式的隐式对象 在之前的文章中介绍了JSP的内置对象,在此列举几个EL表达式的常用对象 pageContext 对应于JSP页面中的pageContext对象 pageScope 代表page域中用于保存属性的Map对象 requestScope 代表request域中用于保存属性的Map对象 sessionScope 代表session域中用于保存属性的Map对象 applicationScope 代表application域中用于保存属性的Map对象 param 表示一个保存所有请求参数的Map对象 paramValues 表示一个保存了所有请求参数的Map对象,它对于某个请求参数返回一个string类型数组 header 表示一个保存了所有http请求头字段的Map对象 headerValues 表示一个保存了所有http请求头字段的Map对象,返回string类型数组 cookie 表示一个保存了所有cookie的Map对象 initParam 表示一个保存了所有web应用初始化参数的map对象 JSTL JSTL简介 从JSP 1.1规范开始JSP就支持使用自定义标签,使用自定义标签极大地降低了JSP页面的复杂度,同时增强了代码的重用性,所以自定义标签在WEB应用中被广泛使用。故,许多WEB应用厂商都开发出了自己的一套标签库,这导致出现了许多功能相同的标签,令开发人员手忙脚乱,无所适从。为了解决该问题,Apache开发了一套用于解决这些常用问题的自定义标签库,这套标签库被SUN公司定义为标准标签库(The JavaServer Pages Standard Tag Library),简称JSTL。在开发中可使用JSTL实现JSP页面中逻辑处理,如判断、循环等等操作。 添加JSTL <%@ taglib uri=”http://java.sun.com/jsp/jstl/core” prefix=”c” %> 利用taglib指令设置JSTL的路径和前缀名 导入包jstl.jar和standard.jar JSTL常用标签 <c:set>标签 用于设置各种域中的属性 <c:out>标签 用于输出一段文本内容到客户端浏览器 <c:remove>标签 用于删除各种域中的属性 <c:if>标签 用于条件判断 <c:choose>标签 该标签非常类似于Java中的switch语句,用于多个条件选择。它必须与<c:when>和<c:otherwise>一起使用 <c:forEach>标签 用于对集合对象进行循环迭代操作 请看如下示例: <%@ page language="java" import="java.util.*" pageEncoding="UTF-8" %> <%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %> <html> <head> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"> <title>JSTL</title> <style type="text/css"> </style> </head> <body> <!-- 测试set、remove、out --> <c:set var="number" value="9527" scope="session"/> number=<c:out value="${number}"/> <c:remove var="number" scope="session"/> number=<c:out value="${number}" default="没有取到number的值"/> </br> <!-- 测试if --> <c:if test="${9527>1234}"> zxx number is 9527 </c:if> </br> <!-- 测试choose --> <c:set var="size" value="${3}"></c:set> <c:choose> <c:when test="${size==1 }">The size is 1</c:when> <c:when test="${size==2 }">The size is 2</c:when> <c:when test="${size==3 }">The size is 3</c:when> <c:otherwise> The size is other </c:otherwise> </c:choose> </br> <!-- 测试forEach --> <c:forEach var="i" begin="1" end="20" step="3"> ${i}<br/> </c:forEach> </br> <% List list = new ArrayList(); list.add("a"); list.add("b"); list.add("c"); list.add("d"); request.setAttribute("list", list); %> <c:forEach items="${list}" var="listitem"> ${listitem}<br/> </c:forEach> <br/> <!-- 测试forEach --> <% List arrayList = new ArrayList(); arrayList.add("aaa"); arrayList.add("bbb"); arrayList.add("ccc"); arrayList.add("ddd"); arrayList.add("eee"); request.setAttribute("arrayList", arrayList); %> <table border="1"> <tr> <th>数据</th> <th>索引</th> <th>计数</th> <th>是否是第一个</th> <th>是否是最后一个</th> </tr> <c:forEach items="${arrayList}" var="item" varStatus="vs"> <tr> <td>${item}</td> <td>${vs.index}</td> <td>${vs.count}</td> <td>${vs.first}</td> <td>${vs.last}</td> </tr> </c:forEach> </table> </body> </html> 运行后效果如下图所示:

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

后台(16)——JSP(2)

探索Android软键盘的疑难杂症 深入探讨Android异步精髓Handler 详解Android主流框架不可或缺的基石 站在源码的肩膀上全解Scroller工作机制 Android多分辨率适配框架(1)— 核心基础 Android多分辨率适配框架(2)— 原理剖析 Android多分辨率适配框架(3)— 使用指南 自定义View系列教程00–推翻自己和过往,重学自定义View 自定义View系列教程01–常用工具介绍 自定义View系列教程02–onMeasure源码详尽分析 自定义View系列教程03–onLayout源码详尽分析 自定义View系列教程04–Draw源码分析及其实践 自定义View系列教程05–示例分析 自定义View系列教程06–详解View的Touch事件处理 自定义View系列教程07–详解ViewGroup分发Touch事件 自定义View系列教程08–滑动冲突的产生及其处理 版权声明 本文原创作者:谷哥的小弟 作者博客地址:http://blog.csdn.net/lfdfhl JSP语法 在上一篇博客我们说过:JSP中可以书写Java代码也可以放置Html标签,但是从本质上来说JSP就是一个Servlet,所以我们站在Java的角度来看JSP的语法就会简单许多。 可以利用尖括号和百分号的形式在JSP文件中编写脚本 利用<% %>编写java代码,例如: <% Date date=new Date(); out.println(date.toLocaleString()); %> 在该脚本中的代码必须严格遵循Java语法且每行代码以分号结束,而且不能出现其它模板元素。因为JSP引擎在翻译JSP页面中会将该脚本中的Java代码将被原封不动地放到_jspService( )中 利用<%= %>实现输出,例如: <%= 9527+45 %> 等价于out.print(9527+45); 利用<%! %> 表示在类中定义全局成员和静态块 请注意在<%! %>定义的变量和方法和是全局的,它们会成为JSP对应的Servlet里的全局成员和方法。 利用<%– –%>添加JSP注释,例如: <%– 这是JSP注释 –%> 利用<!– –>添加Html注释,例如: <!– 这是网页注释 –> JSP指令 JSP指令是为JSP引擎而设计的,用于告知JSP引擎如何处理JSP页面中代码。 指令的语法格式: <%@ 指令名称 属性1=“属性值1” 属性2=“属性值2”…….%> 在JSP2.0规范中一共定义了三个指令:page、include、taglib。 现在来一起学习这三个指令,就从我们最熟悉的page开始。 page指令 page指令用于定义JSP页面的各种属性,比如在创建Web项目的时候系统会自动帮我们生成一个index.jsp,我们来瞅瞅该文件的第一行: <%@ page language=”java” import=”java.util.*” pageEncoding=”UTF-8”%> 以前看这行代码的时候有点懵懂,现在就清楚了:这就是一个page指令。在该指令中利用language属性指明该jsp文件中是java代码,利用import属性表明为该jsp文件导入了java.util包。说到导包有点人可能有疑惑了:我们没有导入java.lang为什么可以在JSP文件中写一些很基础的代码呢?这是因为系统会自动帮我们导入: import java.lang.*; import javax.servlet.*; import javax.servlet.http.*; import javax.servlet.jsp.*; 嗯哼,我们看看page指令常用的属性有哪些: session: 是否自动创建session对象,默认值是true errorPage: 假若页面中有错误跳转到指定的资源 isErrorPage: 是否创建throwable对象,默认是false contextType: 设置内容类型,等同于response.setContextType() pageEncoding: 告知JSP引擎用什么编码翻译该jsp文件 isELIgnored: 是否支持EL表达式,默认是false include指令 include指令用于把其它资源包含到当前页面中 静态包含: <%@ include file="xxxxxx.jsp" %> 动态包含: <jsp:include page="xxxxxx.jsp"></jsp:include> 请注意这两者的区别: 静态包含:在JSP转义时把两个文件合并 动态包含:在JSP转义时不会合并文件当代码执行到include时才包含另一个文件的内容 使用原则:尽量多采用静态包含 taglib指令 taglib指令用于在JSP页面中导入JSTL标签库。在导入JSTL标签库后就可用标签替换JSP中的java代码。 使用方式: <%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %> JSP的动作 在此介绍JSP常用的动作 <jsp:include> 用于动态地把其它资源包含到当前页面中 <jsp:forward> 用于请求转发 <jsp:param> 用于请求转发时携带参数 请看如下示例: <jsp:forward page="/test.jsp"> <jsp:param value="rose" name="name" /> <jsp:param value="23" name="age" /> </jsp:forward> 在此转发至test.jsp且携带了name和age两个参数 <jsp:useBean> 创建一个对象 <jsp:setProperty> 给对象的属性赋值 <jsp:getProperty> 取出对象的属性值 请看如下示例: <jsp:useBean id="p" class="cn.com.Person"/> <jsp:setProperty property="name" name="p" value="武疼兰"/> <jsp:getProperty property="name" name="p"/> 在此创建了cn.com.Person类的对象p,且为该对象的name属性赋值

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

后台(12)——Servlet(4)

探索Android软键盘的疑难杂症 深入探讨Android异步精髓Handler 详解Android主流框架不可或缺的基石 站在源码的肩膀上全解Scroller工作机制 Android多分辨率适配框架(1)— 核心基础 Android多分辨率适配框架(2)— 原理剖析 Android多分辨率适配框架(3)— 使用指南 自定义View系列教程00–推翻自己和过往,重学自定义View 自定义View系列教程01–常用工具介绍 自定义View系列教程02–onMeasure源码详尽分析 自定义View系列教程03–onLayout源码详尽分析 自定义View系列教程04–Draw源码分析及其实践 自定义View系列教程05–示例分析 自定义View系列教程06–详解View的Touch事件处理 自定义View系列教程07–详解ViewGroup分发Touch事件 自定义View系列教程08–滑动冲突的产生及其处理 版权声明 本文原创作者:谷哥的小弟 作者博客地址:http://blog.csdn.net/lfdfhl 在前几篇博客中我们学习了Tomcat,Http,Servlet,HttpServletResponse在此基础上我们来深入学习Servlet的请求即HttpServletRequest HttpServletRequest的简单使用 HttpServletRequest代表客户端的请求,当客户端通过HTTP协议访问服务器时与请求相关的所有信息都将被封装在这个对象中,所以我们可通过该对象获得客户端的相关信息。 常用的方法如下: getRequestURL():返回客户端发出请求时的完整URL getRequestURI():返回请求行中的资源名部分 getContextPath():返回请求的上下文路径 getQueryString():返回请求行中的参数部分 getParameter():返回参数中的具体值 getRemoteAddr():返回发出请求的客户端的IP地址 getRemoteHost():返回发出请求的客户端的完整主机名 getRemotePort():返回客户端所使用的网络端口号 getMethod():返回客户端使用的请求方式 getHeaderNames():返回此请求包含的所有头的名称 getHeader():返回指定的请求头的值 请看如下示例: /** * 本文作者:谷哥的小弟 * 博客地址:http://blog.csdn.net/lfdfhl */ package cn.com; import java.io.IOException; import java.util.Enumeration; import javax.servlet.ServletException; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; public class TestServlet extends HttpServlet { @Override protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { getRequestInfo(request); getRequestHeader(request); } @Override protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { } //获取与请求相关的信息 private void getRequestInfo(HttpServletRequest request){ String method=request.getMethod(); StringBuffer requestURL = request.getRequestURL(); String requestURI = request.getRequestURI(); String contextPath = request.getContextPath(); String queryString = request.getQueryString(); String name=request.getParameter("name"); String remoteAddr = request.getRemoteAddr(); String remoteHost = request.getRemoteHost(); int remotePort = request.getRemotePort(); System.out.println("----> method="+method); System.out.println("----> requestURL="+requestURL); System.out.println("----> requestURI="+requestURI); System.out.println("----> contextPath="+contextPath); System.out.println("----> queryString="+queryString); System.out.println("----> remoteAddr="+remoteAddr); System.out.println("----> remoteHost="+remoteHost); System.out.println("----> remotePort="+remotePort); } //获取请求的Header中的信息 private void getRequestHeader(HttpServletRequest request){ Enumeration<String> headerNames = request.getHeaderNames(); while(headerNames.hasMoreElements()){ String headKey=headerNames.nextElement(); String headValue=request.getHeader(headKey); System.out.println(headKey+":"+headValue); } } } 代码写好了,我们现在通过浏览器发出请求: http://localhost:8081/TestServlet03/helloServlet?name=lucy 在Console中观察输入结果: —-> method=GET —-> requestURL=http://localhost:8081/TestServlet03/helloServlet —-> requestURI=/TestServlet03/helloServlet —-> contextPath=/TestServlet03 —-> queryString=name=lucy —-> name=lucy —-> remoteAddr=127.0.0.1 —-> remoteHost=127.0.0.1 —-> remotePort=63484 host:localhost:8081 connection:keep-alive cache-control:max-age=0 accept:text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8 upgrade-insecure-requests:1 user-agent:Mozilla/5.0 (Windows NT 10.0;Win64; x64)Chrome/47.0.2526.111 accept-encoding:gzip, deflate, sdch accept-language:zh-CN,zh;q=0.8 嗯哼,结合这些输出结果再回过头看这些方法就能有更好的体会。在此,再次强调一下:请注意getRequestURL()和getRequestURI()以及getContextPath()、getQueryString()这四个方法的区别 getRequestURL()返回的是请求的路径,但是不含请求的参数部分 getRequestURI()返回请求行中的资源名部分,但是不含有请求的主机和端口以及请求的参数部分 getContextPath()返回请求的上下文,即/+项目名称,比如此处的/TestServlet03 在前几篇博客中我们学习了Tomcat,Http,Servlet,HttpServletResponse在此基础上我们来深入学习Servlet的请求即HttpServletRequest HttpServletRequest处理表单请求 在刚才的示例中,我们利用getParameter()获取GET请求中请求参数中的具体值,那么在POST请求时又该怎么获取传递给服务端的值呢?请看如下示例 先准备一个表单: <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"> <html> <head> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"> <title>Welcome</title> </head> <body> <form action="/TestServlet03/form" method="post"> 用 户:<input type="text" name="username"/> <br/><br/> 密 码:<input type="password" name="password"/> <br/><br/> 性 别:<input type="radio" name="gender" value="男" />男 <input type="radio" name="gender" value="女" checked="checked"/>女 <br/><br/> 爱 好:<input type="checkbox" name="hobby" value="摄影"/>摄影 <input type="checkbox" name="hobby" value="唱歌"/>唱歌 <input type="checkbox" name="hobby" value="拍照"/>拍照 <br/><br/> 城 市: <select name="city"> <option>------请选择------</option> <option value="dj">东京</option> <option value="db">大阪</option> <option value="xt">仙台</option> </select> <br/><br/> <input type="submit" value="欢迎注册VIP会员"/> </form> </body> </html> 再实现服务端的代码: /** * 本文作者:谷哥的小弟 * 博客地址:http://blog.csdn.net/lfdfhl */ package cn.com; import java.io.IOException; import java.util.Enumeration; import javax.servlet.ServletException; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; public class TestForm extends HttpServlet{ @Override protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { } @Override protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { testForm1(request); } private void testForm1(HttpServletRequest request)throws ServletException,IOException { request.setCharacterEncoding("UTF-8"); Enumeration<String> parameterNames = request.getParameterNames(); while(parameterNames.hasMoreElements()){ String name = parameterNames.nextElement(); String[] values = request.getParameterValues(name); for(int i=0;values!=null&&i<values.length;i++){ System.out.println("name="+name+" "+"value="+values[i]); } } } } 代码解析如下: 利用setCharacterEncoding()设置编码方式 利用getParameterNames()获取所有请求参数的名称,请参见代码第28行 对于此处的表单POST提交而言传递的参数的名称分别为username,password,gender,hobby,city 获取每个参数对于的值,请参见代码第29-35行 嗯哼,现在我们通过浏览器来发起一个请求: 在Console观察打印信息: name=username value=杉原杏璃 name=password value=123456789 name=gender value=女 name=hobby value=摄影 name=hobby value=拍照 name=city value=dj 在实际开发中,我们可能需要将这些传递给服务端的信息封装成实体,请看如下示例: 先准备一个实体Entity: /** * 本文作者:谷哥的小弟 * 博客地址:http://blog.csdn.net/lfdfhl */ package cn.com; import java.io.Serializable; import java.util.Arrays; public class User implements Serializable{ private String username; private String password; private String gender; private String[] hobby; private String city; public String getUsername() { return username; } public void setUsername(String username) { this.username = username; } public String getPassword() { return password; } public void setPassword(String password) { this.password = password; } public String getGender() { return gender; } public void setGender(String gender) { this.gender = gender; } public String[] getHobby() { return hobby; } public void setHobby(String[] hobby) { this.hobby = hobby; } public String getCity() { return city; } public void setCity(String city) { this.city = city; } @Override public String toString() { return "User [username=" + username + ", password=" + password + ", gender=" + gender + ", hobby=" + Arrays.toString(hobby) + ", city=" + city + "]"; } } 请注意,该实体中字段的名字必须与表单中各个name的值相对应 再来看服务端的代码: private void testForm2(HttpServletRequest request)throws ServletException,IOException { try { request.setCharacterEncoding("UTF-8"); User user = new User(); Map<String,String[]> parameterMap = request.getParameterMap(); for (Map.Entry<String, String[]> map : parameterMap.entrySet()) { String name = map.getKey(); String[] values = map.getValue(); PropertyDescriptor propertyDescriptor = new PropertyDescriptor(name, User.class); Method method = propertyDescriptor.getWriteMethod(); if(values.length==1&&!name.equals("hobby")){ method.invoke(user, values[0]); }else{ method.invoke(user, (Object)values); } } System.out.println("------> "+user); } catch (Exception e) { e.printStackTrace(); } } 代码解析如下: 利用getParameterMap()得到请求中所有的参数及其对应的值,请参见代码第5行 注意:有的参数只有一个值,比如该示例中的username;有的参数会有多个值,比如该示例中的hobby 获取每个参数及其对应的值,请参见代码第7-8行 反射出每个参数在实体中的setter()方法,请参见代码第9-10行 设置每个参数对应的值,请参见代码第11-15行 请求重定向与请求转发的区别 我们知道: 利用response.sendRedirect()实现请求的重定向 利用request.getRequestDispatcher().forward()实现请求的转发 请注意它们的区别: 重定向时浏览器中的地址栏会发生变化,但是请求转发时浏览器中地址栏不会发生改变。从本质上来讲:重定向是两次不同的请求,而请求转发只向服务端的发送了一次请求由服务端的内部实现请求的跳转。 重定向可以跳转到应用的外部,而重定向的范围只能是应用的内部 请求转发可利用request.setAttribute()将数据传递到下一个Servlet且下一个Servlet可用request.getAttribute()取出数据;但是重定向不可以也没有道理这么做。至于为什么,请参考第一点。

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

后台(11)——Servlet(3)

探索Android软键盘的疑难杂症 深入探讨Android异步精髓Handler 详解Android主流框架不可或缺的基石 站在源码的肩膀上全解Scroller工作机制 Android多分辨率适配框架(1)— 核心基础 Android多分辨率适配框架(2)— 原理剖析 Android多分辨率适配框架(3)— 使用指南 自定义View系列教程00–推翻自己和过往,重学自定义View 自定义View系列教程01–常用工具介绍 自定义View系列教程02–onMeasure源码详尽分析 自定义View系列教程03–onLayout源码详尽分析 自定义View系列教程04–Draw源码分析及其实践 自定义View系列教程05–示例分析 自定义View系列教程06–详解View的Touch事件处理 自定义View系列教程07–详解ViewGroup分发Touch事件 自定义View系列教程08–滑动冲突的产生及其处理 版权声明 本文原创作者:谷哥的小弟 作者博客地址:http://blog.csdn.net/lfdfhl 在前几篇博客中我们已经对Tomcat,Http,Servlet等方面有了简单的接触,在此基础上我们来深入学习Servlet的响应即HttpServletResponse. Request、Response简介 Web服务器接收到客户端的http请求后会针对每一次请求创建用于代表请求的request对象以及代表响应的response对象。故,若要获取客户机提交的数据,只需要处理request对象就行;若要向客户端输出数据,只需要处理response对象即可。 HttpServletResponse的简单使用 在此以HttpSerlet为例,介绍HttpServletResponse的使用方式,请看如下示例: /** * 本文作者:谷哥的小弟 * 博客地址:http://blog.csdn.net/lfdfhl */ public class TestServlet extends HttpServlet { @Override protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { response.setCharacterEncoding("GBK"); response.setContentType("text/html;chartset=GBK"); PrintWriter writer = response.getWriter(); writer.write("佐佐木希写真全集"); response.addHeader("Pragma", "no-cache"); response.setHeader("Cache-Control", "no-cache"); response.setHeader("Expires", "0"); response.setHeader("refresh", "10;url=http://blog.csdn.net/lfdfhl"); } @Override protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { } } 在这段代码中对Get请求做出响应,代码解析如下: 设置将发送到客户端的响应内容的字符编码,请参见代码第9行 在此将字符编码设置为GBK 设置将发送到客户端的响应内容的类型,请参见代码第10行 在此将内容的类型设置为text/html;chartset=GBK 设置客户端不缓存,请参见代码第13-15行 设置客户端在几秒钟后自动跳转,请参见代码第16行 亦可利用response.setContentType()设定发送到客户端的响应内容的类型,即: response.setContentType(“text/html;chartset=GBK”); 与此类似,也可设置重定向 response.setStatus(302); response.setHeader(“location”,”http://blog.csdn.net/lfdfhl“); 或者利用response.sendRedirect( )设置重定向 response.sendRedirect(“http://blog.csdn.net/lfdfhl“); 同理,我们可用ServletOutputStream向客户端返回字节流 response.setCharacterEncoding("GBK"); response.setContentType("text/html;chartset=GBK"); ServletOutputStream outputStream = response.getOutputStream(); outputStream.write("大泽玛利亚摄影写真".getBytes("GBK")); HttpServletResponse返回下载文件 刚才的示例中,我们只是返回了简单的文字,现在我们来返回一张照片给客户端,请看如下示例: /** * 本文作者:谷哥的小弟 * 博客地址:http://blog.csdn.net/lfdfhl */ package cn.com; import java.io.FileInputStream; import java.io.IOException; import javax.servlet.ServletException; import javax.servlet.ServletOutputStream; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; public class TestResponsePhoto extends HttpServlet{ @Override protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { String path=getServletContext().getRealPath("/WEB-INF/ZXM.jpg"); FileInputStream fileInputStream = new FileInputStream(path); ServletOutputStream outputStream = response.getOutputStream(); int len=0; byte [] b=new byte[1024]; while((len=fileInputStream.read(b))!=-1){ outputStream.write(b, 0, len); } outputStream.close(); fileInputStream.close(); } @Override protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { } } 代码解析如下: 得到文件路径,请参见代码第20行 得到文件输入流,请参见代码第21行 从response中获取到ServletOutputStream作为输出流,请参见代码第22行 将图片返回给客户端,请参见代码第23-29行 运行后,效果如下图所示: 嗯哼,看到了吧:浏览器中显示了从服务端获取的我女朋友的照片 但是,如果想让用户下载这张照片该怎么办呢?请看如下示例: /** * 本文作者:谷哥的小弟 * 博客地址:http://blog.csdn.net/lfdfhl */ package cn.com; import java.io.FileInputStream; import java.io.IOException; import java.net.URLEncoder; import javax.servlet.ServletException; import javax.servlet.ServletOutputStream; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; public class DownPhoto extends HttpServlet { @Override protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { String path=getServletContext().getRealPath("/WEB-INF/zxm.jpg"); System.out.println("-----------------> path="+path); String fileName=path.substring(path.lastIndexOf("\\")+1); fileName=URLEncoder.encode(fileName, "UTF-8"); response.setHeader("content-disposition", "attachment;filename="+fileName); response.setHeader("content-type", "image/jpeg"); FileInputStream fileInputStream = new FileInputStream(path); ServletOutputStream outputStream = response.getOutputStream(); int len=0; byte [] b=new byte[1024]; while((len=fileInputStream.read(b))!=-1){ outputStream.write(b, 0, len); } outputStream.close(); fileInputStream.close(); } @Override protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { super.doPost(req, resp); } } 代码解析如下: 获取到文件名称,请参见代码第23行 设置Header指示客户端下载文件,请参见代码第24行 利用Header设置响应的内容的类型 嗯哼,待用户输入地址后即可下载对应的文件。 除此以外,我们可利用画笔在内存中创作一张图片BufferedImage然后再返回给客户端;有的网站登录时的验证码正是采用该方式实现的。 HttpServletResponse的几个细节问题 getOutputStream()和getWriter()分别用于得到输出字节流和输出字符流的ServletOuputStream、Printwriter。但是请注意:getOutputStream()和getWriter()这两个方法互相排斥,调用了其中任意一个方法后就不能再调用另一方法;否则系统抛出异常。 在Servlet中向ServletOutputStream对象或PrintWriter对象写入的数据将被Servlet引擎(如Tomcat)从response里面获取,然后Servlet引擎再将这些数据当作响应消息的正文且与响应状态行和响应头组合并输出到客户端。这也就是说:并不是response将响应消息传递给了客户端,而是Servlet引擎将响应消息封装到了response中再由Servlet引擎传递给客户端 Serlvet的service()结束后,Servlet引擎将检查getWriter()或getOutputStream()返回的输出流对象是否已经调用过close();如果未关闭则Servlet引擎将调用close()关闭该流对象,如果已经关闭则不作处理

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

后台(10)——Servlet(2)

探索Android软键盘的疑难杂症 深入探讨Android异步精髓Handler 详解Android主流框架不可或缺的基石 站在源码的肩膀上全解Scroller工作机制 Android多分辨率适配框架(1)— 核心基础 Android多分辨率适配框架(2)— 原理剖析 Android多分辨率适配框架(3)— 使用指南 自定义View系列教程00–推翻自己和过往,重学自定义View 自定义View系列教程01–常用工具介绍 自定义View系列教程02–onMeasure源码详尽分析 自定义View系列教程03–onLayout源码详尽分析 自定义View系列教程04–Draw源码分析及其实践 自定义View系列教程05–示例分析 自定义View系列教程06–详解View的Touch事件处理 自定义View系列教程07–详解ViewGroup分发Touch事件 自定义View系列教程08–滑动冲突的产生及其处理 版权声明 本文原创作者:谷哥的小弟 作者博客地址:http://blog.csdn.net/lfdfhl 在上一篇博客中,我们已经对Servlet有了基础的简单的了解,在深入认识Servlet之前,我们来一起学习HTTP协议。 HTTP简介 HTTP协议(HyperText Transfer Protocol)又叫作超文本传输协议,它是用于从服务器传输超文本到浏览器的传送协议。HTTP不仅保证计算机正确、高效、快速地传输超文本文档还可确定传输文档中的哪一部分内容优先显示。 HTTP是一个应用层协议,一次HTTP通信由请求和响应构成。客户端向服务器发送一个请求,在该请求中包含:请求的方法、URL、用户信息、协议版本、等等信息。服务器在接收到请求后发对此作出响应并返回一个答复消息,该消息包括:响应的时间、请求成功或失败的标识、协议的版本、回复的内容等等信息。 HTTP的特点 标准的HTTP协议是无连接的,无状态的。无连接指的是:每次连接只处理一个请求。服务器处理完客户的请求并收到客户的应答后即断开连接。无状态指的是:协议对于请求的处理没有记忆能力,也就是说每次请求都是独立的,它的执行结果与之前和之后的请求是无直接关系的 标准HTTP协议既有优点也有缺点:优点在于极大地减轻了服务器的压力,每一次请求不会造成不必要连接占用;缺点在于繁复地建立连接和断开连接,请求会传输大量重复的信息,所以随后出现了Keep-Alive、Cookie、Session在某些程度上弥补了标准HTTP的缺点。 HTTP的请求与响应 我们知道:每一次HTTP的请求都由请求(request)和响应(response)构成,在此对这两部分作进一步的说明。 HTTP的请求 HTTP请求由三部分组成:请求消息行、请求消息头、请求消息体 请求消息行 示例如下: GET /HelloWeb/test.html HTTP/1.1 请求消息行包含三部分内容: 请求方式,比如此处的GET 资源URI,比如此处的/HelloWeb/test.html 协议及其版本号,比如此处的HTTP/1.1 请求消息头 示例如下: Host:localhost Accept: text/html,application/xhtml+xml,application/xml;q=0.9,/;q=0.8 Accept-Charset: UTF-8 Accept-Encoding: gzip, deflate, sdch Accept-Language: zh-CN,zh;q=0.8,zh-TW;q=0.6 Cache-Control: max-age=0 Cookie:JSESSIONID=97C4302F3350676F65B5E4409ABA87CF Referer: http://blog.csdn.net/lfdfhl Content-Type:application/x-www-form-urlencoded If-Modified-Since: Wed, 02 Feb 2011 12:04:56 GMT User-Agent: Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/44.0.2403.130 Safari/537.36 Connection: keep-alive Date: Tue, 15 Nov 2017 01:12:31 GMT 请求消息头中内容较多,现将常用和重要的字段介绍如下: Host:发起请求的主机名及其端口号 Accept:客户端可接收的文档类型。其中,*/*表示可接收任何类型 Accept-Charset: 客户端可接受的字符编码集 Accept-Encoding: 客户端支持的压缩格式 Accept-Language:客户端可接受的语言 Cache-Control:设置请求和响应遵循的缓存机制 Cookie:HTTP请求发送时会把保存在该请求域名下的所有cookie值一起发送至服务器。 Referrer:发起HTTP请求的页面的URL Content-Type:请求时所传递的内容的类型 Content-Length:请求时所传递的内容的长度 If-Modified-Since:该值与响应消息头的Last-Modified字段相对应。Last-Modified与If-Modified-Since都是用来记录页面的最后修改时间。当客户端访问页面时,服务器会将页面最后修改时间通过Last-Modified由服务器发往客户端,客户端会记录下该值;当客户端再次发起请求时,客户端会通过 If-Modified-Since将此前服务器端发过来的最后修改时间戳发送回去;服务器通过这个时间戳判断客户端的页面是否是最新的,如果不是最新的则返回新的内容,如果是最新的则返回304告诉客户端其本地cache的页面是最新的,于是客户端就可直接从本地加载页面。 User-Agent:表示发出该请求的客户端的相关信息,比如操作系统及版本、CPU类型、浏览器及版本、浏览器渲染引擎、浏览器语言等等 Connection: 表示是否需要持久连接,HTTP 1.1默认进行持久连接。 Date: 请求发送的日期和时间 Range:用于告知服务器只需返回部分内容,故用该Range描述这部分内容的范围;该字段对于断点续传等功能非常实用 其实,从这里可以看出来:在每次请求时,客户端需要告诉服务器:自身支持的字符集,压缩格式,语言;请求的消息内容,长度等等众多信息,从而尽量清楚地表达自身的条件和需求以便服务器做出相应的响应。 请求消息体 username=大泽玛利亚&hobby=摄影 当请求方式是POST方式时可见消息正文,假若是GET方式则请求消息体中无内容 HTTP的响应 HTTP响应由三部分组成:响应消息行、响应消息头、响应消息体 响应消息行 HTTP/1.1 200 OK 响应消息行由三部分组成: 协议及其版本号,比如此处的 HTTP/1.1 响应状态码,比如此处的200 常用的状态码有:200(正常)、02/307(临时重定向)、304(未修改)表示客户端缓存的版本是最新的,客户端无需到服务器请求、404(找不到)、500(服务器内部错误) 响应码的描述,比如此处的OK 响应消息头 Accept-Ranges: bytes Content-Encoding: gzip Content-Length: 4533 Content-Language: en,zh Content-Type: text/html Date: Sun, 06 Sep 2015 07:56:07 GMT Last-Modified: Fri, 04 Sep 2015 13:37:55 GMT Server: apache tomcat Location: http://blog.csdn.net/lfdfhl Refresh: 5; url=http://www.csdn.net/ Content-Disposition: attachment; filename=test.zip Set-Cookie: UserID=tom; Max-Age=3600; Version=1 Expires: Thu, 01 Dec 2015 12:20:00 GMT Cache-Control: no-cache Pragma: no-cache Connection: keep-alive 响应消息头中内容较多,现将常用和重要的字段介绍如下: Accept-Ranges: 表明服务器是否支持指定范围请求及哪种类型的分段请求。假若服务器想告诉浏览器不要使用Range,则返回Accept-Range: none;假若服务器想告诉浏览器可使用以byte为单位的Range的请求,则返回Accept-Range:bytes Content-Encoding: 服务器的返回内容的压缩编码类型 Content-Length: 服务器的返回内容的的长度 Content-Language: 服务器的返回内容的语言 Content-Type: 服务器的返回内容的类型 Date:服务器发出响应的时间 Last-Modified: 返回的内容的最后修改时间 Server: 服务器的类型 Location:指示被请求的资源的位置,常和请求消息头一起使用完成请求重定向 Refresh:延时跳转到某个页面或者定时刷新本页面 Content-Disposition: 指示客户端下载文件 Set-Cookie: 服务器端发送给客户端的Cookie Expires: 响应过期的时间 Cache-Control: 指定响应遵循的缓存机制 Pragma: 用来包含实现特定的指令,最常用的是Pragma:no-cache。在HTTP/1.1协议中,它的含义和Cache-Control:no-cache相同 Connection: 表示是否需要持久连接,HTTP 1.1默认进行持久连接。 响应消息体 响应消息体中表示响应的的具体内容。比如,响应的内容是网页,那么响应消息体的内容和网页右键”查看网页源代码”所看到的内容一样。 后会有期 在此简单地介绍了HTTP协议,但也不会如此仓促地完结。我们将在后续的博客中结合Servlet对与HTTP紧密相关的Cookie和Session进行详细的学习,再会。

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

后台(05)——MySQL(5)

探索Android软键盘的疑难杂症 深入探讨Android异步精髓Handler 详解Android主流框架不可或缺的基石 站在源码的肩膀上全解Scroller工作机制 Android多分辨率适配框架(1)— 核心基础 Android多分辨率适配框架(2)— 原理剖析 Android多分辨率适配框架(3)— 使用指南 自定义View系列教程00–推翻自己和过往,重学自定义View 自定义View系列教程01–常用工具介绍 自定义View系列教程02–onMeasure源码详尽分析 自定义View系列教程03–onLayout源码详尽分析 自定义View系列教程04–Draw源码分析及其实践 自定义View系列教程05–示例分析 自定义View系列教程06–详解View的Touch事件处理 自定义View系列教程07–详解ViewGroup分发Touch事件 自定义View系列教程08–滑动冲突的产生及其处理 版权声明 本文原创作者:谷哥的小弟 作者博客地址:http://blog.csdn.net/lfdfhl 本文简介 在之前的博客中我们学习了MySQL的DDL、DML、DCL、DQL;还有数据的完整性、多表查询。在这篇博客中我们将一起学习MySQL中的函数、MySQL数据库的备份与恢复。 MySQL中的函数 MySQL提供了不少自带的函数,在此简要介绍,时间函数,字符串函数,数学函数。 时间函数 SELECT NOW(); SELECT DAY (NOW()); SELECT DATE (NOW()); SELECT TIME (NOW()); SELECT YEAR (NOW()); SELECT MONTH (NOW()); SELECT CURRENT_DATE(); SELECT CURRENT_TIME(); SELECT CURRENT_TIMESTAMP(); SELECT ADDTIME('14:23:12','01:02:01'); SELECT DATE_ADD(NOW(),INTERVAL 1 DAY); SELECT DATE_ADD(NOW(),INTERVAL 1 MONTH); SELECT DATE_SUB(NOW(),INTERVAL 1 DAY); SELECT DATE_SUB(NOW(),INTERVAL 1 MONTH); SELECT DATEDIFF('2013-12-30','2013-12-20'); 这些时间函数与Java中Date类提供的函数非常类似,故在此不再赘述,欲知其意观其名即可。 字符串函数 SELECT CONCAT ('星爷','9527'); SELECT INSTR ('星爷9527','9527'); SELECT LENGTH('本文作者:http://blog.csdn.net/lfdfhl'); 这些字符串函数与Java中String类提供的函数非常类似,故在此不再赘述,欲知其意观其名即可。 数学函数 SELECT BIN(2); SELECT ABS(-136); SELECT FLOOR(3.14); SELECT CEILING(3.14); 这些数学函数与Java中Math类提供的函数非常类似,故在此不再赘述,欲知其意观其名即可。 数据库的备份与恢复 在此简要介绍MySQL数据库的备份与恢复 MySQL数据库的备份 在DOS中使用mysqldump命令可以生成指定数据库的脚本文本,语法如下: mysqldump –u用户名 –p密码 数据库名>生成的脚本文件路径 但是请注意:脚本文本中只包含数据库的内容,而不存在创建数据库的语句!所以在恢复数据时需要自已手动创建一个数据库,然后再去恢复数据。 在此,备份这几天使用的数据库db1到D盘: C:\Users\lf>mysqldump -uroot -proot db1>d:\db1.sql 即下图所示: MySQL数据库的恢复 在DOS中使用source命令可执行数据库的恢复,语法如下: source 数据库脚本文件路径 再次提醒: 在恢复之前必须先创建数据库或者使用已有的数据库 在此,将刚才导出的数据库脚本恢复到新的数据库db2: mysql> create database db2; mysql> use db2; mysql> source d:\db1.sql 也可参见下图: 至此,关于MySQL的基础知识就全部结束完了,谢谢。

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

后台(02)——MySQL(2)

探索Android软键盘的疑难杂症 深入探讨Android异步精髓Handler 详解Android主流框架不可或缺的基石 站在源码的肩膀上全解Scroller工作机制 Android多分辨率适配框架(1)— 核心基础 Android多分辨率适配框架(2)— 原理剖析 Android多分辨率适配框架(3)— 使用指南 自定义View系列教程00–推翻自己和过往,重学自定义View 自定义View系列教程01–常用工具介绍 自定义View系列教程02–onMeasure源码详尽分析 自定义View系列教程03–onLayout源码详尽分析 自定义View系列教程04–Draw源码分析及其实践 自定义View系列教程05–示例分析 自定义View系列教程06–详解View的Touch事件处理 自定义View系列教程07–详解ViewGroup分发Touch事件 自定义View系列教程08–滑动冲突的产生及其处理 版权声明 本文原创作者:谷哥的小弟 作者博客地址:http://blog.csdn.net/lfdfhl SQL简述 Structure Query Language(结构化查询语言)简称SQL,它被美国国家标准局(ANSI)确定为关系型数据库语言的美国标准,后被国际化标准组织(ISO)采纳为关系数据库语言的国际标准。数据库管理系统可以通过SQL管理数据库;定义和操作数据,维护数据的完整性和安全性 SQL的优点 简单易学,具有很强的操作性 绝大多数重要的数据库管理系统均支持SQL 高度非过程化;用SQL操作数据库时大部分的工作由DBMS自动完成 SQL的分类 DDL(Data Definition Language) 数据定义语言,用来操作数据库、表、列等; 常用语句:CREATE、 ALTER、DROP DML(Data Manipulation Language) 数据操作语言,用来操作数据库中表里的数据; 常用语句:INSERT、 UPDATE、 DELETE DCL(Data Control Language) 数据控制语言,用来操作访问权限和安全级别; 常用语句:GRANT、DENY DQL(Data Query Language) 数据查询语言,用来查询数据 常用语句:SELECT 在此,请注意区分DDL和DML: 它们两者的作用对象和级别不一样。DDL操作的对象是数据库,或者一张表,或者表中的某列;DML操作的是表里的数据。 在了解完这些基础知识之后,我们来依次详细学习DDL、DML、DCL、DQL DDL 创建数据库 create database db1; 创建名字为db1的数据库 show create database db1; 查看刚才创建的数据库db1,结果如下: | Database | Create Database | db1 | CREATE DATABASE db1 /!40100 DEFAULT CHARACTER SET utf8 /** 从这里可以看出,刚才创建的数据库采用的是utf8作为字符集。 当然,我们还可以在创建数据库时为其指定字符集 create database db2 character set gbk; 查询数据库 show databases; 查询出所有的数据库 删除数据库 drop database db2; 其实,从这也可以看出来创建数据库和删除数据库是非常类似的,只不过它们的关键是不同;前者使用的是create后者是drop 修改数据库 alter database db1 character set gbk; 将数据库db1的字符集修改为gbk 切换数据库 use db1; 表示现在开始使用数据库db1 查看当前使用的数据库 select database(); 请注意该语句最后有个括号() 创建表 语法如下: create table 表名( 字段1 字段类型, 字段2 字段类型, …………… 字段n 字段类型 ); 示例,创建一张员工表: 在此创建一张员工表,包含了员工的基本信息。 查看当前数据库中的所有表 show tables; 查看刚才创建的表 show create table employee; 查看表的字段信息 describe employee; 也可以简写为: desc employee; 修改表的字符集 alter table employee character set gbk; 为表增加列 alter table employee add column photo blob; 为表添加了数据类型为blob的列photo 也可以简写为: alter table employee add photo blob; 修改列的长度 alter table employee modify job varchar(170); 将表中job字段的长度修改为170 修改列的名称 alter table employee change name username varchar(100); 将表中原name改名为username 删除列 alter table employee drop photo; 删除表中的photo列 修改表的名字 rename table employee to user; 删除表 drop table user; DML DML用于对表中的数据进行增、删、改的操作。常用的语句有INSERT 、UPDATE、 DELETE 嗯哼,我们先创建一张表 create table employee( id int, username varchar(100), gender varchar(10), birthday date, salary float(10,2), entry_date date, resume text ); 插入操作:INSERT INSERT的语法: INSERT INTO 表名(列名1,列名2 …)VALUES(列值1,列值2…); 请注意: 列名与列值的类型、个数、顺序要一一对应 列值不要超出列定义的长度 如果插入空值,请使用null 插入的日期和字符需用引号括起来,例如:’sun’、’2016-09-04’ INSERT的示例: INSERT INTO employee (id,username,gender,birthday,salary,entry_date,RESUME) VALUES (1,’大泽玛利亚’,’female’,’1990-09-12’,20000.00,’2010-11-22’,’beauty’); INSERT INTO employee (id,username,gender,birthday,salary,entry_date,RESUME) VALUES (2,’武藤兰姐姐’,’female’,’1980-09-12’,30000.00,’2000-12-12’,’good’); INSERT INTO employee (id,username,gender,birthday,salary,entry_date,RESUME) VALUES (3,’井空苍妹妹’,’female’,’1987-07-28’,50000.00,’2001-08-09’,’great’); 当然,也可以批量插入数据: INSERT INTO employee VALUES (4,’杉原杏璃’,’female’,’1992-04-01’,60000.00,’2014-09-03’,’very good’), (5,’佐佐木希’,’female’,’1993-05-09’,70000.00,’2013-04-04’,’very nice’), (6,’伊藤梅子’,’female’,’1995-11-07’,80000.00,’2012-03-05’,’very sexy’); 修改操作:UPDATE UPDATE的语法: UPDATE 表名 SET 列名1=列值1 , 列名2=列值2…..WHERE 列名=值 UPDATE的示例: UPDATE employee SET salary=88000; 将表中所有员工的salary修改为88000 UPDATE employee SET salary=99000 WHERE username=’大泽玛利亚’; 将表中username为大泽玛利亚的员工的salary修改为99000 UPDATE employee SET salary=69000,birthday=’1993-02-28’ WHERE username=’井空苍妹妹’; 将表中username为井空苍妹妹的员工的salary修改为69000、birthday修改为1993-02-28 UPDATE employee SET salary=salary+1000 WHERE username=’佐佐木希’; 将表中username为佐佐木希的员工的salary增加1000 删除操作:DELETE DELETE的语法: DELETE FROM 表名 WHERE 列名=值 DELETE的示例: DELETE FROM employee WHERE username=’大泽玛利亚’; 将表中username为大泽玛利亚的员工删除 DELETE FROM employee; 使用DELETE将表中所有数据删除,执行该操作后表结构还存在,删除后的数据可以找回 TRUNCATE TABLE employee; 使用TRUNCATE将表中所有数据删除,执行该操作后系统将该表DROP然后再创建一个同样的新表,其执行速度远快于DELETE;但是请注意:该方式删除的数据不能找回。 DCL 之前我们说过:DCL(数据控制语言)用来定义访问权限和安全级别;在此对其进行简单的介绍 创建用户 创建用户的语法为: create user 用户名@IP地址 identified by 密码; 请看如下示例: create user xy@loaclhost identified by ‘123456’; 在此创建一个新用户,用户名为xy密码是123456 请注意,创建新用户后需要执行命令更新权限表(grant tables) FLUSH PRIVILEGES; 给用户授权 给户授权的语法为: grant 权限1,权限2,……..,权限n on 数据库名.* to 用户名@IP地址 identified by ‘密码’; 如果将操作数据库的所有的权限授予用户,语法为: grant all on 数据库名.* to 用户名@IP地址 identified by ‘密码’; 请看如下示例: grant all on db1.* to xy@localhost identified by ‘123456’; 撤销授权 撤销授权的语法为: revoke 权限1,权限2,……..,权限n on 数据库名.* from 用户名@IP地址; 如果撤销用户拥有的某数据库的全部权限,语法为: revoke all on db1.* from xy@localhost; 查看用户权限 查看用户权限的语法为: show grants for 用户名@IP地址; 请看如下示例: show grants for xy@localhost; 删除用户 删除用户权限的语法为: drop user 用户名@IP地址; 请看如下示例: drop user xy@localhost;

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

后台(03)——MySQL(3)

探索Android软键盘的疑难杂症 深入探讨Android异步精髓Handler 详解Android主流框架不可或缺的基石 站在源码的肩膀上全解Scroller工作机制 Android多分辨率适配框架(1)— 核心基础 Android多分辨率适配框架(2)— 原理剖析 Android多分辨率适配框架(3)— 使用指南 自定义View系列教程00–推翻自己和过往,重学自定义View 自定义View系列教程01–常用工具介绍 自定义View系列教程02–onMeasure源码详尽分析 自定义View系列教程03–onLayout源码详尽分析 自定义View系列教程04–Draw源码分析及其实践 自定义View系列教程05–示例分析 自定义View系列教程06–详解View的Touch事件处理 自定义View系列教程07–详解ViewGroup分发Touch事件 自定义View系列教程08–滑动冲突的产生及其处理 版权声明 本文原创作者:谷哥的小弟 作者博客地址:http://blog.csdn.net/lfdfhl DQL 在上一篇博客中我们学习了DDL、DML、DCL在本篇博客中我们将着重讨论MySQL中非常重要的DQL。 数据库执行DQL语句并不会对数据进行改变,而是从数据库返回结果集给客户端;该结果集是一张存在于内存的虚拟表。 DQL语法: SELECT 列名 FROM 表名【WHERE—GROUP BY—HAVING—ORDER BY】 在正式进入这部分之前,我们先准备一下稍后会用到的数据。 CREATE TABLE student ( sid CHAR(6), sname VARCHAR(50), age INT, gender VARCHAR(50) ); 创建学生表student INSERT INTO student VALUES(‘S_1001’, ‘liuYi’, 14, ‘male’); INSERT INTO student VALUES(‘S_1002’, ‘chenEr’, 15, ‘female’); INSERT INTO student VALUES(‘S_1003’, ‘zhangSan’, 13, ‘male’); INSERT INTO student VALUES(‘S_1004’, ‘liSi’, 16, ‘female’); INSERT INTO student VALUES(‘S_1005’, ‘wangWu’, 17, ‘male’); INSERT INTO student VALUES(‘S_1006’, ‘zhaoLiu’, 18, ‘female’); INSERT INTO student VALUES(‘S_1007’, ‘sunQi’, 13, ‘male’); INSERT INTO student VALUES(‘S_1008’, ‘zhouBa’, 15, ‘female’); INSERT INTO student VALUES(‘S_1009’, ‘wuJiu’, 11, ‘male’); INSERT INTO student VALUES(‘S_1010’, ‘zhengShi’, 15, ‘female’); INSERT INTO student VALUES(‘S_1011’, ‘xxx’, NULL, NULL); 向学生表student中插入数据。 CREATE TABLE emp( empno int, ename varchar(50), job varchar(50), mgr int, hiredate date, sal decimal(7,2), comm decimal(7,2), deptno int ); 建立员工表emp INSERT INTO emp values(7369,’SMITH’,’CLERK’,7902,’1980-12-17’,800,NULL,20); INSERT INTO emp values(7499,’ALLEN’,’SALESMAN’,7698,’1981-02-20’,1600,300,30); INSERT INTO emp values(7521,’WARD’,’SALESMAN’,7698,’1981-02-22’,1250,500,30); INSERT INTO emp values(7566,’JONES’,’MANAGER’,7839,’1981-04-02’,2975,NULL,20); INSERT INTO emp values(7654,’MARTIN’,’SALESMAN’,7698,’1981-09-28’,1250,1400,30); INSERT INTO emp values(7698,’BLAKE’,’MANAGER’,7839,’1981-05-01’,2850,NULL,30); INSERT INTO emp values(7782,’CLARK’,’MANAGER’,7839,’1981-06-09’,2450,NULL,10); INSERT INTO emp values(7788,’SCOTT’,’ANALYST’,7566,’1987-04-19’,3000,NULL,20); INSERT INTO emp values(7839,’KING’,’PRESIDENT’,NULL,’1981-11-17’,5000,NULL,10); INSERT INTO emp values(7844,’TURNER’,’SALESMAN’,7698,’1981-09-08’,1500,0,30); INSERT INTO emp values(7876,’ADAMS’,’CLERK’,7788,’1987-05-23’,1100,NULL,20); INSERT INTO emp values(7900,’JAMES’,’CLERK’,7698,’1981-12-03’,950,NULL,30); INSERT INTO emp values(7902,’FORD’,’ANALYST’,7566,’1981-12-03’,3000,NULL,20); INSERT INTO emp values(7934,’MILLER’,’CLERK’,7782,’1982-01-23’,1300,NULL,10); 向员工表emp中插入数据 CREATE TABLE dept( deptno INT, dname varchar(14), loc varchar(13) ); 建立部门表dept INSERT INTO dept values(10, ‘ACCOUNTING’, ‘NEW YORK’); INSERT INTO dept values(20, ‘RESEARCH’, ‘DALLAS’); INSERT INTO dept values(30, ‘SALES’, ‘CHICAGO’); INSERT INTO dept values(40, ‘OPERATIONS’, ‘BOSTON’); 向部门表dept中插入数据 基本查询 查询所有列 SELECT * FROM student; 查询指定列 SELECT sid, sname, age FROM student; 条件查询 在进行条件查询时需在WHERE子句使用以下运算符和关键字: =、!=、<、<=、>、>= BETWEEN…AND IN(value1,value2,….valueN) NOT IN(value1,value2,….valueN) IS NULL IS NOT NULL AND OR NOT 请看如下示例: 查询性别为女,并且年龄小于20的记录 SELECT * FROM student WHERE gender = ‘female’ AND age<20; 查询学号为S_1001或者姓名为liSi的记录 SELECT * FROM student WHERE sid=’S_1001’ OR sname=’liSi’; 查询学号为S_1001,S_1002,S_1003的记录 SELECT * FROM student WHERE sid IN (‘S_1001’,’S_1002’,’S_1003’); 查询学号不为S_1001,S_1002,S_1003的记录 SELECT * FROM student WHERE sid NOT IN (‘S_1001’,’S_1002’,’S_1003’); 查询年龄为null的记录 SELECT * FROM student WHERE age IS NULL; 查询姓名不为null的记录 SELECT * FROM student WHERE sname IS NOT NULL; 查询年龄在14到16之间的记录 SELECT * FROM student WHERE age>=14 AND age<=16; SELECT * FROM student WHERE age BETWEEN 14 AND 16; 查询性别非男的记录 SELECT * FROM student WHERE gender !=’male’; SELECT * FROM student WHERE NOT gender =’male’; 模糊查询 模糊查询常使用关键字LIKE 通配符_表示任意一个字符 通配符%表示任意0—N个字符 请看如下示例: 查询姓名由5个字母构成的记录 SELECT * FROM student WHERE sname LIKE “_____”; 查询姓名由5个字母构成且第5个字母为i的记录 SELECT * FROM student WHERE sname LIKE “____i”; 查询姓名以“z”开头的记录 SELECT * FROM student WHERE sname LIKE “z%”; 查询姓名中第2个字母为i的记录 SELECT * FROM student WHERE sname LIKE “_i%”; 查询姓名中包含a字母的记录 SELECT * FROM student WHERE sname LIKE “%a%”; 字段控制查询 请看如下示例: 利用DISTINCT去除重复记录 SELECT DISTINCT job FROM emp; 利用函数IFNULL把NULL转换成数值0 SELECT IFNULL(comm,0) FROM emp; 查看雇员的月薪与佣金之和 SELECT sal+IFNULL(comm,0) FROM emp; 利用关键字AS给列名添加别名 SELECT sal+IFNULL(comm,0) AS total FROM emp; SELECT sal AS salary ,ename AS NAME FROM emp; 在使用AS给列名添加别名,可空格代替AS;但不推荐这么做。 排序 常用的两种排序方式 ORDER BY 列名 ASC ORDER BY 列名 DESC 请看如下示例: 查询所有学生记录,按年龄降序排序 SELECT * FROM student ORDER BY age DESC; 查询所有雇员,按月薪降序排序,若月薪相同时按员工编号降序排序 SELECT * FROM emp ORDER BY sal DESC,empno DESC; 聚合函数 聚合函数用于纵向(即列)运算;它具有自动滤空的功能,若某一个值为NULL,那么会自动将其过滤使其不参与运算。 常用的聚合函数如下: COUNT( ):统计指定列其值不为NULL的记录行数 MAX( ):计算指定列的最大值,如果指定列是字符串类型则使用字符串排序运算 MIN( ):计算指定列的最小值,如果指定列是字符串类型则使用字符串排序运算 SUM( ):计算指定列的数值和,如果指定列类型不是数值类型则计算结果为0 AVG( ):计算指定列的平均值,如果指定列类型不是数值类型则计算结果为0 请看如下示例: 查询emp表中记录数 SELECT COUNT(*) FROM emp; 查询emp表中有佣金的人数 SELECT COUNT(comm) AS total FROM emp; 查询emp表中月薪大于2000的人数 SELECT COUNT(*) FROM emp WHERE sal>2000; 查询月薪与佣金之和大于2000的人数 SELECT COUNT(*) FROM emp WHERE (sal+IFNULL(comm,0))>2000; 查询有佣金的人数,有领导的人数 SELECT COUNT(comm), COUNT(mgr) FROM emp; 查询所有雇员月薪的总和 SELECT SUM(sal) FROM emp; 查询所有雇员月薪和,以及所有雇员佣金之和 SELECT SUM(sal) , SUM(comm) FROM emp; 统计所有员工平均工资 SELECT AVG(sal) FROM emp; 查询所有员工中的最高工资 SELECT MAX(sal) FROM emp; 查询所有员工中的最低工资 SELECT MIN(sal) FROM emp; 分组查询 当分组查询时需使用GROUP BY子;例如,查询每个部门的工资总和,此时需用部门来进行分组 请看如下示例: 查询每个部门的部门编号和每个部门的工资和 SELECT deptno,SUM(sal) FROM emp GROUP BY deptno; 查询每个部门的部门编号以及每个部门的人数 SELECT deptno,COUNT(*) FROM emp GROUP BY deptno; 查询每个部门的部门编号以及每个部门工资大于2000的人数 SELECT deptno,COUNT(*) FROM emp WHERE sal>2000 GROUP BY deptno; 请务必注意: 在MySQL中使用SQL时,如果某列名和聚合函数同时出现在了SELECT与FROM之间,那么该列名一定要写在group by 之后。 还是来看刚才的例子: 查询每个部门的部门编号和每个部门的工资和,SQL语句如下: SELECT deptno,SUM(sal) FROM emp GROUP BY deptno; 如果按照下面的写法: SELECT ename,COUNT(*) FROM emp GROUP BY deptno; 在MySQL中执行时不会报错,但是查询出来的结果明显不对,稀奇古怪的样子;而且该写法在其他数据库中执行时会报错! 关于这个注意事项需要多加体会。 HAVING子句 HAVING子句用于在GROUP BY分组之后对数据再次筛选。 请看如下示例: 查询工资总和大于10000的部门编号以及工资和 SELECT deptno, SUM(sal) FROM emp GROUP BY deptno HAVING SUM(sal) >10000; LIMIT LIMIT用来限定查询结果的起始行和总行数,常用于分页。 请看如下示例: 从student表中查询5行记录,起始行从2开始 SELECT * FROM student LIMIT 2, 5; 查询代码的书写顺序和执行顺序 查询语句的书写顺序: select – from – where – group by – having – order by – limit 查询语句的执行顺序: from – where – group by – having - select - order by – limit

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

源雀 Scrm 开源版:AI 客服会话总结 功能发布

1.AI客服回话总结 本功能通过AI大模型分析企微客服客户之间的对话记录,生成简明扼要的总结报告,帮助管理人员快速把握服务要点,提升服务质量与效率。 核心价值 1.​​效率提升​​:将冗长的对话浓缩为关键信息,节省80%以上的阅读时间 2.​​质量监控​​:自动识别服务过程中的关键节点和问题点 3.​​知识沉淀​​:形成可追溯的服务案例库,支持新人培训 4.​​智能分析​​:发现服务流程中的优化机会和常见问题模式 2.客户群发 基于企微可方API接口实现,多种内容客户群发。 ​ 3.客户列表 支持同步企微客户,未群发等功能提供基础数据,便于后续客户跟进管理等功能的拓展。

资源下载

更多资源
Mario

Mario

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

Spring

Spring

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

Sublime Text

Sublime Text

Sublime Text具有漂亮的用户界面和强大的功能,例如代码缩略图,Python的插件,代码段等。还可自定义键绑定,菜单和工具栏。Sublime Text 的主要功能包括:拼写检查,书签,完整的 Python API , Goto 功能,即时项目切换,多选择,多窗口等等。Sublime Text 是一个跨平台的编辑器,同时支持Windows、Linux、Mac OS X等操作系统。

WebStorm

WebStorm

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

用户登录
用户注册