2020年了,为何仍是看Servlet???首先这是一个必经的阶段,当初开始学习Java Web的时候,这部分就是重点,第二就是在学习了一些更加高级的框架时,仍是时不时会看到它的身影,像Spring等,在学习他的源码的时候就能够看到它维护的DispatcherServlet,因此不要再问为何2020还看这么土的东西?? html
固然还有一个问题就是要不要看JSP,这个我的认为简单了解便可,如今企业中的开发基本都是先后端分离的模式,就算是简单的搭建,Freemarker、Thymeleaf模板语言也是更加的方便,so 若是你有空,那么你能够去看看,可是不须要太过深究java
Servlet是一个接口,定义了servlet容器识别Java程序的规范web
最基础的servlet实现,直接实现servlet接口,重写他的5个方法,同时须要在web.xml中配置这个servlet,servlet是根据web.xml中的配置找到对应url的处理者后端
public class HelloServlet implements Servlet { @Override public void init(ServletConfig servletConfig) throws ServletException { System.out.println("init ..."); } @Override public ServletConfig getServletConfig() { return null; } @Override public void service(ServletRequest servletRequest, ServletResponse servletResponse) throws ServletException, IOException { System.out.println("service ..."); } @Override public String getServletInfo() { return null; } @Override public void destroy() { System.out.println("destroy ..."); } }
<?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_3_1.xsd" version="3.1"> <servlet> <servlet-name>HelloServlet</servlet-name> <servlet-class>com.learn.servlet.HelloServlet</servlet-class> </servlet> <servlet-mapping> <servlet-name>HelloServlet</servlet-name> <url-pattern>/hello</url-pattern> </servlet-mapping> </web-app>
注意⚠️:Servlet3.0是从J2EE 6开始才支持的哦,主要是用于简化开发,不用再繁琐的xml配置了,因此能够直接不使用web.xml数组
使用方法很简单,直接在servlet上吗使用@WebServlet注解即刻,经过查看源码能够知道,原来配置在web.xml中的配置所有均可以挪到注解的值中浏览器
@WebServlet(name = "hello3", urlPatterns = {"/hello3"}) public class HelloServlet3 implements Servlet { @Override public void init(ServletConfig servletConfig) throws ServletException { } @Override public ServletConfig getServletConfig() { return null; } @Override public void service(ServletRequest servletRequest, ServletResponse servletResponse) throws ServletException, IOException { System.out.println("servlet 3.0 ..."); } @Override public String getServletInfo() { return null; } @Override public void destroy() { } }
@Target({ElementType.TYPE}) @Retention(RetentionPolicy.RUNTIME) @Documented public @interface WebServlet { String name() default ""; String[] value() default {}; String[] urlPatterns() default {}; int loadOnStartup() default -1; WebInitParam[] initParams() default {}; boolean asyncSupported() default false; String smallIcon() default ""; String largeIcon() default ""; String description() default ""; String displayName() default ""; }
Servlet中有五个方法,其中有3个和生命周期有关缓存
Servlet默认状况下,是在初次被访问时建立,也能够经过配置load-on-startup(默认状况为负数,开启则配置为0或正整数)来使其随服务器的启动而建立,建立时会执行init方法,由此也能够看出Servlet是一个单例对象,因此在多个用户访问的时候会存在线程安全问题,因此尽可能不要在Servlet中使用成员变量或尽可能不要修改Servlet的成员变量。tomcat
destroy方法是只有在容器正常关闭时才会去执行,注意是正常关闭,且它是先于容器关闭而执行,因此通常用它来释放资源。安全
HttpServlet继承自GenericServlet,是在http协议的基础上对Servlet进行进一步封装,其中经常使用的doGet和doPost根据http method的不一样按需使用,简化开发服务器
@WebServlet("/http") public class HelloHttpServlet extends HttpServlet { @Override protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { System.out.println("do get ..."); } @Override protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { System.out.println("do post ..."); } }
规则:
Servlet在tomcat中是经过反射机制建立的对象,传递来的Request对象其实是实现了HttpRequestServlet的RequestFacade,此对象由tomcat提供(门面模式)
获取请求行
假设一个请求的请求行是 GET /hello/abc?name=123 HTTP/1.1
获取请求头
request.getHeader(key) 经过请求头名称获取请求头信息
防盗链
request.getHeader("referer")能够获取来源,当值为null表明浏览器地址栏直接输入访问,经过对于域名的匹配能够达到防盗链的效果
@Override protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { System.out.println("do get ..."); System.out.println(req.getHeader("referer")); String referer = req.getHeader("referer"); if (referer == null) { System.out.println("来自浏览器的地址直接访问"); }else if (referer.contains("localhost:8080")) { System.out.println("本地访问"); }else { System.out.println("不知道什么鬼地方访问的"); } }
获取请求体
获取请求参数
request.getRequestParam(name) 根据名字获取参数
request.getParamterMap 获取全部请求参数 k-v,封进一个map
请求转发
域对象
获取ServletContext对象
设置响应头
设置响应体
获取输出流
其实就是利用绘图工具绘制一张二维码的图片,再经过response的字节输出流,输出图片对象
@WebServlet("/verify") public class VerifyCodeServlet extends HttpServlet { @Override protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { BufferedImage bufferedImage = new BufferedImage(100, 50, BufferedImage.TYPE_INT_RGB); Graphics graphics = bufferedImage.getGraphics(); graphics.setColor(Color.PINK); graphics.fillRect(0,0,100, 50); graphics.setColor(Color.BLUE); graphics.drawRect(0,0,100-1,50-1); // 这里能够自定义验证码内容 String i = String.valueOf(new Random().nextInt(100)); graphics.drawString(i, 50, 25); ImageIO.write(bufferedImage, "jpg", resp.getOutputStream()); } }
ServletContext域对象是在容器启动时便建立,对于全部项目中的Servlet共享,不管是request对象获取的servlet context仍是GenericServlet抽象父类提供的方法,获取的都是同一个对象,域对象存取数据方法和request对象同样
@WebServlet("/context") public class ContextServlet extends HttpServlet { @Override protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { // 这两个获取的是同一个 ServletContext servletContext = req.getServletContext(); // ServletContext servletContext1 = getServletContext(); File file = new File(servletContext.getRealPath("/hehe.html")); String mimeType = servletContext.getMimeType(file.getName()); // 输出:text/html System.out.println(mimeType); } }
其实步骤很简单
一、告诉浏览器要下载一个文件 resp.setHeader("content-disposition", "attachment;filename=你好.jpg");
二、把要下载的文件加载进入resp的字节输出流中
@WebServlet("/download") public class DownloadServlet extends HttpServlet { @Override protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { resp.setCharacterEncoding("utf-8"); String fileName = req.getParameter("fileName"); String realPath = getServletContext().getRealPath(fileName); resp.setHeader("content-disposition", "attachment;filename=你好.jpg"); ImageIO.write(ImageIO.read(new File(realPath)), "jpg", resp.getOutputStream()); } }
运行servlet时可能会要一些辅助的信息,ServletConfig提供了这个空间去存这些信息
对于传统的xml方式的配置,在servlet标签中添加init-param标签便可
<servlet> <servlet-name>HelloServlet</servlet-name> <servlet-class>com.learn.servlet.HelloServlet</servlet-class> <init-param> <param-name>hello</param-name> <param-value>this is hello value</param-value> </init-param> </servlet>
对于更加简单方便的注解,使用方式也是更加简单
@WebServlet(urlPatterns = "/config", initParams = {@WebInitParam(name = "abc", value = "123")})
注意⚠️:servlet config的配置仅仅在配置的servlet中有效哦
servlet获取配置的方法和servlet context相似
@WebServlet(urlPatterns = "/config", initParams = {@WebInitParam(name = "abc", value = "123")}) public class ConfigServlet extends HttpServlet { @Override protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { ServletConfig servletConfig = getServletConfig(); String abc = servletConfig.getInitParameter("abc"); System.out.println(abc); } }
一次会话中包含着屡次请求和响应,所谓一次会话就是浏览器第一次和服务端发请求,此时会话创建,知道有一方关闭,这次会话才中止。会话技术就是在此次会话内共享数据,方式主要有二 一、cookie 二、session
新建cookie对象
Cookie cookie = new Cookie("abc", "123");
向客户端设置cookie
resp.addCookie(cookie);
获取客户端cookie
Cookie[] cookies = req.getCookies();
本质就是利用了http header的add-cookie和cookie,服务端设置cookie本质就是在header中加上add-cookie,服务端取出cookie也就是解析header中cookie的值
容许同时存在多个cookie,只要屡次addCookie便可
实现思路比较简单,程序开始直接获取cookie,并检索获取到的cookie中是否存在咱们设置过的cookie,有表明曾经登陆过,没有表明初次登陆
@WebServlet("/ct") public class CookieTestServlet extends HttpServlet { @Override protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { resp.setContentType("text/html;charset=utf-8"); Cookie[] cookies = req.getCookies(); if (cookies != null) { for (Cookie item: cookies) { if (item.getName().equals("loginTime")) { // 取出上次的时间 String lastLoginTime = URLDecoder.decode(item.getValue(), "utf-8"); // 存入当前的时间 item.setMaxAge(60 * 60 * 24 * 30); item.setValue(URLEncoder.encode(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss")\ .format(new Date()),"utf-8")); resp.addCookie(item); resp.getWriter().println("上次登陆时间:" + lastLoginTime); return; } } } Cookie cookie = new Cookie("loginTime", URLEncoder.encode(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss") .format(new Date()),"utf-8")); cookie.setMaxAge(60 * 60 * 24 * 30); resp.addCookie(cookie); resp.getWriter().println("初次登陆,欢迎您"); } }
和cookie相似
session实现本质是经过cookie来实现的,能够看到会话创建完成后,服务器会在内存中建立session对象,并会在响应中添加name为JSESSIONID的cookie,表明本次会话的对象标识。在同一次会话过程当中,浏览器访问就会带上这个JSESSIONID进行请求,服务端接受以后经过对比ID与内存中的对象,判断本次会话是否和以前的相同。
使用tomcat容器,session的默认存活时间时30分钟,默认的配置是在tomcat/conf/web.xml中进行配置,用户能够修改其中的session-config手动对其修改
默认状况下,客户端关闭,因为cookie的特性,cookie会随着浏览器关闭而清除,因此不相同;可是咱们了解了session的基本原理以后,能够经过手动设置JSESSIONID cookie的存活时间,达到缓存cookie的效果,那么下次在打开浏览器的请求就能够保持同一个session
不作任何处理的状况下,服务端关闭,内存中的session对象天然就不存在,因此不可能相同。要保持session,须要进行session的钝化,即在服务端关闭以前持久化存储session对象信息,并在服务端重启时进行session活化,即将钝化的session从新加载进入内存。
咱们经常使用的tomcat容器为咱们提供了这一个功能
用法十分简单,经过实现Filter接口,重写三个方法便可,其中主要的过滤方法就是doFilter
@WebFilter(urlPatterns = "/hello3") public class FilterDemo implements Filter { @Override public void init(FilterConfig filterConfig) throws ServletException { // 随服务器启动而建立 System.out.println("init ... "); } @Override public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException { System.out.println("filter ..."); filterChain.doFilter(servletRequest, servletResponse); } @Override public void destroy() { // 随服务器关闭而销毁 System.out.println("destroy ..."); } }
能够对于同一个访问地址配置多个Filter,构成所谓的过滤器链,在访问到达servlet的具体方法前,先依次由外而内的经过filter的过滤(filterChain.doFilter以前的语句),结束servlet的service方法后,再由内而外的执行一次filter过滤(filterChain.doFilter以后的语句)
分为两种状况:
做用同ServletConfig,用法更是同样,再也不过多赘述,都是在xml中配置init-param或者在注解中配置,并只能在对应的filter中才能获取。
默认状况下,过滤器只过滤请求,若是要过滤转发或其余的一些状况,也想拦截时须要配置这个参数
关于JSP&Listener或者一些细枝末节我不作过多的探究,由于这部分对于后续的学习没有更多的帮助,由于确实用的实在太少了,后续研究框架的源码也不多会看到他们,因此算了算了,哈哈哈哈主要仍是懒