Java后端系统学习路线--JavaEE基础

266 阅读9分钟

1、JavaEE的三大组件

1.1、Servlet

大榜:前面我们讨论和回顾了Java后端学习路线中的Java基础部分,包含Java基础(一)Java基础(二),α阶段的Java基础部分就走完了,接下来我们进入JavaEE的世界看看。

小汪:好啊好啊。我记得JavaEE也叫做Java企业版,为企业级应用服务的。

大榜:是的,狭义的JavaEE是Sun公司为企业级应用推出的标准平台,也可以说是一种规范,目前经常使用的主要是Servlet规范。广义的Java EE,则包含各种框架,其中最重要的是Spring全家桶,诞生的目的是为了改进Java EE开发的体验。

小汪:那JavaSE与JavaEE的区别是什么呢?

大榜:JavaSE也就是我们之前讨论的Java基础,JavaEE是在JavaSE的基础上构建的,可以说是对JavaSE的扩展。下面我们先讨论狭义的JavaEE,重点介绍现在广泛使用的三大组件:Servlet、Filter和Listener。

小汪:Servlet我知道,Servlet是运行在Web服务器上的Java小程序,可以收集来自网页表单的用户输入,然后经过后台处理,动态生成网页。代码是下面这样的:

response.getWriter().println("<h1>Hello Servlet!</h1>" + "欢迎开发者: " + developer)

大榜:小伙子,基础很好啊。而且Servlet还有自己的生命周期,Servlet生命周期的三个关键方法,init、service、destroy。具体来说是下面这样的:

1)当Servlet第一次被调用的时候,其实例会被装在内存中,这个过程只会进行一次,也就是单例。init方法只会在Servlet初始化时被调用一次;

2)接着,Servlet的service函数会被调用。每次请求,都会调用一次service()方法;

3)当Web应用reload重新加载或者服务器关闭时,Servlet实例会被销毁。destroy方法是在Servlet销毁时被调用。Servlet生命周期的代码是下面这样:

package com.atguigu.admin.servlet;
​
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
​
/**
 * 1、JavaEE的概念 【原文链接:https://blog.csdn.net/Neuf_Soleil/article/details/80962686】
 *
 * 1)JavaEE是在JavaSE的基础上构建的,是对JavaSE的扩展,增加了一些方便的应用框架。
 * 2)JavaEE 号称有十三种核心技术。它们分别是:JDBC、JNDI、EJB、RMI、Servlet、JSP、XML、JMS、Java IDL、JTS、JTA、JavaMail和JAF;
 *
 *
 * 2、Servlet的基本概念
 * 1)Servlet是运行在Web服务器上的Java小程序,可以收集来自网页表单的用户输入,然后经过后台处理,动态生成网页。例如:
 *           response.getWriter().println("<h1>Hello Servlet!</h1>" + "欢迎开发者: " + developer);
 *
 * 2)狭义的 Servlet 是指 Java 语言实现的一个接口,广义的 Servlet 是指任何实现了这个 Servlet 接口的程序;
 *    一般情况下,我们称Servlet 为广义的Servlet。
 *
 * 3)我们也可以重写 service方法,但会导致 doGet 和 doPost方法失效,一般不建议这么做。
 *
 *
 * 3、Servlet的生命周期:
 *  Servlet生命周期的三个关键方法,init、service、destroy
 * 1)当Servlet第一次被调用的时候,其实例会被装在内存中,这个过程只会进行一次,也就是单例。init方法只会在Servlet初始化时被调用一次。
 * 2)接着,Servlet的service函数会被调用。每次请求,都会调用一次service()方法
 * 3)当Web应用reload重新加载或者服务器关闭时,Servlet实例会被销毁。destroy方法是在Servlet销毁时被调用
 *
 * 4、MVC分层架构
 * MVC指的是:Controller、Service、Dao这三层,层层之间的依赖关系,我们是通过IOC容器来帮我们做依赖注入的。
 *
 * @author qinxubang
 * @Date 2022/5/23 16:20
 */@WebServlet(urlPatterns = "/qin/developer") // 对应test-servlet.html的表单请求
