JavaWeb技术

JavaWeb技术

1、统一了项目的整体结构(标准化)。

2、可以动态的加载jar包(导入依赖)。
jdbc技术
----- 导入jar包---mysql数据库驱动包。
3、便于项目的打包、部署、发布。

一、JSP简介

JSP其实就是Java  Server  Pages的缩写,是一种动态网页技术。能够支持的编程语言只有Java程序。专门负责进行软件界面的设计。

JSP = Java程序 + Server服务器 + 页面(前端技术)

Java程序:我们在之前所学习到的所有的Java应用程序;

Server服务器:这是JavaWeb项目运行的环境容器;

Pages页面:主要由前端技术所构成(HTML、CSS、Js、Jquery、Vue.....)

二、Server服务器

2.1、Web服务器的介绍

Server服务器就是运行JavaWeb项目的一种容器和环境,所以我们需要搭建好这个容器,否则是无法运行我们的Web应用程序的。

目前市面上比较流行的Server服务器:

Tomcat服务器:运行JavaWeb项目的;

Jboss服务器:运行JavaWeb项目的;

WebLogic服务器:运行JavaWeb项目的;

IIS服务器:运行.net程序的;

..............................................

注意!

不同类型的编程语言所开发的Web项目,是使用不同类型的Server服务器来运行的。

2.2、Tomcat集成到Eclipse开发工具

1645668491202

1645668718944

注意! 设置完毕后,需要使用Ctrl+S进行保存,再关闭。

2.3、JavaWeb项目的部署和运行

创建JavaWeb项目---->编写项目---->将项目发布到Tomcat服务器---->启动Tomcat服务器----->访问项目

实例:

http://web服务器的地址:web服务器的端口号/项目名称/项目资源

1、都是基于http协议的;

2、web服务器的地址就是web服务器所在的计算机的ip地址;

3、web服务器的端口号可以在web服务器的配置文件中进行查看或者修改;

4、项目名称;

5、具体的资源;

有关Tomcat服务器端口号的查看和修改:

Tomcat根目录-------conf--------server.xml

1645673840747

针对Tomcat服务器需要掌握的重点

1、在配置文件中针对GET形式的请求进行编码的设置;

2、在配置文件中修改服务器的端口号;

三、JSP文件的结构

3.1、指令元素

page指令:设置当前这个JSP文件相关的特性。比如:支持的编程语言

<%@  page  属性名称="属性值"  ......   %>
<%@ page language="java" contentType="text/html; charset=UTF-8"   pageEncoding="UTF-8"%>

include指令:包含指令,用于包含其他的页面(静态包含)。

<%@ include  file="hello.jsp" %>

taglib指令:导入标签库。

<%@taglib  prefix="前缀名称"  uri="标签库的地址" %>   

3.2、脚本元素

脚本片段:

<%  java程序代码片段  %>

表达式(显示输出):

<%=  变量|常量  %>

脚本声明:

<%! 定义变量|常量等  %>

关键点:脚本片段 和 脚本声明的区别?

<% 脚本片段 %> : 最终定义的内容都是在_jspService的方法中生成的。属于一个局部性的内容信息;

<%!脚本声明 %> :  最终定义的内容都是在jsp文件所生成的Servlet结构的类中的成员信息;
<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Insert title here</title>
</head>
<body>
         <%
                 String    name = "admin";
         %>

        <%!
                 String    address = "上海";
        %>
</body>
</html>

1645685680994

四、JSP的底层原理

4.1、原理分析

当我们访问一个JavaWeb项目中的某一个jsp文件的时候,首先Tomcat服务器就会把这个jsp文件生成一个Servlet结构的类(class)。执行的时候自动运行类中的_jspService的方法。

1645685270196

4.2、源码分析原理

jsp转换为Servlet结构后的源码:

/*
 * Generated by the Jasper component of Apache Tomcat
 * Version: Apache Tomcat/7.0.75
 * Generated at: 2022-02-24 07:03:51 UTC
 * Note: The last modified time of this file was set to
 *       the last modified time of the source file after
 *       generation to assist with modification tracking.
 */
package org.apache.jsp;

import javax.servlet.*;
import javax.servlet.http.*;
import javax.servlet.jsp.*;

