蒋振飞的博客 - Java提升03:Servlet   
正在加载蒋振飞的博客...
V3.0
蒋振飞的博客

Java提升03:Servlet

发布时间: 2018年10月23日 发布人: 蒋振飞 热度: 925 ℃ 评论数: 0

一、HTTP

    1.HTTP版本

        1) 1.0:请求数据,服务器返回后, 将会断开连接。
        2) 1.1:请求数据,服务器返回后, 连接还会保持着。 除非服务器 | 客户端 关掉。 有一定的时间限制,如果都空着这个连接,那么后面会自己断掉。

    2.Http请求数据解释

        请求的数据里面包含三个部分内容 : 请求行 、 请求头 、请求体。

        1) 请求行

POST /examples/servlets/servlet/RequestParamExample HTTP/1.1 

POST:请求方式,以post去提交数据
			
/examples/servlets/servlet/RequestParamExample
请求的地址路径,就是要访问哪个地方。
	
HTTP/1.1 协议版本

        2) 请求头

Accept:application/x-ms-application, image/jpeg, application/xaml+xml, image/gif, image/pjpeg, application/x-ms-xbap
Referer:http://localhost:8080/examples/servlets/servlet/RequestParamExample
Accept-Language:zh-CN
User-Agent:Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 6.1; WOW64; Trident/4.0; SLCC2; .NET CLR 2.0.50727; .NET CLR 3.5.30729; .NET CLR 3.0.30729; Media Center PC 6.0; .NET4.0C; .NET4.0E)
Content-Type:application/x-www-form-urlencoded
Accept-Encoding:gzip, deflate
Host:localhost:8080
Content-Length:31
Connection:Keep-Alive
Cache-Control:no-cache

Accept:客户端向服务器端表示,我能支持什么类型的数据。 
Referer:真正请求的地址路径,全路径
Accept-Language:支持语言格式
User-Agent:用户代理 向服务器表明,当前来访的客户端信息。 
Content-Type:提交的数据类型。经过urlencoding编码的form表单的数据
Accept-Encoding:gzip, deflate:压缩算法。 
Host:主机地址
Content-Length:数据长度
Connection:Keep-Alive 保持连接
Cache-Control:对缓存的操作

        3) 请求体

            浏览器真正发送给服务器的数据,发送的数据呈现的是key=value ,如果存在多个数据,那么使用 &。

firstname=zhang&lastname=sansan

    3.Http响应数据解析

        1) 请求的数据里面包含三个部分内容

            响应行 、 响应头 、响应体

HTTP/1.1 200 OK
Server: Apache-Coyote/1.1
Content-Type: text/html;charset=ISO-8859-1
Content-Length: 673
Date: Fri, 17 Feb 2017 02:53:02 GMT

        2) 响应行

协议版本  
状态码 
HTTP/1.1 200 OK

        3) 响应头

Server:服务器是哪一种类型。  Tomcat
Content-Type:服务器返回给客户端你的内容类型
Content-Length:返回的数据长度
Date:通讯的日期,响应的时间

    4.Get 和  Post请求区别

        1) post

            ①数据是以流的方式写过去,不会在地址栏上面显示。  现在一般提交数据到服务器使用的都是POST。
            ②以流的方式写数据,所以数据没有大小限制。

        2) get

            ①会在地址栏后面拼接数据,所以有安全隐患。 一般从服务器获取数据,并且客户端也不用提交上面数据的时候,可以使用GET。
            ②能够带的数据有限, 1kb大小。

二、Servlet

    1.servlet

        其实就是一个java程序,运行在web服务器上,用于接收和响应 客户端的http请求。
        更多的是配合动态资源来做,静态资源也需要使用到servlet,只不过是Tomcat里面已经定义好了一个 DefaultServlet。

    2.Hello Servlet

        1) 写一个Web工程

        2) 测试运行Web工程

            ①新建一个类, 实现Servlet接口。
            ②配置Servlet , 用意: 告诉服务器,应用有这么些个servlet。
            ③在webContent/WEB-INF/web.xml里面写上以下内容。

<!-- 向tomcat报告, 我这个应用里面有这个servlet, 名字叫做HelloServlet , 具体的路径是com.itheima.servlet.HelloServlet -->
<servlet>
    <servlet-name>HelloServlet</servlet-name>
    <servlet-class>com.jzfblog.servlet.HelloServlet</servlet-class>
</servlet>
  
<!-- 注册servlet的映射,servletName:找到上面注册的具体servlet,url-pattern:在地址栏上的path 一定要以/打头 -->
<servlet-mapping>
    <servlet-name>HelloServlet</servlet-name>
    <url-pattern>/a</url-pattern>
