Java入门(九)Servlet JSP MVC

📢:哈喽~☀️欢迎加入程序🐒大家庭,快点开始✒️自己的黑客帝国吧 ~🌊🌊

内容简述:Http协议、Servlet工作原理、DAO、Servlet生命周期、JSP基础、cookie、Session、过滤器、监听器、线程安全、JSTL、自定义标签、MVC。

一、Servlet基础

1.什么是Servlet?

  • sun公司制订的一种用来扩展web服务器功能的组件规范。

扩展web服务器功能

  • a. web服务器通常只能处理静态资源 (即需要事先将html文件写好并存放在服务器上)的请求,

  • 不能够处理动态资源(即需要计算,动态生成相应的页面)的请求。

  • b. 早期,开发者会使用CGI程序来扩展web服务器功能。

    了解:CGI程序指的是使用c,perl等语言开发的符合CGI(Common Gateway Interface)标准的程序。因为CGI程序开发繁琐,并且可移值性不好,所以用得越来越少了。

  • c. 可以使用Servlet来扩展web服务器功能。

    补充:当web服务器收到请求之后,如果是动态资源的请求,可以调用servlet来处理。

组件规范

  • a. 什么是组件?

    • 符合规范,具有一定功能,并且需要部署到相应的容器上才能运行的软件模块。
    • Servlet就是一个符合Servlet规范的组件,需要部署到Servlet容器里面才能运行。
  • b. 什么是容器?

    • 符合规范,提供组件的运行环境的程序。
    • Servlet容器(比如Tomcat)提供Servlet运行环境。

在这里插入图片描述

2.如何开发一个Servlet?

  • step1. 写一个java类,实现Servlet接口或者继承HttpServlet类。

  • (通常我们选择继承HttpServlet类)

  • step2. 编译。

  • step3. 打包。

    • 需要创建一个具有如下结构的文件夹:
      • appname (名称可以自定义) 应用名
      • WEB-INF
      • classes (存放.class文件)
      • lib(可选,用来存放.jar文件)
      • web.xml (部署描述文件)
  • step4. 部署。

    • 将step3创建好的文件夹拷贝到容器指定的位置。

      (也可以将step3创建好的文件夹使用 jar命令压缩成.war结尾的文件,然后拷贝)

  • step5. 启动容器

    • 打开浏览器,在地址栏输入:http://ip:port/appname/url-pattern

3.安装Tomcat(Tomcat是一个相对标准的容器)

http://localhost:8080

创建servlet测试项目:

1)创建一个maven项目;

在这里插入图片描述

2)设置响应的名称并且把打包方式改为war;

3)问题:创建后的day01项目上有一个小红叉;

解决办法:在javaEE模式下,右键Deployment选择Generate Deployment.,然后小红叉消失了。

原因:之所以有小红叉是因为没有生成部署描述文件(web.xml),操作之后,在src文件夹下main文件夹下的webapp中会出现一个WEB-INF文件夹,里面有web.xml文件。

在这里插入图片描述

4)右键day01项目,选择最后一项Properties,然后选择Targeted Runtimes,在右侧选中tomcat7.0
原因:这一步是为了将想要运行的tomcat 的相关jar包导入我们的项目当中;

在这里插入图片描述

5)在Java Resources中的src/main/java中创建Serlvet类:

public class TimeServlet extends HttpServlet {

    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp)
        							throws ServletException, IOException {
        // 1.使用request接收请求数据
		// 1)请求行(3组数据)
        System.out.println("协议类型:" + req.getProtocol());
        System.out.println("访问路径:" + req.getServletPath());
        System.out.println("请求方式:" + req.getMethod());

        // 2)消息头(N组数据)
        // 该数据是按照键值对的方式存储
        // Enumeration是一个古老的迭代器
        // 其用法和Iterator相似.
        Enumeration<String> headerNames = req.getHeaderNames();
        while(headerNames.hasMoreElements()) {
            String key = headerNames.nextElement();
            String value = req.getHeader(key);
            System.out.println(key + ":" + value);
        }

        // 3)实体内容
        // 获取服务器的时间
        Date date = new Date();
        SimpleDateFormat sdf = new SimpleDateFormat("HH:mm:ss");
        String now = sdf.format(date);

        // 将时间拼到一个网页里给浏览器返回
        // 告诉浏览器向它发送的是什么类型的内容
        resp.setContentType("text/html");
        // 设置编码格式,不然中文不能正常显示
        resp.setCharacterEncoding("utf-8");
        // 获取输出流,该流指向的目标是浏览器
        PrintWriter w = resp.getWriter();
        // 此处偷懒,拼一个简化版的网页
        w.println("<lable>服务器时间:</lable>");
        w.println("<p>" + now + "</p>");
        // 注意,不用关闭输出流,servlet会自动关闭
    }
}

输出结果

在这里插入图片描述

doGet()方法和service()方法的区别:

  • doGet()方法只处理Get请求;
  • doPost()方法只处理Post请求;
  • service()方法即处理Get请求,也处理Post请求;
  • 官方文档上建议尽量避免使用service()方法。

6)在web.xml中配置serlvet

<?xml version="1.0" encoding="UTF-8"?>

<web-app xmlns:xsi=**"http://www.w3.org/2001/XMLSchema-instance"** xmlns=**"http://java.sun.com/xml/ns/javaee"** xsi:schemaLocation=**"http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"** version=**"2.5"**>

 <servlet>
 	<servlet-name>helloServlet</servlet-name>
 	<!-- 要写完整的类名(要注意大小写) -->
 	<servlet-class>web.HelloServlet</servlet-class>
 </servlet>
 <servlet-mapping>
 	<!-- 两个name要一致 -->
 	<servlet-name>helloServlet</servlet-name>
 	<!-- 告诉容器,如何访问该Servlet -->
 	<url-pattern>/hello</url-pattern>
 </servlet-mapping>
</web-app>

在这里插入图片描述

7)运行:在项目处右键→Run as→Run on Server→选中Tomcat 7.0→点next→点finish→重启Server→完成

4.Servlet是如何运行的?

比如,在浏览器地址栏输入:http://ip:port/day01/hello

step1.浏览器依据ip和port建立与服务器之间的连接 (比如,建立与tomcat之间的连接)。

step2.浏览器将相关数据打包 (即按照http协议的要求,创建请求数据包)。

step3.浏览器将请求数据包发送给服务器。

step4.服务器解析请求数据包的内容,并且将解析到的数据添加到request对象里面,同时创建一个response对象。

step5.服务器将对应的Servlet(比如HelloServlet)

实例化,接下来调用Servlet实例的service方法。

注意

  • 服务器会将request和response作为参数传递给service方法,可以通过request对象获取请求数据(比如一些请求参数值),然后通过response对象写入处理结果。

step6.服务器通过response对象获取处理结果,然后创建响应数据包。

step7.服务器发送响应数据包给浏览器。