public final class _03_jsp extends org.apache.jasper.runtime.HttpJspBase
    implements org.apache.jasper.runtime.JspSourceDependent {


                 String    name = "上海";
                //这里面能定义哪些东西?
                
                final   int   age = 50;
                
                public  String    add = "admin";
                
                //定义方法吗?
                
                
        
  private static final javax.servlet.jsp.JspFactory _jspxFactory =
          javax.servlet.jsp.JspFactory.getDefaultFactory();

  private static java.util.Map<java.lang.String,java.lang.Long> _jspx_dependants;

  private volatile javax.el.ExpressionFactory _el_expressionfactory;
  private volatile org.apache.tomcat.InstanceManager _jsp_instancemanager;

  public java.util.Map<java.lang.String,java.lang.Long> getDependants() {
    return _jspx_dependants;
  }

  public javax.el.ExpressionFactory _jsp_getExpressionFactory() {
    if (_el_expressionfactory == null) {
      synchronized (this) {
        if (_el_expressionfactory == null) {
          _el_expressionfactory = _jspxFactory.getJspApplicationContext(getServletConfig().getServletContext()).getExpressionFactory();
        }
      }
    }
    return _el_expressionfactory;
  }

  public org.apache.tomcat.InstanceManager _jsp_getInstanceManager() {
    if (_jsp_instancemanager == null) {
      synchronized (this) {
        if (_jsp_instancemanager == null) {
          _jsp_instancemanager = org.apache.jasper.runtime.InstanceManagerFactory.getInstanceManager(getServletConfig());
        }
      }
    }
    return _jsp_instancemanager;
  }

  public void _jspInit() {
  }

  public void _jspDestroy() {
  }

  public void _jspService(final javax.servlet.http.HttpServletRequest request, final javax.servlet.http.HttpServletResponse response)
        throws java.io.IOException, javax.servlet.ServletException {

    final javax.servlet.jsp.PageContext pageContext;
    javax.servlet.http.HttpSession session = null;
    final javax.servlet.ServletContext application;
    final javax.servlet.ServletConfig config;
    javax.servlet.jsp.JspWriter out = null;
    final java.lang.Object page = this;
    javax.servlet.jsp.JspWriter _jspx_out = null;
    javax.servlet.jsp.PageContext _jspx_page_context = null;


    try {
      response.setContentType("text/html; charset=UTF-8");
      pageContext = _jspxFactory.getPageContext(this, request, response,
      			null, true, 8192, true);
      _jspx_page_context = pageContext;
      application = pageContext.getServletContext();
      config = pageContext.getServletConfig();
      session = pageContext.getSession();
      out = pageContext.getOut();
      _jspx_out = out;

      out.write("\r\n");
      out.write("<!DOCTYPE html PUBLIC \"-//W3C//DTD HTML 4.01 Transitional//EN\" \"http://www.w3.org/TR/html4/loose.dtd\">\r\n");
      out.write("<html>\r\n");
      out.write("<head>\r\n");
      out.write("<meta http-equiv=\"Content-Type\" content=\"text/html; charset=UTF-8\">\r\n");
      out.write("<title>Insert title here</title>\r\n");
      out.write("</head>\r\n");
      out.write("<body>\r\n");
      out.write("         ");

                 String    name = "admin";
                 //这里面能定义哪些东西?
                 
                 final     int  age = 40;
                 
                // public  String    add = "admin";
                     
         
      out.write("\r\n");
      out.write("\r\n");
      out.write("        ");
      out.write("\r\n");
      out.write("        \r\n");
      out.write("        \r\n");
      out.write("</body>\r\n");
      out.write("</html>");
    } catch (java.lang.Throwable t) {
      if (!(t instanceof javax.servlet.jsp.SkipPageException)){
        out = _jspx_out;
        if (out != null && out.getBufferSize() != 0)
          try {
            if (response.isCommitted()) {
              out.flush();
            } else {
              out.clearBuffer();
            }
          } catch (java.io.IOException e) {}
        if (_jspx_page_context != null) _jspx_page_context.handlePageException(t);
        else throw new ServletException(t);
      }
    } finally {
      _jspxFactory.releasePageContext(_jspx_page_context);
    }
  }
}

五、JSP常见内置对象

5.1、内置对象基本概念

所谓的内置对象就是JSP内部本身已经提供的对象,不需要像Java程序中那样使用new关键字进行创建。

在JSP中存在的内置对象共有九个

9个内置对象====

pageContext 、page 、request、response 、session 、application

out、 config 、 exception

四个是属于作用域对象

范围(小)------------------------------------> 范围(大)

pageContext 、 request 、 session 、 application

request对象:表示的是一个请求。

//基于请求对象设置编码格式(只是针对post请求有效)
request.setCharacterEncoding("UTF-8");

//通过请求对象获取参数数据,最终都是String类型
request.getParameter("参数名称");

response对象:表示的是一个响应。

//基于response对象进行页面的重新定向
response.sendRedirect("jsp页面");

5.2、内置对象介绍

在JSP中存在的内置对象共有9个:

pageContext : 只能在当前页面存,在当前页面取值;

注意!pageContext对象具备获取其他内置对象的能力。

request:基于一次请求的范围内,可以自由的存值和取值;

超链接、重定向 都会发出2次请求。而基于服务器端的转发是不会重新发出新的请求的。

session :基于同一个会话范围内,可以自由的存值和取值;

只要在没有关闭浏览器的情况下,使用 超链接、重定向 、基于服务器端的转发都属于同一个会话,如果关闭浏览器进行重新打开进行访问则这次会话中断了。

application :基于服务器端的一个应用为范围的;
所谓的应用的范围指代的是这个应用所部署的服务器没有被重新启动的情况下,一直会存在。

以上这4个对象,既是内置对象,同时还是“作用域对象”。

何为作用域对象?
作用域对象就是可以进行数据的存储,在作用范围内可以进行取值。

response :表示的是针对客户端的响应;

page :表示的就是本页面的对象;

out:用于向页面写出信息的对象;

config :管理配置的一个对象;

exception:处理异常的对象;

5.3、内置对象常见方法

pageContext对象常用方法:

//有关作用域的存值和取值操作
pageContext.findAttribute(""); //针对同名的情况,按照作用域从小---大的范围进行查找;
pageContext.setAttribute("key", value); 
pageContext.getAttribute("key");
		
//pageContext对象具备获取其他的内置对象的特性
pageContext.getOut();
pageContext.getRequest();
pageContext.getResponse();
pageContext.getServletConfig();
pageContext.getServletContext(); // ServletContext就是application对象的类型;
pageContext.getSession();

request对象常用方法:

       //设置编码格式
       request.setCharacterEncoding("编码格式");
	   //获取单个参数的数据值
	   request.getParameter("参数名称");

	   //获取批量数据值
	   String[]   request.getParameterValues("参数名称");
	   
	   //有关作用域特性的存值和取值
	   request.setAttribute("key", value);
	   request.getAttribute("key");
	   
       //转发
	   request.getRequestDispatcher("地址").forward(request, response);
	    
	   //获取提交请求的方式
	   request.getMethod();   //  get  |  post
	   request.getCookies();   //操作Cookie的时候,用于获取Cookie对象

提示!

超链接属于什么类型的请求: get类型的。

form表单请求类型:自定义的get | post。

session对象常用方法:

//有关作用域的存值和取值
session.setAttribute("key", value);
session.getAttribute("key");
	    
//通过程序设置及获取session的最大有效值和相关信息
session.setMaxInactiveInterval(1000);
session.getMaxInactiveInterval();
//注意!
//有关session相关的参数的设置,也可以在配置文件中进行设置,并且是推荐的做法。

//通过程序销毁session对象
session.invalidate();
//获取session的ID
session.getId();

application对象中常用的方法:

//有关作用域的存值和取值
application.setAttribute("key", value);
application.getAttribute("key");
	     
//获取项目路径(相对路径)
application.getContextPath();
//定义路径(获取到的是项目的真实路径+自定义的目录)
application.getRealPath("/uploads");

