0%

Java Web 基础

1 Java Web 目录结构

image-20200816173234356

目录 描述
/test1_war_exploded Web应用根目录,存储 jsp 或 html 文件
/test1_war_exploded/WEB-INF 存放配置文件,不能直接访问
/test1_war_exploded/WEB-INF/classes 存放编译后的 class 文件
/test1_war_exploded/WEB-INF/lib 存放所需 jar 文件,如 JDBC 驱动的 jar 文件

web.xml:servlet 、servlet mapping 以及其他配置

编译 servlet 命令:

1
javac  -sourcepath src -classpath D:\soft\server\apache-tomcat-9.0.37\lib\servlet-api.jar -d WEB-INF\classes src\mypack\DispatcherServlet.java

2 Servlet

Servlet API 主要由两个 Java 包组成:javax.servletjavax.servlet.http 。在 javax.servlet 中定义了 Servlet 接口以及相关通用接口和类。在 javax.servlet.http 主要定义了与 HTTP 协议相关的 HttpServlet 类、HttpServletRequest 接口和 HttpServletResponse 接口。

javax.servlet.Servlet 接口主要定义了servlet基础生命周期方法:init(初始化)getServletConfig(配置)service(服务)destroy(销毁)

javax.servlet.http.HttpServlet类继承于javax.servlet.GenericServlet,而GenericServlet又实现了javax.servlet.Servletjavax.servlet.ServletConfig。而HttpServlet不仅实现了servlet的生命周期并通过封装service方法抽象出了doGet/doPost/doDelete/doHead/doPut/doOptions/doTrace方法用于处理来自客户端的不一样的请求方式,我们的Servlet只需要重写其中的请求方法或者重写service方法即可实现servlet请求处理。

TestServlet示例代码:

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
package com.anbai.sec.servlet;

import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;

/**
* Creator: yz
* Date: 2019/12/14
*/
// 如果使用注解方式请取消@WebServlet注释并注释掉web.xml中TestServlet相关配置
//@WebServlet(name = "TestServlet", urlPatterns = {"/TestServlet"})
public class TestServlet extends HttpServlet {

@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws IOException {
doPost(request, response);
}

@Override
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws IOException {
PrintWriter out = response.getWriter();
out.println("Hello World~");
out.flush();
out.close();
}

}

2.1 配置 Servlet 的两种方式

(1)web.xml

1
2
3
4
5
6
7
8
9
<servlet>
<servlet-name>dispatcher</servlet-name>
<servlet-class>mypack.DispatcherServlet</servlet-class>
</servlet>

<servlet-mapping>
<servlet-name>dispatcher</servlet-name>
<url-pattern>/dispatcher</url-pattern>
</servlet-mapping>

(2)使用 Annotation 标注

在 Servlet3.0 之后( Tomcat7+)可以使用注解方式配置 Servlet 了,在任意的Java类添加javax.servlet.annotation.WebServlet注解即可。

1
2
3
4
5
……
import javax.servlet.annotation.*;

@WebServlet(name="FontServlet1", urlPatterns={"/font1"}, initParams={@WebInitParam(name="color",value="blue"),@WebInitParam(name="size",value="15")})
public class FontServlet1 extends HttpServlet{……}

image-20200818112122387

2.2 Request & Response

HttpServletRequest 常用方法

方法 说明
getParameter(String name) 获取请求中的参数,该参数是由name指定的
getParameterValues(String name) 返回请求中的参数值,该参数值是由name指定的
getRealPath(String path) 获取Web资源目录
getAttribute(String name) 返回name指定的属性值
getAttributeNames() 返回当前请求的所有属性的名字集合
getCookies() 返回客户端发送的Cookie
getSession() 获取session回话对象
getInputStream() 获取请求主题的输入流
getReader() 获取请求主体的数据流
getMethod() 获取发送请求的方式,如GET、POST
getParameterNames() 获取请求中所有参数的名称
getRemoteAddr() 获取客户端的IP地址
getRemoteHost() 获取客户端名称
getServerPath() 获取请求的文件的路径

HttpServletResponse 常用方法

