【框架】Servlet API

编程入门 行业动态 更新时间:2024-10-28 12:18:55

本文需要结合【HTTP】阅读:
https://blog.csdn/weixin_42915286/article/details/84871898

https://www.bilibili/video/av40697944?from=search&seid=9275670543812089695

说在前面

JavaEE是一个开放的平台,包含的技术很多,主要有13种;
需要强调的是:不是每个技术都要用到,实际上需要有选择地使用;

实际上,把每一种JavaEE技术都精通是不现实的,有人则认为,若能够成为JavaEE程序员,必须掌握的技术是:Java、Servlet、JSP;若这三门没学好就去追求spring等web【框架】,也只是空中楼阁,华而不实的;

—————————————————————————————

Servlet 3.0

Servlet = Java服务器小程序
Applet = Java客户端小程序(严重过时)

Servlet是一个供其他 Java程序(【Servlet引擎】或【Web服务器】或【Tomcat】)调用的Java类,他不能独立运行,它的运行完全由Servlet引擎来控制和调度;

针对客户端的多次Servlet请求,通常情况下,【服务器只会创建一个Servlet实例对象(单例模式)】,也就是说Servlet实例对象一旦创建,也就是说Servlet实例对象一旦创建,他就会潴留在内存中,为后续的其他请求服务,直至Web容器退出或者reload该web应用,Servlet实例对象才会被销毁;

重要部分:页面搜索:Servlet的单例问题

  • 提问:为什么会出现Servlet?
    需求:请你用现有的JavaEE技术开发一个动态网页,比如让用户留言,其他人可回复交互;
    用普通的Java技术完成不了,那么Sun就开发了Servlet技术供程序员使用;
    Servlet是JavaEE中出现较早的一项技术,在Struts、Spring出现前,动态网页技术都采用Servlet,在1999-2000大行其道;
    Servlet是JSP的基础,是Spring Controller的本质;是JavaWeb的基础,到2019年的今天仍然有重要性;

  • 提问:Servlet特点?
    1.服务器端(直接说Tomcat也没问题)调用和执行的;(非客户端!)
    2.Java编写;(Servlet就是一个Java写的类)
    3.按照Servlet规范开发的;(规范需要花时间学习下)
    4.功能强大,几乎可以完成所有网站功能(很多老程序员仍然喜欢用Servlet写整个网站)
    5.Spring Controller的基础;


【Servlet有规范】!
必须放在Tomcat - webapps - examples - ??? - WEB-INF - classes文件夹
因为Servlet说到底是Java程序,Java程序不能直接运行,要编译成classes;
另外,还要部署web.xml文件,一个Sevlet类对应一个<servlet><servlet-mapping>
(项目中所有Servlet类的配置都写在同一个web.xml中)

需求:
通过一个用户管理系统来讲解Servlet,单纯用Servlet来实现,是一个很好练习Servlet技术的案例;
操作人员:
管理员;普通用户;
同时还需要完成:
分页、网站计数器、保存用户访问网站的习惯、追踪用户的来源;

Internet上供外接访问的Web资源有:
静态Web资源(HTML):web页面中工人们浏览的数据始终是不变的;
动态Web资源(JSP/Servlet):web页面中工人们浏览的数据是有程序产生的,不同时间点访问web页面看到的内容各不相同;

Java中,动态web资源开发技术统称为JavaWeb;

——————————————

b/s结构和c/s结构的比较

这部分的选择,程序员一般不参与,由项目经理说了算;

b/s
即:browser/server 浏览器 服务器
优势:
1.开发成本低;
2.管理维护简单;(浏览器安装就可以用)
3.产品升级便利;(比如新浪想要新增一个调查功能,开个会加个模块就可以了;但如果微信想要添加一个新模块,需要对全世界的客户端添加该功能)
4.对用户的培训费用低;
5.用户使用方便,出现故障几率小;(比如使用c/s制作了一款软件给珠宝开发商,正式使用时,老板和与员工却发现彼此看不到对方上线的情况,后来开发终于找到原因所在:网管的防火墙只开放了80端口,且不允许开放其他端口,那么开发就努力使用防火墙穿透tunnel技术解决了问题)
不足:
1.安全性不足;(因为浏览器是别人写的;浏览器大多是开源的,可被大众研究)
2.客户端不能随心变化,受到浏览器的限制;

c/s 用tcp/ip协议(socket)
即:client/server 客户端(往往是程序员开发的) 服务器;

b/s和c/s从本质上来说没有区别,只是表现形式不一样:
b/s:客户端用浏览器,是现有的工具,程序员无需自己再包装;
c/s:客户端自己来开发,程序员要自己包装tcp/ip;

——————————————

实现Servlet

  • 提问:实现Servlet有几种方式?
    三种;
  • 1.implements Servlet接口;(稍微重要,已废弃)
    但通过此方式很好理解Servlet生命周期,而Servlet生命周期必须掌握!!!
  • 2.extends GenericServlet类;(高不成低不就,已废弃)
  • 3.extends HttpServlet类;(唯一选择,最重要!!!)

需求:
用实现接口的方式开发一个Servlet,要求该Servlet可以显示HelloWorld,同时显示当前时间;
(用静态文件HTML是做不到的,动态文件可以实现)

如何新建?
页面搜索:TODO

implements Servlet

查阅JavaEE API,可知里面就五个方法:
而既然需要继承一个接口,那么这五个方法就都要实现;

  • init()
    初始化Servlet,就是把该Servlet装载到内存中,只会被调用一次;
  • getServletConfig()
    得到ServletConfig对象;(之后会讲)
  • service()
    核心;服务函数,写业务逻辑代码,该函数每次都会被调用;
  • getServletInfo()
    得到Servlet的配置信息;
  • destroy()
    销毁,从内存中清除,只会被调用一次;
    (和init()相对应,都只加载一次)

tips:
若Servlet被访问十次,init()只会被调用一次,service()会被调用十次,destroy()会被调用一次;

——————————————
Servlet01 implements Servlet

package com.yau.Servlet;

import javax.servlet.*;
import java.io.IOException;
import java.util.Date;

public class Servlet01 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("Hello,World!!!" + new Date());
        // 浏览器输出 servletResponse.getWriter().println("Hello,World!!!"+new Date());
    }

    @Override
    public String getServletInfo() {
        return null;
    }

    @Override
    public void destroy() {

    }
}

配置web.xml
映射:对已注册的Servlet进行映射;


这么配置只能在IDE的console返回语句;
浏览器访问:http://localhost:8080/Servlet01
IDE可返回语句;(浏览器无返回)
(注意:默认浏览器访问http://localhost:8080,浏览器返回$END$;记得注意修改URL,才能正确访问)

没看完:
https://www.bilibili/video/av40697944/?p=10
——————————————

Servlet生命周期(重要)

必须搞得很明白!!!


—————————————————————————————

extends HttpServlet

Servlet 3.0

市面上的公司,90%都是通过HttpServlet的方式来开发Servlet;
追踪源码,会发现doGet()doPost()都源自于service(),等于对此方法进行了包装而已;
Servlet3.0开始,配置Servlet支持注解方式,但还是保留了配置web.xml方式;

HttpServlet指能够处理HTTP请求的Servlet,他在原有Servlet接口上添加了一些与HTTP协议进行处理的方法,他比Servlet接口的功能更为强大,因此开发人员在编写Servlet时,通常应该implements这个类,避免直接去实现Servlet接口;
HttpServlet在实现Servlet接口时,Override了service(),该方法内的代码会自动判断用户的请求方式,若为GET请求,则调用HttpServlet的doGet方法,若为Post请求,则调用doPost方法;
因此客服人员在编写Servlet时,通常只需要Override doGet或者doPost方法,而不需要Override service方法;

配置Servlet的nameurlPatterns有两种方式:

1.web.xml文件中配置
2.Servlet类上使用@WebServlet注解进行配置(此方法不用web.xml)

——————————————
@WebServlet常用属性

属性	               类型	        是否必须	说明
asyncSupported	   boolean	      否	指定Servlet是否支持异步操作模式
displayName	       String	      否	指定Servlet显示名称
initParams	       WebInitParam[] 否	配置初始化参数
loadOnStartup	   int	          否	标记容器是否在应用启动时就加载这个Servlet
name	           String	      否	指定Servlet名称
urlPatterns/value  String[]	      否	这两个属性作用相同,指定Servlet处理的url

——————————————
方法1
Servlet02
(注意:此方法我采用的是Web Application模版,非Spring Boot)

public class Servlet02 extends HttpServlet {
    protected void doPost(HttpServletRequest request,
                          HttpServletResponse response)
                          throws ServletException, IOException {
        response.getWriter().println("I am httpservlet doGet()");
    }

    protected void doGet(HttpServletRequest request,
                         HttpServletResponse response)
                         throws ServletException, IOException {
        response.getWriter().println("I am httpservlet doPost()");
    }
}

web.xml

    <servlet>
        <servlet-name>Servlet02</servlet-name>
        <servlet-class>com.yau.Servlet.Servlet02</servlet-class>
    </servlet>

    <servlet-mapping>
        <servlet-name>Servlet02</servlet-name>
        <url-pattern>/Servlet02</url-pattern>
    </servlet-mapping>

访问:http://localhost:8080/Servlet02
——————————————
方法2

MyServlet
(注意:此方法我采用的是Spring Boot 2.0模版,SB特别之处在于在启动项添加@ServletComponentScan

@WebServlet(name="myservlet",urlPatterns = "/myservlet")
public class MyServlet extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        PrintWriter out = resp.getWriter();
        String str = "doGet() here.";
        out.println(str);
    }

    @Override
    protected void doPost(HttpServletRequest req, 
                          HttpServletResponse resp) 
                          throws ServletException, IOException {
        PrintWriter out = resp.getWriter();
        String str = "doPost() here.";
        out.println(str);
    }
}
@SpringBootApplication
@ServletComponentScan("com.yau.sb_servlet.Servlet")
public class ServletApp {

