Java Servlet,JSP,Filter,Listener

一、WEB项目的演变

1.发展规律
   - 由单机向网络发展
   - 由CS向BS发展
   
2.CS和BS的区别
   - CS
   -- Client Server
   -- 客户端服务器程序
   -- 客户端需要单独开发,用户需要单独下载并安装
   
   - BS
   -- 浏览器服务器程序
   -- 客户端不用单独开发,用户不用单独安装

二、Servlet介绍

1.服务器如何保存并返回一个网页?
    - 静态网页
    -- 无论谁看其内容都一样
    -- 服务器直接存储HTMl,直接返回HTML
    
    - 动态网页
    -- 不同的人看到的内容有差异
    -- 服务器保存一个组件,动态给每个用户动态生产网页
    -- 在Java语言中这个组件就是Servlet
    -- 组件:满足规范的对象
    
2.Servlet的特点
    -- 是服务器端的组件
    -- 满足sun的规范
    -- 可以动态拼接资源(HTML/IMG等)
    -- 术语:处理HTTP协议
    
3.什么是Servlet?
-- 是sun推出的用于在服务器端处理HTTP协议的组件

三、服务器

1.名称
    - Java服务器
    - WEB服务器
    - Java WEB服务器
    - Servlet容器
    
2.本质
    - 是一个软件
    - 它和浏览器是平级的关系

3.举例
    - Tomcat(Apache)
    - JBoss
    - WebLogic
    - WebSphere

四、Tomcat的使用方式

- 绿色版软件,解压缩(释放)可用

1.单独使用(项目上线时)
    -- 配置好JAVA_HOME

2.通过Eclipse调用(开发时)


使用:
修改Tomcat端口
    - 打开server.xml,在65行修改tomcat的端口,修改后重启

五、Servlet开发步骤

1.创建WEB项目
    - 必须具备标准的WEB目录
    - /webapp/WEB-INF/web.xml
    - eclipse 右击出现Generate Deployment Descriptor Stub自动生成

2.导入jar包
    -- 使用maven搜索javaee
    -- 在结果中选择javaee-api
    
    -- 使用Tomcat自带的包
    -- 选择项目,右键点击properties
    -- 弹出框里在左侧选择Targeted Runtimes
    -- 在右侧勾选Apache Tomcat
    -- Apply

3.开发Servlet
    - 编写Servlet
    -- 创建package,创建一个类,名为XXXServlet
    -- 继承HttpServlet,从而间接实现了
    
    - 配置Servlet
    -- 先声明类,并给它取别名--ts
    -- 再通过别名引用此类,
    
    部署(拷贝)
    -- 在Servers视图下,选择tomcat7
    -- 右键点击Add and Remove
    -- 在弹出框内将左边的待部署项目移动到右侧
    -- 启动tomcat即可
    
    
    访问
    -- 格式:项目名/别名
    -- http:localhost:8080/servlet1/ts

web.xml 文件添加的内容

<servlet>
    <servlet-name>test</servlet-name>
    <servlet-class>web.TestServlet</servlet-class>
</servlet>
<servlet-mapping>
    <servlet-name>test</servlet-name>
    <url-pattern>/ts</url-pattern>
</servlet-mapping>

项目中的TestServlet类

package web;

import java.io.IOException;
import java.io.PrintWriter;
import java.util.Random;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

public class TestServlet extends HttpServlet {

    @Override
    protected void service(HttpServletRequest req, HttpServletResponse res) throws ServletException, IOException {
        res.setContentType("text/html");
        PrintWriter out = res.getWriter();
        Random rand = new Random();
        int num = rand.nextInt(100);
        out.println("<h1>"+ num +"</h1>");
        out.close();
    }
}

效果是在访问中可以看到返回一个随机数,访问路径就是 项目名/别名


2.png

获取请求消息头数据,迭代遍历取值

package web;

import java.io.IOException;
import java.io.PrintWriter;
import java.util.Enumeration;
import java.util.Random;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

public class TestServlet extends HttpServlet {

    @Override
    protected void service(HttpServletRequest req, HttpServletResponse res) throws ServletException, IOException {
        
        System.out.println("请求方式:" + req.getMethod());
        System.out.println("请求路径:" + req.getServletPath());
        System.out.println("协议类型:" + req.getProtocol());
        
         * 消息头(键值对)
         * Enumeration是老版本的迭代器,作用及用法和Iterator类似
        Enumeration<String> e = req.getHeaderNames();
        while(e.hasMoreElements()) {
            String key = e.nextElement();
            String value = req.getHeader(key);
            System.out.println(key + " : " + value);
        }
        
         * 消息头中的大部分数据由服务器填充,但返回的内容格式需要自己指定
        
        res.setContentType("text/html");
        PrintWriter out = res.getWriter();
        Random rand = new Random();
        int num = rand.nextInt(100);
        out.println("<h1>"+ num +"</h1>");
        out.close();
    }
}

打印:
请求方式:GET
请求路径:/ts
协议类型:HTTP/1.1
host : localhost:8080
connection : keep-alive
cache-control : max-age=0
upgrade-insecure-requests : 1
user-agent : Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/65.0.3325.181 Safari/537.36
accept : text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,**;q=0.8
accept-encoding : gzip, deflate, br
accept-language : zh-CN,zh;q=0.9
cookie : csrftoken=cTqd6QXbuKfzXZt4KqoEGJ1fHVxquYXCxgUQS42DGqH1Q870NgiKZs68KD4R1dAp; pgv_pvi=6176773120; Pycharm-10f6446f=8a4b4f57-683d-4839-bd3d-7b42b1b1a034

