Servlet学习

1.servlet规范

​ 遵循Servlet规范的webapp可以放在不同的web服务器中运行;

2.创建servlet项目

  1. 创建项目目录,目录名称自定义不能是中文,例如crm;

  2. 在项目目录下创建WEB-INF文件夹,必须是这个名字;

  3. WEB-INF文件下新建classes文件夹,必须是这个名字,这个目录下存放的一定是java程序编译之后的class文件(字节码文件);

  4. 在WEB-INF目录下新建目录lib,这个目录不是必须的,但是如果一个webapp需要第三方jar包的话,这个jar包要放到lib目录下,名字必须是全部小写的lib,例如java语言连接数据库的驱动jar包,那么这个Jar包就一定要放到lib目录;

  5. 在WEB-INF目录下新建web.xml文件,这个文件是必须的,名字必须是web,xml,一个合法的webapp,web.xml文件是必须的,这个web.xml文件是一个配置文件,这个文件中描述了请求路径与Servlet类之间的对应关系,这个文件最好从其他webapp中拷贝,没必要手写,可以从tomcat内置的项目中拷贝,删除内容,只保留标签内容;

  6. 编写java程序HelloWorld.java,实现Servlet接口;

    这个Servlet接口不在jdk中,因为Servlet不属于JAVASE,属于JAVAEE,是另外的一套类库,是Oracle提供的,最早是sun公司提供的,Servlet是JAVAEE规范中的一员,tomcat实现了Servlet规范,所以tomcat也需要使用Servlet接口,在CATALINA_HOME/lib/servlet-api.jar中,从JAVAEE9开始Servlet的包名是jakarta.servlet.Servlet;

  7. 编译java程序HelloWorld.java,需要将servlet-api.jar配置到环境变量的classpath中才能编译;

  8. 将HelloWorld.class放入到项目文件夹下的WEB-INF的classes文件夹下;

  9. 在web.xml文件中编写配置信息,让请求路径和Servlet类名关联,这一步用专业术语描述叫做在web.xml文件中注册Servlet类;

    <?xml version="1.0" encoding="UTF-8"?>
    <web-app xmlns="https://jakarta.ee/xml/ns/jakartaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="https://jakarta.ee/xml/ns/jakartaee
                          https://jakarta.ee/xml/ns/jakartaee/web-app_5_0.xsd" version="5.0" metadata-complete="true">
    
        <!-- Servlet描述信息 -->
        <!-- 任何一个servlet都对应一个Servlet-Mapping -->
        <servlet>
            <servlet-name>hellourl</servlet-name>
            <!-- 带有包名的全类名 -->
            <servlet-class>com.ServletTest1.Servlet.ServletTest1</servlet-class>
        </servlet>
        <!-- Servlet映射信息 -->
        <servlet-mapping>
            <!-- 路由命名,与上面的servlet-name相同 -->
            <servlet-name>hellourl</servlet-name>
            <!-- 这里写路径,必须以/开头-->
            <url-pattern>/hello</url-pattern>
        </servlet-mapping>
    </web-app>
    

10.重启tomcat,在浏览器上输入:localhost:8080/项目名/url-pattern;

​ 此时可以将catalina_home下面的conf/logging.properties 修改为

​ java.util.logging.ConsoleHandler.encoding = GBK,即可显示中文提示

注意点:html文件只能放在WEB-INF文件夹外面;

整个url请求过程:浏览器通过/hello地址发出请求,tomcat根据/hello找到com.ServletTest1.Servlet.ServletTest1类,再通过反射机制调用类的service方法;

3.JAVAEE版本

​ javaee版本目前最高是javaee8,javaee被Oracle捐献给力Apache,Apache把JAVAEE更名为jakartaEE,以后都叫做jakataEE,javaee8升级之后的版本叫做jakartaee9,jakartaee10…

​ javaee8的Servlet的全类名是javax.servlet.Servlet

​ javaee9的Servlet的全类名是jakarta.servlet.Servlet

​ 如果之前的项目使用的是javax.servlet.Servlet,那么该项目无法之间部署到tomcat10+的版本上,只能部署到tomcat9-的版本上,在tomcat9以及tomcat9以下的版本能够识别javax.servlet这个包;

4.向浏览器输出一段html代码

	@Override
    public void service(ServletRequest request, ServletResponse response) throws ServletException, IOException {
        // TODO Auto-generated method stub
        response.setContentType("text/html;charset=UTF-8");
        PrintWriter pri = response.getWriter();
        pri.print("<h1>hello servlet,你好啊</h1>");
    }

5.IDEA创建普通java项目

​ 1.创建空项目,再在空项目下创建普通java模块,将普通java模块转换成webapp框架支持的项目,右键模块目录添加框架支持,勾选JAVAEE下面的web应用程序,点击完成,删除jsp文件;

​ 2.点击文件-项目结果-导入jar包或者库;

​ 3.编写类实现Servlet接口,实现Servlet接口的方法;

​ 4.在service中编写业务代码;

​ 5.idea关联tomcat,