方法 说明
getWriter() 获取响应打印流对象
getOutputStream() 获取响应流对象
addCookie(Cookie cookie) 将指定的Cookie加入到当前的响应中
addHeader(String name,String value) 将指定的名字和值加入到响应的头信息中
sendError(int sc) 使用指定状态码发送一个错误到客户端
sendRedirect(String location) 发送一个临时的响应到客户端
setDateHeader(String name,long date) 将给出的名字和日期设置响应的头部
setHeader(String name,String value) 将给出的名字和值设置响应的头部
setStatus(int sc) 给当前响应设置状态码
setContentType(String ContentType) 设置响应的MIME类型

3 JSP 基础

3.1 JSP 指令

JSP 指令用来设置和整个网页相关的属性,如编码方式和脚本语言等

一般语法:

1
<%@ 指令名 属性="值"%>
  1. page 指令

指定所用的编程语言,与 JSP 对应的 servlet 接口,所拓展的类以及导入的软件包等

常用属性:https://www.cnblogs.com/sharpest/p/10068832.html

  1. include 指令<%@ include file="filename" %> 包含其他文件(静态包含)

  2. <%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %> 引入标签库的定义

3.2 JSP 声明

用于声明成员变量和方法

语法:<%! declaration;[declaration;]……%>

example:

1
2
3
4
5
6
7
8
9
<%! int v1=0;%>
<%! String v5="hello";
static int v6;
%>
<%!
public String amethod(int i){
return i+1;
}
%>

3.3 Java 程序片段(Scriptlet)

在 JSP 文件中,可以在 <%%> 标记间嵌入任何有效的 Java 程序代码。

3.4 JSP 表达式(EL)

传统 Java 表达式:<%=%> 之间

https://www.jb51.net/article/105314.htm

3.5 JSP 九大隐含对象

变量名 类型 作用
pageContext PageContext 当前页面共享数据,还可以获取其他8个内置对象
request HttpServletRequest 客户端请求对象,包含了所有客户端请求信息
session HttpSession 请求会话
application ServletContext 全局对象,所有用户间共享数据
response HttpServletResponse 响应对象,主要用于服务器端设置响应信息
page Object 当前Servlet对象,this
out JspWriter 输出对象,数据输出到页面上
config ServletConfig Servlet的配置对象
exception Throwable 异常对象

3.6 JSP 标准标签库(JSTL)

JSP标准标签库(JSTL)是一个JSP标签集合,它封装了JSP应用的通用核心功能。

JSTL支持通用的、结构化的任务,比如迭代,条件判断,XML文档操作,国际化标签,SQL标签。 除了这些,它还提供了一个框架来使用集成JSTL的自定义标签。

https://www.runoob.com/jsp/jsp-jstl.html

4 JDBC

JDBC连接数据库的一般步骤:

  1. 注册驱动,Class.forName("数据库驱动的类名")
  2. 获取连接,DriverManager.getConnection(xxx)
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
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
<!--首先导入一些必要的packages-->
<%@ page import="java.io.*"%>
<%@ page import="java.util.*"%>
<!--告诉编译器使用SQL包-->
<%@ page import="java.sql.*"%>
<!--设置中文输出-->
<%@ page contentType="text/html; charset=GB2312" %>

<html>
<head>
<title>dbaccess.jsp</title>
</head>
<body>
<%
try{
Connection con;
Statement stmt;
ResultSet rs;
//加载驱动程序,下面的代码加载MySQL驱动程序
Class.forName("com.mysql.jdbc.Driver");
//注册MySQL驱动程序
DriverManager.registerDriver(new com.mysql.jdbc.Driver());
//用适当的驱动程序连接到数据库
String dbUrl = "jdbc:mysql://localhost:3306/BookDB?useUnicode=true&characterEncoding=GB2312&useSSL=false";
String dbUser="root";
String dbPwd="root";
//建立数据库连接
con = java.sql.DriverManager.getConnection(dbUrl,dbUser,dbPwd);
//创建一个SQL声明
stmt = con.createStatement();
//增加新记录
stmt.executeUpdate("insert into BOOKS (ID,NAME,TITLE,PRICE) values('999','Tom','Tomcat Bible',44.5)");

//查询记录
rs = stmt.executeQuery("select ID,NAME,TITLE,PRICE from BOOKS");
//输出查询结果
out.println("<table border=1 width=400>");
while (rs.next()){
String col1 = rs.getString(1);
String col2 = rs.getString(2);
String col3 = rs.getString(3);
float col4 = rs.getFloat(4);
//打印所显示的数据
out.println("<tr><td>"+col1+"</td><td>"+col2+"</td><td>"+col3+"</td><td>"+col4+"</td></tr>");
}
out.println("</table>");

//删除新增加的记录
stmt.executeUpdate("delete from BOOKS where ID='999'");

//关闭数据库连接
rs.close();
stmt.close();
con.close();

//注销 JDBC Driver
Enumeration<Driver> drivers = DriverManager.getDrivers();
while(drivers.hasMoreElements()) {
DriverManager.deregisterDriver(drivers.nextElement());
}
}catch(Exception e){out.println(e.getMessage());}

