0%

java_web

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

搭建完成,访问服务器

img

这里的内容是由index.jsp控制的,我们可以尝试修改一下index.jsp

img

img

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指令,主要用来提供jsp页面的相关信息,以及编码配置

简单来说,JSP就是在Html中嵌入Java代码。

大概的执行流程如下

  1. 用户向服务器发一个HTTP请求,例如请求index.jsp
  2. 服务器查找到index.jsp
  3. 将jsp文件转换成Servlet源文件,实际上就是java代码
  4. 编译转换后的java代码生成.class文件并执行
  5. 将执行结果返回给用户

声明语法

1
<%!      %>

示例

1
2
3
4
5
6
7
8
<%!
/*声明成员变量*/
String name = "ycxlo";
/*声明成员方法*/
String getName() {
return name;
}
%>

注意:声明语法用来定义成员变量成员方法

程序脚本

语法:

1
<%  %>

代码写在<% %>之间,包括局部变量的声明,表达式,逻辑语句等
可以看做是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.*" %>

<%

/*JSP程序脚本
*可以声明局部变量,表达式,编写逻辑语句等
*
* */

String name = "ycxlo";//注意这里是局部变量
int age = 25;//局部变量
if (age > 25)
%>
你的名字是:
<%
out.print(name);
if (age > 30) {
%>
<h1>中年油腻大叔</h1>

<%
} else {
%>
<h1>青年</h1>
<%
}
%>

输出如下:

img

注意:第一个if是和else连接的

注释语法

1
<%-- --%>

输出语法

1
<%= %>

示例

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()%>

输出:

img

包引入语法

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对象中存储一个值 key-value的形式
request.setCharacterEncoding("utf-8"); //设置编码格式

request.getParameter("");//获取提交的指定参数的值
request.getParameterNames();//返回请求中所有参数的集合
request.getParameterValues("");//获取包含指定参数的所有值的数组
request.getAttributeNames();//获取所有属性名称集合
request.getAttribute("name");//获取指定属性的属性值,如果不存在返回null
request.getCharacterEncoding();//获取编码格式
request.getProtocol();//获取HTTP使用的协议
request.getServletPath();//获取用户提交信息的页面的路径
request.getMethod();//获取用户提交的方式(GET/POST 等)
request.getHeaderNames();//返回所有HTTP头的名称集合
request.getHeader("");//获取header中指定属性的值
request.getRemoteAddr();//获取用户的ip地址
request.getRemoteHost();//获取用户的主机名
request.getServerName();//获取服务器的名称
request.getServerPort();//获取服务器端口号
request.getCookies();//返回客户端所有的Cookie的数组
request.getSession();//返回request对应的session对象,如果没有,则创建一个
request.getInputStream();//返回请求的输入流
request.getContextPath();//返回request URI中指明的上下文路径
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");//设置响应的MIME类型
response.getCharacterEncoding();//获取编码格式
response.addCookie(new Cookie("",""));//添加Cookie
response.setHeader("Content-Disposition","attachment; filename=fileName");//配置header,表示浏览器已下载的方式打开文件
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();//获取sessionid
session.invalidate();//取消session,使session不可用
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>

<%

/*http://localhost:8080/JavaWebDemo/loginServlet*/

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);//所有的post请求交由doGet处理

}

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);
}
}


}

img

进入登录页面输出ycx/123即可跳转到success.jsp

img

Servlet生命周期

Servlet的生命周期分为三个阶段

  1. 初始化阶段,对应init()方法,首次访问Servlet的时候执行
  2. 响应请求阶段,对应service()方法,每次处理请求时调用
  3. 销毁阶段,对应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 {
/*初始化方法 接收一个FilterConfig类型的参数 该参数是对Filter的一些配置*/
public void init(FilterConfig config) throws ServletException {
}
/*销毁时调用*/
public void destroy() {
}
/*过滤方法 主要是对request和response进行一些处理,然后交给下一个过滤器或Servlet处理*/
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws ServletException, IOException {
chain.doFilter(request, response);
}
}

img

默认实现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 {
/*过滤方法 主要是对request和response进行一些处理,然后交给下一个过滤器或Servlet处理*/
System.out.println(filterName + "doFilter()");
req.setCharacterEncoding(charset);
resp.setCharacterEncoding(charset);
chain.doFilter(req, resp);
}

public void init(FilterConfig config) throws ServletException {

/*初始化方法 接收一个FilterConfig类型的参数 该参数是对Filter的一些配置*/

filterName = config.getFilterName();
charset = config.getInitParameter("charset");

System.out.println("过滤器名称:" + filterName);
System.out.println("字符集编码:" + charset);

}

}

这样一个简单的字符集编码处理的过滤器就完成了

我们看看执行打印的结果

img

需要注意的是

过滤器是在服务器启动时就会创建的,只会创建一个实例,常驻内存,也就是说服务器一启动就会执行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) {
/* This method is called when the servlet context is initialized(when the Web application is deployed). */
}

@Override
public void contextDestroyed(ServletContextEvent sce) {
/* This method is called when the servlet Context is undeployed or Application Server shuts down. */
}

@Override
public void sessionCreated(HttpSessionEvent se) {
/* Session is created. */
}

@Override
public void sessionDestroyed(HttpSessionEvent se) {
/* Session is destroyed. */
}

@Override
public void attributeAdded(HttpSessionBindingEvent sbe) {
/* This method is called when an attribute is added to a session. */
}

@Override
public void attributeRemoved(HttpSessionBindingEvent sbe) {
/* This method is called when an attribute is removed from a session. */
}

@Override
public void attributeReplaced(HttpSessionBindingEvent sbe) {
/* This method is called when an attribute is replaced in a session. */
}
}

可以看到默认实现了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) {
/*servletContext创建时调用*/
System.out.println("项目启动了");
}

@Override
public void contextDestroyed(ServletContextEvent sce) {
/*servletContext销毁时调用*/
System.out.println("项目停止了");
}
}

img

这样我们就实现了对ServletContext作用域的监听了。其他两个作用域监听器的使用方式也是类似的。