第四章 Servlet编程
目录
1、Servlet 执行过程
1.1 Servlet简介
1.1.1 servlet说明
- 用java语言开发网页动态资源的技术
- Servlet 是 JavaEE 规范之一。规范就是接口
- Servlet 就 JavaWeb 三大组件之一。三大组件分别是:Servlet 程序、Filter 过滤器、Listener 监听器。
- Servlet 是运行在服务器上的一个 java 小程序,它可以接收客户端发送过来的请求,并响应数据给客户端。
1.1.2 servlet特点:
- servlet就是一个普通的java类,继承HttpServlet类
- 一个普通的java类实现了Servlet接口,也叫Servlet程序。我们通常继承HttpServlet是为了创建一个基于http协议的servlet程序。
- servlet程序交给tomcat服务器运行
1.2 手动实现
-
编写一个类去实现 Servlet 接口
实现 service 方法,处理请求,并响应数据
public class HelloServlet implements Servlet {
/*** service 方法是专门用来处理请求和响应的
* @param servletRequest
* @param servletResponse
* @throws ServletException
* @throws IOException */
@Override public void service(ServletRequest servletRequest, ServletResponse servletResponse) throws ServletException, IOException {
System.out.println("Hello Servlet 被访问了");
}
}
public class HelloServlet2 extends HttpServlet {
/*** doGet()在 get 请求的时候调用
* @throws ServletException
* @throws IOException */
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
System.out.println("HelloServlet2 的 doGet 方法");
}
/*** doPost()在 post 请求的时候调用
* @throws ServletException
* @throws IOException */
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
System.out.println("HelloServlet2 的 doPost 方法");
}
}
- 到 web.xml 中去配置 servlet 程序的访问地址
<!-- 浏览器访问: http://localhost:8080/day10/hello -->
<!-- 配置一个servlet -->
<!-- servlet的配置 -->
<servlet>
<!-- servlet内部名称。可以自定义 -->
<servlet-name>HelloServlet</servlet-name>
<!-- servlet类的全名:包名+简单类名 -->
<servlet-class>gz.itcast.a_servlet.HelloServlet</servlet-class>
</servlet>
<!-- servlet的映射配置 -->
<servlet-mapping>
<!-- sevlet内部名称。和servlet配置的内部名称保持一致!! -->
<servlet-name>HelloServlet</servlet-name>
<!-- servlet的路径映射。访问servlet的名称 -->
<url-pattern>/hello</url-pattern>
</servlet-mapping>
1.3 Servlet开发过程(url 地址到 Servlet 程序的访问)
- 前提: tomcat服务器启动时,会加载%conf%/web.xml文件。然后再加载站点下的每个web应用的web.xml文件。
- http:// 本地hosts文件查询域名和ip的映射,找不到,联网到运营商的DNS服务起器找域名和ip的映射
- **8080:**Tomcat端口
- **06_servlet:**站点下的web应用名称
- **hello:**资源名称。
- 如果读到对应的静态资源文件,那么就把内容返回给浏览器
- 如果读不到对应的静态资源文件,那么就返回404的错误页面。
2、Servlet的路径映射
url-pattern: 表示servlet的路径映射,也叫servlet访问名称。
2.1 精确匹配
- /demo1 http://localhost:8080/day10/demo1
- /itcast/demo1 http://localhost:8080/day10/itcast/demo1
2.2 模糊匹配
样式 | 路径 |
---|---|
/* | http://localhost:8080/day10/任意路径 |
/itcast/* | http://localhost:8080/day10/itcast/任意路径 |
后缀 (.do、 .action、.jsf、.html) | http://localhost:8080/day10/任意路径.后缀 |
2.3 注意事项
- url-pattern要么以/开头,要么以*开头 例如: itcast/demo1错误写法。
- 不能同时使用两种模糊匹配。例如: /itcast/*.html 错误写法
- 当多个url-pattern同时符合匹配规则,那么
- 精确匹配的url-pattern会优先被执行。(长的最像的url-pattern优先)
- 后缀名结尾的模糊匹配的url-pattern优先级最低
3、Servlet生命周期
3.1 说明
- 平常的java类自己去new对象,自己去使用对象调用。sevlet程序,servlet的生命周期由tomcat服务器控制的。
3.2 Servlet的四个生命周期
- 构造方法: 在创建servlet对象时调用,只调用1次。证明servlet对象在tomcat服务器中是单实例的。
- init方法:在创建完servlet对象后调用,只调用1次。
- service方法: 在每次请求servlet时调用,用n次。
- destroy方法: servlet对象销毁时调用只调用1次。tomcat服务器停止或web应用重新部署时调用
3.3 用伪代码模拟tomcat服务器调用servlet生命周期
3.3.1 tomcat服务器通过反射构造对象
得到字节码对象
Class clazz = Class.forName("浏览器中的地址");
构造对象
Object hello = clazz.newInstance(); //servlet的构造方法被调用
3.3.2 tomcat服务器调用init方法
得到init方法对象
Method m = clazz.getDeclareMethod("init",ServeletConfig.class);
执行方法
m.invoke(hello,config); //servlet的init方法被调用
3.3.3 tomcat服务器创建request和response对象,调用service方法
得到service方法对象
Method m =clazz.getDeclareMethod("service",HttpServletRequest.class,HttpServletRespnose.class);
执行方法
m.invoke(hello,request,response); //servlet的service方法被调用
3.3.4 tomcat服务器停止或web应用重新部署时,tomcat服务器调用destroy方法
得到destroy方法对象
Method m = clazz.getDeclareMethod("destroy",null)
执行方法
m.invoke(hello,null); //servlet的destroy方法被调用
3.4 时序图描述servlet执行过程
4、 Servlet线程安全问题
4.1 servlet对象特点
在tomcat服务器中是单实例多线程的。多个线程同时操作了Servlet的成员变量(共享数据)。
4.2 避免Servlet并发问题建议
- 尽量不要在servlet类中使用成员变量。
- 如果要使用成员变量,那么就要给使用到成员变量的代码块加上代码锁,尽量缩小同步锁的范围,以避免因为同步产生代码并发执行效率降低的问题。
5、Servlet自动加载
默认情况下,第一次访问servlet时创建servlet对象。创建对象的过程中会调用构造方法和inti方法。如果init方法的业务逻辑需要消耗比较长的时间,用户的第一次访问servlet时需要等待较长的时间。
改变servlet对象创建的时机: tomcat服务器启动的时候创建servlet对象。
<servlet>
<servlet-name>LoadDemo2</servlet-name>
<servlet-class>gz.itcast.e_load.LoadDemo2</servlet-class>
<!-- 正整数:数值越大,创建对象的优先级越低 -->
<load-on-startup>1</load-on-startup>
</servlet>
6、init问题
在GenericServlet中,有两个init方法。
- 有参数的init方法:该方法是servlet的四个生命周期方法中的一个。由tomcat服务器默认调用的初始化方法。在GenericServet的实现中,该方法会调用无参数的init方法。
- 无参数的init方法:该方法的是Sun公司设计出来用于给开发者去覆盖,用于实现初始化逻辑的方法。
如果要编写servlet的初始化逻辑,只需要覆盖无参数的init方法即可
7、Servlet中重要对象
对象 | 作用 |
---|---|
HttpServletRequest对象 | 封装请求信息 |
HttpServletResponse对象 | 封装响应信息 |
ServletConfig对象 | 封装一个servlet配置参数信息 |
ServletContext对象 | 封装web应用环境信息 |
7.1 ServletContext对象
7.1.1 ServletContext对象简介
- ServletContext叫Servlet上下文对象,该对象表示当前的web应用环境信息。一个web应用只会创建一个ServletContext对象。
- ServletContext 是一个接口,它表示 Servlet 上下文对象
- 一个 web 工程,只有一个 ServletContext 对象实例。
- ServletContext 对象是一个域对象。
- ServletContext 是在 web 工程部署启动的时候创建。在 web 工程停止的时候销毁。
-
域对象,是可以像 Map 一样存取数据的对象,叫域对象。 这里的域指的是存取数据的操作范围,整个 web 工程。主要用于保存数据和获取数据,用于在web应用中不同资源之间共享数据。
-
域对象的作用: 主要用于保存数据和获取数据,用于在web应用中不同资源之间共享数据。参数形式: 只能传递字符串数据
Servlet中所有域对象:
- HttpServletRequest对象: request域
- ServletContext对象: context域
- HttpSession对象: session域
Jsp中域对象:PageContext对象: page域
存数据 | 取数据 | 删除数据 | |
---|---|---|---|
Map | put() | get() | remove() |
域对象 | setAttribute() | getAttribute() | removeAttribute() |
7.1.2 ServletContext 类的四个作用
- 获取 web.xml 中配置的上下文参数 context-param
- 获取当前的工程路径,格式: /工程路径
- 获取工程部署后在服务器硬盘上的绝对路径
- 像 Map 一样存取数据
1、演示代码:
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
// 1、获取 web.xml 中配置的上下文参数 context-param
ServletContext context = getServletConfig().getServletContext();
String username = context.getInitParameter("username");
System.out.println("context-param 参数 username 的值是:" + username);
System.out.println("context-param 参数 password 的值是:" + context.getInitParameter("password"));
// 2、获取当前的工程路径,格式: /工程路径
System.out.println( "当前工程路径:" + context.getContextPath() ); // 3、获取工程部署后在服务器硬盘上的绝对路径
/**
* / 斜杠被服务器解析地址为:http://ip:port/工程名/ 映射到 IDEA 代码的 web 目录<br/>
*/
System.out.println("工程部署的路径是:" + context.getRealPath("/"));
System.out.println("工程下 css 目录的绝对路径是:" + context.getRealPath("/css"));
System.out.println("工程下 imgs 目录 1.jpg 的绝对路径是:" + context.getRealPath("/imgs/1.jpg")); }
2、web.xml 配置
<!--context-param 是上下文参数(它属于整个 web 工程)-->
<context-param>
<param-name>username</param-name>
<param-value>context</param-value>
</context-param>
<!--context-param 是上下文参数(它属于整个 web 工程)-->
<context-param>
<param-name>password</param-name>
<param-value>root</param-value>
</context-param>
3、 ServletContext存取数据:
/**
* ContextServlet1,写入内容
*/
public class ContextServlet1 extends HttpServlet {
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
// 获取 ServletContext 对象
ServletContext context = getServletContext(); System.out.println(context);
System.out.println("保存之前: Context1 获取 key1 的值是:"+ context.getAttribute("key1"));
context.setAttribute("key1", "value1"); System.out.println("Context1 中获取域数据 key1 的值是:"+ context.getAttribute("key1")); }
}
/**
* ContextServlet2,读取内容
*/
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
ServletContext context = getServletContext();
System.out.println(context);
System.out.println("Context2 中获取域数据 key1 的值是:"+ context.getAttribute("key1")); }
7.1.3 ServletContext对象的常用方法
1、得到web应用的上下文路径
//得到web应用上下路径。也就是部署到tomcat服务器上运行的web应用名称。
java.lang.String getContextPath()
2、得到web应用的全局参数(所有servlet有效的!!)
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) //清除数据
4、转发相关的
response.sendRedirect(路径); //请求重定向
// 在servlet中实现页面跳转
RequestDispatcher
getRequestDispatcher(java.lang.String path)
request.getRequestDispacher(路径).forward(request,respone); //请求转发
5、web应用读取资源文件
java.lang.String getRealPath(java.lang.String path)
java.io.InputStream getResourceAsStream(java.lang.String path)
//不同资源
//web应用内部资源。在当前web应用中的资源
//其他web应用资源。在同一个站点下的其他web应用的资源
//站点外的资源。其他站点下的资源
6、请求重定向和请求转发区别
请求重定向 | 请求转发 |
---|---|
地址栏改变,改变为重定向到地址 | 地址栏不会改变 |
可以重定向到当前web应用,其他web应用,甚至是其他站点资源 | 只能转发到当前web应用内部资源 |
两次不同的请求。不可以使用request域对象来共享数据 | 同一次请求。可以使用request域对象来共享数据 |
7.2 ServletConfig对象
ServletConfig 类是 Servlet 程序的配置信息类。 Servlet 程序和 ServletConfig 对象都是由 Tomcat 负责创建,我们负责使用。 Servlet 程序默认是第一次访问的时候创建,ServletConfig 是每个 Servlet 程序创建时,就创建一个对应的ServletConfig 对象。
7.2.1 ServletConfig 类的三大作用
- 可以获取 Servlet 程序的别名 servlet-name 的值
- 取初始化参数 init-param
- 获取 ServletContext 对象
7.2.2 使用
<!-- XML配置 -->
<!-- servlet 标签给 Tomcat 配置 Servlet 程序 -->
<servlet>
<!--servlet-name 标签 Servlet 程序起一个别名(一般是类名) -->
<servlet-name>HelloServlet</servlet-name>
<!--servlet-class 是 Servlet 程序的全类名-->
<servlet-class>com.atguigu.servlet.HelloServlet</servlet-class> <!--init-param 是初始化参数-->
<init-param>
<!--是参数名-->
<param-name>username</param-name>
<!--是参数值-->
<param-value>root</param-value>
</init-param>
<!--init-param 是初始化参数-->
<init-param>
<!--是参数名-->
<param-name>url</param-name>
<!--是参数值-->
<param-value>jdbc:mysql://localhost:3306/test</param-value> </init-param>
</servlet>
<!--servlet-mapping 标签给 servlet 程序配置访问地址-->
<servlet-mapping>
<!--servlet-name 标签的作用是告诉服务器,我当前配置的地址给哪个 Servlet 程序使用-->
<servlet-name>HelloServlet</servlet-name>
<!--url-pattern 标签配置访问地址 <br/> / 斜杠在服务器解析的时候,表示地址为:http://ip:port/工程路径 <br/> /hello 表示地址为:http://ip:port/工程路径/hello <br/> -->
<url-pattern>/hello</url-pattern> </servlet-mapping>
@Override
public void init(ServletConfig servletConfig) throws ServletException {
System.out.println("2 init 初始化方法");
// 1、可以获取 Servlet 程序的别名 servlet-name 的值
System.out.println("HelloServlet 程序的别名是:" + servletConfig.getServletName());
// 2、获取初始化参数 init-param
System.out.println("初始化参数 username 的值是;" + servletConfig.getInitParameter("username"));
System.out.println("初始化参数 url 的值是;" + servletConfig.getInitParameter("url")); //
//3、获取 ServletContext 对象
System.out.println(servletConfig.getServletContext()); }
- 注意:重写init()方法里面一定要调用父类的init(ServletConfig)操作,子类重写了init()方法,在调用init()方法的时候会直接调用子类的方法
//父类的init()方法
public void init(ServletConfig config) {
this.config = config;
this.init();
}