一个秃了头的大师托梦给我说要想称为大神就要有一个完善的我的知识体系,我从梦中惊醒,打开笔记本开始了整理。。。php
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方法是只有在容器正常关闭时才会去执行,注意是正常关闭,且它是先于容器关闭而执行,因此通常用它来释放资源。安全
GenericServlet实现了Servlet接口,是对于Servlet接口基础的封装抽象类,继承者必须须要重写service方法,同时能够根据自身须要重写其余4个方法
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) 经过请求头名称获取请求头信息
防盗链
所谓防盗链是指防止其余web站点页面经过链接本站点的页面来访问本站点内容,这样对于本站点来讲侵犯了本站点的版权
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("不知道什么鬼地方访问的");
}
}
复制代码
其实就是利用绘图工具绘制一张二维码的图片,再经过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或者一些细枝末节我不作过多的探究,由于这部分对于后续的学习没有更多的帮助,由于确实用的实在太少了,后续研究框架的源码也不多会看到他们,因此算了算了,哈哈哈哈主要仍是懒