</servlet-mapping>

        3) 在地址栏上输入 http://localhost:8080/项目名称/a

    3.Servlet的执行过程

        ①找到tomcat应用。
        ②找到项目。
        ③找web.xml,然后在里面找到url-pattern,有没有哪一个pattern是a的。
        ④找到servlet-mapping中的那个servlet-name[HelloServlet]。
        ⑤找到上面定义的servlet元素中的servlet-name中的[HelloServlet]。
        ⑥找到下面定义的servlet-class,然后创建该类的实例。
        ⑦最后执行servlet中的service方法。

    4.Servlet的通用写法

        定义一个类,继承HttpServlet 复写doGet 和 doPost。

    5.Servlet的生命周期

        1) 生命周期

            ①从创建到销毁的一段时间。

        2) 生命周期方法

            ①从创建到销毁,所调用的那些方法。

        3) init方法

            ①在创建该servlet的实例时,就执行该方法。
            ②一个servlet只会初始化一次, init方法只会执行一次。
            ③默认情况下是:初次访问该servlet,才会创建实例。 

        4) service方法

            ①只要客户端来了一个请求,那么就执行这个方法了。
            ②该方法可以被执行很多次。 一次请求,对应一次service方法的调用。

        5) destroy方法

            ①servlet销毁的时候,就会执行该方法。
            ②该项目从tomcat的里面移除。
            ③正常关闭tomcat就会执行 shutdown.bat。

        5) doGet 和 doPost

            不算生命周期方法,所谓的生命周期方法是指,从对象的创建到销毁一定会执行的方法, 但是这两个方法,不一定会执行。

    6.让Servlet创建实例的时机提前

        ①默认情况下,只有在初次访问servlet的时候,才会执行init方法。 有的时候,我们可能需要在这个方法里面执行一些初始化工作,甚至是做一些比较耗时的逻辑。
        ②那么这个时候,初次访问,可能会在init方法中逗留太久的时间。 那么有没有方法可以让这个初始化的时机提前一点。
        ③在配置的时候, 使用load-on-startup元素来指定, 给定的数字越小,启动的时机就越早。 一般不写负数, 从2开始即可。 

<servlet>
    <servlet-name>HelloServlet04</servlet-name>
    <servlet-class>com.jzfblog.servlet.HelloServlet04</servlet-class>
    <load-on-startup>2</load-on-startup>
</servlet>

    7.ServletConfig

        Servlet的配置,通过这个对象,可以获取servlet在配置的时候一些信息。

<servlet>
    <servlet-name>HelloServletConfig</servlet-name>
    <servlet-class>com.jzfblog.helloservlet.HelloServletConfig</servlet-class>
   	
    <!-- 添加自定义参数 -->
    <init-param>
        <param-name>address</param-name>
        <param-value>jiangxi...</param-value>
    </init-param>
    <init-param>
        <param-name>name</param-name>
        <param-value>jack</param-value>
    </init-param>
</servlet>
// 1.得到servlet配置对象 专门用于在配置servlet的信息
ServletConfig config = getServletConfig();
// 获取到的是配置servlet里面servlet-name 的文本内容
String servletName = config.getServletName();
System.out.println("servletName="+servletName);

// 2.可以获取具体的某一个参数。 
String address = config.getInitParameter("address");
System.out.println("address="+address);


// 3.获取所有的参数名称
Enumeration<String> names = config.getInitParameterNames();
// 遍历取出所有的参数名称
while (names.hasMoreElements()) {
    String key = (String) names.nextElement();
    String value = config.getInitParameter(key);
    System.out.println("key==="+key + "   value="+value);
}

三、ServletContext

    1.ServletContext

        每个web工程都只有一个ServletContext对象,就是不管在哪个servlet里面,获取到的这个类的对象都是同一个。

    2.如何得到对象

//1. 获取对象
ServletContext context = getServletContext();

    3. ServletContext的作用

        1) 获取全局配置参数

<!-- 全局参数 -->
<context-param>
	<param-name>address</param-name>
	<param-value>浙江杭州</param-value>
</context-param>
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
    // TODO Auto-generated method stub
    // 1.获取对象
    ServletContext context = getServletContext();
    String address = context.getInitParameter("address");
    System.out.println("address:" + address);
}

        2) 获取web工程中的资源

