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或者一些細枝末節我不作過多的探究,由於這部分對於後續的學習沒有更多的幫助,由於確實用的實在太少了,後續研究框架的源碼也不多會看到他們,因此算了算了,哈哈哈哈主要仍是懶