在这里插入图片描述

step8.浏览器解析响应数据包,并且生成相应的页面。

5.常见的错误及解决方式

(1) 404

  • 404 Not Found,表示服务器依据请求地址找不到对应的资源。

  • 错误原因:

    a.请求地址写错了;

    b.应用没有部署成功。

(2)500

  • 500 Internal Server Error,表示服务器端的程序运行出错。

  • 错误原因:

    a.没有继承HttpServlet或者实现Servlet接口;

    b.web.xml配置错误,比如servlet-class写错;

    c.程序写得不够严谨,异常报错,比如将一个非数字转换成数字。

(3)405

  • 405 Method Not Allowed(GET请求和POST接口对接时就会报405)
  • 错误原因:
    • service()方法签名不符合要求(方法名,参数类型,异常类型要符合规范–两个异常)。

课堂练习

练习1:

  • 写一个DateServlet,输出当前的系统日期, 比如在浏览器地址栏输入 http://ip:port/day01-lab/date 显示  2018-02-06
  • 建议新建工程,从零开始写。

练习2:

  • 写一个BmiServlet,计算一个人的bmi指数。
  • bmi指数 = 体重(公斤) / 身高(米) / 身高(米)
  • http://ip:port/day01-lab/bmi?weight=80&height=1.5

二、HTTP协议、抓包、Servlet工作原理

1.http协议

什么是http协议?

  • 一种网络应用层协议,规定了浏览器与web服务器之间如何通信以及通信过程当中所使用的数据格式。

http协议如何通信

  • step1.建立连接;

  • step2.发送请求;

  • step3.发送响应;

  • step4.关闭连接

  • 即“一次请求,一次连接”,如果要发送新的请求,需要重新建立新的连接。

    在这里插入图片描述

http协议的优点:

  • web服务器可以使用尽可能少的连接为更多的请求服务。

2.数据格式

抓包:

1)在Eclipse中Windows→Show View→Others→Debug→TCP/IP Monitor

2)在最左上角的框中右键,点击Properties

3)点击Add,在Local monitoring port处填写监视器的端口号(比如8888)

  • 在Host name处填写想要拦截的地址(localhost);
  • 在Port处填写想要拦截的端口号(8080)如图:

在这里插入图片描述

4) 在浏览器输入网页的网址,只不过网址的端口号改为监视器的端口号

在这里插入图片描述

在这里插入图片描述

5)在Eclipse的监视器的框中便会显示抓到的信息(第二行,左边是浏览器发送给服务器的请求内容,有边是服务器发送给浏览器的响应内容)

1)请求数据包

  • 请求行 (请求方式 请求资源路径 协议类型和版本)

  • 若干消息头

    • 消息头是一些键值对(使用":"隔开),可以用来传递一些特定的信息;

      比如,浏览器可以发送 user-agent消息头告诉服务器,浏览器的类型和版本。

  • 实体内容

    • 只有请求类型为post方式时,实体内容才会有数据。

2)响应数据包

  • 状态行 (协议类型和版本 状态码 状态描述)

  • 状态码是一个三位数字,表示服务器处理请求的一种状态,200(正常),500(系统出错),404(找不到对应的资源)。

  • 若干消息头

    • 服务器也可以发送一些消息头给浏览器;

      比如:发送 content-type 消息头,告诉浏览器,服务器返回的数据类型。

  • 实体内容

    • 程序返回的处理结果,浏览器会解析出来,并生成相应的页面。

(3)请求方式

  • GET 请求

    • 浏览器在什么情况下会发送get请求?

      a.在地址栏直接输入某个地址。

      b.点击链接。

      c.表单默认提交方式。

    • GET 请求的特点:

      a.会将请求参数显示在浏览器地址栏,不安全。比如,路由器会记录请求地址。

      b.会将请求参数添加到请求行里面,只能提交少量的数据(整个请求行大概能存放2k左右的数据)。

  • POST 请求

    • 浏览器在什么情况下会发送post请求?

      a.将表单的提交方式设置为post。

    • post请求的特点:

      a.不会将请求参数显示在浏览器地址栏,相对安全。

      • 并不会对请求参数进行加密处理,所以,对于敏感数据,需要加密(使用https协议)

      b.会将请求参数添加到实体内容里面,可以提交大量的数据。

Servlet输出中文,要注意什么?

  • (1)为什么会有乱码?
    • out默认会使用iso-8859-1来编码。
  • (2)如何解决?
    • response.setContentType(“text/html;charset=utf-8”);

如何获得请求参数值?

  • (1) String request.getParameter(String paramName)

    • a.paramName是请求参数名。
    • b.如果paramName写错,则获得null值。
    • c.表单提交时,如果没有填写任何数据,会获得空字符串。
  • (2) String[] request.getParameterValues(String paramName)

    • a.当有多个请求参数名相同时,使用该方法。
    • b.对于多选框,如果没有选择任何选项,则获得null值。

表单提交时,如何读取中文参数值?

  • (1) 为什么会有乱码?

  • 表单提交时,浏览器会对中文参数值进行编码(会使用打开表单所在的页面时的字符集来编码)。而服务器端默认会使用iso-8859-1来解码。

  • (2) 如何解决?

      1. POST 请求

      request.setCharacterEncoding(String charset)

      • 这行代码要添加到所有的getParameter方法的前面。只针对post请求有效。
      1. GET 请求

      配置 URIEncoding=“utf-8”。

      • 只针对get请求有效。

课堂练习

练习1:注册用户

参考代码如下:

public class RegServlet extends HttpServlet {
    private static final long serialVersionUID = 1L;

    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOExceptio {
        // 采用方案三解决POST请求乱码问题
        request.setCharacterEncoding("utf-8");
        
        // 处理请求的一般流程
        // 1.接收参数
        String username = request.getParameter("username");
        String password = request.getParameter("password");
        String[] hobby = request.getParameterValues("hobby");
        
        // 采用方案一解决乱码问题
        //     byte[] bs = username.getBytes("iso8859-1");
        //     username = new String(bs, "utf-8");
        // 2.处理业务
        System.out.println("username:" + username)
        System.out.println("password:" + password);
        System.out.println("hobby:");
        for (String str : hobby) {
            System.out.println("   " + str);
        }
        
        // 3.发送响应
        response.setContentType("text/html;charset=utf-8");
        PrintWriter pw = response.getWriter();
        pw.println("<h1>恭喜,亲爱的" + username + ",您已注册成功!</h1>");
    }
}

练习2:添加用户

CREATE TABLE t_user(
  id INT PRIMARY KEY AUTO_INCREMENT,
  username varchar(50) unique,
  password varchar(20),
  phone varchar(20)
);

SELECT e.ename,d.dname 
FROM EMP e 
JOIN Dept d ON e.deptno=d.deptno 
WHERE e.sal = (SELECT max(sal) FROM EMP);

