Servlet编程

时间:2021-02-07 07:13:13

Servlet编程

1. servlet概念及相关接口简介

java Servlet是运行在web服务器或应用服务器上的程序,他是作为来自web浏览器或其他HTTP客户端的请求和HTTP服务器山的数据库或应用程序之间的中间层

什么是Servlet

是一个java类,继承自HttpServlet类

这个类在服务器端运行,用以处理客户端的请求

主要作用

1.读取客户端(浏览器)发送来的显式数据和隐式的HTTP请求数据,包括html文件,cookies,HTTP请求等信息

2.处理数据被返回生成结果。

3.发送显式数据和隐式HTTP响应到客户端

2.开发一个Servlet流程

步骤:

1.编写java类,继承自HttpServlet类

2.重写doGet和doPost方法

3.Servlet程序交给tomcat服务器运行

3.1.servlet程序的class码拷贝到WEB-INF/calsses目录,如果是IDE,会自动拷贝

3.2.在web.xml文件中进行配置

3.3.配置的另一种方法是在servlet程序开头写上这样的语句,表示访问路径@WebServlet("/first")

先看servlet的程序

/**
* 运行在tomcat服务器端的servlet类
*/
@WebServlet(name = "FirstServlet")
public class FirstServlet extends HttpServlet { protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
//向浏览器输出内容
response.getWriter().write("This is first servlet!");
//向控制台打印信息
System.out.println("helloWorld");
}
}

servlet程序写完之后还需要在WEB-INF目录下配置web.xml

配置如下:

    <!--配置一个servelt-->
<!--servlet的配置-->
<servlet>
<!--servlet的内部名称,名字可以自定义,但是最好有意义-->
<servlet-name>FirstServlet</servlet-name>
<!--servlet的类全名:包名+简单类名-->
<servlet-class>servers.FirstServlet</servlet-class>
</servlet> <!--servlet的映射配置-->
<servlet-mapping>
<!--servlet的内部名称,一定要和上面的内部名称保持一致-->
<servlet-name>FirstServlet</servlet-name>
<!--servlet的映射路径(访问servlet的名称)-->
<url-pattern>/first</url-pattern>
</servlet-mapping>

3.servlet路径映射

当我们访问 http://localhost:8080/sayhello/first路径时,是如何调用servlet呢?

xml路径开始之前:

tomcat服务器启动时,首先加载webapps中的每一个web应用的web.xml配置文件

http:// :http协议

localhost : 在本地的hosts文件中查找是否存在该域名对应的IP地址

8080 : 找到tomcat服务器

/sayhello : 在tomcat的webapps目录下找到sayhello目录

/first 资源名称

访问到/first时开始执行:

1.在sayhello的web.xml中查找是否有匹配的url-pattern的内容(/first)

2.如果找到匹配的url-pattern,则使用当前的servlet-name的名称到web.xml文件中查询是否有相同的servlet配置

3.如果找到,则取出对应的servlet配置信息中的servlet-calss内容

4.通过反射:

4.1.构造FirstServlet的对象

4.2.然后调用FirstServlet里面的方法

Servlet的映射路径

路径有两种精准匹配和模糊匹配两种方式

精确匹配:

/first 访问 http://localhost:8080/sayhello/first

