创建java EE应用并部署
新建java EE项目
1.新建Jakarta EE项目 应用服务器可以先不选

2.用tomact 10版本的话 EE版本9.1是没有问题的
servlet版本>=5.0时 运行它的tomcat版本需要tomcat10.x版本的 具体的情况可以去看tomcat官网查

等待maven将需要的依赖都拉下来

查看一下文件的内容

index.jsp是输出Hello World!到前端 同时还插入了一个hello-servlet链接。
hello-servlet是什么?在HelloServlet.java文件中有定义

仔细看代码发现有HttpServletRequest和HttpServletResponse,这两个是Servlet API提供的HTTP请求和响应的对象,具体的执行是在底层的服务器应用(也就是我们过会要用的Tomact上执行的),后续关于请求和响应的操作我们在上层用request和response这俩参数就可以了。
观察pom.xml 我们还会发现 它打包的格式是war文件

war就是java web项目文件的打包形式 其实就是类似于java普通项目的jar文件 也相当于是一个压缩包
但是war文件的执行必须要依赖底层的web应用服务器,比如tomcat,后面我们就是把war文件放到tomcat目录下执行的。
部署项目
1.将该项目打包成为war包

2.部署tomcat 注意tomcat各个版本的运行要求

部署时直接解压即可
3.运行tomcat 进入bin目录执行startup

乱码不用管
4.尝试访问tomcat 端口8080
5.找到项目war文件 放到tomcat的webapps目录下

在这儿遇到个问题 为啥直接给我起的是9.0.105版本的tomcat啊?
因为tomcat9.0.105先安装的 环境变量的路径是它的

现在改一下它 指向tomcat10的路径

现在是切换到10.1.30版本了

没问题了 可自动解压

访问该目录

点击Hello Servlet