解决请求数据的中文乱码

1.get/post
- 将乱码后的字符串,安装ISO还原成byte
- 再将byte按照UTF-8编码为字符串
优点:万能    缺点:麻烦

2.get
- 修改tomcat配置文件
- 在server.xml第65行(不一定是65行),加URIEncoding="utf-8",修改路径传参编码
优点:简单    缺点:只对get有效
修改成如下
    <Connector URIEncoding="utf-8" port="8080" protocol="HTTP/1.1"
               connectionTimeout="20000"
               redirectPort="8443" />

3.post
- 在接收参数前,加 req.setCharacterEncoding("utf-8")
- 声明实体内容的编码为utf-8
优点:简单    缺点:只对post有效

一般将第二种和第三种相结合解决get和post方式的乱码

解决返回数据的中文乱码

out.println输出中文时,Servlet默认会使用"iso-8859-1",该编码不支持中文,故不管如何都会乱码

res.setCharacterEncoding("utf-8");
res.setContentType("text/html");

或者写成一句
res.setContentType("text/html;charset=utf-8");

一个注册的小例子

web.xml文件

<servlet>
    <servlet-name>reg</servlet-name>
    <servlet-class>web.RegServlet</servlet-class>
</servlet>
<servlet-mapping>
    <servlet-name>reg</servlet-name>
    <url-pattern>/reg</url-pattern>
</servlet-mapping>

register.html 文件,只需要放在webapp目录下,不需要配置

<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
</head>
<body>
<form action="reg" method="post">
    <p>账号:<input type="text" name="username"></p>
    <p>密码:<input type="password" name="password"></p>
    <p>
        性别:
        <input type="radio" name="sex" value="man">
        <input type="radio" name="sex" value="woman">
    </p>
    <p>
        兴趣:
        <input type="checkbox" name="interest" value="sport">运动
        <input type="checkbox" name="interest" value="music">音乐
        <input type="checkbox" name="interest" value="movie">电影
    </p>
    <p><input type="submit" value="提交"></p>
</form>
</body>
</html>

请求路径,项目名/register.html


1.png

RegServlet.java文件

package web;

import java.io.IOException;
import java.io.PrintWriter;
import java.util.Random;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

public class RegServlet extends HttpServlet {
    @Override
    protected void service(HttpServletRequest req, HttpServletResponse res) throws ServletException, IOException {
        req.setCharacterEncoding("utf-8");
        
        String username = req.getParameter("username");
        
        String password = req.getParameter("password");
        System.out.println("username = " + username + ", password = " + password);
        String sex = req.getParameter("sex");
        String[] interests = req.getParameterValues("interest");  --- 获取多个数据
        System.out.println("sex = " + sex);
        if(interests != null) {
            System.out.print("interests =");
            for(String i:interests) {
                System.out.print(" " + i);
            }
            System.out.println();
        }
        res.setContentType("text/html;charset=utf-8");   --解决中文乱码
        PrintWriter out = res.getWriter();
        out.println("<h1>注册成功!!!<h1>");
        out.close();
    }
}

2.png



控制台输出
3.png

JavaBean - 满足如下规范的类:

- 有包
- 有默认构造器
- 实现序列化接口

URI和URL的区别

1.狭义
    -- 只在Java项目中的URI和URL
    -- URI:绝对路径
    -- URL:完整路径
     > 从表面上看URI包含了URL
     
2.广义
    - 在任意的WEB项目中理解URI和URL
    -- URI:资源的名称
    -- URL:资源的真名
     > URI包含了URL
@Override
protected void service(HttpServletRequest req, HttpServletResponse res) throws ServletException, IOException {
    System.out.println("请求方式:" + req.getMethod());
    System.out.println("项目名:" + req.getContextPath());
    System.out.println("请求的后部分路径:" + req.getServletPath());
    System.out.println("URI:" + req.getRequestURI());
    System.out.println("URL:" + req.getRequestURL());
}

打印:
请求方式:GET
项目名:/servlet3
请求的后部分路径:/abc
URI:/servlet3/abc
URL:http://localhost:8080/servlet3/abc

重定向

解决一个网站内2个独立组件之间的跳转
一般增加、修改、删除后重定向到查询

res.sendRedirect("[别名]");

建议:

 * 1.尽量使用封装类型,因为它比基本类型多了null
 * 2.使用java.sql包下的日期,因为JDBC支持这样的日期类型。

设置访问路径的三种方式

所谓的访问路径都是针对部署代码而言的
1.静态资源(HTML/IMG/CSS/JS等)
    访问路径就是它在Tomcat内存放的位置
2.动态资源(Servlet)
    访问路径就是web.xml中声明的别名
    
在Servlet中如果没有写返回响应信息,浏览器会看到一片空白,并不会报错。
         