private void method1() throws FileNotFoundException, IOException {
    // 1.获取servletContext对象
    ServletContext context = getServletContext();
    String path = context.getRealPath("file/config.properties");
		
    // 2.创建属性对象
    Properties properties = new Properties();
    FileInputStream is = new FileInputStream(path);
    properties.load(is);
		
    // 3.获取name属性值
    String name = properties.getProperty("name");
    System.out.println("name:" + name);
}
private void method2() throws IOException {
    // 1.获取servletContext对象
    ServletContext context = getServletContext();
		
    // 2.web工程下的资源,转化成流对象
    InputStream is = context.getResourceAsStream("file/config.properties");
		
    // 3.创建属性对象
    Properties properties = new Properties();
    properties.load(is);
		
    // 3.获取name属性值
    String name = properties.getProperty("name");
    System.out.println("name:" + name);
}

        3) 存取数据,servlet间共享数据,域对象

            ①定义一个登陆的html页面, 定义一个form表单

<form action="Login" method="post">
    账号:<input type="text" name="username"/>
    密码:<input type="password" name="password"/>
    <input type="submit" value="登录"/>
</form>

            ②定义一个Servlet,名为Login。针对成功或者失败,进行判断,然后跳转到不一样的网页

protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
    // 1.获取数据
    String username = request.getParameter("username");
    String password = request.getParameter("password");
	
    System.out.println("username:" + username + "password:" + password);
    // 2.校验数据
	
    // 3.向客户端输出内容
    PrintWriter pw = response.getWriter();
	
    if("admin".equals(username) && "123".equals(password)) {
        System.out.println("登录成功");
        // pw.write("login success...");
		
        // 成功跳转页面
        response.setStatus(302);
        response.setHeader("Location", "login_success.html");
    }else {
        System.out.println("登录失败");
        pw.write("login failed...");
    }
}

            ③ServletContext存取值分析

// 成功次数的累加
// 获取以前的值,在以前的值基础上+1
Object obj = getServletContext().getAttribute("count");
int totalCount = 0;
if(obj != null) {
    totalCount = (Integer) obj;
}
System.out.println("已登录成功的次数是:" + totalCount);
getServletContext().setAttribute("count", totalCount+1);

四、HttpServletRequest

    1. 可以获取客户端请求头信息

// 得到一个枚举集合  
Enumeration<String> headerNames = request.getHeaderNames();
while (headerNames.hasMoreElements()) {
    String name = (String) headerNames.nextElement();
    String value = request.getHeader(name);
    System.out.println(name+"="+value);
}

    2. 获取客户端提交过来的数据

String name = request.getParameter("name");
String address = request.getParameter("address");
System.out.println("name="+name);
System.out.println("address="+address);

// name=zhangsan&name=lisi&name=wangwu 一个key可以对应多个值。

Map<String, String[]> map = request.getParameterMap();
Set<String> keySet = map.keySet();
Iterator<String> iterator = keySet.iterator();
while (iterator.hasNext()) {
    String key = (String) iterator.next();
    System.out.println("key="+key + "--的值总数有:"+map.get(key).length);
    String value = map.get(key)[0];
    String value1 = map.get(key)[1];
    String value2 = map.get(key)[2];
 
    System.out.println(key+" ======= "+ value + "=" + value1 + "="+ value2);
}

    3. 获取中文数据

        客户端提交数据给服务器端,如果数据中带有中文的话,有可能会出现乱码情况,那么可以参照以下方法解决。

        1) GET方式

String username = request.getParameter("username");
String password = request.getParameter("password");
			
System.out.println("userName="+username+"==password="+password);
			
// get请求过来的数据,在url地址栏上就已经经过编码了,所以我们取到的就是乱码,
// tomcat收到了这批数据,getParameter 默认使用ISO-8859-1去解码
			
// 先让文字回到ISO-8859-1对应的字节数组,然后再按utf-8组拼字符串
username = new String(username.getBytes("ISO-8859-1") , "UTF-8");
System.out.println("userName="+username+"==password="+password);

    可以在tomcat里面做设置处理 conf/server.xml 加上URIEncoding="utf-8"

<Connector connectionTimeout="20000" port="8080" protocol="HTTP/1.1" redirectPort="8443" URIEncoding="UTF-8"/>

        2) POST方式

request.setCharacterEncoding("UTF-8");

五、HttpServletResponse

    1.作用

        负责返回数据给客户端。 

    2.输出数据到页面上

// 以字符流的方式写数据
// response.getWriter().write("<h1>hello response...</h1>");
// 以字节流的方式写数据 
response.getOutputStream().write("hello response2222...".getBytes());

    3.响应的数据中有中文,那么有可能出现中文乱码

        1) 以字符流输出

