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,流程如下图:
当下次(第2次以后)客户端再往服务器发送请求时,客户端会自动在请求报文中加入Cookie值后发送出去,服务端发现客户端发送过来的Cookie后,会去检查究竟是从哪一个客户端发来的连接请求,然后对比服务器上的记录,最后得到之前的状态信息,流程图是下面这样(图片来源于《图解HTTP》书籍):
3、总结
通过小汪和大榜的对话,我们讨论了JavaEE,对Servlet规范进行回顾,重点介绍了Servlet的doGet、doPost方法,接着介绍了过滤器Filter、监听器Listener;然后我们讨论了客户端和服务端如何通过Cookie技术进行交互。后面,我们将进入Web开发,在白卷项目的基础上,进行改造和优化。
4、参考内容
1)《图解HTTP》