三、DAO、重定向

在这里插入图片描述

1.DAO (Data Access Object 数据访问对象)

(1)什么是DAO?

  • 封装了数据访问逻辑的对象。

(2)如何写一个DAO?

  • step1.写一个实体类(一个java类,里面有字段、get()、set()、toString()方法)

    • 将查询到的记录存放到一个java对象里面,就需要设计一个对应的java类(即实体类),该类与要访问的表要一致(字段与属性要一一对应)。

    • 比如,要访问的表是:

      t_user(id,username,password,phone) 就可以设计 User类(id,uname,pwd,phone)

  • step2.写一个DAO类

    • 封装数据访问逻辑,比如,将查询到的记录中的信息存放到一个实体类的实例里面。

2.重定向

(1)什么是重定向?

  • 服务器通过发送302状态码及Location消息头(该消息头的值是一个地址,一般称之为重定向地址)

    通知浏览器访问一个新的地址(即重定向地址)。

在这里插入图片描述

(2)如何重定向?

  • response.sendRedirect(String url);
    • url为重定向地址;
    • 重定向之前,容器会先清空response对象上存放的所有数据,然后再重定向。

(3)特点

  • a.重定向地址是任意的。
  • b.重定向之后,浏览器地址栏的地址会发生变化。

练习:

  • 实现添加员工、员工列表功能
-- 员工表:
create table t_emp(
 id int primary key auto_increment,
 ename varchar(50),
 salary double,
 age int
);

四、Servlet生命周期、JSP基础

1.Servlet生命周期

(1)什么是Servlet的生命周期?

  • 容器如何创建Servlet实例,如何对其进行初始化,
  • 如何调用其方法来处理请求,以及如何销毁其实例的整个过程。

在这里插入图片描述

(2)生命周期分成哪几个阶段?(4个阶段–实例化、初始化、调用、销毁)

  • 1.实例化

    • a.什么是实例化?

      容器调用Servlet的构造器,创建相应的对象。

    • b.什么时候实例化?

      情形1:容器收到请求之后才创建实例。(默认)

      情形2:容器启动之后,立即创建实例。(需要额外配置)

<servlet>
 <servlet-name>someServlet</servlet-name> 
 <servlet-class>web.SomeServlet</servlet-class>
 <!-- 
 	配置启动加载(即立即创建其实例)
 	注:
 		其值是一个大于零的整数,越小,
 		优先级越高(其先被创建)
  -->
  <load-on-startup>1</load-on-startup>
 </servlet>

( 注:容器只会创建一个实例,单例模式。)

  • 2.初始化

    • a.什么是初始化?

      容器调用Servlet实例的init方法。( 注:该方法只会执行一次!)

    • b.GenericServlet的init方法?

      将容器传递过来的ServletConfig对象保存下来了,并且提供了一个getServletConfig方法用来获得该对象。

    • c.如何实现自已的初始化处理逻辑?

      重写 GenericServlet提供的init()即可。(不带参的那个方法)

      在这里插入图片描述

    • d.初始化参数

      • step1.先配置初始化参数

      • step2.调用ServletConfig提供的 getInitParameter方法

        @Override
        public void init(servletconfig config) throws ServletException {
            // 保存容器传递过来的 ServletConfig对象
            this.config = config;
        
            // 便于开发人员去扩展自已的初始化逻辑
            // (只需要 override下面这个init方法即可)。
            this.init();
        }
        
  • 3.调用(就绪)

    在这里插入图片描述

    • a.什么是就绪?

      容器调用Servlet实例的service方法来处理请求。

    • b.HttpServlet的service方法

      依据请求类型调用对应的doXXX方法。

      比如:get请求会调用doGet方法,post请求会调用doPost方法。

    注意:doGet,doPost方法只是简单的抛出了一个异常。需要开发人员去重写(override)。

    我们要写一个Servlet,既可以重写HttpServlet的service方法,也可以重写其doGet方法和doPost方法。

  • 4.销毁

    • a.什么是销毁?

      容器在删除Servlet实例之前,会调用该实例的 destroy() 方法。(该方法只会执行一次)

    • b.如何实现自已的销毁处理逻辑?

      只需要override GenericServlet的destroy方法即可。

相关的接口与类

  • Servlet接口
    • init()
    • service()
    • destroy()
  • GenericServlet抽象类
    • 实现了Servlet接口中的init和destroy方法。
  • HttpServlet抽象类
    • 继承了GenericServlet抽象类,实现了 service() 方法。

2.JSP

(1)什么是jsp?

  • sun公司制订的一种服务器端动态页面技术规范。

    • a.虽然直接使用Servlet也可以生成动态页面,但是,过于繁琐(需要使用out.println方法输出),也不利于页面的维护(需要修改java代码),所以,sun才制订了jsp技术规范。
    • b.jsp是一个以.jsp为后缀的文件(主要内容是html和少量的java代码),容器会将这个文件转换成相应的Servlet然后执行。
    • jsp的本质是Servlet

    注意:jsp文件在运行之后需要访问才会生成.class文件

(2)如何写一个jsp文件?

  • step1. 添加一个以.jsp为后缀的文件。

  • step2. 在文件当中,可以添加如下内容:

    • 1)html(包括css,javascript) 直接写即可。

    • 2)java代码

      • 方式一: java代码片断 <% java代码 %>

      在这里插入图片描述

      • 方式二 :jsp表达式 <%= java表达式 %>

        在这里插入图片描述

    • 3)隐含对象

      • a.什么是隐含对象?

        在jsp文件当中可以直接使用的对象(比如out,request,response)。

      • b.为什么可以直接使用这些隐含对象?

        容器会自动生成获得这些对象的代码。

    • 4)指令

      • a.什么是指令?

        告诉容器,在将jsp文件转换成Servlet的时候,做一些额外的处理,比如导包。

      • b.语法 <%@ 指令名 属性=值 %>

      • c.page指令

        import属性:导包,比如:(多个包用","隔开)

        <%page import="java.util.*,java.util.*"%>
        

(3)jsp是如何执行的?

  • step1. 容器将jsp文件转换成一个Servlet

    a. html(css,js) ----> 在service方法里,使用out.write输出。

    b. <% %> ----> 在service方法里,照搬。

    c. <%= %> ----> 在service方法里,使用out.print输出。

  • step2.容器调用该Servlet。

五、page指令、转发、JSP开发常见问题

1.page指令

  • contentType属性:设置response.setContentType的值。

pageEncoding属性:告诉容器,在读取jsp文件的内容时,使用指定的字符集去解码。

在这里插入图片描述

注意:charset和pageEncoding的值可以不一样,比如:

在这里插入图片描述

上图是可以运行的,因为charset是给浏览器说明的编码格式,pageEncoding是读jsp文件的时候需要的编码格式,只要是中文就可以读取,但是我们一般使用相同的编码格式。

2.转发