// 1. 指定输出到客户端的时候,这些文字使用UTF-8编码
response.setCharacterEncoding("UTF-8");
// 2. 直接规定浏览器看这份数据的时候,使用什么编码来看。
response.setHeader("Content-Type", "text/html; charset=UTF-8");
response.getWriter().write("我爱java...");

        2) 以字节流输出 

// 1. 指定浏览器看这份数据使用的码表
response.setHeader("Content-Type", "text/html;charset=UTF-8");
// 2. 指定输出的中文用的码表
response.getOutputStream().write("我爱java..".getBytes("UTF-8"));

        3) 不管是字节流还是字符流,直接使用一行代码就可以了

response.setContentType("text/html;charset=UTF-8");

六、下载资源

    1. 让tomcat的默认servlet提供下载

<a href="download/aa.jpg">aa.jpg</a><br>
<a href="download/bb.txt">bb.txt</a><br>
<a href="download/cc.rar">cc.rar</a><br>

        该原因是因为tomcat里面有一个默认的Servlet -- DefaultServlet ,这个DefaultServlet 专门用于处理放在tomcat服务器上的静态资源。

    2.下载案例

<a href="Download?filename=aa.jpg">aa.jpg</a><br>
<a href="Download?filename=bb.txt">bb.txt</a><br>
<a href="Download?filename=cc.rar">cc.rar</a><br>
// 1.获取要下载的文件名字
String fileName = request.getParameter("filename");

// 2.获取该文件在tomcat中的绝对路径
String path = getServletContext.getRealPath("download/"+fileName);

// 浏览器弹出保存对话框
response.setHeader("Content-Disposition","attachment;filename="+fileName);

// 3.转化成输入流
InputStream is = new InputStream(path);
OutputStream os = response.getOutputStream();

int len = 0;
byte[]buffer  = new byte[1024];
while( ( len = is.read(buffer)) != -1){
    os.write(buffer, 0, len);
}

os.close();
is.close();

    3.中文文件下载

        针对浏览器类型,对文件名字做编码处理 Firefox (Base64) , IE、Chrome ... 使用的是URLEncoder。

public static String base64EncodeFileName(String fileName) {
    BASE64Encoder base64Encoder = new BASE64Encoder();
    try {
        return "=?UTF-8?B?"
            + new String(base64Encoder.encode(fileName
                .getBytes("UTF-8"))) + "?=";
    } catch (UnsupportedEncodingException e) {
        e.printStackTrace();
        throw new RuntimeException(e);
    }
}
/*
* 如果文件的名字带有中文,那么需要对这个文件名进行编码处理
* 如果是IE ,或者  Chrome (谷歌浏览器) ,使用URLEncoding 编码
* 如果是Firefox , 使用Base64编码
*/

// 获取来访的客户端类型
String clientType = request.getHeader("User-Agent");
if(clientType.contains("Firefox")){
    fileName = DownLoadUtil.base64EncodeFileName(fileName);
}else{
    // IE ,或者  Chrome (谷歌浏览器) ,
    // 对中文的名字进行编码处理
    fileName = URLEncoder.encode(fileName,"UTF-8");
}

七、Cookie

    1.Cookie

        其实是一份小数据, 是服务器给客户端,并且存储在客户端上的一份小数据。

    2.应用场景

        自动登录、浏览记录、购物车。

    3.为什么要有Cookie

        客户端与服务器在通讯的时候,是无状态的,其实就是客户端在第二次来访的时候,服务器根本就不知道这个客户端以前有没有来访问过。 为了更好的用户体验,更好的交互 [自动登录],其实从公司层面讲,就是为了更好的收集用户习惯[大数据]。

    4.Cookie怎么用

        1) 添加Cookie给客户端

// 创建cookie对象
Cookie cookie = new Cookie("aa", "bb");
 
// 给响应,添加一个cookie
response.addCookie(cookie);

        2) 获取客户端带过来的Cookie

Cookie[] cookies = request.getCookies();
if(cookies != null){
    for (Cookie c : cookies) {
        String cookieName = c.getName();
        String cookieValue = c.getValue();
        System.out.println(cookieName + " = "+ cookieValue);
    }
}

        3) 常用方法

// 关闭浏览器后,cookie就没有了。 ---> 针对没有设置cookie的有效期。
// expiry:有效 以秒计算。
// 正值:表示 在这个数字过后,cookie将会失效。
// 负值:关闭浏览器,那么cookie就失效, 默认值是 -1
cookie.setMaxAge(60 * 60 * 24 * 7);
		
