整合营销服务商

电脑端+手机端+微信端=数据同步管理

免费咨询热线:

JSP第一篇:JSP介绍、工作原理、生命周期、语法、指令(修订版)

么是JSP

JSP全名为Java Server Pages,java服务器页面。JSP是一种基于文本的程序,其特点就是HTML和Java代码共同存在

为什么需要JSP

JSP是为了简化Servlet的工作出现的替代品,Servlet输出HTML非常困难,JSP就是替代Servlet输出HTML的。

简单使用一下JSP

  • 在idea下生成一个JSP,我们来看一下JSP长什么样子

  • 看起来就像一个HTML页面,前面也说了:JSP的特点就是HTML和Java代码共同存在
  • 我们向浏览器输出一句HelloWorld,至于<%%>这个东西,我先不解释!

JSP的工作原理

  • 在Tomcat博客中我提到过:Tomcat访问任何的资源都是在访问Servlet!,当然了,JSP也不例外!JSP本身就是一种Servlet。为什么我说JSP本身就是一种Servlet呢?其实JSP在第一次被访问的时候会被编译为HttpJspPage类(该类是HttpServlet的一个子类)
  • 刚才我简单使用了一下JSP,它被编译成了这么一个Servlet:

  • 编译过程是这样子的:浏览器第一次请求1.jsp时,Tomcat会将1.jsp转化成1_jsp.java这么一个类,并将该文件编译成class文件。编译完毕后再运行class文件来响应浏览器的请求
  • 以后访问1.jsp就不再重新编译jsp文件了,直接调用class文件来响应浏览器。当然了,如果Tomcat检测到JSP页面改动了的话,会重新编译的
  • 既然JSP是一个Servlet,那JSP页面中的HTML排版标签是怎么样被发送到浏览器的?我们来看下上面1_jsp.java的源码就知道了。原来就是用write()出去的罢了。说到底,JSP就是封装了Servlet的java程序罢了。

  • 有人可能也会问:JSP页面的代码服务器是怎么执行的?再看回1_jsp.java文件,java代码就直接在类中的service()中。
String s = "HelloWorda";
out.println(s);
  • JSP比Servlet更方便更简单的一个重要原因就是:内置了9个对象!内置对象有:out、session、response、request、config、page、application、pageContext、exception,这几个内置对象不在这里讲。现在先知道一下即可!

JSP生命周期

JSP也是Servlet,运行时只有一个实例,JSP初始化和销毁时也会调用Servlet的init()和destroy()方法。另外,JSP还有自己初始化和销毁的方法

JSP的语法

JSP代码可以分为两部分:

  1. 模板数据:就是HTML代码
  2. 元素:JSP页面中的java代码、JSP指令、JSP标签


JSP脚本

  • JSP的脚本就是JSP页面中的java代码,也叫做scriptlet。JSP的脚本必须使用<%%>括起来,不然会被当成是模板数据的!
  • JSP脚本有三种方式:
  • <%%>【定义局部变量,编写语句】
  • <%!%>【定义类或方法,但是没人这样用!
  • <%=%>(也称之为表达式输出)【输出各种类型的变量,int、double、String、Object等】
  • 如果过多地使用<%%>会导致代码混乱,JSP还提供了一种scriptlet标签,使用此标签和<%%>有相同的功能,只不过它更美观了一些
<jsp:scriptlet>
 String s = "HelloWorld";
 out.println(s);
</jsp:scriptlet>

JSP注释

<%--这是JSP注释--%>
<%--%>
//这是java的当行注释
//
/*这是java的多行注释*/
/**/

JSP指令

JSP指令用来声明JSP页面的相关属性,例如编码方式、文档类型等等

JSP指令的语法:

<%@指令 属性名="值" %>


page指令

  • 我在idea生成的JSP页面就有page指令了。
<%@ page contentType="text/html;charset=UTF-8" language="java" %>


  • page指令常见属性:
  • language="java"
  • extends="package.class"
  • import="{package.class | package.*}, …"
  • session="true | false"
  • buffer="none | 8kb | sizekb"
  • autoFlush="true | false"
  • isThreadSafe="true | false"
  • info="text"
  • errorPage="relative_url"
  • isErrorPage="true | false"
  • contentType="mimeType ;charset=characterSet " | "text/html ; charset=ISO-8859-1"
  • pageEncoding="characterSet | ISO-8859-1"
  • isELIgnored="true | false"
  • 一般地,在eclipse或idea这些高级开发工具上开发,我们只需要在page指令中指定contentType="text/html;charset=UTF-8",就不会出现中文乱码问题!
  • 当然了contentType 不仅仅可以指定以text/html的方式显示,还可以使用其他的形式显示出来。在conf/web.xml文件中可以查询出来

  • 比如,我以doc形式显示jsp的数据