(1)什么是转发?

  • 一个web组件将未完成的处理转交给另外一个web组件继续做。

    web组件(servlet或者是jsp),大多数情况下,是一个servlet获得数据之后,转发给jsp来展现这些数据。

(2)如何转发?

  • step1.绑订数据到request对象上。

    request.setAttribute(String name,Object obj)

    • name 绑订名。
    • obj 绑订值。
    • Object request.getAttribute(String name)
  • step2.获得转发器

    RequestDispatcher rd = request.getRequestDispatcher(String uri);

    • uri:转发的目的地地址,通常是一个jsp。
    • RequestDispatcher:是一个接口,getRequestDispatcher方法会返回该接口的实现。
  • step3.转发

    rd.forward(request,response)

在这里插入图片描述

  • 详细步骤:

1:在浏览器地址栏输入地址后回车发送listUser请求

2:创建request对象

3:就绪,调用service()方法

4:在ListUserServlet中向request对象中绑定数据

5:调用forward()方法转发后,通知容器

6:让web容器去调用另外一个组件的service()方法

7:另一个组件再从request对象中获得之前绑定的数据

注意:转发的本质是通知容器调用(即执行service方法)另外一个web组件。

(3)特点

  • a.转发的目的地有限制(同一个应用)。

  • b.转发之后,浏览器地址栏的地址不变。

3.比较转发与重定向

  • a.能否共享request和response?
    • 转发可以,而重定向不行。
    • 分析:当请求到达容器,容器创建request和response,当响应发送完毕,容器会销毁这两个对象,也就是说,request 和 response 的生存时间是一次请求和响应期间存在。
    • 转发是一次请求,重定向是两次请求。
  • b.浏览器地址栏地址有无变化?
    • 转发无变化,重定向有变化。
  • c.目的地有无限制?
    • 转发有限制(同一个应用),重定向无限制。

如何用Eclipse创建Servlet:

  • step1:右键“src/main/java”,新建一个Servlet

    在这里插入图片描述

  • step2:点击next,可以自定义url-mapping地址名

  • step3:点击next,可以选择默认生成的方法,我们一般只选择默认生成service()方法

在这里插入图片描述

4.web容器

什么是web容器,其作用是? 尽可能写出你知道的容器名称

  • web容器给处于其中的应用程序组件(JSP、SERVLET)提供一个环境;
  • 作用:使JSP、SERVLET直接跟容器中的环境变量接口交互,不必关注其它系统问题;
  • 例如:TOMCAT、WEBLOGIC、WEBSPHERE等都为web容器。

容器如何处理请求资源路径?

  • 比如:http://ip:port/day05/abc.html

  • step1.容器默认认为访问的是一个servlet

    如果地址名和HTML等文件名重复的话先访问servlet,会查看配置文件(web.xml)有没有一个匹配的servlet。

  • step2.如果没有匹配的servlet,则查找对应位置的文件,然后执行(如果找不到,则返回404)。

配置规则

  • (1)精确匹配:

    • 要求url-pattern的值必须等于"/abc.html"。
  • (2)通配符匹配:

    • 使用 * 号匹配任意的零个或者多个字符,比如:
<url-pattern>/*</url-pattern>
<url-pattern>/demo/*</url-pattern>
  • (3)后缀匹配:(注意:后缀匹配不能用/开头)
    • 使用"*."开头,后接一个后缀,比如:
<url-pattern>*.do</url-pattern>
<!-- 用来匹配所有以.do结尾的请求 -->

如何让一个servlet处理多种请求?

  • step1.采用后缀匹配。比如:
<url-pattern>*.do</url-pattern>
  • step2.分析请求资源路径,然后进行不同的处理。

六、路径问题

链接:

<a href="">

表单提交:

 <form action="">

重定向:

response.sendRedirct("");

转发:

request.getRequestDispatcher("");

相对路径

  • 不以/开头的路径。

绝对路径

  • 以/开头的路径。
  • 如何写绝对路径?
    • 链接,表单提交,重定向从应用名开始写;转发从应用名之后开始写。

注意

  • 不要直接将应用名写在路径里面,而应该使用 request.getContextPath()来获取。

  • (应该尽量避免硬编码----把应用名写死)

  • 建议使用绝对路径,好写,并且更好维护。

综合练习:登录

需求

  1. 用户填写用户名和密码并提交;
  2. 服务器端查询数据库,看是否有匹配的记录(即用户名和密码都匹配);
  3. 如果有,则登录成功(返回用户列表);
  4. 如果没有,则登录失败(在登录页面当中,提示用户名或密码错误)。

提示:

  • step1.给UserDAO添加一个方法
public User findByUsername(String uname)
  • step2.添加一个登录页面 login.jsp

  • step3.给ActionServlet添加一个处理 “/login” 的分支,用于处理登录请求。

request.setAttribute("login_failed", "用户名或密码错误");
request.getRequestDispatcher("login.jsp").forward(request,response);

七、状态管理-cookie

1.状态管理

(1) 什么是状态管理?

  • 将浏览器与web服务器之间多次交互做为一个整体来处理,将多次交互所涉及的数据(即状态)保存下来。

(2) 如何进行状态管理?

  • 方式一: 将状态保存在浏览器端,通常使用cookie技术;

  • 方式二: 将状态保存在服务器端,通常使用session技术。

2.cookie

(1) 什么是cookie?

  • 服务器临时存放在浏览器端的少量数据。

(2) cookie工作原理

  • 浏览器访问服务器时,服务器会将一些数据以set-cookie消息头的形式发送给浏览器,
  • 浏览器会将这些数据临时保存下来;
  • 当浏览器再次访问服务器时,会将之前存放的数据以cookie消息头的形式发送给服务器。

在这里插入图片描述

(3) 添加cookie

Cookie c = new Cookie(String name,String value);
response.addCookie(c);

注意:cookie的名称和值都必须是String。

(4) 读取cookie

// 有可能返回null
Cookie[] request.getCookies();
String cookie.getName()
String cookie.getValue();

(5) cookie的生存时间

  • a.默认情况下,cookie会被浏览器保存在内存里面。即,浏览器只要不关闭,cookie就会一直存在。

  • b.setMaxAge(int seconds)

    • 单位是秒。
    • seconds > 0: 浏览器会将cookie保存在硬盘上,当超过指定时间,浏览器会删除cookie。
    • seconds < 0: 默认值(即保存在内存)
    • seconds = 0: 删除cookie。
  • 比如,要修改名称为username的cookie的值:

    • 再发送一个Cookie,名为username,值为想要修改成的值
Cookie c = new Cookie("username", "");
  • 比如,要删除名称为username的cookie:
Cookie c = new Cookie("username", "");
c.setMaxAge(0);
response.addCookie(c);

注意:如果Cookie的路径不一样是不能被覆盖的。

6)编码问题

cookie只能保存合法的ascii字符,如果要保存中文,

要将中文转换成相应的ascii字符的形式。

String URLEncoder.encode(String str,String charset);

String URLDecoder.decode(String str,String charset);

注:建议,添加cookie时,最好对保存的字符统一编码处理。

7)cookie的路径问题

a.什么是cookie的路径问题?

当浏览器访问服务器上的某个地址时,会比较

该地址是否匹配cookie的路径,只有匹配的cookie

才会发送给服务器。

b.cookie的默认路径

等于添加该cookie的web组件的路径。

c.匹配规则

要访问的地址必须等于cookie的路径或者

是其子路径。

比如 cookie的路径是"/day07/biz01",则

/day07/findCookie1.jsp no

/day07/biz01/findCookie2.jsp yes

/day07/biz01/sub/findCookie3.jsp yes

d.修改cookie的路径

cookie.setPath(String path);

在这里插入图片描述

8)cookie的限制

a.cookie可以被用户禁止。

b.cookie不安全。

对于敏感数据,一定要加密。

c.cookie只能保存少量的数据。

大约4k左右

d.cookie的数量也有限制。

总的数量大约是几百个,对于某个网站,数量

也有限制(大约是二十个左右)。

e.cookie只能保存字符串。

练习:写一个Servlet(比如CountServlet),记录

用户访问的次数(要求使用cookie技术)。

在这里插入图片描述

八、状态管理-Session

1.session 会话

(1) 什么是session?

  • 服务器端为了保存状态而创建的一个特殊的对象。

(2) session的工作原理

  • 浏览器访问服务器时,服务器创建一个session对象(该对象有一个唯一的id,称之为sessionId);
  • 然后将sessionId以cookie的方式发送给浏览器(意味着浏览器重启的话,sessionId将会更新)(是在request.getSession()方法中实现的);
  • 当浏览器再次访问服务器时,会将sessionId发送过来,服务器可以利用sessionId找到之前的session对象。

在这里插入图片描述

(3) 如何获得session对象?

  • 方式一:

    HttpSession session = request.getSession(boolean flag);

    • a. HttpSession是一个接口。
    • b. 当flag为true时,先查看请求当中是否有sessionId,
      • 如果没有,则创建一个session对象;
      • 如果有,则依据sessionId查找对应的session对象,
      • 如果找到了,则返回,找不到,则创建一个新的session对象。
    • c. 当flag为false时,先查看请求当中是否有sessionId,
      • 如果没有,则返回null;
      • 如果有,则依据sessionId查找对应的session对象,
      • 如果找到了,则返回,找不到,返回null。
  • 方式二:

    HttpSession session = request.getSession();

    • 等价于 request.getSession(true)。

    request.getSession();

(4) 保存状态相关的几个方法

  • setAttribute(String name,Object obj);–绑定数据
  • Object getAttribute(String name);–获取数据
  • removeAttribute(String name);–解除绑定

在这里插入图片描述

(5) session超时

  • 什么是session超时?

    • 服务器会将空闲时间过长的session对象删除掉。
    • 注:这样可以节省内存空间。一般空闲时间长度为半个小时左右。
  • 修改超时时间长度

    • 方式一:

      在Servers的web.xml文件中修改以下参数(单位是分钟):(一般没有必要去修改!)

      <session-config>
      	<session-timeout>30</session-timeout>
      </session-config>
      
    • 方式二:

      session.setMaxInactiveInterval(int seconds);

      • 两次请求之间的间隔如果超过指定的时间(单位是秒),则session对象被销毁。

(6) 删除session

  • session.invalidate();(invalidate–失效)

session验证(案例)

  • step1.登录成功之后,将一些数据绑订到session对象之上,比如:
session.setAttribute("user",user);
  • step2.如果请求涉及到需要保护的资源(即只有登录成功之后才能访问的资源,比如用户列表),则进行session验证。比如:
Object obj = session.getAttribute("user");
if(obj == null){
	response.sendRedirect("login.jsp");
}

在这里插入图片描述

九、过滤器(上)

1.比较cookie与session

  • cookie的优点是不占用服务器端的内存资源,

  • session的优点是安全,并且可以保存更丰富的数据类型。

在这里插入图片描述

2.验证码

protected void service(HttpServletRequest request, HttpServletResponse response)
    										throws ServletException, IOException {

	System.out.println("CheckcodeServlet's service()");

    /*
	 * 绘图
	 */
	//step1.创建一个内存映像对象(画布)
	//三个参数为,画布的宽度,高度,类型
	BufferedImage image=new BufferedImage(80,30,BufferedImage.TYPE_INT_RGB);

    //step2.获得画笔
	//导包的时候导awt的包
	Graphics g=image.getGraphics();

    //step3.给笔设置颜色
	g.setColor(new Color(255,255,255));

    //step4.给画布设置背景颜色(左上角的左边距,左上角的右边距,背景宽,背景高)
	g.fillRect(0, 0, 80, 30);

    //step5.重新给笔设置颜色和字体大小
	Random r=new Random();
	g.setColor(new Color(r.nextInt(255),r.nextInt(255),r.nextInt(255)));
	//Font(字体类型,风格,大小)
	g.setFont(new Font(null, Font.ITALIC|Font.BOLD, 24));

    //step6.生成number(验证码)
	String number=""+r.nextInt(10)+r.nextInt(10)+r.nextInt(10)+r.nextInt(10)+r.nextInt(10);

    //step7.将验证码添加到图片里面(字符串,左下角的左边距,左下角的上边距)
	g.drawString(number, 5, 25);

    //step8.加一些干扰线
	for(int i=0;i<8;i++) {
		//设置干扰线颜色
		g.setColor(new Color(r.nextInt(255),r.nextInt(255),r.nextInt(255)));
		//设置干扰线位置
		g.drawLine(r.nextInt(80), r.nextInt(30), r.nextInt(80), r.nextInt(30));
	}

    /*
	 * 将图片压缩,发送给浏览器
	 */

    //step1.设置MEMI类型(告诉浏览器,
	//服务器返回的数据类型是什么,这里返回
	//的是一张jpeg格式的图片)
	response.setContentType("image/jpeg");

    //step2.获得字节输出流
	OutputStream output=response.getOutputStream();

    //step3.压缩图片并输出
	//(原始图片对象,压缩成的格式,输出流)
	javax.imageio.ImageIO.write(image, "jpeg", output);
}

3.过滤器

(1) 什么是过滤器?

  • servlet规范当中定义的一种特殊的组件,用来拦截容器的调用过程。
  • 容器收到请求之后,会调用过滤器,再调用servlet。

在这里插入图片描述

(2) 如何写一个过滤器?

  • step1.写一个java类,实现Filter接口。

  • step2.在doFilter方法里面,实现拦截处理逻辑。

  • step3.配置过滤器。(web.xml)

练习:写一个过滤器,检查评论的字数,如果超过 20个字,则提示“字数过多”。

