package com.mystudy.servlet;
import java.io.IOException;
import java.io.PrintWriter;
import javax.servlet.RequestDispatcher;
import javax.servlet.ServletConfig;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.apache.catalina.connector.Request;
public class helloServlet extends HttpServlet {
//http请求方法不只是post和get,还有options用于返回服务器支持的方法,put方法用于上传指定的资源,
//delete方法用于删除指定的资源,head和get相同只是返回http的报头,一般用到的只有post和get
@Override
public void init() throws ServletException {
System.out.println("========servlet init without param============");
super.init();//无参数的时候可以不调用super
}
@Override
public void init(ServletConfig config) throws ServletException {
System.out.println("========servlet init with param============");
//有参数时需要手动调用super方法,init方法并不一定要重写,在需要servlet初始化的时候做一些操作,则重写,其他时候也不需要重写
//有参数的init方法在父类中会显示的调用无参的init方法。
super.init(config);
}
@Override
protected void service(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException {
System.out.println("========servlet service============");
PrintWriter p=resp.getWriter();
p.print("hello world");
p.close();
}
@Override
public void destroy() {
//当servlet容器销毁servlet的时候才会调用destroy方法
System.out.println("========servlet destroy============");
}
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException {
// TODO Auto-generated method stub
super.doGet(req, resp);
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException {
// TODO Auto-generated method stub
super.doPost(req, resp);
PrintWriter p=resp.getWriter();
p.print("hello world");
p.close();
String userNameString=req.getParameter("uname");
String passwordString=req.getParameter("upwd");
System.out.println("用户名: "+userNameString);
System.out.println("密码: "+passwordString);
//请求重定向,直接返回这个url,浏览器会重新访问返回 的url,因此这个url可以是当前应用的也可以是其他应用的地址,
//那么浏览器的请求实际上完成的是两次,第一次访问目标地址,第二次访问重定向地址
resp.sendRedirect(req.getContextPath()+"/04/success.jsp");
//请求转发 和请求重定向不一样的是,请求转发只有一个http请求,不会产生第二次,
//并且转发的地址必须是同一个应用中的地址,各个组件公用一个req请求和resp返回对象
String forwordString="/04/error.jsp";
RequestDispatcher rDispatcher=req.getRequestDispatcher(forwordString);
rDispatcher.forward(req, resp);
}
}
还需要在web.xml中配置servlet
<servlet>
<servlet-name>helloServlet</servlet-name>
<servlet-class>com.mystudy.servlet.helloServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>helloServlet</servlet-name>
<url-pattern>/hello</url-pattern>
</servlet-mapping>
先让我们来看一下这几个方法:
1. init()方法
在Servlet的生命周期中,仅执行一次init()方法,它是在服务器装入Servlet时执行的,可以配置服务器,以在启动服务器或客户机首次访问Servlet时装入Servlet。无论有多少客户机访问Servlet,都不会重复执行init();
2. service()方法
它是Servlet的核心,每当一个客户请求一个HttpServlet对象,该对象的Service()方法就要调用,而且传递给这个方法一个“请求”(ServletRequest)对象和一个“响应”(ServletResponse)对象作为参数。在HttpServlet中已存在Service()方法。默认的服务功能是调用与HTTP请求的方法相应的do功能。
3. destroy()方法
仅执行一次,在服务器端停止且卸载Servlet时执行该方法,有点类似于C++的delete方法。一个Servlet在运行service()方法时可能会产生其他的线程,因此需要确认在调用destroy()方法时,这些线程已经终止或完成。
下面来谈谈Servlet的生命周期,Servlet的生命周期是由Servlet容器来控制的,它始于装入Web服务器的内存时,并在终止或重新装入Servlet时结束。这项操作一般是动态执行的。然而,Server通常会提供一个管理的选项,用于在Server启动时强制装载和初始化特定的Servlet。
在代码中,Servlet生命周期由接口javax.servlet.Servlet定义。所有的Java Servlet 必须直接或间接地实现javax.servlet.Servlet接口,这样才能在Servlet Engine上运行。javax.servlet.Servlet接口定义了一些方法,在Servlet 的生命周期中,这些方法会在特定时间按照一定的顺序被调用。
Servlet生命周期
加载和实例化Servlet
我们来看一下Tomcat是如何加载的:
1. 如果已配置自动装入选项,则在启动时自动载入。
2. 在服务器启动时,客户机首次向Servlet发出请求。
3. 重新装入Servlet时。
当启动Servlet容器时,容器首先查找一个配置文件web.xml,这个文件中记录了可以提供服务的Servlet。每个Servlet被指定一个Servlet名,也就是这个Servlet实际对应的Java的完整class文件名。Servlet容器会为每个自动装入选项的Servlet创建一个实例。所以,每个Servlet类必须有一个公共的无参数的构造器。
初始化
当Servlet被实例化后,Servlet容器将调用每个Servlet的init方法来实例化每个实例,执行完init方法之后,Servlet处于“已初始化”状态。所以说,一旦Servlet被实例化,那么必将调用init方法。通过Servlet在启动后不立即初始化,而是收到请求后进行。在web.xml文件中用<load-on-statup> ...... </load-on-statup>对Servlet进行预先初始化。
初始化失败后,执行init()方法抛出ServletException异常,Servlet对象将会被垃圾回收器回收,当客户端第一次访问服务器时加载Servlet实现类,创建对象并执行初始化方法。
请求处理
Servlet 被初始化以后,就处于能响应请求的就绪状态。每个对Servlet 的请求由一个Servlet Request 对象代表。Servlet 给客户端的响应由一个Servlet Response对象代表。对于到达客户机的请求,服务器创建特定于请求的一个“请求”对象和一个“响应”对象。调用service方法,这个方法可以调用其他方法来处理请求。
Service方法会在服务器被访问时调用,Servlet对象的生命周期中service方法可能被多次调用,由于web-server启动后,服务器中公开的部分资源将处于网络中,当网络中的不同主机(客户端)并发访问服务器中的同一资源,服务器将开设多个线程处理不同的请求,多线程同时处理同一对象时,有可能出现数据并发访问的错误。
另外注意,多线程难免同时处理同一变量时(如:对同一文件进行写操作),且有读写操作时,必须考虑是否加上同步,同步添加时,不要添加范围过大,有可能使程序变为纯粹的单线程,大大削弱了系统性能;只需要做到多个线程安全的访问相同的对象就可以了。
卸载Servlet
当服务器不再需要Servlet实例或重新装入时,会调用destroy方法,使用这个方法,Servlet可以释放掉所有在init方法申请的资源。一个Servlet实例一旦终止,就不允许再次被调用,只能等待被卸载。
Servlet一旦终止,Servlet实例即可被垃圾回收,处于“卸载”状态,如果Servlet容器被关闭,Servlet也会被卸载,一个Servlet实例只能初始化一次,但可以创建多个相同的Servlet实例。如相同的Servlet可以在根据不同的配置参数连接不同的数据库时创建多个实例。
通过HttpServlet类中的service方法可以看到,他的功能是根据请求的方法,自动的映射到不同的请求处理方法中,如果请求是get,那么请求交给doget(),如果是post交给dopost()。那么默认的service方法相当于一个请求转发的功能,因此如果我们自己重写servce方法, 父类HttpServlet中的service方法就会失效
所以收到的任何请求都会由我们自己覆写的service方法来处理
如果我们的servlet中只有service方法, 是没有问题的
但值得注意的是,一定要在执行完自己代码后调用父类service方法, super.service;
否自你的doGet和doPost是不会被执行的
servlet的过滤器 filter
Filter,过滤器,顾名思义,即是对数据等的过滤,预处理过程。为什么要引入过滤器呢?在平常访问网站的时候,有时候发一些敏感的信息,发出后显示时 就会将敏感信息用*等字符替代,这就是用过滤器对信息进行了处理。这只是一个简单的例子,当然,过滤器那么强大,它的功能也不可能局限于此,它不仅能预处 理数据,只要是发送过来的请求它都是可以预处理的,同时,它还可以对服务器返回的响应进行预处理,这样,大大减轻了服务器的压力。例如,实现URL级别的 权限访问控制、过滤敏感词汇、压缩响应信息等一些高级功能
过滤作用,对从客户端向服务器端发送的请求进行过滤,也可以对服务器端返回的响应进行处理。它使用户可以改变一个request和修改一个 response.。Filter 不是一个servlet,它不能产生一个response,但是它能够在一个request到达servlet之前预处理request,也可以在 response离开servlet时处理response。换句话说,filter其实是客户端与servlet中间的一个传递者,并且它可以对要传递 的东西进行修改。
如何实现拦截
- 当客户端发生请求后,在HttpServletRequest 到达Servlet 之前,过滤器拦截客户的HttpServletRequest 。
- 根据需要检查HttpServletRequest ,也可以修改HttpServletRequest 头和数据。
- 在过滤器中调用doFilter方法,对请求放行。请求到达Servlet后,对请求进行处理并产生HttpServletResponse发送给客户端。
- 在HttpServletResponse 到达客户端之前,过滤器拦截HttpServletResponse 。
- 根据需要检查HttpServletResponse ,可以修改HttpServletResponse 头和数据。
- 最后,HttpServletResponse到达客户端。
package com.mystudy.filter;
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.annotation.WebFilter;
public class FilterOne implements Filter {
/**
* Default constructor.
*/
public FilterOne() {
// filter的构造函数
System.out.println("========filter one construct===========");
}
/**
* @see Filter#init(FilterConfig)
*/
public void init(FilterConfig fConfig) throws ServletException {
// filter 的初始化,可以获取在注册时候的参数
System.out.println("========filter one init===========");
// System.out.println("param= "+fConfig.getInitParameter("encoding"));
}
/**
* @see Filter#doFilter(ServletRequest, ServletResponse, FilterChain)
*/
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
throws IOException, ServletException {
// 过滤器的具体实现,在请求进入对应的servlet之前输出一句话,在退出servlet之后输出一句话。
System.out.println("========filter one dofilter start===========");
chain.doFilter(request, response);
System.out.println("========filter one dofilter end===========");
}
/**
* @see Filter#destroy()
*/
public void destroy() {
// 过滤器销毁的时候执行
System.out.println("========filter one destroy===========");
}
}
在web.xml中进行注册,和servlet一样。
<filter>
<filter-name>FilterOne </filter-name>
<filter-class>com.mystudy.filter.FilterOne </filter-class>
<init-param>//指定参数
<param-name>encoding</param-name>
<param-value>UTF-8</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>FilterOne </filter-name>
<url-pattern>/*</url-pattern>//对于所有该应用的请求都做拦截
</filter-mapping>
对于一个应用可以有多个过滤器,那么过滤器按照其配置的顺序进行调用。在dofilter()方法中的Filterchain对象就是放行到下一层过滤器的作用,可以通过
chain.doFilter(request, response);
实现。
如果没有filter了,那么直接放行到servlet,具体如上图那样。
//一个简单的字符编码过滤器,在注册该过滤器的时候指定一个参数encoding,值为UTF-8
//每次请求来的时候截获检查是不是指定的编码方式,不是就需要进行修改
public void init(FilterConfig fConfig) throws ServletException {
charEncodingString=fConfig.getInitParameter("encoding");
if (charEncodingString==null) {
throw new ServletException("encoding can't null");
}
}
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
throws IOException, ServletException {
if (!charEncodingString.equals(request.getCharacterEncoding())) {
request.setCharacterEncoding("UTF-8");
}
response.setCharacterEncoding("UTF-8");
chain.doFilter(request, response);
}
servlet本质是什么?
servlet接口定义的是一套处理网络请求的规范,所有实现servlet的类,都需要实现它那五个方法,其中最主要的是两个生命周期方法 init()和destroy(),还有一个处理请求的service(),也就是说,所有实现servlet接口的类,或者说,所有想要处理网络请求的类,都需要回答这三个问题:
- 你初始化时要做什么
- 你销毁时要做什么
- 你接受到请求时要做什么
这是Java给的一种规范!servlet是一个规范,那实现了servlet的类,就能处理请求了吗?不是
我们不会在servlet中写什么监听8080端口的代码,servlet不会直接和客户端打交道!
那请求怎么来到servlet呢?答案是servlet容器,比如我们最常用的tomcat,,我们需要把servlet部署到一个容器中,不然你的servlet压根不会起作用。
tomcat才是与客户端直接打交道的家伙,他监听了端口,请求过来后,根据url等信息,确定要将请求交给哪个servlet去处理,然后调用那个servlet的service方法,service方法返回一个response对象,tomcat再把这个response返回给客户端。
jsp和servlet的区别是什么?
jsp的本质就是servlet,jsp经过编译之后就成了servlet。
- Servlet在Java代码中通过HttpServletResponse对象动态输出HTML内容
- JSP在静态HTML内容中嵌入Java代码,Java代码被动态执行后生成HTML内容
- Servlet能够很好地组织业务逻辑代码,但是在Java源文件中通过字符串拼接的方式生成动态HTML内容会导致代码维护困难、可读性差。servlet更擅长逻辑控制
- JSP虽然规避了Servlet在生成HTML内容方面的劣势,但是在HTML中混入大量、复杂的业务逻辑同样也是不可取的。jsp更擅长页面显示
Servlet中没有内置对象,所需要必须通过HttpServletRequest对象,HttpServletResponse对象以及HttpServlet对象得到.
servlet的构造函数和init方法????
首先,构造函数是有的,虽然我们通常不写servlet的构造函数,但是就像任何一个普通的java类一样,编译器会自动给你生成一个默认构造函数;
其次,构造函数和init方法都会被web容器调用,而且是先调用构造函数,然后调用init方法;
最后,貌似容器只会调用默认构造函数,所以如果你自己写了带参数的构造函数(系统就不会自动生成默认构造函数),容器初始化servlet就会出错……P.S.:任何时候都不推荐自己写构造函数来初始化servlet类,哪怕你自己提供不带参数的构造函数……