%>
</body>
</html>

4.1 数据源(DataSource)

在真实的Java项目中通常不会使用原生的JDBCDriverManager去连接数据库,而是使用数据源(javax.sql.DataSource)来代替DriverManager管理数据库的连接。一般情况下在Web服务启动时候会预先定义好数据源,有了数据源程序就不再需要编写任何数据库连接相关的代码了,直接引用DataSource对象即可获取数据库连接了。

META-INF 目录下创建一个 content.xml 文件,在里面定义数据源

1
2
3
4
5
6
7
<Context  reloadable="true" >
<Resource name="jdbc/BookDB" auth="Container" type="javax.sql.DataSource"
maxActive="100" maxIdle="30" maxWait="10000"
username="root" password="root"
driverClassName="com.mysql.jdbc.Driver"
url="jdbc:mysql://localhost:3306/BookDB?autoReconnect=true&amp;useUnicode=true&amp;characterEncoding=GB2312&amp;useSSL=false"/>
</Context>

web.xml 中加入 <resource-ref> 元素

1
2
3
4
5
6
<resource-ref>
<description>DB Connection</description>
<res-ref-name>jdbc/BookDB</res-ref-name>
<res-type>javax.sql.DataSource</res-type>
<res-auth>Container</res-auth>
</resource-ref>

获取 jdbc/BookDB 数据源引用,并获取连接对象

1
2
3
4
5
Connection con;

Context ctx = new InitialContext();
DataSource ds =(DataSource)ctx.lookup("java:comp/env/jdbc/BookDB");
con = ds.getConnection();

example

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
48
49
50
51
52
53
54
55
56
57
<!--首先导入一些必要的包-->
<%@ page import="java.io.*"%>
<%@ page import="java.util.*"%>
<%@ page import="java.sql.*"%>
<%@ page import="javax.sql.*"%>
<%@ page import="javax.naming.*"%>
<!--设置中文输出-->
<%@ page contentType="text/html; charset=GB2312" %>
<html>
<head>
<TITLE>dbaccess1.jsp</TITLE>
</head>
<body>
<%
try{
Connection con;
Statement stmt;
ResultSet rs;

//建立数据库连接
Context ctx = new InitialContext();
DataSource ds =(DataSource)ctx.lookup("java:comp/env/jdbc/BookDB");
con = ds.getConnection();

//创建一个SQL声明
stmt = con.createStatement();
//增加新记录
stmt.executeUpdate("insert into BOOKS(ID,NAME,TITLE,PRICE) values ('999','Tom','Tomcat Bible',44.5)");

//查询记录
rs = stmt.executeQuery("select ID,NAME,TITLE,PRICE from BOOKS");
//输出查询结果
out.println("<table border=1 width=400>");
while (rs.next()){
String col1 = rs.getString(1);
String col2 = rs.getString(2);
String col3 = rs.getString(3);
float col4 = rs.getFloat(4);

//打印所显示的数据
out.println("<tr><td>"+col1+"</td><td>"+col2+"</td><td>"+col3+"</td><td>"+col4+"</td></tr>");
}

out.println("</table>");

//删除新增加的记录
stmt.executeUpdate("delete from BOOKS where ID='999'");

//关闭结果集、SQL声明、数据库连接
rs.close();
stmt.close();
con.close();
}catch (Exception e) {out.println(e.getMessage());e.printStackTrace();}

%>
</body>
</html>

5 JavaBean

JavaBean 是特殊的 Java 类,使用 Java 语言书写,并且遵守 JavaBean API 规范,是一种可重复使用、且跨平台的软件组件。

  • 提供一个默认的无参构造函数。
  • 需要被序列化并且实现了 Serializable 接口。
  • 可能有一系列可读写属性。
  • 可能有一系列的 getter 或 setter 方法。

JavaBean 示例

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
package mypack;

