1. Servlet 简介
JavaSE:
java标准版本,Sun公司为java程序员提供的一套基础类库。这套类库主要包括,基本语法、异常、IO、集合、反射、线程等。
JavaEE:
java企业版,Sun公司为java程序员准备的另一套庞大的类库,帮助程序员完成企业级项目开发。(高效、稳定)
Servlet:
是JavaEE13大子规范中的一个规范(接口:解耦合、可插拔),主要功能在于交互式的浏览和生成数据,用于开发动态web 的一门技术。
2. Servlet 实现
2.1 实现Servlet 接口
(直接实现Servlet 接口的方式,实际开发并不常用,作为了解)
2.1.1 创建类并实现Servlet 接口
public class HelloServlet implements Servlet{
// 若希望在销毁时刻执行一段特殊代码,编写在此处,自动被容器调用,且只执行一次
// destory方法执行的时候,Servlet对象还没有被销毁,而是即将被销毁
@Override
public void destroy() {
}
// 为子类提供ServletConfig方法
@Override
public ServletConfig getServletConfig() {
return null;
}
@Override
public String getServletInfo() {
return null;
}
// 若希望初始化时执行一段特殊程序,可以编写在此方法内,会自动被调用,且只执行一次
// 注意:init方法执行时,Servlet对象已经被创建好了
// ServletConfig:是一个Servlet 对象的配置信息对象,web.xml文件中配置的信息,一个Servlet对象对应一个ServletConfig对象
@Override
public void init(ServletConfig config) throws ServletException {
}
// 将业务逻辑重写到该方法中,请求处理、完成响应。
@Override
public void service(ServletRequest req, ServletResponse res) throws ServletException, IOException {
// 请求分发
HttpServletRequest httpServletRequest = (HttpServletRequest)req;
String method = httpServletRequest.getMethod();
if("GET".equals(method)){
System.out.println("GET");
}else {
System.out.println("POST");
}
}
}
2.1.2 映射
Web.xml 文件中配置servlet 映射
<servlet>
<servlet-name>helloServlet</servlet-name>
<servlet-class>com.chengyu.servlet.HelloServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>helloServlet</servlet-name>
<url-pattern>/hello</url-pattern>
</servlet-mapping>
2.1.3 JSP界面访问Servlet
<form action="hello" method="POST">
<input type="submit"/>
</form>
2.2 继承HttpServlet 类
2.2.1 创建类并继承HttpServlet 类
public class HelloHttpServlet extends HttpServlet{
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
System.out.println("HelloHttpServlet doGet()");
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
System.out.println("HelloHttpServlet doPost()");
}
}
2.2.2 映射
<servlet>
<servlet-name>helloHttpServlet</servlet-name>
<servlet-class>com.chengyu.servlet.HelloHttpServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>helloHttpServlet</servlet-name>
<url-pattern>/hello2</url-pattern>
</servlet-mapping>
2.2.3 JSP界面访问Servlet
<form action="hello2" method="POST">
<input type="submit"/>
</form>
2.3 补充
1)若希望在web服务器启动阶段实例化Servlet对象,需要在web.xml中配置如下内容:
<servlet>
<servlet-name>helloServlet</servlet-name>
<servlet-class>my.service.factory.myServlet</servlet-class>
<load-on-startup>N</load-on-startup>
</servlet>
N 处设置自然数,越小级别越高,可直接设置为 1;
2)web.xml文件只能有一个,服务器启动阶段被解析;
3)tomcat服务器是一个实现了Servlet规范和JSP规范的容器。
3. Servlet 生命周期
用户发送URL 请求,web 容器截取到请求路径后,在上下文中查找对应的Servlet 对象;
若没有,则根据web.xml 文件中的配置信息,通过反射技术调用Servlet 类的无参构造器,完成Servlet 对象的创建,web 容器调用Servlet 对象的init() 进行初始化,调用 service() 提供服务;
若已存在,web 容器直接调用 Servlet 对象的 service() 提供服务;
web 容器关闭、webapp 重新部署、该Servlet 对象长时间没有再次被访问的情况下,web 容器会将Servlet 对象销毁,销毁前调用该对象的 destory() 完成销毁前的准备。
4. ServletContext 对象
web 容器启动的时候,会创建一个ServletContext 对象,它代表了当前web 应用,服务关闭时被销毁。
4.1 servlet 之间共享数据
public class ServletContext1 extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
ServletContext context = this.getServletContext();
String name = "chengyu";
context.setAttribute("name",name);
}
}
public class ServletContext2 extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
ServletContext context = this.getServletContext();
String name = (String) context.getAttribute("name");
System.out.println(name);
}
}
4.2 获取xml 中初始化参数
web.xml
<context-param>
<param-name>url</param-name>
<param-value>www.baidu.com</param-value>
</context-param>
<servlet>
<servlet-name>paramServlet</servlet-name>
<servlet-class>com.chengyu.controller.ContextTest</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>paramServlet</servlet-name>
<url-pattern>/getparam</url-pattern>
</servlet-mapping>
public class ContextTest extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
ServletContext context = this.getServletContext();
String url = context.getInitParameter("url");
System.out.println(url);
}
}
4.3 请求转发
public class ContextTest extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
ServletContext context = this.getServletContext();
context.getRequestDispatcher("/servlet2").forward(req, resp);
}
}
ServletContext 的请求转发应用并不多,请求转发的详细介绍参考 5.2。
4.4 读取资源文件
WEB-INF/db.properties
username=root
password=tiger
public class ContextTest extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
ServletContext context = this.getServletContext();
InputStream is = context.getResourceAsStream("/WEB-INF/db.properties");
Properties properties = new Properties();
properties.load(is);
String username = properties.getProperty("username");
System.out.println(username);
}
}
5. 请求信息 HttpServletRequest
5.1 获取页面参数
1)页面
<form action="helloApi" method="POST">
姓名:<input type="text" name="username"><br/>
年龄:<input type="text" name="userage"><br/>
技术:<input type="checkbox" name="study" value="java">java <input type="checkbox" name="study" value="C#">C# <br/>
<input type="submit"/>
</form>
2)Web.xml
<servlet>
<servlet-name>HttpServletRequestApi</servlet-name>
<servlet-class>com.chengyu.servlet.HttpServletRequestAPI</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>HttpServletRequestApi</servlet-name>
<url-pattern>/helloApi</url-pattern>
</servlet-mapping>
3)获取参数
// 获取请求参数
System.out.println("username ⇒ " + req.getParameter("username"));
System.out.println("userage ⇒ " + req.getParameter("userage"));
// 获取请求参数(多个值)
String[] study = req.getParameterValues("study");
System.out.println("study ⇒ " + Arrays.asList(study));
补充:
POST 请求时,会出现中文乱码,解决方法如下
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse res) throws ServletException, IOException {
System.out.println("========doPost=======");
req.setCharacterEncoding("UTF-8");
// 获取请求参数
System.out.println("username ⇒ " + req.getParameter("username"));
}
5.2 请求转发
Servlet1 调取Servlet2
1)创建Servlet1
public class Servlet1 extends HttpServlet{
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
System.out.println("====== Servlet1 doGet ======");
String username = req.getParameter("username");
System.out.println("Servlet1 获取用户名:" + username);
req.setAttribute("key", "OK");
// RequestDispatcher requestDispatcher = req.getRequestDispatcher("/servlet2");
// requestDispatcher.forward(req, resp);
req.getRequestDispatcher("/servlet2").forward(req, resp);
// 也可转发到页面
// req.getRequestDispatcher("/index.jsp").forward(req, resp);
}
}
2)创建Servlet2
public class Servlet2 extends HttpServlet{
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
System.out.println("====== Servlet2 doGet ======");
String username = req.getParameter("username");
String key = (String)req.getAttribute("key");
System.out.println("Servlet2 获取Servlet1参数:" + key);
}
}
3)Web.xml
<servlet>
<servlet-name>Servlet1</servlet-name>
<servlet-class>com.chengyu.servlet.Servlet1</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>Servlet1</servlet-name>
<url-pattern>/servlet1</url-pattern>
</servlet-mapping>
<servlet>
<servlet-name>Servlet2</servlet-name>
<servlet-class>com.chengyu.servlet.Servlet2</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>Servlet2</servlet-name>
<url-pattern>/servlet2</url-pattern>
</servlet-mapping>
4)index1.jsp
<body>
<form action="servlet1" method="Get">
姓名:<input type="text" name="username"><br/>
<input type="submit"/>
</form>
</body>
5)URL
http://localhost:8080/WebProjectTest04/index1.jsp
特点:
① 浏览器地址栏不变;
② 是一次请求;
③ 共享Reqest 域中的数据;
④ 可以转发到WEB-INF 目录下(WEB-INF 目录不可直接用浏览器访问)。
5.3 补充路径问题 ※
【.】:当前目录
【…】:上一级目录
【/】:将路径映射到【WebContent】目录下
页面访问Servlet 时,不用加【/】,因为页面在【WebContent】下。例,action=“servlet1”
Java 中访问Servlet 时,用加【/】。例,req.getRequestDispatcher("/servlet1").forward(req, resp);
Web端解析到端口:
如,<… href="/"> 浏览器解析为:http://localhost:8080/
服务器端解析到工程:
如servletContext.getReadPath("/"):服务器端解析为:http://localhost:8080/WebProjectTest04
特例:response.sendRedirect("/"),虽然在服务器端,但将斜杠发送给浏览器解析,得到http://localhost:8080/
相对路径在工作时会参照当前浏览器地址进行跳转,如果设置了base标签,则会参照地址base标签设置的地址。
<head>
<base href="http://localhost:8080/WebProjectTest04/a/b/c.jsp">
</head>
<body>
<a href="../../index.jsp">返回</a>
</body>
http://localhost:8080/WebProjectTest04/a/b/c.jsp
…/…/index.jsp
http://localhost:8080/WebProjectTest04/index.jsp
注意:【c.jsp】不算地址
6. 响应信息 HttpServletResponse
6.1 设置响应信息
字节流getOutputStream():下载,传递二进制数据;
字符流getWrite():回传字符串(常用);
注意:两者不能同时使用。
public class HttpServletResponse extends HttpServlet{
@Override
protected void doGet(HttpServletRequest req, javax.servlet.http.HttpServletResponse resp)
throws ServletException, IOException {
PrintWriter writer = resp.getWriter();
writer.write("response information!!!");
}
}
6.2 乱码问题
req.setCharacterEncoding("UTF-8");
resp.setContentType("text/html;charset=utf-8");
6.3 请求重定向
客户端给服务器发请求,服务器提供新地址,然后去新地址访问。
public class Response1 extends HttpServlet{
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
System.out.println("====== HttpServletResponse1 ======");
// 重定向状态吗
// resp.setStatus(302);
// resp.setHeader("Location", "http://localhost:8080/WebProjectTest04/response2");
resp.sendRedirect("http://localhost:8080/WebProjectTest04/response2");
}
}
public class Response2 extends HttpServlet{
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
resp.getWriter().write("====== HttpServletResponse2 ======");
}
}
特点:
① 浏览器地址发生变化;
② 发生两次请求;
③`不共享response 数据;
④ 不能访问WEB-INF目录资源;
⑤ 可以访问当前工程以外的资源。
7. 过滤器 Filter
实现Filter 接口:
利用过滤器处理乱码
public class FilterDemo01 implements Filter {
// 初始化:Web服务器启动时调用
@Override
public void init(FilterConfig filterConfig) throws ServletException {
System.out.println("init");
}
@Override
public void doFilter(ServletRequest req, ServletResponse resp, FilterChain filterChain) throws IOException, ServletException {
System.out.println("doFilter");
req.setCharacterEncoding("UTF-8");
resp.setContentType("text/html;charset=utf-8");
// 过滤器继续执行
filterChain.doFilter(req,resp);
}
// 销毁:Web服务器关闭时调用
@Override
public void destroy() {
System.out.println("destroy");
}
}
有乱码的代码:
public class ShowServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
System.out.println("HelloHttpServlet doGet()");
resp.getWriter().write("你好");
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
System.out.println("HelloHttpServlet doPost()");
}
}
web.xml
<filter>
<filter-name>demo01</filter-name>
<filter-class>com.cheng.FilterDemo01</filter-class>
</filter>
<filter-mapping>
<filter-name>demo01</filter-name>
<!--过滤的请求-->
<url-pattern>/servlet/*</url-pattern>
</filter-mapping>
<servlet>
<servlet-name>Servlet</servlet-name>
<servlet-class>com.cheng.ShowServlet</servlet-class>
</servlet>
<!--走过滤器-->
<servlet-mapping>
<servlet-name>Servlet</servlet-name>
<url-pattern>/servlet/show</url-pattern>
</servlet-mapping>
<!--不走过滤器-->
<servlet-mapping>
<servlet-name>Servlet</servlet-name>
<url-pattern>/show</url-pattern>
</servlet-mapping>
8. 监听器 Listener
实现监听器接口:
public class ListenerDemo implements HttpSessionListener {
// 创建Session 监听:一旦创建Session 就会触发一次这个事件
@Override
public void sessionCreated(HttpSessionEvent se) {
ServletContext context = se.getSession().getServletContext();
Integer count = (Integer) context.getAttribute("OnlineCount");
if(count == null){
count = new Integer(1);
}else {
int countInt = count.intValue();
count = new Integer(countInt ++);
}
context.setAttribute("OnlineCount",count);
}
// 销毁Session 监听:
@Override
public void sessionDestroyed(HttpSessionEvent httpSessionEvent) {
}
}
index.jsp
<listener>
<listener-class>com.cheng.ListenerDemo</listener-class>
</listener>
页面
<html>
<head>
<title>$Title$</title>
</head>
<body>
<h1><%= this.getServletConfig().getServletContext().getAttribute("OnlineCount")%></h1>
</body>
</html>