​ ①IDEA右上角绿色小锤子,选择tomcat本地,然后添加应用服务器配置(本地tomcat文件路径)、jre配置(本地JDK路径),

​ ②部署,加号,工件,应用程序上下文填写项目名称

​ 6.点击右上角绿色虫子,debug模式启动程序,开发中建议用debug模式启动;

6.Servlet对象的生命周期

​ Servlet对象的创建、对象方法的调用、对象的销毁javaweb程序员是无权干预的,Servlet对象的生命周期是由Tomcat服务器(web server)全权负责的;Tomcat服务器通常外面称之为WEB容器(WEB Container),WEB容器在管理Servlet的生命周期;

​ 我们自己创建的Servlet对象是不受WEB容器管理的,WEB容器创建的Servlet对象会被放到一个集合当中(HashMap),只要在HashMap中的Servlet才能被WEB容器管理;

​ WEB容器底层有一个HashMap这样的集合,在这个集合中存储Servlet对象和请求路径之间的关系;

​ 默认情况下。服务器在启动的时候不会实例化Servlet对象;这个设计是合理的,在用户没有发送请求之前,如果提前创建所有Servlet对象,必然是非常耗费内存的,如果这个Servlet对象一直,没有用户访问的话,也是浪费资源的;

如何让服务器在启动的时候创建Servlet对象?

​ 在Servlet子标签中添加3,该子标签中的数字越小,优先级越高;

<servlet>
        <servlet-name>aservlet</servlet-name>
        <servlet-class>com.ywxk.ServletTest.AServlet</servlet-class>
        <load-on-startup>3</load-on-startup>
    </servlet>

​ 用户在发送第一次请求的时候Servlet对象被实例化,Servlet对象被创建出来后,容器马上调用Servlet对象的init方法,后续继续发送请求的时候,容器只会调用service方法,说明Servlet对象是单例的,但是Servlet类并不符合单例模式,我们称之为假单例,真单例模式构造方法是私有化的,而Servlet的构造方法我们是可以自由编写的,在服务器关闭的时候,服务器在销毁Servlet对象的内存之前会自动调用Servlet对象的destroy方法,destroy方法在执行的时候Servlet对象还没有被销毁,destroy方法执行完后Servlet对象的内存才会被服务器释放;

通常在init方法中做初始化操作,例如初始化数据库连接池,初始化线程池…

通常在destroy方法中做一些资源的关闭

Servlet类必须有无参构造,否则会导致Servlet对象无法实例化,所以官方不建议程序员去编写Servlet的构造方法,如果有必需在构造方法中处理的业务,可以写在init方法中;

7.适配器设计模式

通常不能将手机直接放到220v电压上充电,需要一个适配器,手机通过适配器充电;

同理并不是所有的Servlet对象都会用到Servlet接口的所有方法,如果每个Servlet类都去实现Servlet接口的5个方法,那么代码会臃肿、不够优雅,此时我们可以创建一个通用类去实现Servlet接口,然后其他的Servlet类都去继承这个通用类,这样就可以避免写一些不必要的方法,简化代码,我们称这个设计模式为适配器设计模式

/**
 * 编写一个标准通用的Servlet类
 * 以后所有的Servlet类都不要直接实现Serlet接口
 * 都继承Servlet通用类GenericServlet
 * GenericServlet就是一个适配器
 */
public abstract class GenericServlet implements Servlet {
    @Override
    public void init(ServletConfig servletConfig) throws ServletException {

    }

    @Override
    public ServletConfig getServletConfig() {
        return null;
    }

    /**
     * Servlet子类必须实现service方法
     * @param servletRequest
     * @param servletResponse
     * @throws ServletException
     * @throws IOException
     */
    @Override
    public abstract void service(ServletRequest servletRequest, ServletResponse servletResponse) throws ServletException, IOException;

    @Override
    public String getServletInfo() {
        return null;
    }

    @Override
    public void destroy() {

    }
}

8.GenericServlet

jakarta.servlet.GenericServlet;

实现了Servlet接口,并对init方法做了重写,扩展了一些其他方法;我们只需要继承该方法即可通过getServletConfig()方法获得servletConfig实例;

9.ServletConfig

jakarta.servlet.ServletConfig;

ServletConfig是一个接口,tomcat的org.apache.catalina.core.StandardWrapperFacade类实现了ServletConfig接口,jetty服务器的实现类可能不一样,但是他们实现的都是ServletConfig接口;

不同的Servlet类通过getServletConfig()方法获取的ServletConfig对象是不一样的,每一个Servlet对象都有一个对应的ServletConfig对象;

ServletConfig对象是tomcat服务器(web服务器)创建的,服务器在创建完servlet对象之后立马又创建了ServletConfig对象,然后通过servlet的init方法传递了ServletConfig对象;

源码大概是这样的:

//通过反射获取Servlet类
Class cls=Class.forName("com.xxx.xxxServlet");
//创建Servlet对象
Object obj=cls.newInstance();
//向上转型成Servlet对象
Servlet servlet=(Servlet)obj;
//创建ServletConfig对象
ServletConfig servletConfig=new org.apache.catalina.core.StandardWrapperFacade();
//将ServletConfig对象传递给我们的servlet对象
servlet.init(servletConfig);