public class StudentsBean implements java.io.Serializable
{
private String firstName = null;
private String lastName = null;
private int age = 0;

public StudentsBean() {
}
public String getFirstName(){
return firstName;
}
public String getLastName(){
return lastName;
}
public int getAge(){
return age;
}

public void setFirstName(String firstName){
this.firstName = firstName;
}
public void setLastName(String lastName){
this.lastName = lastName;
}
public void setAge(int age) {
this.age = age;
}
}

编译后的 .class 文件存放在 /WEB_INF/classes/mypack/

  1. 导入 JavaBean 类

要想访问,首先需要导入:<%@ page import="mypack.StudentsBean"%>

  1. 声明 JavaBean 对象

使用 <jsp:useBean> 来声明:<jsp:useBean id="myBean" class="mypack.StudentsBean" scope="session"/>

<jsp:useBean> 属性:

  • id: 命名引用该Bean的变量。如果能够找到id和scope相同的Bean实例,jsp:useBean动作将使用已有的Bean实例而不是创建新的实例。

  • class: 指定Bean的完整包名

  • scope: 指定Bean在哪种上下文内可用,可以取下面的四个值之一:page,request,session和application

    1
    2
    3
    4
    1. 默认值是page,表示该Bean只在当前页面内可用(保存在当前页面的PageContext内)。 
    2. request表示该Bean在当前的客户请求内有效(保存在ServletRequest对象内)。
    3. session表示该Bean对当前HttpSession内的所有页面都有效。
    4. application则表示该Bean对所有具有相同ServletContext的页面都有效。
  • type: 指定引用该对象的变量的类型,它必须是Bean类的名字、超类名字、该类所实现的接口名字之一。请记住变量的名字是由id属性指定的。

    beanName: 指定Bean的名字。如果提供了type属性和beanName属性,允许省略class属性。

  1. 访问 JavaBean 属性

1)使用 <jsp:getProperty> 标签

1
<jsp:getProperty name="myBean" property="count" />

2)Java表达式

1
<%=myBean.getCount() %>

3)EL 表达式

1
${myBean.count}

给 JavaBean 属性赋值:

1
2
3
<jsp:setProperty name="myBean" property="count" value="1"/>

<% myBean.setCount(1);%>

6 Filter

javax.servlet.FilterServlet2.3新增的一个特性,主要用于过滤URL请求,通过Filter我们可以实现URL请求资源权限验证、用户登陆检测等功能。

Filter是一个接口,实现一个Filter只需要重写initdoFilterdestroy方法即可,其中过滤逻辑都在doFilter方法中实现。

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
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
package mypack;
import java.io.*;
import javax.servlet.*;
import javax.servlet.http.*;
import javax.servlet.annotation.*;

/*

@WebFilter( //用@WebFilter标注配置NoteFilter
filterName = "NoteFilter",
urlPatterns = "/note",
initParams = {
@WebInitParam(name = "ipblock", value = "221.45"),
@WebInitParam(name = "blacklist", value = "捣蛋鬼")}
)
*/

public class NoteFilter implements Filter {
private FilterConfig config = null;
private String blackList=null;
private String ipblock=null;

public void init(FilterConfig config) throws ServletException {
System.out.println("NoteFilter: init()");
this.config = config;

//读取拒绝IP地址
ipblock=config.getInitParameter("ipblock");

//读取blacklist初始化参数
blackList=config.getInitParameter("blacklist");
}

public void doFilter(ServletRequest request, ServletResponse response,
FilterChain chain) throws IOException, ServletException {

System.out.println("NoteFilter: doFilter()");

if(!checkRemoteIP(request,response))return;

if(!checkUsername(request,response))return;

//记录响应客户请求前的时间
long before = System.currentTimeMillis();
config.getServletContext().log("NoteFilter:before call chain.doFilter()");

//把请求转发给后续的过滤器或者Web组件
chain.doFilter(request, response);

//记录响应客户请求后的时间
config.getServletContext().log("NoteFilter:after call chain.doFilter()");
long after = System.currentTimeMillis();

String name = "";
if (request instanceof HttpServletRequest) {
name = ((HttpServletRequest)request).getRequestURI();
}
//记录响应客户请求所花的时间
config.getServletContext().log("NoteFilter:"+name + ": " + (after - before) + "ms");
}

private boolean checkRemoteIP(ServletRequest request, ServletResponse response)
throws IOException, ServletException {
//读取客户的IP地址
String addr=request.getRemoteAddr();
if(addr.indexOf(ipblock)==0){
response.setContentType("text/html;charset=GB2312");
PrintWriter out = response.getWriter();
out.println("<h1>对不起,服务器无法为你提供服务。</h1>");
out.flush();
return false;
}else{
return true;
}
}

private boolean checkUsername(ServletRequest request, ServletResponse response)
throws IOException, ServletException {

String username =((HttpServletRequest) request).getParameter("username");
if(username!=null)
username=new String(username.getBytes("ISO-8859-1"),"GB2312");

if (username!=null && username.indexOf(blackList) != -1 ) {
//生成拒绝用户留言的网页
response.setContentType("text/html;charset=GB2312");
PrintWriter out = response.getWriter();
out.println("<h1>对不起,"+username + ",你没有权限留言 </h1>");
out.flush();
return false;
}else{
return true;
}

}
public void destroy() {
System.out.println("NoteFilter: destroy()");
config = null;
}

}