模糊匹配:

  1. /* http://localhost:8080/sayhello/任意路径

    2./itcast/* http://localhost:8080/day10/sayhello/任意路径

    3.*.后缀名 例:×.do http://localhost:8080/sayhello/任意路径.do

注意:

1.url-pattern要么以 / 开头,要么以开头。 例如, itcast是非法路径。

2.不能同时使用两种模糊匹配,例如 /itcast/
.do是非法路径

3.当有输入的URL有多个servlet同时被匹配的情况下:

3.1 精确匹配优先。(长的最像优先被匹配)

3.2 以后缀名结尾的模糊url-pattern优先级最低!!!

4.缺省servlet

servlet的缺省路径(/)是在tomcat服务器内置的一个路径,该路径对应的是一个DefaultServlet(缺省Servlet)。这个缺省的Servlet的所用是用于解析web应用的静态资源文件

案例:URL输入http://localhost:8080/sayhello/index.html 如何读取文件????

1.首先到sayhello应用下的web.xml文件查找是否有匹配的url-pattern

2.如果没有匹配的url-pattern,则交给tomcat的内置DefaultServlet处理

3.DefaultServlet程序到sayhello应用的根目录下查找是否存在一个名称为index.html的静态文件

4.如果找到静态文件,则读取该文件内容,返回给浏览器

5.如果找不到该文件,则返回404错误页面

结论:先找动态资源,再找静态资源,不要轻易在web.xml使用/*,如果要使用,推荐加上后缀

5.servlet生命周期(重点)

5.1.引入

在之前接触的类中,可以使用new方法来创建一个对象,可以调用对象的方法,可以使对象等于null来消除对象。但是在servlet这个类中,我们只是重写了doGet等方法,并没有自己实现这个类的对象创建,初始化,调用,消除。

那么servlet这个类的生命周期是怎么样的呢?创建,初始化,调用,消除又是怎么实现的呢?

5.2.Servlet重要的四个生命周期方法

构造方法:创建servlet对象的时候调用。默认情况下,第一次访问servlet的时候创建servlet对象。只调用1次,证明servlet对象在tomcat是单实例的

init方法:创建完servlet对象的时候调用。只调用1次

service方法:每次发出请求时调用。请求几次调用几次

destory方法:销毁servlet对象的时候调用。停止服务器或者重新部署web应用时销毁servlet对象,只调用1次

代码示例:

/**
* 测试servlet的生命周期的四个重要方法
*/
@WebServlet(name = "LifeDemo")
public class LifeDemo extends HttpServlet {
//1.构造方法
public LifeDemo(){
System.out.println("1.LifeDemo被创建了");
} //2.inti方法
@Override
public void init(ServletConfig config) throws ServletException {
System.out.println("2.init方法被调用");
} //3.service方法
@Override
public void service(ServletRequest req, ServletResponse res) throws ServletException, IOException {
System.out.println("3.service方法被调用");
} //4.destory方法
@Override
public void destroy() {
System.out.println("4.servlet对象销毁");
}
}

5.3.伪代码演示servlet的生命周期

servlet是通过反射来调用的,这里使用伪代码模拟过程

tomcat内部代码运行

1.通过映射找到servlet-class的内容(字符串:servers.FirstServlet)

2.通过反射构造FirstServlet对象

2.1.得到字节码对象

Class clazz = class.forName("servers.FirstServlet");

2.2.调用无参的构造方法来构造对象

Object obj = clazz.newInstance(); ---1.servlet的构造方法被调用

3.创建ServletConfig对象,通过反射调用init方法

3.1.得到方法对象

Method m = clazz.getDeclareMethod("init",ServletConfig.class);

3.2.调用方法

m.invoke(obj,config); --2.servlet的init方法被调用

4.创建request,response对象,通过反射调用service方法

4.1 得到方法对象

Methodm m =clazz.getDeclareMethod("service",HttpServletRequest.class,HttpServletResponse.class);

4.2 调用方法

m.invoke(obj,request,response); --3.servlet的service方法被调用

5.当tomcat服务器停止或web应用重新部署,通过反射调用destroy方法

5.1 得到方法对象

Method m = clazz.getDeclareMethod("destroy",null);

5.2 调用方法

m.invoke(obj,null); --4.servlet的destroy方法被调用

5.4.用时序图来演示servlet的生命周期

Servlet编程

5.5.Servlet的自动加载

默认情况下,第一次访问servlet的时候创建servlet对象。如果servlet的构造方法或init方法中执行了比较多的逻辑代码,那么导致用户第一次访问sevrlet的时候比较慢。为了解决这个问题,我们可以设置在web应用第一次被访问的时候,就执行servlet的构造方法和init方法

改变servlet创建对象的时机: 提前到加载web应用的时候!!!

方法:

在servlet的配置信息中,加上一个即可!!

<servlet>
<servlet-name>LifeDemo</servlet-name>
<servlet-class>gz.itcast.c_life.LifeDemo</servlet-class>
<!-- 让servlet对象自动加载 -->
<load-on-startup>1</load-on-startup>
</servlet>

注意: 整数值越大,创建优先级越低!!

6.有参的init方法和无参的init方法

有参的init方法是tomcat的生命周期方法。

无参的init方法使用提供给开发者重写该方法的地方