从ServletConfig名字也可以很明显的看出ServletConfig是一个配置信息类,每个Servlet对象应该要对应一个自己的配置信息,所以每个Servlet对象都会对应一个ServletConfig对象是合理的;

ServletConfig对象中配置的信息就是:

​ web.xml文件中标签的信息;

获取初始化参数:

<servlet>
        <servlet-name>loginServlet</servlet-name>
        <servlet-class>com.ywxk.ServletTest.LoginServlet</servlet-class>
        <init-param>
            <param-name>driver</param-name>
            <param-value>org.mysql.jdbc.Driver</param-value>
        </init-param>
        <init-param>
            <param-name>url</param-name>
            <param-value>jdbc:mysql://localhost:8080/Servlet02/register</param-value>
        </init-param>
        <init-param>
            <param-name>username</param-name>
            <param-value>root</param-value>
        </init-param>
</servlet>
            
            
public void service(ServletRequest servletRequest, ServletResponse servletResponse) throws ServletException, IOException {
        servletResponse.setContentType("text/html;charset=UTF8");
        ServletConfig servletConfig = getServletConfig();

        PrintWriter pw= servletResponse.getWriter();

        Enumeration<String> paramNames=getInitParameterNames();
        while (paramNames.hasMoreElements()){
           String paramName= paramNames.nextElement();
           pw.println(paramName+"---"+getInitParameter(paramName));
        }
    }

10.ServletContext

ServletContext是接口

是Servlet的环境接口(Servlet对象的上下文对象)

由web服务器实现

ServletContext对象是由web服务器在启动的时候创建的

对于一个webapp来说,ServletContext对象只有一个,ServletContext对象在服务器关闭的时候销毁,

ServletContext对象其实对应的就是整个web.xml文件

放在ServletContext对象当中的数据是所有Servlet共享的;

tomcat的实现类:org.apache.catalina.core.ApplicationContextFacade;

获取ServletContext的初始化参数

<!-- 上下文的初始化参数 -->
    <context-param>
        <param-name>projectName</param-name>
        <param-value>webapp-ywxk</param-value>
    </context-param>
    <context-param>
        <param-name>projectDate</param-name>
        <param-value>2022-05-04</param-value>
    </context-param>
    
    public void test2(ServletResponse servletResponse) throws IOException {
        PrintWriter pw=servletResponse.getWriter();
        ServletContext sc= getServletContext();
        Enumeration<String> names=sc.getInitParameterNames();
        while (names.hasMoreElements()){
            String name=names.nextElement();
            pw.println(name+"<hr/>");
            pw.println(sc.getInitParameter(name));
        }
    }

ServletContext相关方法:

		ServletContext sc= getServletContext();
        //获取项目文件名
        pw.println(sc.getContextPath());
        //获取项目文件的绝对地址
        pw.println(sc.getRealPath(sc.getContextPath()));
        //动态获取文件的绝对路径
        pw.println(sc.getRealPath("/index.html"));
        //记录日志,日志的目录CATALINA_HOME/logs,IDEA的tomcat记录在IDEA的CATALINA_BASE里面
        sc.log("记录了日志");
        sc.log("记录异常",new RuntimeException("异常日志"));

ServletContext又叫做应用域,如果所有的用户共享一份数据,并且这个数据很少别被修改,并且这个数据量很少,可以将这些数据放到ServletContext这个应用域当中,当作一个长时间的缓存,可以提高工作效率;

		PrintWriter pw=servletResponse.getWriter();
        ServletContext sc= getServletContext();
        //添加、修改
        sc.setAttribute("name","James");
        //获取,在其他的servlet中也能获取
        pw.println(sc.getAttribute("name"));
        //删除
        sc.removeAttribute("name");

实际开发中,我们编写Servlet类的时候,不会去直接继承GenericServlet类,因为我们开发的是B/S架构的web应用,在Servlet规范当中,提供了一个HttpServlet类,它是专门为HTTP协议准备的一个Servlet类,HttpServlet继承了GenericServlet类,我们编写的Servlet类只要去继承HttpServlet类就可以了,使用HttpServlet类处理Http协议更方便;

Servlet类关系:

​ jakarta.servlet.Servlet(接口)

​ jakarta.servlet.GenericServlet(抽象类) implements Servlet (GenericServlet实现了Servlet )

​ jakarta.servlet.HttpServlet(抽象类) extends GenericServlet(HttpServlet继承了GenericServlet )

​ 我们编写的类继承HttpServlet;

11.模板方法设计模式

在模板类的模板方法中定义核心算法骨架,具体的实现步骤可以延迟到子类当中完成;

模板类通常是一个抽象类,模板类当中的模板方法定义核心算法,这个方法通常是final的,但也可以不是;

模板类当中的抽象方法是不确定实现的方法,这个不确定怎么实现的方法交给子类完成;