//在配置文件中获取全局性参数
application.getInitParameter("参数名称");

response对象常用方法:

//重新定向页面
response.sendRedirect("页面地址");

page对象:

//page ---- 表示本JSP页面(对象),相当于类中的this
//page对象中的方法就是Object类中的所有方法,基本不会使用;

config对象常用方法:

//config--- 管理配置文件的
//获取Servlet配置中的参数
config.getInitParameter("参数名称");

//获取application对象
config.getServletContext();

out对象常用方法:

//out对象专门负责向JSP页面写出信息
out.println();
out.print("信息");

out.write("信息");

//备注!最终我们都不会进行使用,太繁琐!

exception对象常用方法:

//exception---表示异常的对象,在jsp中是处于一个关闭的状态,在page指令元素中可以通过 //isErrorPage="true" 进行开启才能使用
exception.printStackTrace();  //打印异常的轨迹信息;
exception.getMessage();//打印异常的简略信息;

六、JSP动作元素

6.1、动作元素简介

与JSP指令元素不同的是,JSP动作元素在请求处理阶段起作用。JSP动作元素是用XML语法写成的。利用JSP动作可以动

态地插入文件、重用JavaBean组件、把用户重定向到另外的页面、为Java插件生成HTML代码。

注意!强调的是在请求处理阶段进行的相关操作,也就是所谓的动态性。

6.2、常见动作元素

//等价于服务器端的转发
<jsp:forward page="页面地址"></jsp:forward>

//动态的创建一个对象(反调的是类的无参数的构造)
<jsp:useBean id=""  class="全限定类名"  scope="作用域"></jsp:useBean>

//给对象动态赋值
<jsp:setProperty property="属性名称" name="指定的bean"  value="属性值"/>

//从对象中取出属性的值
<jsp:getProperty property="属性名称" name="指定的bean"/>

//动态包含
<jsp:include page="页面地址"></jsp:include>
<jsp:useBean id="dept"  class="com.it.www.po.Dept"></jsp:useBean> 

<%
     //以上的这个useBean动作元素所做的事情是如下这些事情(底层原理):
/*    
   Class   clas =   Class.forName("com.it.www.po.Dept");
   Dept  dept = (Dept)clas.newInstance();
   pageContext.setAttribute("dept", dept); 
   */
%>

<!-- 通过动作元素进行赋值操作 -->
<jsp:setProperty property="deptid" name="dept"  value="100"/>
<jsp:setProperty property="deptname" name="dept"  value="售后部"/>

<!-- 通过动作元素进行取值操作 -->
<jsp:getProperty property="deptid" name="dept"/>
<jsp:getProperty property="deptname" name="dept"/>

七、常见问题

7.1、重定向和转发的区别

1、重定向是基于客户端(浏览器)的,发出的全新的一次新请求(request作用域丢失了)。url地址是会发生变化的

2、转发是基于服务器端的,延续的是同一次request请求,转发是不会体现出地址栏的地址的变化的。

7.2、静态包含和动态包含的区别

<%@ include file=""/>  
静态包含是首先将各个页面进行合并,再统一进行解析,最终生成一个Servlet类结构,可以进行程序变量的共享。

<jsp:include page=""/>
动态包含是将所有的页面进行各自独立的解析,生成各自的Servlet类结构,不能进行变量的共享。

共性:
最终都是将内容进行了合并显示。

7.3、HTML注释和JSP注释的区别

<!-- 
  HTML注释
 -->
HTML注释,只会在浏览器端生效,浏览器能够进行识别和辨认,可以见到源码,但是没有显示效果。      
      
<%-- 
  JSP标准注释
--%>

JSP注释:在服务器中就可以进行识别,并且将内容进行了注销,最终是没有达到客户端的。

7.4、Get与Post请求的区别

1、Get请求和Post请求是浏览器可以直接支持的,都是属于Http协议下的请求方式;

2、Get请求会将数据暴露在URL地址栏,我们会认为不安全的;

3、Post请求是将数据写入到协议体的内部从而进行传输的,相对比较安全的;

4、Get请求在地址栏中传递的参数数据针对不同的浏览器有不同的长度限制;

5、Post请求传递的数据是没有限制的;

6、从设计的理念上来讲,Get侧重于从服务器端获取数据到客户端,Post侧重于将客户端的数据提交到服务器端;

八、Servlet技术

从技术诞生的角度来讲,首先出现的是Servlet技术,然后才有JSP技术。我们在讲解的时候,首先讲解的是JSP技术,再

讲解的是Servlet技术。因为JSP是Servlet的简化。

其实JSP本质上就是一个Servlet类。

Servlet技术在我们的三层结构中,充当的是“控制器”的角色。

三层结构:基于MVC分层结构

M:Model模型层(Bean 、 dao 、 service)

V:View视图层(JSP页面、Html页面)

C:Controller控制层(Servlet)

8.1、Servlet简介

Servlet(Server Applet)是Java Servlet的简称,称为小服务程序或服务连接器,用Java编写的服务器端程序,具有独立于平台和协议的特性,主要功能在于交互式地浏览和生成数据,生成动态Web内容。
狭义的Servlet是指Java语言实现的一个接口,广义的Servlet是指任何实现了这个Servlet接口的类,一般情况下,人们将Servlet理解为后者。Servlet运行于支持Java的应用服务器中。从原理上讲,Servlet可以响应任何类型的请求,但绝大多数情况下Servlet只用来扩展基于HTTP协议的Web服务器。
最早支持Servlet标准的是JavaSoft的Java Web Server,此后,一些其它的基于Java的Web服务器开始支持标准的Servlet。

总结如下几点:

1、Servlet是基于服务器端的程序;

2、Servlet主要是获取界面参数、调用业务加工数据,并向页面进行输出(Servlet的响应);

3、Servlet都是通过实现Servlet接口的类来进行定义的;

4、Servlet可以接收http协议下的各种请求(get | post);

8.2、Servlet角色

1646099045966

8.3、编写Servlet

FirstServlet.java