// 赋值新的值
// cookie.setValue(newValue);
		
// 用于指定只有请求了指定的域名,才会带上该cookie
cookie.setDomain(".jzfblog.com");
		
// 只有访问该域名下的cookieDemo的这个路径地址才会带cookie
cookie.setPath("/CookieDemo");

// 删除cookie是没有什么delete方法的,只有设置maxAge 为0
cookie.setMaxAge(0); //设置立即删除

    5.显示最近访问的时间

        ①判断账号是否正确。
        ②如果正确,则获取cookie。 但是得到的cookie是一个数组, 我们要从数组里面找到我们想要的对象。
        ③如果找到的对象为空,表明是第一次登录。那么要添加cookie。
        ④如果找到的对象不为空, 表明不是第一次登录。 

public static Cookie findCookie(Cookie[] cookies, String name){
    
    if(cookies != null){
        for(Cookie cookie : cookies){
            if(name.equals(cookie.getName)){
                return cookie;
            }
        }
    }
    return null;  
}
if("admin".equals(userName) && "123".equals(password)){
    // 获取cookie last-name --- >
    Cookie [] cookies = request.getCookies();
 
    // 从数组里面找出我们想要的cookie
    Cookie cookie = CookieUtil.findCookie(cookies, "last");
 
    // 是第一次登录,没有cookie
    if(cookie == null){
 
        Cookie c = new Cookie("last", System.currentTimeMillis()+"");
        c.setMaxAge(60*60); //一个小时
        response.addCookie(c);
 
        response.getWriter().write("欢迎您, "+userName);
 
    }else{
        // 1. 去以前的cookie第二次登录,有cookie
        long lastVisitTime = Long.parseLong(cookie.getValue());
 
        // 2. 输出到界面,
        response.getWriter().write("欢迎您, "+userName +",上次来访时间是:"+new Date(lastVisitTime));
 
        //3. 重置登录的时间
        cookie.setValue(System.currentTimeMillis()+"");
        response.addCookie(cookie);
    }
}else{
    response.getWriter().write("登陆失败 ");
}

八、Session

    1.Session

        会话 , Session是基于Cookie的一种会话机制。 Cookie是服务器返回一小份数据给客户端,并且存放在客户端上。 Session是,数据存放在服务器端。

    2.常用API

// 得到会话ID
String id = session.getId();
// 存值
session.setAttribute(name, value);
// 取值
session.getAttribute(name);
// 移除值
session.removeAttribute(name);

    3. Session何时创建,何时销毁?

        1) 创建

            ①如果有在servlet里面调用了 request.getSession()

        2) 销毁

            ①session 是存放在服务器的内存中的一份数据。 当然可以持久化Redis . 即使关了浏览器,session也不会销毁。
            ②关闭服务器
            ③session会话时间过期。 有效期过了,默认有效期: 30分钟。

    4.简单购物车

response.setContentType("text/html;charset=utf-8");
			
// 1. 获取要添加到购物车的商品id
int id = Integer.parseInt(request.getParameter("id")); // 0 - 1- 2 -3 -4 
String [] names = {"Iphone7","小米6","三星Note8","魅族7" , "华为9"};
// 取到id对应的商品名称
String name = names[id];
			
// 2. 获取购物车存放东西的session  Map<String , Integer>  iphoen7 3
// 把一个map对象存放到session里面去,并且保证只存一次。 
Map<String, Integer> map = (Map<String, Integer>) request.getSession().getAttribute("cart");
// session里面没有存放过任何东西。
if(map == null){
    map = new LinkedHashMap<String , Integer>();
    request.getSession().setAttribute("cart", map);
}
			
			
// 3. 判断购物车里面有没有该商品
if(map.containsKey(name)){
    // 在原来的值基础上  + 1 
    map.put(name, map.get(name) + 1 );
}else{
    // 没有购买过该商品,当前数量为1 。
    map.put(name, 1);
}
			
// 4. 输出界面。(跳转)
response.getWriter().write("<a href='product_list.jsp'><h3>继续购物</h3></a><br>");
response.getWriter().write("<a href='cart.jsp'><h3>去购物车结算</h3></a>");

    5.移除Session中的元素

// 强制干掉会话,里面存放的任何数据就都没有了。
session.invalidate();
		
// 从session中移除某一个数据
// session.removeAttribute("cart");

打赏 蒋振飞

取消

感谢您的支持,我会继续努力的!

扫码支持
一分也是爱     一块不嫌多

点击 支付宝 或 微信 打赏蒋振飞

打开支付宝扫一扫,即可进行扫码打赏哦

评论列表