十、过滤器(下)、监听器

(3) 过滤器的优先级

  • 当有多个过滤器都满足拦截要求,则依据配置的先后顺序来执行。

(4) 初始化参数

  • step1.配置初始化参数。

  • step2.调用FilterConfig对象的 getInitParameter() 方法来获取初始化参数值。

    <!-- 配置初始化参数 -->
    <init-param>
    	<param-name>illegal</param-name>
        <param-value></param-value>
    </init-param>
    

(5) 过滤器的优点

  • a.可以在不修改原有代码的基础上,增加新的功能。

  • b.可以将多个组件相同的逻辑集中写在一个类里面(即过滤器里面),方便代码的维护。(比如session验证功能)

2.监听器

(1) 什么是监听器?

  • servlet规范当中定义的一种特殊的组件,用来监听容器产生的事件并进行相应的处理。

容器主要会产生两大类事件:

  • a.生命周期相关的事件:

    容器创建或者销毁了request,session,servlet上下文时产生的事件。

  • b.绑订数据相关的事件:

    调用了request,session,servlet上下文的setAttribute,removeAttribute时产生的事件。

(2) servlet上下文

  • 容器启动之后,会为每一个应用创建唯一的一个符合ServletContext接口要求的对象,该对象一般称之为servlet上下文。
  • 特点:
    • a.唯一性:一个应用对应一个servlet上下文;
    • b.持久性:只要容器不关闭,应用没有被卸载,则servlet上下文会一直存在。
  • 如何获取servlet上下文?
    • GenericServlet、ServletConfig、FilterConfig、HttpSession都提供了一个方法:getServletContext()
  • servlet上下文的作用:
    • a.绑订数据(类似于request、session)
      • setAttribute(String name,Object obj)
      • Object getAttribute(Sting name)
      • removeAttribute(String name)
    • b.读取全局的初始化参数。

注1

  • request,session和servlet上下文都提供了绑订数据相关的方法,

  • 从生存时间的长度来看:servlet上下文 > session >request;

  • 在满足使用条件的情况下,应该优先使用生命周期短的。

在这里插入图片描述

注2:如果数据需要所有用户共享,应该绑订到servlet上下文对象上。

在这里插入图片描述

在这里插入图片描述

注3:同一个应用中的所有组件(servlet,filter)都可以访问的初始化参数。

  • step1.配置初始化参数;
  • step2.调用ServletContext提供的getInitParameter方法来读取初始化参数值。
<!-- 配置全局的初始化参数 -->
<context-param>
	<param-name>company</param-name>
    <param-value>A公司</param-value>
</context-param>

(3) 如何写一个监听器?

  • step1.写一个java类,实现相应的监听器接口。

    注:要依据监听的事件类型来选择实现相应的接口,比如,要监听session对象的创建和销毁,可以实现HttpSessionListener接口。

  • step2.在接口方法当中,实现监听处理逻辑。

  • step3.配置监听器。(web.xml)(没有先后顺序)

统计在线人数:

在这里插入图片描述

/**
 * session对象创建之后,容器会调用
 * sessionCreated方法。
 * se:事件对象
 */
public void sessionCreated(HttpSessionEvent se) {
	System.out.println("sessionCreated()");
	//获得servlet上下文
	ServletContext sctx=se.getSession().getServletContext();
	//通过上下文获得绑定人数
	Integer count=(Integer)sctx.getAttribute("count");
	if(count==null) {
		//第一个用户
		count=1;
	}else {
		//不是第一个用户,人数加一
		count++;
	}
	//绑定最新的人数到上下文
	sctx.setAttribute("count", count);
}

/**
 * session对象销毁之后,容器会调用
 * sessionDestroyed方法。
 */
public void sessionDestroyed(HttpSessionEvent se) {
	System.out.println("sessionDestroyed()");
	ServletContext sctx=se.getSession().getServletContext();
	Integer count=(Integer)sctx.getAttribute("count");
	count--;
	sctx.setAttribute("count", count);
}

3.实现一个简单的web缓存 (扩展)

基本思路:

  • 将一些常用的、不怎么发生变化,并且数据量不大的数据从数据库中查询出来,并存放到内存里面。

  • 可以写一个监听器,实现servletContextListener接口,在上下文创建时,查询数据库,并且将查询到的数据绑订到上下文上。

  • step1.建表 t_role

create table t_role(
  id int primary key,
  name varchar(20)
);
insert into t_role values(1,'管理员')
insert into t_role values(2,'版主');
insert into t_role values(3,'用户');
  • step2.实体类 Role

  • step3.DAO类 RoleDAO

List findAll();
  • step4.监听器类 CacheListener
/* 伪代码 */
public class CacheListener implements ServletContextListener {
	contextInitialized(){
    	List list = RoleDAO.findAll...
        sctx.setAttribute("",list)
	}
}

注意:可以绑定数据的4个作用域里分别是

pageContext、request、session、ServletContext

十一、Servlet线程安全、jsp基础、EL

1.Servlet线程安全问题

(1) 为什么说Servlet会有线程安全问题?

  • a.容器只会创建一个Servlet实例。
  • b.容器收到一个请求,就会启动一个线程来处理该请求,这样,就可能会有多个线程同时调用同一个对象,就有可能产生线程安全问题(比如,这些线程同时去修改某个属性)。

在这里插入图片描述

(2) 如何解决?

  • 将有可能产生线程安全问题的代码使用synchronized加锁。
  • a.使用synchronized加锁对性能会有一些影响。不要对整个方法加锁,而是对有可能产生线程安全问题的代码块加锁。
  • b.尽量不要修改属性。

2.JSP基础

(1) jsp是什么?

  • JSP全称Java Server Pages,是一种动态网页开发技术。它使用JSP标签在HTML网页中插入Java代码。