package com.it.www.controller;

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 FirstServlet extends HttpServlet {

	public FirstServlet() {
		super();
		System.out.println("----FirstServlet----");
	}

	//init----初始化方法
	public void init(ServletConfig config) throws ServletException {
		System.out.println("-----init------");
	}

	//destory----销毁的方法
	public void destroy() {
		System.out.println("-------destroy-----");
	}

	//service --- 业务方法
	protected void service(HttpServletRequest request,
			HttpServletResponse response) throws ServletException, IOException {
		System.out.println("---service----");
	}

	//处理get请求的方法
	protected void doGet(HttpServletRequest request,
			HttpServletResponse response) throws ServletException, IOException {
		System.out.println("-----doGet-----");
	}

	//处理post请求的方法
	protected void doPost(HttpServletRequest request,
			HttpServletResponse response) throws ServletException, IOException {
		System.out.println("------doPost-------");
	}

}

web.xml

<?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" id="WebApp_ID" version="2.5">

  <!-- servlet的配置信息 -->
  <servlet>
    <servlet-name>FirstServlet</servlet-name>
    <servlet-class>com.it.www.controller.FirstServlet</servlet-class>
  </servlet>
  <!-- servlet映射配置信息 -->
  <servlet-mapping>
    <servlet-name>FirstServlet</servlet-name>
    <url-pattern>/firstServlet.do</url-pattern>
  </servlet-mapping>
  
</web-app>

通过地址: http://localhost:9000/servlet_01/firstServlet.do 进行访问:

输出结果为:

----FirstServlet----
-----init------
---service----

当我们注释掉FirstServlet类中的service方法后,访问后的输出结果为:

----FirstServlet----
-----init------
-----doGet-----

从上面的Servlet的使用中我们可以得出,自定义一个类继承HttpServlet就可以创建出一个Servlet类。

|----------Servlet接口

​ |---------abstract class GenericServlet (实现类)

​ abstract service方法

​ |----abstract class HttpServlet (子类)

​ 完成service 方法-----内部判断(根据请求方式)

​ doGet 和 doPost

​ |----自定义的Servlet类

​ 1、重写doGet 或者是 doPost方法 (存在很大的缺陷)

​ 2、重写service方法(自己在内部进行判断----根据方法名称进行判断)

当我们想去创建一个Serlvet的时候,可以有如下的几种做法:

1、继承HttpServlet类;

2、继承GenericServlet类;

3、实现Servlet接口;

参考源码:

public abstract class GenericServlet implements Servlet

public abstract class HttpServlet extends GenericServlet

8.4、Servlet底层原理的分析

HttpServlet(父类)中的源码是这样的:

public abstract class HttpServlet extends GenericServlet {
    
    protected void service(HttpServletRequest req, HttpServletResponse resp)
        throws ServletException, IOException {
        String method = req.getMethod();
        if (method.equals(METHOD_GET)) {
            doGet(req, resp);
        }  else if (method.equals(METHOD_POST)) {
            doPost(req, resp);
        } 
    }
   
    protected void doGet(HttpServletRequest req, HttpServletResponse resp)
        throws ServletException, IOException{
        .......................
    }
    
    protected void doPost(HttpServletRequest req, HttpServletResponse resp)
        throws ServletException, IOException {
     .......................
    }
    
}    

FirstServlet中的编码是这样的:

package com.it.www.controller;

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 FirstServlet extends HttpServlet {

	//service --- 业务方法(重写的是父类中的service)
	protected void service(HttpServletRequest request,
			HttpServletResponse response) throws ServletException, IOException {
		System.out.println("---service----");
	}

	//处理get请求的方法-----重写父类中的doGet方法
	protected void doGet(HttpServletRequest request,
			HttpServletResponse response) throws ServletException, IOException {
		System.out.println("-----doGet-----");
	}

	//处理post请求的方法-----重写父类中的doPost方法
	protected void doPost(HttpServletRequest request,
			HttpServletResponse response) throws ServletException, IOException {
		System.out.println("------doPost-------");
	}
}

得出一个结论:

当我们通过一个URL地址针对Servlet进行访问的时候,首先运行的是Servlet中的service方法。

基于MVC模式实现(信息的查询)

1646102504293

8.5、基于Servlet动态方法调用

BaseAction.java就是针对Servlet中方法的动态调用而做的一个封装

/**
 * 这是一个基础的Servlet类,我们可以将Servlet进行一个封装处理 
 * 实现方法的动态调用
 * @author Administrator
 */
public class BaseAction extends HttpServlet {

	@Override
	protected void service(HttpServletRequest request,
			HttpServletResponse response) throws ServletException, IOException {
		// 设置编码
		request.setCharacterEncoding("UTF-8");
		// 获取方法名称
		String methodName = request.getParameter("m");
		//将来是 list  、 save 、updateInput 、update 、delete
		// 使用Java的反射机制来实现方法的动态调用
		// 给你一个方法名称-----------反射机制-------------对应的去调用到相应的真实的方法
		// m-----"list"方法名称------通过方法名称找到方法对象Method类--------invoke方法(实现反调)
		try {
			//1、根据方法名称获取方法对象
			Method   method = this.getClass().getDeclaredMethod(methodName, HttpServletRequest.class,HttpServletResponse.class);
			//2、针对方法对象实施反调
			method.invoke(this, request,response);
		} catch (NoSuchMethodException e) {
			e.printStackTrace();
		} catch (SecurityException e) {
			e.printStackTrace();
		} catch (IllegalAccessException e) {
			e.printStackTrace();
		} catch (IllegalArgumentException e) {
			e.printStackTrace();
		} catch (InvocationTargetException e) {
			e.printStackTrace();
		}
	}

}

PersonAction.java

public class PersonAction extends BaseAction {

	private PersonService personService;

	@Override
	public void init() throws ServletException {
		this.personService = new PersonServiceImple();
	}
    
    // 实现查询的控制
	protected void list(HttpServletRequest request, HttpServletResponse response)
			throws ServletException, IOException {
	    ...................
	}
}    

8.6、Servlet的生命周期

Servlet的生命周期,指的就是Servlet从生到死的过程。

Servlet的生命周期,是关系到几个相对应的方法的:

1、构造方法

2、init初始化的方法

3、service业务方法

4、destroy销毁的方法

当我们第一次去访问具体的一个Servlet的时候,首先会调用构造方法生成这个Servlet的实例;

然后自动执行(调用)init初始化的方法,为相应的业务方法的执行做好相应的提前准备工作;

最后执行service业务方法,实现相应的业务功能;

如果我们再次进行请求访问,这时不会执行构造方法 和  初始化方法,仅仅只会执行业务方法;
这句话的意思就是:Servlet的创建 和 初始化在整个生命周期中,只有执行1次,而业务方法可以被多次的执行。

当我们的Servlet在内存中进行销毁的时候,首先会自动的调用(执行)destroy销毁的方法,可以进行相关的资源的回收。这个方法在整个生命周期中也只会执行1次。

注意!
Servlet会为每一个不同的请求,生成一个不同的Servlet对象,所以Servlet的设计是基于非单例的模式。

8.7、Servlet配置参数获取

在web.xml配置文件中,会存在Servlet的注册配置,在注册配置中我们也可以同时定义一些参数,这些参数的值可以通过Servlet生命周期中的init(ServletConfig config)方法中的config对象来进行获取,具体操作如下:

<servlet>
    <servlet-name>TestAction</servlet-name>
    <servlet-class>com.it.www.action.TestAction</servlet-class>
    <!-- 在Servlet中定义的配置参数 -->
    <init-param>
          <param-name>Num</param-name>
          <param-value>10</param-value>
    </init-param>
  </servlet>
  
  <servlet-mapping>
    <servlet-name>TestAction</servlet-name>
    <url-pattern>/testAction.action</url-pattern>
  </servlet-mapping>
@Override
    public void init(ServletConfig config) throws ServletException {
    	//config  对象可以在Servlet的配置文件中获取参数的值
    	String  value = config.getInitParameter("Num");
    	System.out.println(value); //10
    }

九、EL表达式

9.1、EL表达式简介

EL 全名为Expression Language。

EL表达式主要作用:

1、输出数据
   EL表达式主要用于替换JSP页面中的脚本表达式,以从各种类型的web作用域 中检索java对象、获取数据。(某个web域 中的对象,访问javabean的属性、访问list集合、访问map集合、访问数组)
   
   彻底的替换了<%= 表达式 %>的使用。

2、执行运算
    利用EL表达式可以在JSP页面中执行一些基本的关系运算、逻辑运算和算术运算,以在JSP页面中完成一些简单的逻辑运算。${1+2}
    
3、获取web开发常用对象
    EL 表达式定义了一些隐式对象,利用这些隐式对象,web开发人员可以很轻松获得对web常用对象的引用,从而获得这些对象中的数据。
    
4、调用Java方法
    EL表达式允许用户开发自定义EL函数,以在JSP页面中通过EL表达式调用Java类的方法。

9.2、EL语法格式

EL表达式使用的语法规范

${表达式}

9.3、EL的运用实例

EL表达式的运用:

 <%
            int    id = 1;
            String  name = "admin";
            
            //使用EL表达式必须首先要在相应的作用域对象中进行保存才可以。
            pageContext.setAttribute("id", id);
            pageContext.setAttribute("name",name);
            
            
            Dept   dept = new  Dept();
            dept.setDeptid(10);
            dept.setDeptname("行政部");
            dept.setDeptno(2);
            dept.setDeptcreatetime(new  Date());
            
            pageContext.setAttribute("dept",dept);
            
     %>
     
     <%=id %>
     <br/>
     <%=name %>
     
     <hr/>
     <!-- 使用作用域对象中的key来进行取值操作 -->
     ${id}-----${name}
     
     <hr/>
     <!-- dept.deptid  称为“对象导航模式”即OGNL  -->
     ${dept.deptid }------${dept.deptname } ----- ${dept.deptno}-------${dept.deptcreatetime.toLocaleString()   }------${dept.address }
     <!--  
       EL表达式工作的本质,比如: dept.deptid访问的不是dept对象中的deptid属性,而且通过反射机制反调的deptid属性的get方法来取值的。
     -->
         
EL表达式是可以直接进行各种运算的(不一一进行罗列)
     <hr/>
     ${1+1}
     .......

数组、集合数据的处理
<%
       String[]  names = {"admin","guest","tom"};
       pageContext.setAttribute("names",names);
       
       Map<String,Object>  map = new  HashMap<String,Object>();
       map.put("id",1);
       map.put("name","admin");
       map.put("age",30);
       
       pageContext.setAttribute("map", map);
       
       List<Dept>    deptList = new  ArrayList<Dept>();
       Dept  dept1= new  Dept();
       dept1.setDeptid(10);
       dept1.setDeptname("研发部");
       
       deptList.add(dept1);
       
       Dept  dept2= new  Dept();
       dept2.setDeptid(11);
       dept2.setDeptname("财务部");
       
       deptList.add(dept2);
       
       pageContext.setAttribute("deptList", deptList);
       
%>

${names }
<br/>
${names[0]}------${names[1]}

<hr/>
${map.id }-------${map.name }-----${map.age }

<hr/>
${deptList[0] }---------------------${deptList[1] }
<br/>
${deptList[0].deptid }-----------${deptList[0].deptname }
<br/>
${deptList[1].deptid }-----------${deptList[1].deptname }

如果能够提供循环的操作,以上的某些操作会比较方便。

十、JSTL标签库

10.1、JSTL标签库简介

JSP标准标签库(JSTL)是一个JSP标签集合,它封装了JSP应用的通用核心功能。

JSTL支持通用的、结构化的任务,比如迭代,条件判断,XML文档操作,国际化标签,SQL标签。 除了这些,它还提供了一个框架来使用集成JSTL的自定义标签。

根据JSTL标签所提供的功能,可以将其分为5个类别。

核心标签
格式化标签
SQL 标签
XML 标签
JSTL 函数

注意!
我们通常使用的是【核心标签】、【格式化标签】其余的基本不用。我们不会在JSTL中定义和操作SQL、XML等资源。

10.2、使用步骤流程

