Servlet
Tomcat:Web 应用服务器,属于轻量级应用服务器,也叫Servlet容器
1. 生命周期
生命周期方法:
- void init(ServletConfig):出生之后(1次);
- void service(ServletRequest request, ServletResponse response):每次处理请求时都会被调用;
- void destroy():临死之前(1次);
特性:
单例,一个类只有一个对象;当然可能存在多个Servlet类!
2. 实现Servlet的方式
实现Servlet有三种方式:
- 实现javax.servlet.Servlet接口;
- 继承javax.servlet.GenericServlet类;
- 继承javax.servlet.http.HttpServlet类;
通常我们会去继承HttpServlet类来完成我们的Servlet
3. 常用方法
1. HttpServletRequest方法:
- String getParameter(String paramName):
获取指定请求参数的值; - String getMethod():
获取请求方法,例如GET或POST; - String getHeader(String name):
获取指定请求头的值; - void setCharacterEncoding(String encoding):
设置请求体的编码!因为GET请求没有请求体,所以这个方法只只对POST请求有效。当调用request.setCharacterEncoding(“utf-8”)之后,再通过getParameter()方法获取参数值时,那么参数值都已经通过了转码,即转换成了UTF-8编码。所以,这个方法必须在调用getParameter()方法之前调用! - request.getRequestDispatcher(“/Servlet
Test”).forward(request,response):转发
2. HttpServletResponse方法:
- PrintWriter getWriter():获取字符响应流,使用该流可以向客户端输出响应信息。例如
response.getWriter(). print(“<h1>Hello JavaWeb!</h1>”);
- ServletOutputStream getOutputStream():获取字节响应流,当需要向客户端响应字节数据时,需要使用这个流,例如要向客户端响应图片;
- void setCharacterEncoding(String encoding) :用来设置字符响应流的编码,例如在调用setCharacterEncoding(“utf-8”);之后,再response.getWriter()获取字符响应流对象,这时的响应流的编码为utf-8,使用response.getWriter()输出的中文都会转换成utf-8编码后发送给客户端;
- void setHeader(String name, String value) :向客户端添加响应头信息,例如
setHeader(“Refresh”, “3;url=http://www.itcast.cn”)
,表示3秒后自动刷新到http://www.itcast.cn;
- void setContentType(String contentType) :该方法是setHeader(“content-type”, “xxx”)的简便方法,即用来添加名为content-type响应头的方法。content-type响应头用来设置响应数据的MIME类型,例如要向客户端响应jpg的图片,那么可以setContentType(“image/jepg”),如果响应数据为文本类型,那么还要台同时设置编码,例如setContentType(“text/html;chartset=utf-8”)表示响应数据类型为文本类型中的html类型,并且该方法会调用setCharacterEncoding(“utf-8”)方法;
- void sendError(int code, String errorMsg) :向客户端发送状态码,以及错误消息。例如给客户端发送404:response(404, “您要查找的资源不存在!”)。
- response.sendRedirect(“http:…..”):重定向某个地址
3. ServletConfig方法:
ServletConfig对象是由服务器创建的,然后传递给Servlet的init()方法,你可以在init()方法中使用它 - String getServletName() :获取Servlet在web.xml文件中的配置名称,即
<servlet-name>
指定的名称; - ServletContext getServletContext() :用来获取ServletContext对象。
- String getInitParameter(String name) :用来获取在web.xml中配置的初始化参数,通过参数名来获取参数值;
- Enumeration getInitParameterNames() :用来获取在web.xml中配置的所有初始化参数名称;
在元素中还可以配置初始化参数:
4. Servlet与线程安全
因为一个类型的Servlet只有一个实例对象,那么就有可能会现时出一个Servlet同时处理多个请求,那么Servlet是否为线程安全的呢?答案是:“不是线程安全的”。这说明Servlet的工作效率很高,但也存在线程安全问题!
所以我们不应该在Servlet中便宜创建成员变量,因为可能会存在一个线程对这个成员变量进行写操作,另一个线程对这个成员变量进行读操作。
5. ServletContext概述
服务器会为每个应用创建一个ServletContext对象:
- ServletContext对象的创建是在服务器启动时完成的;
- ServletContext对象的销毁是在服务器关闭时完成的。
ServletContext是JavaWeb四大域对象之一:
- PageContext;
- ServletRequest;
- HttpSession;
- ServletContext;
所有域对象都有存取数据的功能,因为域对象内部有一个Map,用来存储数据
setAttribute()、getAttribute()、removeAttribute()
6. 请求转发与重定向比较
- 请求转发是一个请求,而重定向是两个请求;
- 请求转发后浏览器地址栏不会有变化,而重定向会有变化,因为重定向是两个请求;
- 请求转发的目标只能是本应用中的资源,重定向的目标可以是其他应用;
- 请求转发对AServlet和BServlet的请求方法是相同的,即要么都是GET,要么都是POST,因为请求转发是一个请求;
- 重定向的第二个请求一定是GET;
6. 其他
配置servlet初始化时间
<load-on-startup>3</load-on-startup>
通配符使用
<url-pattern>/servlet/*<url-patter>:/servlet/a、/servlet/b,都匹配/servlet/*;
<url-pattern>*.do</url-pattern>:/abc/def/ghi.do、/a.do,都匹配*.do;
<url-pattern>/*<url-pattern>:匹配所有URL;
配置默认参数1
2
3
4<context-param>
<param-name>paramName1</param-name>
<param-value>paramValue1</param-value>
</context-param>
获取值:this.getServletContext().
getInitParameter("paramName1");
获取真实路径servletContext.getRealPath(“/WEB-INF/b.txt”);
读文件InputStream in = servletContext.
getResourceAsStream(“/WEB-INF/b.txt”);
Class类的getResourceAsStream(String path)
- 路径以“/”开头,相对classes路径;
- 路径不以“/”开头,相对当前class文件所有路径,例如在cn.itcast.servlet.MyServlet中执行,那么相对/classes/cn/itcast/servlet/路径;
ClassLoader类的getResourceAsStream(String path)
- 相对classes路径;
get请求乱码name = new String(name.getBytes(“iso-8859-1”), “utf-8”);
post乱码request.setCharacterEncoding(“utf-8”);
String name = request.getParameter(“name”);
JSP
jsp其实就是一个servlet,jsp第一次被请求的时候,会编译成class文件,然后再去执行servlet
1. JSP脚本
- <%…%>:Java语句;
- <%=…%>:Java表达式;
- <%!…%>:Java定义类成员;
其中<%=…%>与out.print()功能是相同的!它们都是向客户端输出,例如:
<%=s1%>等同于<% out.print(s1); %>
2. JSP指令
JSP中有三大指令:page、include、taglib,page最常用。
page指令1
<%true” buffer=”64kb” autoFlush=”true” isThreadSafe=”true” info=”text” errorPage=”error.jsp” isErrorPage=”true” isELIgnored=”true” pageEncoding=”gb2312” import=”java.sql.*”%> language=”java” contenType=”text/html;charset=gb2312” session=”
web.xml也可以配置错误页面1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16<error-page>
<error-code>404</error-code>
<location>/error404.jsp</location>
</error-page>
<error-page>
<error-code>500</error-code>
<location>/error500.jsp</location>
</error-page>
<error-page>
<exception-type>java.lang.RuntimeException</exception-type>
<location>/error.jsp</location>
</error-page>
其他指令<%@include file="filename.jsp"%>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
九大内置对象
- out(JspWriter):等同与response.getWriter(),用来向客户端发送文本数据;
- config(ServletConfig):对应“真身”中的ServletConfig;
- page(当前JSP的真身类型):当前JSP页面的“this”,即当前对象;
- pageContext(PageContext):页面上下文对象。
- exception(Throwable):只有在错误页面中可以使用这个对象;
- request(HttpServletRequest):即HttpServletRequest类的对象;
- response(HttpServletResponse):即HttpServletResponse类的对象;
- application(ServletContext):即ServletContext类的对象;
- session(HttpSession):即HttpSession类的对象,不是每个JSP页面中都可以使用,如果在某个JSP页面中设置
<%@page session=”false”%>
,说明这个页面不能使用session。
JSP动作标签
include指令是在编译级别完成的包含,即把当前JSP和被包含的JSP合并成一个JSP,然后再编译成一个Servlet。
include动作标签是在运行级别完成的包含,即当前JSP和被包含的JSP都会各自生成Servlet,然后在执行当前JSP的Servlet时完成包含另一个JSP的Servlet。它与RequestDispatcher的include()方法是相同的!
forward标签的作用是请求转发!forward标签的作用与RequestDispatcher.forward()方法相同
1 | <jsp:include page="/b.jsp"> |
EL表达式
判断字符串长度是否为0或者集合为0${empty “”}
集合对象:${map.key}、${map[‘key’]}
内置对象访问
- pageScope:${pageScope.name}等同与pageContext.getAttribute(“name”);
- requestScope:${requestScope.name}等同与request.getAttribute(“name”);
- sessionScoep: ${sessionScope.name}等同与session.getAttribute(“name”);
- applicationScope:${applicationScope.name}等同与application.getAttribute(“name”);
其他对象
- ${header.Host}
- ${initParam.par} //获取web.xml配的参数
- ${cookie.}
- ${pageContext.servletContext.serverInfo}
- ${pageContext.session.id}
- ${pageContext.request.requestURL}
JSTL表达式
out和set
<c:out value=”${aaa}” default=”xxx”/>
等于${aaa}c:set var=”a” value=”hello” scope=”session”/>
在session中添加name为a,value为hello的数据。remove
<c:remove var="a"/>
删除所有域中name为a的数据!也可以指定:scope=”page”url
c:url value="/"/>
输入当前项目路径 如:test/if
1
2
3
4<c:set var="a" value="hello"/>
<c:if test="${not empty a }">
<c:out value="${a }"/>
</c:if>choose
1
2
3
4
5
6
7<c:set var="score" value="${param.score }"/>
<c:choose>
<c:when test="${score > 100 || score < 0}">错误的分数:${score }</c:when>
<c:when test="${score >= 90 }">A级</c:when>
<c:when test="${score >= 60 }">D级</c:when>
<c:otherwise>E级</c:otherwise>
</c:choose>forEach
1
2
3
4<c:set var="sum" value="0" />
<c:forEach var="i" begin="1" end="10" step="2">
<c:set var="sum" value="${sum + i}" />
</c:forEach>
1 | <c:forEach var="item" items="${ns }"> |
1 | <c:forEach var="item" items="${ns }" varStatus="vs"> |
- count:int类型,当前以遍历元素的个数;
- index:int类型,当前元素的下标;
- first:boolean类型,是否为第一个元素;
- last:boolean类型,是否为最后一个元素;
- current:Object类型,表示当前项目。
fmt标签库常用标签<fmt:formatDate value="${d }" pattern="yyyy-MM-dd HH:mm:ss"/>
<fmt:formatNumber value="${d2 }" pattern="#.##"/>
<fmt:formatNumber value="${d1 }" pattern="0.00"/>
其他
在请求url后加上sessionId<%=response.encodeURL
("/test/index.jsp") %>
href='/test/index.jsp;jsessionid=<%=session.getId() %>'
JDBC和数据库
1. JDBC
六大步骤
- 加载驱动类
Class.forName ("oracle.jdbc.OracleDriver");
- 建立连接
Connection conn = DriverManager. getConnection(url,name,password);
- 创建statement
Statement state = conn.createStatement();
- 执行SQL语句
String sql = "select id,name from s_emp"; ResultSet rs = state.executeQuery(sql);
处理结果集
1
2
3
4
5
6while(rs.next()){
int id = rs.getInt("id");
String name = rs.getString(2);
System.out.println(id+" "+name);
}关闭连接
rs.close(); state.close(); conn.close();
PreparedStatement与Statement区别
PreparedStatement叫预编译声明!
PreparedStatement是Statement的子接口,你可以使用PreparedStatement来替换Statement。
PreparedStatement的好处:
- 防止SQL攻击;
- 提高代码的可读性,以可维护性;
- 提高效率。
预处理的原理
每个pstmt都与一个sql模板绑定在一起,先把sql模板给数据库,数据库先进行校验,再进行编译。执行时只是把参数传递过去而已!
若二次执行时,就不用再次校验语法,也不用再次编译!直接执行!
JDBC中的事务
Connection的三个方法与事务相关:
- setAutoCommit(boolean):设置是否为自动提交事务,如果true(默认值就是true)表示自动提交,也就是每条执行的SQL语句都是一个单独的事务,如果设置false,那么就相当于开启了事务了;con.setAutoCommit(false)表示开启事务!
- commit():提交结束事务;
- rollback():回滚结束事务。
2. 事务
事务的四大特性是:
- 原子性(Atomicity):事务中所有操作是不可再分割的原子单位。事务中所有操作要么全部执行成功,要么全部执行失败。
- 一致性(Consistency):事务执行后,数据库状态与其它业务规则保持一致。如转账业务,无论事务执行成功与否,参与转账的两个账号余额之和应该是不变的。
- 隔离性(Isolation):隔离性是指在并发操作中,不同事务之间应该隔离开来,使每个并发中的事务不会相互干扰。
- 持久性(Durability):一旦事务提交成功,事务中所有的数据操作都必须被持久化到数据库中,即使提交事务后,数据库马上崩溃,在数据库重启时,也必须能保证通过某种机制恢复数据。
开启事务:start transaction;
结束事务:commit或rollback。
事务的并发读问题
- 脏读:读取到另一个事务未提交数据;
- 不可重复读:两次读取不一致(读取到了另一事务的更新;)
- 幻读(虚读):读到另一事务已提交数据。
四大隔离级别
SERIALIZABLE(串行化)
不会出现任何并发问题,因为它是对同一数据的访问是串行的,非并发访问的;
性能最差;三种问题都能处理REPEATABLE READ(可重复读)(MySQL)
防止脏读和不可重复读,不能处理幻读问题;
性能比SERIALIZABLE好READ COMMITTED(读已提交数据)(Oracle)
防止脏读,没有处理不可重复读,也没有处理幻读;
性能比REPEATABLE READ好READ UNCOMMITTED(读未提交数据)
可能出现任何事务并发问题
性能最好
MySQL的默认隔离级别为REPEATABLE READ。
3. 连接池
4. 监听器、过滤器、拦截器
- 拦截器是基于java的反射机制的,而过滤器是基于函数回调
- 过滤器依赖与servlet容器,而拦截器不依赖与servlet容器
- 拦截器是AOP的一种实现
过滤器的生命周期
- init(FilterConfig):服务器启动创建Filter实例,并且每个类型的Filter只创建一个实例,创建完Filter实例后,这个方法只会被执行一次;
- doFilter(ServletRequest req,ServletResponse res,FilterChain chain):这个方法会在用户每次访问就会执行。
- destroy():服务器会在创建Filter对象之后,把Filter放到缓存中一直使用,通常不会销毁它。一般会在服务器关闭时销毁Filter对象,在销毁Filter对象之前,服务器会调用Filter对象的destory()方法。
配置过滤器拦截方式:REQUEST、FORWARD、INCLUDE、ERROR。1
2
3
4
5
6<filter-mapping>
<filter-name>myfilter</filter-name>
<url-pattern>/b.jsp</url-pattern>
<dispatcher>REQUEST</dispatcher>
<dispatcher>FORWARD</dispatcher>
</filter-mapping>