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
模糊匹配:
- /* 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的生命周期
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);
}
}