/**
 * MakeMedia就是模板设计方法设计模式的模板
 * make就是模板设计方法设计模式当中的模板方法
 * 模板类通常是抽象类
 */
public abstract class MakeMedia {
    //模板方法
    //添加了final之后,这个方法无法被覆盖,核心算法也可以得到保护,算法也得到了重复使用
    //代码也得到了复用,因为算法中某些步骤是固定的,这些固定的代码不会随着子类的变化而变化,这一部分代码可以写到模板类当中
    //模板方法定义核心的算法骨架,具体的实现步骤可以延迟到子类当中去实现
    public final void make(){
        xiejuben();
        qingyanyuan();
        paishechangdi();
        shangyin();
    }

    public void xiejuben(){
        System.out.println("写剧本");
    }

    public void qingyanyuan(){
        System.out.println("请演员");
    }

    public void paishechangdi(){
        System.out.println("挑选拍摄场地");
    }

    //某些方法可以放到子类去具体实现
    public abstract void shangyin();
}

12.HttpServlet

jakarta.servlet.http.HttpServlet

HttpServlet类是专门为HTTP协议准备的,比GenericServlet更适合HTTP协议下的开发

http包下的类和接口

  • jakarta.servlet.http.HttpServlet(Http协议专用的Servlet类,抽象类)
  • jakarta.servlet.http.HttpServletRequest(Http协议专用的请求对象)
  • jakarta.servlet.http.HttpServletResponse(Http协议专用的响应对象)

HttpServletRequest,简称request对象,其中封装了请求协议的全部内容,tomcat服务器(web服务器)将请求协议中的数据全部解析出来,然后将这些数据全部封装到request对象当中了,通过 HttpServletRequest对象我们可以获取请求协议的全部信息

HttpServletResponse对象同理

子类继承HttPServle抽象类后,能享受405错误,如果没有重写doGet方法,而前端用了get请求,那么会调用HttpServlet的doGet方法,页面报405方法不允许错误,后端要求用get请求,就必须重写doGet方法,其他方法同理

Servlet对象不能由程序员自己创建,自己new的Servlet对象生命周期不受tomcat服务器的管理,

13.设置欢迎页

在web文件夹下新建xxx.html,然后在web.xml新建标签

<!--配置欢迎页面-->
    <welcome-file-list>
        <welcome-file>welcome.html</welcome-file>
        <welcome-file>page1/page2/test.html</welcome-file>
    </welcome-file-list>

设置欢迎页的时候不需要添加/,并且这个路径默认是从webapp的根(web文件夹)下开始查找

一个webapp可以设置多个欢迎页面,越靠上的优先级越高,找不到再往下面找

实际上欢迎页有两个地方可以配置

  • 一个是在webapp的web.xml文件中,在这个地方配置属于局部配置

  • 一个是在CATALINA_HOME/conf/web.xml文件中进行配置,在这个地方配置属于全局配置

    <welcome-file-list>
            <welcome-file>index.html</welcome-file>
            <welcome-file>index.htm</welcome-file>
            <welcome-file>index.jsp</welcome-file>
    </welcome-file-list>
    

局部配置优先

欢迎页也可以是一个Servlet类

<welcome-file-list>
        <welcome-file>welcome/page/hello</welcome-file>
</welcome-file-list>

<servlet>
        <servlet-name>welcomePage</servlet-name>
        <servlet-class>com.ywxk.ServletTest.welcome.WelcomePage</servlet-class>
    </servlet>
    <servlet-mapping>
        <servlet-name>welcomePage</servlet-name>
        <url-pattern>/welcome/page/hello</url-pattern>
    </servlet-mapping>
public class WelcomePage extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        resp.setContentType("text/html;charset=utf8");
        PrintWriter pw= resp.getWriter();
        pw.println("<h2>动态欢迎页</h2>");
    }
}

14.关于WEB-INFO目录

放在WEB-INFO目录下的资源是受保护的,在浏览器上是不能通过路径直接访问,蓑衣像html、js、css等资源文件一定要放在WEB-INFO目录外面;

15.HttpServletRequest接口详解

jakarta.servlet.http.HttpServletRequest,是一个接口

用户在发送请求的时候,遵循了HTTP协议,tomcat服务器将http协议中的信息以及数据全部解析出来,然后tomcat服务器把这些信息封装到HttpServletRequest对象当中,]我们调用HttpServletRequest对象就可以获取请求信息了

request对象和response对象只在当前请求中有效,一个request请求对应一个request对象

