Java之Servlet
Servlet
sun公司基于java开发动态web的一门技术
其中在这些API中提供了一个接口就是Servlet
servlet有两个实现类,HttpServlet、GenericServlet,其中GenericServlet是HttpServlet的父类
在servlet3.0版本后可以不用写web.xml文件,可以直接使用注解定义加载。
我们直接在类上面加入注解
@WebServlet(urlPatterns = "/demo")
HelloServlet
-
新建一个普通项目,不要勾选maven中webapp模版,并删掉项目中的src目录,这个空工程就是Maven父工程
-
在pom.xml中导入依赖
<!-- https://mvnrepository.com/artifact/javax.servlet/javax.servlet-api --> <dependency> <groupId>javax.servlet</groupId> <artifactId>javax.servlet-api</artifactId> <version>4.0.1</version> </dependency> <!-- https://mvnrepository.com/artifact/javax.servlet.jsp/javax.servlet.jsp-api --> <dependency> <groupId>javax.servlet.jsp</groupId> <artifactId>javax.servlet.jsp-api</artifactId> <version>2.3.3</version> </dependency>
如果导入失败,手动下载jar包放在本地仓库中
-
创建子modul,勾选webapp模版
-
子项目中pom.xml添加
<parent>
标签关于Maven父子工程:
在父工程的
pom.xml
中会多出<modules> <module>servlet01</module> </modules>
在子工程的
pom.xml
中会多出(Maven3.6.0之后一开始会有,项目加载完后会被自动清除掉,自己手动添加即可)<parent> <artifactId>javaweb01</artifactId> <groupId>com.zh1z3ven</groupId> <version>1.0-SNAPSHOT</version> </parent>
父项目中的jar包,子项目可以直接使用
-
修改子项目中的
web.xml
<?xml version="1.0" encoding="UTF-8" ?> <web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd" version="4.0" metadata-complete="true"> </web-app>
-
子项目中新建
java
和resources
目录,并修改对应目录属性 -
新建com.zh1z3ven.servlet包,编写servlet程序
写一个普通类,实现servlet接口
这里需要注意父项目的pom.xml文件中的
<scope>
标签,注意如果父项目存在<scope>
标签且值为provided
时,子modul无法成功引用父类jar包在Maven中依赖的域有:compile、provided、runtime、system、test、import
一、compile(默认)
当依赖的scope为compile的时候,那么当前这个依赖的包,会在编译的时候被加入进来,并且在打包(mvn package)的时候也会被加入进来。
编译范围有效,在编译与打包时都会加入进去。二、provided
当依赖的scope为provided的时候,在编译和测试的时候有效,在执行(mvn package)进行打包时不会加入。比如, 我们开发一个web应用,在编译时我们需要依赖servlet-api.jar,但是在运行时我们不需要该 jar包,因为这个jar 包已由web服务器提供,如果在打包时又被加入进去,那么就可能产生冲突。此时我们就可以使用 provided 进行范围修饰。
import javax.servlet.ServletException; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.io.IOException; import java.io.PrintWriter; public class HelloServlet extends HttpServlet { //由于get和post只是请求相互实现的方式,可以互相调用,业务逻辑都一样 @Override protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { PrintWriter writer = resp.getWriter(); //响应流 writer.print("Hello Servlet"); } @Override protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { doGet(req,resp); } }
-
修改
web.xml
注册servlet,编写servlet映射<!-- 注册servlet--> <servlet> <servlet-name>hello</servlet-name> <!-- 绑定servlet实现类--> <servlet-class>com.zh1z3ven.servlet.HelloServlet</servlet-class> </servlet> <!-- 注册servlet路由--> <servlet-mapping> <servlet-name>hello</servlet-name> <!-- 设置访问路由--> <url-pattern>/hello</url-pattern> </servlet-mapping>
-
配置Tomcat,配置完后先点击
apply
再点击ok
-
启动测试
-
关于target目录
Servlet原理
客户端请求通过浏览器发送到web服务器,请求包含请求头请求体,之后变成request对象传给servlet,servlet调用service方法处理请求并返回response。而对于Servlet来说,当服务器处理请求时,先根据URL中的路径查找是否在web.xml的<servlet-mapping>
(servlet映射)<url-pattern>
标签有对应的值,有的话根据此Servlet对应的类名,通过该类的实例化对象调用service方法处理请求并返回响应。
Servlet-Mapping
-
一个servlet可以指定一个映射路径
<servlet-mapping> <servlet-name>hello</servlet-name> <url-pattern>/hello</url-pattern> </servlet-mapping>
-
一个servlet可以指定多个映射路径
<servlet-mapping> <servlet-name>hello</servlet-name> <url-pattern>/hello</url-pattern> </servlet-mapping> <servlet-mapping> <servlet-name>hello</servlet-name> <url-pattern>/hello1</url-pattern> </servlet-mapping>
-
一个servlet可以指定通用映射路径
<servlet-mapping> <servlet-name>hello</servlet-name> <url-pattern>/hello/*</url-pattern> </servlet-mapping>
-
指定一些后缀或前缀等...,例如.do
以这个后缀结尾,不管前面uri加了多少层目录都可以访问到
<servlet-mapping> <servlet-name>hello</servlet-name> <url-pattern>*.do</url-pattern> </servlet-mapping>
-
默认请求路径
<servlet-mapping> <servlet-name>hello</servlet-name> <url-pattern>/*</url-pattern> </servlet-mapping>
处理简单报错Servlet
ErrorServelt
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;
public class ErrorServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
resp.setContentType("text/html");
resp.setCharacterEncoding("utf-8");
PrintWriter writer = resp.getWriter();
writer.print("<h1>404</h1>");
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
doGet(req,resp);
}
}
Web.xml中添加,这里<url-pattern>/*</url-pattern>
虽然设置的默认通配符,但是url输入其他注册好的servlet还是会优先走对应的servlet。指定了固定路径的映射默认优先级最高,如果找不到才会走默认的处理请求。
<servlet>
<servlet-name>error</servlet-name>
<servlet-class>com.zh1z3ven.servlet.ErrorServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>error</servlet-name>
<url-pattern>/*</url-pattern>
</servlet-mapping>
ServletContext
Web容器在启动的时候会为每个Web程序创建一个ServletContext对象,代表当前的Web应用对象。这个ServletContext可以保存一些数据,也可以供Servlet从中读取数据。类似于Servlet之间的中介,实现Servlet直接的数据共享。
ServletContext:Servlet上下文
可根据HttpServlet的实例化对象调用getServletContext获取
ServletContext servletContext = this.getServletContext();
1、共享数据
在一个servlet中创建的数据可以在另一个servlet中拿到。ServletContext对象是可以被多个Servlet共用的
写入数据
servletContext.setAttribute("username",username); //将数据写入ServletContext中
public class HelloServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
System.out.println("Hello Servlet doGet");
//this.getInitParameter() 初始化参数
//this.getServletConfig() sevlet配置
//this.getServletContext() sevlet上下文
//获得ServletContext对象
ServletContext servletContext = this.getServletContext(); //this指代当前这个类本身
String username = "zh1z3ven";
servletContext.setAttribute("username",username); //将数据写入ServletContext中
}
}
读取数据
public class GetServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
System.out.println("Hello GetServlet ");
ServletContext servletContext = this.getServletContext();
String username = (String) servletContext.getAttribute("username"); //强制类型转换
PrintWriter writer = resp.getWriter();
writer.print("<h1>username: </h1>" + username);
}
}
Web.xml
<servlet>
<servlet-name>hello</servlet-name>
<servlet-class>com.zh1z3ven.servlet.HelloServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>hello</servlet-name>
<url-pattern>/hello</url-pattern>
</servlet-mapping>
<servlet>
<servlet-name>getname</servlet-name>
<servlet-class>com.zh1z3ven.servlet.GetServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>getname</servlet-name>
<url-pattern>/getname</url-pattern>
</servlet-mapping>
测试结果
需要先访问/s2/hello,才会触发HelloServlet类中的doGet方法将name写入ServletContext中;再访问/s2/getname时触发GetServlet中的doGet方法去读取ServletContext中的name并根据代码回显到页面上。
2、获取初始化参数getInitParameter
参数为web.xml中的<context-param>
下的参数
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
ServletContext servletContext = this.getServletContext();
String url = servletContext.getInitParameter("url");
resp.setCharacterEncoding("utf-8");
resp.setContentType("text/html");
PrintWriter writer = resp.getWriter();
writer.print(url);
}
Web.xml
<context-param>
<param-name>url</param-name>
<param-value>jdbc:mysql://localhost:3306/mybatis</param-value>
</context-param>
<servlet>
<servlet-name>gp</servlet-name>
<servlet-class>com.zh1z3ven.servlet.ServletDemo03</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>gp</servlet-name>
<url-pattern>/gp</url-pattern>
</servlet-mapping>
3、请求转发requestDispatcher
获取ServletContext上下文对象,调用requestDispatcher的forward方法转发请求
RequestDispatcher requestDispatcher = servletContext.getRequestDispatcher("/gp"); //请求转发目标路径
requestDispatcher.forward(req,resp); //调用forward方法转发请求
Web.xml
<servlet>
<servlet-name>sd4</servlet-name>
<servlet-class>com.zh1z3ven.servlet.ServletDemo04</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>sd4</servlet-name>
<url-pattern>/sd4</url-pattern>
</servlet-mapping>
class
public class ServletDemo04 extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
ServletContext servletContext = this.getServletContext();
//RequestDispatcher requestDispatcher = servletContext.getRequestDispatcher("/gp"); //请求转发目标路径
//requestDispatcher.forward(req,resp); //调用forward方法转发请求
servletContext.getRequestDispatcher("/gp").forward(req,resp);
}
}
如果出现tomcat爆500的情况,可以尝试关掉tomcat然后maven clean一下项目的target,在重启tomcat可能就好了
4、读取资源文件
Properties,如果在项目的java目录下有写properties文件的话默认是不会被导出的,需要在pom.xml中多加一段build的配置,使得java目录下的资源文件也可以被导出。
但是不管是resources和java目录下的properties文件都会被打报道WEB-INF/classes目录下,也就是classpath路径
思路:利用this.getServletContext().getResourceAsStream()
方法返回一个流,通过Properties
类的实例化对象调用load()
方法加载这个流获取数据。
public class PropertiesServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
InputStream is = this.getServletContext().getResourceAsStream("/WEB-INF/classes/com/zh1z3ven/servlet/test.properties"); //返回InputStream流
Properties properties = new Properties(); //实例化Properties对象
properties.load(is); //loadInputStream流
String username = properties.getProperty("username");
String password = properties.getProperty("password");
resp.getWriter().print(username + ":" + password);
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
}
}
Web.xml
<servlet>
<servlet-name>sd5</servlet-name>
<servlet-class>com.zh1z3ven.servlet.PropertiesServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>sd5</servlet-name>
<url-pattern>/sd5</url-pattern>
</servlet-mapping>
资源文件路径,同理如果是在源码目录下,只需要写好相对路径即可。
例如:此test.properties
路径为:
/WEB-INF/classes/com/zh1z3ven/servlet/test.properties
HttpServletResponse
web服务器会将客户端发来的http请求进行处理,生成两个对象HttpServletRequest和HttpServletResponse。
- 如果要获取客户端发来的请求信息:HttpServletRequest
- 如果要处理需要响应给客户端的信息:HttpServletResponse
1、简单分类
向客户端发送数据的方法
ServletOutputStream getOutputStream() throws IOException; //普通输出流
PrintWriter getWriter() throws IOException; //处理中
向浏览器发送响应头的方法
void setCharacterEncoding(String var1);
void setContentLength(int var1);
void setContentLengthLong(long var1);
void setContentType(String var1);
void setDateHeader(String var1, long var2);
void addDateHeader(String var1, long var2);
void setHeader(String var1, String var2);
void addHeader(String var1, String var2);
void setIntHeader(String var1, int var2);
void addIntHeader(String var1, int var2);
void setStatus(int var1);
2、常见应用
- 向浏览器输出消息(getWriter,getOutputStream)
- 下载文件
- 获取下载文件的路径
- 下载的文件名
- 让浏览器支持我们能下载的东西
- 获取下载文件的输入流
- 创建缓冲区
- 获取OutputStream对象
- 将FileOutputStream流写入到buffer缓冲区
- 使用OutputStream将buffer缓冲区的数据输出到客户端
public class FileServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
System.out.println("=====================123131===========================");
//获取下载文件的路径
//String realPath = this.getServletContext().getRealPath("/1.png");//获取绝对路径
String realPath = "/Users/b/Desktop/javacode/JavaWeb_Maven/javaweb-02-servlet/response/src/main/resources/1.png";
System.out.println("下载文件的路径: " + realPath);
//下载的文件名
String fileName = realPath.substring(realPath.lastIndexOf("/") + 1);
System.out.println(fileName);
//让浏览器支持我们能下载的东西
resp.setHeader("Content-Disposition", "attachment;filename=" + fileName);
//获取下载文件的输入流
FileInputStream fis = new FileInputStream(realPath);
//创建缓冲区
int len = 0;
byte[] buffer = new byte[1024];
//获取OutputStream对象
ServletOutputStream servletOutputStream = resp.getOutputStream();
//将FileOutputStream流写入到buffer缓冲区
while ((len = fis.read(buffer)) != -1){
// 使用OutputStream将buffer缓冲区的数据输出到客户端
servletOutputStream.write(buffer, 0, len);
}
//释放资源
servletOutputStream.close();
fis.close();
}
重定向
方法:
void sendRedirect(String var1) throws IOException;
Demo:
public class RedirectServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
resp.sendRedirect("/download");
}
}
重定向与转发
重定向与转发都会使用户访问其他资源,只是发生位置不同,请求转发放生在服务端,只能访问该项目内部资源。重定向是第一次收到用户请求后让客户端重新访问一个新的url地址,客户一共请求了两次。
HttpServletRequest
1、Request常用方法
String getMethod() //获取请求方式
String getContextPath() //获取虚拟路径
String getServletPath() //获取Servlet路径
String getQueryString() //获取get请求方式参数
String getRequestURI() //获取请求uri
StringBuffer getRequestURL() //获取URL
String getProtocol() //获取协议版本
String getRemoteAddr() //获取客户端ip
String getHeader(String name) //通过请求头的名称获取请求头的值
Enumeration<String> getHeaderNames() //获取所有的请求头名称
String getParameter(String var1); //获取一个指定参数的值
Enumeration<String> getParameterNames();
String[] getParameterValues(String var1); //获取多个参数的值,返回一个String数组
Map<String, String[]> getParameterMap();
public class LoginServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
req.setCharacterEncoding("UTF-8"); //设置编码
String username = req.getParameter("username");
String password = req.getParameter("password");
String[] hobbys = req.getParameterValues("hobbys");
System.out.println("================================");
System.out.println(username);
System.out.println(password);
System.out.println(Arrays.toString(hobbys)); //获取string数组
System.out.println("================================");
req.getRequestDispatcher("/success.jsp").forward(req,resp);
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
doGet(req,resp);
}
}