1 Java Web 目录结构
目录 | 描述 |
---|---|
/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 命令:
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.servlet
和 javax.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.Servlet
和javax.servlet.ServletConfig
。而HttpServlet
不仅实现了servlet
的生命周期并通过封装service
方法抽象出了doGet/doPost/doDelete/doHead/doPut/doOptions/doTrace
方法用于处理来自客户端的不一样的请求方式,我们的Servlet只需要重写其中的请求方法或者重写service
方法即可实现servlet
请求处理。
TestServlet示例代码:
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
<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
注解即可。
……
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{……}
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 指令用来设置和整个网页相关的属性,如编码方式和脚本语言等
一般语法:
<%@ 指令名 属性="值"%>
- page 指令
指定所用的编程语言,与 JSP 对应的 servlet 接口,所拓展的类以及导入的软件包等
常用属性:https://www.cnblogs.com/sharpest/p/10068832.html
-
include 指令
<%@ include file="filename" %>
包含其他文件(静态包含) -
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
引入标签库的定义
3.2 JSP 声明
用于声明成员变量和方法
语法:<%! declaration;[declaration;]……%>
example:
<%! 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连接数据库的一般步骤:
- 注册驱动,
Class.forName("数据库驱动的类名")
。 - 获取连接,
DriverManager.getConnection(xxx)
。
<!--首先导入一些必要的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项目中通常不会使用原生的JDBC
的DriverManager
去连接数据库,而是使用数据源(javax.sql.DataSource
)来代替DriverManager
管理数据库的连接。一般情况下在Web服务启动时候会预先定义好数据源,有了数据源程序就不再需要编写任何数据库连接相关的代码了,直接引用DataSource
对象即可获取数据库连接了。
在 META-INF
目录下创建一个 content.xml
文件,在里面定义数据源
<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&useUnicode=true&characterEncoding=GB2312&useSSL=false"/>
</Context>
web.xml
中加入 <resource-ref>
元素
<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 数据源引用,并获取连接对象
Connection con;
Context ctx = new InitialContext();
DataSource ds =(DataSource)ctx.lookup("java:comp/env/jdbc/BookDB");
con = ds.getConnection();
example
<!--首先导入一些必要的包-->
<%@ 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 示例
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/
中
- 导入 JavaBean 类
要想访问,首先需要导入:<%@ page import="mypack.StudentsBean"%>
- 声明 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. 默认值是page,表示该Bean只在当前页面内可用(保存在当前页面的PageContext内)。 2. request表示该Bean在当前的客户请求内有效(保存在ServletRequest对象内)。 3. session表示该Bean对当前HttpSession内的所有页面都有效。 4. application则表示该Bean对所有具有相同ServletContext的页面都有效。
-
type: 指定引用该对象的变量的类型,它必须是Bean类的名字、超类名字、该类所实现的接口名字之一。请记住变量的名字是由id属性指定的。
beanName: 指定Bean的名字。如果提供了type属性和beanName属性,允许省略class属性。
- 访问 JavaBean 属性
1)使用 <jsp:getProperty>
标签
<jsp:getProperty name="myBean" property="count" />
2)Java表达式
<%=myBean.getCount() %>
3)EL 表达式
${myBean.count}
给 JavaBean 属性赋值:
<jsp:setProperty name="myBean" property="count" value="1"/>
或
<% myBean.setCount(1);%>
6 Filter
javax.servlet.Filter
是Servlet2.3
新增的一个特性,主要用于过滤URL请求,通过Filter我们可以实现URL请求资源权限验证、用户登陆检测等功能。
Filter是一个接口,实现一个Filter只需要重写init
、doFilter
、destroy
方法即可,其中过滤逻辑都在doFilter
方法中实现。
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
<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
标注
@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 工作流程
- 用户向服务端发送一次请求,这个请求会先到前端控制器DispatcherServlet(也叫中央控制器)。
- DispatcherServlet接收到请求后会调用HandlerMapping处理器映射器。由此得知,该请求该由哪个Controller来处理(并未调用Controller,只是得知)
- DispatcherServlet调用HandlerAdapter处理器适配器,告诉处理器适配器应该要去执行哪个Controller
- HandlerAdapter处理器适配器去执行Controller并得到ModelAndView(数据和视图),并层层返回给DispatcherServlet
- DispatcherServlet将ModelAndView交给ViewReslover视图解析器解析,然后返回真正的视图。
- DispatcherServlet将模型数据填充到视图中
- DispatcherServlet将结果响应给用户
lib 文件夹中必须包含 Spring 软件包的依赖
web.xml
<?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
<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 属性分别设定了视图文件的前缀和后缀。
参考
- 《Tomcat 与 Java Web 开发技术详解》—— 孙卫琴
- https://javasec.org/javaweb/