public class ServletTest extends HttpServlet {
​
    /**
     * Servlet生命周期的三个关键方法,init、service、destroy
     *
     * @throws ServletException
     */
    @Override
    public void init() throws ServletException {
        super.init();
    }
​
    @Override
    public void service(ServletRequest req, ServletResponse res) throws ServletException, IOException {
        super.service(req, res);
    }
​
    @Override
    public void destroy() {
        super.destroy();
    }
​
    @Override
    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        request.setCharacterEncoding(StandardCharsets.UTF_8.name());
        try {
            response.setContentType("text/html; charset=UTF-8");
            // 在给浏览器的响应 response 中写入一句 html
            response.getWriter().println("<h1>Hello Servlet!</h1>" + "请填写开发者人员!");
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
​
    /**
     * 获取表单提交过来的 developer 参数,并把欢迎语句以 UTF-8 编码输出到客户端
     * @param request
     * @param response
     * @throws ServletException
     * @throws IOException
     */
    @Override
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        String developer = request.getParameter("developer");
        response.setContentType("text/html; charset=UTF-8");
        response.getWriter().println("<h1>Hello Servlet!</h1>" + "欢迎开发者: " + developer);
​
    }
}
​

上面的代码中,我们不仅重写了Servlet生命周期的3个方法,而且重写了doGet方法,来接收客户端的GET请求,响应输出一段HTML;重写了doPost方法,接收客户端的POST请求,也是输出一段HTML给客户端。

小汪:这段代码我懂了,你创建了一个ServletTest类,继承了HttpServlet,重写HttpServlet中的方法,就可以实现相应的功能,如Servlet的生命周期、接收客户端的请求。感觉遵循Servlet规范之后,好厉害呀。

大榜:小伙子理解得很快啊!虽然实际的项目开发中很少直接用到 Servlet,但它是 JavaEE 的重要组成部分,也是我们日后学习Web开发的基础,对深入理解 Java 企业级应用的开发有重要作用。其实Servlet 并不复杂,相信经过一些小小的实践,作为初学者的我们可以牢牢掌握这部分的知识。

1.2、过滤器Filter

小汪:遵循Servlet规范就可以接收客户端的请求,开发一个超简单的后台系统了,确实不难,哈哈哈。JavaEE的第二大组件是Filter,它有什么用呢?

大榜:Filter的中文叫做拦截器,它的作用就是对进来的请求,进行拦截过滤。我们可以实现一个自定义的拦截器,对所有进入后端系统的HTTP请求,统一拦截,统一设置编码方式,代码如下:

package com.atguigu.admin.servlet;
​
import org.springframework.util.StringUtils;
​
import javax.servlet.*;
import javax.servlet.annotation.WebFilter;
import javax.servlet.annotation.WebInitParam;
import javax.servlet.http.HttpServletRequest;
import java.io.IOException;
​
/**
 * 对进来的请求,进行拦截过滤,对请求加上自定义的编码格式,默认为“UTF-8”
 * @author qinxubang
 * @Date 2022/5/23 20:28
 */
@WebFilter(urlPatterns = {"/**"}, initParams = {@WebInitParam(name = "encoding", value = "GBK")})
public class CharacterEncodingFilter implements Filter {
​
    // http请求进来时,默认的编码方式为“UTF-8”
    private String encoding = "UTF-8";
​
    @Override
    public void init(FilterConfig filterConfig) throws ServletException {
        String encodingStr = filterConfig.getInitParameter("encoding");
        if (!StringUtils.isEmpty(encodingStr)) {
            encoding = encodingStr;
        }
    }
​
    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
        ((HttpServletRequest)request).setCharacterEncoding(encoding);
        System.out.println("helloA:进入Servlet之前的拦截");
​
        // 放行
        chain.doFilter(request, response);
​
        System.out.println("helloA2:对Servlet返回的响应,进行拦截");
    }
​
    @Override
    public void destroy() {
​
    }
}

小汪:我懂了,下面这段代码,是对请求设置编码格式:

((HttpServletRequest)request).setCharacterEncoding(encoding);

这样,就实现了对所有进到后端系统的HTTP请求,统一拦截,设置编码方式。

1.3、监听器Listener

大榜:理解得很快嘛。下面我们谈论下监听器,看看它有什么作用?

小汪:我感觉,监听器在前端用得比较多,比如JavaScript监听前台用户的点击事件,也是监听器的应用场景。

大榜:是的了。Java中监听器的作用,也是监听事件的,比如监听servletContext上下文对象的创建和销毁的过程,我们可以在servletContext创建之后,去初始化一个IOC容器。代码是这样的:

package com.atguigu.admin.servlet;
​
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.context.support.ClassPathXmlApplicationContext;
​
import javax.servlet.ServletContext;
import javax.servlet.ServletContextEvent;
import javax.servlet.ServletContextListener;
import javax.servlet.annotation.WebListener;
​
/**
 * 1、监听器:观察者模式,例如js监听前台用户的点击事件,也是监听器的应用场景。
 *
 * 2、Servlet中常见的8个监听器,如下所示:
 *
 * 1)ServletContextListener:监听servletContext对象的创建和销毁的过程;
 * 2)HttpSessionListener: 监听httpSession对象的创建和销毁的过程;
 * 3)ServletRequestListener:监听ServletRequest对象的创建和销毁的过程;
 * 4)ServletContextAttributeListener:监听ServletContext的保存作用域的改动(add、remove、replace)
 *
 * 5)HttpSessionAttributeListener:监听HttpSession的保存作用域的改动(add、remove、replace)
 * 6)ServletRequestAttributeListener:监听ServletRequest的保存作用域的创建和销毁的过程;
 * 7)HttpSessionBindingListener:监听某个对象在Session域中的创建和移除;
 * 8)HttpSessionActivationListener:监听某个对象在Session域中的序列化和反序列化。
 *
 *
 * 3、ServletContextListener的使用场景
 *  监听ServletContext上下文启动,在上下文启动的时候去创建IOC容器,然后将其保存到servletContext作用域;
 *    后续需要使用beanFactory容器,我们直接从servletContext对象中获取IOC容器即可。如MyServletContextListener#contextInitialized方法
 *
 */@Slf4j
@WebListener
public class MyServletContextListener implements ServletContextListener {
​
​
    @Override
    public void contextInitialized(ServletContextEvent sce) {
        BeanFactory beanFactory = new ClassPathXmlApplicationContext();
        ServletContext servletContext = sce.getServletContext();
        servletContext.setAttribute("beanFactory", beanFactory);
        log.info("MyServletContextListener监听到项目初始化完成,IOC容器也已初始化完成!");
    }
​
    @Override
    public void contextDestroyed(ServletContextEvent sce) {
        log.info("MyServletContextListener监听到项目销毁");
    }
}

小汪:我懂了,上面的contextInitialized方法会监听ServletContext上下文的启动,并在上下文启动的时候去创建IOC容器,然后将其保存到servletContext作用域;后续如果需要使用IOC容器,我们就可以直接从servletContext对象中获取IOC容器即可,很香啊。

2、Cookie/Session

大榜:JavaEE中除了三大组件(Servlet、Filter、Listener),还有一项技术,是用来进行客户端的状态管理。小汪,你知道HTTP为什么设计成无状态协议吗?

小汪:当然是为了简化HTTP的设计,我们假想一下,如果HTTP设计成有状态的协议,那么服务端就需要管理全部客户端状态,那服务端的身体可吃不消。所以,将HTTP设计为无状态协议,使得HTTP协议变得简单可靠,被应用在各种场景中。

大榜:但HTTP设计为无状态后,它不对之前发生过的请求和响应的状态进行管理,也就是说,无法根据之前的状态进行本次的请求处理。假设要求登录认证的Web页面本身无法进行状态管理(不记录已经登录的状态),那么每次跳转新页面就要再次登录,或者要在每次请求报文中附加参数来管理登录状态。

小汪:我记得HTTP协议中可以使用Cookie技术来解决该问题,管理客户端的状态。

大榜:基础不赖啊。Cookie技术是通过在请求和响应报文中写入Cookie信息,来控制客户端的状态。

客户端第一次登录时,是没有Cookie信息状态下的请求,服务器接收请求后,会在响应中添加一个Set-Cookie的首部字段信息,通过客户端保存Cookie,流程如下图:

image.png

当下次(第2次以后)客户端再往服务器发送请求时,客户端会自动在请求报文中加入Cookie值后发送出去,服务端发现客户端发送过来的Cookie后,会去检查究竟是从哪一个客户端发来的连接请求,然后对比服务器上的记录,最后得到之前的状态信息,流程图是下面这样(图片来源于《图解HTTP》书籍):

image.png

3、总结

通过小汪和大榜的对话,我们讨论了JavaEE,对Servlet规范进行回顾,重点介绍了Servlet的doGet、doPost方法,接着介绍了过滤器Filter、监听器Listener;然后我们讨论了客户端和服务端如何通过Cookie技术进行交互。后面,我们将进入Web开发,在白卷项目的基础上,进行改造和优化。

4、参考内容

1)《图解HTTP》

2)JavaEE 从入门到放弃

3)Vue + Spring Boot 项目实战(白卷)