原理: 本质就是一个servlet.主要用于显示页面;访问jsp页面,就相当于访问一个servlet。
运行过程: 第一次访问jsp时 => jsp 编译成.java => java生成字节码 => 加载到虚拟机运行.
jsp脚本
jsp中的脚本:用来在jsp中嵌入java代码.
<% %> 嵌入在service方法中
<%! %> 嵌入在类中.
<%= %> 嵌入在service方法中,并使用out.write();方法包裹. 就是输出.
JSP中的 注释:<%-- --%> 多行注释. 被注释的代码,不会参与编译.
1.JSP中指令(一个指令可以具备多个属性)
JSP 指令:用来设置整个JSP页面相关的属性,如网页的编码方式和脚本语言。指令可以有很多个属性,它们以键值对的形式存在,并用逗号隔开。
语法格式:<%@ directive attribute="value" %>
<%@ page contentType="text/html;charset=UTF-8"language="java"pageEncoding="UTF-8" %>
页面保存到硬盘的编码方式为utf-8,表示页面中使用的语言为java语言,发给浏览器的编码方式,*import="java.util.*" 就是导包. 是所有属性中唯一一个可以在页面中出现多次的属性.
以上两个码表最好一致. 但是一般设置一个属性即可.另外一个属性自动设置.
缓冲区属性:
autoFlush="true" 如果缓冲区装满是否自动刷新到浏览器. 如果装满并没有选择自动刷新,那么会抛出异常.
buffer="8kb" 决定jsp输出缓冲区大小为8kb
如果访问当前页面报错,就会跳转到指定的页面(demo2.jsp)
errorPage="/demo2.jsp" 配置当前页面的错误页面
isErrorPage="true" 指定当前页面是否是一个错误页面(在demo.jsp中应该配置此属性,然后就可以使用exception对象,获取前一个报错页面的错误信息;使用exception对象的前提条件是什么?在错误页面打印出报错信息)
开发中,我们可以使用如下配置统一配置错误页面(考虑到代码的复用性),比上面的方式要省事(上面的每个配置使用不方便,几乎每个jsp页面都需要配置):
在web.xml中统一进行配置错误页面,当遇到500错误时,显示如下页面。
<%@ page contentType="text/html;charset=UTF-8" language="java" isErrorPage="true" %>
<!--统一配置错误页面,当遇到500错误时,跳转到如下页面-->
<!--考虑到代码的复用性,这是一个比较好的解决方案-->
<!--最好不要把错误页面暴露给用户,这样用户体验差-->
<error-page>
<error-code>500</error-code>
<location>/error.jsp</location>
</error-page>
extends="" 决定当前jsp的父类是谁.父类必须是servlet的子类.
isELIgnored="false" 决定当前页面能否使 EL表达式.默认值就是支持EL.
isThreadSafe="true" 指定当前的jsp是否线程安全(废弃). 实现一个标记性接口. STM => SingleThreadModel
session="true" 当前jsp页面是否可以直接使用session对象.默认值就是true.
include 静态包含指令
include 属于静态包含 , 现将两个jsp的代码合并.然后在共同编译成一个java文件.在编译成class ,然后执行.
因为存在合并代码,所以变量可以共享. 键名为file。
使用: <%@ include file="/include/B.jsp" %> 将该指令加入到需要引入的位置即可.
2.JSP中的9大内置对象(每个对象都对应一个类型)
指的在jsp中不加以声明就可以直接使用的9个对象(因为它也是Servlet对象).
原理: 因为我们的代码是写在jsp对应java文件的service方法中的.所以在service方法中声明的变量,我们可以直接使用.(运行之后查看.java文件中的_jspService方法)
public void _jspService(HttpServletRequest request, HttpServletResponse response)
throws java.io.IOException, ServletException {
对象名称 对象类型
request HttpServletRequest
response HttpServletResponse
session HttpSession
exception Throwable
application ServletContext
config ServletConfig
page Object
out JspWriter
pageContext PageContext
1、out 对象是jsp当中的输出对象,页面显示,可以直接显示字符串.
<%@ page contentType="text/html;charset=UTF-8" language="java" pageEncoding="UTF-8" %>
<html>
<head>
<title>Title</title>
</head>
<body>
<% out.write("c"); %>
<% response.getWriter().write("b");%>
<% out.write("c");%>
<% response.getWriter().write("d");%>
</body>
</html>
输出: bd ac
原理:在输出到浏览器时,会先把两个流合并,再输出.合并时response的字符流在前.JSPWriter在后. 所以不管代码书写顺序如何.最终response流的内容总会在JSPwriter流的内容之前显示。
结论: 在jsp中输出使用out(JSPWriter)输出,不要使用response.getWriter输出.
2、page对象:page对象指向的就是 this(当前jsp生成Servlet对象);
一般是在开发框架时,框架需要用到JSP对象,进行一些页面的操作时,将page对象传过去即可.
3、pageContext对象(域对象,域对象底层都是map集合)(3个知识点)
本身是一个域对象. 在pageContext对象上有一个map. 这个Map就是Page域.
操作: 4个操作.
域范围: 就只在当前页面中有用.
作用: 在jsp中应避免在页面上书写任何java代码.
pageContext.setAttribute(name, value);
pageContext.getAttribute(name);
pageContext.removeAttribute(name);
//获取某个域中的所有键
pageContext.getAttributeNamesInScope(scope);
pageContext对象还可以操作其他3个域,目的就是为了方便.(从其他三个域中取值,想其他三个域中存入值,清除其他三个域中的数据,就这三个操作)
pageContext.setAttribute("name","applicationTom",PageContext.APPLICATION_SCOPE );
pageContext.setAttribute("name", "sessionTom",PageContext.SESSION_SCOPE );
pageContext.setAttribute("name", "requestTom",PageContext.REQUEST_SCOPE );
pageContext.getAttribute(name, scope);
pageContext.removeAttribute(name, scope);
pageContext.getAttributeNamesInScope(scope);
pageContext.findAttribute("name") 会从所有域中查找键name 的值. 从小到大,返回最小域的值 pageContext到application域
持有其他8个内置对象的引用.根据这个对象可以获得其他8个内置对象。(如果需要其他八个内置对象,直接传递pageContext对象,然后从此对象中获取其他对象,主要是简化代码和复用)
pageContext.getRequest();
pageContext.getResponse();
pageContext.getSession();
pageContext.getServletContext();
pageContext.getException();
pageContext.getServletConfig();
pageContext.getOut();
pageContext.getPage();
3.JSP中的动作标签(动作标签是干什么用的) 在jsp当中调用外部类的方法。
动作标签出现的年代是在JSP model1 .
<jsp:forward page="/index.jsp"></jsp:forward> 主要用于请求转发forward。现在可以直接使用request对象进行实现。
<jsp:include page="/index.jsp"></jsp:include> ==> 将来可能会使用.
<% request.getRequestDispatcher("").include(request, response); %>
上面的包含就是之前学习的request的请求包含. 这种包含也叫做动态包含.动态包含的两个页面会分别编译成两个java.class.分别执行. 只是在最后输出时将输出结果合并.所以页面中的变量不会共享.
4.JAVA中内省以及在项目中的应用
java中的内省. 内省是基于java中反射技术的一套工具API.
功能: 用来操作javaBean的属性的(用来操作java对象的属性,设置值和取值,特别简单,经常用的).
javaBean: 就是类. 只要符合以下规则就可以称这个类为javaBean. 比如Student这个类(类和对象是不一样的,javaBean不是对象,一定要记得)
javaBean的规则:
1.必须有空参的构造方法.
2.所有属性提供共有的Get/Set方法.
3.所有属性对应的字段私有.
4.(可选)实现串行化接口.
属性指的就是提供get/set方法的.
而类的中成员变量不能称为属性.
如果你的类符合javabean规则. 那么久可以使用内省这套api来操作你的bean.
//----------------------------------------------------------------------------------------------
//使用内省操作bean的例子.
//功能: 使用内省技术将User对象的name属性设置为rose;
//使用内省的应用: 可以将表单中的参数封装到JavaBean中。
//上面的方法是模拟BeanUtils工具类的对应方法.
BeanUtils.populate(u, request.getParameterMap());
注意: 表单的参数的键必须与Bean中属性名称对应.
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
//解决乱码问题
request.setCharacterEncoding("UTF-8");
//获取用户输入的用户名和密码并封装成对象
//凡是通过表单提交的参数,都要进行这样的处理特别麻烦。使用内省进行操作
//String name = request.getParameter("name");
//String password = request.getParameter("password");
//LoginEntity loginEntity = new LoginEntity();
//loginEntity.setName(name);
//loginEntity.setPassword(password);
//思路:自动将表单信息封装到Bean中
//遍历Bean中国的属性,从封装表单的map集合中,以属性名为键进行查找提交上来的值,
//如果找到值,那么就将值封装到Bean对应的属性中
//如果能够取到值,,就放进去
LoginEntity loginEntity = new LoginEntity();
//Map<String, String[]> paras = request.getParameterMap();
try {
//processMethod(loginEntity, paras);
//使用BeanUtils进行处理
//注意:集合不能事String的泛型
BeanUtils.populate(loginEntity,request.getParameterMap());
//打印属性的值,基础好便于对框架原理的理解
System.out.println(loginEntity.toString());
} catch (Exception e) {
e.printStackTrace();
}
}
private void processMethod(LoginEntity loginEntity, Map<String, String[]> paras) throws Exception {
BeanInfo beanInfo = Introspector.getBeanInfo(LoginEntity.class);
PropertyDescriptor[] pds = beanInfo.getPropertyDescriptors();
//遍历属性数组,判断当前遍历的属性是否是name属性
for (PropertyDescriptor pd : pds) {
String name = pd.getName();
String[] values = paras.get(name);
//表示在map中找到了对应的提交信息
if (values != null && values.length > 0) {
Method method = pd.getWriteMethod();
//将用户提交的信息封装到对应的属性当中
method.invoke(loginEntity, values[0]);
}
}
}
BeanUtils 工具类的使用:(需要导入jar包)
BeanUtils 可以自动帮你转换8个基本数据类型. 如果遇到自定义类型需要转换.我们要自己写一个转换器并注册:实现Converter接口
转换器注册
注册我们自定义的转换器
参数1 自定的转换器;参数2 注册转换的类型
ConvertUtils.register(new MyDateConverter(), Date.class);
完成如上两步即可.
5. EL表达式与EL函数 :${ var } 主要用于获取变量的值,并进行输出。
EL表达式为了替代页面上的java代码 .
<%= %>输出脚本 ==> el表达式为了替换该脚本, EL表达式就是用来输出的.
格式: ${}
主要有三个知识点:1)内置对象,2)使用el表达式操作array,list,map,javabean等,3)EL在输出的同时还可以对输出的内容进行简单的判断。
1)EL中有11个内置对象,常用的5个,pageScope,requestScope,sessionScope,applicationScope,pageContext(这就是对象的名字)
2)EL可以显示 JavaBean,Map,List,Array 当中的内容或属性
3)EL在输出的同时还可以对输出的内容进行简单的判断
注意:empty 运算符 判断的时候是根据判断的内容标准变化.
<%@ page import="java.util.ArrayList" %>
<%@ page import="java.util.Map" %>
<%@ page import="java.util.HashMap" %>
<%@ page import="com.tniu.app.entity.LoginEntity" %><%--
Created by IntelliJ IDEA.
User: Administrator
Date: 2017/7/8
Time: 17:55
To change this template use File | Settings | File Templates.
--%>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>EL的学习</title>
</head>
<br>
<!--往四大域中存入值-->
<% request.setAttribute("name", "requestTom"); %>
<% session.setAttribute("name", "sessionTom");%>
<% application.setAttribute("name", "applicationTom");%>
<% pageContext.setAttribute("name", "pageContextTom");%>
<!--取出域中对象的值,可以成功-->
<%=request.getAttribute("name")%>
<%=session.getAttribute("name")%>
<%=application.getAttribute("name")%>
<%=pageContext.getAttribute("name")%>
<% Cookie cookie = new Cookie("name", "tomCookie");
response.addCookie(cookie);
%>
<!------------------------------------------------------------------------------------->
<!--el表达式中的几个常用对象,都是以Scope结尾的-->
<!--通过el表达式实现,与上面的操作是等价的-->
${requestScope.name} --</br>
${sessionScope.name} --</br>
${applicationScope.name} --</br>
${pageScope.name} --</br>
<!--el表达式和输出脚本的区别:如果el表达式取不到值,就什么也不输出,但是如果是输出脚本,就直接输出null-->
${requestScope.password} 什么也不输出--</br>
<%=request.getAttribute("password")%> --</br>
<!--输出项目路径,项目路径是空的,pageContext对象的使用-->
${pageContext.request.contextPath}/AServlet
<!--打印客户端ip地址-->
${pageContext.request.remoteAddr}
<!--获取表单提交的参数,键为name的值-->
${param.name} </br>
<!--获取表单提交的参数,键为name的数组-->
${paramValues.name} </br>
<!--获取键为name的cookie值-->
${cookie.name.value} </br>
<!------------------------------------------------------------------------------------->
<!--使用EL表达式操作集合、数组、javabean-->
<!--把数组,集合,放入域对象当中,一直在使用requestScope这个EL对象-->
<!--使用EL表达式从数组中取出值-->
<% String[] arr = {"test1", "test2"};
request.setAttribute("arr", arr);
%>
${requestScope.arr[0]} </br>
${arr[0]} </br>
<!--使用EL表达式从集合中取出值:取出集合当中第二个元素-->
<% ArrayList list = new ArrayList();
list.add("rose1");
list.add("rose2");
request.setAttribute("list", list);
%>
${requestScope.list[1]} </br>
${list[1]} </br>
<!--使用EL表达式从map集合中取出值-->
<% Map map = new HashMap<>();
map.put("name.n", "test");
map.put("age", 99);
request.setAttribute("map", map);
%>
${requestScope.map.name} </br>
${requestScope.map.age} </br>
<!--如果键中有.进行分割,就使用下面这种方式,上面的方法可能就不能用了-->
${requestScope.map["name.n"]} </br>
<!--使用EL表达式从javaBean中取出值-->
<%
LoginEntity loginEntity = new LoginEntity();
loginEntity.setName("response");
request.setAttribute("loginEntity", loginEntity);
%>
${requestScope.loginEntity.name} </br>
${loginEntity.name} </br>
<!------------------------------------------------------------------------------------->
<!--使用EL表达式对输出结果进行判断操作-->
<% request.setAttribute("age1", 33);
request.setAttribute("age2", 22);
%>
${requestScope.age1>request.age2} </br>
${requestScope.age1>=request.age2} </br>
${requestScope.age1<request.age2} </br>
${requestScope.age1<=request.age2} </br>
${requestScope.age1!=request.age2} </br>
${true||false} </br>
<!--判断数组是否有值-->
<% String str = "";
request.setAttribute("str", str);
%>
判断字符串是否有值--->${empty requestScope.str}
返回true,表示是空的
</body>
</html>
Cookie&Session都是属于会话技术.都是用来保存会话状态的技术.其中客户端保存信息的技术是cookie. 服务器端是session.
Cookie原理: 服务器发送 set-Cookie的响应头. 要求浏览器记住(键值对). 在浏览器的后续访问中,如果访问路径符合cookie的规则,那么浏览器会使用.Cookie 请求头将键值对发送给服务器.
服务器端操作: 新建一个cookie,并发送给客户端
Cookie cookie = new Cookie(键,值); response.addCookie(cookie);
客户端操作:获得浏览器发送的cookie: Cookie[] cookies = request.getCookies(); //之后遍历并筛选要找到的cookie.
Cookie的细节:
1、 cookie的保存时间. 该有效时间是绝对的. 设置为2周,那么不受其他因素影响,只能存在2周.除非覆盖修改成其他时间.
cookie.setMaxAge(int 秒); 正整数 : 设置有效时间;0 : 到了浏览器端就删除. 为了覆盖之后删除;-1: 默认值, 关闭浏览器时删除.
2、Cookie的路径api:cookie.setPath
默认值: 发送cookie的资源所在路径..
访问时: 访问的路径属于浏览器记录的cookie的子路径.那么浏览器将会发送cookie.
3、浏览器端保存cookie的数量有限制.
例子:访问历史记录
例子:记住用户名.
Session
服务器端保存信息的技术.一个会话可能有多个请求。
原理: 浏览器连接到服务器时,如果没有携带sessionID那么服务器会创建一个session对象并产生一个唯一的ID. 响应时服务器会把sessionID发送给
浏览器要求浏览器记住sessionID(Cookie), 浏览器下次访问时携带sessionID.服务器就可以找到与之对应的session对象.
如何判断浏览器是否是第一次访问服务器?看看是否携带SessionId或者Cookie
获得session: request.getSession();
session细节:
1、 session的有效时间:默认值: tomcat中默认是30分钟.修改web.xml中的
<session-config> <session-timeout>30</session-timeout> <session-config>
以上配置可以加在 tomcat下的web.xml 或 项目下的web.xml. 影响的范围不同.
2、 session生命周期
session的创建: 浏览器第一次访问.
session的销毁: 1> session过期时 自动销毁.2、手动调用销毁方法 session.invalidate();
Cookie与Session的模板性代码
案例:(把案例记到脑子里面)
Jsp中page指令
Jsp错误页面的配置:exception对象的使用
Jsp的静态包含指令include,合并效果的实现,变量共享的实现
Jsp中的九大内置对象,每个对象都有不同的作用:session、request、application、pageContextout(四个域对象)、exception、
Jsp中的out对象特征,与Response.getWrite()进行对比
pageContext域对象的使用:三个方法
什么是动态包含? 一定要记得
使用过的jar包,每个jar包都是做什么用的,框架名一般都小写 Beanutils,logging。
如果是通过maven下载的依赖,要在pom.xml文件中配置。
BeanUtils没有转化成功, 乱码问题还是没有得到解决。
J2EE的工作很简单:
前端就写一些JSP页面,后台就写Servlet。
热更新的注意事项:
Tomcat热更新功能的使用:修改完jsp代码之后,需要进行编译,才能访问,否则会报出异常:org.apache.jasper.JasperException: Unable to compile class for JSP(未能期编译)