Tomcat
概述
特殊的Servlet容器,开启服务后可通过访问本地ip获取文档;WebApps目录下为网站下的资源,可通过域名后加"/"定位;每个资源为一个网站页面,包括一个index.html或者index.jsp代码的主页,和一个WEB-INF目录储存相关依赖包,多种类以及配置文件;
Http请求流程
分析一个http://localhost:8080/docs/api 请求。
第一步:连接器监听的端口是8080。由于请求的端口和监听的端口一致,连接器接受了该请求。
第二步:因为引擎的默认虚拟主机是 localhost,并且虚拟主机的目录是webapps。所以请求找到了 tomcat/webapps 目录。
第三步:解析的 docs 是 web 程序的应用名,也就是 context(Application Context:只是url后缀而已)。
第四步:解析的 api 是具体的业务逻辑地址。此时需要从 docs/WEB-INF/web.xml 中找映射关系,最后调用具体的函数。
Maven
约定大于配置,抽象设计理念,符合直觉且合理,减少团队内耗;
安装
maven.apache.org
镜像
阿里云Maven中央仓库 为 阿里云云效 提供的公共代理仓库,帮助研发人员提高研发生产效率,使用阿里云Maven中央仓库作为下载源,速度更快更稳定。
Maven 配置
打开 Maven 的配置文件(windows机器一般在maven安装目录的conf/settings.xml),在标签中添加 mirror 子节点:
<mirror>
<id>aliyunmaven</id>
<mirrorOf>*</mirrorOf>
<name>阿里云公共仓库</name>
<url>https://maven.aliyun.com/repository/public</url>
</mirror>
环境变量配置
变量名MAVEN_HOME,值为安装地址
变量名M2_HOME,值为安装地址下bin目录
本地仓库配置
conf下的settings.xml
<localRepository>E:\Maven\apache-maven-3.8.4\repository</localRepository>
新建Maven项目
GAV : GroupId, AtifactsId,Version,在POM.XML文件里说明项目信息;
Maven仓库,用来导入dependencies到pom(项目对象模型)
Servlet
Sun公司的第一代动态Web开发接口,实现了Servlet接口即为Servlet应用;在Web开发时往往以一个空白project为父模块,在其目录下建新的Web模块;之后在父模块POM里会有module目录生成,同时父模块的依赖可以被使用(前提Scope设定)。
请求响应过程
生命周期:init > service > destroy
Servlet容器(Tomcat)接收到客户端请求,解析请求协议,如果servlet程序还没有被加载,就会执行>加载过程<并调用service()方法,否则直接调用service()方法。
其中,>加载过程<为Servlet容器调用init()方法将Servlet类载入内存,并产生Servlet实例。在调用init()方法的时候,Servlet容器会传入一个ServletConfig对象进来从而对Servlet对象进行初始化。该过程只会被执行一次,即在一个应用程序中,每类Servlet程序只能有一个实例。
service()的过程:由Servlet容器解析请求参数并封装成一个ServletRequest和ServletResponse对象。Servlet容器把ServletRequest和ServletResponse作为参数传递给了service()方法,通过执行service()方法,实现响应的逻辑,并通过ServletResponse对象返回内容到客户端。
最后,调用destroy()。
首次访问Servlet
最终成果
Web.XML的配置
因为浏览器连接服务器,此配置实质就是在配置服务器的映射代码;
<?xml version="1.0" encoding="UTF-8"?>
<!-- 从Tomcat的web.xml中复制过来的版本配置,否则版本WebApps过低-->
<web-app xmlns="https://jakarta.ee/xml/ns/jakartaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="https://jakarta.ee/xml/ns/jakartaee
https://jakarta.ee/xml/ns/jakartaee/web-app_5_0.xsd"
version="5.0"
metadata-complete="true">
<request-character-encoding>
UTF-8
</request-character-encoding>
<!-- 设置了firstServlet下为FirstServlet类(必须继承HttpServlet)-->
<servlet>
<servlet-name>firstServlet</servlet-name>
<servlet-class>FirstServlet</servlet-class>
</servlet>
<!-- 设置映射,/testing保证了url下此文档为名为firstServlet的servlet -->
<servlet-mapping>
<servlet-name>firstServlet</servlet-name>
<url-pattern>/testing</url-pattern>
</servlet-mapping>
</web-app>
servelet类,动态请求渲染页面
//注意依赖为Jakarta,instead of javax.servelet
import jakarta.servlet.ServletException;
import jakarta.servlet.http.HttpServlet;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;
public class FirstServlet extends HttpServlet {
//注意方法为public,否则405
@Override
public void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
resp.setCharacterEncoding("utf-8");//防止乱码
{
resp.setContentType("text/html");
PrintWriter out = resp.getWriter();//获取输出流
out.println("<html>");
out.println("<head>");
out.println("<title>TEST!!!</title>");
out.println("</head>");
out.println("<body>");
out.println("<h1>Everything's worth of it!凸(艹皿艹 )</h1>");
out.println("</body>");
out.println("</html>");
}
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
super.doPost(req, resp);
}
}
Mapping
web.xml下的servlet-mapping标签下的url-parttern,可以加通配符,但是优先级低于固有路径
ServletContext
Servlet容器的上下文环境,参考进程上下文环境,其内一个个线程即类似一个个的Servlet服务类,可共享参数(.setAttribute(string:K,object:V)),传递配置文件(.getSourceAsInputStream(相对路径)),转发服务(.dispatcher("/new_url",context.forward()))等等;
下载实现
public class FileDownload extends HttpServlet
{
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
String path = "C:\\Users\\lenovo\\Desktop\\2.jpg";
//resp.setHeader("refresh","5");每五秒刷新后台
//设置响应头,响应下载格式并提供文件名
resp.setHeader("Content-Disposition","attachment;filename=" + path.substring(path.lastIndexOf("\\")));
File file = new File(path);
byte[] buffer = new byte[1024]; //in和out流间的缓冲字节数组
FileInputStream in = new FileInputStream(file);//从文件读取的流
int length = 0;//记录读入长度
ServletOutputStream out = resp.getOutputStream();//向浏览器输出的流
while ((length = in.read(buffer)) > 0) //从输入流read进buffer,由于进程异步性,不能一次读取完毕,设置length表示读取长度
{
out.write(buffer,0,length);//从buffer读取字节写出到输出流
}
}
}
转发和重定向
目的是在不同servlet间流转,形成不同服务间的切换;
重定向:sendRedirect(绝对url或者/相对url);迭代式
转发:getDispcher(绝对url或者/相对url).forward(req,resp); 递归式
Cookie
- 通过req获取;resp.addCookie(“key”,“value”);
- 存在maxAge过期时间,以设定0maxAge函数使用户能够主动消除客户端cookie;
- 以本地浏览器为主体,保存在本地浏览器缓存中,不以客户端的关闭访问为结束;
Session
- 在web.xml中可配置session过期时间,或是在session对象的MaxInactiveTime中,一个是分钟一个是秒;
- 以服务端(web服务器)为主体,浏览器关闭访问session就结束了;
- 以JsessionID为cookie的基础,通常这个cookie设为-1MaxAge,即为即时会话;
JSP
- 本质就是Servlet,在Tomcat容器加载请求时,work目录下的index.java存有jsp的相应Servlet程序,就是吧jsp里的各种标签代码加载成Servlet样式再执行这个Servlet向网站交互,提供渲染;可以和html互动,但本质还是转换成out.write写出html标签格式,以及伴随的java代码。
一些标签
- <%-- annotation --%>在前端不展示, <% java programming code %>service方法内部加载;
- <%! global variables %>在jsp类内部加载, <%= recall methods or variables %>
- <%@ include file="…/header.jsp"%>,<jsp:include page = “…/header.jsp”>;二者区别就是前者把header.jsp的代码加进来,后者只是引入jsp文件;
- 展示error页面,第一种方法可在web-xml里配置标签,里面有code和location等属性可配置;第二种方法可以在jsp命令标签添加<%@ page error-page = “…/error.jsp”%>;
- <jsp:param name = “xx” value = “xx”></jsp:param>参数参数,<jsp:forward page="/xxx.jsp"></jsp:forward>转发标签,内部可嵌套参数标签
- Express Language表达式${使用请求对象获取参数};
JSP内置对象作用域
- Application(ServletContext,随服务器作用域)
- Session(会话,随浏览器作用域)
- Request(请求作用域)
- PageContext(当前页作用域),可forward()转发,可setScope设置提升作用域;
- 上四个内置对象都可setAttribute和findAttribute进行参数存取,只是作用域(生命周期)不一样;
JSTL(JAVA STANDARD TAG LIBRARY)
- 核心标签(core)导入前缀:<%@taglib prefix=“c” uri=http://java.sun.com/jsp/jstl/core %>
EG:
<c:forEach var="var" items="${value}">
<c:out value="${var}"> This is from test 1</c:out>
</c:forEach>
其中el表达式代表引用变量,适用参数为引用变量的时候;
MVC
- MVC指MVC模式的某种框架,它强制性地使应用程序的输入、处理和输出分开。使用MVC应用程序被分成三个核心部件:模型、视图、控制器。它们各自处理自己的任务。最典型的MVC就是JSP + servlet + javabean的模式。
Filter过滤器
- 在服务器与请求的资源间的Servlet类,实现了Servlet包下的Filter接口,存在init,doFilter和destroy三个约束方法分别在服务器加载时初始化,请求资源时执行Filter逻辑以及关闭服务器时销毁;
- 注意doFilter中的chain参数,在过滤逻辑代码结束时需调用chain.doFilter(req,resp),以在多个过滤器间传递参数,形成多层过滤;
- 最后不要忘了在web.xml配置filter和filter-mapping,一般url-pattern设置为xxx/*以过滤一个目录资源下的所有请求响应;
登录拦截
- Filter
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
//强转至http的请求响应对象
HttpServletRequest req = (HttpServletRequest) servletRequest;
HttpServletResponse resp = (HttpServletResponse) servletResponse;
//若是没有验证成功就想直接访问主页,重定向至登录页面
if(req.getSession().getAttribute("id") == null) {
resp.sendRedirect("/index.jsp");
System.out.println("filtered");
}
filterChain.doFilter(servletRequest,servletResponse);
}
- Servlet验证类
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
String username;
//传回的用户名不为空的情况下判断是否为“administrator”
if(req.getParameter("username") != null) {
username = req.getParameter("username");
System.out.println(username);
if(username.equals("administrator"))
{ //是的话定向至主页并且对session进行绑定id,进而方便过滤直接访问情况
req.getSession().setAttribute("id",req.getSession().getId());
resp.sendRedirect("/home.jsp");
}
else
resp.sendRedirect("/error.jsp");
}
else
resp.sendRedirect("/error.jsp");
}
- 登录页面的action注意是web.xml里配置的mapping-url,不能写jsp或者Servlet类;
Listener监听器
- 大量接口描述监听内容,对象等等,例如SessionListener,统计会话数量,得到当前访问人数。
- 在we.xml中只需配置一个listener-calsses即可被动生效。
项目整合
架构设计
- View层:多个JSP展示与用户的交互,提供接收数据的容器;
- Service层:多个业务逻辑Servlet桥接数据与持久化层;
- Model层:Dao数据持久化,提供与数据库的交互,CRUD等等;
- 总体来说就是前端的多个JSP静态资源以及JS组成了少部分逻辑编写,例如判断新旧密码是否相等;其次在Servlet包下的多个Servlet逐一实现了系统的逻辑功能,通过调用Service层的业务类中二次调用Dao层持久化功能函数,实现对数据库的CRUD,达到诸如登录验证,密码修改,订单查询等功能。
登录验证
- 由前端返回的登录请求携带的用户名和密码,调用Service.login(用户名,密码)二次调用Dao实现类中的getUser通过封装的工具类Connector.query查询有无该用户并返回结果集或null,之后在Dao层建立返回的User实体类交给Service层后判断密码是否正确,最后返回该用户,并在Servlet层添加该用户至Session后重定向至主页,否则设置登录失败参数转发回登录页面;
- 需要注意的是各层返回值为空的问题,需要频繁地非空验证;
退出
- 移除Session中的user信息,并且重定向至登录页面;
- 需要注意的是权限验证问题,通过Filter验证Session中有无当下User参数来判断是否有人想通过返回指定url访问功能页,如果有的话设定请登录参数,转发回到登录页面;
密码修改
- 底层执行Dao实现类调用的Connector.update执行修改sql,成功后返回true,在Servlet层移除session并携密码已修改参数转发至登录页面,失败则刷新当前页面,提示修改失败;
- Servlet复用,通过前端隐藏域的参数提交在统一Servlet中进行分支判断走不同的功能方法;
- 新旧密码验证通过修改密码jsp引入的js代码实现,其中旧密码验证用失焦判断Ajax方法执行异步请求与数据库交互,通过对旧密码的各种判断put进map里不同参数后以JSON形式响应返回,最后在前端以不同CSS样式实时提醒用户旧密码是否正确等;
用户查询
- 点击用户查询时,不同于之前配置webxml后走Servlet提交表单再返回结果,该点击的请求额外夹带了method为query的参数使页面加载时直接显示结果;
- 动态SQL,根据页面是否填写参数对SQL进行append多个where条件进而返回不同的结果集;
文件上传
- 为保证服务器安全(外界访问),上传的文件应该保存在Web-Inf下(外界不可通过url访问),一般通过file对象的mkdir方法在此建立两个目录,uploadedFiles和temp,用来储存文件和临时文件(会被清理的文件)
- 前端html须加上"multi-part/form-data判断",同时后端接收时也需要判断有无此字段
- 必须使用post请求,因为get限制大小,此外文件也需要可以限制大小或者后缀
- 需要ServletFileUpload类,而它又需要DiskFileItemFactory对象最为构造参数(工厂代理)
- 利用ServletFileUpload对象的parseRequest方法解析请求,返回表单对象list,遍历并判断是否为携带文件的提交对象
- 通过输入流进缓存数组,输出流到目标目录,上传的指定位置
邮件发送
- 利用多线程跑诸如邮件等访问控制服务器的业务,在前端直接写回结果,即可满足“三秒网站”准则
- POP3协议 = 用户离线服务器缓存接受后在登录是时候转发
- SMTP协议 = 利用TCP在多种邮箱服务器间转发