<%@ page contentType="application/msword;charset=UTF-8" language="java" %>
<html>
<head>
 <title>简单使用JSP</title>
</head>
<body>
 1111
</body>
</html>
  • 我们上网的时候,如果我们操作不当,或者服务器出错了,页面都是会出现友好提示的!这个也能通过page指令来实现跳转到友好提示页面上
  • page指令errorPage=和isErrorPage这两个属性,下面我们来看一下怎么使用!
  • 1.jsp出现了错误,通过page指令的errorPage属性跳转到error.jsp页面
<%@ page contentType="text/html;charset=UTF-8" language="java" errorPage="error.jsp" %>
<html>
<head>
 <title>该页面出错了!</title>
</head>
<body>
 <%--模拟页面出错了!!!--%>
 <%
 int result = 2 / 0;
 %>
 你好呀
</body>
</html>
  • error.jsp页面要通过page指令的isErrorPage属性设置页面就是错误页面
<%@ page contentType="text/html;charset=UTF-8" language="java" isErrorPage="true" %>
<html>
 <head>
 <title>友好提示页面</title>
 </head>
 <body>
 服务器正忙着呢!
 </body>
</html>
  • 下面是效果:

  • 当然了,细心的朋友可以发现地址栏是没有变化的,所以属于是服务器跳转。以上的做法是单个页面设置的,如果我会有很多错误(JSP多的情况下,错误就会多),单个设置太麻烦了!
  • 我们可以在web.xml文件中全局设置错误页,只要发生了404错误或者空指针异常的错误都会跳转到error.jsp页面上
<error-page>
 <error-code>404</error-code>
 <location>/error.jsp</location>
</error-page>
<error-page>
 <exception-type>java.lang.NullPointerException</exception-type>
 <location>/error.jsp</location>
</error-page>
  • 随便输个资源进行,会发生发404错误的,跳转到错误页面。下面是效果:


include指令

  • 在讲解request对象的时候,我们曾经使用过request.getRequestDispatcher(String url).include(request,response)来对页头和页尾面进行包含
  • inclue指令也是做这样的事情,我们来试验一下吧!
  • 这是页头
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
 <head>
 <title>页头</title>
 </head>
 <body>
 我是页头
 <br>
 <br>
 <br>
 </body>
</html>
  • 这是页尾
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
 <title>页尾</title>
</head>
<body>
我是页尾
</body>
</html>
  • 在1.jsp中把页头和页尾包含进来
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
 <title>包含页头和页尾进来</title>
</head>
<body>
<%@include file="head.jsp" %>
<%@include file="foot.jsp" %>
</body>
</html>
  • 访问1.jsp

  • include指令是静态包含。静态包含的意思就是:把文件的代码内容都包含进来,再编译!,看一下jsp的源代码就知道了!

  • jsp还提供另一种包含文件的方式:JSP行为---动态包含。jsp行为在下面会讲到!



taglib指令

  • JSP支持标签技术,要使用标签技术就先得声明标签库和标签前缀。taglib指令就是用来指明JSP页面内使用标签库技术。
  • 这里就不详细说明了,等到学习JSP标签的时候再使用吧!现在记住有这个指令即可。



JSP行为

JSP行为(JSP Actions)是一组JSP内置的标签,只书写少量的标记代码就能够使用JSP提供丰富的功能,JSP行为是对常用的JSP功能的抽象和封装

为什么我不把它直接称为JSP标签呢?我把这些JSP内置的标签称之为JSP行为,能够和JSTL标签区分开来。当然了,你也可以把它称之为JSP标签,你不要搞混就行了。我个人喜欢把这些JSP内置标签称之为JSP行为。

include行为

  • 上面已经提及到了,include指令是静态包含,include行为是动态包含其实include行为就是封装了request.getRequestDispatcher(String url).include(request,response)
  • include行为语法是这个样子的
 <jsp:include page=""/>
  • 我们先来使用一下把,在1.jsp页面中也将页头和页尾包含进来
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
 <title>包含页头和页尾进来</title>
</head>
<body>
 <jsp:include page="head.jsp"/>
 <jsp:include page="foot.jsp"/>
