分析:SingleThreadModel 接口指定了系统如何处理对同一个Servlet的调用。如果一个Servlet被这个接口指定,那么在这个Servlet中的service方法将不会有两个线程被同时执行,当然也就不存在线程安全的问题。但是,如果一个Servlet实现了SingleThreadModel接口,Servlet引擎将为每个新的请求创建一个单独的Servlet实例,这将引起大量的系统开销,在现在的Servlet开发中基本看不到SingleThreadModel的使用,这种方式了解即可,尽量避免使用。
第三种办法:避免使用实例变量
线程安全问题很大一部分是由于实例变量造成的,那么我们只要在 Servlet 里面不定义任何的实例变量,那么就不会有线程安全的问题。因为在 Java 内存模型中,方法中的临时变量是在栈上分配空间,而且每个线程都有自己的私有栈空间,不会造成线程安全问题。
package com.ys.servlet;
import java.io.IOException;
import javax.servlet.GenericServlet;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
public class HelloServlet extends GenericServlet{
public void service(ServletRequest req, ServletResponse res) throws ServletException, IOException {
int i = 0;
i ;
//为了使多线程访问安全问题更加突出,我们增加一个延时程序
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(i);
}
}
结果:
6、Servlet 和 JSP 的区别
①、JSP 的本质就是 Servlet,JSP 经过编译后就会变为一个类似 Servlet 的Java文件
②、Servlet 基本是JAVA程序代码构成,擅长于流程控制和事务处理,当然也可以用来生成html代码,但是通过Servlet来生成动态网页很不直观.
③、JSP由HTML代码和JSP标签构成,可以方便地编写动态网页,当然里面也可以编写 Java代码,但是整体看上去不够优雅。而且比较麻烦
所以:JSP侧重于视图,Servlet主要用于控制逻辑。
我们可以看一个 JSP 文件,index.jsp
<%@ page language="java" contentType="text/html; charset=ISO-8859-1"
pageEncoding="utf-8"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1">
<title>Insert title here</title>
</head>
<body>
index.jsp
</body>
</html>
经过编译后:很显然下面的代码结构和 Servlet 是差不多的
package org.apache.jsp;
import javax.servlet.*;
import javax.servlet.http.*;
import javax.servlet.jsp.*;
public final class index_jsp extends org.apache.jasper.runtime.HttpJspBase
implements org.apache.jasper.runtime.JspSourceDependent {
private static final javax.servlet.jsp.JspFactory _jspxFactory =
javax.servlet.jsp.JspFactory.getDefaultFactory();
private static java.util.Map<java.lang.String,java.lang.Long> _jspx_dependants;
private javax.el.ExpressionFactory _el_expressionfactory;
private org.apache.tomcat.InstanceManager _jsp_instancemanager;
public java.util.Map<java.lang.String,java.lang.Long> getDependants() {
return _jspx_dependants;
}
public void _jspInit() {
_el_expressionfactory = _jspxFactory.getJspApplicationContext(getServletConfig().getServletContext()).getExpressionFactory();
_jsp_instancemanager = org.apache.jasper.runtime.InstanceManagerFactory.getInstanceManager(getServletConfig());
}
public void _jspDestroy() {
}
public void _jspService(final javax.servlet.http.HttpServletRequest request, final javax.servlet.http.HttpServletResponse response)
throws java.io.IOException, javax.servlet.ServletException {
final javax.servlet.jsp.PageContext pageContext;
javax.servlet.http.HttpSession session = null;
final javax.servlet.ServletContext application;
final javax.servlet.ServletConfig config;
javax.servlet.jsp.JspWriter out = null;
final java.lang.Object page = this;
javax.servlet.jsp.JspWriter _jspx_out = null;
javax.servlet.jsp.PageContext _jspx_page_context = null;
try {
response.setContentType("text/html; charset=ISO-8859-1");
pageContext = _jspxFactory.getPageContext(this, request, response,
null, true, 8192, true);
_jspx_page_context = pageContext;
application = pageContext.getServletContext();
config = pageContext.getServletConfig();
session = pageContext.getSession();
out = pageContext.getOut();
_jspx_out = out;
out.write("\r\n");
out.write("<!DOCTYPE html PUBLIC \"-//W3C//DTD HTML 4.01 Transitional//EN\" \"http://www.w3.org/TR/html4/loose.dtd\">\r\n");
out.write("<html>\r\n");
out.write("<head>\r\n");
out.write("<meta http-equiv=\"Content-Type\" content=\"text/html; charset=ISO-8859-1\">\r\n");
out.write("<title>Insert title here</title>\r\n");
out.write("</head>\r\n");
out.write("<body>\r\n");
out.write("\tindex.jsp\r\n");
out.write("</body>\r\n");
out.write("</html>");
} catch (java.lang.Throwable t) {
if (!(t instanceof javax.servlet.jsp.SkipPageException)){
out = _jspx_out;
if (out != null && out.getBufferSize() != 0)
try { out.clearBuffer(); } catch (java.io.IOException e) {}
if (_jspx_page_context != null) _jspx_page_context.handlePageException(t);
else throw new ServletException(t);
}
} finally {
_jspxFactory.releasePageContext(_jspx_page_context);
}
}
}
JSP 页面的九个隐含对象:
①、request:HttpServletRequest的一个对象,封装请求信息
②、pageContext:页面的上下文,是PageContext的一个对象,可以从该对象中获取其它8个隐含对象。
③、session:代表浏览器和服务器的一次会话,是HttpSession 的一个对象
④、application:代表当前WEB应用,是ServletContext对象
⑤、config:当前JSP对应Servlet的ServletConfig对象
⑥、out:JspWriter对象,调用out.prinln()可以直接把字符串打印到浏览器上
⑦、page:指向当前JSP对应的Servlet对象的应用,但为Object类型,只能调用 Object 类的方法
⑧、exception:在声明了page指令的isErrorPage="true"时,才可以使用
7、Servlet 的转发和重定向
重定向:
public void service(ServletRequest req, ServletResponse res) throws ServletException, IOException {
HttpServletRequest request = (HttpServletRequest) req;
HttpServletResponse response = (HttpServletResponse) res;
response.sendRedirect("index.jsp");//重定向
}
转发:
HttpServletRequest request = (HttpServletRequest) req;
HttpServletResponse response = (HttpServletResponse) res;
//response.sendRedirect("index.jsp");
request.getRequestDispatcher("/index.jsp").forward(request, response);//转发
我们再看看浏览器访问:同时输入 http://localhost:8080/ServletImprove/hello
重定向变为:
转发为:
本质区别:转发只发出了一次请求,而重定向发出了两次请求
①.转发:地址栏是初次发出请求的地址
重定向:地址栏不再是初次发出的请求地址,地址栏为最后响应的那个地址
②.转发:在最终的Servlet中,request对象和中转的那个request是同一个对象
重定向:在最终的Servlet中,request对象和中转的那个request不是同一个对象
③.转发:只能转发给当前WEB应用的资源
重定向:可以重定向到任何资源
response.sendRedirect("http://www.baidu.com");是可以的
转发就不行
④.转发:/ 代表的是当前WEB应用的根目录(http://localhost:8080/项目名称/)
重定向: / 代表的是当前WEB站点的根目录(http://localhost:8080/)
注意:这两条跳转语句不能同时出现在一个页面中,否则会报IllegalStateException - if the response was already committed
8、Servlet 的过滤器
①、什么是 过滤器?
JavaWEB 的一个重要组件,可以对发送到 Servlet 的请求进行拦截,并对响应也进行拦截
②、如何实现一个过滤器?
第一步:创建一个过滤器类,实现 Filter 接口
package com.ys.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;
public class HelloFilter implements Filter{
public HelloFilter() {
System.out.println("构造器 HelloFilter()...");
}
@Override
public void init(FilterConfig filterConfig) throws ServletException {
System.out.println("init()...");
}
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
throws IOException, ServletException {
System.out.println("doFilter()...");
}
@Override
public void destroy() {
System.out.println("destroy()...");
}
}
第二步:在 web.xml 文件中配置过滤器
<!--给创建的过滤器配置关系 -->
<filter>
<filter-name>helloFilter</filter-name>
<filter-class>com.ys.filter.HelloFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>helloFilter</filter-name>
<url-pattern>/*</url-pattern><!-- 这表示可以拦截任何请求 -->
</filter-mapping>
启动服务器:我们发现还没发送请求,过滤器的 构造方法和 init() 方法就已经开始运行了