(2) 如何写jsp?

  • step1.写一个以.jsp为后缀的文件。

  • step2.添加:

      1. html (css,js) 直接写即可。
      1. java代码。
      • a.java代码片断 <% java代码 %>
      • b.jsp表达式 <%= java表达式 %>
      • c. jsp声明 (a1.jsp) <%! 声明变量或者方法 %>
      1. 指令
      • a.什么是指令?

        告诉容器,在将jsp转换成servlet时,做一些额外的处理,比如导包。

      • b.语法 <%@ 指令名 属性=值 %>

      • c.page指令

        import属性:导包

        pageEncoding属性:jsp文件的编码。

        contentType属性:设置setContentType的内容。

        session属性:缺省值(默认值)是true,如果值是false,则不能够使用session隐含对象。 (a2.jsp)

        errorPage属性:指定一个异常处理页面。

        (注: 当jsp运行发生了异常,则容器会调用异常处理页面。(a3.jsp))

        isErrorPage属性:缺省值是false,如果值为true,则可以使用exception隐含对象。

        • 使用exception.getMessage()获得错误信息。(a4.jsp)
      • d.include指令

        告诉容器,在将jsp转换成servlet时,将file属性指定的文件的内容插入到该指令所在的位置。

      • e. taglib指令

        jsp标签技术中,用来引入某个标签。

      1. 隐含对象

      a.什么是隐含对象? 可以直接使用的对象。

      b.为什么可以直接使用这些隐含对象? 容器会自动添加获得这些对象的代码。

      c.有哪些隐含对象?

      • out,request,response

      • session,application

      • exception

      • pageContext: 页面上下文

        容器会为每一个jsp实例创建唯一的一个符合PageContext接口要求的对象,该对象会一直存在,除非jsp实例被删除。

        • 作用1:绑订数据 (a6.jsp,a7.jsp) setAttribute、getAttribute、removeAttribute

          (注:绑订到pageContext上的数据,只有对应的jsp实例能访问。)

        • 作用2:获得其它所有隐含对象

          (注:该对象提供了获得其它隐含对象的方法。)

      • config: 相当于ServletConfig,通过它来读取初始化参数 (a5.jsp)

      • page: jsp实例本身(过度设计,从来不用)

        (注: jsp对应的那个servlet实例。)

      1. 注释 (a8.jsp)

      a. 如果注释的内容是java代码,java代码会执行,但是不会显示。

      b. <%-- 注释内容 --%> 如果注释的内容是java代码,不会执行。

(3) JSP是如何执行的?

  • step1. JSP要转换成一个servlet

    • html -----> service方法里,使用out.write输出。
    • <% %> —> service方法里,照搬。
    • <%= %> --> service方法里,使用out.print
    • <%! %> —> 增加新的属性和方法。
  • step2.调用servlet。

3.jsp标签和el表达式

(1) jsp标签是什么?

  • jsp标签语法类似于html标签,用于替换jsp文件中的java代码。
    • a.因为直接在jsp文件当中写java代码,不利于jsp文件的维护(比如,将带有java代码的jsp文件交给美工去修改就很不方便)。所以,sun才制订了jsp标签技术规范。
    • b.使用jsp标签来替换java代码,一方面jsp文件会变得简洁,利于代码的维护与复用,另外一方面,也利于美工去修改。

(2) el表达式是什么?

  • 一套简单的运算规则,用于给jsp标签的属性赋值,也可以脱离jsp标签直接运行。

(3) el表达式的使用

1) 访问bean的属性

补充:一个类,满足如下几个条件,就可以称之为一个javabean:

要求:

  1. public class

  2. 有无参构造器

  3. 实现序列化接口(不是强制要求)

  4. 有一些属性

  5. 有对应的get/set方法

  • 方式一:(e1.jsp)

    • ${user.username}
    • a.执行过程:容器依次从pageContext-> request -> session -> application 中查找绑订名为"user"的对象 (即getAttribute),如果找到了,则调用该对象的"getUsername"方法并输出。
    • b.优点:会将null转换成""(空字符串)输出。如果找不到对应的对象,不会报空指针异常,而是输出""(空字符串)。但是不能把方法名写错,否则会报错。
    • c.指定查找范围:可以使用pageScope,requestScope、sessionScope、applicationScope 指定查找范围,比如:${sessionScope.user.username}
  • 方式二:

    • ${user[‘username’]} 等价于 ${user.username}

    • []里面允许出现绑订名,比如 ${user[str]}。找不到则输出空字符串"" ${user[requestScope.str]}

    • []里面还可以出现从0开始的下标,用于访问实体类属性数组中的某个元素。例:

      <%request.setAttribute("str", "username");%>
      username:${user[str] }
      

2) 做一些简单的运算 (e2.jsp)

  • 运算的结果可以用来给jsp标签的属性赋值,也可以直接输出。

    • a.算术运算 +,-,*,/,%,其中"+"只能求和。不能做字符串链接,但是可以计算”1”+”1”(结果为2)

    • b.关系运算 >,>=,<,<=,==,!=(结果为true或false)

      ==可以用来判断字符串是否相同。如果不指定查找范围就会依次查找。

    • c.逻辑运算 &&,||,!

    • d.empty运算 empty:判断是否为一个空字符串或者是否为一个空的集合。例:

      <%
      request.setAttribute("s2", "");
      %>
      空字符串:${empty s2 }<br/><!-- true -->
      空的集合:${empty list1 }<br/><!-- true -->
      值为null:${empty null}<br/><!-- true -->
      找不到对应的值:${empty abc }<br/><!-- true -->
      

十二、JSTL、自定义标签

3) 读取请求参数值 (e1.jsp)

  • ${param.username} 等价于 request.getParameter(“username”);
  • ${paramValues.interest} 等价于 request.getParameterValues(“interest”);

jstl (java standard tag lib 即JAVA标准标签库)

(1)jstl是什么?

  • apache开发的一套通用的jsp标签,后来捐献给了sun,sun将其命名为jstl。

(2) 如何使用jstl?

  • step1.导入jstl相关的jar包。

  • step2.使用taglib指令引入要使用的jsp标签。

    uri属性:指定要引入的jsp标签的命名空间。

    • 命名空间:为了区分同名的元素而在元素前添加的一个限定(通常是一个域名)。

    prefix属性:命名空间的别名。

(3) 几个核心标签

1) if标签(e3.jsp)

<c:if test="" var="" scope="">标签体</c:if>
  • test属性值为true时,容器会执行标签体的内容。
  • test属性,可以使用el表达式赋值来作为绑定值。
  • var属性,用来指定绑订名。
  • scope属性用来指定绑订范围(“page”,“request”,“session”,“application”)。例:
性别:
<c:if test="${user.gender=='m' }" var="rs" scope="page">男</c:if>
<c:if test="${!rs }">女</c:if>

2) choose标签 (e4.jsp)

<c:choose>
   <c:when test="">
   </c:when>  <!--可以出现1到多次-->
   <c:otherwise>
   </c:otherwise>  <!--可以出现0或一次-->
</c:choose>
  • when可以出现1次或者多次,表示一个分支;
  • 当test属性值为true时,执行标签体的内容;
  • otherwise可以出现0次或者1次,表示例外。

注意:这两个标签不能独立使用,必须要嵌套在choose标签中才有意义!

例:

<c:choose>
    <c:when test="${u.gender=='m'}">男</c:when>
    <c:when test="${u.gender=='f'}">女</c:when>
    <c:otherwise>保密</c:otherwise>
</c:choose>

