开发工具
Tomcat
是一个轻量级的JSP/Servlet容器,可以用于中小型项目中的开发和调试,也有用于生产环境的
下载地址https://tomcat.apache.org/download-10.cgi
下载完成后直接解压即可使用
Tomcat的目录
bin:tomcat启动、关闭之类的脚本文件
- 启动服务器 startup.bat
- 关闭服务器 shutdown.bat
- 启动服务器后则可以在浏览器地址栏中输入localhost:8080,如果没有报错,可以看到一个页面则表示运行正常
conf:tomcat各种配置文件。其核心配置文件有:
- server.xml 配置与server相关信息
- web.xml是web应用(一个web应用相当于一个站点)的父配置文件
- tomcat-users.xml 配置tomcat相关信息(管理tomcat服务器的用户名、密码和权限等)
lib:tomcat启动所依赖的jar包
logs:tomcat的系统日志
temp:tomcat运行时产生的临时文件
webapps:web应用所在的目录,即提供外界访问的web资源存放目录。
work:tomcat工作目录,主要用于存放jsp被访问后生成对应的servlet文件以及class文件
Tomcat端口号的配置
conf/server.xml中查找有效的连接子配置
<Connector port="8080端口号" protocol="HTTP/1.1所支持的协议版本" connectionTimeout="20000连接超时配置,单位为ms" redirectPort="8443" />
Maven
使用的是最基本的功能,即依赖管理
-
添加依赖需要知道对应依赖的坐标值GAV
-
通过中央库mvnrepository.com提供的web页面可以查询对应的依赖GAV坐标
<!-- https://mvnrepository.com/artifact/mysql/mysql-connector-java --> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>8.0.23</version> </dependency>
-
将GAV坐标值拷贝到当前项目的pom.xml配置文件中,注意位置
<dependencies> <dependency> <groupId>javax.servlet</groupId> <artifactId>javax.servlet-api</artifactId> <version>4.0.1</version> <scope>provided</scope> </dependency> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>8.0.23</version> </dependency> </dependencies>
添加依赖配置后,默认会自动从中央库中下载对应的jar包。一般建议修改配置使用ali的镜像库进行下载,不使用国外站点。如果下载失败则会报错
在
C:\Users\Administrator\.m2
目录下添加settings.xml,然后重启IDEA即可<mirror> <id>aliyunmaven</id> <mirrorOf>central</mirrorOf> <name>aliyun maven</name> <url>https://maven.aliyun.com/repository/public</url> </mirror>
-
-
需要遵循一般的约定
- 源代码在src/main/java目录下
- 配置文件位于src/main/resources目录下
- 页面文件位于src/main/webapp目录下
- 测试源代码文件位于src/test/java目录下
- 测试配置文件位于src/test/resources目录下
-
三大核心生命周期
Servlet组件
什么是servlet
Servlet是一种用于扩展服务器功能的服务器端组件技术,用于实现动态网页编程
- 是直接或者间接实现Servlet接口的类
- 有三种:Servlet Filter xxxListener
Hello Servlet
Servlet接口不在JavaSE中,需要因为servlet-api依赖
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>4.0.1</version>
<scope>provided</scope> scope=provider表示该jar包在编译阶段使用,并不会打包到应用中
</dependency>
1、定义一个类实现Servlet接口
public class HelloServlet implements Servlet { //所谓的Servlet就是直接或者间接的实现Servlet接口
@Override
public void init(ServletConfig config) throws ServletException {
}
@Override
public ServletConfig getServletConfig() {
return null;
}
@Override
public void service(ServletRequest req, ServletResponse res) throws ServletException, IOException { // Servlet组件用于对外提供服务的方法
//接收用户提交数据,不管是使用get还是post提交
String username=req.getParameter("username");
if(username==null ||username.trim().length()<1)
username="Servlet";
String message="Hello "+username+"!";
//使用rep生成针对客户端请求的响应信息
PrintWriter pw=res.getWriter();
pw.println("<h2>"+message+"</h2>");//生成html文档
pw.flush();
pw.close();
}
@Override
public String getServletInfo() {
return null;
}
@Override
public void destroy() {
}
}
service方法实际上是由tomcat服务器负责调用执行,服务器调用方法时会传入两个参数,一个req用于封装所有的用户请求信息,一个res用于封装服务器向客户端的响应信息
-
req.getParameter用于获取用户提交的数据,例如get请求 hello.do?username=zhangsan
-
res.setContentType用于告知客户端的浏览器,如何处理响应信息
- res.setContentType(“text/html;charset=utf-8”);
- 遵循一个MIME协议(多用途互联网邮件协议),其中text表示是一个文本文档,html表示是一个html格式的文档文档
-
res.getWriter用于获取向客户端输出数据的输出流,在编码中向输出流中写出数据,实际上就是向客户端输出的内容
2、在web应用的核心配置文件web.xml中将Servlet和一个URL地址建立对应关系,当用户请求对应的URL地址时则触发Servlet运行
- Servlet类中没有main方法,所以程序的执行是由服务器按照规则进行调用的
- 由客户端对特定路径发起请求触发执行
<?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">
<!-- 将Servlet类和一个名称建立对应关系 -->
<servlet>
<servlet-name>helloServlet</servlet-name>
<servlet-class>com.yan.action.HelloServlet</servlet-class>
</servlet>
<!-- 将一个地址和名称建立对应关系 -->
<servlet-mapping>
<servlet-name>helloServlet</servlet-name>
<url-pattern>/hello.do</url-pattern>
</servlet-mapping>
</web-app>
3、启动Tomcat服务器
4、在浏览器中输入请求URL地址 http://localhost:8080/demo1_war/hello.do
5、使用get提交数据 http://localhost:8080/demo1_war/hello.do?username=zhangsan
IDEA控制台提示乱码
在Tomcat服务器下conf/logging.properties是日志输出的相关配置
java.util.logging.ConsoleHandler.encoding = GBK
Servlet的特点
Servlet用MIME过滤数据,由javax.servlet和javax.servlet.http两个包组成Java Servlet API框架
编写Servlet必须直接或间接实现作为Servlet的核心javax.servlet.Servlet接口,通常是实现javax.servlet.http.HttpServlet的子类,可以覆盖继承doGet()或者doPost()方法,两者分别对应于Http(s)中的Get请求和Post请求。
优点
- 可以移植性:由于Servlet是用Java语言编写的,因此它可以在不同的操作系统和服务器上移植
- 安全:Servlet具有类型检查特征,并利用Java的垃圾收集和没有指针的设计,使得Servlet避免了内存管理等问题
- 高效:Servlet加载执行后会常驻服务器内存中,当再次受到客户端的请求时,服务器会产生新的线程而不是进程为客户端服务,这样就提高了响应速度。 – Java是最差的内存性价比
缺点
- 不能所见即所得开发
- 生成html文档需要通过一系列的输出完成,非常的繁琐低效
应用场景
主要是处理客户端的请求并将其结果发送到客户端 ,由于生成html文档繁琐低效,所以一般不建议使用Servlet直接生成响应页面。建议考虑使用JSP
Servlet开发
Servlet编程
-
直接实现Servlet接口,需要实现5个方法
public interface Servlet { public void init(ServletConfig config) throws ServletException; //servlet对象创建后执行初始化操作,例如通过config读取配置信息 public ServletConfig getServletConfig(); //获取当前Servlet对象的相关ServletConfig对象。ServletConfig对象用于封装当前Servlet对象的配置信息 public void service(ServletRequest req, ServletResponse res)throws ServletException, IOException; //对外提供服务。Servlet针对多用户会采用单实例多线程的方式对外提供服务 public String getServletInfo(); //获取当前Servlet的说明信息,一般供工具使用 public void destroy(); //在当前Servlet实例被GC回收前所运行的方法,一般用于资源回收 }
-
为了简化开发,所以Sun提供了一个通用抽象父类GenericServlet。针对大部分方法提供了默认的空实现,只需要Servlet提供service方法的实现即可,一般用于非通用协议开发中。采用的是适配器模式
public class Hello3Servlet extends GenericServlet { @Override public void service(ServletRequest req, ServletResponse res) throws ServletException, IOException { } }
-
为了进一步简化开发,提供针对http协议的支持,Sun提供了一个GenericServlet的子类HttpServlet,这个父类专门用于http协议的应用开发中,是模板设计模式
protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { String method = req.getMethod(); //获取客户端的请求方法,例如get/post之类 if (method.equals(METHOD_GET)) { //如果是GET请求则调用doGet方法 ... doGet(req, resp); //转向调用自己的doGet方法 } else if (method.equals(METHOD_HEAD)) { doHead(req, resp); } else if (method.equals(METHOD_POST)) { doPost(req, resp); } else if (method.equals(METHOD_PUT)) { doPut(req, resp); } else if (method.equals(METHOD_DELETE)) { doDelete(req, resp); } else if (method.equals(METHOD_OPTIONS)) { doOptions(req,resp); } else if (method.equals(METHOD_TRACE)) { doTrace(req,resp); } else { //如果是其它请求方法则报错 String errMsg = lStrings.getString("http.method_not_implemented"); Object[] errArgs = new Object[1]; errArgs[0] = method; errMsg = MessageFormat.format(errMsg, errArgs); resp.sendError(HttpServletResponse.SC_NOT_IMPLEMENTED, errMsg); } }
默认的doGet方法实现
protected void doGet(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException {
String protocol = req.getProtocol(); //获取请求所使用的协议,例如http/1.1
String msg = lStrings.getString("http.method_get_not_supported");
if (protocol.endsWith("1.1")) {
resp.sendError(HttpServletResponse.SC_METHOD_NOT_ALLOWED, msg);
} else {
resp.sendError(HttpServletResponse.SC_BAD_REQUEST, msg);
}
}
###Servlet的三生命周期
-
默认servlet不会自动加载,只有当第一次访问时才会被服务器自动加载,一般Servlet会采用单实例多线程的方式对外提供服务
-
加载完成立即执行初始化操作(new操作),构建对象完成后立即执行init方法,同时服务器会将当前Servlet的配置信息封装在一个ServletConfig对象中 。在一个Servlet的生命周期过程中init方法执行且只执行一次
<servlet> <servlet-name>hello2Servlet</servlet-name> <servlet-class>com.yan.action.Hello2Servlet</servlet-class> <!--初始化配置参数胡--> <init-param> <param-name>name</param-name> <param-value>lisi</param-value> </init-param> </servlet>
-
针对用于请求信息以多线程的方式运行service方法,同时服务器会自动封装request和response对象传入方法中
-
请求处理完毕,Servlet对象常驻内存。
-
只有当关闭服务器或者由于内存不足导致当前Servlet对象被调度销毁对象时,才会执行destroy方法,在一个Servlet的生命周期过程中destroy方法执行且只执行一次
###练习:用户登录
流程:
地址栏中输入/login.do打开输入页面,供用户输入对应的用户名和口令,再点击提交按钮则再服务器中执行数据库查询验证,成功则显示欢迎页,否则报错
- 地址栏中输入地址----Get请求
- 点击提交按钮,因为涉及敏感数据—Post提交
开发:
public class LoginServlet extends HttpServlet {
//针对get请求的处理,需要生成一个带有form表单的输入页面
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
resp.setContentType("text/html;charset=UTF-8");
PrintWriter out=resp.getWriter();
out.println("<html><head><title>用户登录</title></head><body>");
out.println("<form><table>");
out.println("<tr><td>用户名称:</td><td><input name=‘username’/></td></tr>");
out.println("<tr><td>用户口令:</td><td><input type='password' name=‘passwird’/></td></tr>");
out.println("<tr><td colspan=2><input type='submit' value='登录系统'/><input type='reset' value='重置数据'/></td></tr>");
out.println("</table></form>");
out.println("</body></html>");
}
}
配置web.xml
<servlet>
<servlet-name>login</servlet-name>
<servlet-class>com.yan.action.LoginServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>login</servlet-name>
<url-pattern>/login.do</url-pattern>
</servlet-mapping>
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-bKHtzz9Y-1617958848777)(images/login页面.png)]
点击提交按钮的处理
-
如果针对
<form>
表单不设置method,则默认采用get请求;- http://localhost:8080/demo1_war/login.do?username=adfasd&password=adfasd
- 如果定义一个
<form>
没有action,则提交到当前页面
-
如果需要使用post提交,则必须设置method=post
package com.yan.action;
import com.yan.util.JdbcUtil;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;
import java.sql.Connection;
import java.sql.ResultSet;
public class LoginServlet extends HttpServlet {
//针对get请求的处理,需要生成一个带有form表单的输入页面
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { //对应get请求的具体处理
resp.setContentType("text/html;charset=UTF-8");
PrintWriter out=resp.getWriter();
out.println("<html><head><title>用户登录</title></head><body>");
out.println("<form method='post'><table>");
out.println("<tr><td>用户名称:</td><td><input name='username'/></td></tr>");
out.println("<tr><td>用户口令:</td><td><input type='password' name='password'/></td></tr>");
out.println("<tr><td colspan=2><input type='submit' value='登录系统'/><input type='reset' value='重置数据'/></td></tr>");
out.println("</table></form>");
out.println("</body></html>");
}
//针对post请求的处理,需要调用数据库查询判断用户提交的数据是否正确
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { //针对post请求的具体处理,目前可以实现post请求的方法只有一种<form method=post>
String username=req.getParameter("username"); //获取用户在<input name="username"/>输入框中输入的数据
String password=req.getParameter("password");
boolean bb=false;
//这里实际上就是登录处理逻辑,直接写在Servlet类中是不合理的,应该参照JavaEE的三层开发的方式来实现
Connection conn=null;
ResultSet rs=null;
JdbcUtil ju=JdbcUtil.getInstance();
try{
conn= ju.getConnection();
String sql="select * from t_users where username=? and password=?";
rs=ju.executeQuery(conn,sql,username,password);
bb=rs.next();
}catch(Exception e){
throw new ServletException(e);
}finally{
try {
ju.close(rs,null,conn);
} catch (Exception e) {
e.printStackTrace();
}
}
resp.setContentType("text/html;charset=utf-8");
PrintWriter out=resp.getWriter();
if(bb){
//成功页
out.println("success!");
}else{
//失败页
out.println("failure!");
}
out.flush();
out.close();
}
}
注意:需要在MySQL中创建一个数据库和对应的数据表,表中包含数据才能登录成功
Servlet技术
Servlet规范是JavaEE规范中的一个部分,是用于扩展服务器功能的运行在服务器中的小型Java程序(即服务器端小应用程序),是实现了Servlet接口通过http接收和响应web客户端请求的类
静态和动态
- 因人因时因地而发生变化
服务器
- web服务器:定位资源—Apache
- jsp/servlet容器:运行jsp或者servlet程序—Tomcat
Servlet概述
Servlet是一种用于扩展服务器功能的服务器端组件技术
- 要求直接或者间接的实现Servlet接口
- 运行在服务器端,执行结果是一个html文档
Servlet的特征
- 一般采用单实例多线程的方式对外提供服务,一个客户请求对应一个线程
- 如果在Servlet类中定义属性,则需要考虑线程安全问题
Servlet的优点
1.高效。在服务器上仅有一个Java虚拟机在运行,它的优势在于当多个来自客户端的请求进行访问时,Servlet为每个请求分配一个线程而不是进程。
- 面试题:Servlet和CGI的区别
- 早期的CGI采用的是一个用户对应一个线程的方式对外提供服务,所以比较浪费资源。而Servlet采用的就是多线程的方式对外提供服务,所以比较节约资源,而且方便相互通讯
2.方便。Servlet提供了大量的实用工具例程,例如处理很难完成的HTML表单数据、读取和设置HTTP头、处理Cookie和跟踪会话等。
3.跨平台。Servlet是用Java类编写的,它可以在不同的操作系统平台和不同的应用服务器平台下运行。
4.灵活性和可扩展性。采用Servlet开发的Web应用程序,由于Java类的继承性、构造函数等特点,使得其应用灵活,可随意扩展。
6.共享数据。Servlet之间通过共享数据可以很容易地实现数据库连接池。它能方便地实现管理用户请求,简化Session和获取前一页面信息的操作。而在CGI之间通信则很差。由于每个CGI程序的调用都开始一个新的进程,调用间通信通常要通过文件进行,因而相当缓慢。同一台服务器上的不同CGI程序之间的通信也相当麻烦。
7.安全。有些CGI版本有明显的安全弱点。即使是使用最新的标准和PERL等语言,系统也没有基本安全框架。而Java定义有完整的安全机制,包括SSL\CA认证、安全政策等规范。
Servlet缺陷
1、不能所见即所得的实现页面的开发和开发测试不方便。解决方案是JSP
##Servlet开发流程
1、定义Servlet类
-
直接实现Servlet接口
public class MyServlet implements Servlet{ //需要定义5个Servlet接口中声明的方法 }
-
为了简化开发,针对非标准协议,提供了一个抽象父类GenericServlet
- 采用的是适配器模式
- 一般用于游戏的服务器端开发
public class MyServlet extends GenericServlet{ public void service(ServletRequest request,ServletResponse response)throws ServletException,IOException { } }
-
为了进一步简化开发,针对http协议,提供了一个父类HttpServlet
- 采用的是模板模式,主要提供的service方法的实现,所以一般不允许覆盖定义service
- 内部实现实际上就是通过request.getMethod():String获取请求方法,然后根据对应的请求方法转发给对应的doXxxx方法
- 例如/add.do?id=124&name=lisi
- get请求使用?作为地址和参数的分割,例如add.do是请求地址,?之后的内容就是请求串
- 多个请求参数之间采用&符号作为分割
- 参数的格式为[名称=值]
- 例如/add.do?id=124&name=lisi
public class MyServlet extends HttpServlet{ public void doXxx(HttpServletRequest request,HttpServletResponse response)throws ServletException,IOException{ ... } }
2、使用web应用的核心配置文件/WEB-INF/web.xml映射当前Servlet
-
Servlet类中没有main方法,也就是说具体调用由服务器负责。具体执行时机默认为第一次用户请求时
<servlet> <servlet-name>名称</servlet-name> <servlet-class>对应的类的全名</servlet-class> </servlet> <servlet-mapping> <servlet-name>对应上面定义的名称</servlet-name> <url-pattern>/请求地址.do</url-pattern> </servlet-mapping>
3、当用户在浏览器中输出xml中配置的请求路径,则由服务器负责按照一定的标准调用Servlet类中的定义方法
###Servlet常见错误
404错误:资源未找到
- 在请求地址中的servlet的别名书写错误
- 虚拟项目名称拼写错误
500错误:内部服务器错误
- ClassNotFoundException,检查web.xml中Servlet类的全限定类名称拼写是否正确
- 因为service方法体的代码执行错误导致的,可以根据提示信息对方法体中的代码进行更改
405错误:请求方式不支持
- 请求方式和servlet中的方法不匹配所导致的
Caused by: java.net.BindException: Address already in use: JVM_Bind
- 多次重复启动服务器,导致服务器端口号冲突
####Servlet与JSP区别
简单的说,SUN首先发展出Servlet,其功能比较强劲,体系设计也很先进,只是,它输出HTML语句还是采用了老的CGI方式,是一句一句输出,所以,编写和修改HTML非常不方便。 后来SUN推出了类似于ASP的镶嵌型的JSP,把JSP标签镶嵌到HTML语句中,这样,就大大简化和方便了网页的设计和修改。很多网络语言如ASP,PHP,JSP都是镶嵌型的SCRIPT语言
1、JSP在本质上就是SERVLET,但是两者的创建方式不一样
2、Servlet完全是JAVA程序代码构成,擅长于流程控制和事务处理,通过Servlet来生成动态网页很不直观
3、JSP由HTML代码和JSP标签构成,可以方便地编写动态网页
因此在实际应用中采用Servlet来控制业务流程,而采用JSP来生成动态网页.在struts框架中,JSP位于MVC设计模式的视图层,而Servlet位于控制层.
1、JSP是Servlet技术的扩展,本质上就是Servlet的简易方式
2、JSP编译后是“类servlet”
3、Servlet和JSP最主要的不同点在于,Servlet的应用逻辑是在Java文件中,并且完全从表示层中的HTML里分离开来。而JSP是Java和HTML组合成一个扩展名为.jsp的文件
4、JSP侧重于视图,Servlet主要用于控制逻辑
Servlet实例对象的生命周期
Servlet遵循严格的生命周期,在每个Servlet实例的生命中有三种类型的事件,这三种事件分别对应于由Servlet引擎所唤醒的三个方法:
- init()。当Servlet第一次被装载时,Servlet引擎调用这个Servlet的init()方法,只调用一次。如果某个Sevlet需要特殊的初始化需要。那么Servlet编写人员可以重写该方法来执行初始化任务。这是一个可选的方法。如果某个Servlet不需要初始化,那么默认情况下将调用它父类的init方法。系统保证,在init方法成功完成以前,是不会调用Servlet去处理任何请求的
- service()。这是Servlet最重要的方法,是真正处理请求的地方。对于每个请求,Servlet引擎将调用Servlet的service方法,并把Servlet请求对象和Servlet响应对象最为参数传递给它
- 常驻内存
- destroy()。这是相对于init的可选方法,当Servlet即将被卸载时由Servlet引擎来调用,这个方法用来清除并释放在init方法中所分配的资源。
###Servlet技术的要点:
-
当前应用中的所有Servlet接口实现类的实例对象,只能由服务器负责创建,开发人不能不能手动创建
-
默认情况下以单实例多线程的方式对外提供服务
-
Servlet实例一旦创建则常驻内存,只有当服务器资源不足而导致当前Servlet对象被调度或者服务器关闭时才会被销毁
-
默认情况下,服务器接收到对于当前Servlet接口实现类的第一次请求时自动创建这个Servlet接口实现类的实例对象
- 可以手动配置要求服务器在启动时自动创建某个Servlet接口实现类对象
##核心对象
API和SPI 分散关注
ServletConfig对象
对应的是Servlet的配置参数信息,由服务器提供对应的具体实现。具体实现是在服务器调用init方法时传入到Servlet实例中
public void init(ServletConfig config) throws ServletException;
config对象由服务器提供实现,并在服务器调用该方法时传入Servlet实例
public interface ServletConfig {
public ServletContext getServletContext(); //用于获取Servlet上下文对象
public String getInitParameter(String name); //用于获取当前Servlet对象的配置参数
public Enumeration<String> getInitParameterNames();//用于获取当前Servlet对象的所有配置参数的迭代器
}
Servlet的配置 web.xml
<servlet>
<servlet-name>showServlet</servlet-name>
<servlet-class>com.yan.action.ShowServlet</servlet-class>
<!-- 初始化参数 -->
<init-param>
<param-name>name</param-name>
<param-value>yanjun</param-value>
</init-param>
<init-param>
<param-name>age</param-name>
<param-value>123</param-value>
</init-param>
</servlet>
在servlet中获取初始化参数
init(ServletConfig)是Servlet接口中定义的方法,需要自行编程处理config对象,因为Servlet接口中还有一个方法时和config对象相关的public ServletConfig getServletConfig();
public class MyServlet implements Servlet{
private ServletConfig config;
public void init(ServletConfig config){
this.config=config;
}
public ServletConfig getServletConfig(){
return config;
}
......
}
实际上HttpServlet已经提供了以上的实现
public abstract class HttpServlet extends GenericServlet {
private transient ServletConfig config; //父类提供
public void init(ServletConfig config) throws ServletException {
this.config = config;
this.init(); //提供一个扩展点
}
public void init() throws ServletException {
}
自定义Servlet接收初始化配置参数
@Override
public void init() throws ServletException {
//对应web.xml中init-param标签中定义的参数
String ss = this.getServletConfig().getInitParameter("name"); //根据预先知道的名称获取对应的配置参数值
/*
<init-param> 配置初始化参数,在一个servlet标签中可以定义多个
<param-name>name</param-name> 参数名称
<param-value>yanjun</param-value> 参数值
</init-param>
*/
System.out.println(ss);
//迭代访问所有的配置参数
Enumeration<String> en = this.getServletConfig().getInitParameterNames();
while (en.hasMoreElements()) {
String key = en.nextElement();
String val = this.getServletConfig().getInitParameter(key);
System.out.println(key + ":" + val);
}
}
配置自动加载
默认情况下,当第一次客户端访问对应地址时,Servlet才会被加载并执行初始化操作。也可以考虑到执行效率要求服务器启动时自动加载并初始胡特定的Servlet
<servlet>
<servlet-name>showServlet</servlet-name>
<servlet-class>com.yan.action.ShowServlet</servlet-class>
<load-on-startup>1</load-on-startup> 应该是大于等于0的整数,值越小越优先加载
</servlet>
###ServletRequest对象
在servlet接口中所定义的service方法中出现
public void service(ServletRequest req, ServletResponse res) throws ServletException, IOException;
- 用于封装客户端的请求信息
- 是由服务器提供对应的实现类
当服务器调用Servlet实例的service方法时传入对应的接口实现的实例,其中包含服务器所接受到的所有客户端请求信息。一般在具体开发中针对http协议使用的是ServletRequest接口的子接口HttpServletRequest
public interface HttpServletRequest extends ServletRequest
主要方法
1、请求参数
get:使用地址的方式传递数据,实际上使用的是协议头,上限2k
- http://localhost:8080/test/del.do?id=123&name=lisi其中id=123&name=lisi就是请求参数
post:使用协议体传递数据,默认格式还是id=123&name=lisi,只是不在地址栏中出现,一般用于敏感数据传递或者上传大于2k的数据
String username = request.getParameter("username");
按照名称可以获取对应的传递数据,获取的数据都是String或者String[]类型
使用一个名称传递多个数据
/abc.do?hobby=111&hobby=333&hobby=444
<input type="checkbox" name="hobby" value="111">足球
<input type="checkbox" name="hobby" value="222">篮球
<input type="checkbox" name="hobby" value="333">弹球
<input type="checkbox" name="hobby" value="444">其它
服务器获取数据的方法:
String[] hobby=request.getParameterValues("hobby");
- 如果使用getParameter则只能获取第一个数据,后续数据丢失
注意:实际上使用一个名称传递一个值,也可以使用getParameterValues,只是获取的是一个长度为1的字符串数组
特殊方法:一般很少用,除非开发表现层框架
Map<String, String[]> map= request.getParameterMap(); //获取所有的请求参数,其中请求参数名称为key,对应的值为String[]
Map<String,String[]> map=request.getParameterMap();
for(Map.Entry<String,String[]> en:map.entrySet()){
System.out.println(en.getKey());
for(String temp:en.getValue()){
System.out.println("\t\t"+temp);
}
}
Enumeration<String> names=request.getParameterNames(); //获取所有请求参数名称的迭代器
while(names.hasMoreElements()){
String key=names.nextElement(); //通过迭代器对象获取每个请求参数名称
String value=request.getParameter(key); //根据名称获取对应的值
System.out.println(key+"--->"+value);
}
2、以流的方式处理请求信息
InputStream is=request.getInputStream();
BufferedReader br=request.getReader();
上传文件
1、要求使用post提交数据,必须设置enctype=“multipart/form-data”
<form action="test.do" method="post" enctype="multipart/form-data"> 注意表单中的post必须设置,因为默认为get;必须设置enctype,因为默认值application/x-www-form-urlencoded
<input type="file" name="photo"/>
<input type="submit" value="上传文件"/>
</form>
2、jspSmartUp组件是一种早期流行使用的上传下载工具,适合于上传小型的文件,具有灵活性简单高效。
@Override
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
response.setContentType("text/html;charset=utf-8");
PrintWriter pw=response.getWriter();
SmartUpload su=new SmartUpload();//创建上传组件对象
su.initialize(this.getServletConfig(),request,response);//初始化上传组件
su.setMaxFileSize(2*1024*1024); //设置允许上传的文件最大大小,单位字节,一般设置为2M
su.setAllowedFilesList("jpg,png,gif");//设置允许上传的文件后缀类型
try {
su.upload();//上传文件,将文件从客户端上传到服务器,如果上传文件不符合要求,则包异常
//SmartUpload组件支持多文件上传
Files files=su.getFiles();//获取所有的上传文件
if(files!=null && files.getCount()>0){//判断上传文件个数是否大于0
File file=files.getFile(0);//获取第一个上传文件
System.out.println(file.getFileName());
String ss=request.getRealPath(""); //获取当前目录的绝对路径,一般推荐通过application对象获取,不建议使用request对象获取
System.out.println(ss);
file.saveAs("upload/"+file.getFileName()); //使用的是相对路径,相对于ss的路径
}
pw.println("上传上传,点<a href='abc.html'>这里</a>继续上传")
} catch (Exception e){
e.printStackTrace(pw);
pw.println("<a href='abc.html'>重新上传</a>");
}
pw.flush();
pw.close();
}
下载处理
1、列表显示所有的可下载内容
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
response.setContentType("text/html;charset=utf-8");
PrintWriter out=response.getWriter();
String ss=request.getRealPath("upload/"); //获取upload文件夹的绝对路径
File ff=new File(ss);
if(ff.exists()){
File[] fs=ff.listFiles();
if(fs!=null && fs.length>0){
for(File tmp:fs)
out.println("<a href='down.do?fname="+tmp.getName()+"'><img src='upload/"+tmp.getName()+"' width='80' height='60'></a>");
}
}
out.flush();
out.close();
}
2、下载处理要点:
告知浏览器以附件的方式打开响应数据
响应头的名称Content-Disposition,对应的值为attachment;filename=FileName.txt
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
String fname=request.getParameter("fname");
String ss=request.getRealPath("upload/");
File ff=new File(ss,fname);
InputStream is=new BufferedInputStream(new FileInputStream(ff));
response.setHeader("Content-Disposition","attachment;filename=abc.png");
OutputStream outputStream=response.getOutputStream();
byte[] buffer=new byte[8192];
int len=0;
while((len=is.read(buffer))>0)
outputStream.write(buffer,0,len);
outputStream.close();
is.close();
}
3、请求头参数
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-vMAU1mFN-1617958861914)(images/request请求.png)]
编程实现获取请求头数据
String val=request.getHeader("Accept"); //根据名称获取指定的请求头数据,数据类型一般有3种,默认String,还有int和date
//获取所有的请求头名称的迭代器
Enumeration<String> names= request.getHeaderNames();
while(names.hasMoreElements()){
String name=names.nextElement();
String value=request.getHeader(name);
System.out.println(name+"-->"+value);
}
实际显示内容
host-->localhost:8080
connection-->keep-alive
upgrade-insecure-requests-->1
user-agent-->Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/89.0.4389.72 Safari/537.36 Edg/89.0.774.45
accept-->text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9
sec-fetch-site-->none
sec-fetch-mode-->navigate
sec-fetch-user-->?1
sec-fetch-dest-->document
accept-encoding-->gzip, deflate, br
accept-language-->zh-CN,zh;q=0.9,en;q=0.8,en-GB;q=0.7,en-US;q=0.6
cookie-->JSESSIONID=C25DED076B79C8246177CF7E2C937089
ServletResponse对象
用于封装服务器的响应信息
1、设置响应头
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-P0Lg0k1R-1617958861918)(images/response响应.png)]
是由服务器传递给客户端浏览器的额外数据
response.setHeader("Content-Disposition","attachment;filename=abc.png"); //告知浏览器,以附件的方式打开响应数据
Expires: 日期数据 用于设置响应内容类型的缓存过期时间
Date now=new Date();
//例如允许缓存1个月
now=new Date(now.getTime()+1*30*24*60*60*1000);
response.setDateHeader("Expires",now.getTime());
//是否开启缓存
响应头名称Cache-Control,值no-cache表示不缓存数据
写法2:Pragma的值为no-cache时表示禁用缓存,这是一个逐步需要放弃的写法
//禁用缓存
response.setHeader("Pragma","no-cache"); //针对http1.0禁用缓存
response.setHeader("Cache-Control","no-cache"); //针对http1.1禁用缓存
response.setDateHeader("Expires",0); //表示立即过期