Map<String,String[]> getParameterMap()获取map
Enumeration<String>	getParameterNames() 获取map集合的所有key
String[] getParameterValues(String name) 根据key获取map集合的value
String getParameter(String name) 根据key获取map集合当中的value,再获取value数组的第一个元素,最常用
    
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
    resp.setContentType("text/html;charset=utf8");
    PrintWriter pw= resp.getWriter();
    pw.println("POST方法");
    Map<String,String[]> maps= req.getParameterMap();
    Set<String> keys= maps.keySet();
    Iterator<String> it= keys.iterator();
    while (it.hasNext()){
        String key= it.next();
        pw.println(key);
        String[] values=maps.get(key);
        for (String val:values) {
            pw.println(val);
        }
        pw.println();
    }

    pw.println("<hr />");

    Enumeration<String> parameterNames =req.getParameterNames();
    while (parameterNames.hasMoreElements()){
        String key2=parameterNames.nextElement();
        pw.println(key2+"=");
        String[] values2= req.getParameterValues(key2);
        for (String v: values2) {
            pw.println(v);
        }
    }

    pw.println("<hr />");
    //获取一维数组中的第一个元素
    pw.println(req.getParameter("username"));
    pw.println(req.getParameter("password"));
    String[] values3=req.getParameterValues("hobby");
    for (String val: values3) {
        pw.println(val);
    }
}

request对象实际上又称为请求域对象

一个请求对象request对应一个请求域对象,一次请求结束之后,这个请求域就销毁了

两个servlet共享数据

  • 将数据放到servletContext应用域当中当然是可以的,但是应用域范围太大,占用资源较多,不建议使用
  • 可以将数据放到request域当中,然后Aservlet转发到Bservlet当中,保证Aservlet与Bservlet在同一次请求当中,这样就可以做到两个Servlet或者多个Servlet共享一份数据

请求转发器

		//请求转发机制,执行了AServlet之后跳转到BServlet
        //第一步,获取请求转发器对象,相当于把/burl这个路径包装到请求转发器当中,实际上是吧下一个跳转的路径告诉给tomcat服务器
        RequestDispatcher reqDis=req.getRequestDispatcher("/burl");
        //第二部,调用请求转发器,将req与resp传给下一个资源
        reqDis.forward(req,resp);
        //合并代码
        //req.getRequestDispatcher("/burl").forward(req,resp);

转发的下一个资源可以不是Servlet,只要是Tomcat服务器当中的合法资源都是可以转发的,例如:html…,转发的路径以/开头,不加项目名

req.getRequestDispatcher("/welcome.html").forward(req,resp);

request的其他方法

		//获取客户端的ip地址
        String addrIp= req.getRemoteAddr();
        //设置请求体的字符集(处理的是post请求的乱码问题,不能解决get请求的乱码问题)
		//tomcat10之后,request请求体当中的字符默认是UTF8,不需要设置字符集,不会出现乱码问题
        //tomcat9之前包括9的版本,如果请求体提交的是中文,后端获取之后会出现乱码,需要设置字符集
        req.setCharacterEncoding("UTF-8");
		//获取应用的根路径
		req.getContextPath()
        //获取请求方式
        req.getMethod();
		//获取请求的URI
        String uri=req.getRequestURI();
        //获取servlet路径
        String servletPath=req.getServletPath();

16.转发与重定向

转发:

req.getRequestDispatcher("/burl").forward(req,resp);

由于调用forward方法的时候,都会将request对象与response对象传递给下一个Servlet,所以不管转发了多少次,都是在同一个request对象中进行的,所以转发是一次请求;

转发之后浏览器地址上的请求地址没变

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-kN1N94eE-1651927172325)(C:\Users\Administrator\Desktop\临时\1651903152(1)].png)

重定向:

		//重定向的路径要以项目名开始
        //response对象将路径响应给了浏览器,浏览器自发的向服务器发送了一次请求
        //浏览器一共发生了两次请求,浏览器上最终显示的是最后一次发送请求的地址,所以重定向会导致地址栏上的地址发生改变
        resp.sendRedirect(req.getContextPath()+"/burl");

重定向一次,浏览器发送了2次请求;

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-mhQgCX0E-1651927172326)(C:\Users\Administrator\Desktop\临时\1651903215(1)].png)

转发和重定向的本质区别:

​ 转发是由web服务器来控制的,A资源跳转到B资源,这个跳转动作是tomcat服务器内部完成的;

​ 重定向是由浏览器完成的,具体跳转哪个地址由浏览器决定;

实际开发中如果在上一个Servlet当中向request域当中绑定了数据,希望在下一个Servlet中取出数据,使用转发机制,其余情况均使用重定向,转发会存在浏览器刷新提交问题;

17.javabean

javabean,咖啡豆,咖啡由咖啡豆研磨而成,寓意是java程序是由一个又一个的javabean组成;

一个javabean一般是有规范的:

  • 无参构造方法
  • 属性私有化
  • 对外提供setter和getter方法
  • 重写toString、hashCode、equals
  • 实现java.io.Serializable接口

javabean其实就是java的实体类,负责数据的封装,通常我们把这样的类叫做javabean

18.Servlet注解式开发

servlet3.0版本之后,推出了各种Servlet基于注解式开发

其优点是:直接在java类上使用注解进行标注,不需要编写大量的配置信息,开发效率高,缩小了web.xml的体积

@WebServlet,在Servlet类上使用

属性:

name:相当于<servlet-name>
urlPatterns:指定该Servlet的多个映射路径,一个Servlet可以有多个映射路径,等同于<url-pattern>
value:作用与urlPatterns一样,都是指定多个映射路径
loadOnStartup:服务器启动时是否创建servlet对象,等同于<load-on-startup>
initParams:等同于<init-param>
@WebServlet(name="helloServlet的注解",loadOnStartup = 1,urlPatterns = {"/helloUrl1","/helloUrl2","/helloUrl3"},initParams = {@WebInitParam(name="user",value = "root"),@WebInitParam(name = "password",value = "root")})
public class HelloServlet extends HttpServlet {

    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        //super.doGet(req, resp);
        resp.setContentType("text/html;charset=utf8");
        PrintWriter pw=resp.getWriter();
        String name=getServletName();
        pw.println(name);
        pw.println(req.getServletPath());
        pw.println("<hr />");
        Enumeration<String> parameterNames=getInitParameterNames();
        while (parameterNames.hasMoreElements()){
            String pName=parameterNames.nextElement();
            pw.println(pName);
            pw.println(getInitParameter(pName));
        }
    }
}
@WebServlet("/worldUrl")
public class WorldServlet extends HttpServlet {

    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        //super.doGet(req, resp);
        PrintWriter pw=resp.getWriter();
        pw.println("worldUrlworldUrlworldUrl");
    }
}

19.使用模板设计方法解决类爆炸问题

之前的开发都是一个请求对应一个Servlet类,1000个请求对应1000个Servlet类,会导致类爆炸

解决方案1:

  • 一个请求对应一个方法,一个业务对应一个Servlet类
@WebServlet({"/movieList","/movieDetail","/movieDel","/movieAdd","/movieUpdate"})
public class MovieAction extends HttpServlet {

    @Override
    protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        //super.service(req, resp);
        PrintWriter pw=resp.getWriter();
        resp.setContentType("text/html;charset=utf8");
        String path= req.getServletPath();
        if("/movieList".equals(path)){
            getMovieList(req,resp);
        }

        if("/movieAdd".equals(path)){
            addMovie(req, resp);
        }
    }

    protected void getMovieList(HttpServletRequest req,HttpServletResponse resp) throws IOException {
        resp.setContentType("text/html;charset=utf8");
        PrintWriter pw=resp.getWriter();
        pw.println("getMovieList<hr />");
        ResultSet rs=null;
        PreparedStatement ps=null;
        Connection conn=null;
        try {
            conn= JDBCUtile.getConnection();
            String sql="select * from y_movie";
            ps=conn.prepareStatement(sql);
            rs = ps.executeQuery();
            while (rs.next()){
                pw.println("m_name="+rs.getString("m_name"));
                pw.println("m_director="+rs.getString("m_director"));
                pw.println("m_up_year="+rs.getString("m_up_year"));
            }
        } catch (SQLException e) {
            e.printStackTrace();
        } finally {
            JDBCUtile.close(rs,conn,ps);
        }
    }

    protected void addMovie(HttpServletRequest request,HttpServletResponse response) throws IOException {
        request.setCharacterEncoding("UTF-8");
        Connection conn=null;
        PreparedStatement ps=null;
        ResultSet rs=null;
        PrintWriter pw=response.getWriter();
        pw.println("addMovie方法<hr />");
        response.setContentType("text/html;charset=utf8");
        try {
            conn= JDBCUtile.getConnection();
            String m_name=request.getParameter("m_name");
            String m_director=request.getParameter("m_director");
            String m_up_year=request.getParameter("m_up_year");

            String sql="insert into y_movie (m_name,m_director,m_up_year) values(?,?,?)";
            ps=conn.prepareStatement(sql);
            ps.setString(1,m_name);
            ps.setString(2,m_director);
            ps.setString(3,m_up_year);
            int count= ps.executeUpdate();
            pw.println(count>0 ? "新增成功" : "新增失败");
        } catch (SQLException e) {
            e.printStackTrace();
        } finally {
            JDBCUtile.close(rs,conn,ps);
        }
    }
}

20.Session

jakarta.servlet.http.HttpSession

用户打开浏览器,进行一系列操作,最终关闭浏览器,这整个过程称为一次会话,一次会话包含多次请求,这个会话在服务器端也有一个对象,这个java对象叫做session,session也称为会话域;

因为http协议是一种无状态协议,请求结束之后链接就断开了,这样可以减轻服务器压力;

session最重要的机制是保存会话状态;