Filter的配置类似于Servlet,由<filter><filter-mapping>两组标签组成,如果Servlet版本大于3.0同样可以使用注解的方式配置Filter

web.xml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
<filter> 
<filter-name>NoteFilter</filter-name>
<filter-class>mypack.NoteFilter</filter-class>

<init-param>
<param-name>ipblock</param-name>
<param-value>221.45</param-value>
</init-param>

<init-param>
<param-name>blacklist</param-name>
<param-value>捣蛋鬼</param-value>
</init-param>
</filter>

使用 @WebFilter 标注

1
2
3
4
5
6
7
8
@WebFilter( //用@WebFilter标注配置NoteFilter
filterName = "NoteFilter",
urlPatterns = "/note",
initParams = {
@WebInitParam(name = "ipblock", value = "221.45"),
@WebInitParam(name = "blacklist", value = "捣蛋鬼")}
)
public class NoteFilter implements Filter {

Filter和Servlet的总结:https://javasec.org/javaweb/Filter&Servlet/

7 序列化

8 XML

9 MVC 设计模式

spring MVC 工作流程

  1. 用户向服务端发送一次请求,这个请求会先到前端控制器DispatcherServlet(也叫中央控制器)。
  2. DispatcherServlet接收到请求后会调用HandlerMapping处理器映射器。由此得知,该请求该由哪个Controller来处理(并未调用Controller,只是得知)
  3. DispatcherServlet调用HandlerAdapter处理器适配器,告诉处理器适配器应该要去执行哪个Controller
  4. HandlerAdapter处理器适配器去执行Controller并得到ModelAndView(数据和视图),并层层返回给DispatcherServlet
  5. DispatcherServlet将ModelAndView交给ViewReslover视图解析器解析,然后返回真正的视图。
  6. DispatcherServlet将模型数据填充到视图中
  7. DispatcherServlet将结果响应给用户

lib 文件夹中必须包含 Spring 软件包的依赖

web.xml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
<?xml version="1.0" encoding="UTF-8"?>

<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee
http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
version="4.0" >

<display-name>Spring MVC Sample</display-name>

<servlet>
<servlet-name>HelloWeb</servlet-name>
<servlet-class>
org.springframework.web.servlet.DispatcherServlet
</servlet-class>
<load-on-startup>1</load-on-startup>
</servlet>

<servlet-mapping>
<servlet-name>HelloWeb</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
</web-app>

为 DispatcherServlet 映射的URL为”/“,所有访问应用的用户都会由 DispatcherServlet 来预处理,然后再由它转发给后续组件。为 DispatcherServlet 设置的 Servlet 名字为 “HelloWeb”,即必须为 Spring MVC 提供一个名为 HelloWeb-servlet.xml 的配置文件。

HelloWeb-servlet.xml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<beans xmlns = "http://www.springframework.org/schema/beans"
xmlns:context = "http://www.springframework.org/schema/context"
xmlns:xsi = "http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation = "http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-3.0.xsd">

<context:component-scan base-package = "mypack" />

<bean class = "org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name = "prefix" value = "/WEB-INF/jsp/" />
<property name = "suffix" value = ".jsp" />
</bean>

</beans>

指定解析视图组件的为 InternalResourceViewResolver ,prefix 和 suffix 属性分别设定了视图文件的前缀和后缀。

参考