不要覆盖有参的方法,只能覆盖无参的方法

/**
* init有参和无参方法的区别
*/
@WebServlet(name = "InitDemo")
public class InitDemo extends HttpServlet {
/**
* 有参数的init方法
* 该方法是servlet的生命周期方法,一定会被tomcat服务器调用
* 这个init方法不能被覆盖
* @param config
* @throws ServletException
*/
@Override
public void init(ServletConfig config) throws ServletException {
System.out.println("有参的init方法");
} /**
* 无参数的init方法
* 该方法是servlet的编写初始化代码的方法
* 是sun公司设计出来专门给开发这进行覆盖的,然后在里面编写servlet的出事逻辑代码方法
* @throws ServletException
*/
@Override
public void init() throws ServletException {
System.out.println("无参的init方法");
}
}

7.Servlet线程安全

从上面的示例可以知道,Servlet是单实例,但是servlet又能被多客户端访问,所以又是一个多线程的。所以servlet是一个单实例多线程的

如果多线程同时访问了servlet对象的共享数据(成员变量)可能会引发线程安全问题

解决方法

1.把使用到共享数据的代码块进行同步(使用synchronized关键字进行同步)

2.建议在servlet类中尽量不要使用成员变量,如果确实要使用成员,必须同步。而且尽量缩小同步代码块的范围(建议哪里使用成员变量,就同步哪里!!),以避免因为同步导致并发效率降低

/**
* servlet的多线程并发问题
*/
@WebServlet("/thread")
public class ThreadDemo extends HttpServlet { int count=1; @Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
resp.setContentType("text/html;charset=utf-8"); //多线程锁
synchronized (ThreadDemo.class) {//锁对象必须唯一,建议使用字节码
resp.getWriter().write("你现在是当前网站的第" + count + "个访客"); count++;
}
}
}

Servlet中的对象

HttpServletRequest 请求对象:获取请求信息

HttpServletResponse 响应对象: 设置响应对象

ServletConfig对象 servlet配置对象

ServletContext对象; servlet的上下文对象

8.servletConfig对象

8.1.作用

servletConfig对象:主要是用于加载servlet的初始化对象

8.2.对象创建和得到

创建时机:在创建完servlet对象之后,在调用init方法之前

得到对象:直接从有参数的init方法得到

8.3.servlet的初始化参数配置

ServletConfig的API:

java.lang.String getInitParameter(java.lang.String name) 根据参数名获取参数值

java.util.Enumeration getInitParameterNames() 获取所有参数

ServletContext getServletContext() 得到servlet上下文对象

java.lang.String getServletName() 得到servlet的名称

配置文件:

    <servlet>
<servlet-name>ServletConfigDemo</servlet-name>
<servlet-class>servers.ServletConfigDemo</servlet-class>
<!-- 初始参数: 这些参数会在加载web应用的时候,封装到ServletConfig对象中 -->
<init-param>
<param-name>name</param-name>
<param-value>rocco</param-value>
</init-param>
</servlet>

ServletConfig的代码

/**
* 测试ServletConfig对象
*/
@WebServlet(name = "ServletConfigDemo")
public class ServletConfigDemo extends HttpServlet { protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
//读取servlet的初始参数
String name = this.getServletConfig().getInitParameter("name"); response.getWriter().write("hello");
System.out.println(name); }
}

9. ServletContext对象

9.1.引入

ServletContext对象,叫做Servlet的上下文对象。表示一个当前的web应用环境,一个web应用中只有一个ServletContext对象

9.2.对象创建和得到

创建时间:加载web应用时创建该对象

得到对象:从ServletConfig的getServletContext方法得到

9.3.ServletContext对象的核心API

得到ServletContext对象:

ServletContext context = this.getServletContext();

得到web应用路径

String getContextPath(); 得到当前web的应用路径

得到web应用的初始化参数(全局)

config参数是当前servlet的参数,只能由当前servlet对象获得

Context参数是全局参数,通过ServletContext对象,所有的都可以获得

全部参数的写法:

    <!--配置web应用参数-->
<context-param>
<param-name>AAA</param-name>
<param-value>AAA's value</param-value>
</context-param>
<context-param>
<param-name>BBB</param-name>
<param-value>BBB's value</param-value>
</context-param>
<context-param>
<param-name>CCC</param-name>
<param-value>CCC's value</param-value>
</context-param>

