zoukankan      html  css  js  c++  java
  • JavaEE笔记

    JavaEE笔记

    作者:光和影子
    我的博客

    前言

    阿里来我们学校搞实习招聘了,想投个Java开发岗但奈何自己已经完全忘记了本科所学,上一次碰Java还是大二时的课程作业,hhh。希望能够在简历投递截止之前尽量准备吧。
    这几天学习JavaEE时做了一个记录,主要是参考廖雪峰老师的Java教程
    我觉得廖老师讲的很好,将整个Java Web的来龙去脉讲的很清楚,没有深陷细节的泥潭。这个笔记也基本上是参照廖老师的教程逻辑来的,不过用我自己的语言和理解进行了重述,更加精简一些。
    image

    概述

    JavaEE即Java企业平台,其实就是Java SE(标准版Java)的基础上加上一些支持包,这些包的作用是帮助我们更方便地构建应用。其中最核心的是基于Servlet的Web服务器。

    JavaEE并不是一个软件产品,它更多的是一种软件架构和设计思想。我们可以把JavaEE看作是在JavaSE的基础上,开发的一系列基于服务器的组件、API标准和通用架构。
    -- 廖雪峰,Java教程

    一. 没有JavaEE时应该怎么做web服务

    Web服务响应过程

    所谓web服务,即浏览器通过TCP链接向服务程序发来HTTP协议的信息,服务程序收到信息后对HTTP文本进行检查、解析并通过TCP向浏览器发送HTTP响应。

    Web服务器细节

    所以我们需要有一个deamon对TCP的某一端口(如80)进行监听,一旦有请求就产生一个线程对这个请求进行处理(这个线程负责这个Socket)。这个线程首从socket中获取输入流和输出流,根据输入流的请求信息将输出内容通过输出流发送给浏览器。
    以上过程可以使用如下代码实现(代码来源于廖雪峰,Java教程):

    展开查看
    import java.io.*;
    import java.net.ServerSocket;
    import java.net.Socket;
    import java.nio.charset.StandardCharsets;
    
    
    /*
    没有javaEE时,使用TCP模拟一个web服务器
    浏览器访问: http://127.0.0.1:8080/
     */
    public class Server {
        public static void main(String[] args) throws IOException {
            ServerSocket ss = new ServerSocket(8080); // 监听指定端口
            System.out.println("server is running...");
            for (;;) {
                Socket sock = ss.accept();
                System.out.println("connected from " + sock.getRemoteSocketAddress());
                Thread t = new Handler(sock);
                t.start();
            }
        }
    }
    
    class Handler extends Thread {
        Socket sock;
    
        public Handler(Socket sock) {
            this.sock = sock;
        }
    
        public void run() {
            try (InputStream input = this.sock.getInputStream()) {
                try (OutputStream output = this.sock.getOutputStream()) {
                    handle(input, output);
                }
            } catch (Exception e) {
                try {
                    this.sock.close();
                } catch (IOException ioe) {
                }
                System.out.println("client disconnected.");
            }
        }
    
        private void handle(InputStream input, OutputStream output) throws IOException {
            System.out.println("Process new http request...");
            var reader = new BufferedReader(new InputStreamReader(input, StandardCharsets.UTF_8));
            var writer = new BufferedWriter(new OutputStreamWriter(output, StandardCharsets.UTF_8));
            // 读取HTTP请求:
            boolean requestOk = false;
            String first = reader.readLine();
            if (first.startsWith("GET / HTTP/1.")) {
                requestOk = true;
            }
            for (;;) {
                String header = reader.readLine();
                if (header.isEmpty()) { // 读取到空行时, HTTP Header读取完毕
                    break;
                }
                System.out.println(header);
            }
            System.out.println(requestOk ? "Response OK" : "Response Error");
            if (!requestOk) {
                // 发送错误响应:
                writer.write("HTTP/1.0 404 Not Found
    ");
                writer.write("Content-Length: 0
    ");
                writer.write("
    ");
                writer.flush();
            } else {
                // 发送成功响应:
                String data = "<html><body><h1>Hello, world!</h1></body></html>";
                int length = data.getBytes(StandardCharsets.UTF_8).length;
                writer.write("HTTP/1.0 200 OK
    ");
                writer.write("Connection: close
    ");
                writer.write("Content-Type: text/html
    ");
                writer.write("Content-Length: " + length + "
    ");
                writer.write("
    "); // 空行标识Header和Body的分隔
                writer.write(data);
                writer.flush();
            }
        }
    }
    

    在浏览器输入http://local.liaoxuefeng.com:8080/可以看到返回页面。

    二. Servlet入门

    专注业务逻辑,让Servlet处理HTTP底层内容

    上一节介绍了如何web服务器的相应过程,然而上面的服务器功能不完善.一个服务器需要有以下几个功能:

    • 识别正确和错误的HTTP请求;
    • 识别正确和错误的HTTP头;
    • 复用TCP连接;
    • 复用线程;
    • IO异常处理;
    • 等...

    为了更加专注于业务逻辑而非服务器的编写,JavaEE提供的Servlet API将封装了解析HTTP协议这些底层的服务器该做的工作,开发人员只需要遵循Servlet API的规范进行编程就行了.这一系列的类在package javax.servlet包中.

    一个简单的Servlet例子

    一个简单的Servlet如下:

    展开查看
    package com.itranswarp.learnjava;
    
    import javax.servlet.ServletException;
    import javax.servlet.annotation.WebServlet;
    import javax.servlet.http.HttpServlet;
    import javax.servlet.http.HttpServletRequest;
    import javax.servlet.http.HttpServletResponse;
    import java.io.*;
    
    
    // WebServlet注解表示这是一个Servlet,并映射到地址/:
    @WebServlet(urlPatterns = "/")
    public class HelloServlet extends HttpServlet {
        protected void doGet(HttpServletRequest req, HttpServletResponse resp)
                throws ServletException, IOException {
            // 设置响应类型:
            resp.setContentType("text/html");
            // 获取输出流:
            PrintWriter pw = resp.getWriter();
            // 写入响应:
            pw.write("<h1>Hello, world!</h1>");
            // 最后不要忘记flush强制输出:
            pw.flush();
        }
    }
    

    如上,开发人员完全不用去管HTTP应用层的事情,只需要专注业务逻辑重写doGetdoPost方法就行了.即当浏览器发来GET请求时应该如何相应和当浏览器发来POST请求时应该如何相应.上面代码没有实现HttpServlet接口doPost方法,所以不能处理浏览器发来POST请求.

    工程目录

    吃人嘴短,用人手短.我们选择使用Servlet包,就需要遵循Servlet定的规矩.
    除了编写servlet之外,网站项目的工程结构还需要像下面这样:

    web-servlet-hello
    ├── pom.xml
    └── src
        └── main
            ├── java
            │   └── com
            │       └── itranswarp
            │           └── learnjava
            │               └── servlet
            │                   └── HelloServlet.java
            ├── resources
            └── webapp
                └── index.jsp
                └── WEB-INF
                    └── web.xml //对网站的配置文件
    

    Servlet容器

    是不是没有发现主程序,那网站应用怎么运行起来呢?我们需要一个Servlet容器来帮忙.顾名思义,和其他容器一样,Servlet容器就是用来装东西的,不过它装的是Servlet.
    Servlet容器启动后会加载我们编写的Servlet并为每个Servlet创建一个实例,然后根据浏览器的请求路径将不同的请求分发(dispatch)给对应的Servlet.

    支持Servlet API的Web服务器。常用的服务器有:

    • Tomcat:由Apache开发的开源免费服务器
    • Jetty:由Eclipse开发的开源免费服务器
    • GlassFish:一个开源的全功能JavaEE服务器
    • WebLogic:Oracle的商用服务器
    • WebSphere:IBM的商用服务器

    注意:Servlet容器只会给每个Servlet类创建唯一实例,而Servlet容器会使用多线程执行doGet()或doPost()方法,在Servlet中定义的实例变量会被多个线程同时访问,要注意线程安全;

    三. Servlet调试

    Tomcat其实就是一个程序而已.Tomcat的启动过程如下:

    • 启动JVM并执行Tomcat的main()方法;
    • 加载war并初始化Servlet;
    • 正常服务。
      为了方便调试,我们可以使用嵌入式的tomcat包,然后自己编写主程序.

    主程序如下:

    展开查看
    public class Main {
        public static void main(String[] args) throws Exception {
            // 启动Tomcat:
            Tomcat tomcat = new Tomcat();
            tomcat.setPort(Integer.getInteger("port", 8080));
            tomcat.getConnector();
            // 创建webapp:
            Context ctx = tomcat.addWebapp("", new File("src/main/webapp").getAbsolutePath());
            WebResourceRoot resources = new StandardRoot(ctx);
            resources.addPreResources(
                    new DirResourceSet(resources, "/WEB-INF/classes", new File("target/classes").getAbsolutePath(), "/"));
            ctx.setResources(resources);
            tomcat.start();
            tomcat.getServer().await();
        }
    }
    

    四. Servlet详解

    路径注解

    每个Servlet负责处理一个路径,一个web app由多个Servlet组成,如:

    展开查看
    @WebServlet(urlPatterns = "/hello") // 通过注解说明自己能处理的路径
    public class HelloServlet extends HttpServlet {
        ...
    }
    
    @WebServlet(urlPatterns = "/signin")
    public class SignInServlet extends HttpServlet {
        ...
    }
    
    @WebServlet(urlPatterns = "/")
    public class IndexServlet extends HttpServlet {
        ...
    }
    
    

    注意:早期的Servlet需要在web.xml中配置映射路径,但最新Servlet版本只需要通过注解就可以完成映射。

    根据请求路径分配Servlet图解

    浏览器发出的HTTP请求总是由Web Server先接收,然后,根据Servlet配置的映射,不同的路径转发到不同的Servlet:

                  ┌ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ┐
    
                   │            /hello    ┌───────────────┐│
                              ┌──────────>│ HelloServlet  │
                   │          │           └───────────────┘│
    ┌───────┐    ┌──────────┐ │ /signin   ┌───────────────┐
    │Browser│───>│Dispatcher│─┼──────────>│ SignInServlet ││
    └───────┘    └──────────┘ │           └───────────────┘
                   │          │ /         ┌───────────────┐│
                              └──────────>│ IndexServlet  │
                   │                      └───────────────┘│
                                  Web Server
                   └ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ┘
    

    这种根据路径转发的功能我们一般称为Dispatch。映射到/IndexServlet比较特殊,它实际上会接收所有未匹配的路径,相当于/*

    HttpServletRequest

    我们通过HttpServletRequest提供的接口方法可以拿到HTTP请求的几乎全部信息,常用的方法有:

    展开查看
    • getMethod():返回请求方法,例如,"GET","POST";
    • getRequestURI():返回请求路径,但不包括请求参数,例如,"/hello";
    • getQueryString():返回请求参数,例如,"name=Bob&a=1&b=2";
    • getParameter(name):返回请求参数,GET请求从URL读取参数,POST请求从Body中读取参数;
    • getContentType():获取请求Body的类型,例如,"application/x-www-form-urlencoded";
    • getContextPath():获取当前Webapp挂载的路径,对于ROOT来说,总是返回空字符串"";
    • getCookies():返回请求携带的所有Cookie;
    • getHeader(name):获取指定的Header,对Header名称不区分大小写;
    • getHeaderNames():返回所有Header名称;
    • getInputStream():如果该请求带有HTTP Body,该方法将打开一个输入流用于读取Body;
    • getReader():和getInputStream()类似,但打开的是Reader;
    • getRemoteAddr():返回客户端的IP地址;
    • getScheme():返回协议类型,例如,"http","https";

    此外,HttpServletRequest还有两个方法:etAttribute()getAttribute(),可以给当前HttpServletRequest对象附加多个Key-Value.

    HttpServletResponse

    HttpServletResponse封装了一个HTTP响应。
    由于HTTP响应必须先发送Header,再发送Body,所以,操作HttpServletResponse对象时,必须先调用设置Header的方法,最后调用发送Body的方法。
    常用的设置Header的方法有:

    展开查看
    • setStatus(sc):设置响应代码,默认是200;
    • setContentType(type):设置Body的类型,例如,"text/html";
    • setCharacterEncoding(charset):设置字符编码,例如,"UTF-8";
    • setHeader(name, value):设置一个Header的值;
    • addCookie(cookie):给响应添加一个Cookie;
    • addHeader(name, value):给响应添加一个Header,因为HTTP协议允许有多个相同的Header;
    写入响应时,需要通过getOutputStream()获取写入流,或者通过getWriter()获取字符流,二者只能获取其中一个。

    重定向(Redirect)

    重定向是指当浏览器请求一个URL时,服务器返回一个重定向指令,告诉浏览器地址已经变了,麻烦使用新的URL再重新发送新请求。

    例如,我们已经编写了一个能处理/hello的HelloServlet,如果收到的路径为/hi,希望能重定向到/hello,可以再编写一个RedirectServlet:

    展开查看
    @WebServlet(urlPatterns = "/hi")
    public class RedirectServlet extends HttpServlet {
        protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
            // 构造重定向的路径:
            String name = req.getParameter("name");
            String redirectToUrl = "/hello" + (name == null ? "" : "?name=" + name);
            // 发送重定向响应:
            resp.sendRedirect(redirectToUrl);
        }
    }
    

    重定向时浏览器现后发送两次请求,过程如下:

    ┌───────┐   GET /hi     ┌───────────────┐
    │Browser│ ────────────> │RedirectServlet│
    │       │ <──────────── │               │
    └───────┘   302         └───────────────┘
    
    
    ┌───────┐  GET /hello   ┌───────────────┐
    │Browser│ ────────────> │ HelloServlet  │
    │       │ <──────────── │               │
    └───────┘   200 <html>  └───────────────┘
    

    服务器内部转发(Forward)

    Forward是指内部转发。当一个Servlet处理请求的时候,它可以决定自己不继续处理,而是转发给另一个Servlet处理。

    例如,我们已经编写了一个能处理/hello的HelloServlet,继续编写一个能处理/morning的ForwardServlet:

    展开查看
    @WebServlet(urlPatterns = "/morning")
    public class ForwardServlet extends HttpServlet {
        protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
            //收到请求后,并不自己发送响应,而是把请求和响应都转发给路径为/hello的Servlet
            req.getRequestDispatcher("/hello").forward(req, resp);
        }
    }
    
    后续请求的处理实际上是由HelloServlet完成的。这种处理方式称为转发(Forward),转发在服务器内部进行,浏览器并不知道发生过转发,浏览器地址也仍然为转发前的地址.其过程如下:
                              ┌────────────────────────┐
                              │      ┌───────────────┐ │
                              │ ────>│ForwardServlet │ │
    ┌───────┐  GET /morning   │      └───────────────┘ │
    │Browser│ ──────────────> │              │         │
    │       │ <────────────── │              ▼         │
    └───────┘    200 <html>   │      ┌───────────────┐ │
                              │ <────│ HelloServlet  │ │
                              │      └───────────────┘ │
                              │       Web Server       │
                              └────────────────────────┘
    

    Session

    未完待续...

    image

  • 相关阅读:
    redies安装
    跟我一起学koa之在koa中使用mongoose(四)
    mongoDB可视化工具RoBo 3T的安装和使用
    跟我一起了解koa之koa洋葱圈模型(二)
    跟我一起了解koa之koa的cookie(三)
    跟我一起了解koa之koa-generator(一)
    2进制数转成10进制
    koa中同步与异步的写法
    bootstrap-内联表单 水平(横向)表单 响应式图片 辅助类 [转]
    Bootstrap 杂记
  • 原文地址:https://www.cnblogs.com/rainwelcome/p/14394637.html
Copyright © 2011-2022 走看看