</body>
</html>
  • 访问1.jsp页面看一下效果:

  • 使用jsp行为来包含文件,jsp源文件是这样子的:

  • jsp行为包含文件就是先编译被包含的页面,再将页面的结果写入到包含的页面中(1.jsp)
  • 当然了,现在有静态包含和动态包含,使用哪一个更好呢?答案是:动态包含
  • 动态包含可以向被包含的页面传递参数(用处不大),并且是分别处理包含页面的(将被包含页面编译后得出的结果再写进包含页面)【如果有相同名称的参数,使用静态包含就会报错!】
  • 模拟一下场景吧,现在我的头页面有个名为s的字符串变量
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
 <head>
 <title>页头</title>
 </head>
 <body>
 <%
 String s = "zhongfucheng";
 %>
 我是页头呀
 <br>
 <br>
 <br>
 </body>
</html>
  • 我的页尾也有个名为s的字符串变量
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
 <title>页尾</title>
</head>
<body>
<%
 String s = "zhongfucheng";
%>
我是页尾呀
</body>
</html>
  • 现在我使用静态包含看看会发生什么,出现异常了。

  • 出现异常的原因很简单,就是同一个文件中有两个相同的变量s

  • 使用动态包含就可以避免这种情况


param行为

  • 当使用和行为引入或将请求转发给其它资源时,可以使用行为向这个资源传递参数。


forward行为

  • 在讲解request对象的时候,我们使用request.getRequestDispatcher(String url).forward(request,response)进行跳转。其实forward行为就是对其封装
  • 我们来看一下forward的语法:
<jsp:forward page=""/>
  • 好的,我们来使用一下吧。访问1.jsp页面就跳转到head.jsp页面中
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
 <title>访问1.jsp就跳转到head.jsp</title>
</head>
<body>
<jsp:forward page="head.jsp"/>
</body>
</html>
  • 看一下效果

  • 如果我要传递参数,就要在forward行为嵌套param行为
  • 在跳转到head.jsp时传入参数username值为zhongfucheng
<jsp:forward page="head.jsp">
 <jsp:param name="username" value="zhongfucheng"/>
</jsp:forward>
  • 在head.jsp页面中获取到传递过来的参数
<%
 String ss = request.getParameter("username");
%>
获取到的参数是:
<%=ss%>
  • 效果如下图所示

directive行为

  • directive的中文意思就是指令该行为就是替代指令<%@%>的语法的
  • 相当于<%@include file="" %>
  • 相当于<%@page %>
  • 相当于<%@taglib %>
  • 我们来试一下能不能用的
<jsp:directive.include file="head.jsp"></jsp:directive.include>
<jsp:directive.include file="foot.jsp"></jsp:directive.include>
  • 看下效果,正常可以包含页面:

  • 使用该指令可以让JSP页面更加美观
  • 使用scriptlet行为替代<%%>是同样一个道理


javaBean行为

  • JSP还提供了操作javaBean对象的行为在这里就不详细说明了,后面会讲到的!现在记住JSP提供了javaBean行为来操作简单类即可!
<jsp:useBean id=""/>
<jsp:setProperty name="" property=""/>
<jsp:getProperty name="" property=""/>

文章来源:https://dwz.cn/OtXvyvh3

作者:Java3y

困难行业企业稳就业补贴”线上经办系统已上线运行啦!符合条件的企业可通过上海市人力资源和社会保障自助经办系统的“惠企点单办”申请。具体步骤赶紧往下看:

01