String getInitParameter(String name); 得到web应用指定的初始化参数

Enumeration geetInitParameterNames();得到web应用所有的初始化参数

域对象有关的方法

域对象:作用是用于保存数据,获取数据。可以在不同的动态资源之间共享数据

servlet是运行在服务器上的程序,如何在不同的servlet的程序之间传递参数的呢?常规做法是通过重定向后面附带参数来传递,例如:response.sendRedirect("/Servlet2?name=eric") 但是这样有一个问题,就是只能传递字符串的参数,且参数被暴露

最好的解决方案是使用域对象,域对象可以共享任何类型的数据

ServletContext其实就是一个域对象

ServletContext域对象:作用范围在整个web应用中有效!!!

域对象的一些操作

void setAttribute(java.lang.String name, java.lang.Object object) --保存数据

Object getAttribute(java.lang.String name) --获得数据

void removeAttribute(java.lang.String name) --删除数据

所有域对象:

HttpServletRequet域对象

ServletContext域对象

HttpSession 域对象

PageContext域对象

转发

转发和重定向的效果是一样的

RequestDispatcher getRequestDispatcher(java.lang.String path) --转发(类似于重定向)

转发和重定向的区别

1.转发:

  • a.地址栏不会改变
  • b.转发只能转发到当前web应用内的资源
  • c.在转发过程中,可以把数据保存到request域对象中

2.重定向

  • a.地址栏会改变,变成重定向到的新地址
  • b.重定向可以跳转到当前web应用,或其他web应用,甚至是外部域名网站
  • c.不能再重定向的过程中,把数据保存到request中

结论:如果要使用request域对象进行数据共享,只能使用转发技术

其他一些方法

通常在一个java路径中,我们用 . 表示项目文件所在的地址,但是在java web项目中,点 . 表示tomcat下bin里的startup.sh所在的目录。所以在java web中不能使用点 . 来查找文件

但是可以使用 “/” 来查找, / 表示WEB-INF所在的目录地址,通过这个找到资源文件

java.lang.String getRealPath(java.lang.String path) --得到web应用的资源文件

java.io.InputStream getResourceAsStream(java.lang.String path) 得到资源文件,但是返回的是一个输入流,不需要再转换

/**
* 测试ServletContext对象的核心API
*/
@WebServlet(name = "ServletContextDemo")
public class ServletContextDemo extends HttpServlet {
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { //1.得到ServletContext对象
//下面两种方法都可以,推荐使用第二种
// ServletContext context = this.getServletConfig().getServletContext();
ServletContext context = this.getServletContext();//推荐使用 //2.得到web应用路径
//web应用路径:部署到tomcat服务器上运行的web应用名称
String contextPath = context.getContextPath();
System.out.println(contextPath); //案例:请求重定向
response.sendRedirect(contextPath+"/index.html"); //3.得到web应用参数
String aaa = context.getInitParameter("AAA");//获取指定的一个
Enumeration<String> enums = context.getInitParameterNames();//得到所有 //4.域对象的一些操作
//保存到域对象中
context.setAttribute("name","tom");
//从域对象中获得数据
Object name = context.getAttribute("name");
//从域对象中删除数据 //5.转发,效果就是跳转页面
//不能转发到项目以外的路径
RequestDispatcher rd = context.getRequestDispatcher("/index.html");
rd.forward(request,response); /**
* 6.读取web应用下的资源文件
*/ //在java项目中,点 . 表示java项目所在的地址
//java web项目中,点 . 表示tomcat的bin目录,所以不能使用这种相对路径
// File file = new File("./src/db.properties");
// FileInputStream in = new FileInputStream(file); //正确获取
//方法1.getRealPath
// “/”表示WEB-INF所在目录地址
String path = this.getServletContext().getRealPath("/WEB-INF/classes/db.properties");
File file = new File(path);
FileInputStream in = new FileInputStream(file); //方法2: 得到资源文件,返回的是输入流
InputStream in = this.getServletContext().getResourceAsStream("/WEB-INF/classes/db.properties"); Properties prop = new Properties();
//读取资源文件
prop.load(in); String user = prop.getProperty("user");
String password = prop.getProperty("password");
System.out.println(user+"="+password); }
}