servletRequest保存的数据只在一次请求中有效,生命周期太短;servletContext在服务器启动的时候创建,在服务器停止的时候销毁,生命周期太长;

		//从服务器获取session对象,如果获取不到则新建
        HttpSession session =req.getSession();
        //从服务器获取session对象,如果获取不到则不新建,返回null
        HttpSession session2 =req.getSession(false);		
		//向会话域当中存数据
        session.setAttribute("user","root");
        //向会话域中取数据
        String user= (String) session.getAttribute("user");
        //销毁session
		session.invalidate();```

session对象是存储在服务器端的;

session对象的销毁方式:超时销毁、手动销毁

![在这里插入图片描述](https://img-blog.csdnimg.cn/a0f33bb0c46347a88df43c25dd9920cd.png#pic_center)
Session的实现原理:

	session列表是一个map,map的key是sessionId,map的value是session对象,用户第一次请求,服务器生成session对象,同时生成sessionId,将sessionId发送给浏览器,浏览器将它保存在cookie当中,例如Cookie:JSESSIONID=407AABC32F163AF4FB43C05E8BFAFE7F用户再次请求的时候,浏览器将内存中的sessionId发送给服务器,服务器根据sessionId查找session对象,关闭浏览器,内存消失,cookie消失,会话结束;

如果cookie被禁用了,可以用url重写机制获取session

http://localhost:8080/Servlet04/test01;JSESSIONID=F8B261F0CEA6E1BA69F7B5B39282C694

url重写机制会提供开发成本,开发中在任何请求的后面都有带上sessionId,给开发带来了很大的难度;
### 21.Cookie

jakarta.servlet.http.Cookie

cookie最终是保存在浏览器的客户端上的;

cookie和session机制都是为了保存会话状态,cookie是将会话的状态保存在浏览器客户端,session是将会话保存在服务器端;

实际上cookie机制和session机制都是http协议的一部分,其他后端编程语言也有;

HTT协议规定:任何一个cookie都是由name和value组成的,name和value都是字符串,当浏览器发送请求的时候会自动携带该path下的cookie数据给服务器;

```java
		Cookie cookie=new Cookie("movie","haiwang2");
        //设置cookie的有效时间,在2小时后失效
        cookie.setMaxAge(60*60*2);
        response.addCookie(cookie);

如果cookie没有设置有效时间,则默认保存在浏览器内存当中,浏览器关闭则cookie消失,只要设置cookie的有效时间大于0,则cookie就会保存在硬盘当中;

设置cookie的有效期为0,表示该cookie被删除,主要用这种方式删除浏览器上的同名cookie;

设置cookie的有效期小于0,则表示cookie被存储在内存当中,不会被存储到硬盘当中,和不设置有效时间是一个效果;

如果没有设置setPath,那么cookie会与保存cookie的请求路径的父路径相关联;只要请求路径包含该父路径,则浏览器就会提交cookie给服务端;

		//表示/Servlet04路径下的请求都会提交该cookie给服务器
        cookie.setPath("/Servlet04");

		//获取cookie
		//如果没有cookie,getCookies返回的是null
        Cookie[] cookies=request.getCookies();
        if(cookies!=null){
            for (Cookie c: cookies) {
                pw.println(c.getName());
                pw.println(c.getValue());
            }
        }

22.JSP9大内置对象

ServletContext(application)

request

response

pageContext(当前页面作用域)

page(this,当前的Servlet对象)

session

out(负责输出)

exception

config

23.过滤器Filter

jakarta.servlet.Filter

在这里插入图片描述

filter是过滤器,可以在Servlet目标程序之前添加代码,也可以在Servlet目标程序之后添加代码,之前之后都可以添加过滤规则,一般情况下,都是在过滤器中编写公共代码;

Filter接口的3个方法

  • init():在filter对象第一次被创建之后调用,并且只调用一次
  • doFilter():该方法用户发送一次请求执行一次,在该方法中编写过滤规则
  • destroy():在filter对象被释放/销毁之前调用,并且只调用一次

实现过滤器的步骤:

  • 编写一个类实现jakarta.servlet.Filter,并实现接口的所有方法
  • 在web.xml编写Filter配置(或者过滤器加注解@WebFilter)

默认情况下,服务器会在启动时创建过滤器对象

Filter对象与Servlet对象都是单例(单实例)的

