创建一个maven项目, 名称目录自己设定
Servlet 的版本要和 Tomcat 匹配.如果我们使用 Tomcat 8.5, 那么就需要使用 Servlet 3.1.0
把中央仓库中提供的 xml 复制到项目的 pom.xml 中
javax.servlet
javax.servlet-api
3.1.0
provided
Archetype Created Web Application
在 java 目录中创建一个类 HelloServlet, 代码如下:
@WebServlet("/hello")
public class HelloServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//super.doGet(req, resp);
System.out.println("hello world");
resp.getWriter().println("hello world");
}
}
war 包和 jar 包的区别jar 包是普通的 java 程序打包的结果. 里面会包含一些 .class 文件.war 包是 java web 的程序, 里面除了会包含 .class 文件之外, 还会包含 HTML, CSS, JavaScript, 图片, 以及其他的 jar 包. 打成 war 包格式才能被 Tomcat 识别
我们需要在 pom.xml 中新增一个 packing 标签, 表示打包的方式是打一个 war 包
war
java108_hello
重新使用 maven 打包, 可以看到生成的新的 war 包的结果.
此时通过浏览器访问 http://127.0.0.1:8080/java108_hello/hello
注意: URL 中的 PATH 分成两个部分, 其中 java108_hello为 Context Path, hello 为 Servlet Path
一个项目中, 第一次使用smart tomcat 需要简单配置一下
点击运行发现:
因为之前已经打开了tomcat ,占用了8080端口, 只要管关掉tomcat就可以了
实验:改动输出值和返回值
API就是一组类和方法 我们只需要学习3各类就可以
这是编写Servlet 代码用到的核心类, 通过继承这个类, 并重写其中的方法, 让tomcat去调用到这里的逻辑
核心方法
init 在 webapp 被加载的时候, 执行init (理论)
但是 tomcat 是可以配置webapp为 "懒加载" 的状态的
会使webapp在真正被访问到的时候才加载,(首次访问的时候, 才会触发 init)
destory webapp在被销毁的时候(tomcat结束)执行, 进行收尾工作
service 每次收到请求,都会执行Service 处理每个请求
@WebServlet("/method")
public class MethodServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
resp.getWriter().write("doGet");
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
resp.getWriter().write("doPost");
}
@Override
protected void doPut(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
resp.getWriter().write("doPut");
}
@Override
protected void doDelete(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
resp.getWriter().write("doDelete");
}
}
针对上述方法, 浏览器只能比较方便的构造get请求, 不太方便构造其他的
针对其他的方法要想构造, 使用Ajax或者postman
GET
POST
@WebServlet("/request")
public class RequestServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
// 上面这个操作也是必要的, 显示告诉浏览器, 你拿到的数据是html
resp.setContentType("text/html");
// 调用req的各个方法, 把得到的结果汇总到一个字符串中, 统一返回到页面上
StringBuilder respBody = new StringBuilder();
// 下列内容实在浏览器上按照html的方式来展示, 此时 \n 在html中并不是换行
// 而是用
标签表示换行
respBody.append(req.getProtocol());
respBody.append("
");
respBody.append(req.getMethod());
respBody.append("
");
respBody.append(req.getRequestURI());
respBody.append("
");
respBody.append(req.getContextPath());
respBody.append("
");
respBody.append(req.getQueryString());
respBody.append("
");
//拼接header
Enumeration headers = req.getHeaderNames();
while(headers.hasMoreElements()) {
String header = headers.nextElement();
respBody.append(header + ":" + req.getHeader(header));
respBody.append("
");
}
//统一返回结果
resp.getWriter().write(respBody.toString());
}
}
当然更多时候, 是希望获取到query string 或者 body 中的内容(用户自定义)
@WebServlet("/parameter")
public class ParameterServlet extends HttpServlet {
// 约定, 客户端使用 query string 传递数据
// query string 形如: username=zhangsan&password=123
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
String username = req.getParameter("username");
String password = req.getParameter("password");
System.out.println("username=" + username);
System.out.println("password=" + password);
resp.getWriter().write("ok");
}
}
@WebServlet("/parameter2")
public class Parameter2Servlet extends HttpServlet {
// 预期让客户端发送一个POST请求, 同时使用form 格式的数据, 在body中把数据传递过来
// body 形如:
// username=zhangsan&password=123
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
String username = req.getParameter("username");
String password = req.getParameter("password");
System.out.println("username" + username);
System.out.println("password" + password);
resp.getWriter().write("OK");
}
}
Jackson也是第三方库, 也需要通过maven从中央仓库把这个库下载来了导入到项目中
使用这个库的核心类就是ObjectMapper : 可以把一个对象映射到 JSON 字符串 也可以把 JSON 字符串映射到 对象
class User {
public String username;
public String password;
}
@WebServlet("/json")
public class JsonServlet extends HttpServlet {
// 此处约定客户端 body 按照 json 格式来进行传输
//其格式想入:
// {
// username : zhangsan
// password : 123
// }
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
ObjectMapper objectMapper = new ObjectMapper();
// json => java对象
User user = objectMapper.readValue(req.getInputStream(), User.class);
System.out.println("username=" + user.username + ", password=" + user.password);
// Java对象 => json
String userString = objectMapper.writeValueAsString(user);
System.out.println("userJson=" + userString);
resp.getWriter().write("okk");
}
}
这个映射方法有许多版本, 作用就是把json字符串解析成Java对象, 其中这里的第一个参数, 是一个流对象, 也就是表示json从哪里来读.
第一个参数: http请求中的body(是通过 getInputStream() 方法得到流对象, 进一步读取出来的)
第二个参数, 则是指定的类型, 当我得到json字符串(第一个参数) 需要转成一个啥样的Java对象, 需要指定一下对象的类型
要想写作private 也可以, 但是必须要提供对应的getter和setter方法
class User {
private String username;
private String password;
public String getUsername() {
return username;
}
public String getPassword() {
return password;
}
public void setUsername(String username) {
this.username = username;
}
public void setPassword(String password) {
this.password = password;
}
}
@WebServlet("/json")
public class JsonServlet extends HttpServlet {
// 此处约定客户端 body 按照 json 格式来进行传输
//其格式想入:
// {
// username : zhangsan
// password : 123
// }
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
ObjectMapper objectMapper = new ObjectMapper();
// json => java对象
User user = objectMapper.readValue(req.getInputStream(), User.class);
System.out.println("username=" + user.getUsername() + ", password=" + user.getPassword());
// Java对象 => json
String userString = objectMapper.writeValueAsString(user);
System.out.println("userJson=" + userString);
resp.getWriter().write("oKK");
}
}
如果不提供getter() 和 setter()方法, 就会出现状态500报错
获取和匹配的过程都发生在Jackson 的 readValue 工作过程中:
由于把username 改成 private了, 而Jackson 并不会直接针对private 属性进行扫描, username就不认识了
同样也是和HTTP响应数据, 是匹配的
响应数据中的状态码, 各种header, body 针对这些属性, 就可以进行设置
请求对象, 我们拿到之后的母的, 是为了获取里面的属性(读)
响应对象, 我们拿到之后的目的, 是为了设置里 面的属性(写)
对于 doXXX这样的方法来说, 本身要做的事情就是"根据请求 计算响应"
请求对象, 是Tomcat 收到请求之后, 对http 协议解析得到的对象
响应对象, 是Tomcat 创建的空的对象, 我们在代码中把 响应对象的属性设置好
@WebServlet("/status")
public class StatusServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
resp.setStatus(404);
}
}
其是是可以在返回 状态码的同时, 给body也写入数据的, 就可以得到一些"个性化的错误页面"
@WebServlet("/refresh")
public class RefreshServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
resp.setHeader("refresh", "2");
resp.getWriter().write("time" + System.currentTimeMillis());
}
}
refresh : 2 ==> 浏览器就会每隔两秒自动刷新一次
设置这个属性之后, 此时, 每隔2s就会自动刷新一次, 但是这里的刷新间隔也不是精确的2000ms, 会比2000稍微长点, 毕竟, 浏览器发起请求, 服务器响应, 再到页面被解析出来, 都是需要消耗一定的时间
@WebServlet("/redirect")
public class RedirectServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
// 让页面被重定向 搜狗主页
resp.setStatus(302);
// 重定向响应,一定要带有 Location , 表示要重定向到哪里
resp.setHeader("Location", "https://www.bilibili.com");
}
}
@WebServlet("/body")
public class BodyServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
// 让服务器返回一个 html 数据
resp.getWriter().write("你好");
}
}
l浏览器吗默认会跟随系统的编码
windows简体中文版, 默认的编码是gbk
拿着utf8的数据, 浏览器按照gbk的方式来解析, 势必就会出现乱码
解决乱码的原则, 就是编码方式匹配~~
utf8是更主流的编码方式, 而gbk这种编码只能表示简体中文
@WebServlet("/body") public class BodyServlet extends HttpServlet { @Override protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { resp.setContentType("text/html; charset=utf8"); // 让服务器返回一个 html 数据 resp.getWriter().write("你好"); } }