java_web java的最流行的web服务器:Tomcat
1 2 3 4 5 Tomcat是Apache 软件基金会(Apache Software Foundation)的Jakarta 项目中的一个核心项目,由于有了Sun 的参与和支持,最新的Servlet 和JSP 规范总是能在Tomcat 中得到体现,Tomcat 5支持最新的Servlet 2.4 和JSP 2.0 规范。因为Tomcat 技术先进、性能稳定,而且免费,因而深受Java 爱好者的喜爱并得到了部分软件开发商的认可,成为目前比较流行的Web 应用服务器。 Tomcat 服务器是一个免费的开放源代码的Web 应用服务器,属于轻量级应用服务器,在中小型系统和并发访问用户不是很多的场合下被普遍使用,是开发和调试JSP 程序的首选。对于一个Java初学Web者来说,它是最佳的选择。 诀窍是,当配置正确时,Apache 为HTML页面服务,而Tomcat 实际上运行JSP 页面和Servlet。另外,Tomcat和IIS等Web服务器一样,具有处理HTML页面的功能,另外它还是一个Servlet和JSP容器,独立的Servlet容器是Tomcat的默认模式。不过,Tomcat处理静态HTML的能力不如Apache服务器。目前Tomcat最新版本为9.0.37。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 JSP(JavaServer Pages)是一种基于Java的服务器端技术,用于创建动态Web页面。它允许开发者在HTML(超文本标记语言)中嵌入Java代码,以实现动态内容的展示和与用户的交互。 JSP页面通常包含HTML代码和JSP标签(使用尖括号 <% %> 将Java代码嵌入到HTML中),其中的Java代码可以被服务器解释和执行。当客户端请求一个JSP页面时,服务器会将其转换成一个Servlet,并由Java Servlet容器来处理请求和生成响应。 JSP具有以下特点和优势: 简单易学:由于JSP页面主要基于HTML,因此对于熟悉HTML的开发者来说,学习和使用JSP相对容易。 动态内容:通过嵌入Java代码,开发者可以在JSP页面中生成动态内容,如从数据库获取数据、处理表单提交等。 重用和模块化:JSP支持使用自定义标签库和模板来实现页面的重用和模块化,提高了代码的可维护性和可扩展性。 强大的Java生态系统:作为Java的一部分,JSP可以直接调用Java的类库和API,利用Java强大的生态系统进行开发。 MVC架构支持:JSP通常与Java Servlet一起使用,以实现MVC(模型-视图-控制器)架构,将业务逻辑与视图分离,提高了代码的可读性和可维护性。
环境搭建 可以参考这篇文章,写的很详细:https://blog.csdn.net/m0_51545690/article/details/123077550?ydreferer=aHR0cHM6Ly93d3cuYmluZy5jb20v
乱码问题可以将conf/logging.properties文件中的java.util.logging.ConsoleHandler.encoding赋值为GBK
搭建完成,访问服务器
这里的内容是由index.jsp控制的,我们可以尝试修改一下index.jsp
JSP基本语法 jsp由Html、css、js、Java代码以及jsp标签等组成,其后缀名为.jsp jsp本质上就是一个Servlet(Servlet是http请求与响应的中间节点,主要用于处理Http请求与响应)
我们打开之前创建项目自动生成的index.jsp
可以看到index.jsp中包含有常见的html标签和下面这行代码
1 <%@ page contentType="text/html;charset=UTF-8" language="java" %>
简单来说,JSP就是在Html中嵌入Java代码。
大概的执行流程如下
用户向服务器发一个HTTP请求,例如请求index.jsp
服务器查找到index.jsp
将jsp文件转换成Servlet源文件,实际上就是java代码
编译转换后的java代码生成.class文件并执行
将执行结果返回给用户
声明语法
示例
1 2 3 4 5 6 7 8 <%! String name = "ycxlo" ; String getName () { return name; } %>
注意:声明语法用来定义成员变量 和成员方法
程序脚本 语法:
代码写在<% %>之间,包括局部变量 的声明,表达式,逻辑语句等 可以看做是java中的方法 程序脚本可以跟页面代码进行组合使用
示例
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 <%-- Created by IntelliJ IDEA. User: 杨晨鑫 Date: 2023 /7 /19 Time: 19 :49 To change this template use File | Settings | File Templates. --%> <%@ page contentType="text/html;charset=UTF-8" language="java" import ="java.io.*, java.util.*, javax.servlet.*" %> <% String name = "ycxlo" ; int age = 25 ; if (age > 25 ) %> 你的名字是: <% out.print(name); if (age > 30 ) { %> <h1>中年油腻大叔</h1> <% } else { %> <h1>青年</h1> <% } %>
输出如下:
注意:第一个if是和else连接的
注释语法
输出语法
示例
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 <%-- Created by IntelliJ IDEA. User: 杨晨鑫 Date: 2023 /7 /19 Time: 19 :49 To change this template use File | Settings | File Templates. --%> <%@ page contentType="text/html;charset=UTF-8" language="java" import ="java.io.*, java.util.*, javax.servlet.*" %> <%! String name = "ycxlo" ; String getName () { return name; } %> <%=name%> <%=getName()%>
输出:
包引入语法 1 <%@ page import ="要导入的包" %>
JSP内置对象 什么是内置对象? 内置对象其实就是已经定义好的对象,我们不需要创建直接使用即可。 内置对象主要是为了方便我们的开发,提升我们的开发效率 JSP有9个内置对象
request ( HttpServletRequest ) 作用域,表示一次请求 response ( HttpServletResponse ) 表示一次响应 session ( HttpSession ) 作用域 表示一次会话 out ( JspWriter )向浏览器返回数据 page (Servlet) 当前jsp对应的Servlet对象 application ( ServletContext )作用域,表示当前Web应用,全局对象。 pageContext ( pageContext ) 作用域 页面上下文,获取页面的信息 config (ServletConfig) 当前JSP对应的ServletConfig对象 exception (Exception) JSP页面中发生的异常 不同的内置对象有不同的作用域
常用的内置对象如下: request
response
session
application
pageContext
request request对象就是一个请求对象,封装了客户端的请求信息,比如我们填写的表单信息,服务端需要通过request对象拿到需要的数据,然后做出响应
常用方法:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 <% request.setAttribute("name" ,"喻志强" ); request.setCharacterEncoding("utf-8" ); request.getParameter("" ); request.getParameterNames(); request.getParameterValues("" ); request.getAttributeNames(); request.getAttribute("name" ); request.getCharacterEncoding(); request.getProtocol(); request.getServletPath(); request.getMethod(); request.getHeaderNames(); request.getHeader("" ); request.getRemoteAddr(); request.getRemoteHost(); request.getServerName(); request.getServerPort(); request.getCookies(); request.getSession(); request.getInputStream(); request.getContextPath(); request.getRequestDispatcher("result.jsp" ).forward(request,response); %>
response response对象对应着http请求的响应,其封装了响应体的信息,我们可以通过response对象响客户端返回数据。 response对象是HttpServletResponse的实例
常用方法:
1 2 3 4 5 6 7 8 9 response.getOutputStream(); response.getWriter(); response.sendRedirect("" ); response.setContentLength(1000 ); response.setContentType("text/html; charset=utf-8" ); response.getCharacterEncoding(); response.addCookie(new Cookie ("" ,"" )); response.setHeader("Content-Disposition" ,"attachment; filename=fileName" ); response.setStatus(200 );
session session:代表客户端与服务端的一次会话
用户第一次进入系统直到退出系统或关闭浏览器,期间的一系列行为称为会话,是HttpSession的实例。 session主要用于会话跟踪,可以用来共享数据,例如登录的用户信息等等
一次会话可能包含多次request和response
常用方法:
1 2 3 4 5 6 7 8 9 10 <% session.setAttribute("name" , "yzq" ); session.setAttribute("age" , 25 ); session.getCreationTime(); session.getId(); session.invalidate(); session.removeAttribute("name" ); %>
out out对象代表输出流对象,我们可以通过out对象向客户端输出内容 out对象是JspWriter类的实例
常用方法:
1 2 3 4 5 6 7 8 <% out.println("" ); out.clear(); out.clearBuffer(); out.close(); %>
servelet Servlet:是Java Servlet的简称,是运行在 Web 服务器中的小型 Java 程序。主要负责与客户端进行通信。
在之前的例子中,我们都是在jsp页面中直接去处理逻辑,但这在实际项目开发中明显是不合适的,会导致jsp页面过多,逻辑处理过于负复杂等问题。
在实际项目,我们更多的是使用servlet来处理数据
新建一个servelet
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 import javax.servlet.*;import javax.servlet.http.*;import javax.servlet.annotation.*;import java.io.IOException;@WebServlet(name = "LoginServlet", value = "/LoginServlet") public class LoginServlet extends HttpServlet { @Override protected void doGet (HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { } @Override protected void doPost (HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { } }
可以看到,LoginServlet继承自HttpServlet ,默认实现了doGet和doPost方法,我们在这里就可以处理请求,idea默认给我们的servlet加上了注解
处理请求 下面我们来写一个简单的表单,将数据提交给LoginServlet处理。
表单如下,需要注意的是,LoginServlet的访问路径是:http://localhost:8080/JavaWebDemo/loginServlet,不同的项目访问路径也是不同的,所以我们需要动态的获取访问路径。
通过上面的访问路径我们可以看出该路径由协议+主机名+端口号+项目名+servlet的对外访问路径组成。 那我们先动态获取一下项目路径
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 <%-- Created by IntelliJ IDEA. User: yzq Date: 2018 /7 /24 Time: 17 :04 To change this template use File | Settings | File Templates. --%> <%@ page contentType="text/html;charset=UTF-8" language="java" %> <html> <head> <title>登录</title> </head> <% String basePath = request.getScheme() + "://" + request.getServerName() + ":" + request.getServerPort() + application.getContextPath(); %> <body> <form action="<%=basePath%>/LoginServlet" method="post" > 账号:<input name="account" ><br> 密码:<input name="pwd" > <br> <br> <input type="submit" value="提交" > </form> </body> </html>
然后修改一下LoginServlet中的代码,代码很简单,从request中拿到表单数据后进行对比,这里没有连接数据库,先暂时写死,用户名密码正确则跳转至success.jsp,失败则跳转至fail.jsp.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 import javax.servlet.ServletException;import javax.servlet.annotation.WebServlet;import javax.servlet.http.HttpServlet;import javax.servlet.http.HttpServletRequest;import javax.servlet.http.HttpServletResponse;import java.io.IOException;@WebServlet(name = "LoginServlet", urlPatterns = "/LoginServlet") public class LoginServlet extends HttpServlet { protected void doPost (HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { doGet(request, response); } protected void doGet (HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { System.out.println("doGet" ); String account = request.getParameter("account" ); String pwd = request.getParameter("pwd" ); System.out.println("account" + account + ",pwd" + pwd); if (account.equals("ycx" ) && pwd.equals("123" )) { request.getRequestDispatcher("success.jsp" ).forward(request,response); }else { request.getRequestDispatcher("fail.jsp" ).forward(request,response); } } }
进入登录页面输出ycx/123即可跳转到success.jsp
Servlet生命周期 Servlet的生命周期分为三个阶段
初始化阶段,对应init()方法,首次访问Servlet的时候执行
响应请求阶段,对应service()方法,每次处理请求时调用
销毁阶段,对应destroy()方法,servlet销毁时调用
Servlet注解(@WebServlet) 前面我们实际上已经使用了一部分的注解了,使用注解使我们的代码变得简洁且容易维护,不必每次创建Servlet就去web.xml中进行配置。
实际上所有在web.xml中的配置我们都可以通过注解的形式来编写。
我们通过**@WebServlet**关键字对Servlet进行注解,并配置相关属性
常用注解
asyncSupported (Servlet是否支持异步操作模式)
name( Servlet的名称)
initParams ( 初始化参数 )
urlPatterns ( 访问路径 )
loadOnStartup (默认值为-1,当值为0或大于0时,表示服务器启动时就加载,否则,访问时才加载)
Filter 过滤器实际上就是对web资源进行拦截,做一些处理后再交给下一个过滤器或servlet处理 通常都是用来拦截request进行处理的,也可以对返回的response进行拦截处理
大致流程图如下
应用场景 自动登录
统一设置编码格式
访问权限控制
敏感字符过滤等
创建filter 在Servlet中我们一般都会对request和response中的字符集编码进行配置,如果Servlet过多字符集编码发生变化时修改起码会很麻烦,这些通用的字符集编码配置等工作我们可以放到Filter中来实现。 下面我们来创建一个处理字符集编码的Filter。
右键创建filter
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 package filter;import javax.servlet.*;import javax.servlet.annotation.*;import java.io.IOException;@WebFilter(filterName = "CharsetFilter") public class CharsetFilter implements Filter { public void init (FilterConfig config) throws ServletException { } public void destroy () { } @Override public void doFilter (ServletRequest request, ServletResponse response, FilterChain chain) throws ServletException, IOException { chain.doFilter(request, response); } }
默认实现3个方法
配置filter 可配置的属性有这些
常用配置项 urlPatterns 配置要拦截的资源
以指定资源匹配。例如”/index.jsp” 以目录匹配。例如”/servlet/“ 以后缀名匹配,例如” .jsp” 通配符,拦截所有web资源。”/*” initParams 配置初始化参数,跟Servlet配置一样
例如
1 2 3 initParams = { @WebInitParam(name = "key",value = "value") }
dispatcherTypes
配置拦截的类型,可配置多个。默认为DispatcherType.REQUEST 例如
dispatcherTypes = {DispatcherType.ASYNC,DispatcherType.ERROR}
其中DispatcherType是个枚举类型,有下面几个值
FORWARD,//转发的
INCLUDE,//包含在页面的
REQUEST,//请求的
ASYNC,//异步的
ERROR;//出错的
下面我们来对CharsetFilter 代码进行一下修改
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 package filter;import javax.servlet.*;import javax.servlet.annotation.WebFilter;import javax.servlet.annotation.WebInitParam;import java.io.IOException;@WebFilter(filterName = "CharsetFilter", urlPatterns = "/*",/*通配符(*)表示对所有的web资源进行拦截*/ initParams = { @WebInitParam(name = "charset", value = "utf-8")/*这里可以放一些初始化的参数*/ }) public class CharsetFilter implements Filter { private String filterName; private String charset; public void destroy () { System.out.println(filterName + "销毁" ); } public void doFilter (ServletRequest req, ServletResponse resp, FilterChain chain) throws ServletException, IOException { System.out.println(filterName + "doFilter()" ); req.setCharacterEncoding(charset); resp.setCharacterEncoding(charset); chain.doFilter(req, resp); } public void init (FilterConfig config) throws ServletException { filterName = config.getFilterName(); charset = config.getInitParameter("charset" ); System.out.println("过滤器名称:" + filterName); System.out.println("字符集编码:" + charset); } }
这样一个简单的字符集编码处理的过滤器就完成了
我们看看执行打印的结果
需要注意的是
过滤器是在服务器启动时 就会创建的,只会创建一个实例 ,常驻内存,也就是说服务器一启动就会执行Filter的
init(FilterConfig config)方法.
当Filter被移除或服务器正常关闭时,会执行destroy方法
Listener 作为一名Android开发人员,对监听器那是再熟悉不过了。在Android开发中,可以说是充斥着各种监听器。 监听器实际上就是接口回调。
作用 在JavaWeb中Listener是Servlet规范定义的一种特殊类,主要用于监听3个作用域的创建、销毁,以及其属性变更
Servlet中的三个作用域分别为
HttpServletRequest
HttpSession
ServletContext
注意pageContext表示jsp整个页面,不属于Servlet中的作用域
应用场景:
统计在线人数 页面访问量 应用启动时做一些初始化工作等等
新建一个监听器 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 package listener;import javax.servlet.*;import javax.servlet.http.*;import javax.servlet.annotation.*;@WebListener public class Listener implements ServletContextListener , HttpSessionListener, HttpSessionAttributeListener { public Listener () { } @Override public void contextInitialized (ServletContextEvent sce) { } @Override public void contextDestroyed (ServletContextEvent sce) { } @Override public void sessionCreated (HttpSessionEvent se) { } @Override public void sessionDestroyed (HttpSessionEvent se) { } @Override public void attributeAdded (HttpSessionBindingEvent sbe) { } @Override public void attributeRemoved (HttpSessionBindingEvent sbe) { } @Override public void attributeReplaced (HttpSessionBindingEvent sbe) { } }
可以看到默认实现了ServletContextListener、HttpSessionListener和**HttpSessionAttributeListener **3个接口,这3个接口是比较常用的,所以IDE自动帮我们实现了这些接口。
实际上3个作用域对应的监听器是不一样的
HttpServletRequest作用域对应的监听器是ServletRequestListener(监听request的创建和销毁)和ServletRequestAttributeListener(监听request中属性的变更) HttpSession作用域对应的监听器是HttpSessionListener(监听session的创建)和HttpSessionAttributeListener(监听session中属性的变更) ServletContext作用域对应的监听器是ServletContextListener(监听servletContext的创建于销毁)和ServletContextAttributeListener(servletContext中属性的变更) 其实这三个作用域对应监听器中的方法是差不多的,下面我们以ServletContext作用域的监听器为例。看看里面的方法什么时候执行。
新建一个ContextListener类,并实现其对应的接口,监听器的配置比较简单,就一个value属性
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 package listener;import javax.servlet.ServletContextAttributeEvent;import javax.servlet.ServletContextAttributeListener;import javax.servlet.ServletContextEvent;import javax.servlet.ServletContextListener;import javax.servlet.annotation.WebListener;@WebListener(value = "servletContextListener") public class ContextListener implements ServletContextListener , ServletContextAttributeListener { @Override public void attributeAdded (ServletContextAttributeEvent scae) { System.out.println("新增的属性:" +scae.getName()+":" +scae.getValue()); } @Override public void attributeRemoved (ServletContextAttributeEvent scae) { System.out.println("移除的属性:" +scae.getName()+":" +scae.getValue()); } @Override public void attributeReplaced (ServletContextAttributeEvent scae) { System.out.println("替换的属性:" +scae.getName()+":" +scae.getValue()); } @Override public void contextInitialized (ServletContextEvent sce) { System.out.println("项目启动了" ); } @Override public void contextDestroyed (ServletContextEvent sce) { System.out.println("项目停止了" ); } }
这样我们就实现了对ServletContext作用域的监听了。其他两个作用域监听器的使用方式也是类似的。