Servlet
简单使用
Servlet本身是一组接口,在javax
包中,创建实现该接口的Java类就具备了接收客户端请求并响应的功能,在客户端访问Tomcat时,会执行Servlet类中的service()
方法,使用service()
方法中的servletRequest
对象和servletResponse
对象进行客户端和服务端之间的交互
Tomcat采用Servlet类懒加载策略,访问的详细步骤如下:
- 未访问时并不会创建Servlet类对象,而是等到访问时才会创建Servlet对象,利用反射机制执行无参构造方法,之后再执行
init()
方法进行初始化(只会执行一次) - 再执行
service()
方法与客户端进行交互(会执行多出) - 再Tomcat服务器关闭时Servlet类对象会被销毁,在销毁前执行
destroy()
方法
package com;
import javax.servlet.*;
import java.io.IOException;
import java.io.PrintWriter;
public class MyServlet implements Servlet {
public MyServlet() {
System.out.println("MyServlet 实例被创建");
}
@Override
public void init(ServletConfig servletConfig) throws ServletException {
System.out.println("servlet 初始化");
}
@Override
public ServletConfig getServletConfig() {
return null;
}
@Override
public void service(ServletRequest servletRequest, ServletResponse servletResponse) throws ServletException, IOException {
String id = servletRequest.getParameter("id"); //获取请求query参数
System.out.println("server " + id + " : 你好,世界"); //服务端输出
servletResponse.setContentType("text/html;charset=UTF-8"); //设置响应头的编码,在响应前设置
PrintWriter writer = servletResponse.getWriter(); //获取打印输出流对象
writer.write("client " + id + " 你好,世界"); //向客户端输出内容
}
@Override
public String getServletInfo() {
return null;
}
@Override
public void destroy() {
System.out.println("servlet 销毁");
}
}
因为Servlet类在编译时会被打包到WEB-INF/classes
目录中,所以无法直接访问,需要在web.xml
文件中进一步进行配置,之后就可以通过浏览器访问了
<servlet> <!--用于servlet取别名,保护真正的servlet类-->
<servlet-name>MyServlet</servlet-name> <!--servlet类的别名,可任意取,一般使用简单类名-->
<servlet-class>com.MyServlet</servlet-class> <!--servlet类路径,即全类名-->
</servlet>
<servlet-mapping> <!--用于映射servlet类和URL之间的关系-->
<servlet-name>MyServlet</servlet-name> <!--servlet中定义的别名-->
<url-pattern>/myservlet</url-pattern> <!--servlet映射的全URL路径-->
</servlet-mapping>
Servlet的其他方法
package com;
import javax.servlet.*;
import java.io.IOException;
import java.util.Enumeration;
public class MyServlet implements Servlet {
private ServletConfig servletConfig; //再init()方法中为配置对象赋值,getServletConfig()方法中返回
@Override
public void init(ServletConfig servletConfig) throws ServletException {
this.servletConfig = servletConfig; //为servletConfig对象赋值
System.out.println("*************************************单个servlet配置对象**********************************");
String servletName = servletConfig.getServletName();
System.out.println("servletConfig.getServletName() ==> " + servletName); //返回该Servlet的全类名
/* 获取参数 */
String username = servletConfig.getInitParameter("username"); //获取web.xml中配置的init-param标签中的参数
System.out.println("servletConfig.getInitParameter() ==> " + username); //返回param-name为username的param-value中的值
Enumeration<String> enumeration = servletConfig.getInitParameterNames(); //获取全部配置参数的param-name中的值
System.out.print("servletConfig.getInitParameterNames() ==> ");
while (enumeration.hasMoreElements()) { //Enumeration类与Set类似,但可以直接遍历
String paramName = enumeration.nextElement();
String initParameter = servletConfig.getInitParameter(paramName); //使用getInitParameter()方法获取param-value中的值
System.out.print(paramName + " : " + initParameter + "、 ");
}
System.out.println();
System.out.println("*************************************全局Context配置对象**********************************");
ServletContext servletContext = servletConfig.getServletContext(); //获取全局的servletContext对象
String contextPath = servletContext.getContextPath();//获取项目部署路径,即前缀URL,若是 / 则返回空字符串
System.out.println("servletContext.getContextPath() ==> " + contextPath);
String serverInfo = servletContext.getServerInfo(); //获取服务器名和版本信息
System.out.println("servletContext.getServerInfo() ==> " + serverInfo);
/* 获取参数 */
String globalUsername = servletContext.getInitParameter("globalUsername"); //获取web.xml中配置的context-param标签中的参数
System.out.println("servletContext.getInitParameter() ==> " + globalUsername); //返回param-name为username的param-value中的值
Enumeration<String> globalEnumeration = servletContext.getInitParameterNames(); //获取全部配置参数的param-name中的值
System.out.print("servletContext.getInitParameterNames() ==> ");
while (globalEnumeration.hasMoreElements()) { //Enumeration类与Set类似,但可以直接遍历
String paramName = globalEnumeration.nextElement();
String initParameter = servletContext.getInitParameter(paramName); //使用getInitParameter()方法获取param-value中的值
System.out.print(paramName + " : " + initParameter + "、 ");
}
System.out.println();
}
@Override
public ServletConfig getServletConfig() { //返回当前Servlet配置对象,用于其他方法中获取配置对象
return this.servletConfig;
}
@Override
public void service(ServletRequest servletRequest, ServletResponse servletResponse) throws ServletException, IOException {
servletResponse.getWriter().write(" hello world");
}
@Override
public String getServletInfo() { //获取当前Servlet的信息,不需要可返回空字符串
return "";
}
@Override
public void destroy() {
}
}
web.xml
中添加初始化配置参数和上下文配置参数
<context-param><!--配置全局context的参数-->
<param-name>globalUsername</param-name>
<param-value>全局张三</param-value>
</context-param>
<context-param>
<param-name>globalPassword</param-name>
<param-value>全局123</param-value>
</context-param>
<servlet> <!--用于servlet取别名,保护真正的servlet类-->
<servlet-name>MyServlet</servlet-name> <!--servlet类的别名,可任意取,一般使用简单类名-->
<servlet-class>com.MyServlet</servlet-class> <!--servlet类路径,即全类名-->
<init-param> <!--配置Servlet类初始化时的参数-->
<param-name>username</param-name>
<param-value>张三</param-value>
</init-param>
<init-param>
<param-name>password</param-name>
<param-value>123</param-value>
</init-param>
</servlet>
<servlet-mapping> <!--用于映射servlet类和URL之间的关系-->
<servlet-name>MyServlet</servlet-name> <!--servlet中定义的别名-->
<url-pattern>/myservlet</url-pattern> <!--servlet映射的全URL路径-->
</servlet-mapping>
Servlet的通常用法
- 使用
web.xml
配置URL路径的方式过于繁琐,所以提供了@WebServlet()
注解配置URL路径的方式,注解方式提供配置参数过于繁琐,但是大多数情况下也不需要参数 - 通常都只需要
service()
方法处理即可,service()
方法中的ServletRequest
类对象也无法直接获取请求方法,需要向下转型为HttpServletRequest
对象才行,所以通常使用Servlet时使用的是继承HttpServlet
类,选择性的复写方法 HttpServlet
是对Servlet的进一步封装,请求和响应对象是HttpServletRequest
和HttpServletResponse
对象,进一步封装了请求响应对象
package com;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
@WebServlet("/myservlet")
public class MyServlet extends HttpServlet {
// @Override
// protected void service(HttpServletRequest request, HttpServletResponse response) throws IOException {
// //处理任意请求,使用该方法后doPost()和doGet()方法将不会执行
// response.getWriter().write(" service: hello world!!!");
// response.getWriter().write(request.getMethod());
// }
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws IOException {
//处理post请求
response.getWriter().write(" Post: hello world");
}
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws IOException {
//处理get请求
response.getWriter().write(" Get: hello world");
}
}
以下是测试用的index.jsp
,注意要加项目前缀
<form action="/javaWeb/myservlet" method="get">
<input type="submit" value="get提交"/>
</form>
<form action="/javaWeb/myservlet" method="post">
<input type="submit" value="post提交"/>
</form>
动态路由模拟
@WebServlet("/myservlet/*") //使用星号通配符
public class MyServlet extends HttpServlet {
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws IOException {
String url = request.getRequestURL().toString(); //获取请求URL地址
String urlDecode = URLDecoder.decode(url, "utf-8"); //对URL百分号转义进行解码
String parameter = urlDecode.substring(urlDecode.lastIndexOf("/") + 1); //截取最后一个反斜杠后的参数
response.setContentType("text/html;charset=utf-8");
response.getWriter().write(parameter);
}
}
请求参数(解决中文乱码)
对于Post请求,在解析参数前需要添加request.setCharacterEncoding("UTF-8");
设置,否则中文会乱码,因为默认请求体编码是ISO-8859-1
对于Get请求,Tomcat8及以上版本Get请求参数默认就是UTF-8,Tomcat7需要在Tomcat的server.xml配置文件中找到这项配置:<Connector port="8080" protocol="HTTP/1.1" connectionTimeout="20000" redirectPort="8443" />
,添加一个属性字段URLEncoding="UTF-8"
,改为这样<Connector port="8080" protocol="HTTP/1.1" connectionTimeout="20000" redirectPort="8443" URLEncoding="UTF-8" />
即可
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException {
//访问 http://localhost:8080/javaWeb/myservlet?id=1&id=2
//request.setCharacterEncoding("UTF-8"); //对于post请求来说需要加上该设置否则中文会乱码
PrintWriter out = response.getWriter();
String id = request.getParameter("id"); //若有多个只会返回第一个
out.println(id);
String[] ids = request.getParameterValues("id"); //返回所有该参数的值
out.println(Arrays.toString(ids));
}
Cookie
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException {
PrintWriter out = response.getWriter();
Cookie[] cookies = request.getCookies() == null ? new Cookie[]{} : request.getCookies();
//获取所有cookie,若该返回null则替换为空Cookie数组,避免空指针异常
for (Cookie cookie : cookies) { //遍历所有cookie
out.println(cookie.getName() + " : " + cookie.getValue());
}
Cookie cookie = new Cookie("key", "value"); //创建cookie对象
cookie.setMaxAge(30); //设置cookie失效,单位秒,不设置默认是session级别的
cookie.setHttpOnly(true); //设置httpOnly,默认是false
response.addCookie(cookie); //将cookie对象添加到响应对象中
}
请求转发和重定向
在请求对象上设置的自定义属性仅本次请求有效
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException, ServletException {
request.setAttribute("key", "value"); //设置自定义属性,属性值可以是任意数据类型
String key = (String) request.getAttribute("key"); //获取自定义属性,返回Object,需强制类型转化
request.getRequestDispatcher(request.getContextPath() + "/index.jsp").forward(request, response); //请求转发,使用forward方法将请求响应对象转发
response.sendRedirect(request.getContextPath() + "/index.jsp"); //请求重定向
//通常会使用request.getContextPath()进行项目名的拼接
}
请求转发 | 响应重定向 |
---|---|
使用request 对象的方法 |
使用response 对象方法 |
一次请求,服务器内转发 | 两次请求,客户端重新请求 |
一次请求,所以可使用setAttribute() 和getAttribute() 方法挂自定义属性( getAttribute() 方法返回Object对象,需要强制类型转化) |
两次请求,所以setAttribute() 和getAttribute() 方法挂自定义属性不会传递到重定向的URL而返回null( getAttribute() 方法返回Object对象,需要强制类型转化) |
地址栏不会改变 | 地址栏会改变 |
Session
在session对象上设置的自定义属性仅本次会话有效
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException {
HttpSession session = request.getSession(); //获取session对象
session.setMaxInactiveInterval(10); //session为操作失效时间间隔,单位秒,默认30分钟
session.setAttribute("key", "value"); //与request.setAttribute()用法一致
//session.invalidate(); //使当前session失效
String key = (String) session.getAttribute("key"); //与request.getAttribute()用法一致
}
ServletContext
在ServletContext对象上设置的自定义属性在整个程序运行中都有效
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException {
ServletContext servletContext = request.getServletContext(); //获取ServletContext对象
servletContext.setAttribute("key", "value"); //与request.setAttribute()用法一致
String key = (String) servletContext.getAttribute("key"); //与request.getAttribute()用法一致
}
Servlet预加载
Servlet预加载通常用于数据库导入等费时操作,一般不需要对应URL,只是用于初始化,所以配置URL就不要与其他配置冲突即可
web.xml
配置方式:
<servlet> <!--用于servlet取别名,保护真正的servlet类-->
<servlet-name>MyServlet</servlet-name> <!--servlet类的别名,可任意取,一般使用简单类名-->
<servlet-class>com.MyServlet</servlet-class> <!--servlet类路径,即全类名-->
<load-on-startup>0</load-on-startup> <!--打破servlet懒加载,使用预预加载,值越大越先加载-->
</servlet>
<servlet-mapping> <!--用于映射servlet类和URL之间的关系-->
<servlet-name>MyServlet</servlet-name> <!--servlet中定义的别名-->
<url-pattern>/myservlet</url-pattern> <!--servlet映射的全URL路径-->
</servlet-mapping>
注解配置方式:
@WebServlet(urlPatterns = "/myservlet",loadOnStartup = 0)
web.xml中的其他配置
<welcome-file-list> <!--配置当URL以 / 结尾,则自动按顺序配匹下面文件名-->
<welcome-file>index.html</welcome-file> <!--默认就是这些配置,一般不用配置-->
<welcome-file>index.jsp</welcome-file>
</welcome-file-list>
<error-page> <!--线上环境下需要配置404和500等错误页面,以免暴露出程序的漏洞-->
<error-code>404</error-code>
<location>/error/404.html</location>
</error-page>
<error-page>
<error-code>500</error-code>
<location>/error/500.html</location>
</error-page>
Comments NOTHING