本章开始涉及javaweb主体内容,主要讲解Servlet、Jsp以及Tomcat在客户端与服务端的交互过程中各自有什么用
如果还有新手村玩家(比如我 )没有看前两张基础篇,建议先去学一下基础知识哦
Javaweb(一)基础知识篇
JavaWeb(二)框架搭建篇
服务器三个主要过程:1.接受请求 2.处理请求 3.响应请求
其中1和3是共性的功能,这个就交由tomcat来做了
由于处理请求的逻辑不同,那么就把这个过程出去出来,作为Servlet,交给程序员编写
随着互联网的发展,有一些逻辑也从servlet中抽取出来分为service层和Dao层
首先去查看Servlet的源码
public interface Servlet {
//Tomcat(由web.xml解析反射创建Servlet之后)调用init(),传入ServletConfig
void init(ServletConfig var1) throws ServletException;
//Servlet配置 由web.xml解析,创建ServletConfig实例
ServletConfig getServletConfig();
//Http请求到了Tomcat之后,Tomcat通过解析把url、参数等都封装进了Request,并同时生成空Response
//由Servlet处理后的数据通过response.write()写入response的缓冲区,最后Tomcat拿到response,组装成http响应发给客户端
void service(ServletRequest var1, ServletResponse var2) throws ServletException, IOException;
String getServletInfo();
void destroy();
}
再去找实现了Servlet的抽象类GenericServlet
public abstract class GenericServlet implements Servlet, ServletConfig, Serializable {
//得到ServletContext
public ServletContext getServletContext() {
ServletConfig sc = this.getServletConfig();
if (sc == null) {
throw new IllegalStateException(lStrings.getString("err.servlet_config_not_initialized"));
} else {
return sc.getServletContext();
}
}
//提升ServletConfig作用域
public void init(ServletConfig config) throws ServletException {
this.config = config;
this.init();
}
public void init() throws ServletException {
}
public ServletConfig getServletConfig() {
return this.config;
}
//最重要的service还是没有实现
public abstract void service(ServletRequest var1, ServletResponse var2) throws ServletException, IOException;
}
继续看GenericServlet的子类HttpServlet
public abstract class HttpServlet extends GenericServlet {
//实现了各种请求方式
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {...}
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {...}
protected void doHead(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {...}
protected void doPut(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {...}
protected void doDelete(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {...}
protected void doOptions(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {...}
//实现了service 完成了复杂的请求方式判断,并且调用了对应的函数
protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {...}
public void service(ServletRequest req, ServletResponse res) throws ServletException, IOException {... }
}
到此,基础源码就差不多看完了
也就是说Servlet是一个接口,我们需要写一个类继承一个实现了Servlet接口的抽象类
换句话说,我们实现了doGet和doPost就可以实现一个Servlet了
另:这种装载共享数据的对象,在Javaweb中有四个,被叫作“域对象”:
- ServletContext域(Servlet间共享数据)
- Session域(一次会话间共享数据,也可以理解为多次请求间共享数据)
- Request域(同一次请求共享数据)
- Page域(JSP页面内共享数据)
它们都可以看做是map,都有getAttribute()/setAttribute()方法。
浏览器发送的请求会先经过映射器,由映射规则配发给各个Servlet
我们继续扒源码,这次翻到tomcat的web.xml下,可以看到tomcat默认带了俩Servlet
也就是说
另:如果某个Servlet的映射路径仅仅为一个正斜杠(/),那么这个Servlet就成为当前Web应用的缺省Servlet。凡是在web.xml文件中找不到匹配的元素的URL,它们的访问请求都将交给缺省Servlet处理。就像是Tomcat里面自带的DefaultServlet,当我们访问静态页面的时候,就是在访问他
过滤器Fileter是一个小型的web组件,它们通过拦截请求和响应,以便查看、提取或以某种方式操作客户端和服务器之间交换的数据,实现“过滤”的功能
继续翻源码,我们依旧可以在web.xml中找到拦截器及其映射
对于每个映射到的url,都会经过过滤器,也就是说我们一般会把共性的操作放到过滤器实现
会不会有人想到用线程锁(synchronized)?
很好,可是这样的话就会出现一个问题,对于高并发的情况,我们确实保证了数据的安全,但是对于每一个线程都会上锁,也就是说用户的请求需要排队一个一个进行处理,这好像并不是很合适吧。
持续更新…
早期的服务器端是用Java开发的,我们看到的html页面都是由前端写好,交给后端程序员,后端在Servlet中调用out.println("
JSP全称Java Server Page,就是在html里面嵌套Java代码,实现动态数据
Web容器(Tomcat)接受到以.jsp为拓展名的url请求的访问时,会将他交给Jsp引擎处理(JspServlet),也就是我们上面提到过的Tomcat自带的默认映射。每个Jsp页面第一次被访问时,JspServlet都会把他翻译成一个Servlet程序(.java),接着再把这个程序编译成类文件(.class),然后再由Tomcat像调用普通Servlet那样执行程序。
本质
我们在本地这个目录里面能找到被编译成的.java和.class
C:\Users\(用户名)\AppData\Local\JetBrains\IntelliJIdea2021.2\tomcat\(项目名)\work\Catalina\localhost\ROOT\org\apache\jsp
打开看源码
可以看到,被编译成的.java其实也是用out.write()写的html页面,也就是说程序帮我们做了 “把html代码复制进Servlet并用out.write()输出” 的操作。
我们再去看index_jsp这个类的继承情况
HttpJspBase,我们再去翻他(强迫症将就一下吧,SuppressWarnings都没压住报错)
看到了什么?HttpServlet!我们的Servlet不也是继承的他嘛。也就是说Jsp本质上其实就是一个Servlet,只不过Jsp被包装了太多东西了。
对象
继续看源码就能看到Jsp里面有这些属性
final javax.servlet.jsp.PageContext pageContext; //页面上下文
javax.servlet.http.HttpSession session = null; //session
final javax.servlet.ServletContext application; //applicationContext
final javax.servlet.ServletConfig config; //config
javax.servlet.jsp.JspWriter out = null; //out
final java.lang.Object page = this; //page:当前页
HttpServletRequest request //请求
HttpServletResponse response //响应
这啥,先不着急,接着往下看
预处理
依旧是看源码,我们可以看到在Jsp开始疯狂的out.write()之前有这么一段代码
response.setContentType("text/html"); //设置响应页面类型(动态资源变成静态)
pageContext = _jspxFactory.getPageContext(this, request, response,
null, true, 8192, true);
_jspx_page_context = pageContext;
application = pageContext.getServletContext(); //application=ServletContext
config = pageContext.getServletConfig();
session = pageContext.getSession();
out = pageContext.getOut();
_jspx_out = out;
认真看一下就能发现,这不就是一个类似于构造函数得到东西嘛,而且正好是对上面那些对象的初始化,那么一切就清晰了
内置对象
传说中的九大内置对象已经开始明朗了
- PageContext 【存东西】
- Request 【存东西】
- Response
- Session 【存东西】
- Application ——就是SerlvetContext 【存东西】
- config ——就是SerlvetConfig
- out
- page
- exception
那他们有什么区别呢
pageContext.setAttribute("name1","1"); //保存的数据只在一个页面中有效
request.setAttribute("name2","2"); //保存的数据只在一次请求中有效,请求转发会携带这个数据
session.setAttribute("name3","3"); //保存的数据只在一次会话中有效,从打开浏览器到关闭浏览器
application.setAttribute("name4","4"); //保存的数据只在服务器中有效,从打开服务器到关闭服务器
我们取属性的先后顺序是pageContext -> request -> session -> application(双亲委派机制)
有了上面两内容的学习,相信各位对 Tomcat已经有了初步的理解
Tomcat是轻量级的服务器,也被称为Servlet/JSP容器。
对于他的架构,我们在前面的内容中基本已经说完了,这里再来复习一下
首先
各个文件夹的作用
bin:——启动脚本文件:startup.bat ——关闭脚本文件:shutdown.bat
conf:tomcat配置(如果碰到乱码问题可以在里面的logging.properties配置)
lib:依赖的jar包
logs:日志
webapps:存放网站内容
其次我们在web.xml里找到了两个默认的Servlet及其映射
在server.xml里找到了基础配置,其中
讲到这也就差不多了,如果还想了解其他这方面的东西,就需要去拜读其他大佬的博客了
文章参考:
Servlet基础
深究Servlet
Jsp
Tomcat