深入理解Servlet
第一:动态资源与静态资源的区别:
静态资源: 当用户多次访问这个资源,资源的源代码永远不会改变的资源。
动态资源:当用户多次访问这个资源,资源的源代码可能会发送改变
第二:Servlet
2.1 什么是Servlet
使用java语言来编写动态Web的开发技术。
2.2 Servlet 开发流程
1、继承HttpServlet类,覆写doGet\doPost等相关方法。
2、自定义HttpServ类写入Web.xml 文件中。
2.3 Servlet 开发实例(基于SpringBoot 基础环境开发)
package com.zzg.eshop.servlet;
import java.io.IOException;
import java.util.Date;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
@SuppressWarnings("serial")
public class HelloServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//解决中文乱码问题
resp.setCharacterEncoding("utf-8");// 内容编码,防止出现中文乱码
resp.setContentType("text/html;charset=utf-8"); //向浏览器输出内容
resp.getWriter().write("这是第一个servlet程序。当前时间为:"+new Date());
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
// TODO Auto-generated method stub
this.doGet(req, resp);
}
}
package com.zzg.eshop.config;
import org.springframework.boot.web.servlet.ServletRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import com.zzg.eshop.servlet.HelloServlet;
/**
* servlet 配置类
* @author Administrator
*
*/
@Configuration
public class ServletInitConfig {
// 注册servlet
@Bean
public ServletRegistrationBean hello(){
//第一个参数是第1步中创建的WeChatServlet实例,第二个参数是其对应的路径,相当于web.xml中配置时的url-pattern。
return new ServletRegistrationBean(new HelloServlet(), "/hello");
}
}
package com.zzg.eshop;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.builder.SpringApplicationBuilder;
import org.springframework.boot.web.servlet.support.SpringBootServletInitializer;
@SpringBootApplication
public class Application extends SpringBootServletInitializer {
public static void main(String[] args) {
// TODO Auto-generated method stub
SpringApplication.run(Application.class, args);
}
@Override
protected SpringApplicationBuilder configure(SpringApplicationBuilder builder) {
// TODO Auto-generated method stub
return builder.sources(Application.class);
}
}
2.4 Servlet 开发实例(SpringBoot 开发环境注解版本)
package com.zzg.eshop;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.builder.SpringApplicationBuilder;
import org.springframework.boot.web.servlet.ServletComponentScan;
import org.springframework.boot.web.servlet.support.SpringBootServletInitializer;
@SpringBootApplication
@ServletComponentScan
public class Application extends SpringBootServletInitializer {
public static void main(String[] args) {
// TODO Auto-generated method stub
SpringApplication.run(Application.class, args);
}
@Override
protected SpringApplicationBuilder configure(SpringApplicationBuilder builder) {
// TODO Auto-generated method stub
return builder.sources(Application.class);
}
}
package com.zzg.eshop.servlet;
import java.io.IOException;
import java.util.Date;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
@SuppressWarnings("serial")
@WebServlet(urlPatterns = "/annotation", description = "servlet 注解版本")
public class AnnotationServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
// TODO Auto-generated method stub
//解决中文乱码问题
resp.setCharacterEncoding("utf-8");// 内容编码,防止出现中文乱码
resp.setContentType("text/html;charset=utf-8"); //向浏览器输出内容
resp.getWriter().write("servlet注解版本程序。当前时间为:"+new Date());
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
// TODO Auto-generated method stub
this.doGet(req, resp);
}
}
2.4 Servlet 生命周期:
servlet类对象什么时候创建,什么时候调用什么方法,什么时候销毁。由tomcat服务器控制
2.5 Servlet 生命周期重要的四个方法:
2.5.1 四个方法详解:
构造方法: 创建servlet对象的时候调用。默认情况下,第一次访问servlet的时候创建servlet对象 ,只调用1次。证明servlet对象在tomcat是单实例的。
init方法: 创建完servlet对象的时候调用。只调用1次。
service方法: 每次发出请求时调用。调用n次。
destroy方法: 销毁servlet对象的时候调用。停止服务器或者重新部署web应用时销毁servlet对象。只调用1次。
2.5.2 伪代码演示Servlet 的生命周期
Tomtcat内部代码运行:
1)通过映射找到到servlet-class的内容,字符串: com.zzg.eshop.servlet.HelloServlet
2)通过反射构造FirstServlet对象
2.1 得到字节码对象
Class clazz = class.forName("com.zzg.eshop.servlet.HelloServlet");
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方法被调用
2.5.3 时序图演示Servlet 的生命周期
2.6 Servlet 优先级设置
2.6.1 Springboot 代码版:在Servlet 注册对象ServletRegistrationBean中调用setLoadOnStartup方法,设置Servlet 的优先级。
package com.zzg.eshop.config;
import org.springframework.boot.web.servlet.ServletRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import com.zzg.eshop.servlet.HelloServlet;
/**
* servlet 配置类
* @author Administrator
*
*/
@Configuration
public class ServletInitConfig {
// 注册servlet
@Bean
public ServletRegistrationBean hello(){
//第一个参数是第1步中创建的WeChatServlet实例,第二个参数是其对应的路径,相当于web.xml中配置时的url-pattern。
ServletRegistrationBean bean = new ServletRegistrationBean(new HelloServlet(), "/hello");
// 优先级设置,注意:整数值越大,创建优先级越低
bean.setLoadOnStartup(1);
return bean;
}
}
2.6.2 Springboot 注解版:在Servlet 注解对象@WebServlet标签中设置loadOnStartup属性,实现Servlet 的优先级的设置。
package com.zzg.eshop.servlet;
import java.io.IOException;
import java.util.Date;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
@SuppressWarnings("serial")
@WebServlet(urlPatterns = "/annotation", description = "servlet 注解版本", loadOnStartup = 2)
public class AnnotationServlet extends HttpServlet {
@Override
public void init() throws ServletException {
// TODO Auto-generated method stub
super.init();
System.out.println("-----------AnnotationServlet启动" + new Date());
}
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
// TODO Auto-generated method stub
//解决中文乱码问题
resp.setCharacterEncoding("utf-8");// 内容编码,防止出现中文乱码
resp.setContentType("text/html;charset=utf-8"); //向浏览器输出内容
resp.getWriter().write("servlet注解版本程序。当前时间为:"+new Date());
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
// TODO Auto-generated method stub
this.doGet(req, resp);
}
}
2.7 Servlet的多线程并发问题
因为servlet是多线程的,所以当多个servlet的线程同时访问了servlet的共享数据,如成员变量,可能会引发线程安全问题。
解决办法:
1)把使用到共享数据的代码块进行同步(使用synchronized关键字进行同步)
2)建议在servlet类中尽量不要使用成员变量。如果确实要使用成员,必须同步。而且尽量缩小同步代码块的范围。(哪里使用到了成员变量,就同步哪里!!),以避免因为同步而导致并发效率降低。
实例:多线程不安全Servlet
package com.zzg.eshop.servlet;
import java.io.IOException;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
@SuppressWarnings("serial")
@WebServlet(urlPatterns = "/unsafe", description = "servlet 线程不安全")
public class ThreadUnSafeServlet extends HttpServlet {
private int i = 1;
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
// TODO Auto-generated method stub
// 设置编码格式
// resp.setContentType("text/html;charset=utf-8");
resp.setCharacterEncoding("utf-8");// 内容编码,防止出现中文乱码
resp.setContentType("text/html;charset=utf-8");
// 向浏览器输出内容
resp.getWriter().write("这是第" + i + "次访问...");
try {
Thread.sleep(5000);
} catch (Exception e) {
// TODO: handle exception
}
i++;
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
// TODO Auto-generated method stub
this.doGet(req, resp);
}
}
实例:多线程安全Servlet
package com.zzg.eshop.servlet;
import java.io.IOException;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
@SuppressWarnings("serial")
@WebServlet(urlPatterns = "/safe", description = "servlet 线程安全")
public class ThreadSafeServlet extends HttpServlet {
private int i = 1;
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
// TODO Auto-generated method stub
// 设置编码格式
// resp.setContentType("text/html;charset=utf-8");
resp.setCharacterEncoding("utf-8");// 内容编码,防止出现中文乱码
resp.setContentType("text/html;charset=utf-8");
synchronized (ThreadSafeServlet.class) {
// 向浏览器输出内容
resp.getWriter().write("这是第" + i + "次访问...");
try {
Thread.sleep(5000);
} catch (Exception e) {
// TODO: handle exception
}
i++;
}
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
// TODO Auto-generated method stub
this.doGet(req, resp);
}
}
2.8 ServletContext对象
1、获取Web应用路径
2、用于保存数据,获取数据。可以在不同的Servlet之间共享数据。
实例代码:
package com.zzg.eshop.servlet;
import java.io.IOException;
import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
@SuppressWarnings("serial")
@WebServlet(urlPatterns = "/context", description = "ServletContext 功能演示")
public class ContextServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
// TODO Auto-generated method stub
ServletContext context = this.getServletConfig().getServletContext();
// 输出web应用路径
System.out.println("应用路径" + context.getContextPath());
// 域对象保存数据
context.setAttribute("uuid", System.currentTimeMillis());
// 域对象获取数据
long uuid = (long) context.getAttribute("uuid");
System.out.println("uuid" + uuid);
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
// TODO Auto-generated method stub
this.doGet(req, resp);
}
}
2.9 转发和重定向
2.9.1 转发
1)地址栏不会改变
2)转发只能转发到当前web应用内的资源
3)可以在转发过程中,可以把数据保存到request域对象中
2.9.2 重定向
1)地址栏会改变,变成重定向到地址。
2)重定向可以跳转到当前web应用,或其他web应用,甚至是外部域名网站。
3)不能再重定向的过程,把数据保存到request中。
1.本站遵循行业规范,任何转载的稿件都会明确标注作者和来源;2.本站的原创文章,请转载时务必注明文章作者和来源,不尊重原创的行为我们将追究责任;3.作者投稿可能会经我们编辑修改或补充。