配置Servlet访问路径:
    1.精确匹配
    -- 举例:/abc
    -- 只有/abc才能访问此Servlet
    -- 此Servlet只能处理这一个请求
     > 适合规模很小的项目
     
    2.通配符
    -- 举例:/*
    -- 所有的路径都能访问此Servlet
    -- 此Servlet能处理所有u请求
     > 适合一个项目只写一个Servlet
     
     3.后缀
     -- 举例:*.hi
     -- 所有以hi为后缀的请求都能访问此Servlet
     -- 此Servlet能处理多个请求
      > 适合一个项目写少量的几个Servlet

例子,查询员工,通配路径 *.do,在Servlet中配置特定路径访问
web.xml文件配置匹配路径

<servlet>
    <servlet-name>main</servlet-name>
    <servlet-class>web.MainServlet</servlet-class>
</servlet>
<servlet-mapping>
    <servlet-name>main</servlet-name>
    <url-pattern>*.do</url-pattern>
</servlet-mapping>

Main.Servlet文件

package web;

import java.io.IOException;
import java.io.PrintWriter;
import java.util.List;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import dao.EmpDao;
import dao.EmpDaoImpl;
import entity.Emp;

 * 路径规范:
 * 查询员工:/findEmp.do
 * 增加员工:/addEmp.do

public class MainServlet extends HttpServlet {

    @Override
    protected void service(HttpServletRequest req, HttpServletResponse res) throws ServletException, IOException {
        String path = req.getServletPath();
        if("/findEmp.do".equals(path)) {
            findEmp(req,res);
        }else if("/addEmp.do".equals(path)) {
            addEmp(req,res);
        }else {
             * 该异常抛给服务器,服务器可以统一处理异常
            throw new RuntimeException("找不到了");
        }
    }

    protected void findEmp(HttpServletRequest req, HttpServletResponse res) throws ServletException, IOException {
        EmpDaoImpl empdaoimp = new EmpDaoImpl();
        List<Emp> list = empdaoimp.findAll();
        
        res.setContentType("text/html;charset=utf-8");
        PrintWriter out = res.getWriter();
        out.println("<a href=\"addemp.html\">添加员工</a>");
        out.println("<table border=\"1\">");
        out.println("<td>" + "编号" + "</td>");
        out.println("<td>" + "名称" + "</td>");
        out.println("<td>" + "工作" + "</td>");
        out.println("<td>" + "薪水" + "</td>");
        if(list != null) {
            for(Emp emp:list) {
                out.println("<tr>");
                Integer no = emp.getEmpno();
                String name = emp.getEname();
                String job = emp.getJob();
                Double salary = emp.getSalary();
                System.out.println("no =" + no + ", name = " + name + ", job = " + job + ", salary = " + salary);
                out.println("<td>" + no + "</td>");
                out.println("<td>" + name + "</td>");
                out.println("<td>" + job + "</td>");
                out.println("<td>" + salary + "</td>");
                
                out.println("</tr>");
            }
        }
        out.println("</table>");
        out.println("输出结束");
        
        out.close();
    }
    
    protected void addEmp(HttpServletRequest req, HttpServletResponse res) throws ServletException, IOException {
        req.setCharacterEncoding("utf-8");
        String username = req.getParameter("username");
        String password = req.getParameter("password");
        String salary = req.getParameter("salary");
        System.out.println("username = " + username + ", password = " + password + ", salary = " + salary);
        
        Emp emp = new Emp();
        emp.setEname(username);
        emp.setJob(password);
        if(salary != null && !salary.equals("")) {
            emp.setSalary(Double.valueOf(salary));
        }
        EmpDao dao = new EmpDaoImpl();
        dao.save(emp);
        
        res.sendRedirect("findEmp.do");
    }
}

HiServlet的生命周期,init(),destroy()

1.默认首次访问Servlet时Tomcat会实例化HiServlet
2.可以改为启动Tomcat时就实例化HiServlet
3.HiServlet从头到尾只实例化一次,所以HiServlet是单例的(一个)
4.每次请求都调用处理执行
5.意义:当需求规定在什么时刻做什么处理时,开发者便于处理

修改实例化Servlet的顺序
<servlet>
    <servlet-name>hi</servlet-name>
    <servlet-class>web.HiServlet</servlet-class>
    <!- 启动时加载此Servlet,中间的数字是加载的顺序。 -->
    <load-on-startup>1</load-on-startup>
</servlet>

HiServlet.java文件

package web;

import java.io.IOException;

import javax.servlet.ServletConfig;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

public class HiServlet extends HttpServlet{

    public HiServlet() {
        System.out.println("实例化");
    }

    @Override
    public void init(ServletConfig config) throws ServletException {
        super.init(config);
        System.out.println("初始化HiServlet");
    }
    
    @Override
    protected void service(HttpServletRequest req, HttpServletResponse res) throws ServletException, IOException {
        System.out.println("调用HiServlet处理请求");
    }

    @Override
    public void destroy() {
        super.destroy();
        System.out.println("销毁HiServlet");
    }
}

ServletConfig和ServletContext,配置初始化参数

1.作用
    使用web.xml做配置文件,使用config或context来读取该文件中的参数

2.区别
    - config
    -- 和Servlet是1对1的关系
    -- Tomcat在初始化每个Servlet前会给它创建一个config
    -- 调用HelloServlet.init()前给它创建一个config
    -- 如果想给某个Servlet预置数据,使用config
    
    - context
    -- 和Servlet是1对多的关系
    -- Tomcat在启动时就创建唯一的一个context
    -- 所有的Servlet都可以共享这个对象中的数据
    -- 如果想给多个Servlet预置数据,使用context

config使用,init()初始化之前创建一个config
web.xml文件

<servlet>
    <servlet-name>login</servlet-name>
    <servlet-class>web.LoginServlet</servlet-class>
     <!- 给此Servlet预置参数  -->
    <init-param>
        <param-name>maxOnline</param-name>
        <param-value>3000</param-value>
    </init-param>
</servlet>
<servlet-mapping>
    <servlet-name>login</servlet-name>
    <url-pattern>/login</url-pattern>
</servlet-mapping>

读取config参数
LoginServlet.java文件

package web;

import java.io.IOException;

import javax.servlet.ServletConfig;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

public class LoginServlet extends HttpServlet{

     * Tomcat创建Servlet的逻辑:
     * LoginServlet s = new LoginServlet();
     * ServletConfig c = new ServletConfig();
     * c.加载数据();   从web.xml加载数据
     * s.init(c);

    @Override
    public void init(ServletConfig config) throws ServletException {
        super.init(config);
        String maxOnline = config.getInitParameter("maxOnline");
        System.out.println(maxOnline);
    }

    @Override
    protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {

         * 此config就是init()传入的那个
        ServletConfig cfg = getServletConfig();
        System.out.println("正在登录·····"+cfg.getInitParameter("maxOnline"));
    }
}

打印:   --- 获取到数据了
3000
正在登录·····3000

context使用,读取常量
web.xml 中配置

context:
    - 软件内有很多的查询功能,都带有分页
    - 每页显示的行数size是常量,并且可配置
    - 该数据在多个查询功能之间共用,使用context读取

 在标签外配置的参数是给所有Servlet公用的参数, 它们都可以通过context读取该参数
<context-param>
    <param-name>size</param-name>
    <param-value>20</param-value>
</context-param>
public class TextServlet extends HttpServlet{

    @Override
    protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {

         * Tomcat启动时就会创建唯一的context,并且会调用它的方法
         * 加载web.xml中的公用参数,context是全局的,任何Servlet都可以使用
        ServletContext ctx = getServletContext();
        String size = ctx.getInitParameter("size");
        System.out.println(size);
    }
}

打印:
20

context可以读写变量

- 用该对象读写的变量是可以被所有的Servlet共用的
- setAttribute()/getAttribute()

web.xml 配置文件

<servlet>
    <servlet-name>init</servlet-name>
    <servlet-class>web.InitServlet</servlet-class>
    <load-on-startup>1</load-on-startup>
</servlet>
    <servlet-mapping>
    <servlet-name>init</servlet-name>
    <url-pattern>/init</url-pattern>
</servlet-mapping>

初始化设置变量,在所有的Servlet中都可以读写Context中的变量

 * 用来在Tomcat启动时初始化数据,一般WEB项目都有1-2个这样的Servlet。

public class InitServlet extends HttpServlet{

    @Override
    public void init(ServletConfig config) throws ServletException {
        super.init(config);

         * Tomcat启动时会优先创建Context,然后创建Servlet
        ServletContext ctx = getServletContext();
        ctx.setAttribute("count", 0);
    }

    @Override
    protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        ServletContext ctx = getServletContext();
        Integer count = (Integer) ctx.getAttribute("count");
        ctx.setAttribute("count", ++count);
        System.out.println("count = " + count);
    }
}

打印:
count = 1
count = 2
count = 3
count = 4
count = 5
2.png

Servlet线程安全问题

1.同时修改局部变量
 - 局部变量存于栈内
 - 每个线程有自己的栈帧
 - 此时没有线程安全问题
 
2.同时修改成员变量
 - 成员变量存于堆中
 - 所有线程共享这样的数据
 - 此时存在线程安全问题
 
3.解决方案
 - 加锁 

jsp

jsp本质上就是Servlet


3.png

jsp生成Servlet过程


4.png

jsp隐含(内置)对象
1.request
- HttpServletRequest

2.response
- HttpServletResponse

3.out
- JSPWriter
- 类似于PrintWriter

4.config
- ServletConfig

5.application
- ServletContext

6.exception
- Throwable
- jsp翻译成的Servlet所抛出的异常

7.session
- HttpSession

8.page
- Object
- 相当于this,用来指代翻译成的那个Servlet

9.pageContext
- PageContext
- 是管理者,通过它可以获得其他8个隐含对象

如何在jsp上使用隐含对象
- <%Object user = session.getAttribute("user"); %>

time.jsp文件

pageEncoding:声明此文档的编码
contentType:声明输出的内容格式

此jsp被其他网页引用

<%@ page pageEncoding="utf-8" contentType="text/html" import="java.text.SimpleDateFormat,java.util.Date"%>
<%
    Date date = new Date();
    SimpleDateFormat sdf = new SimpleDateFormat("HH:mm:ss");
    String now = sdf.format(date);
%>
<p><%=now %></p>

hello.jsp文件,引入上面的time.jsp文件

<%@page pageEncoding="utf-8"%>
<html>
<head>
<meta charset="uft-8">
<title>测试</title>
</head>
<%!
    public double bb(double d){
        return d*100;
    }
%>
<body>
    <ul>
        <% 
            for(int i=0; i<10; i++){
        %>
            <li><%=bb(Math.random()) %></li>
        <%
            }
        %>
    </ul>
    <!-- 引入一个jsp -->
    <%@include file="time.jsp" %>
</body>
</html>

打印:
十个随机数

jsp转发例子

package web;

import java.io.IOException;
import java.util.List;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import dao.EmpDao;
import dao.EmpDaoImpl;
import entity.Emp;

public class FindEmpServlet extends HttpServlet{

    @Override
    protected void service(HttpServletRequest req, HttpServletResponse res) throws ServletException, IOException {
        EmpDao dao = new EmpDaoImpl();
        List<Emp> emplist = dao.findAll();
        
         * 转发到jsp,让jsp继续完成这个请求
         * 1.将数据绑定到request上
         * 2.将请求提交给jsp让它继续处理
         * 同时要将request、response给jsp
         * 当前:/jsp2/findEmp
         * 目标:/jsp2/emp_list.jsp

        req.setAttribute("emplist", emplist);
        req.getRequestDispatcher("emp_list.jsp").forward(req, res);
    }   
}

被转发的emp_list.jsp文件

<%@ page pageEncoding="utf-8" import="entity.Emp,java.util.*" %>
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Insert title here</title>
</head>
<body>
    <table border="1">
        <tr>
            <td>编号</td>
            <td>姓名</td>
            <td>职位</td>
            <td>月薪</td>
        </tr>
        <%  
            List<Emp> emplist = (List<Emp>) request.getAttribute("emplist");
            if(emplist != null){
                for(Emp e:emplist){
                %>
                <tr>
                    <td><%=e.getEmpno() %></td>
                    <td><%=e.getEname() %></td>
                    <td><%=e.getJob() %></td>
                    <td><%=e.getSalary() %></td>
                </tr>       
                <%
                }
            }
        %>
    </table>
</body>
</html>
5.png

MVC

引入了MVC设计模式,对代码分层,降低代码的耦合度。
MVC是经典的设计模式,是代码的分层思想:
M:Model,业务层,用来处理业务
V:View,视图层,用来展现数据
C:Controller,控制层,用来进行调度,是业务层和视图层的桥梁
其目的是要将代码解耦,便于团队开发及维护

转发和重定向

1.转发和重定向的相同点
- 都是解决WEB组件之间的跳转问题
- WEB组件:Servlet/JSP

转发的特点:
1.一次请求
2.地址不变
3.一个请求只有一个request,组件可以通过它共享数据
4.只能转发到项目内部的资源

重定向的特定:
1.二次请求
2.地址改变
3.两个请求有两个request,组件无法通过它共享数据
4.可以重定向到项目外部的资源

建议:
一般是这样
- 查询时用转发
- 增加、删除、修改后重定向到查询

jsp例子
Course.java类

package entity;

import java.io.Serializable;

public class Course implements Serializable{
    private Integer courseId;
    private String name;
    private Integer days;

     * 1.与get/set对应的属性叫Bean属性
     * 2.通过get/set反应出来的我们以为的属性
     * 3.去掉get并将首字母小写的单词是Bean属性
     * Bean属性通常和对象属性一致,也可以修改为不一致

    public Integer getId() {
        return courseId;
    }
    public void setId(Integer courseId) {
        this.courseId = courseId;
    }
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    public Integer getDays() {
        return days;
    }
    public void setDays(Integer days) {
        this.days = days;
    }
}

Student.java类,调用上面的Course

package entity;

import java.io.Serializable;

public class Student implements Serializable{
    private String name;
    private Integer age;
    private String sex;
    private String[] interests;
    private Course course;
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    public Integer getAge() {
        return age;
    }
    public void setAge(Integer age) {
        this.age = age;
    }
    public String getSex() {
        return sex;
    }
    public void setSex(String sex) {
        this.sex = sex;
    }
    public String[] getInterests() {
        return interests;
    }
    public void setInterests(String[] interests) {
        this.interests = interests;
    }
    public Course getCourse() {
        return course;
    }
    public void setCourse(Course course) {
        this.course = course;
    }
}

FindStudentServlet.java处理逻辑,并转发到jsp

package web;

import java.io.IOException;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import entity.Course;
import entity.Student;

public class FindStudentServlet extends HttpServlet {

    @Override
    protected void service(HttpServletRequest req, HttpServletResponse res) throws ServletException, IOException {
        Student stu = new Student();
        stu.setName("libai");
        stu.setAge(23);
        stu.setSex("M");
        stu.setInterests(new String[] {"a","b","c","d"});
        Course c = new Course();
        c.setId(1);
        c.setName("java");
        c.setDays(80);
        stu.setCourse(c);
        
        req.setAttribute("stu", stu);
        req.getRequestDispatcher("find_student.jsp").forward(req, res);
    }   
}

find_student.jsp的运算和取值,获取请求中返回的数据

<%@page pageEncoding="utf-8"%>
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>El和JSTL</title>
</head>
<body>
    <h1>El</h1>
    <!-- 等价于request.getAttribute("stu").getName() -->
    <p>姓名:${stu.name}</p>
    <p>年龄:${stu["age"]}</p>
    <!-- 等价于request.getAttribute("stu").getCoures().getId() -->
    <p>课程:${stu.course.id }</p>

    <!-- EL的取值范围:
    1.EL默认从如下4个对象中依次取值
    page,request,session,application
    这4个隐含对象是EL默认的取值范围
    2.也可以指定取值范围
    requestScope.stu.name
    sessionScope.stu.name
    3.设计默认取值的目的,是为了开发者不用经常写
    范围,是为了简化EL表达式
     -->
    <p>性别:${requestScope.stu.sex }</p>

    <!-- EL支持运算 -->
    <p>运算:${stu.age+10 }</p>
    <p>是否在某个区间:${stu.age>20 && stu.age<30}</p>
    <p>是否为空:${empty stu.age }</p>

    <!-- 获取请求参数,我的请求路径为 /jsp3/findStudent?p=123 -->
    <p>参数:${param.p }</p>
</body>
</html>
6.png

JSTL
maven中导入包

<dependencies>
    <dependency>
        <groupId>jstl</groupId>
        <artifactId>jstl</artifactId>
        <version>1.2</version>
    </dependency>
</dependencies>

其他的文件不变,就修改jsp文件,引入标签库,重命名为c,调用if,choose,forEach方法

<%@page pageEncoding="utf-8"%>
<%@taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>JSTL</title>
</head>
<body>
    <p>性别:
        <c:if test="${stu.sex=='M' }">男</c:if>
        <c:if test="${stu.sex=='F' }">女</c:if>
    </p>
    <p>
        性别:
        <c:choose>
            <c:when test="${stu.sex=='M' }">男</c:when>
            <c:otherwise>女</c:otherwise>
        </c:choose>
    </p>
    <!-- items:声明遍历的数据,var:每次循环赋值数据 -->
    <p>
        兴趣:
        <c:forEach items="${stu.interests}" var="interest">
        <span>${interest } </span>
        </c:forEach>
    </p>
</body>
</html>
4.png

JSTL标签原理上还是调用类实现的


3.png

note

 * java.sql包下的日期类型:
 * java.sql.Date 年月日
 * java.sql.Time 时分秒
 * java.sql.Timestamp 年月日时分秒
 * 它们都是java.util.Date的子类

一个Dao的例子,注意,其中的数据库连接池的类DBUtils在我之前文章JDBC中有

package dao;

import java.io.Serializable;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.ArrayList;
import java.util.List;

import util.DBUtils;
import entity.Cost;

public class CostDaoImpl implements CostDao,Serializable{

    public List<Cost> findAll() {
        Connection conn = null;
        try {
            conn = DBUtils.getConnection();
            Statement st = conn.createStatement();
            String sql = "select * from zhizhang order by cost_id";
            ResultSet rs;
            rs = st.executeQuery(sql);
            List<Cost> list = new ArrayList<Cost>();
            while(rs.next()) {
                Cost cost = new Cost();
                cost.setCostId(rs.getInt("cost_id"));
                cost.setName(rs.getString("name"));
                cost.setBaseDuration(rs.getInt("base_duration"));
                cost.setBaseCost(rs.getDouble("base_cost"));
                cost.setUnitCost(rs.getDouble("unit_cost"));
                cost.setStatus(rs.getString("status"));
                cost.setDescription(rs.getString("description"));
                cost.setCreatime(rs.getTimestamp("creatime"));
                cost.setStartime(rs.getTimestamp("startime"));
                cost.setCostType(rs.getString("cost_type"));
                
                list.add(cost);
            }
            return list;
        } catch (SQLException e) {
            e.printStackTrace();
            throw new RuntimeException("查询失败", e);
        } finally {
            DBUtils.close(conn);
        }
    }

     * setInt()/setDouble()不允许传入null,
     * 但实际业务中这些字段确实有null的情况,
     * 表里也支持null,此时需要将他们当作对象来处理
     * 都改成setObject()就行了    
    
    public void save(Cost c) {
        Connection conn = null;
        try {
            conn = DBUtils.getConnection();
            String sql = "insert into zhizhang (name,base_duration,base_cost,unit_cost,status,description,creatime,startime) "
                    + "values(?,?,?,?,?,?,?,NULL)";
            PreparedStatement st = conn.prepareStatement(sql);
            st.setString(1, c.getName());
            st.setObject(2, c.getBaseDuration());
            st.setObject(3, c.getBaseCost());
            st.setObject(4, c.getUnitCost());
            st.setString(5, c.getDescription());
            st.setString(6, c.getCostType());
            st.executeUpdate();
            
        } catch(Exception e) {
            e.printStackTrace();
            throw new RuntimeException("增加失败");
        } finally {
            DBUtils.close(conn);
        }
    }
}

MainServlet.java

package web;

import java.io.IOException;
import java.io.PrintWriter;
import java.util.List;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import dao.CostDao;
import dao.CostDaoImpl;
import entity.Cost;

public class MainServlet extends HttpServlet{

    @Override
    protected void service(HttpServletRequest req, HttpServletResponse res) throws ServletException, IOException {
        String path = req.getServletPath();
        System.out.println("path = " + path);
        if("/findCost.do".equals(path)) {
            findCost(req, res);
        }else if("/toAddCost.do".equals(path)) {
            toAddCost(req, res);
        }else if("/addCost.do".equals(path)) {
            addCost(req, res);
        }else if("/toLogin.do".equals(path)){
            toLogin(req, res);
        }else if("/toIndex.do".equals(path)){
            toIndex(req, res);
        }else {
            throw new RuntimeException("查无此页");
        }
    }
    
    protected void toLogin(HttpServletRequest req, HttpServletResponse res) throws ServletException, IOException {
        req.getRequestDispatcher("WEB-INF/cost/login.jsp").forward(req, res);
    }
    protected void toIndex(HttpServletRequest req, HttpServletResponse res) throws ServletException, IOException {
        req.getRequestDispatcher("WEB-INF/cost/index.jsp").forward(req, res);
    }
    
    protected void findCost(HttpServletRequest req, HttpServletResponse res) throws ServletException, IOException {
        CostDao dao = new CostDaoImpl();
        List<Cost> list = dao.findAll();
        req.setAttribute("list", list);
        req.getRequestDispatcher("WEB-INF/cost/find.jsp").forward(req, res);
    }
    
    protected void toAddCost(HttpServletRequest req, HttpServletResponse res) throws ServletException, IOException {
        req.getRequestDispatcher("WEB-INF/cost/add.jsp").forward(req, res);
    }
}

自定义错误页面

配置错误页面:
1.tomcat是所有服务端代码调用的入口,它在调用代码时会尝试捕获异常。
2.默认捕获到异常会自动转发到对应的错误页面,如404.html、500.html。
3.我们可以通过配置改变这个默认的行为,明确告诉
tomcat哪个异常去哪个错误页面,这件事本事解决不了异常,仅仅是看起来更友好。
4.声明错误页面时需要写绝对路径,但由于是转发过去,只能访问此项目内部的资源,所以tomcat自动帮我们加上项目名

<!-- 1.通过异常类型配置 -->
<error-page>
    <exception-type>java.lang.Exception</exception-type>
    <location>/WEB-INF/error.jsp</location>
</error-page>
<!-- 2.通过异常编号(404/405/500)进行配置 -->
JSTL:(JSP Standard Tag Library)JSP标准标签库

WEB-INF具有保护作用,放在它内部的资源受保护,
无法直接访问,必须通过转发来访问。

1.若jsp没放到WEB-INF下,就可以直接访问它
2.在MVC模式下,直接访问jsp会使其丢失数据来源,导致报错
3.将其放到WEB-INF下,就是包含它不被直接访问,从而避免报错。

静态资源不要放在WEB-INF下。

Cookie

cookie的特点:
- cookie保存在浏览器上
- 多个请求可以共用一组cookie
- 多个Servlet可以共用一组cookie

在/main/login路径下创建的cookie,它只对/main及其下级路径有效

EL默认的取值范围是:
page->request->session->application
EL也可以从cookie中取值,语法为:
cookie.key.value

设置cookie

package web;

import java.io.IOException;
import java.io.PrintWriter;

import javax.servlet.ServletException;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

public class LoginServlet extends HttpServlet{

    @Override
    protected void service(HttpServletRequest req, HttpServletResponse res) throws ServletException, IOException {
        String username = req.getParameter("username");

         * 检查账号密码,通过后将账号存入cookie
         * 每个cookie对象只能存一条数据
         * key,value都是字符串
         * 将cookie发送给浏览器,浏览器接收后会自动保存

        Cookie cookie = new Cookie("username", username);

         * 声明cookie的生存时间
         * >0时cookie保存在客户端的硬盘上
         * =0时cookie被浏览器删除
         * 默认<0,保存在内存中

        cookie.setMaxAge(600000); //秒数
        cookie.setPath("/jsp4");  //设置cookie的有效路径,对整个项目都有效
        
        res.addCookie(cookie);
        res.setContentType("text/html;charset=utf-8");
        PrintWriter out = res.getWriter();
        out.println("设置cookie, name = " + cookie.getName() + ", value = " + cookie.getValue());
        out.close();
    }   
}

cookie取值

package web;

import java.io.IOException;
import java.io.PrintWriter;

import javax.servlet.ServletException;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

public class IndexServlet extends HttpServlet{

    @Override
    protected void service(HttpServletRequest req, HttpServletResponse res) throws ServletException, IOException {

         * 获取登陆时存入的cookie的值,检测是否过登陆了
        PrintWriter out = res.getWriter();
        Cookie[] cs = req.getCookies();
        if(cs != null) {
            res.setContentType("text/html;charset=utf-8");
            for(Cookie c:cs) {
                out.println(c.getName() + " = " + c.getValue() + "<br>");
            }
        }
        out.close();
    }   
}

Session

- 浏览器第一次访问服务器时,服务器会给它创建1个session
- 服务器使用cookie将session的SID返回给浏览器
- 浏览器下次访问该服务器时会带上SID
- 多个请求可以共用同一个session
- 多个Servlet可以共用同一个session
* session是存储在服务器上的对象,它的内部可以存储任意类型的数据
String username = req.getParameter("username");
HttpSession session = req.getSession();
session.setAttribute("username", username);

* 响应时服务器会自动创建cookie,将session的ID通过cookie发送给浏览器,类似如下
* Cookie c = new Cookie("JSESSIONID",session.getId());
HttpSession session = req.getSession();
String username = (String)session.getAttribute("username");
如果cookie被禁用了,可以使用URL重写解决问题,也就是把SessionID加到地址后面

过滤器

1.过滤器的作用
- 用来处理项目中的公共需求
- 举例:记录日志、过滤敏感词、权限检查
- 公共的需求:很多请求都包含的业务

2.实现步骤:
- 创建一个类,实现过滤器接口Filter
- 在web.xml中配置这个类
- 过滤器由服务器自动调用

两个简单的业务

public class FindCostServlet extends HttpServlet{
    @Override
    protected void service(HttpServletRequest req, HttpServletResponse res) throws ServletException, IOException {
        System.out.println("查询");
        res.setContentType("text/html;charset=utf-8");
        PrintWriter out = res.getWriter();
        out.println("查询");
        out.close();
    }
}

public class AddCostServlet extends HttpServlet {
    @Override
    protected void service(HttpServletRequest req, HttpServletResponse res) throws ServletException, IOException {
        System.out.println("添加");
        res.setContentType("text/html;charset=utf-8");
        PrintWriter out = res.getWriter();
        out.println("添加");
        out.close();
    }
}

一个日志过滤器,在执行各个Servlet之前,之后记录日志

package web;

import java.io.IOException;

import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;

public class LogFilter implements Filter{
     * 关闭tomcat时自动调用此方法
    public void destroy() {
        System.out.println("Log过滤器的销毁方法");
    }

     * 该方法是处理公共业务的方法
     * Filter相当于是Servlet的管家,tomcat在调用Servlet之前会将请求
     * 提交给Filter,Filter有权让请求继续,也可以让请求终止
     * tomcat就是调用doFilter方法让Filter统一处理请求的,调用它之前会
     * 创建好request和response并传入,创建的类型:RequestFacade和ResponseFacade

    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
            throws IOException, ServletException {
        System.out.println("Log chain 前");

         * 请求跳转到各个Servlet中继续执行
        chain.doFilter(request, response);
        System.out.println("Log chain 后");
    }

     * tomcat启动时会自动实例化Filter,然后调用
     * 其init()来初始化Filter,调用此方法时会传入config,
     * 该对象和Filter是1对1的关系,可以给Filter预置参数(web.xml).
     * 该对象和ServletConfig用法完全一样
    public void init(FilterConfig filterConfig) throws ServletException {
        System.out.println("Log过滤器的初始化方法");
    }
}

检查是否登陆,没有登陆跳转到特定的页面

package web;

import java.io.IOException;

import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;


public class LoginFilter implements Filter{

    public void destroy() {
        System.out.println("Login过滤器的销毁方法");
    }

    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
            throws IOException, ServletException {
        System.out.println("Login chain 前");
        String[] paths = new String[] {
                "/find"
        };
        HttpServletRequest req = (HttpServletRequest)request;
        HttpServletResponse res = (HttpServletResponse)response;
        String path = req.getServletPath();
        for(String p:paths) {
            if(p.equals(path)) {
                chain.doFilter(request, response);
                System.out.println("Login chain 后");
                return;
            }
        }
        HttpSession session = req.getSession();
        String username = (String) session.getAttribute("username");
        if(username != null && username != "") {   // 没有登陆时,username为null
            chain.doFilter(request, response);
            System.out.println("Log chain 后");
        }else {
            res.sendRedirect("/jsp5/find");
        }
    }

    public void init(FilterConfig filterConfig) throws ServletException {
        System.out.println("Login过滤器的初始化方法");
    }   
}

配置web.xml文件,url-pattern中声明需要过滤的请求

<filter>
    <filter-name>log</filter-name>
    <filter-class>web.LogFilter</filter-class>
</filter>
<filter-mapping>
    <filter-name>log</filter-name>
    <!-- 声明此Filter可以过滤哪些请求 -->
    <url-pattern>/*</url-pattern>
</filter-mapping>
<filter>
    <filter-name>login</filter-name>
    <filter-class>web.LoginFilter</filter-class>
</filter>
<filter-mapping>
    <filter-name>login</filter-name>
    <url-pattern>/*</url-pattern>
</filter-mapping>

最终的结果,当你访问 /jsp5/add (jsp5是我的项目名),会跳转到 /jsp5/find页面

Log chain 前
Login chain 前
查询
Login chain 后
Log chain 后

各个过滤器之间是一层套一层的关系,而他们的执行顺序是按照<filter-mapping>标签的位置来确定的

过滤器上也可以设置特定的参数

<filter>
    <filter-name>test</filter-name>
    <filter-class>web.TestFilter</filter-class>
    <init-param>
        <param-name>city</param-name>
        <param-value>北京</param-value>
    </init-param>
</filter>
<filter-mapping>
    <filter-name>test</filter-name>
    <url-pattern>/*</url-pattern>
</filter-mapping>

获取设置的参数
    public void init(FilterConfig filterConfig) throws ServletException {
        System.out.println(filterConfig.getInitParameter("city"));
    }

监听器,可以监听HttpSession,HttpRequest,ServletContext三个对象的创建和销毁,还能监听各个对象的增加、删除、替换值的事件

package web;

import javax.servlet.ServletRequestAttributeEvent;
import javax.servlet.ServletRequestAttributeListener;
import javax.servlet.ServletRequestEvent;
import javax.servlet.ServletRequestListener;

public class MyListener implements ServletRequestListener,ServletRequestAttributeListener {

     * tomcat销毁request前自动调用此方法
    public void requestDestroyed(ServletRequestEvent e) {
        System.out.println("销毁request");
    }

     * tomcat创建request后自动调用此方法
    public void requestInitialized(ServletRequestEvent e) {
        System.out.println("创建request");
        System.out.println(e.getServletRequest());
    }

    public void attributeAdded(ServletRequestAttributeEvent arg0) {
        System.out.println("向request内添加一个值");
    }

    public void attributeRemoved(ServletRequestAttributeEvent arg0) {
    }

    public void attributeReplaced(ServletRequestAttributeEvent arg0) {
    }
}

web.xml中添加配置

<listener>
    <listener-class>web.MyListener</listener-class>
</listener>

打印结果

创建request
org.apache.catalina.connector.RequestFacade@63e1bdfd
Log chain 前
Login chain 前
查询
Login chain 后
Log chain 后
销毁request
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 194,670评论 5 460
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 81,928评论 2 371
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 141,926评论 0 320
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 52,238评论 1 263
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 61,112评论 4 356
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 46,138评论 1 272
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 36,545评论 3 381
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 35,232评论 0 253
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 39,496评论 1 290
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 34,596评论 2 310
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 36,369评论 1 326
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,226评论 3 313
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 37,600评论 3 299
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 28,906评论 0 17
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,185评论 1 250
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 41,516评论 2 341
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 40,721评论 2 335

推荐阅读更多精彩内容

  • 简书 賈小強转载请注明原创出处,谢谢! Servlet是一种允许响应请求的Java类。虽然Servlet可以响应任...
    賈小強阅读 10,607评论 1 44
  • 一、基础知识学习Servlet技术,首先需要有一个Servlet运行环境,也就是需要有一个Servlet容器,本书...
    汪梓文阅读 245评论 0 2
  • 1 如何开发一个Servlet(重点) 1.1 步骤: 1)编写java类,继承HttpServlet类 2)重新...
    yangliangliang阅读 345评论 0 0
  • 一.你应该懂得有关忧虑的根本事实(Fundamental facts you should know about ...
    笑忘书v阅读 639评论 0 0
  • 团建活动定庆丰公园,微风佛面,正惬意之时,勇哥说:“我们列两队、向右看齐、立正,要像军训一样,跑到每个点。...
    泥沙_燕子阅读 183评论 0 1