1.Servlet概念
运行在服务器端的小程序。
Servlet就是一个接口,定义了Java类被浏览器访问到(tomcat识别)的规则。
将来我们自定义一个类,实现Servlet接口,复写方法。
快速入门:
1. 创建JavaEE项目
2. 定义一个类,实现Servlet接口
public class ServletDemo1 implements Servlet
3. 实现接口中的抽象方法
package cn.itcast.web.servlet; import javax.servlet.*; import java.io.IOException; public class ServletDemo1 implements Servlet { @Override public void init(ServletConfig servletConfig) throws ServletException { } @Override public ServletConfig getServletConfig() { return null; } @Override public void service(ServletRequest servletRequest, ServletResponse servletResponse) throws ServletException, IOException { System.out.println("hello servlet..."); } @Override public String getServletInfo() { return null; } @Override public void destroy() { } }
4. 配置Servlet
在web.xml中配置:
<?xml version="1.0" encoding="UTF-8"?> <web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd" version="4.0"> <!--配置serlvet--> <servlet> <servlet-name>demo1</servlet-name> <servlet-class>cn.itcast.web.servlet.ServletDemo1</servlet-class> </servlet> <servlet-mapping> <servlet-name>demo1</servlet-name> <url-pattern>/demo1</url-pattern> </servlet-mapping> </web-app>
实现原理
1. 当服务器接受到客户端浏览器的请求后,会解析请求URL路径,获取访问的Servlet的资源路径
2. 查找web.xml文件,是否有对应的<url-pattern>标签体内容。
3. 如果有,则在找到对应的<servlet-class>全类名
4. tomcat会将字节码文件加载进内存,并且创建其对象
5. 调用其方法
2.注解配置
Servlet3.0:
好处:
支持注解配置。可以不需要web.xml了。
步骤:
1. 创建JavaEE项目,选择Servlet的版本3.0以上,可以不创建web.xml
2. 定义一个类,实现Servlet接口
3. 复写方法
4. 在类上使用@WebServlet注解,进行配置
@WebServlet("资源路径")
package cn.itcast.web.servlet; import javax.servlet.*; import javax.servlet.annotation.WebServlet; import java.io.IOException; @WebServlet("/demo") public class ServletDemo1 implements Servlet { @Override public void init(ServletConfig servletConfig) throws ServletException { } @Override public ServletConfig getServletConfig() { return null; } @Override public void service(ServletRequest servletRequest, ServletResponse servletResponse) throws ServletException, IOException { System.out.println("hello servlet..."); } @Override public String getServletInfo() { return null; } @Override public void destroy() { } }
3.Servlet的体系结构
Servlet -- 接口
|
GenericServlet -- 抽象类
|
HttpServlet -- 抽象类
GenericServlet:将Servlet接口中其他的方法做了默认空实现,只将service()方法作为抽象,将来定义Servlet类时,可以继承GenericServlet,实现service()方法即可。
package cn.itcast.web.servlet; import javax.servlet.*; import javax.servlet.annotation.WebServlet; import java.io.IOException; @WebServlet("/demo") public class ServletDemo2 extends GenericServlet { @Override public void service(ServletRequest servletRequest, ServletResponse servletResponse) throws ServletException, IOException { System.out.println("hello servlet2..."); } }
HttpServlet:对http协议的一种封装,简化操作。
1. 定义类继承HttpServlet
2. 复写doGet/doPost方法
package cn.itcast.web.servlet; import javax.servlet.ServletException; import javax.servlet.annotation.WebServlet; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.io.IOException; @WebServlet("/demo") public class ServletDemo3 extends HttpServlet { @Override protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { System.out.println("doGet..."); } @Override protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { System.out.println("doPost..."); } }
4.缺省路径
4.1 什么是缺省路径
在tomcat服务器内部配置了一个缺省路径 <url-pattern>/</url-pattern>, 这个缺省路径在tomcat服务器内部被一个缺省Servlet(DefaultServlet)匹配。缺省的这个Servlet的作用是专门用于处理所有网站中的静态网页。
浏览器: http://localhost:8080/day09/hello.html
一个路径在tomcat服务器的执行过程:
1)搜索day09下面的web.xml文件信息。搜索是否存在匹配的<url-pattern>
2)匹配到对应的url-pattern,执行对应的Servlet程序。
3)如果没有匹配的url-pattern,就把这个请求交给tomcat服务器的DefaultServlet处理。
4)DefaultServlet会在day09的根目录下搜索是否存在一个名称叫hello.html的文件。
5)如果找到次文件, 那么DefaultServlet读取该文件内容返回给用户。
6)如果找不到次文件, 那么就给用户返回一个404状态码+404错误页面。
结论: 先找动态网页,再找静态网页。
5.Sevlet的生命周期(重点)
5.1 概念
研究servlet的生命周期,就是研究servlet这个在什么时候创建对象,在什么时候调用什么方法,在什么时候销毁对象。
以前: new Student();
现在: servlet是由tomcat服务器执行的生命周期。
5.2 Sevlet的四个重要的生命周期方法
构造方法: 创建servlet对象时调用,只调用1次,证明了sevlet是单例的。
init方法: 创建完对象之后调用。只调用1次。该方法用于初始化对象。
Servlet什么时候被创建?
默认情况下,第一次被访问时,Servlet被创建
可以配置执行Servlet的创建时机。
在<servlet>标签下配置
1. 第一次被访问时,创建
<load-on-startup>的值为负数
2. 在服务器启动时,创建
<load-on-startup>的值为0或正整数
service方法: 在每次请求时调用,调用n次。该方法入口方法,我们的逻辑代码在这里被调用。
destory方法:在servlet对象销毁之后调用。只调用1次。重新部署网站或者停止服务器,servlet对象就会销毁了。
5.3 用伪代码演示tomcat服务器如何调用四个方法
字符串: gz.itcast.a_servlet.HelloServlet
1)通过反射,创建HelloServlet的对象
1.1 得到HelloServlet的CLass对象
Class clazz = Class.forName("gz.itcast.a_servlet.HelloServlet ")
1.2 通过class对象调用构造方法
Object obj = clazz.newInstance(); 调用无参的构造方法。 --1)构造方法被调用
2)通过反射,调用init方法
2.1 得到init方法对象
Method m = clazz.getDeclaraeMethod("init",ServletConfig.class);
2.2 调用方法
m.invoke(obj,config); -2)init方法被调用
3)通过反射,调用service方法
3.1 得到service方法对象
Method m = clazz.getDeclareMethod("service",HttpServletRequest.class,HttpServletResponse.class);
3.2 调用方法
m.invoke(obj,request,response); --3)service方法被调用
4)通过反射,调用destroy方法
4.1 得到destroy方法对象
Method m= clazz.getDeclareMethod('destroy",null);
4.2调用方法
m.invoke(obj,null); --4)destroy方法被调用
package day09Servlet.cn.jxufe.c_life; import java.io.IOException; import javax.enterprise.context.Destroyed; import javax.servlet.ServletException; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; public class LifeDemo1 extends HttpServlet { /* * 1)构造方法。servlet对象创建时调用的 */ public LifeDemo1() { // TODO Auto-generated constructor stub System.out.println("servlet对象被创建了"); } /* * 2)初始化方法 */ @Override public void init() throws ServletException { // TODO Auto-generated method stub System.out.println("init方法被调用"); } /* * 3)服务方法 */ @Override protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { // TODO Auto-generated method stub System.out.println("service方法被调用"); } /* * 4)销毁方法。sevlet对象销毁时调用的 */ @Override public void destroy() { // TODO Auto-generated method stub System.out.println("servlet对象被销毁了"); } }
6.Servlet的线程并发问题
6.1 引入
servlet在tomcat服务器中,是单实例多线程的
6.2 开发线程安全的servlet建议
1)尽量不要使用成员变量,或者静态成员变量。
2)必须要使用成员变量,要么给使用了成员变量的代码块加同步锁,加锁的代码块的范围尽量缩小,因为有可能影响程序并发效率。
package day09Servlet.cn.jxufe.d_thread; /* * servlet的多线程并发问题 */ import java.io.IOException; import javax.servlet.ServletException; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; public class ThreadDemo extends HttpServlet { public ThreadDemo() { System.out.println("TheadDemo对象创建了"); } // 成员变量,那么该数据就可能被不同用户线程共享到。有可能引发多线程并发问题。 static int count = 1; public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { response.setContentType("text/html;charset=utf-8"); /** * 1)同步代码块 * 给使用到共享数据的代码块添加同步锁 * 注意:同步锁必须多个线程唯一的 */ synchronized (ThreadDemo.class) { response.getWriter().write("你当前是第" + count + "个访客!"); // A线程刚刚执行完这句代码,被B线程抢去了执行时机。 // 睡眠 /*try { Thread.sleep(10000); } catch (InterruptedException e) { e.printStackTrace(); }*/ count++; } } /** * 2 )同步方法 * @param response */ /*public synchronized static void method(HttpServletResponse response){ try { response.getWriter().write("你当前是第"+count+"个访客!"); } catch (IOException e1) { e1.printStackTrace(); } // A线程刚刚执行完这句代码,被B线程抢去了执行时机。 //睡眠 try { Thread.sleep(10000); } catch (InterruptedException e) { e.printStackTrace(); } count++; }*/ }
7.Servlet的自动加载机制
7.1 引入
默认情况servlet对象是在第一次访问的时候创建。如果在这个servlet的构造方法或者init方法执行比较多的逻辑,那么第一次访问servlet的用户体验就不好!
那么能不能改变创建servlet的机制? 可以的,可以让servlet在服务器启动的时候自动加载。
<servlet> <servlet-name>LifeDemo</servlet-name> <servlet-class>gz.itcast.c_life.LifeDemo</servlet-class> <!-- 这个配置可以让servlet在tomcat服务器启动的时候自动创建servlet对象 --> <load-on-startup>1</load-on-startup> <--加这一句话就可以在加载的时候启动--> </servlet>
注意:load-on-startup中的整数值越大,创建的优先级越低!!!
8.Servlet的init方法
8.1 两个init方法作用
init(ServletConfig) 这个有参数的init方法的作用就是Servlet的其中一个重要的生命周期方法。这个方法一定会被tomcat服务器调用。
init(): 这个无参的init方法的作用就是为了提供给开发者方便去覆盖,覆盖这个方法,可以在此方法编写初始化的逻辑。这个方法才是真的给开发者做初始化逻辑的方法。
小结:
Servlet开发中涉及的对象:
HttpservletRequest对象: 请求对象
HttpServletResponse对象: 响应对象
ServletConfig对象: servlet配置对象
ServletContext对象: servlet的上下文对象
9.ServletConfig对象
9.1 引入
servletconfig是配置对象,主要把servlet的初始化参数封装到这个对象中。一个网站中可能会存在多个ServletConfig对象,一个ServletConfig对象就封装了一个servlet的配置信息。
9.2 配置初始化参数
<servlet> <servlet-name>ConfigDemo</servlet-name> <servlet-class>day09Servlet.cn.jxufe.e_config.ConfigDemo</servlet-class> <!-- servlet的初始化参数 --> <init-param> <param-name>path</param-name> <param-value>e:/aaa.txt</param-value> </init-param> </servlet>
在servlet中获取初始化参数
config.getInitParameter("name"); 根据参数名称获取参数值
config.getInitParameterNames(); 获取所有参数名称
package day09Servlet.cn.jxufe.e_config; import java.io.BufferedReader; import java.io.FileReader; import java.io.IOException; import javax.servlet.ServletException; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; public class ConfigDemo extends HttpServlet { /*private ServletConfig config * 已经封装了这个方法,不用自己写了,直接用this调用就可以了; @Override public void init(ServletConfig config) throws ServletException { this.config = config; }*/ public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { String path = null; /** * 读取到servlet的初始化参数(init-param) */ path = this.getServletConfig().getInitParameter("path"); /** * 读取文件内容 */ BufferedReader br = new BufferedReader(new FileReader(path)); String str = null; while ((str = br.readLine()) != null) { System.out.println(str); } } }
10.ServletContext对象
10.1 引入
ServletConfig对象叫servlet上下文对象。 一个网站只会创建一个ServletContext对象。代表的是整个网站的环境信息。
10.2 获取ServletContext对象
this.getServletConfig().getServletContext(); 通过ServletConfig对象来获取到ServletContext对象的!!
伪代码:
ServletContext对象:启动的时候创建
ServletConfig对象:调用init方法之前创建的,在ServletContext对象创建之前
public ServletCofig{
ServletContext context;
public ServletConfig(context){
this.context=context;
}
public ServetContxt getServletContext(){
return;
}
}
ServletConfig config = new ServletConfig(context);
public MyServlet extends HttpSevlet{
publlic init(ServletConfig config){
SevletContext context= config. getServletContext();
}
}
10.3 ServletContext的5大作用
1)获取web的上下文路径
java.lang.String getContextPath()
2)获取全局参数
java.lang.String getInitParameter(java.lang.String name)
java.util.Enumeration getInitParameterNames()
3)和域对象相关的
void setAttribute(java.lang.String name, java.lang.Object object)
java.lang.Object getAttribute(java.lang.String name)
void removeAttribute(java.lang.String name)
什么是域对象?
域对象在不同的资源之间来共享数据。保存数据,获取数据。
ServletContext就是我们学习的第一个域对象。
Servlet三个域对象:
ServletContext
HttpServletRequest
HttpSession
4)请求转发的
RquestDispatcher getRequestDispatcher(java.lang.String path)
在servlet跳转页面:
请求重定向:
1)地址栏会发送改变,变成重定向到的地址。
2)可以跳转到项目内的资源,也可以跳转项目外的资源
3)浏览器向服务器发出两次请求,那么就不能使用请求来作为域对象来共享数据。
请求转发:
1)地址栏不会改变。
2)只能跳转到项目内的资源,不能跳转到项目外的资源
3)浏览器向服务器发出一次请求,那么可以使用请求作为域对象共享数据。
5)读取web项目的资源文件
java.lang.String getRealPath(java.lang.String path)
java.io.InputStream getResourceAsStream(java.lang.String path)
java.net.URL getResource(java.lang.String path)
package day09Servlet.cn.jxufe.f_context; import java.io.IOException; import javax.servlet.ServletContext; import javax.servlet.ServletException; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; /* * 【context对象的作用1】-获取web上下文路径 */ public class ContextDemo1 extends HttpServlet { public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { /** * 获取web上下路径 */ // 1.1.获取ServletContext对象 // ServletContext context = this.getServletConfig().getServletContext(); /** * 推荐使用简写 */ ServletContext context = this.getServletContext(); // 1.2 获取 String path = context.getContextPath(); System.out.println(path); ///day09Servlet /* * web上下文路径就是项目在tomcat服务器中运行的路径。注意不是开发目录的项目名称。 */ // 请求重定向 // response.sendRedirect("/day09/hello.html"); // 作用:可以让这个获取文件的路径更加灵活 response.sendRedirect(context.getContextPath() + "/hello.html"); } }
package day09Servlet.cn.jxufe.f_context; import java.io.IOException; import java.util.Enumeration; import javax.servlet.ServletContext; import javax.servlet.ServletException; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; /* * 【context对象作用2】-获取全局参数 */ public class ContextDemo2 extends HttpServlet { public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { /** * 得到全局参数 */ ServletContext context = this.getServletContext(); System.out.println(context.getInitParameter("AAA")); // 遍历所有参数 Enumeration<String> enums = context.getInitParameterNames(); while (enums.hasMoreElements()) { String paramName = enums.nextElement(); String paramValue = context.getInitParameter(paramName); System.out.println(paramName + "=" + paramValue); } } }
package day09Servlet.cn.jxufe.g_scope; import java.io.IOException; import java.util.ArrayList; import java.util.List; import javax.servlet.ServletContext; import javax.servlet.ServletException; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; public class ScopeDemo1 extends HttpServlet { public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { /** * 把数据存储到ServletContext域对象 */ ServletContext context = this.getServletContext(); // context.setAttribute("name", "eric"); List<String> list = new ArrayList<>(); list.add("eric"); list.add("jacky"); list.add("rose"); context.setAttribute("list", list); System.out.println("保存成功"); } }
package day09Servlet.cn.jxufe.g_scope; import java.io.IOException; import java.util.List; import javax.servlet.ServletContext; import javax.servlet.ServletException; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; public class ScopeDemo2 extends HttpServlet { public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { /** * 从ServletContext域对象中取出数据 */ ServletContext context = this.getServletContext(); // String name = (String)context.getAttribute("name"); List<String> list = (List<String>) context.getAttribute("list"); System.out.println(list); } }
package day09Servlet.cn.jxufe.h_forward_redirect; import java.io.IOException; import javax.servlet.RequestDispatcher; import javax.servlet.ServletContext; import javax.servlet.ServletException; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; /* * 【context对象作用4】--请求转发 */ public class ForwardDemo1 extends HttpServlet { public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { /* * 转发:服务器行为 */ // ServletContext context = this.getServletContext(); // RequestDispatcher rd = context.getRequestDispatcher("/hello.html"); // rd.forward(request, response); /** * 注意:转发只能转发都当前项目的资源 */ // this.getServletContext().getRequestDispatcher("../bbs/adv.html").forward(request, // response); /** * 把数据保存到request域对象 */ request.setAttribute("name", "jacky"); // this.getServletContext().getRequestDispatcher("/GetDataServlet").forward(request, response); /** * 简写方式 */ request.getRequestDispatcher("/GetDataServlet").forward(request, response); } }
package day09Servlet.cn.jxufe.h_forward_redirect; import java.io.IOException; import javax.servlet.ServletException; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; public class RedirectDemo2 extends HttpServlet { public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { /** * 请求重定向(浏览器行为) */ // response.sendRedirect("/day09Servlet/hello.html"); /* * 注意: * 可以跳转到当前项目的资源,也可以跳转到其他项目的资源 */ // response.sendRedirect("/bbs/adv.html"); /** * 把数据保存到request域对象 */ request.setAttribute("name", "jacky"); response.sendRedirect("/day09Servlet/GetDataServlet"); } }
package day09Servlet.cn.jxufe.h_forward_redirect; import java.io.IOException; import javax.servlet.ServletException; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; /* * 路径问题 * 服务器行为 / 代表从当前项目的根目标开始 * 浏览器行为 / 代表从当前站点的根目录开始 */ public class PathDemo extends HttpServlet { public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { // 目标地址: hello.html /** * 转发 */ // request.getRequestDispatcher("/hello.html").forward(request, // response); /** * 重定向 */ // response.sendRedirect("/day09Servlet/hello.html"); /** * html页面的超链接 */ response.getWriter().write("<a href='/day09Servlet/hello.html'>link</a>"); /** * html页面的表单的action */ response.getWriter().write("<form action='/day09Servlet/hello.html'><input type='submit'/></form>"); } }
package day09Servlet.cn.jxufe.i_ziyuan; import java.io.IOException; import java.io.InputStream; import java.util.Properties; import javax.servlet.ServletException; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; /* * 【context对象作用5】--读取web项目的资源文件 */ public class Ziyuan extends HttpServlet { public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { /** * 复习: 读取Properties文件? */ /** * 相对路径: * 当前路径相对于java命令运行的目录 * 结论: * 在web项目中,java命令运行的目录就是在tomcat的bin目录。在web项目,不能使用相对路径 */ /** FileInputStream in = new FileInputStream("./src/news.properties"); //1)使用Properteis对象 Properties prop = new Properties(); //2)使用load方法加载properties文件 prop.load(in); //3)通过getProperty()获取内容 System.out.println(prop.getProperty("name")); System.out.println(prop.getProperty("password")); */ /** * 1)getRealPath() 获取资源文件的真实路径 */ String path = this.getServletContext().getRealPath("/WEB-INF/classes/news.properties"); System.out.println(path); /** * 2)getResourceAsStream() 获取资源文件的输入流 */ InputStream in = this.getServletContext().getResourceAsStream("/WEB-INF/classes/news.properties"); /** * 3)getResource() 获取资源文件的URL */ // URL url = // this.getServletContext().getResource("/WEB-INF/classes/news.properties"); // FileInputStream in = new FileInputStream(new File(path)); // 1)使用Properteis对象 Properties prop = new Properties(); // 2)使用load方法加载properties文件 prop.load(in); // 3)通过getProperty()获取内容 System.out.println(prop.getProperty("name")); System.out.println(prop.getProperty("password")); } }