1、首先在web项目中添加jstl标签库的jar包;

2、通过指令元素<%@ taglib %>  进行引入;

<%@ taglib   prefix="c"  uri="http://java.sun.com/jsp/jstl/core" %>

3、在JSP页面中进行使用;

10.3、JSTL的运用

实例:

<%@page import="java.util.Date"%>
<%@page import="java.util.List"%>
<%@page import="com.it.www.po.Dept"%>
<%@page import="java.util.ArrayList"%>
<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<%@ taglib   prefix="c"  uri="http://java.sun.com/jsp/jstl/core" %>
<%@ taglib   prefix="fmt"  uri="http://java.sun.com/jsp/jstl/fmt" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Insert title here</title>
</head>
<body>
      
      <%
            boolean  is = true;
            pageContext.setAttribute("is",is);
            
            List<Dept>    deptList = new  ArrayList<Dept>();
            Dept  dept1= new  Dept();
            dept1.setDeptid(10);
            dept1.setDeptname("研发部");
            
            deptList.add(dept1);
            
            Dept  dept2= new  Dept();
            dept2.setDeptid(11);
            dept2.setDeptname("财务部");
            
            deptList.add(dept2);
            
            pageContext.setAttribute("deptList", deptList);
            
            
            Date   date = new  Date();
            
            pageContext.setAttribute("date", date);
            
      %>

      <c:if test="${is}">
            <h1>我是成立的!</h1>
      </c:if>
      
      <hr/>
      
      <c:choose>
               <c:when test="${is }">
                        <h1>我是成立的!</h1>
               </c:when>
               <c:otherwise>
                  <h1>我不成立!</h1>
               </c:otherwise>
      </c:choose>
      
      <c:forEach  begin="10"  end="20"  step="1"   var="v"  varStatus="vs">
                <h1>${vs.count}---${vs.index}----${v}</h1>
      </c:forEach>
      
      <hr/>
      
      <c:forEach  items="${deptList }"  var="dept"  varStatus="vs">
              <p>
               ${vs.index }------  ${vs.count }------     ${dept.deptid }-------${dept.deptname }
              </p>
      </c:forEach>
      
      <!-- 实现的重定向 -->
    <%--   <c:redirect  url="el02.jsp"></c:redirect> --%>
   
      <hr/>
      <fmt:formatDate value="${date }"   pattern="yyyy-MM-dd"/>    
</body>
</html>

总结:

在JSP中我们通常都是JSTL标签库配合EL表达式来进行使用。

十一、中文乱码解决

描述情况:

在我们进行新增或者是修改数据的时候,必然会存在大量的中文,在请求提交的过程中,会生成乱码形式。那么针对这种乱码的格式我们该如何进行处理呢?

基于Http协议下的请求方式常用的有2种:  POST请求   |   GET请求

所以我们的中文乱码问题的解决需要针对不同形式的请求方式来进行处理:

1、针对POST请求方式:我们可以直接通过request对象来设置编码格式(UTF-8);

2、针对GET请求方式:在Tomcat服务器的配置中进行设置:

   confi目录-----server.xml-----:
   
    <Connector URIEncoding="UTF-8" connectionTimeout="20000" port="9000"  
    protocol="HTTP/1.1" redirectPort="8443"/>
    
 说明:添加一个  URIEncoding="UTF-8"  属性设置;

十二、过滤器的使用

12.1、过滤器简介及功能

Filter也称之为过滤器,它是Servlet技术中最实用的技术,Web开发人员通过Filter技术,对web服务器管理的所有web资源:例如Jsp, Servlet, 静态图片文件或静态 html 文件等进行拦截,从而实现一些特殊的功能。例如实现URL级别的权限访问控制、过滤敏感词汇、压缩响应信息等一些高级功能。

它主要用于对用户请求进行预处理,也可以对HttpServletResponse进行后处理。使用Filter的完整流程:Filter对用户请求进行预处理,接着将请求交给Servlet进行处理并生成响应,最后Filter再对服务器响应进行后处理。

过滤器的功能:
在HttpServletRequest到达 Servlet 之前,拦截客户的HttpServletRequest 。根据需要检查HttpServletRequest,也可以修改HttpServletRequest 头和数据。
在HttpServletResponse到达客户端之前,拦截HttpServletResponse 。根据需要检查HttpServletResponse,也可以修改HttpServletResponse头和数据。

12.2、过滤器的创建和使用

项目中编写一个中文过滤器为例:

PageEncoding.java

package com.it.www.util;

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;

/**
 * 定义一个中文过滤器,必须要实现Filter的接口
 * 
 * @author Administrator
 */
public class PageEncoding implements Filter {
	
	private    String   encode; 

	public PageEncoding() {
		System.out.println("---PageEncoding----");
	}

	// 初始化方法
	@Override
	public void init(FilterConfig filterConfig) throws ServletException {
		System.out.println("-----init-----");
		//filterConfig 就是代表的Filter配置文件的信息
		this.encode = filterConfig.getInitParameter("encode");
	}

	// 执行过滤的核心方法
	@Override
	public void doFilter(ServletRequest request, ServletResponse response,
			FilterChain chain) throws IOException, ServletException {
		System.out.println("----doFilter-----");
		//针对请求拦截后的业务处理
		HttpServletRequest  hr =   (HttpServletRequest)request;
		hr.setCharacterEncoding(encode);
		//chain.doFilter()是将请求放行到下一个过滤器或者是请求目标地址;
		chain.doFilter(hr, response);
	}

	// 销毁方法
	@Override
	public void destroy() {
		System.out.println("----destroy----");
		this.encode = null;
	}
}

以上是我们定义的一个过滤器,定义的步骤总结如下:

1、自定义一个类;

2、实现Filter的接口;

3、内部会产生出几个有关生命周期的方法(构造、init 、doFilter 、 destory);

4、定义的过滤器是需要在web.xml中进行注册(配置)的;

web.xml中Filter过滤器的配置如下:

<!-- 配置过滤器 -->
  <filter>
       <filter-name>PageEncoding</filter-name>
       <filter-class>com.it.www.util.PageEncoding</filter-class>
  </filter>
  
  <filter-mapping>
       <filter-name>PageEncoding</filter-name>
       <url-pattern>/*</url-pattern>
  </filter-mapping>

注意!

/* : 表示的是拦截所有的请求格式,/ 符号不能少。

12.3、Filter生命周期

构造方法 : 创建出过滤器的对象;

public void init(FilterConfig filterConfig) throws ServletException;//初始化
和我们编写的Servlet程序一样,Filter的创建和销毁由WEB服务器负责。 web 应用程序启动时,web 服务器将创建Filter 的实例对象,并调用其init方法,读取web.xml配置,完成对象的初始化功能,从而为后续的用户请求作好拦截的准备工作(filter对象只会创建一次,init方法也只会执行一次)。开发人员通过init方法的参数,可获得代表当前filter配置信息的FilterConfig对象。

public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException;//拦截请求
这个方法完成实际的过滤操作。当客户请求访问与过滤器关联的URL的时候,Servlet过滤器将先执行doFilter方法。FilterChain参数用于访问后续过滤器。

public void destroy();//销毁
Filter对象创建后会驻留在内存,当web应用移除或服务器停止时才销毁。在Web容器卸载 Filter 对象之前被调用。该方法在Filter的生命周期中仅执行一次。在这个方法中,可以释放过滤器使用的资源。

1646364681583

十三、文件的上传

13.1、文件上传的基本介绍

在Java项目的开发中,我们不是直接使用IO流来进行文件的上传和下载的处理,而是使用第三方的开源组件来实现的。
SmartUpload
FileUpload
.........
内部封装的都是原始的IO流的技术。

所以:我们在进行文件的上传和下载的时候,需要下载一个FileUpload的第三方jar包。

13.2、文件上传对前端页面的要求

1)提交方式必须为POST(因为传输的数据量不受限制)。

2)表单中的类型必须为enctype=”multipart/form-data”(表单是以二进制流的形式来进行提交的)。

3)表单中存在文件表单项<input type=”file” name=”f”/>(复杂的上传文件)
 <form action="uploadAction.action"  method="post"  enctype="multipart/form-data">
       
            <table  width="650px"  align="center"  border="1px">
                 <tr>
                      <td>UserName:</td>
                      <td><input type="text"  name="uname"/></td>
                 </tr>
                 <tr>
                      <td>照片:</td>
                      <td><input type="file"  name="pic"/></td>
                 </tr>              
                 <tr>
                    <td  colspan="2"  align="center">
                        <input type="submit" value="上传"/>
                    </td>
                 </tr>                
            </table>      
 </form>

13.3、上传文件Servlet服务器端实现

实现上传操作步骤:

1)创建磁盘文件工厂 

DiskFileItemFactory factory = new DiskFileItemFactory();

2)创建解析器

ServletFileUpload    sevletFileUpload = new ServletFileUpload(factory);			
3)使用解析器解析request,读取上传信息

List<FileItem>  fileItems = sevletFileUpload.parseRequest(request);
			
4)调用FileItem对象中的write方法保存信息到指定文件中。

fileItem.write(File  destFile); 

UploadAction.java实现

package com.it.www.action;

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

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

import org.apache.commons.fileupload.FileItem;
import org.apache.commons.fileupload.disk.DiskFileItemFactory;
import org.apache.commons.fileupload.servlet.ServletFileUpload;
import org.omg.CORBA.FieldNameHelper;

public class UploadAction extends HttpServlet {


	protected void doPost(HttpServletRequest request,
			HttpServletResponse response) throws ServletException, IOException {
		request.setCharacterEncoding("UTF-8");
		
		/**
		 * 如果还是按照原来的方式来获取数据
		 */
	/*	String   name = request.getParameter("uname");
		String   file = request.getParameter("pic");
		System.out.println(name);
		System.out.println(file);*/
		
		/**
		 * 寻求原因  : 基于multipart/form-data格式的输出的传递,内部确实是不同的处理。
		 */
		//ServletInputStream   inputStream  = request.getInputStream();
		//基于inputStream的流来读取相关的信息进行查看(观察底层)-------在浏览器中查看请求信息;
        //或者通过打印来进行观察
         //String requestInfo = IOUtils.toString(inputStream);
		 //System.out.println(requestInfo);
				
		//使用FileUpload组件来解析request请求。
				
		try {
			//1、创建一个磁盘文件工厂
			DiskFileItemFactory   factory = new DiskFileItemFactory();
			
			//2、使用工厂来构建Servlet文件的上传器
			ServletFileUpload    servletFileUpload = new ServletFileUpload(factory);
			
			//3、使用文件上传器来解析request请求
			List<FileItem>     fileList = servletFileUpload.parseRequest(request);
			//fileList  就是页面中提交的2个信息
			for(FileItem  file : fileList){
				
				if(file.isFormField()){
					  //普通的表单元素
					String  fieldame = file.getFieldName();
					String  fieldValue = file.getString();
					System.out.println(fieldame+"------"+fieldValue);
				}else{
					//复杂的文件表单元素------将文件file上传到Tomcat服务器中					
					
					//获取服务器的根目录+创建一个新的文件夹(保存上传的图片)
				    //String  path1 = 	request.getServletPath();  
					//path1是获取的请求路径  /uploadAction.action
				    
				    //path2是项目在服务器上的真实路径 D:\apache-tomcat-7.0.75\apache-tomcat-7.0.75\webapps\servlet_upload
				    String  path = request.getServletContext().getRealPath("/uploads/");
				    
				    File  proPath = new File(path);
				    
				    if(!proPath.exists()){
				    	proPath.mkdir();
				    }
				    //获取的就是上传的文件的全部的名称(名字+后缀)
				    String  fileName =  file.getName();
				    //"x.xxxx.jpg"
				    //".jpg"
				    int  index = fileName.lastIndexOf(".");
				    String   type = fileName.substring(index);
				    String  newFileName = UUID.randomUUID().toString()+type;
				    File  destFile  = new  File(proPath,newFileName);			    
					
                    //将本地的文件的名称(中文)-----自己加工出一个新的文件的名称(字母+数字混合而成)
			        file.write(destFile);
					
			        System.out.println("文件上传成功!");
				}
			}
			
		} catch (Exception e) {
			 e.printStackTrace();
		}
	}
}