    public static void main(String[] args) {
        SpringApplication.run(ServletApp.class, args);
    }
}

访问:http://localhost:8080/myservlet
——————————————
默认返回doGet方法:

doGet() here.

doGet方法的参数都写在URL中,比如
localhost:8080/myservlet?username=haha
存储在HTTP报文的第一行,且其数据容量一般不能超过1K;

GET / myservlet?username=haha HTTP/1.1
User-Agent	Mozilla/5.0 (Macintosh...
Accept	text/html...
Accept-Encoding	gzip, deflate
Connection:Keep-Alive

这样很明显是不安全的,不建议用doGET方法!!!
——————————————
如何返回doPost方法?

resources - static (一定是static文件夹)下新建一个html文件,比如login.html
他的form action要指明Servlet的name,本html才可以指向指定Servlet;

<!DOCTYPE html>
<html>
<head>
    <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
    <title>Test</title>
</head>
<body>
<form action="myservlet" method="post">
    u:<input type="text" name="username"/>
    <input type="submit" name="btnLogin" value="Login"/>
</form>
</body>
</html>

访问该html:http://localhost:8080/login.html

返回该Servlet的doPost方法内容:

doPost() here.

doPost方法的请求参数都存储在HTTP请求包中;

GET / myservlet HTTP/1.1
User-Agent	Mozilla/5.0 (Macintosh...
Accept	text/html...
Accept-Encoding	gzip, deflate
Connection:Keep-Alive

username=haha

传送的数据量无限制,POST提交的参数后台更容易解析(即使是中文数据也更容易解决);
对比doGet,建议用doPost;
————————————————————————————
getParameter()
来自:public interface ServletRequest
getParameter(java.lang.String name)
Returns the value of a request parameter as a String, or null if the parameter does not exist.
会以String形式返回request参数,如果属性不存在,会返回null

myservlet:

resp.getWriter().println("the post name = "
               +req.getParameter("username"));

html中配置mthod=post

——————————————
总结:
通过HttpServlet去开发Servlet,需要重写doGet和doPost方法,这种方法最为流行;

表单提交get请求和post请求的区别:
1.从安全来看,get<post,get提交的数据会在浏览器的地址栏显示;
2.从提交的内容大小来看,get<post,但是get提交的数据不能大于1K,而post提交的数据理论上不受限制,但是集中建议不要大于64K;
3.请求响应速度来看,get>post,get要求服务器立即处理请求,而post可能形成一个队列请求;

————————————————————————————

Servlet细节(重要)

——————————————
由于客户端是通过URL访问web服务器中的资源,所以Servlet若想被外界访问,必须把Servlet映射到一个URL上,此部分工作在
(1)web.xml中由<servlet>``<servlet-mapping>
或者 (2)@WebServlet(name = "", urlPatterns = "")来完成;

————————————————————————————
同一个已注册的Servlet可指定多个urlPattern
同一个已注册的Servlet可映射到多个URL上

  • web.xml:
  • @WebServlet:
    web.xml里的urlPattern在@WebServlet中叫做:urlPatterns,是复数;
    那么可以猜想,这个属性本身就支持填写多个urlPattern???
    查看源码,果然,urlPatterns是一个String数组,那么在@WebService写多个URL的格式就很明朗了;

    ————————————————————————————
    映射一个Servlet时可以写多层

比如:
<url-pattern>/one/two.html</url-pattern>
(这里的.html只是url的一部分,不代表是真的html文件!!)

比如:
@WebServlet(name="myservlet",urlPatterns = {"/myservlet/haha/yoyo"})
————————————————————————————
Servlet映射到的URL中可以使用通配符*,很有用!!

只能有两种固定格式
(1).*.扩展名
(2).以/开头,以/*结尾

易错:
这两种方式注意区分格式,第一种星点do前面不能添加斜杠;

通配符的优先级问题总结

——————————————
/*

这里有个很有意思的点:可以直接写/*,代表所有请求都映射给他;
那么意思是:当用户乱输入URL后缀时,/*就可以把这个不存在的URL映射到,返回用户我们指定的错误页面,不会返回一个让用户看不懂的报错页面;

但这样又来了一个问题:
如果我们精心配制的urlPattern和另一种urlPattern/*同时存在,我们输入正确的URL会不会也被映射到/*去?
答案是不会;
优先级会先考虑正确urlPattern,最后考虑/*,所以正确URL可以放心被访问;

假设我们有一个普通的Servlet:MyServlet,里面有doGet方法;

@WebServlet(name="myservlet",urlPatterns = {"/ouch"})
。。。

又定义了一个表示错误页面的Servlet:ErrorServlet,里面有doGet方法:

@WebServlet(name = "errorservlet",urlPatterns = {"/*"})
public class ErrorServlet extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest req, 
                         HttpServletResponse resp) 
                         throws ServletException, IOException {
        resp.getWriter().println(
            "404,this is not the web page you're looking for.");
    }
}

验收成果:

  • 当我们访问正确的URL:localhost:8080/ouch,返回了我们期望看到的语句;
  • 当我们乱访问URL,如:localhost:8080/buif3gfiuwe,此URL走了一圈,发现没有匹配的URL,最后会被ErrorServleturlPattern/*映射,于是返回:
    404,this is not the web page you're looking for.

——————————————
/news/*

比如说 sohu 有新闻频道、娱乐频道、体育频道等等;
假设现在想对新闻频道(/news/...)进行整改:
此频道下所有路径都被映射到同一个页面,就可以采用...urlPatterns = "/news/*")的方式;
——————————————
*.do
(如果用星点do,就不能在前面加上斜杠,否则会报错!!!)

这个方法的意思是:指定后缀;
浏览器输入.../what.do .../how.do .../fine.do,都可以访问到指定Servlet;

注意!优先级问题!
/**.do同时存在时,*.do的优先级最低;
所以若在他们同时存在的情况下,访问*.do,会被映射到/*去!!!
——————————————

面试提问:

有如下映射关系:
Servlet1 映射到 /abc/*
Servlet2 映射到 /*
Servlet3 映射到 /abc
Servlet4 映射到 *.do

  • 题一:当请求URL为 /abc/a.html 时,匹配到了 /abc/*/* ,哪个Servlet会响应?
  • 题二:当请求URL为 /abc 时,匹配到了 /abc/*/abc ,哪个Servlet会响应?
  • 题三:当请求URL为 /abc/a.do 时,匹配到了 /abc/**.do ,哪个Servlet会响应?
  • 题四:当请求URL为 /a.do 时,匹配到了 *.do/* ,哪个Servlet会响应?
  • 题五:当请求URL为 /xxx/yyy/a.do 时,匹配到了 *.do/* ,哪个Servlet会响应?


——————————————
提问:
网站运行正常,网站URL下有多个Servlet URL;
现在来了一个任务:网站关闭,访问网站下所有URL,都会被显示“对不起,网站暂时关闭”;如何处理?

理所当然可以想到使用/*
但是其他正常URL的Servlet还存在,所以/*起不到作用,用户还是可以访问到想访问的URL;

若使用web.xml,可以把原有的web.xml备份一份,放在隔壁;
然后新建一个Servlet,doGet输出一句“对不起,网站暂时关闭”,在web.xml中只配置此Servlet的urlPattern为:/*,搞定;
网站修复成功后,把备份的web.xml恢复即可;
————————————————————————————
Servlet的单例问题

非常重要的问题,容易牵扯出很多麻烦事;

当Servlet第一次被访问后,就被加载到内存,以后该实例会对各个请求服务,即在使用中是单例的;
比如本blog中多线程文章里那个买火车票的问题,比如定义了2张票,买到一张票后thread.sleep(10*1000),但是被三个人分别买到一张(因为sleep期间,被其他线程访问,导致单例失败),在Servlet中也可以模拟一次;
成员变量:int ticket = 2;
解决办法和多线程一样,这里需要在doGet方法中,用synchronized(this){}把业务逻辑包围起来,就能解决这个问题;

@WebServlet(name = "ticket",urlPatterns = ("/ticket"))
public class Ticket extends HttpServlet {

    int ticket = 2;

    @Override
    protected void doGet(HttpServletRequest req,
                         HttpServletResponse resp)
            throws ServletException, IOException {

        synchronized(this){
            if(ticket >0){
                System.out.println("卖出一张票。");
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                ticket--;
            }else{
                System.out.println("票已售罄");
            }
        }
    }
}

提供一个原则:
如果一个变量需要多个用户共享,则应该在访问该变量的时候,加同步机制:synchronized(对象){};
如果一个变量不需要共享,直接在doGet()或者doPost()定义;

提供一个原则:
如果一个变量不需要共享,则直接在doGet()或者doPost()中定义,这样就不会出现线程安全的问题;

@WebServlet(name = "ticket",urlPatterns = ("/ticket"))
public class Ticket extends HttpServlet {

    int i = 0;

    @Override
    protected void doGet(HttpServletRequest req,
                         HttpServletResponse resp)
            throws ServletException, IOException {
        int j = 0;
        ++j;
        ++i;
        resp.getWriter().println("j = "+j+", i = "+i);
    }
}

访问一次:
j = 1, i = 1
访问二次:
j = 1, i = 2
访问三次:
j = 1, i = 3

因为i定义在方法外,是对象的成员变量,只要对象不消失,就不会变化;
j是局部变量,每发出一个请求都会新创建一个;

总结:

  • 提问:Servlet有没有线程安全问题?
    有;
    我们可以通过添加synchronized同步代码块来避免这个问题;
    另外,如果一个线程不需要共享,那么在doGet或者doPost中定义即可;

————————————————————————————
<load-on-startup>

需求:
当我们的网站启动的时候需要初始化一些数据,比如创建临时表,mysql的cache表就是临时表;
再比如,网站有一些定时完成的任务(比如定时写日志,定时备份数据库,定时发送邮件)

解决办法:
通过<load-on-startup>配合线程知识搞定;
loadOnStartup = 1,或者<load-on-startup>1</load-on-startup>)代表第一个启动;
(很多人不知道服务器可以起线程,其实这是很常见的,应该掌握)

如果在<servlet>中配置了一个<load-on-startup>元素,那么Web应用程序在启动时,就会装载并创建Servlet的实例对象,以及调用Servlet实例对象的init方法;

用途:为Web应用写一个InitServlet,这个Servlet配置为启动时装载,为整个Web应用创建必要的数据库表和数据;或者是启动一个后台线程,定时去完成某些工作(比如每隔10秒钟发送一封电子邮件)

——————————————

ServletConfig

——————————————

Servlet项目

——————————————
——————————————
——————————————
——————————————
——————————————
——————————————
——————————————
—————————————————————————————

结合HTTP来讲Servlet

结合【HTTP】的【请求头 Header】:
https://blog.csdn/weixin_42915286/article/details/84871898

  • 获取Host
    查看req的get方法们,没有getHost,只有getHeader,那么试试req.getHeader("Host");
     resp.setContentType("text/html;charset=utf-8");
     PrintWriter out = resp.getWriter();
     String host = req.getHeader("Host");
     out.println("host = "+host);

浏览器返回:
host = localhost:8080

  • 获取Referer
@WebServlet(name="myservlet",urlPatterns = {"/ouch"})
public class MyServlet extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest req, 
                         HttpServletResponse resp) 
                         throws ServletException, IOException {
        resp.setContentType("text/html;charset=utf-8");
        PrintWriter out = resp.getWriter();
        String referer = req.getHeader("referer");
        if(referer == null || !referer.startsWith("http://localhost:8080/ouch")){
            resp.sendRedirect("/forbidreferer");
            return;
        }
        out.println("referer = "+referer);
    }

!referer.startsWith("http://localhost:8080/ouch")
只允许这个链接(由网站点击进来的链接)发来的请求能被获得referer;
如果没有判空,输出为null;

@WebServlet(name="forbidreferer",urlPatterns = "/forbidreferer")
public class ForbidRefererServlet extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest req, 
                         HttpServletResponse resp) 
                         throws ServletException, IOException {
        resp.setContentType("text/html;charset=utf-8");
        PrintWriter out = resp.getWriter();
        String str = "禁止非法盗链。";
        out.println(str);
    }
}

于是访问localhost:8080/ouch时,返回空referer;
被redirect到了forbidreferer,返回:禁止非法盗链;

————————————————————————————————

HttpServletResponse

Web服务器收到客户端的HTTP请求,会针对每一次请求分别创建一个用户代表请求的Request对象,和代表相应的Response对象;

Request和Repsonse对象代表请求和相应,那我们要获取客户端提交来的数据,找Request对象即可;
要向客户端输出数据,找Response对象即可;

结合Servlet的生命周期那张图,Web服务器会把收到的HTTP请求包装成ServletRequest对象,作为Service方法的参数传入,而Service方法每次的发送都是新的,说明每次都会创建一个新的Request对象;
————————————————
HttpServletResponse封装了Web服务器向客户端发送数据、发送响应头、发送响应状态码的方法:

setStatus
setHeader
setWriter:Return a PrintWriter object that can send character text to the client.
setOutputStream:Return a ServletOutputStream suitable for writing binary data.

  • 提问:setWritersetOutputStream的区别?
    setWriter:向客户端返回 文本;
    setOutputStream:向客户端返回 二进制文件(字节数据)、文本(但是需要加上.getByte());

  • 提问:字符串能不能放在字节数据里输出?

可以,加上.getByte()即可;(但PrintWriter和OutputStream不能同时存在,不然报错)

    resp.setContentType("text/html;charset=utf-8");
    OutputStream os = resp.getOutputStream();
    os.write("用OutputStream输出".getBytes());
  • 提问:既然getWriter和getOutputStream都可以返回字符数据,怎么选?

建议,如果只返回字符数据,就使用PrintWriter对象,效率高;
如果返回字节数据(二进制文件)和字符数据,就只能用OutputStream对象,虽然他在字符传输方面效率低一些。

注意:PrintWriter和OutputStream不能同时存在,不然报错:500

  • 【重要】提问:为什么PrintWriter和OutputStream这两个流不能同时存在?
    比如这样使用,会报错:
    IllegalStateException: getWriter() has already been called for this response.
    (PrintWriter流已经被关闭了)
   resp.setContentType("text/html;charset=utf-8");
   OutputStream os = resp.getOutputStream();
   os.write("用OutputStream输出".getBytes());
   
   PrintWriter out = resp.getWriter();
   out.println("用PrintWriter输出");

  • 提问:为什么在Servlet使用完PrintWriterOutputStream后,不用配置流的关闭语句?

因为当一个Response信息被Web服务器返回后,Web服务器会自动检测此流有无被关闭,若未关闭,他会帮我们自动关闭;
当然,我们可以主动关闭流:os.close();;但是不关也可以;
————————————————
1.向客户端输出数据:
OutputStreamPrintWriter
但同一个Response中,不能同时使用他们两个;
————————————————
2.SendRedirect()可以实现请求重定向(可以带数据给下一个页面)
https://www.bilibili/video/av40697944/?p=21
2019一般使用AJAX进行重定向;
——————————————

中文乱码问题

发生中文乱码的情况:

  • 1.表单

    (1).POST
<!DOCTYPE html>
<html>
<head>
    <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
    <title>Test</title>
</head>
<body>
<h1>我的表单</h1>
<form action="/myformservlet" method="post">
    username:<input type="text" name="username"/><br/>
    <input type="submit" value="提交"/>
</form>
</body>
</html>
@WebServlet(name = "myformservlet",urlPatterns = {"/myformservlet"})
public class MyFormServlet extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest req,
                         HttpServletResponse resp)
            throws ServletException, IOException {
    }

    @Override
    protected void doPost(HttpServletRequest req,
                          HttpServletResponse resp)
            throws ServletException, IOException {
        // resp.setContentType("text/heml;charset=utf-8");
        // req.setCharacterEncoding("utf-8");
        PrintWriter out = resp.getWriter();
        String u = req.getParameter("username");
        out.println("u = "+u);
        System.out.println("u = "+u);
    }
}

针对POST方法:
此种情况下,浏览器输入汉字参数:username = 哈哈
如果缺少了resp.setContentType("text/heml;charset=utf-8");(浏览器端编码方式设置成utf-8)
req.setCharacterEncoding("utf-8");(Web服务器端设置成utf-8)
那么浏览器返回结果username = ??
如果加上可这句话,浏览器返回结果username = 哈哈

(2).GET

回顾之前,GET和POST的一大区别是:
GET参数会放在HTTP请求行里,而POST参数会封装在HTTP内容中;
所以GET参数出现中文乱码,问题在于请求行里出现乱码;
对于GET的中文参数出现乱码:仅仅填写req.setCharacterEncoding("utf-8");这种方式失效了,因为它仅仅针对请求体;

1.(个人推荐)这么写就解决了问题:resp.setContentType("text/heml;charset=utf-8");
2.或者:String u = new String(req.getParameter("username").getBytes("iso-8859-1"),"utf-8");

第二种方法可以写成一个工具类;

public class Tool{
        public static String getNewString(String str){
            String newString="";
            try{
                newString = new String(str.getBytes("iso-8859-1"),"utf-8");
            }catch(Exception e){
                e.printStackTrace();
            }
            return newString;
        }
}
  • 2.超链接
    大多数方式都是通过HTTP协议传输的,超链接本质上就是GET;
<!DOCTYPE html>
<html>
<head>
    <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
    <title>Test</title>
</head>
<body>
    <h1>我的表单</h1>
    <a href="/myformservlet?username=啦啦啦">测试</a>
</body>
</html>

访问浏览器,点开:测试,返回信息:
u = 啦啦啦
中文无乱码;

  • 3.sendRedirect()发生乱码

Reponse中可以这么写:
resp.setContentType
resp.setCharacterEncoding

protected void doGet(HttpServletRequest req, 
                     HttpServletResponse resp) 
                     throws ServletException, IOException {
    resp.setContentType("text/html");
    resp.setCharacterEncoding("utf-8");
    String str = "中文字符串";
    resp.getWriter().println(str);
}

或者【更建议】这么写:

protected void doGet(HttpServletRequest req, 
                     HttpServletResponse resp) 
                     throws ServletException, IOException {
    resp.setContentType("text/html;charset=utf-8");
    PrintWriter out = resp.getWriter();
    String str = "中文字符串";
    out.println(str);
}

返回的中文无乱码!!!

我写错成以下情况过,注意避免!!!
text/html;charset=utf-8 不要写成:content/html;utf-8,否则访问该页面会变成下载;
text/html;charset=utf-8 不要写成:text/html,charset=utf-8,否则中文仍然是乱码;

————————————————
用过HttpServletResponse返回的HTTP头,可以控制浏览器的行为,比如控制浏览器禁止缓存当前文档内容;
————————————————————————————————

HttpServletRequest

getOutputStreamgetWriter方法分别用于得到输出二进制数据、输出文本数据的ServletOutputStream、PrintWriter对象;
【在同一个Response对象中】,getOutputStream和getWriter两个方法互斥,调用了其中一个,就不能再调用另一个;
Servlet程序向ServletOutputStream或PrintWriter对象中写入的数据将被Servlet引擎从response中获取,Servlet引擎将这些数据当作响应消息的正文,然后再与响应状态行合格响应头组合后输出到客户端;
(意思就是doGet和doPost的内容都会经过service方法执行,然后被Servlet包装到response中,返回给Web服务器,Web服务器把他拆分成HTTP报文返回给浏览器)
Servlet的service方法结束后,Servlet引擎将检查getWriter或getOutputStream方法返回的输出流对象是否已经调用过close方法;如果没有,Servlet引擎会自动调用close方法;

HttpServletRequest对象代表客户端的请求,当客户端通过HTTP协议访问服务器时,HTTP请求头里所有信息都封装在这个对象中,开发人员通过这个对象的方法,可以获得客户信息;
————————————————
public interface HttpServletRequest extends ServletRequest

String getRequestURI(); 返回客户端发出Request时的完整URL;
StringBuffer getRequestURL(); 返回请求行的资源名;
String getQueryString(); 【很有用】准确来说就是接收GET方式提交的:参数名和值

public interface ServletRequest

String getRemoteAddr(); 返回发出Request的客户端IP;
String getRemoteHost(); 返回发出Request的客户端完整主机名;
int getRemotePort(); 返回发出Request的客户端使用的网络端口;
int getLocalPort(); 返回Web服务器所用的网络端口;
String getLocalAddr(); 返回Web服务器的IP;
String getLocalName(); 返回Web服务器的主机名;
————————————————

@WebServlet(name = "servlet1",urlPatterns = {"/oh"})
public class Servlet01 extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest req,
                         HttpServletResponse resp)
            throws ServletException, IOException {
        resp.setContentType("text/html;charset=utf-8");
        req.setCharacterEncoding("utf-8");
        PrintWriter out = resp.getWriter();

        // 得到URL http://localhost:8080/oh
        String url = req.getRequestURL().toString();
        System.out.println("URL = "+url);

        // 得到URI /oh
        String uri = req.getRequestURI();
        System.out.println("URI = "+uri);

        // 得到 输入的参数+值 如 localhost:8080/haha?username=tom
        String queryString =  req.getQueryString();
        System.out.println("queryString = "+queryString);

        // 得到 发出请求的客户端IP 127.0.0.1
        String addr = req.getRemoteAddr();
        System.out.println("addr = "+addr);

        // 得到 发出请求的客户端完整主机(客户端需要DNS注册名字,为注册则返回IP)
        String host = req.getRemoteHost();
        System.out.println("host = "+host);

        // 得到 客户端(浏览器)使用的端口 64494
        int port = req.getRemotePort();
        System.out.println("客户端(浏览器)使用的端口是:"+port);
        int serverPort = req.getLocalPort();
        System.out.println("Web服务器使用的端口是:"+serverPort);
URL = http://localhost:8080/oh
URI = /oh
queryString = null
addr = 0:0:0:0:0:0:0:1
host = 0:0:0:0:0:0:0:1
客户端(浏览器)使用的端口是:64494
Web服务器使用的端口是:8080

————————————————

  • 面试提问:我想用GET方法在URL输入自定义属性和参数,浏览器都能返回一模一样的属性和参数列表,怎么实现?
    比如我输入URL如下:
    localhost:8080/oh?name=haha&city=北京
    浏览器返回:name = haha city = 北京
    localhost:8080/oh?名字=汤姆&city=上海
    浏览器返回:名字 = 汤姆 city = 上海
        PrintWriter out = resp.getWriter();

        String queryString = req.getQueryString();

        String querStrings[] = queryString.split("&");
        for(String s:querStrings){
            String[] name_val = s.split("=");
            out.println(name_val[0]+" = "+UTF8Utils.getNewString(name_val[1]));
        }

要实现中文不乱码还要用到UTF8Utils里getNewString方法…

其实工作中用不到用get传递属性和参数的地方;

—————————————————————————————

会话

会话技术在JavaEE中主要分成两种:Cookie + Session;

  • 提问:什么是Session会话?(重要)
    用户用浏览器访问【某一个网站】,点击多个超链接,访问服务器多个Web资源,然后关闭浏览器,整个过程叫做【一个会话】;
    (会话只针对一个网站:如果说在浏览器访问sohu后,又去访问sina,访问了不止一个网站,这样就不止一个会话了)
    (只要不关闭浏览器,不管该用户在这【一个】网站里点了多少超链接,访问多少资源,直到关闭浏览器;整个过程都叫【一个会话】)
    (Request是发送一个请求就算一次新的请求,而会话是只要不关闭浏览器+不打开其他网站 就算一次)

会话过程中要解决的一些问题:
每个用户在使用浏览器和服务器进行会话过程中,不可避免个字会产生一些数据,服务器要想办法为每个用户保存这些数据;
例如:多个用户点击超链接通过一个Servlet各自购买了一个商品,服务器应该想办法把每一个用户购买的商品保存在各自的地方,一边这些用户买单时,买单Servlet可以得到各自用户各自购买的商品为用户账单;

提问:这些数据保存在Request可不可以?
不可以,因为点一次商品链接就是一次Request,所以不能保存;

—————————————————————————————
进入Cookie章
进入Cookie章
进入Cookie章

先思考一些问题:
1.想一想,有些网站上可以显示用户上一次登录的时间,如果不用Cookie的话,能够怎么样实现?

可以在DB中新建一个lastVisitTime列表,用户一旦登录就记录下此刻时间,下一次登录时就可以读取这个数据;
然后用户之后每次登录都会替换掉原有的lastVisitTime;

这个方法是可行的,但是显然没有使用Cookie好;
————————————————
2.大家在访问购物网站时,能看到提示:你曾经浏览过的商品,不同用户浏览过的商品是不一样的,怎么实现?

也可以保存在DB里,但是这些方法都是在已经登录的情况下才能查询DB;
我们的要求是,未登录时也有记录显示;
所以未登录时,用DB是绝对查不到的,因为DB在服务器那头,我们不能在本地就实现这些功能;

————————————————
————————————————
3.每次我们非首次登录某网站账号时,往往都可选择是否保存登录信息,不用再手动输入了;网站是怎么记下这些信息的?

以上问题的解决方案都是:Cookie;
————————————————

Cookie

Cookie是客户端技术,服务器把每个用户的数据以Cookie形式写给用户各自的客户端;
当用户再使用浏览器访问服务器中的Web资源时,就会带着各自的数据去;
这样,Web资源处理的就是用户各自的数据了;

  • 提问:什么是Cookie?
    服务器在客户端保存用户的信息,比如登录名、密码等,就是Cookie;
    这些信息就像小甜饼一样,数据量并不大,服务端在需要的时候可以从客户端获取,保存在客户端的浏览器缓存目录下;

    ————————————————————————————————
    根据原理图,我们发现制造Cookie分成以下几个步骤:

1.在服务器生成Cookie;

查看Cookie API:

查看构造器,可发现Cookie只允许简单存储String信息(String name,String value),无法存储对象;
也发现它来自:javax.servlet.http.Cookie

Cookie cookie = new Cookie("name","yau");

提问:Cookie的生命周期有多长?可以存放多久?

我们发现API内有这个方法:
void setMaxAge(int expiry)
Sets the maximum age of the cookie in seconds.
可以给Cookie设置最大存活时间(单位:秒)
注意:如果不设置setMaxAge,此Cookie会是个短命鬼,马上OVER;

我们设置一个小时试试:cookie.setMaxAge(3600


resp.addCookie(cookie);

import javax.servlet.http.Cookie;

@WebServlet(name = "cookieservlet",urlPatterns = {"/cookieservlet"})
public class CookieServlet extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest req,
                         HttpServletResponse resp)
            throws ServletException, IOException {
        resp.setContentType("text/html;charset=utf-8");
        PrintWriter out = resp.getWriter();

        Cookie cookie = new Cookie("name","yau");
        cookie.setMaxAge(3600); // 单位是秒
        resp.addCookie(cookie);
    }
}



————————————————
2.获取Cookie

提问:怎么把服务器中的Cookie返回到本地浏览器?

没办法根据参数读取Cookie,只能获得全部Cookie集合,再通过for循环筛选;
注意!获取本地Cookie要通过Request对象,而不是Response对象!!

Cookie[] getCookies()
Returns an array containing all of the Cookie objects the client sent with this request.
This method returns null if no cookies were sent.
(注意!Response没有getCookie这一功能)

@WebServlet(name = "readcookieservlet",urlPatterns = {"/readcookieservlet"})
public class ReadCookieServlet extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest req,
                         HttpServletResponse resp)
            throws ServletException, IOException {
        resp.setContentType("text/html;charset=utf-8");
        PrintWriter out = resp.getWriter();

        Cookie cookies[] = req.getCookies();
        //System.out.println(cookies.length); 查看本地Cookie个数
        for(int i = 0 ; i<cookies.length ; i++){
            Cookie cookie = cookies[i];
            out.println("cookie的信息:名字 = "+cookie.getName()+" 参数 = "+cookie.getValue());
        }
    }
}

为什么要在Request取得Cookie,而不是Response?
这个问题我讨论了很久,终于想清楚了;

我当初的想法是:第一次Request时,Cookie根本还没存在;Cookie在服务器里产生,通过Response返回给浏览器;所以觉得应该在Request里面拿Cookie;

我搞错的一点是:拿Cookie,是拿浏览器本地已经存在的Cookie文件;
第一次Request时确实无Cookie,本地Cookie文件是从第一次Response后才会出现在本地的;
我要拿到这个本地Cookie,只能从他下一次Request时拿到,而不是Response时!!!

注:若获取Cookie时报错NullPointerException,可能是原Cookie已过期(这里设置的是3600秒);
————————————————

  • 提问:不同浏览器可以共享Cookie吗?

一般情况下不能;
我认为:
1.浏览器内核不同;
2.浏览器Cookie存储位置不同(有的浏览器还不一定保存在本地文件夹里!!!);
3.Cookie加密存储,非明文存储;
4.出于安全考虑,浏览器厂商不会让你轻易得到Cookie;
————————————————

  • 提问:Cookie能干嘛?
    1.保存上次登录时间信息等;
    2.保存用户名密码;
    3.记录用户访问网站的喜好(背景音乐、背景色等等);
    4.网站个性化,比如定制网站服务、内容;

————————————————
Cookie的基本使用

1.Cookie像一张表,有两列:name和value;
2.实例化Cookie:Cookie cookie = new Cookie(String name,String value)
cookie.setMaxAge(秒);
3.把Cookie添加到客户端:resp.addCookie(cookie);
4.读取Cookie:Cookie cookies[] = request.getCookies()
————————————————

  • 提问:Cookie的属性如果重名怎么办?

一个Servlet类声明name,tom
另一个Servlet类声明name,jack
结果会是什么?
答案会覆盖;
————————————————

  • 提问:Cookie存储中文参数报错了怎么办?
@WebServlet(name = "ha",urlPatterns = "/ha")
public class CreateChineseCookie extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest req,
                         HttpServletResponse resp)
            throws ServletException, IOException {
        resp.setContentType("text/html;charset=utf-8");
        PrintWriter out = resp.getWriter();

        Cookie cookie = new Cookie("name","这是中文参数");
        cookie.setMaxAge(3600);
        resp.addCookie(cookie);
    }

浏览器访问,报错:
java.lang.IllegalArgumentException: Cookie name [哈哈] is a reserved token

中文和空格编码:base64
把中文参数Encoder(编码、加密)成utf-8即可;
添加:String value = java.URLEncoder.encode("这是中文参数","utf-8");

@WebServlet(name = "ha",urlPatterns = "/ha")
public class CreateChineseCookie extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest req,
                         HttpServletResponse resp)
            throws ServletException, IOException {
        resp.setContentType("text/html;charset=utf-8");
        PrintWriter out = resp.getWriter();

        String value = java.URLEncoder.encode("这是中文参数","utf-8");
        Cookie cookie = new Cookie("name",value);

        //Cookie cookie = new Cookie("哈哈","这是参数");
        cookie.setMaxAge(3600);
        resp.addCookie(cookie);
    }
}

我们查看该Cookie时看到的中文参数是一堆看不懂的信息,这就是base64,总之它解决了乱码;

怎么读取中文参数呢?
如果未处理,读取中文参数也会出现乱码;

@WebServlet(name = "ha1",urlPatterns = "/ha1")
public class ReadChineseCookie extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest req,
                         HttpServletResponse resp)
            throws ServletException, IOException {
        resp.setContentType("text/html;charset=utf-8");
        PrintWriter out = resp.getWriter();

        Cookie cookies[] = req.getCookies();
        for(int i = 0;i<cookies.length;i++){
            Cookie cookie = cookies[i];
            out.println("Cookie名:"+cookie.getName()+";Cookie参数:"+cookie.getValue());
        }
    }
}

浏览器返回:
Cookie名:name,Cookie参数:%E8%BF%99%E6%98%AF%E4%B8%AD%E6%96%87%E5%8F%82%E6%95%B0

解决办法:把中文参数的base64格式转换(解码)成utf-8即可;
判断属性是否为name,如果是,就Decode;
(注意!判断属性是否为name时要用equals,不能用==,否则无效)

@WebServlet(name = "ha1",urlPatterns = "/ha1")
public class ReadChineseCookie extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest req,
                         HttpServletResponse resp)
            throws ServletException, IOException {
        resp.setContentType("text/html;charset=utf-8");
        PrintWriter out = resp.getWriter();

        Cookie cookies[] = req.getCookies();
        for(int i = 0;i<cookies.length;i++){
            Cookie cookie = cookies[i];
            if(cookie.getName().equals("name")){
                String val = java.URLDecoder.decode(cookie.getValue(),"utf-8");
                out.println("Cookie名:"+cookie.getName()+";Cookie参数:"+val);
            }else{
                out.println("Cookie名:"+cookie.getName()+";Cookie参数:"+cookie.getValue());
            }

        }
    }

浏览器返回:
Cookie名:name;Cookie参数:这是中文参数
————————————————
Cookie小结:
(1).Cookie创建于服务器;
(2).Cookie保存在浏览器;
(3).Cookie的生命周期通过cookie.setMaxAge(秒)来配置;
如果不配置生命周期(秒),若浏览器关闭Cookie则消失;

—————————————————————————————
进入Session章
进入Session章
进入Session章

Session

Session是服务器端技术(Cookie是客户端技术),利用这个技术,服务器可以在运行时为每个用户的浏览器创建一个供其独享的Session对象;
由于Session为用户浏览器独享,所以用户在访问服务器的Web资源时,可以把各自的数据放在各自的Session中,当用户在去访问服务器中其他Web资源时,其他Web资源再从用户各自的Session中去除数据为用户服务;

当用户打开浏览器访问某网站(操作Session)时,服务器会在【服务器内存】中为该浏览器分配一个Session对象,该Session对象被这个浏览器独享;
这个Session对象也可以看作是一个容器,Session默认存在时间30min,可以自行修改;


注:Session的生命周期?
服务器关闭则消失;
浏览器关闭则消失;
默认生命周期30分钟到期则消失;
————————————————
HttpSession

getSession()
Returns the current session associated with this request,
or if the request does not have a session, creates one.
说明第一次访问getSession时(Session还不存在),会自动创建一个;

getSession(boolean create)
Returns the current HttpSession associated with this request or,
if there is no current session and create is true, returns a new session.

易错!创建和获取Session要用到的包是:HttpSession,而不是Session!!!

@WebServlet(name="session1",urlPatterns="/session1")
public class Session1 extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest req, 
                         HttpServletResponse resp) 
            throws ServletException, IOException {
        resp.setContentType("text/html;charset=utf-8");
        PrintWriter out = resp.getWriter();

        HttpSession session =  req.getSession(); // 第一次获取session = 创建
        session.setAttribute("username","Ivy");
        session.setAttribute("age","30");
        // session.setMaxInactiveInterval(分钟);
        System.out.println("Session已创建");
    }
}

————————————————

getAttribute(java.lang.String name)
Returns the object bound with the specified name in this session
or null if no object is bound under the name.

易错!取出Session的属性不能用String username = req.getSession("username");
取出Session的属性要用String username = (String)req.getSession().getAttribute("username");
取出Session还要注意强制转换!!!

@WebServlet(name="session2",urlPatterns="/session2")
public class Session2 extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        resp.setContentType("text/html;charset=utf-8");
        PrintWriter out = resp.getWriter();

        HttpSession session =  req.getSession(false); // 参数false表示会直接获取Session,不会新建
        String username = (String)session.getAttribute("username");
        if(username == null){
            System.out.println("Session中不存在username或者age");
        }else{
            System.out.println("session username = "+username);
        }
    }
}

因为Session在内存中(不像Cookie确实存在文件),不能抓出来看,只能验证是否存在;

换个浏览器访问servlet2,发现Session不能共享;

一旦服务器关闭,Session则消失,因为Session是服务端技术,回顾Session概念;
而一旦更换浏览器(不同内核的浏览器),Session则不能共享,因为一个浏览器独占一份Session;
————————————————
Session可以用来干嘛?

1.商城购物车;
2.保存登录用户的信息;
3.将某些数据放入Session中,供同一用户各个页面使用;
4.防止用户非法登录到各个页面;
————————————————

  • 提问:如果同一个浏览器中,向Session设置了两个name相同的属性,会出现什么情况?

第一个value被覆盖;
————————————————
Session基本使用:

得到Session:
HttpSession session = req.getSession();
向Session添加属性:
session.setAttribute(String username,Object val);
从Session得到某属性:
String name = (String)session.getAttribute(String name)
从Session剔除某属性:
session.removeAttribute(String name);

————————————————
注意!!!

  • 提问:.getSession(ture).getSession(false)的区别是什么?

意思就是,因为HttpSession session = req.getSession();有两层含义:
(1).第一次使用(Session不存在时),自动创建Session;
(2).第二次使用(Session存在时),获取该Session;

所以为了更好的区分getSession(),避免【误创建】新Session,我们可以在参数里加上truefalse来区分他;

  • HttpSession session = req.getSession(true);
    完全等于HttpSession session = req.getSession();
  • HttpSession session = req.getSession(false);
    我绝对不想新建Session(如果不存在Session时返回null,提醒你该Session还不存在),只获取该Session;

因此,一般情况下,尽量要明确使用参数为false的写法。

【问题和bug】:

我周围很多同事是这样写的:

HttpSession session = request.getSession(); // a new session created if no session exists, 完蛋啦!如果session不存在的话你又创建了一个!

String user_name = session.getAttribute(“user_name”);

需要注意的地方是request.getSession() 等同于 request.getSession(true),除非我们确认session一定存在或者sesson不存在时明确有创建session的需要,否则 尽量使用request.getSession(false)。在使用request.getSession()函数,通常在action中检查是否有某 个变量/标记存放在session中。

我们在本文的例子里怎么实践这个问题?

因为Servlet1和Servlet2中:
Servlet1的创建Session和Servlet2的查看Session语句是一样的;
我们为了保证Servlet2只会查看,不会新建Session,把Servlet2的语句参数改成false,这样就能保证Servlet2只能查看Servlet1创建的Session;

若先运行Servlet2,看看是否会新建Session?报错空指针异常,因为false语句不能创建新Session;
只能先Servlet1,再Servlet2;
————————————————
实践删除Session中的属性

需要配合前面的Session1(来新建Session);

Session3
我做了一个判空,如果显示Session不存在时,再启动Session1新建Session;

@WebServlet(name = "session3",urlPatterns = "/session3")
public class Session3 extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest req, 
                         HttpServletResponse resp) 
            throws ServletException, IOException {
        resp.setContentType("text/html;charset=utf-8");
        PrintWriter out = resp.getWriter();

        HttpSession session = req.getSession(false);
        if(session == null){
            System.out.println("Session不存在");
            out.println("Session不存在");
        }else{
            session.removeAttribute("age");
            System.out.println("已删除Session中的age属性");
            out.println("已删除Session中的age属性");
        }
    }
}

然后再根据其他Servlet类的逻辑检验是否存在指定属性;
————————————————
Session中Value存储对象

@WebServlet(name="session1",urlPatterns="/session1")
public class Session1 extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest req,
                         HttpServletResponse resp)
            throws ServletException, IOException {
        resp.setContentType("text/html;charset=utf-8");
        PrintWriter out = resp.getWriter();

        HttpSession session =  req.getSession(); // 第一次获取session = 创建
        User user = new User();
        user.setName("小猫");
        user.setAge(5);

        session.setAttribute("猫",user);
    }
}
@WebServlet(name="session2",urlPatterns="/session2")
public class Session2 extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest req,
                         HttpServletResponse resp)
            throws ServletException, IOException {
        resp.setContentType("text/html;charset=utf-8");
        PrintWriter out = resp.getWriter();

        HttpSession session =  req.getSession(false);
        User user = (User)session.getAttribute("猫");
        if(user == null){
            System.out.println("Session中不存在「猫」这个属性");
            out.println("Session中不存在「猫」这个属性");
        }else{
            System.out.println("「猫」的name为:"+user.getName()+",age为:"+user.getAge());
            out.println("「猫」的name为:"+user.getName()+",age为:"+user.getAge());
        }
    }
}

————————————————
Session小结:
1.Session存在服务器内存中(去服务器文件里是找不到的);
2.一个浏览器(不同内核)独享一个Session域对象;
3.(重要!)Session默认生命周期30min,可以通过Tomcat的web.xml修改(单位:分钟);
tomcat/conf/web.xml:

<sessin-config>
    <session-timeout>30</session-timeout>
...

或者直接在项目的web.xml修改:

<session-config>
    <session-timeout>分钟</session-timeout>
...

如果发生冲突,以项目里的web.xml为准;
或者项目里修改:(单位秒,因为它针对单个Session)
session.setMaxInactiveInterval(秒);

(Session只能全部配置周期,Cookie可以单个配置周期)

  • Session的周期是发呆时间,如果设置Session周期10s,10s内如果没有访问,session失效;
    如果第9秒访问了,那么重新计时;如果重启tomcat或reload了web应用或关机,session也会失效;
    invalidate()方法也可以使所有session失效,用于安全退出(不过这个方法在实际的开发中,并不推荐,可能在强制注销用户的时候会使用);
    如果想让某属性失效,removeAttribute(String name)

特别注意!Session的生命周期完全由服务器负责,跟浏览器无关,浏览器关掉了,只要没超出生命周期,Session就会存在!!!

  • Cookie的生命周期是累积时间,不管有无访问过了时间都会失效;

4.Session可以存放多个属性;
5.Session可以存放对象;
6.如session.setAttribute(String name,Object val),如果名字重复,value会被覆盖(相当于修改);

————————————————

服务器如何实现一个Session只为一个浏览器服务的?


比如Chrome即使访问了A2,也会回到SessionA?
IE即使访问了A2,也知道要创建一个新的SessionC?不会去到SessionB?

————————————————

既然在生命周期内,浏览器关掉,Session仍存在,为何再打开浏览器,Session会消失?

Session的生命周期完全由服务器负责,跟浏览器无关,浏览器关掉了,只要没超出生命周期,Session就会存在!!!

那么,为何关闭浏览器后,再次访问会觉得session失效了呢,这里的失效意思是session的数据丢失了?

其实之前的Session一直都在服务器端;
而当我们关闭浏览器时,此时的Cookie是存在于浏览器的进程中的,当浏览器关闭时,Cookie也就不存在了。

其实Cookie有两种:

  • 一种:会话Cookie,存在于浏览器的进程中;
  • 一种是存在于硬盘上

Session的Cookie是存在于浏览器的进程中,那么这种Cookie我们称为会话Cookie;
当我们重新打开浏览器窗口时,之前的Cookie中存放的Sessionid已经不存在了;
此时服务器从HttpServletRequest对象中没有检查到sessionid,服务器会再发送一个新的存有SessionidCookie到客户端的浏览器中,此时对应的是一个新的会话,而服务器上原先的session等到它的默认时间到之后,便会自动销毁。

其实这里session数据并没有丢失;
只是关闭浏览器后,因为默认的cookie生命周期为浏览器的缓存,即关掉浏览器之后cookie就失效了,此时JSESSIONID也就没有了。
再次访问后,服务器又生成一个新的JSESSIONID,此时request.getSession()通过JSESSIONID获取到的session就不是之前的session了。

要把Cookie保存在硬盘上?
在生成Session的Servlet中简单加上代码即可:

Cookie cookie = new Cookie("JSESSIONID",session.getId());
cookie.setMaxAge(60*30);
resp.addCookie(cookie);

实践:如何在浏览器重启后,且在Session生命周期内,恢复关闭前的Session?

分析:
1.Session生命周期默认30分钟,该Session不会随着浏览器的关闭而关闭 - 而自动销毁,回到了30分钟后,被【服务器】销毁;(销毁掌握在服务器手里,不管你的浏览器关不关)
2.我们使用代码来实现该功能(Session + Cookie);

改造前:

@WebServlet(name = "cart1",urlPatterns = "/cart1")
public class CartSession1 extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest req,
                         HttpServletResponse resp)
            throws ServletException, IOException {
        resp.setContentType("text/html;charset=utf-8");
        PrintWriter out = resp.getWriter();

        HttpSession session = req.getSession();
        session.setAttribute("username","Colon");
        out.println("Session创建成功,且放入了属性:username。");
    }
}

@WebServlet(name = "cart2",urlPatterns = "/cart2")
public class CartSession2 extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest req,
                         HttpServletResponse resp)
            throws ServletException, IOException {
        resp.setContentType("text/html;charset=utf-8");
        PrintWriter out = resp.getWriter();

        String username = (String)req.getSession().getAttribute("username");
        if (username.equals(null)){
            out.println("username为空。");
        }else{
            out.println("Session取回成功:username = "+username);
        }
    }
}


重启浏览器,再访问第二个Servlet,看Session是否还存在;

开始改造

添加三行数据,保存Cookie到硬盘;
注意!Cookie的属性名必须和浏览器规范:JSESSIONID保持一摸一样,否则浏览器不能识别!!!
(如果写成小写,浏览器是无法识别的)

@WebServlet(name = "cart1",urlPatterns = "/cart1")
public class CartSession1 extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest req,
                         HttpServletResponse resp)
            throws ServletException, IOException {
        resp.setContentType("text/html;charset=utf-8");
        PrintWriter out = resp.getWriter();

        HttpSession session = req.getSession();
        session.setAttribute("username","Colon");
        out.println("Session创建成功,且放入了属性:username。");
        
        Cookie cookie = new Cookie("JSESSIONID",session.getId());
        cookie.setMaxAge(60*30);
        resp.addCookie(cookie);
    }
}


————————————————

浏览器禁用Cookie带来的问题(URL重写)


————————————————
Cookie和Session有千丝万缕的联系;
Session之所以能用,是因为Cookie在传递编号;
上一个例子里,一旦浏览器禁用了Cookie,即使不用关闭浏览器,Servlet1运行后,Servlet2都无法显示参数了;

禁用Cookie后:
Servlet1响应中仍有set-cookie产生;
但Servlet2运行时,HTTP请求中无cookie(JSESSIONID)发送;
————————————————
有些用户喜欢禁用浏览器Cookie,那么我们应该弥补这一缺陷呢?

  • 提问:浏览器禁用Cookie后,我们应该怎样保证Session的正常运行?

URL重写
https://www.bilibili/video/av40697944/?p=46

用关于对sendRedirect方法后的URL进行重写;
resp.encodeRedirectURL(String URL);

用于对表单action和超链接的URL进行重写;
resp.encodeURL(String URL);
—————————————————————————————

Filter

—————————————————————————————

ServletContext

这是Servlet和JSP中共同的重要技术;

大家在访问某网站时,往往可以看到网站首页可以显示你是第几位访问者(网站计数器),这是怎么实现的?
我们在访问某论坛时,可以看到有多少人在线,这是怎么实现的?

https://www.bilibili/video/av40697944/?p=47

  • 提问:什么是 ServletContext

当我们启动 web 容器的后,会为每个 web 应用程序都创建一个对应的 ServletContext 对象,它代表当前 web 应用。
由于一个 web 应用中的所有 Servlet 共享同一个 servletcontext 对象,因此servlet 对象之间可以通过 servletcontext 对象来实现通讯。ServletContext 对象通常也被称之为 context 域对象。公共聊天室的项目就会用到它。

————————————————

————————————————

—————————————————————————————

—————————————————————————————

—————————————————————————————

—————————————————————————————

—————————————————————————————

—————————————————————————————

用IDEA Spring Boot 新建一个Servlet项目(建议)

新建步骤和普通的Spring Boot项目一样;
和用Web Application模版使用的差别是:
需要在启动项添加注解:@ServletComponentScan()(写上Servlet包所在路径)
—————————————————————————————

用IDEA Web Application 新建一个Servlet项目


新建两个文件夹:classes、lib;




—————————————————————————————


以前写的

ServletAPI官方文档:http://tomcat.apache/tomcat-8.0-doc/servletapi/

Servlet

一门用于开发动态web资源的技术,由sun公司提供;
JAVA类编写的服务端应用程序;
Servlet程序是由web服务器(tomcat等)调用的;

说简单点,就是Sun公司开发出了一套API(java.servlet、java.sevlet、http等),你可以用这些api开发Java程序,用来处理客户端请求,并控制输出的响应(向客户端浏览器输出的内容)。

总结起来,你只需要:
① 编写一个实现了servlet接口的java类;
② 把开发好的Java类部署到web服务器中。

所以,有时我们也称实现了servlet接口的java程序为Servlet。

Java Servlet 是运行在 Web 服务器或应用服务器上的程序,独立于平台和协议,可以动态的生成web页面。它是作为来自 Web 浏览器或其他 HTTP 客户端的请求和 HTTP 服务器上的数据库或应用程序之间的中间层。

使用 Servlet,您可以收集来自网页表单的用户输入,呈现来自数据库或者其他源的记录,还可以动态创建网页。

Java Servlet 通常情况下与使用 CGI(Common Gateway Interface,公共网关接口)实现的程序可以达到异曲同工的效果。但是相比于 CGI,Servlet 有以下几点优势:
性能明显更好。
Servlet 在 Web 服务器的地址空间内执行。这样它就没有必要再创建一个单独的进程来处理每个客户端请求。
Servlet 是独立于平台的,因为它们是用 Java 编写的。
服务器上的 Java 安全管理器执行了一系列限制,以保护服务器计算机上的资源。因此,Servlet 是可信的。
Java 类库的全部功能对 Servlet 来说都是可用的。它可以通过 sockets 和 RMI 机制与 applets、数据库或其他软件进行交互。

——————————————————

Servlet的功能

Servlet 执行以下主要任务:

  • 读取】【客户端】(浏览器)发送的【显式数据】。这包括网页上的 HTML 表单,或者也可以是来自 applet 或自定义的 HTTP 客户端程序的表单。
  • 读取】【客户端】(浏览器)发送的【隐式的 HTTP 请求数据】。这包括 cookies、媒体类型和浏览器能理解的压缩格式等等。
  • 处理数据】并【生成结果】。这个过程可能需要访问数据库,执行 RMI 或 CORBA 调用,调用 Web 服务,或者直接计算得出对应的响应。
  • 发送】【显式数据】(即文档)到【客户端】(浏览器)。该文档的格式可以是多种多样的,包括文本文件(HTML 或 XML)、二进制文件(GIF 图像)、Excel 等。
  • 发送】【隐式的 HTTP 响应】到【客户端】(浏览器)。这包括告诉浏览器或其他客户端被返回的文档类型(例如 HTML),设置 cookies 和缓存参数,以及其他类似的任务。

1.对客户端发送的数据进行读取和拦截;
客户端发送一个请求会携带数据(url中的参数,页面表单提交,ajax请求),当一个Servlet接收到请求,java Servlet中的类可以通过提供的方法获得这些参数(request.getParameterName(name)等)。因此Servlet可以起拦截作用,在请求前先判断该客户端能否做出这些请求 ,若能再从服务器得到请求的资源。

2.读取客户端的请求的隐含数据
客户端请求的数据可分为:隐含数据,显式数据。

  • 隐含数据:不存在于URL中,而存在于请求的来源,缓存数据(cookie)、客户端类型(在header中)。
  • 显式数据:用户可见的,表单数据(来自于form)或URL参数。
    Servlet都可处理。

3.运行结果或者生成结果
一个Web应用程序对客户端发出的请求做出响应时,一般有很多中间过程才能得到结果;
Servlet就担起了这个中间角色功能。
(后面深入学习会知道,javaweb开发中的控制层是由Servlet实现的,就是常说的MVC中的C)

4.发送响应的数据
Servlet对客户端做出响应并经过处理得出结果后,会对客户端发送响应的数据,让客户端获取请求的结果数据。

——————————————————

  • 提问:Servlet的生命周期

——————
分成五部分:

1.【加载Servlet实例】:当Tomcat第一次访问Servlet时,Tomcat会负责创建Servlet的实例;
2.【初始化】:当Servlet被实例化后,Tomcat会调用【init()】初始化此对象;
3.【处理服务】:当浏览器访问Servlet时,Servlet会调用【service()】处理请求;
4.【销毁】:当Tomcat关闭或检测到Servlet要从Tomcat删除的时候,会自动调用【destroy()】,让该实例释放掉所占的资源;如果一个Servlet长期不被使用的话,也会被Tomcat自动销毁;
5.【卸载】:destroy()调用完后,等待【垃圾回收】。如果再次需要使用此Servlet,才会重新调用init()方法初始化操作;
——————
简单说,分成三部分:
1.【初始化】:Web容器加载servlet,调用init()方法
2.【处理请求】:当请求到达时,运行其service()方法。service()自动派遣运行与请求相对应的doXXX(doGet或者doPost)方法。
3.【销毁】:服务结束,web容器会调用servlet的destroy()方法销毁servlet。
——————
简单总结:
只要访问Servlet,service()就会被调用;
init()只有第一次访问Servlet时才会被调用;
destroy()只有在Tomcat关闭时才会被调用;

  • 1. 初始化阶段
    当web服务器收到客户端对某一url地址的访问请求时,若该url配置了对应的Servlet程序,则开始该阶段。
    检查请求url是否有对应的Servlet程序:

  • web服务器检查是否已经装载并创建了该Servlet的实例对象。如果是,则跳过初始化阶段,否则进入下一步。
  • 装载(由Servlet容器装载一个Servlet类,把它装在到Java内存中)并创建该Servlet的一个实例对象,与 web.xml 中的配置对应起来。
  • 调用Servlet实例对象的 init() 方法。在整个Servlet生命周期中,init() 方法只被调用一次。


步骤图示


首次访问的两个注意点

  • 2. 运行阶段
    该阶段实际响应客户端的请求。

Servlet创建 HttpServletRequest 和 HttpServletResponse 对象,前者与HTTP协议有关,后者与协议无关。
调用 service(HttpServletRequest request, HttpServletResponse response) 方法,该方法通过request对象获得请求对象的信息并加以处理,再由response对象给客户端做出响应。

每当有请求时,服务端的一系列动作


调用 service() 方法,可访问请求和响应对象


运用request对象读取请求信息,处理后运用response对象写入响应信息


service() 方法返回


服务器读取响应信息


服务器发出http响应,将数据返回到客户端浏览器中

  • 3. 消亡阶段

web应用程序被停止或重新启动之前,Servlet容器将调用 destroy() 方法,对Servlet对象进行销毁,释放其所占资源。在整个Servlet生命周期中,destory() 方法只被调用一次。

参考博客
https://www.jianshu/p/6cd40132eb71

————————————————————————————

  • 提问:get提交和post提交有何区别?

(1)get一般用于从服务器上获取数据,post一般用于向服务器传送数据

(2)请求的时候参数的位置有区别,get的参数是拼接在url后面,用户在浏览器地址栏可以看到。post是放在http包的包体中。

比如说用户注册,你不能把用户提交的注册信息用get的方式吧,那不是说把用户的注册信息都显示在Url上了吗,是不安全的。

(3)能提交的数据有区别,get方式能提交的数据只能是文本,且大小不超过1024个字节,而post不仅可以提交文本还有二进制文件。

所以说想上传文件的话,那我们就需要使用post请求方式

(4)servlet在处理请求的时候分别对应使用doGet和doPost方式进行处理请求
————————————————————————————

  • 提问:JSP与Servlet有什么区别?

Servlet是服务器端的程序,动态生成html页面发送到客户端,但是这样程序里会有很多out.println(),java与html语言混在一起

很乱,所以后来sun公司推出了JSP.其实JSP就是Servlet,每次运行的时候JSP都首先被编译成servlet文件,然后再被编译成

.class文件运行。有了jsp,在MVC项目中servlet不再负责动态生成页面,转而去负责控制程序逻辑的作用,控制jsp与javabean之间的流转。
————————————————————————————

  • 提问:doGetdoPost方法的两个参数是什么?

HttpServletRequest:封装了与请求相关的信息

HttpServletResponse:封装了与响应相关的信息
————————————————————————————

  • 提问:request.getAttribute()和request.getParameter?

(1)有setAttribute,没有setParameter方法

(2)getParameter获取到的值只能是字符串,不可以是对象,而getAttribute获取到的值是Object类型的。

(3)通过form表单或者url来向另一个页面或者servlet传递参数的时候需要用getParameter获取值;getAttribute只能获取setAttribute的值

(4)setAttribute是应用服务器把这个对象放到该页面所对应的一块内存当中,当你的页面服务器重定向到另一个页面的时候,应用服务器

会把这块内存拷贝到另一个页面对应的内存当中。通过getAttribute可以取得你存下的值,当然这种方法可以用来传对象。

用session也是一样的道理,这是说request和session的生命周期不一样而已。
————————————————————————————

  • 提问:forward和redirect的区别?

转发与重定向

(1)从地址栏显示来说

forward是服务器请求资源,服务器直接访问目标地址的URL,把那个URL的响应内容读取过来,然后把这些内容再发给浏览器.浏览器根本不知道服务器发送

的内容从哪里来的,所以它的地址栏还是原来的地址.redirect是服务端根据逻辑,发送一个状态码,告诉浏览器重新去请求那个地址.所以地址栏显示的是

新的URL.

(2)从数据共享来说

forward:转发页面和转发到的页面可以共享request里面的数据.

redirect:不能共享数据.

(3)从运用地方来说

forward:一般用于用户登陆的时候,根据角色转发到相应的模块.

redirect:一般用于用户注销登陆时返回主页面和跳转到其它的网站等.

(4)从效率来说

forward:高.

redirect:低.


Servlet和Filter的区别?(面试真题)

从四个方面来区分:
——————————
【概念】

Servlet
一种运行在Server端的Java应用程序,独立于平台和协议,可以动态的生成web页面,它工作于Client请求和Server的中间层;

Filter
是一个可以复用的代码片段,可以用来转换请求,响应以及头信息,filter不能产生请求和响应,他只能在请求到达servlet之前对请求进行修改,或者在请求返回客户端之前对响应进行处理。
也可以理解一个一种特殊Servlet,主要用于对用户请求进行【预处理】,也可以对HttpServletResponse进行后处理;是一个典型的处理链,过滤请求,无法向用户生成响应;
——————————
【生命周期】

Servlet
在系统启动或者请求到达servlet时,通过init()方法进行初始化,一旦被装入了web服务器,一般不会从Web服务器删除,直到比如Tomcat关闭才会调用destroy()方法进行销毁。每次请求,Request都会被初始化,响应请求后,请求被销毁。但是Servlet不会随着请求的销毁而销毁;

如果某个Servlet配置了<load-on-startup >1 </load-on-startup >,该Servlet也是在Tomcat(Servlet容器)启动时初始化。
如果Servlet没有配置<load-on-startup >1 </load-on-startup >,该Servlet不会在Tomcat启动时初始化,而是在请求到来时初始化。

Filter
是在系统启动的时候通过init()初始化的,每次请求都只会调用dofilter()进行处理,服务器停止的时候调用destroy()进行销毁

注意:服务器关闭时,servlet和filter依次销毁;
——————————
【职责】

Servlet
可以动态创建基于客户请求的页面;可以读取Client发来的隐藏数据和显示数据;可以和其他的Server资源进行通讯;通过状态代码和响应头向Client返回数据。

Filter
主要是对请求到达Servlet之前对请求和请求头信息进行前处理,对数据返回Client之前进行后处理;
——————————
【区别】

Servlet
流程比较短,url来了之后Servlet就对其进行处理,处理完就返回数据或者转向另一个页面(不具有【传递性】,不会继续向下传递);

Filter
流程比较长,在一个filter处理之后还可以转向另一个filter进行处理(【传递性】),然后再交给servlet,但是servlet处理之后不能向下传递了。

Filter可用来进行字符编码的过滤,检测用户是否登录的过滤,禁止页面缓存等;
——————————
处理流程:

Filter对用户请求进行预处理;
接着,将请求交给 Servlet进行处理,并生成响应;
最后,Filter再对服务器响应进行后处理;


API

API(Application Programming Interface 应用程序编程接口)
是一些预先定义的函数,目的是提供应用程序与开发人员基于某软件或硬件得以访问一组例程的能力,而又无需访问源码,或理解内部工作机制的细节。

从文件操作开始谈API。
以C语言为例,我们使用fopen()函数可以打开一个文件,感觉非常简单。文件保存在硬盘上,要经过复杂的处理才能显示,这些细节对我们来说是透明的,由操作系统完成。也就是说,我们调用fopen()函数来通知操作系统,让操作系统打开一个文件。
那么,我们如何告诉操作系统打开文件呢?

看似简单的操作到底层都非常复杂,打开文件首先要扫描硬盘,找到文件的位置,然后从文件中读取一部分数据,将数据放进I/O缓冲区,放进内存;这些数据都是0、1序列,还要对照ASCII表或Unicode表”翻译“成字符,再在显示器上显示出来。这个过程如果要让程序员来完成,那简直是噩梦!
怎么办呢?操作系统想了一个很好的办法,它预先把这些复杂的操作写在一个函数里面,编译成一个组件(一般是动态链接库),随操作系统一起发布,并配上说明文档,程序员只需要简单地调用这些函数就可以完成复杂的工作,让编程变得简单有趣。这些封装好的函数,就叫做API(Application Programming Interface),即应用程序编程接口。

说得更加通俗易懂一些,别人写好的代码,或者编译好的程序,提供给你使用,就叫做API。你使用了别人代码(或者程序)中的某个函数、类、对象,就叫做使用了某个API。

操作系统 API

操作系统已经为我们实现了很多功能,它们都被封装成了一个一个的函数,有成百上千个之多,这些函数就叫做 API。程序员要想使用某个功能,只需要调用相应的函数。Windows、Linux、Mac OS、Unix 这些常见的操作系统大部分功能都使用C语言开发,它们的 API 也以C语言的形式呈现。操作系统 API 数目众多,官方必须提供详细的说明文档(Windows API 的说明文档叫 MSDN),程序员在使用 API 时,需要频繁地查阅这些文档。

各种编程语言自带的标准库其实也是API。这些API由编程语言的开发者们编写,安全、高效、健壮,为我们实现了常见的功能,让我们不用再重复造轮子。

C语言 API 以函数的形式呈现,例如 printf()、scanf()、fopen() 等。
Java API 主要以类的形式呈现,例如 String、Thread、Date 等。
C++ 是在C语言的基础上进行的扩展,所以 C++ API 既包含函数也包含类。

第三方库(框架)

  • libxml2——xml的c语言版库,2个项目使用过,win和Linux下都很犀利~~比较快捷
  • CURL—— 这个用的主要是使用了他的封装的http和https的请求,比较犀利,其中包含了openssl的内容
  • iconv——功能强大的编码格式转化库,UTF8,Unicode等互相转化很方便
  • openssl——C的开源密库,可以进行证书加密和https的访问的模拟提交可以和CURL配合使用
  • cocos2d——2D游戏引擎,相比较传统的UI自己控制的重画
  • OpenCV——开源图像库

还有很多第三方(非官方)的组织机构、公司、个人提供的代码,也是一种 API。这些代码有的免费,有的收费;有的开源,有的闭源。这些代码大都针对某个特定的应用领域编写,有时候被称为框架或者库。
例如基于Java的大数据处理平台Hadoop,基于C语言的图形界面库GTK,基于C++的网络库ACE,基于Python的Web开发框架Django,基于JavaScript的前端开发框架React。

全球最大的代码托管网站GitHub也贡献了很多优秀的代码,它们大都开源免费。作为开源代码库以及版本控制系统,Github拥有超过900万开发者用户。随着越来越多的应用程序转移到了云上,Github已经成为了管理软件开发以及发现已有代码的首选方法。这些第三方的API数目众多,种类丰富,我们应该大胆去使用,尽量避免重复造轮子。

API随处可见,它屏蔽了很多底层细节,实现了很多常用功能,大大简化了程序员的工作。用好API,事半功倍。

参考博客:
https://baijiahao.baidu/s?id=1586026960877760125&wfr=spider&for=pc

——————————————————————————————————

更多推荐

【框架】Servlet API

本文发布于:2023-06-14 04:15:00,感谢您对本站的认可!
本文链接:https://www.elefans.com/category/jswz/34/1437347.html
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,我们将在24小时内删除。
本文标签:框架   Servlet   API

发布评论

评论列表 (有 0 条评论)
草根站长

>www.elefans.com

编程频道|电子爱好者 - 技术资讯及电子产品介绍!