登录“上海市人力资源和社会保障自助经办系统”(https://zzjb.rsj.sh.gov.cn/zzjbdl/jsp/login.html)。

02

03

选择“困难行业企业稳就业补贴”事项,点击“办理”。

04

企业确认承诺书内容,点击“下一步”。

05

系统自动检验后显示企业相关信息,需填写补贴申请经办人信息。

06

企业可根据实际,点击“登记/修改银行账号”进入银行账号修改界面,更改补贴拟发放账户信息。

07

系统自动提取企业统一社会信用代码证书等电子资料,若提取失败,须自行上传相关影像资料,完成后点击“下一步”。

08

确认申请信息无误后(重点核对“银行账户”“联系手机”“上月应缴城镇职工社会保险人数”等关键信息),点击“确认提交”。

我们的开发工程中经常会使用到各种图,所谓的图就是由节点和节点之间的连接所形成的系统,数学上专门有一个分支叫图论(Graph Theroy)。利用图我们可以做很多工具,比如思维导图,流程图,状态机,组织架构图,等等。今天我要做的是用开源的HTML5工具来快速构造一个做图的工具。

工具选择

工预善其事,必先利其器。第一件事是选择一件合适的工具,开源时代,程序员还是很幸福的,选择很多。

  • flowchart.js http://adrai.github.io/flowchart.js/ , 基于SVG创建Flow Chart
  • go.js http://www.gojs.net/latest/index.html go.js 提供一整套的JS工具 ,支持各种交互式图表的创建。有免费版和收费版
  • joint.js http://www.jointjs.com/ joint.js 是另一个创建图标库的工具,也提供免费版和商业版
  • jsPlumb http://www.jsplumb.org/ jsPlumb是一套开源的流程图创建工具 ,小巧精悍,使用简单
  • d3 http://d3js.org 在html5领域,d3可谓是最好的可视化基础库,提供方面的DOM操作,非常强大。

最终,我选择了jsPlumb,因为它完全开源,使用很简单,用D3的话可能会多花很多功夫。joint.js也不错。大家可以根据自己的需要选择。

构建静态应用

下面我们一步一步的来使用jsPlumb来创建我们的流程图工具。

第一步是等待DOM和jsPlumb初始化完毕,类似document.ready()和jquery.ready(), 要使用jsPlumb, 需要把代码放在这个函数里:

jsPlumb.ready(function() {
    // ... your code goes here ...
}


创建一个jsPlumb的实例,并初始化jsPlumb的配置参数:

//Initialize JsPlumb
var color = "#E8C870";
var instance = jsPlumb.getInstance({
    // notice the 'curviness' argument to this Bezier curve.  the curves on this page are far smoother
    // than the curves on the first demo, which use the default curviness value.      
    Connector : [ "Bezier", { curviness:50 } ],
    DragOptions : { cursor: "pointer", zIndex:2000 },
    PaintStyle : { strokeStyle:color, lineWidth:2 },
    EndpointStyle : { radius:5, fillStyle:color },
    HoverPaintStyle : {strokeStyle:"#7073EB" },
    EndpointHoverStyle : {fillStyle:"#7073EB" },
    Container:"container-id"
 });


这里给给出了一些配置包括,连接线(这里配置了一个贝塞尔曲线),线的风格,连接点得风格。Container需要配置一个对应的DIV容器的id。(这里也可以使用setContainer的方法)

下面我们要创建一个节点(node),每一个节点可以用一个DIV来实现。我这里提供了一个函数来创建节点。

function addNode(parentId, nodeId, nodeLable, position) {
  var panel = d3.select("#" + parentId);
  panel.append('div').style('width','120px').style('height','50px')
    .style('position','absolute')
    .style('top',position.y).style('left',position.x)
    .style('border','2px #9DFFCA solid').attr('align','center')
    .attr('id',nodeId).classed('node',true)
    .text(nodeLable);

  return jsPlumb.getSelector('#' + nodeId)[0];
}


这里做的事情就是创建了一个DIV元素,并放在对应的容器的制定位置上,注意为了支持拖拽的功能,必须使用position:absolute 。

我使用D3来操作DOM,大家可能会更习惯JQuery,这纯属个人喜好的问题。

最后返回创建节点的实例引用,这是的selector使用了jsPlumb.getSelector()方法,它和JQuery的selector是一样的,这样用的好处是你可以使用不同的DOM操作库,例如Vanilla

下面我使用一个函数来创建端点/锚点(anchor),锚点就是节点上的连接点,用于连接不同的节点。

function addPorts(instance, node, ports, type) {
  //Assume horizental layout
  var number_of_ports = ports.length;
  var i = 0;
  var height = $(node).height();  //Note, jquery does not include border for height
  var y_offset = 1 / ( number_of_ports + 1);
  var y = 0;

  for ( ; i < number_of_ports; i++ ) {
    var anchor = [0,0,0,0];
    var paintStyle = { radius:5, fillStyle:'#FF8891' };
    var isSource = false, isTarget = false;
    if ( type === 'output' ) {
      anchor[0] = 1;
      paintStyle.fillStyle = '#D4FFD6';
      isSource = true;
    } else {
      isTarget =true;
    }

    anchor[1] = y + y_offset;
    y = anchor[1];

    instance.addEndpoint(node, {
      uuid:node.getAttribute("id") + "-" + ports[i],
      paintStyle: paintStyle,
      anchor:anchor,
      maxConnections:-1,
      isSource:isSource,
      isTarget:isTarget
    });
  }
}


instance是jsPlumb的实例

node是我们用addNode方法创建的Node实例

ports,是一个string的数组,指定端点的个数和名字

type,可能是output或者input,指定端点的种类,一个节点的输出端口可以连接另一个节点的输入端口。

这里anchor是一个四维数组,0维和1维分别是锚点在节点x轴和y轴的偏移百分比。我这里希望把端口画在节点的左右两侧,并按照端口的数量均匀分布。

最后使用instance.addEndpoint来创建端点。注意这里只要指定isSource和isTarget就可以用drag&drop的方式来连接端点,非常方便。

下面一步我们提供一个函数来连接端点:

function connectPorts(instance, node1, port1, node2 , port2) {
  // declare some common values:
  var color = "gray";
  var arrowCommon = { foldback:0.8, fillStyle:color, width:5 },
  // use three-arg spec to create two different arrows with the common values:
  overlays = [
    [ "Arrow", { location:0.8 }, arrowCommon ],
    [ "Arrow", { location:0.2, direction:-1 }, arrowCommon ]
  ];

  var uuid_source = node1.getAttribute("id") + "-" + port1;
  var uuid_target = node2.getAttribute("id") + "-" + port2;

  instance.connect({uuids:[uuid_source, uuid_target]});
}


node1和node2是源节点和目标节点的引用,port1和port2是源端口和目标端口的名字。

使用instance.connect方法来创建连接。 overlays用来添加连接线的箭头效果或者其他风格,我这里没有使用,因为觉得都不是很好看。大家如果要用,只要把overlays加入到instance.connect的方法参数就可以了。

调用以上方法来创建节点,端点和连接线。

var node1 = addNode('container-id','node1', 'node1', {x:'80px',y:'20px'});
var node2 = addNode('container-id','node2', 'node2', {x:'280px',y:'20px'});

addPorts(instance, node1, ['out1','out2'],'output');
addPorts(instance, node2, ['in','in1','in2'],'input');

connectPorts(instance, node1, 'out2', node2, 'in');


这里我们创建了两个节点,第一个节点有两个输出端口,第二个节点有三个输入端口,然后把第一个节点的out2端口连接到第二个端点的in端口。效果如下:

最后我们给节点增加drag&drop的功能,这样我们就可以拖动这些节点来改变图的布局了。

instance.draggable($('.node'));


这里似乎依赖于JQuery-UI,我还不是很清楚。

交互式创建节点

我们已经初步具有了创建图的功能,可是节点的创建必须通过程序,我们希望用交互的方式来创建节点。

通常我们希望有一个tree view的控件,让后通过拖拽来创建对应类型的节点。这里我使用了这个开源的tree view,基于bootstrap https://github.com/jonmiles/bootstrap-treeview

我们先创建一个tree view:

function getTreeData() {
  var tree = [
    {
      text: "Nodes",
      nodes: [
        {
          text: "Node1",
        },
        {
          text: "Node2"
        }
      ]
    }
  ]; 

  return tree;
}
//Initialize Control Tree View
$('#control-panel').treeview({data: getTreeData()});


树上有两个节点:

然后我实现从树上拖拽对应的节点,到流程图上的逻辑。

//Handle drag and drop
$('.list-group-item').attr('draggable','true').on('dragstart', function(ev){
  //ev.dataTransfer.setData("text", ev.target.id);
  ev.originalEvent.dataTransfer.setData('text',ev.target.textContent);
  console.log('drag start');
});

$('#container-id').on('drop', function(ev){
  //avoid event conlict for jsPlumb
  if (ev.target.className.indexOf('_jsPlumb') >= 0 ) {
    return;
  }

  ev.preventDefault();
  var mx = '' + ev.originalEvent.offsetX + 'px';
  var my = '' + ev.originalEvent.offsetY + 'px';

  console.log('on drop : ' + ev.originalEvent.dataTransfer.getData('text'));
  var uid = new Date().getTime();
  var node = addNode('flow-panel','node' + uid, 'node', {x:mx,y:my});
  addPorts(instance, node, ['out'],'output');
  addPorts(instance, node, ['in1','in2'],'input');
  instance.draggable($(node));
}).on('dragover', function(ev){
  ev.preventDefault();
  console.log('on drag over');
});


这里要注意的是要避免和jsPlumb拖拽端点的逻辑冲突,当检测到target是jsPlumb对象是需要直接从drop方法中退出以执行对应的jsPlumb的drop逻辑。

好了,一个绘制流程图的软件工具初步完工。

我把代码放在oschina的代码托管服务上了, 大家有兴趣可以去试试。