13.4、下载文件实现

FileUpload下载文件实现步骤

设置响应头
String contentType = this.getServletContext().getMimeType(fileName);
String contentDisposition = "attachment;filename="+fileName;

response.setHeader("Content-Type", contentType);
response.setHeader("Content-Disposition", contentDisposition);

2、给服务器端下载的文件绑定流			
FileInputStream  inputStream = new FileInputStream(path);

3、获得一个响应给客户端的流
OutputStream  out = response.getOutputStream();

4、把输入流中的数据写入到输出流中
IOUtils.copy(inputStream, out);  

5、关闭流资源
inputStream.close();

DownLoadAction.java实现

package com.it.www.action;

import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;

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

import org.apache.commons.io.IOUtils;

/**
 * Servlet implementation class DownLoadAction
 */
public class DownLoadAction extends HttpServlet {

	@Override
	public  void service(HttpServletRequest request, HttpServletResponse response)
			throws ServletException, IOException {
		String      fileName = "8772874.jpg";   // abc.jpg;
		//通过文件的名称,获取其MIME类型。因为名称是带了后缀的。
		String contentType = this.getServletContext().getMimeType(fileName);
		String  contentDisposition = "attachment;filename="+fileName;
		//设置响应头(2个)。
		response.setHeader("Content-Type", contentType);
		response.setHeader("Content-Disposition", contentDisposition);
		//获得服务器端的指定的下载的文件。
		String  realPath = this.getServletContext().getRealPath("/uploads/"+fileName);
		//创建为文件对象。
		File  file = new File(realPath);
	    if(file.exists()){
	    	//使用inputStream的流,绑定了需要下载的文件。
			InputStream  in = new FileInputStream(realPath);
			//获得客户端的OutputStream流,向客户端写出信息。
			OutputStream   out = response.getOutputStream();
			//使用IOUtils中的copy方法,完成流的对接。
			IOUtils.copy(in, out);
			//关闭流资源。
			in.close();
	    }
	}
}

十四、Cookie的使用

Cookie是一个客户端记录数据的机制。

1620808804016

保存数据:
//针对数据进行编码处理--URLEncoder
username = URLEncoder.encode(username,"utf-8");
//使用数据构建Cookie
Cookie cookie = new Cookie("username",username);
//设置路径,这个路径即该工程下都可以访问该cookie 如果不设置路径,那么只有设置该cookie路径及其子路径可以访问
cookie.setPath("/");
//设置cookie生命周期
cookie.setMaxAge(60*60);
//添加Cookie
response.addCookie(cookie);
取数据:
	  String username = "";
      Cookie[] cookies = request.getCookies();
      //注意!
      //在提取指定的Cookie的时候,需要通过名称来进行确定
      if(cookies!=null && cookies.length!=0){
         	for(int i=0;i<cookies.length;i++){
         		System.out.println(cookies[i].getName());
         		if(cookies[i].getName().equals("username")){
         			username = cookies[i].getValue();
                    //解码的处理---URLDecoder
         			username = URLDecoder.decode(username,"utf-8");
         		}
         	}
     }

十五、数据源和连接池

在目前我们操作数据库的过程中,频繁的建立连接 和 关闭连接(性能损耗较为严重),因此推出了一些连接池的技术来解决性能的瓶颈问题。

JNDI ( JNDI是 Java 命名与文件夹接口(Java Naming and Directory Interface)),在J2EE中是比较重要的规范。

在Tomcat中配置数据源的步骤:

1、Tomcat中加入数据库驱动文件

2、配置Tomcat的conf/context.xml
    <Resource name="jdbc/news" 
              auth="Container"  type="javax.sql.DataSource"  maxActive="100" 
              maxIdle="30" maxWait="10000" username="root"  password="bdqn" 
              driverClassName="com.mysql.jdbc.Driver" 
              url="jdbc:mysql://127.0.0.1:3306/msg"/>


3、在java应用程序中使用JNDI获取连接对象
  //初始化上下文
  Context cxt=new InitialContext();
  //获取与逻辑名相关联的数据源对象
  DataSource ds=(DataSource)cxt.lookup("java:comp/env/jdbc/news");    
  //使用JNDI方式获取连接的固化方式  java:comp/env/逻辑名称
  Connection  conn=ds.getConnection();

提示!

JNDI数据源的创建,必须要在Web容器的环境中方可实现;

DBCP(第三方的连接池技术)

导入dbcp-jar包

编写dbcp.properties配置文件

# dbcp实现连接池的相关参数设置
driverClassName=com.mysql.jdbc.Driver
url=jdbc:mysql://localhost:3306/msg
username=root
password=root
 
#\u521D\u59CB\u5316\u8FDE\u63A5
initialSize=10
#\u6700\u5927\u8FDE\u63A5\u6570\u91CF
maxActive=50
#\u6700\u5927\u7A7A\u95F2\u8FDE\u63A5 
maxIdle=20
#\u6700\u5C0F\u7A7A\u95F2\u8FDE\u63A5
minIdle=5

Java程序编码实现

InputStream  inputStream = this.getClass().getClassLoader().getResourceAsStream("dbcp.properties");
Properties  ps = new Properties();
ps.load(inputStream);
DataSource  ds = BasicDataSourceFactory.createDataSource(ps);
System.out.println(ds.getConnection());

提示!

更多的信息可以参考dbcp详解.doc文档;

十六、Servlet监听器

16.1、监听器简介

Servlet监听器:就是web应用程序中的一种事件监听模型。当我们的操作事件发生的时候,监听器就可以被接收到,从而可以进行相应的处理。在Servlet中共有8个监听器,分别是针对ServletContext 、 HttpSession 、 ServletRequest来进行设计的。

1648540812465

16.2、实现在线人数的统计 HttpSessionListener

源码见项目案例中...

posted @ 2024-04-19 14:58  疏影橫斜水清淺  阅读(29)  评论(0编辑  收藏  举报