1.1 Jsp文件
(1) Jsp文件
Jsp文件是在HTML文件里面嵌入了java脚本,就形成了jsp页面,不能直接用计算机打开,打开就是源代码了;
<%@ page language="java" import="java.util.*" pageEncoding="ISO-8859-1"%>
<%
String path = request.getContextPath();
String basePath = request.getScheme()+"://"+request.getServerName()+":"+request.getServerPort()+path+"/";
%>
这些代码就需要tomcat把它转换成HTML格式;<%%>里面就是java代码,是动态的部分了;第一句是指令元素;jsp页面就是动态页面(java脚本、指令元素)加上静态页面(HTML、CSS、JS)组成的
(2) index_jsp.java文件
Final不能写子类;extends继承;_jspService每一个用户访问都调用这个方法;out是一个JspWriter里面的内置对象,在jsp页面可以直接使用;
(3) 运行
第一次运行时执行:Jsp文件转换为Servlet文件(java文件),编译为class文件(.class文件),执行Servlet实例
一个jsp文件能被多用户访问,另外一个用户访问时,jsp文件已经生成,除非有改变有修改,才会重新去编译;如果没有修改,第二个用户就直接运行已经生成的jsp文件。
1.2 使用jsp的java脚本
(1)解决循环问题
下拉框select,里面是option,通过value知道用户选择哪个;写java代码用<%%>;转译用\
1、
<body>
出生年份:<select>
<option value="1970">1970年</option>
<option value="1971">1971年</option>
<option value="1972">1972年</option>
<option value="1973">1973年</option>
<option value="1974">1974年</option>
</select>
</body>
1_1:
<body>
出生年份:<select>
<%
for (int i=1970;i<=2023;i++){
String s="<option value=\""+i+"\">"+i+"年</option>";
out.println(s);
}
%>
</select>
</body>
(2)for问题
2:
<%int i=10; %>
<%
if(i==10){
out.println("i==10");
}
%>
2_1:
<%int i=10; %>
<%
if(i==10){
%>
i==10
<%
}
%>
For循环里面只有一句的情况省略{},是错误的!因为java语句是以分号作为一句话的,HTML是没有办法判断一行还是两行的,省略了后面的都会显示;
For循环的{}一定不能省略的。
1.3 jsp脚本元素
(1)声明标识<%! %>
定义变量和方法
<%!%>声明的是成员变量或者方法,不会放在方法里面,而是放在类里面作为成员变量,例如
<%
int i=10;
double a =20;
%>
<%
double a =30;
out.println("a=" + a);
%>
只有在<%! %>声明方法,<%%>里面不能写方法,因为所有代码全都编到了servlet方法里面,方法里面不能再声明方法了。“ <%!”里面不能有空格。
(2)成员变量与全局变量
按照路径,点击文件ctrl+c复制文件,点击MyEcilpse点击ctrl+v复制文件,打开index_jsp.java文件,放到了我们类的成员变量了,权限是默认权限(default)default是包访问权限,如果跟这个类在一个包里面就可以访问这个权限,比私有权限要大一点
在后面servlet方法里面才有double a=30,这个是方法里面的变量,是一个局部变量,double a=20是一个成员变量
成员变量的权限可以加private,public等;局部变量不能加权限,方法里面的不能加权限;声明定义的是全局变量,a=20是每个都可以访问的,a=30只有访问了方法的才可以访问;
(3)Servlet的运行方式
验证在servlet里面只有一个实例,大家访问的都是同一个对象:
<%!
int num = 1;
%>
<%
int count = 1;
%>
<%
out.println("num"+num++);
out.println("<br/>");
out.println("count"+count++);
%>
运行并访问,点击刷新,发现num一直在增加,count不变是count=1;num是我们用全局变量,刷新时不停访问,num就一直在加,它是成员变量,在内存中只有一个实例,所以不停访问都是那一个成员变量;count是局部变量,每次访问方法才重新创建,每次访问方法都执行初始化,不像成员变量只初始化一次;重新打开一个浏览器,相当于一个新用户,两个用户来访问这个页面,num还是原浏览器的基础上再增加,count还是1,两个浏览器的num是不停修改的,所以如果使用全局变量容易导致脏数据,没有办法根据不同用户的访问来区分,这个数据就会不停的增加,当然也可以用来保存该网站的访问次数,访问的用户数等,但是如果重启服务器,实例关闭了,值重新为1,是新的对象了。
(4)JSP表达式
赋值表达式:<%=num=count=5 %>
Count=5赋值给num,a=b=c这种,从右往左赋值,表达式后面是不能加“; ” 不然就变成语句了;<%=true %>//逻辑表达式,返回true <%=3+2 %>//算术表达式,返回5 <%=3>=2 %>//逻辑表达式,返回true;
使用赋值表达式替代字符串的组合:
出生年份:<select>
<%
for (int i=1970;i<=2023;i++){
String s="<option value=\""+i+"\">"+i+"年</option>";
out.println(s);
}
%>
</select>
</br>
出生年份:<select>
<%
for (int i=1970;i<=2023;i++){
%>
<option value="<%=i%>"><%=i%>年</option>
<%}%>
</select>
表达式可以放到html和jsp页面任何位置,只要需要显示值的位置都可以使用,“<%=>”是一个完整的符号,“<%”和“=”之间不能有空格,且jsp表达式中的变量或表达式后面不能有分号(;)。
JSP注释
<%-- jsp注释 --%> <!-- html注释 -->
Page指令的常用属性
import, 碰到导包出现歧义的问题:
<%@ page import="java.sql.*" %>
<%@ page import="java.util.*" %>
<%=new Date()%>这里的会报错显示有歧义,可以明确写出使用哪个包里面的类:<%=new java.util.Date()%>
session, 使用session:
<%@ page language="java" pageEncoding="UTF-8" session="true"%>
Session为true时页面可以使用session对象,代码里面可以使用<%=session.getId() %> ,返回16进制的32位,就是IPV6的长度,每次刷新不变,可以确定我们访问的用户,在另外的页面访问不可能存在一样的,有2的128次方个,这个值可以唯一标识客户身份,登录的就放到session里面,如果重新用这个浏览器打开一个新窗口,值是不变的,说明同一浏览器的不同选项卡还是同一个用户,要使用不同浏览器才是不同用户登录。这里可以假设是购物时,选择不同商品就打开一个选项,但是都加载到同一个用户里面的。
使用了session后,查看源代码可以看到定义了一个session:javax.servlet.http.HttpSession session = null; ,这里就可以使用session,能够定义是因为前面加了session为true,默认情况也是为true的,如果将其改为false,页面不支持session这个内置对象,编译时就没有定义了,不能使用了,所以通常是不修改的。
isErrorPage和errorPage:
isErrorPag和errorPage相对应,一个是是不是错误页面,一个是指定错误页面。isErrorPage为true时才可以使用Exception对象,一般的页面是普通页面,只有指定是错误页面才是错误页面,所以用于指定错误页面。比如一般报错会显示500(,代码运行有问题服务器内部有错),404(找不到页面)等,我们可以使用errorPage="error.jsp"将其设置一旦出错就跳转到错误页面,可以自己编辑错误信息给用户看:
<%@ page language="java" pageEncoding="UTF-8" errorPage="error.
jsp" %>
<%=1/0 %> //被0除的算术错误
Error.jsp中:出错了,请稍后重试
效果:
errorPage指定出错后跳转到那个界面去,然后我们可以将处理页面error.jsp变成处理页面,添加isErrorPage为true,默认是false:
<%@ page language="java" import="java.util.*" pageEncoding=
"UTF-8" isErrorPage="true"%>
指定这个是为了可以使用exception内置对象,但是用处不大,打印结果还是“出错了,请稍后重试java.lang.ArithmeticException: / by zero”这些信息不适合给用户使用:
<body>
出错了,请稍后重试<%=exception %>
</body>
所以isErrorPage相对用的少一些,errorPage用来指定出错的页面
这里补充一点:<%=1/0 %> //被0除的算术错误,但是被0.0除没有出错,报出“Infinity ”,无限大,因为0.0是一个double类型的双精浮点数,0.0f就是float型,double的精度很高,0.0后面还有很多个0,然后可能才是1,相当于除了一个很小的数,所以变成了无限大的数。所以这里如果判断一个浮点数是否为0,不能使用“=”比较,要有个精度,0是否小于0.01,如果小于0.001就认为是0,不能使用0来比较。0.0/0.0显示NaN,因为两个都不确定,一运算就不是一个数了,但是不会发生异常。被0除这种异常不能使用抛出处理,异常有编译时错误,运行时错误;这种运行时错误没有办法用编译器来做处理,有错误只能退出来,没有办法去捕获它,因为它不是异常而是错误。
contentType:
contentType="text/html; charset=ISO-8859-1"
内容类型,前面是指定内容类型,后面是指定编码和pageEncoding,加了后面就不用再使用pageEncoding;内容类型就是前面是大的类型,文本等:text/plain就显示普通文本,直接显示源代码;image/png显示图片,后面是文件类型。contentType和pageEncoding 是为了以后再servlet里面设置类型和编码。response.setContentType(“text/html”)在servlet里面调用这种方法来设置编码。
怎么查找文档的类型:win+R然后输入regedit-打开注册表-*-选择文件,容然后数据里面有显示清楚。
相对地址和绝对地址:
相对地址,test.jsp和index.jsp在同一目录下:
<a href="test.jsp">test.jsp</a>
<a href="./test.jsp">test.jsp</a>当前目录,/是根目录
<a href="../test.jsp">test.jsp</a>上一级目录
绝对地址:
<a href="http://www.baidu.com">test.jsp</a>
然后再javaWeb中以“/”开始就是绝对地址,一般不这样使用绝对地址,把项目名固定了:
<a href="/example1_1/test.jsp">test.jsp</a>
<%String path = request.getContextPath();%>
<%=path%>这里path返回的就是我们的上下文,所以我们想使用绝对地址就使用:
<a href="<%=path%>/test.jsp">test.jsp</a>
include:
<%@ include file="test.jsp"%>
包含这个页面,编译时会把test.jsp里面的内容全复制过来,如果test.jsp里面有局部变量的声明(<%%>包含的)就会报错,包含过来时注意不要变量重名,不要影响文件的结构,一般被包含的页面只是一部分。
后面还有page属性,page是页面,file是文件。Page是一个文件,是完整的,file一般就是不是完整的是页面的一部分,任何文件都是可以的,如果是完整的会影响原来的结构,所以这里把test.jsp除内容和page指令的都删除掉,page指令不能删除,以防不能写中文。
这里静态包含,是指把test的内容全拿过来在进行编译,把apache里面的文件全删除然后编译,发现只生成了index.jsp文件没有test.jsp文件的生成。
还有一种动态包含,如果新建了jsp文件,然后编写内容,其余的我们并不删除,我们要使用动态包含:
<jsp:include page="footer.jsp"></jsp:include>
这里包含的就是一个完整的页面,是动态包含,而test就是一部分。这里的footer是编译了的,在index_jsp_java里面是: org.apache.jasper.runtime.JspRuntimeLibrary.include(request, response, "footer.jsp", out, false);是在运行时去包含它,编码不相同也没有影响;而test是:out.write("杩欐槸鍖呭惈鐨勯〉闈r\n");相当于函数调用,编码就尽量相同,避免错误,这种使用要多一点,像文件头部的文件我们就只直接放在头部,尾部就直接放进尾部,任何一个页面只需要写内容,包含时再把头部和尾部包含进去就可以省略一些结构了。
因为其不严谨,所以就算删除了html和body标签,依然可以显示包含的文本,单独删除</html>标签也不会报错;将文本写在</html>后面会显示文本,但是使用xml就会稍微严谨一些。
1.4 Jsp编码:表单往后台传数据
index.jsp:
<form action="action.jsp">
<input type="text" name="username" >
<input type="submit" value="提交">
</form>
有一个输入框,一个提交按钮,提交到action.jsp页面
action.jsp里面去处理数据,获取username,把用户输入值给显示出来,这里在<%%>里面写Java脚本,然后使用request来获取客户端传参数的值,request.getParameter("username"),里面是获取参数的名字,就是获取前台传过来的表单里的参数值,返回的是一个字符串的值。获取表单域输入数据的value值,然后把值给显示出来:
<%
String username = request.getParameter("username");
%>
<%=username %>//这个就是表单数据的接收,在action里面,接收字符串并显示
表单提交的信息和处理页面,pageEncoding一定要统一。
2 Session和cookie视频
Session实现登录功能:主页有用户名、密码,点击登录后成功了显示某某登录成功,可以退出登录,然后加入自动登录多少天,使得只要打开浏览器,只要在登入日期内,都可以自动登录。(使用cookie和session来实现)
1. 新建项目
新建login项目-部署到服务器-启动服务器-访问http://localhost:8080/login
功能它上面这一部分是变化的,有两种状态:默认登录和没有登录是不一样的,主页我们可以使用包含,头部使用page包含,单独写个header把“这是主页”包含进去,先写header。像主页我们就不用做什么,去header里面写。
2. 新建header.jsp
删除多余指令,只留下page指令,后面会包含到login里面,修改编码语言为“UTF-8”
3. 编写表单form
先写form表单,action是等下要实现的servlet,method是post,表单里只需要用户名和密码和按钮。可以加一条<hr>水平线:
<%@ page language="java" import="java.util.*" pageEncoding="UTF-8"%>
<form action="login" method="post">
用户名:<input type="text" name="username">
密码:<input type="password" name="password">
<input type="submit" value="登录">
</form>
4. 在index.jsp中把heade包含进去
把header包含进login的index.jsp里面,使用<%@ %>包含指令,使用file把header包含进去,这样访问index.jsp是直接是表格,然后才是“这是主页”:
<%@ include file="header.jsp" %>
这是主页!
访问的一定是index.jsp,header.jsp是被包含的
5. 新建servlet
在src中右键点击新建-选择“Servlet”-包名login.servlet,类名UserServlet(以后可以写更多的servlet,登录、注册、用户列表等)
-点击下一步(Next),修改映射路径与action相同(这里没有修改可以在源码里面去修改)
-点击下一步(Next)-不要构造方法,doget和dopost都需要-点击完成(Finish)
6. 编写servlet
删除多余的代码-登录提交用的post,所以写dopost,因为是模拟,没有使用数据库,所以我们先假定那种情况登录成功-先获取用户名和密码:
//获取用户名与密码
String username = request.getParameter("username");
String password = request.getParameter("password");
-假定什么时候是登录成功的,并写到session里面去保存起来,判断是否成功,就把session里面的内容取出来,看有没有这个值;这里我们就写一个标志,把session获取过来,往里面写登录成功的信息来表示登录成功了,检查时就检查标志是否为空,然后在写相应信息(以后就可以直接存,因为可能需要显示用户的各类信息,所以需要把用户信息保存起来)-先获取session对象:
HttpSession session = request.getSession();
这里需要ctrl+shift+o导包,导包就不要自己去输入了-成功了之后就保存新息了,把用户名存进去(后续就检查用户名是否为空,登录之后用户名不为空,就认为用户登录了;以后登录成功可以把user保存起来,就可以将用户信息都查询出来):
//保存登录信息
session.setAttribute("username", username);
这上面都是登录一个控制器,我们最终成功与否都显示index.jsp页面,只是登录不成功时会传回一个信息,这个信息就可以写成:
提交过来就是两种情况,登录成功我们就往里面写登录信息,否则就显示提示信息;这样的情况就是无论成功与否都要跳转到主页去,如果写到request里面,跳转时不能使用客户端跳转,要用服务器端跳转,但是如果使用服务器端跳转,一点击登录就看到login的地址,而我们是需要浏览器的地址不会变,对于我们来说是不希望用户看到login地址的,一点击登录不成功还是应该在index地址,所以最终还是需要使用客户端跳转的,所以不能放到request里面-这里我们把session放到外面,不要放到if语句里面,不然else分支就不能使用session,登录不成功的信息就不放到request里面,改为session里面,这样无论客户端还是服务器端都可以获得:
-客户端跳转(重定向response.sendRedirect("index.jsp"); //客户端跳转,服务器端跳转就是 //request.getRequestDispatcher("index").forward(request, response);服务器端跳转),然后我们这里的控制器是不负责显示的,只是把数据显示出来,跳转到视图去,这里使用客户端跳转,避免使用服务器端跳转后地址停在login位置,因为按照用户来说,点击登录后应该还是在index页面,成功与否都是在index.jsp页面,中心点都在index.jsp;使用session是避免放在request里面客户端取不出来:
这里检查servlet是否生效需要重启服务器,代码才有效,效果是点击登录可以看到地址刷新了
7. 使用存取的信息
Session中保存了username和msg属性,因为在jsp中session是内置对象,所以直接getAttribute(),因为返回是个String类型,所以进行强制转换
编写错误信息:
登录成功时:
修改内容为:
<%@ page language="java" import="java.util.*" pageEncoding="UTF-8"%>
<%
String username = (String)session.getAttribute("username");
String msg = (String)session.getAttribute("msg");
if(username!=null){
out.println("欢迎您" + username + ".");
out.println("<a href='logout'>退出登录</a>");
}else{
%>
<form action="login1" method="post">
用户名:<input type="text" name="username">
密码:<input type="password" name="password">
<input type="submit" value="登录">
<%
if(msg!=null){
out.print(msg);
session.removeAttribute("msg");
}
%>
</form>
<%
}
%>
<hr>
if和else进行判断:登录成功显示“欢迎您admin 退出登录”否则就显示表单并且打印错误。
8. 映射
@WebServlet("/login")映射路径可以映射多个,可以写成一个数组,也可以写成@WebServlet(value={"/login","/logout"})这样login和logout都可以访问它,这种用法其实不好,如果有了用户添加但是还有一个用户列表就不好直接添加一个列表了,因为列表通常是使用doGet来访问,没办法同时控制了;所以通常是使用一个user然后通过传参数来判断是登录还是注册:
9. 编写退出登录时的logout
访问时是使用doGet()方法。判断退出登录是通过删除username这个属性,这样在判断是属性为空就说明没有登录:
获取属性:HttpSession session = request.getSession();
删除属性:session.removeAttribute("username");
用户一点击登录还是显示主页,所以跳转到主页去:response.sendRed
irect("index.jsp");操作完页面,无论是登录删除添加,最后都是返回主控页面,这就是从控制器到视图的跳转:
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
HttpSession session = request.getSession();
session.removeAttribute("username");
response.sendRedirect("index.jsp");
}
这里servlet就是一个控制器,jsp页面就是一个vu,显示都是一样的,但是给的东西不一样显示不一样。完成后重启项目查看效果。(@WebServlet(value={"/login","/logout"})和 @WebServlet(value=
{"/login1","/logout"}) 只能改为 @WebServlet("/login1"),这里有两个servlet,value里面不能同时都有“/logout”会显示404)