//web.xml
<filter>
        <filter-name>myFilter</filter-name>
        <filter-class>com.ywxk.servlet04.Filter.MyFilter</filter-class>
    </filter>
    <filter-mapping>
        <filter-name>myFilter</filter-name>
        <!--模糊匹配中的扩展匹配,不能以/开头-->
        <url-pattern>*.do</url-pattern>
        <!--前缀匹配-->
        <url-pattern>/filter/*</url-pattern>
        <!--匹配所有路径-->
        <url-pattern>/*</url-pattern>
        <!--精确匹配-->
        <url-pattern>/myFilter2</url-pattern>
    </filter-mapping>
//过滤器类
//@WebFilter("/*")
public class MyFilter implements Filter {

    public MyFilter() {
        System.out.println("Myfilter构造方法");
    }

    @Override
    public void init(FilterConfig filterConfig) throws ServletException {
        System.out.println("init方法执行");
    }

    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
        //目标程序之前执行过滤程序
        System.out.println("doFilter方法执行之前");
        //执行下一个过滤器,如果下一个不是过滤器,则执行目标程序
        chain.doFilter(request,response);
        //目标程序之后执行过滤程序
        System.out.println("目标程序执行之后");
    }

    @Override
    public void destroy() {
        System.out.println("destroy方法执行");
    }
}
    
//目标路径
@WebServlet("/filter/a")
public class FilterTestA extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
       // super.doGet(req, resp);
        System.out.println("FilterTestA的方法");
    }
}

目标Servlet是否执行需要满足两个条件:

  • 在过滤器当中是否编写了:chain.doFilter(request,response)代码;
  • 用户发送的请求路径与Servlet一致;

chain.doFilter(request,response)的作用是:执行下一个过滤器,如果下面没有过滤器了,则执行最终的Servlet;

Filter的优先级比Servlet的优先级高

如果在web.xml配置多个过滤器,越靠上,优先级越高

使用注解@WebFilter的时候,多个filter执行顺序是按照类名的ASCII码的顺序执行的;

过滤器的执行顺序遵循栈数据结构;

过滤器里面有一个设计模式:责任链设计模式;

过滤器最大的优点是:在程序编译阶段不会确定调用顺序,因为Filter的调用顺序是配置到web.xml里面的,只要修改web.xml配置文件中的的顺序就可以跳转filter的执行顺序,显然filter的执行顺序是在程序运行阶段动态组合的,那么这种设计模式被称为责任链设计模式;

责任链设计模式的核心思想是:

​ 在程序运行阶段,动态的组合程序的调用顺序;

所以为了实现责任链设计模式,建议将filter配置写在web.xml文件中,而不要使用注解方式

24.监听器Listener

在Servlet中,所有的监听器都是以Listener结尾的;

监听器其实是Servlet规范留给javaweb程序员的特殊时机;

实现一个监听器的步骤,以ServletContextListener为例:

  • 编写一个类实现ServletContextListener接口,并实现接口的方法
	@Override
	//这个方法在ServletContext对象被创建的时候调用
    public void contextInitialized(ServletContextEvent sce) {
        //ServletContextListener.super.contextInitialized(sce);
    }

    @Override
	//这个方法在ServletContext对象被销毁的时候调用
    public void contextDestroyed(ServletContextEvent sce) {
        //ServletContextListener.super.contextDestroyed(sce);
    }
  • 在web.xml进行配置,或者对实现类添加注解@WebListener
<listener>
    <listener-class>com.ywxk.servlet04.listener.MyListener</listener-class>
</listener>

所有监听器中的方法都不需要javaweb程序员调用,当某个特殊事件发生后,由服务器来负责调用;

@WebListener
public class MyServletRequestListener implements ServletRequestListener{

    @Override
    public void requestDestroyed(ServletRequestEvent sre) {
       // ServletRequestListener.super.requestDestroyed(sre);
        System.out.println("request对象销毁时执行");
    }

    @Override
    public void requestInitialized(ServletRequestEvent sre) {
        //ServletRequestListener.super.requestInitialized(sre);
        System.out.println("requst对象创建时执行");
    }
}

@WebListener
public class MyHttpSessionListener implements HttpSessionListener {
    @Override
    public void sessionCreated(HttpSessionEvent se) {
        //HttpSessionListener.super.sessionCreated(se);
        System.out.println("session创建时执行");
    }

    @Override
    public void sessionDestroyed(HttpSessionEvent se) {
        //HttpSessionListener.super.sessionDestroyed(se);
        System.out.println("session销毁时执行");
    }
}

@WebListener
//session域中的数据发生变化的时候触发该监听
public class MyHttpAttributeListener implements HttpSessionAttributeListener {
    @Override
    public void attributeAdded(HttpSessionBindingEvent se) {
        //HttpSessionAttributeListener.super.attributeAdded(se);
        System.out.println("向session内存数据的时候调用");
    }

    @Override
    public void attributeRemoved(HttpSessionBindingEvent se) {
        //HttpSessionAttributeListener.super.attributeRemoved(se);
        System.out.println("删除session属性的时候调用");
    }

    @Override
    public void attributeReplaced(HttpSessionBindingEvent se) {
        //HttpSessionAttributeListener.super.attributeReplaced(se);
        System.out.println("修改session属性的时候调用");
    }
}

//实现该接口的类被绑定到session的时候触发该监听,不需要添加@WebListener
public class Movie1 implements HttpSessionBindingListener {

    private String mName;

    public Movie1() {
    }

    public Movie1(String mName) {
        this.mName = mName;
    }

    public String getmName() {
        return mName;
    }

    public void setmName(String mName) {
        this.mName = mName;
    }

    @Override
    public void valueBound(HttpSessionBindingEvent event) {
        //HttpSessionBindingListener.super.valueBound(event);
        System.out.println("movie1绑定数据");
    }

    @Override
    public void valueUnbound(HttpSessionBindingEvent event) {
        //HttpSessionBindingListener.super.valueUnbound(event);
        System.out.println("movie1解绑数据");
    }
}

可以用HttpSessionBindingListener来实现统计登录用户数量,User类实现HttpSessionBindingListener接口,并实现接口的方法,当用户登录的时候,往session中添加user对象的时候可以触发valueBound方法,用户数据++,当用户退出的时候,session中移除user对象,触发valueUnbound方法,用户数据–;

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

y_w_x_k

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

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

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

打赏作者

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

抵扣说明:

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

余额充值