Servlet学习
一个java web的项目就是有多个Servlet组成的
浏览器发出的HTTP请求总是由Web Server先接收,然后,根据Servlet配置的映射,不同的路径转发到不同的Servlet。这种根据路径转发的功能我们⼀般称为dispatch。映射到 /比较特殊,相当于/*,会接收所有未匹配的路径。
有了 HttpServletRequest 和 HttpServletResponse 这两个⾼级接⼝,我们就不需要直接处理HTTP协议了。
接下来我们再去完善一下我们那个java EE的小网站
IDEA自动部署tomcat
之前我们是通过把war文件放到tomcat 的webapps目录下进行部署的
但是现在要频繁地去修改代码 每次改完重新打war包再放入tomcat的目录下这也太蠢了吧
那应该怎么做?IDEA提供了自动部署tomcat和java web进行修改和调试的方式
1.当前文件->编辑配置 点击+号新增配置 选择本地tomcat服务器

2.选择我们的tomcat文件位置去配置应用服务器

3,由于我的8080是给bp预留的 所以程序执行我就选择8081了

4,点击修复 选择java ee war:exploded

5.设置应用程序上下文 点击应用 确定。(一定要记得这一步,不然访问时就得带着javaee_war_exploded这个url了)

6.点击右上角图标运行

会自动进行浏览器的跳转

后面如果有代码修改更新的话需要点击右上角图标进行重新的部署

OK 可以开始弄了
接下来就可以去完善这web项目了
重定向与转发
我们已经编写了⼀个能处理 /hello 的 HelloServlet ,如果收到的路径为 /hi ,希望能重定向到 /hello ,可以再编写⼀个 RedirectServlet 。代码如下:
java
@WebServlet(urlPatterns = "/hi")
public class RedirectServlet extends HttpServlet {
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
//构造重定向的路径
String name = request.getParameter("name");
String redirectToUrl = "/hello"+(name == null ? "" : "?name="+name);
//发送重定向的响应
response.sendRedirect(redirectToUrl);
}
}
抓包可以看到 当我们访问/hi时 响应包会返回302 重定向到Location: /hello

随后客户端发送/hello请求到服务端 访问到了HelloServlet

如何实现转发呢?
Forward是指内部转发。当⼀个Servlet处理请求的时候,它可以决定⾃⼰不继续处理,⽽是转发给另⼀个Servlet处理。
现在,我们已经编写了⼀个能处理 /hello 的 HelloServlet ,继续编写⼀个能处理 /morning 的 ForwardServlet。
ForwardServlet 在收到请求后,它并不⾃⼰发送响应,⽽是把请求和响应都转发给路径为 /hello 的Servlet。
代码如下:
java
@WebServlet(urlPatterns = "/morning")
public class ForwardServlet extends HttpServlet {
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
request.getRequestDispatcher("/hello").forward(request, response);
}
}
抓包看的话 虽然我们访问的url是/morning 但是返回的却是HelloServlet的内容

继续哈!
Session和Cookie的使用
对于Web应⽤程序来说,我们总是通过 HttpSession 这个⾼级接⼝访问当前Session。JSESSIONID 是由Servlet容器⾃动创建的,⽬的是维护⼀个浏览器会话,它和我们的登录逻辑没有关系。即使没有登录功能,仍然可以使⽤ HttpSession 追踪⽤户。
session id 是维护浏览器会话自动创建的,和登录逻辑是没有关系的,登录逻辑给cookie中新增的key是要和这个sessionID做绑定的 也就是他俩都是cookie的内容。
写一个登录的servlet 代码如下:
登录成功后去给cookie增加一个user字段保存用户名
写⼊完毕后调⽤ flush() 却是必须的,因为⼤部分Web服务器都基于HTTP/1.1协议,会复⽤TCP连接。如果没有调⽤ flush() ,将导致缓冲区的内容⽆法及时发送到客户端。
java
@WebServlet(urlPatterns = "/signin")
public class SignInServlet extends HttpServlet {
//模拟一个数据库
private Map<String, String> users = Map.of("bob", "bob123", "alice","alice123", "tom", "tomcat");
//get请求时显示登录页面
protected void doGet(HttpServletRequest req, HttpServletResponse resp)throws ServletException, IOException{
resp.setContentType("text/html");
PrintWriter pw = resp.getWriter();
pw.write("<h1>Sign In</h1>");
pw.write("<form action=\"/signin\" method=\"post\">");
pw.write("<p>Username: <input name=\"username\"></p>");
pw.write("<p>Password: <input name=\"password\" type=\"password\"></p>");
pw.write("<p><button type=\"submit\">Sign In</button> <a href=\"/\">Cancel</a></p>");
pw.write("</form>");
pw.flush();
}
// POST请求时处理⽤户登录:
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException{
String name = req.getParameter("username");
String password = req.getParameter("password");
String expectedPassword = users.get(name.toLowerCase());
if (expectedPassword != null && expectedPassword.equals(password)){
// 登录成功:
req.getSession().setAttribute("user", name);
resp.sendRedirect("/");
}else {
resp.sendError(HttpServletResponse.SC_FORBIDDEN);
}
}
}
/路径的servlet也要写一下 /代表/* 其他没有匹配到的url都会转到它
在 AdminServlet 中,我们设置可以从 HttpSession 取出⽤户名 从而完成身份权限的校验。
java
@WebServlet(urlPatterns = "/")
public class AdminServlet extends HttpServlet {
protected void doGet(HttpServletRequest req, HttpServletResponse resp)throws ServletException, IOException{
// 从HttpSession获取当前⽤户名:
String user = (String) req.getSession().getAttribute("user");
resp.setContentType("text/html");
resp.setCharacterEncoding("UTF-8");
resp.setHeader("X-Powered-By", "JavaEE Servlet");
PrintWriter pw = resp.getWriter();
pw.write("<h1>Welcome, " + (user != null ? user : "Guest") + "</h1>");
if (user == null){
//未登录
pw.write("<p><a href=\"/signin\">Sign In</a></p>");
}else {
pw.write("<p><a href=\"/signout\">Sign Out</a></p>");
pw.write("<h2>这是管理员后台</h2>");
}
}
}
未匹配到的路径会自动匹配到AdminServlet

补全signout的逻辑 登出逻辑就是从 HttpSession 中移除⽤户相关信息。
java
@WebServlet(urlPatterns = "/signout")
public class SignOutServlet extends HttpServlet {
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws IOException {
// 从HttpSession移除⽤户名:
req.getSession().removeAttribute("user");
resp.sendRedirect("/");
}
}
完整流程如下:记得删除index.jsp否则它优先执行可能会有问题
1.访问任何不存在的路径自动转到AdminServlet 但是由于未登录 会提示signin

2.点击 Sign in进行登录 账号bob先输入错误的密码bob

会禁止访问

3.输出正确的密码bob123

登陆成功
4.点击sign out 退出登录

ok了!
JSP开发
JSP是Java Server Pages的缩写,它的⽂件必须放到 /src/main/webapp 下,⽂件名必须以 .jsp 结尾,整个⽂件与HTML并⽆太⼤区别,但需要插⼊变量,或者动态输出的地⽅,使⽤特殊指令 。
写一个jsp文件:代码hello.jsp
java
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>>Hello World - JSP</title>
</head>
<body>
<%-- JSP Comment --%>
<h1>Hello World!</h1>
<p>
<%
out.println("Your IP address is ");
%>
<span style="color:red">
<%= request.getRemoteAddr() %>
</span>
</p>
</body>
</html>

有报红 但是可以运行的
因为他在idea中找不到对应的依赖 但是它在tomcat运行时tomcat中是有对应的依赖的

当然也可以在项目结构中去引入对应版本的tomcat的依赖 来消除这个问题


JSP⻚⾯内置了⼏个变量:
out:表示HttpServletResponse的PrintWriter;
session:表示当前HttpSession对象;
request:表示HttpServletRequest对象。
JSP的指令除了 外,JSP⻚⾯本身可以通过 page 指令引⼊Java类,这样后续的Java代码才能引⽤简单类名⽽不是完整类名。也可以使⽤ include 指令可以引⼊另⼀个JSP⽂件
JSP和Servlet没什么区别,因为JSP在执⾏前⾸先被编译成⼀个Servlet,这么看JSP要比Servlet高级一丢丢啊,但是本质上还是一样。
Tomcat的临时⽬录下,可以找到⼀个 hello_jsp.java 的源⽂件,这个⽂件就是Tomcat把JSP⾃动,转换成的Servlet源码。
Filter
为了把⼀些公⽤逻辑从各个Servlet中抽离出来,JavaEE的Servlet规范还提供了⼀种Filter组件,即过滤器,它的作⽤是,在HTTP请求到达Servlet之前,可以被⼀个或多个Filter预处理,类似打印⽇志、登录检查等逻辑,完全可以放到Filter中。
chain.doFilter(request, response); 写完Filter如果要继续处理请求的话 就得要加chain.daFilter 否则到filter这儿就不转发了 到不了Servlet 看到的就是空白页
新建一个Filter 软件包 写一个EncodingFilter 代码如下:
java
@WebFilter(urlPatterns = "/*")
public class EncodingFilter implements Filter {
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
System.out.println("EncodingFilter:doFilter");
request.setCharacterEncoding("UTF-8");
response.setCharacterEncoding("UTF-8");
chain.doFilter(request, response);
}
}
去访问sign in

加断点进行调试 为了确定servlet和filter的顺序

确实是先调用了filter 然后才调用的 Signinservlet
Filter可以有针对性地拦截或者放⾏HTTP请求 比如写一段代码 MyFilter 不写chain.doFilter
java
@WebFilter("/*")
public class MyFilter implements Filter {
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain){
}
}
看到的页面就是空白页了

所以如果Filter要使请求继续被处理,就⼀定要调⽤chain.doFilter()
Listener
Listener顾名思义就是监听器,有好⼏种Listener,其中最常⽤的是 ServletContextListener,它其实就是去监听程序是否进行初始化or关闭。
任何标注为 @WebListener ,且实现了特定接⼝的类会被Web服务器⾃动初始化
它会在整个Web应⽤程序初始化完成后,以及Web应⽤程序关闭后获得回调通知
新建一个listener软件包 写一个AppListener 代码如下
java
@WebListener
public class AppListener implements ServletContextListener {
// 在此初始化WebApp,例如打开数据库连接池等:
public void contextInitialized(ServletContextEvent sce){
System.out.println("WebApp initialized.");
}
// 在此清理WebApp,例如关闭数据库连接池等:
public void contextDestroyed(ServletContextEvent sce){
System.out.println("WebApp destroyed.");
}
}
在网站部署和停止时都回去调用
