创建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 。代码如下:

@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。

代码如下:

@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() ,将导致缓冲区的内容⽆法及时发送到客户端。

@WebServlet(urlPatterns = "/signin")
public class SignInServlet extends HttpServlet {
    //模拟一个数据库
    private Map 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("

Sign In

"); pw.write("
"); pw.write("

Username:

"); pw.write("

Password:

"); pw.write("

Cancel

"); pw.write("
"); 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 取出⽤户名 从而完成身份权限的校验。

@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("

Welcome, " + (user != null ? user : "Guest") + "

"); if (user == null){ //未登录 pw.write("

Sign In

"); }else { pw.write("

Sign Out

"); pw.write("

这是管理员后台

"); } } }

未匹配到的路径会自动匹配到AdminServlet

补全signout的逻辑 登出逻辑就是从 HttpSession 中移除⽤户相关信息。

@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

<%@ page contentType="text/html;charset=UTF-8" language="java" %>


    >Hello World - JSP


    <%-- JSP Comment --%>
    

Hello World!

<% out.println("Your IP address is "); %> <%= request.getRemoteAddr() %>

有报红 但是可以运行的

因为他在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 代码如下:

@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

@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 代码如下

@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.");
    }
}

在网站部署和停止时都回去调用