关于昨天作业的分析:
1.JSP
Java Server Pages是java动态页面的规范。tomcat做了实现。
jsp的特点:
1.就可以把jsp当做html来对待,但是它相较于html还有很大的优势
2.这里面可以嵌套java代码,然后显示动态数据
3.java代码不可以随意的书写,需要在特殊的标签里才有效
1.1.jsp原理
从本质上来说,jsp也就是servlet。
从访问http://localhost:8080/index.jsp开始分析。
1.当前请求会交给谁?tomcat里面配置了一个servlet,它的url-pattern是*.jsp *.jspx,所以当前请求会交给它
2.这个servlet其实就是jsp引擎。该引擎做了一个什么事情呢?首先根据你访问的资源index.jsp,到指定目录下去寻找该文件,如果找得到,则将该文件翻译成java文件(其实就是servlet,形成index_jsp.java文件),之后再经过编译,形成index_jsp.class文件
3.会调用servlet的service方法,然后显示出页面里面的数据
1.1.1.既然jsp本质来说就是servlet,那么在哪?
该目录就是IDEA复制tomcat配置文件,开启新的tomcat的目录。
查看java文件源码,发现的确就是servlet。
index_jsp.java:
HttpJspBase是继承自HttpServlet的,所以jsp本质上也是Servlet
Servlet三要素:init、service、destroy
service方法就是调用Writer写出数据到响应报文中。
对于绝大多数的数据,完全是直接包裹起来,写给客户端
只是在jsp一些特殊标签里面,会做一些处理。
1.1.2.既然jsp也是servlet,为什么大费周章地去弄一个jsp
利用各自的长处,各司其职。代码会比较容易进行维护。逻辑代码变更不会影响到页面代码,页面代码需要维护,也不会一不小心影响到逻辑代码。
不管是JSP还是Servlet,虽然都可以用于开发动态web资源。但由于这2门技术各自的特点,在长期的软件实践中,人们逐渐把servlet作为web应用中的控制器组件来使用,而把JSP技术作为数据显示模板来使用。
其原因为,程序的数据通常要美化后再输出:
让JSP既用java代码产生动态数据,又做美化会导致页面难以维护。
让servlet既产生数据,又在里面嵌套html代码美化数据,同样也会导致程序可读性差,难以维护。
因此最好的办法就是根据这两门技术的特点,让它们各自负责各得,servlet只负责响应请求产生数据,并把数据通过转发技术带给jsp,数据的显示jsp来做。
1.2.jsp语法
jsp其实就是将绝大多数的代码原封不动的发送给客户端,让客户端去处理,但是自己只会去处理一部分,这部分其实就是jsp语法。
1.2.1.jsp表达式
jsp表达式里面的java代码,会原封不动地被out.print方法包裹起来。最终翻译过后要符合java的语法。
1.2.2.jsp脚本片段
注释完全可以写
jsp脚本片段可以写多个,每一个的话允许不符合java语法,但是最终拼接出来的结果一定要符合java语法。
1.2.2.1.jsp脚本片段和jsp表达式是否都可以向客户端输出数据
jsp表达式肯定可以输出。脚本片段能不能输出,取决于你调不调用response相关代码。
out.print(i);就是response相关代码。
<%
for (int i = 0; i < 10; i++) {
out.print(i);
%>
<h1>hello world</h1>
<%
}
%>
1.2.3.jsp声明
1.2.4.jsp注释
是写在主体区域里面的,不能嵌套在jsp表达式、脚本片段、声明这些里面,只能写在页面的主体部分。
html注释会原封不动的翻译到客户端。
jsp注释,在翻译成为java代码时,就会消失。
1.3.jsp九大对象
在service方法内部会创建出来9个对象,可以直接供我们来使用。默认情况下只能看到8个,如果想看到第9个,需要设置isErrorPage=true
Request
Response
pageContext
Session
Exception
Application–servletContext
Config
Out
Page
演示session的时候,我把项目完成以后打开页面的勾选去掉了,为什么?和jsp有关系。不让它自动去打开jsp页面。
如果让jsp页面自动打开,那么就会去创建一个session,创建了session,后面在servlet中演示的时候,就会看不到创建session的过程。
1.3.1.pageContext对象
九大对象中最为核心的一个对象。通过该对象可以获得其他八个对象。
1.3.1.1.API
本身也是一个域。Request、session、application。
该域大小是当前页面内。
1.域API
2.可以给其他域赋值
第三个参数表示一个范围,通过设定这个参数给其他域赋值。
如果让你来实现,你怎么实现?
setAttribute(key,value,scope){
If(scope ==1){
pageContext.setAttribute(key,value)
}else if(scope == 2){
pageContext.getRequest.setAttribute(key,value)
}
}
3.findAttribute
演示步骤:
Object.jsp:
Page.jsp:
- 首先在当前页面内先不转发,直接打印pageContext.findAttribute(“name”)
显示的是page - 调用转发,然后在被转发页面打印该语句,显示的是request
- 直接访问被转发页面page.jsp,打印的是session
- 在page.jsp中调用session.invalidate,打印的是application
得出结论:
一级一级去查找。从最小域pageContext域开始,依次request、session、application域去查找,在某个域找到数据则结束,找不到则接着下一个,,,,,,
1.3.2.out对象
输出数据到客户端。带有缓存功能的Writer。
冲洗条件:
1.buffer缓冲区满
2.关闭buffer缓冲区
3.页面需要响应
Tip: out隐式对象的工作原理图
先输出ServletResponse.getWriter()中的数据,再输出out中的数据
我们可以关闭JspWriter对象的缓冲区功能,通过在page中设置buffer=”none”
为什么要有这个JspWriter对象的缓冲区呢?
减缓了频繁向response中进行书写。
Vue才是工作中真正的页面解决技术。
Jsp是比较老旧的技术
1.4.如何servlet debug
1.明确代码执行流程是什么样的?
2.找到servlet的入口,service方法(doGet或者doPost)(看form的method, a标签默认get请求)
3.在方法第一行打一个断点。
4.一步一步调试
5.该看的都看了之后,点击resume program,会直接执行到下一个断点,如果没有下一个断点,则直接全部执行完毕
登录案例:
Login.jsp:
<%--
Created by IntelliJ IDEA.
User: zsquirrel
Date: 2020/6/29
Time: 2:42 下午
To change this template use File | Settings | File Templates.
--%>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>Title</title>
</head>
<body>
<form action="<%=request.getContextPath()%>/user" method="post">
<%--type="hidden"意思就是可以用来传输数据,但是不会显示在页面上--%>
<%--整个逻辑:这是登录界面,提交后跳转到/app/user--%>
<input type="hidden" name="op" value="login">
<input type="text" name="username"><br>
<input type="password" name="password"><br>
<input type="submit">
</form>
</body>
</html>
UserServlet:
package com.cskaoyan.login;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
/**
* @author shihao
* @create 2020-06-30 21:23
*/
@WebServlet("/user")
//高内聚,低耦合
//相关联的代码写的紧密一些,模块和模块之间尽可能松散的耦合,不会产生过多的依赖
public class userServlet extends HttpServlet {
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
String op = request.getParameter("op");
//首先会来到这,因为刚开始我们就设定的op=login
if ("login".equals(op)) {
login(request, response);
}
}
private void login(HttpServletRequest request, HttpServletResponse response) throws UnsupportedEncodingException {
request.setCharacterEncoding("utf-8");
//MIME类型,如果浏览器解析不了就会下载下来
response.setContentType("text/html;charset=utf-8");
String username = request.getParameter("username");
String password = request.getParameter("password");
if ("admin".equals(username) && "admin".equals(password)) {
request.getSession().setAttribute("username", username);
request.getSession().setAttribute("password", password);
//刷新到/app/info.jsp页面
response.setHeader("refresh", "2,url=" + request.getContextPath() + "/info.jsp");
}
}
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
String op = request.getParameter("op");
if ("logout".equals(op)) {
//从infor.jsp页面中返回的数据中op=logout
logout(request, response);
}
}
private void logout(HttpServletRequest request, HttpServletResponse response) throws IOException {
request.setCharacterEncoding("utf-8");
response.setContentType("text/html;charset=utf-8");
request.getSession().invalidate();
response.getWriter().println("注销成功,调转回登录页面");
response.setHeader("refresh", "2,url=" + request.getContextPath() + "/login.jsp");
}
}
Info.jsp:
<%--
Created by IntelliJ IDEA.
User: Administrator
Date: 2020/6/30
Time: 21:02
To change this template use File | Settings | File Templates.
--%>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>Title</title>
</head>
<body>
<%--超链接到/app/user?op=logout页面,这个时候op=logout--%>
欢迎您,<a href="<%=request.getContextPath()%>/user?op=logout"><%=session.getAttribute("username")%>点我注销</a>
</body>
</html>
2.Listener(监听器)–记住一个listener即可,过程能够掌握更好
监听器。这个listener是接下来Spring开始的入口。
现实生活中的案例:
监听对象:艺人明星
监听事件:吸毒嫖娼
监听器:朝阳人民群众
触发事件:报警
Web:
监听对象:ServletContext
监听事件:context创建和销毁
监听器:自己编写的一个监听器
触发事件:监听器里面的代码执行
20年前写好的代码是怎么知道去调用现在编写的一个方法的呢?
2.1.如何去编写一个listener呢?
- 编写一个类实现ServletContextListener接口
- 注册该Listener(@WebListener)
MyServletContextListener :
import javax.servlet.ServletContext;
import javax.servlet.ServletContextEvent;
import javax.servlet.ServletContextListener;
import javax.servlet.annotation.WebListener;
/**
* @author shihao
* @create 2020-06-30 22:41
*/
@WebListener
public class MyServletContextListener implements ServletContextListener {
@Override
public void contextInitialized(ServletContextEvent servletContextEvent) {
System.out.println("context init");
}
@Override
public void contextDestroyed(ServletContextEvent servletContextEvent) {
System.out.println("context destroy");
}
}
2.2.回调案例
员工老板。员工工作工作完成之后汇报给老板。
整一个leader接口,manager和boss都继承leader
Leader:
package update;
public interface Leader {
void report();
}
Employee:
package update;
public class Employee {
private Leader leader;
public Employee(Leader leader) {
this.leader = leader;
}
public void work(){
System.out.println("员工在工作");
System.out.println("........");
System.out.println(".........");
System.out.println(".........");
leader.report();
}
}
Test:
package update;
public class Test {
public static void main(String[] args) {
//Leader leader = new Boss();
Leader leader = new Manager();
// listener注册然后将其注入到context中
Employee employee = new Employee(leader);
// context.init()---内部 listener.contextInitialized
employee.work();
}
}
Listener的实现原理:
Listener 就可以类比以上代码的关系,ServletContext相当于employee,ServletContextListener相当于leader接口,MyServletContextListener相当于boss和manager类。
而注册listener就相当于在ServletContext的创建代码中有这么一段:listener.contextInitialized、在ServletContext的销毁代码中有这么一段:
Listener.contextDestroyed
listener分类:三个域对象创建销毁的listener