3) forEach标签 (e5.jsp)

  • 用于遍历集合或者数组。

  • a.语法:

    <c:forEach items="" var="" varStatus="">
    </c:forEach>
    
  • b.items属性用来指定要遍历的集合或者数组,可以使用el表达式来赋值。

  • c.var属性用来指定绑订名(绑订范围固定是pageContext,该标签每次从集合或者数组当中取一个元素,然后绑订到pageContext上)。

  • d.varStatus属性也是用来指定绑定名(绑订范围固定是pageContext,绑订值是一个特殊对象,该对象由该标签创建,提供了几个方法用来获得当前遍历的状态,比如,

    • getIndex():获得当前正在被遍历的元素的下标(从0开始)
    • getCount():获得当前是第几次被遍历(从1开始)例:
<c:forEach items="${users }" var="u" varStatus="s">
<tr class="row${s.index%2+1 }">
	<td>${u.id }</td>
	<td>${u.uname }</td>
	<td>${u.pwd }</td>
	<td>${u.phone }</td>
	<td><a href="delUser.do?id=${u.id }"
	οnclick="return confirm('确定删除${u.uname}吗?')">删除			
</a>&nbsp;</td>
</tr>
</c:forEach>

补充:容器依据标签的命名空间找到标签的描述文件(.tld文件),然后依据标签的名称找到对应的标签类,然后将该类实例化,并且调用该实例对应的方法。

注意:如果是更改项目名,要记得右键项目名称,然后更改web project settings的值为相应的项目名称。这个web project settings是你访问服务器上你部署的应用的应用名默认情况下 不指定这个的话应用名和你的工程名是一样的指定了之后 就是以这个名字为准了。

在这里插入图片描述

2.自定义标签

(1) 编程步骤

  • step1.写一个java类,继承SimpleTagSupport类。

    • 简单标签技术(new),继承SimpleTagSupport类来开发。
    • 复杂标签技术(old)
  • step2.override doTag方法,在该方法里面,编写处理逻辑。

  • step3.描述标签(.tld文件)

    <body-content>JSP</body-content>
    
    • body-content的值可以是emptyscriptlessJSP
    • empty: 该标签没有标签体。
    • scriptless: 该标签可以有标签体,但是标签体的内容不能够出现java代码(<% %>,<%= %>,<%! %>)
    • JSP:该标签可以有标签体,并且标签体可以出现java代码。只有复杂标签技术才支持该值。

案例:

  1. java标签类:

    /**
     * 自定义标签,用来输出系统的当前时间
     * 
     * @author ACGkaka
     */
    public class SysdateTag extends SimpleTagSupport {
    
        //设置默认格式
        private String format="yyyy/MM/dd HH:mm:ss";
    
        //设置format的get方法
        public String getFormat() {
            return this.format;
        }
    
        //设置format的set方法
        public void setFormat(String format) {
            this.format=format;
        }
    
        //重写doTag方法来实现具体的功能
        @Override
        public void doTag() throws JspException, IOException {
    
            //创建服务器时间
            Date date=new Date();
    
            //格式化时间
            SimpleDateFormat sdf=new SimpleDateFormat(format);
            String now=sdf.format(date);
    
            //将时间输出给浏览器
            //PageContext extends JspContext
            //该方法声明返回JspContext
            //但在实现时返回的是PageContext
            //所以可以将其强转为PageContext
            //从而获得其他8个隐含对象
            PageContext ctx=(PageContext)getJspContext();
            JspWriter out=ctx.getOut();
            out.println(now);
    
            //注意:此处一定不能关闭流,因为其他的标签
            //也要用这个流
            super.doTag();
        }
    }
    
  2. s.tld文件:

    <?xml version="1.0" encoding="UTF-8" ?>
    
    <taglib 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-jsptaglibrary_2_1.xsd"
        version="2.1">
        
      <description>这是我自己的标签库</description>
      <display-name>My tag</display-name>
      <tlib-version>3.1</tlib-version>
      <short-name>s</short-name>
      <uri>/my-tag</uri>
    
      <tag>
        <description>用来输出服务器的时间</description>
        <name>sysdate</name>
        <tag-class>web.SysdateTag</tag-class>
        <!-- 声明该标签可以包含哪些内容 -->
        <!-- empty: 该标签没有标签体。
        	scriptless: 该标签可以有标签体,但是标签体
            		的内容不能够出现java代码(<% %>,
            		<%= %>,<%! %>)。
        	JSP:该标签可以有标签体,并且标签体可以出现
            		java代码。只有复杂标签技术才支持该值
        -->
        <body-content>empty</body-content>
        <!-- 以下是标签的属性设置 -->
        <attribute>
            <description>用来设置时间的格式</description>
            <name>format</name>
            <!-- 设置是否必须给这个属性赋值 -->
            <required>false</required>
            <!-- 设置是否可以用EL表达式给次属性赋值 -->
            <rtexprvalue>true</rtexprvalue>
        </attribute>
      </tag>
    
    </taglib>
    
  3. 应用:

    <%@ taglib uri="/my-tag" prefix="s" %>
    <s:sysdate format="yyyy-MM-dd" />
    

十三、MVC

1.MVC (Model 模型,View 视图,Controller 控制器)

(1) MVC模式

  • 是一个经典的设计模式,是软件的分层思想:
    • M:Model,即业务层,用来处理业务;
    • V:View,即视图层,用来处理数据;
    • C:Controller,即控制层,是业务层和视图层的桥梁。
  • 它可以降低软件中代码的耦合度,便于团队开发及维护。

(2) 如何使用MVC?

  • 在web开发当中,我们经常使用Servlet充当控制器,jsp充当视图,java充当模型。它们的关系如下图所示:

在这里插入图片描述

(3) MVC的优点

  • a.模型返回的处理结果,可以使用不同的视图来展现。

  • b.方便测试

    • 比如将业务逻辑写在java类里面,可以直接测试。
    • 如果写在Servlet里面,需要部署之后才能测试。
  • c.方便代码的维护,修改模型或者视图,彼此不受影响。

注意

使用MVC,会增加代码量,会增加软件的成本,也会增加设计的难度。所以,只有一定规模的软件才需要使用MVC。

案例:

NETCTOSS–中国电信运营支持系统-网络版

在这里插入图片描述

注意:浏览器访问服务器获得网页,以及加载网页的过程中包含多次请求

在这里插入图片描述

在这里插入图片描述

2.实现一个简单的MVC框架

在这里插入图片描述

(1) 基本架构

在这里插入图片描述

十四、smartmvc

1.smartmvc架构

在这里插入图片描述

2.使用smartmvc

  • step1.导包 dom4j

  • step2.将base包下面的common,web包拷贝到工程里面。(这两个包是smartmvc的核心包)。

  • step3.配置DispatcherServlet。

    (注:主要是指定配置文件的位置。)

  • step4.添加处理器类,比如,添加LoginController

    • a.方法前添加@RequestMapping。

    • b.方法的返回值是一个字符串(即视图名,默认会转发),如果是重定向,前面要加"redirect:"。

  • step5.在配置文件(比如context.xml)中添加处理器的配置信息。

    (注:要写完整的类名。)

  • step6.添加jsp文件。

    • a.要添加到WEB-INF下。
    • b.文件名要等于视图名。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

不愿放下技术的小赵

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值