1.Tomcat
1.1. 前期知识回顾:
软件架构
C/S:客户端/服务器端
B/S:浏览器/服务器端
资源分类
1. 静态资源:所有用户访问后,得到的结果都是一样的,称为静态资源.静态资源可以直接被浏览器解析 * 如: html,css,JavaScript
2. 动态资源:每个用户访问相同资源后,得到的结果可能不一样。称为动态资源。动态资源被访问后,需要先转换为静态资源,在返回给浏览器 * 如:servlet/jsp,php,asp....
3. 网络通信三要素 (本地连接网络服务器)
IP:电子设备(计算机)在网络中的唯一标识。
端口:应用程序在计算机中的唯一标识。 0~65536 3
传输协议:规定了数据传输的规则 1. 基础协议: 1. tcp:安全协议,三次握手。 速度稍慢 2. udp:不安全协议。 速度快
4. 响应/回显:动态资源转为静态资源返回给前台页面,称之为响应或回显。
1.2. web服务器软件:
* 服务器:安装了服务器软件的计算机
* 服务器软件:接收用户的请求,处理请求,做出响应
* web服务器软件:也是服务器软件,接收用户的请求,处理请求,做出响应。 在web服务器软件中,可以部署web项目,让用户通过浏览器来访问这些项目,例如百度网址。也就是说动态资源依赖web服务器软件,通过web服务器软件来实现响应转为静态资源,我们也称之未web容器(web服务器软件)
1.2.1 常见的java相关的web服务器软件:
javaEE:JAVA语言在企业级开发中使用的技术规范的总和,一起规定的13大类规范。
* webLogic:oracle公司,大型的JavaEE服务器,支持所有的JavaEE规范,收费的。
* webSphere:IBM公司,大型的JavaEE服务器,支持所有的JavaEE规范,收费的。
* JBOSS:JBOSS公司的,大型的JavaEE服务器,支持所有的JavaEE规范,收费的。
* Tomcat:Apache基金组织,中小型的JavaEE服务器,仅仅支持少量的JavaEE规范servlet/jsp。开源的,免费的。
* Tomcat:web服务器软件
1. 下载:http://tomcat.apache.org/
2. 安装:解压压缩包即可。
* 注意:安装目录建议不要有中文和空格
3. 卸载:删除目录就行了
4. 启动:
* bin/startup.bat ,双击运行该文件即可,linux是sh后缀。
* 访问:浏览器输入:http://localhost:8080 回车访问自己
http://别人的ip:8080 访问别人
* 可能遇到的问题:
1. 黑窗口一闪而过:
* 原因: 没有正确配置JAVA_HOME环境变量
* 解决方案:正确配置JAVA_HOME环境变量,原因是tomcat是基于java编写的,需要java_home
2. 启动报错:
1. 暴力:找到占用的端口号,并且找到对应的进程,杀死该进程
* netstat -ano
2. 温柔:修改自身的端口号
* conf/server.xml
* <Connector port="8888" protocol="HTTP/1.1"
connectionTimeout="20000"
redirectPort="8445" />
* 一般会将tomcat的默认端口号修改为80。80端口号是http协议的默认端口号。
* 好处:在访问时,就不用输入端口号
5. 关闭:
1. 正常关闭:
* bin/shutdown.bat
* ctrl+c
2. 强制关闭:
* 点击启动窗口的×
6. 配置:
项目内容:
1.3 * 部署项目的方式:
1. 直接将项目放到webapps目录下即可。 用浏览器localhost:80/hello/hello.html
* /hello:项目的访问路径-->虚拟目录(本方式)
localhost:80/hello/hello.html,前面hello是文件夹包路径,后面是资源名称。
* 简化部署:将项目打成一个war包,将war包放置到webapps目录下。
war包会自动解压缩,生成hello文件夹。自然也就可以用上面路径访问。
2. 配置conf/server.xml文件
在<Host>标签体中配置<Context docBase="D:hello" path="/hehe" />
* docBase:项目存放的路径
* path:虚拟目录
3. 在confCatalinalocalhost创建任意名称的xml文件。在文件中编写
<Context docBase="D:hello" /> * 虚拟目录:xml文件的名称
* 静态项目和动态项目在目录结构区别:
* 目录结构
* java动态项目的目录结构:
-- 项目的根目录
-- WEB-INF目录:
-- web.xml:web项目的核心配置文件
-- classes目录:放置字节码文件的目录
-- lib目录:放置依赖的jar包
* 将Tomcat集成到IDEA中,并且创建JavaEE的项目,部署项目。
tomcat热部署设置 on update action/on frame deactivation都设置为udpate resources,因为java代码修改较多,因此update classes就不选。以免影响性能。
2.Servlet
2.1. Servlet: server applet
* 概念:运行在服务器端的小程序
* Servlet就是一个接口,定义了Java类被浏览器访问到(tomcat识别)的规则。
* 将来我们自定义一个类,实现Servlet接口,复写方法。
2.2. 快速入门:
1. 创建JavaEE项目/web项目,以前java项目不能用的
2. 定义一个类,实现Servlet接口
* public class ServletDemo1 implements Servlet
3. 实现接口中的抽象方法:找到service,输出一句话
// 提供服务的方法 @Override public void service(ServletRequest servletRequest, ServletResponse servletResponse) throws ServletException, IOException { System.out.println("hello servlet"); }
4. 配置Servlet
在web.xml中配置:
为什么要进行web.XML配置,是因为我们要浏览器通过路径去找资源的时候,动态资源需要给予路径引导,因此xml中需要给予
<!--配置Servlet ,给全限定类名一个别名--> <servlet> <servlet-name>demo1</servlet-name> <servlet-class>cn.itcast.web.servlet.ServletDemoOne</servlet-class> </servlet> <!--上面类别名的访问路径的映射--> <servlet-mapping> <servlet-name>demo1</servlet-name> <url-pattern>/demo1</url-pattern> </servlet-mapping>
2.3.执行原理:
补充知识点,当我们后面项目文件越来越多,需要给予一个总根目录,一般以项目名称为主,这时候就需要去tomcat里面进行设置:否则会导致出现:
day就是虚拟目录
localhost/dem1或者localhost/dem2 不如localhost/day/dem2 看起来好些
1. 当服务器接受到客户端浏览器的请求后,会解析请求URL路径,获取访问的Servlet的资源路径,url的端口号决定是什么服务器软件,此处是tomcat,通过mapping路径映射的demo1,找到全限定类名,此处是反射,通过tomcat将类进行字节码文件,进而进行创建对象newInstance();
2. 查找web.xml文件,是否有对应的<url-pattern>标签体内容。
3. 如果有,则在找到对应的<servlet-class>全类名
4. tomcat会将字节码文件加载进内存,并且创建其对象
5. 调用其方法,为什么会调用,因为我们重写了servlet方法里有service
所以,之前我们说servlet运行,依赖于容器,此处的tomcat就是容器。
2.4. Servlet中的生命周期方法:
public class ServletDemoOne implements Servlet{ /** * 初始化方法 * 在servlet被创建时,执行,执行一次 * @param servletConfig * @throws ServletException */ @Override public void init(ServletConfig servletConfig) throws ServletException { } @Override public ServletConfig getServletConfig() { return null; } // 提供服务的方法 /** * 每一次servlet被访问时,执行,执行多次 * @param servletRequest * @param servletResponse * @throws ServletException * @throws IOException */ @Override public void service(ServletRequest servletRequest, ServletResponse servletResponse) throws ServletException, IOException { System.out.println("hello servlet"); } @Override public String getServletInfo() { return null; } /** * 在服务器正常关闭时,执行,执行一次。 */ @Override public void destroy() { } }
1. 被创建:执行init方法,只执行一次
* Servlet什么时候被创建?
* 默认情况下,第一次被访问时,Servlet被创建
* 可以配置执行Servlet的创建时机。
* 在<servlet>标签下配置
1. 第一次被访问时,创建
* <load-on-startup>的值为负数
2. 在服务器启动时,创建
* <load-on-startup>的值为0或正整数
<servlet> <servlet-name>demo1</servlet-name> <servlet-class>cn.itcast.web.servlet.ServletDemoOne</servlet-class> <!--第一次被访问时,创建,为-1负数--> <load-on-startup>-1</load-on-startup> <!--在服务器启动时,创建,为0或整数--> <load-on-startup>0</load-on-startup> </servlet>
* Servlet的init方法,只执行一次,说明一个Servlet在内存中只存在一个对象,Servlet是单例的
* 多个用户同时访问时,可能存在线程安全问题。
* 解决:尽量不要在Servlet中定义成员变量。即使定义了成员变量,也不要对修改值
2. 提供服务:执行service方法,执行多次
* 每次访问Servlet时,Service方法都会被调用一次。
3. 被销毁:执行destroy方法,只执行一次
* Servlet被销毁时执行。服务器关闭时,Servlet被销毁
* 只有服务器正常关闭时,才会执行destroy方法。
* destroy方法在Servlet被销毁之前执行,一般用于释放资源
/** * 获取servlet配置文件 * @return */ @Override public ServletConfig getServletConfig() { return null; } /** * 获取servlet的一些信息,例如版本,作者 * @return */ @Override public String getServletInfo() { return null; }
@Target({ElementType.TYPE}) @Retention(RetentionPolicy.RUNTIME) @Documented public @interface WebServlet { String name() default "";//相当于<Servlet-name> String[] value() default {};//代表urlPatterns()属性配置 String[] urlPatterns() default {};//相当于<url-pattern> int loadOnStartup() default -1;//相当于<load-on-startup> WebInitParam[] initParams() default {}; boolean asyncSupported() default false; String smallIcon() default ""; String largeIcon() default ""; String description() default ""; String displayName() default ""; }
2.5.Servlet3.0:
此前servlet开发,每一个class文件,都需要用web.xml进行servlet代码编写,这样非常的麻烦和繁琐,因此在servlet3.0用有了新的做法。
* 好处:
* 支持注解配置。可以不需要web.xml了。
* 步骤:
1. 创建JavaEE项目,选择Servlet的版本3.0以上,可以不创建web.xml
2. 定义一个类,实现Servlet接口
3. 复写方法
4. 在类上使用@WebServlet注解,进行配置
* @WebServlet("资源路径"),切记他不是虚拟路径,虚拟路径是day14_tomcat。后面/是虚拟路径
@WebServlet(urlPatterns = "/demoTwo") 常规写法 @WebServlet(value = "/demoTwo") value最重要的就是替代路径 @WebServlet("/demoTwo") 就1个路径,可以不写
public @interface WebServlet { String name() default ""; //这个name就是web.xml中配置全限定类名 String[] value() default {};//最重要的属性,如果就一个value,可以不写 String[] urlPatterns() default {};//最直接的路径:可多个存储 int loadOnStartup() default -1; WebInitParam[] initParams() default {}; boolean asyncSupported() default false; String smallIcon() default ""; String largeIcon() default ""; String description() default ""; String displayName() default ""; }
2.6. IDEA与tomcat的相关配置
1. IDEA会为每一个tomcat部署的项目单独建立一份配置文件
* 查看控制台的log:Using CATALINA_BASE: "C:UsersAdministratorAppDataLocalJetBrainsIntelliJIdea2020.2 omcatTomcat_8_5_31_tomcat"
2. 工作空间项目 和 tomcat部署的web项目(out目录下)
* tomcat真正访问的是“tomcat部署的web项目”,"tomcat部署的web项目"对应着"工作空间项目" 的web目录下的所有资源,src下面会对应到class。
* WEB-INF目录下的资源不能被浏览器直接访问。文件一般不放进去,未来用其他技术使用。
2.7. Servlet的体系结构
Servlet -- 接口
|
GenericServlet -- 实现和继承了servlet,它是抽象类
|
HttpServlet -- 抽象类
* GenericServlet:将Servlet接口中其他的方法做了默认空实现,只将service()方法作为抽象。
public class ServletDemoThree extends GenericServlet { @Override public void service(ServletRequest servletRequest, ServletResponse servletResponse) throws ServletException, IOException { } }
* 将来定义Servlet类时,可以继承GenericServlet,实现service()方法即可,我们知道前端表单提交数据方式分post和get,但其service获取数据需要进行post,get方式区分,因此service内部要写区分代码,也是极其繁琐,因此HttpServlet进行了封装http协议,方便我们开发。
* HttpServlet:对http协议的一种封装,简化操作
1. 定义类继承HttpServlet
2. 复写doGet/doPost方法,因为都会调用service进行请求方式分发
通过浏览器直接访问是get请求,而通过表单可以设置其请求方式,未来可以用其他技术调整。
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> <form action="/day13_tomcat/demo4" method="post"> <input type="text" > <input type="submit" value="提交"> </form> </body> </html>
2.8. Servlet相关配置
1. urlpartten:Servlet访问路径
1. 一个Servlet可以定义多个访问路径(数组) : @WebServlet({"/d4","/dd4","/ddd4"})
@WebServlet({"/demo5","/demo55","/demo555"}) public class ServletDemoFive extends HttpServlet { @Override protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { System.out.println("demo5执行"); } }
2. 路径定义规则:
1. /xxx:路径匹配
2. /xxx/xxx:多层路径,目录结构
3. *.do:扩展名匹配。没有/
4./user/*:user下面所以都可以访问,因为*是通配符,优先级非常低
3.HTTP
* 概念:Hyper Text Transfer Protocol 超文本传输协议
* 传输协议:定义了,客户端和服务器端通信时,发送数据的格式,规定了请求消息和响应消息的组成方式。
* 特点:
1. 基于TCP/IP的高级协议
2. 默认端口号:80,也就是域名对应端口号若是80可以不写,因为http默认是80.
3. 基于请求/响应模型的:一次请求对应一次响应
4. 无状态的:每次请求之间相互独立,不能交互数据
* 历史版本:
* 1.0:每一次请求响应都会建立新的连接
* 1.1:复用连接,比方说某个资源请求了,但后面可能还要,就等一会再释放,1.1对缓存的优化比较好。
* 请求消息数据格式(对应service的HttpServletRequest req)
1. 请求行
请求方式 请求url 请求协议/版本
GET /login.html HTTP/1.1
* 请求方式:
* HTTP协议有7中请求方式,常用的有2种
* GET:
1. 请求参数在请求行中,也可以理解为在url后。
2. 请求的url长度有限制的
3. 不太安全
* POST:
1. 请求参数在请求体中
2. 请求的url长度没有限制的,比方说文件上传。
3. 相对安全,如果拦截了请求头,也不安全
2. 请求头:客户端浏览器告诉服务器一些信息
请求头名称: 请求头值
HOST:请求主机名,例如localhost
* 常见的请求头:
1. User-Agent:浏览器告诉服务器,我访问你使用的浏览器版本信息
* 可以在服务器端获取该头的信息,解决浏览器的兼容性问题
User-Agent Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:84.0) Gecko/20100101 Firefox/84.0
2. Referer:http://localhost/login.html
* 告诉服务器,我(当前请求)从哪里来?(如果不是从别的路径过来访问,则referer为空,比如从自己路径就是referer为null)
* 作用:
1. 防盗链:
2. 统计工作:
3.Accept:text/html,application/xhtml,告诉浏览器我可以响应什么格式
4. Connection:keep-alive,一直存在,也就是1.1复用链接。
3. 请求空行
空行,就是用于分割POST请求的请求头,和请求体的。
4. 请求体(正文):
* 封装POST请求消息的请求参数的,而get方式是没有请求体的。
* 字符串格式:
POST /login.html HTTP/1.1
Host: localhost
User-Agent: Mozilla/5.0 (Windows NT 6.1; Win64; x64; rv:60.0) Gecko/20100101 Firefox/60.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language: zh-CN,zh;q=0.8,zh-TW;q=0.7,zh-HK;q=0.5,en-US;q=0.3,en;q=0.2
Accept-Encoding: gzip, deflate
Referer: http://localhost/login.html
Connection: keep-alive
Upgrade-Insecure-Requests: 1
username=zhangsan
* 响应消息数据格式(对应Service的HttpServletResponse resp)
4.Request
4.1. request对象和response对象的原理
1. request和response对象是由服务器创建的。我们来使用它们
2. request对象是来获取请求消息,response对象是来设置响应消息
因此,我们其实只是负责第四步:service方法进行request数据处理和response数据设置,以便回传操作。
4.2. request对象继承体系结构:
ServletRequest -- 接口
| 继承
HttpServletRequest -- 接口(我们以后都用这个)
| 实现
org.apache.catalina.connector.RequestFacade 类(tomcat实现的,这也证实了我们上面说的tomcat实现了request对象和response对象,传递给service方法)
4.3. request功能:
4.3.1. 获取请求消息数据
1. 获取请求行数据
* GET /day14/demo1?name=zhangsan HTTP/1.1
* 方法:
1. 获取请求方式 :GET * String getMethod()
2. (*)获取虚拟目录:/day14 * String getContextPath()
3. 获取Servlet路径: /demo1 * String getServletPath()
4. 获取get方式请求参数:name=zhangsan * String getQueryString()
多个参数会&分割。
5. (*)获取请求URI:/day14/demo1 * String getRequestURI():
http://localhost/day14/demo1 * StringBuffer getRequestURL()
* URL:统一资源定位符: http://localhost/day14/demo1中华人民共和国
* URI:统一资源标识符 : /day14/demo1 共和国
因此URI权限比URL大,因为匹配的更多。
6. 获取协议及版本:HTTP/1.1 (父servlet中) * String getProtocol()
7. 获取客户机的IP地址: * String getRemoteAddr()
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { // 1. 获取请求方式 :GET * String getMethod() String method = request.getMethod(); System.out.println(method); // 2. (*)获取虚拟目录:/day13_tomcat * String getContextPath() String contextPath = request.getContextPath(); System.out.println(contextPath); // 3. 获取Servlet路径: /RequestServletDemoOne * String getServletPath() String servletPath = request.getServletPath(); System.out.println(servletPath); // 4. 获取get方式请求参数:name=zhangsan * String getQueryString() String name = request.getQueryString(); System.out.println(name); // 5. (*)获取请求URI:/day13_tomcat/RequestServletDemoOne * String getRequestURI(): String requestURI = request.getRequestURI(); System.out.println(requestURI); // http://localhost/day14/demo1 * StringBuffer getRequestURL() // //* URL:统一资源定位符: http://localhost/day14/demo1中华人民共和国 //* URI:统一资源标识符 : /day14/demo1 共和国 // 6. 获取协议及版本:HTTP/1.1 (父servlet中) * String getProtocol() String protocol = request.getProtocol(); System.out.println(protocol); // 7. 获取客户机的IP地址: * String getRemoteAddr() String remoteAddr = request.getRemoteAddr(); System.out.println(remoteAddr); }
4.3.2. 获取请求头数据
* 方法:
* (*)String getHeader(String name):通过请求头的名称获取请求头的值
* Enumeration<String> getHeaderNames():获取所有的请求头名称,可以把它返回值当成一个迭代器来使用。
Enumeration<String> headerNames = request.getHeaderNames(); while (headerNames.hasMoreElements()){ String name2 = headerNames.nextElement(); String headerValue = request.getHeader(name2); System.out.println(name2+"---"+headerValue); }
host---localhost:8080
user-agent---Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:84.0) Gecko/20100101 Firefox/84.0
accept---text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8
accept-language---zh-CN,zh;q=0.8,zh-TW;q=0.7,zh-HK;q=0.5,en-US;q=0.3,en;q=0.2
accept-encoding---gzip, deflate
connection---keep-alive
upgrade-insecure-requests---1
cache-control---max-age=0
4.3.3. 获取请求体数据:
* 请求体:只有POST请求方式,才有请求体,在请求体中封装了POST请求的请求参数,其中request对象将请求体封装为流,因此需要获取流对象
* 步骤:
1. 获取流对象(servletRequest父类中的获取方法)
* BufferedReader getReader():获取字符输入流,只能操作字符数据
* ServletInputStream getInputStream():获取字节输入流,可以操作所有类型数据(继承了InputStream流) * 在文件上传知识点后讲解
2. 再从流对象中拿数据
public class RequestServletDemoOne extends HttpServlet { protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { BufferedReader bufferedReader = request.getReader(); System.out.println(bufferedReader+"---bufferedReader");//org.apache.catalina.connector.CoyoteReader@1dff3fd4---bufferedReader //拿数据 String line = null; while ((line=bufferedReader.readLine())!=null){ System.out.println(line);//username=11&password=22 } } protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { } }
Tomcat的核心分为3个部分:
(1)Web容器---处理静态页面;
(2)catalina --- 一个servlet容器-----处理servlet;
(3)还有就是JSP容器,它就是把jsp页面翻译成一般的servlet。
4.3.5. 其他功能:
4.3.5.1. 获取请求参数通用方式:这类方法都是让我们更方便使用request对象的方法
不论get还是post请求方式都可以使用下列方法来获取请求参数(以下方法均是servletRequest类实现的,httpServletRequest继承了)
1)String getParameter(String name):根据参数名称获取参数值 username=zs&password=123
2)String[] getParameterValues(String name):根据参数名称获取参数值的数组 hobby=xx&hobby=game
3)Enumeration<String> getParameterNames():获取所有请求的参数名称(也就是key)
Enumeration<String> parameterNames = request.getParameterNames();
while (parameterNames.hasMoreElements()){
hasMoreElements:如果有更多标记,则为true
;否则为false
。
nextElement(); 一次返回一个数据
String name = parameterNames.nextElement();
String value = request.getParameter(name);
4)Map<String,String[]> getParameterMap():获取所有参数的map集合(key和value都获取出来)Map集合遍历方式:
先获取key
Map<String, String[]> parameterMap = request.getParameterMap();
Set<String> keyName = parameterMap.keySet();
再遍历获取值
for (String name : keyName) {
System.out.println(name);
String[] strings = parameterMap.get(name);
for (String value : strings) {
System.out.println(value);
}}
Map<String, String[]> parameterMap = request.getParameterMap(); Set<String> keyName = parameterMap.keySet(); for (String name : keyName) { System.out.println(name); String[] strings = parameterMap.get(name); for (String value : strings) { System.out.println(value); } System.out.println("----------"); }
1.通过Map.keySet遍历key和value map.keySet()返回的是所有key的值 map.get(in)得到每个key对应value的值 2.通过Map.entrySet使用iterator遍历key和value 3.通过Map.entrySet遍历key和value map.entrySet() 返回此映射中包含的映射关系的 Set视图。 4.通过Map.values()遍历所有的value,但不能遍历key
public static void main(String[] args) { Map<Integer, String> map = new HashMap<Integer, String>(); map.put(1, "a"); map.put(2, "b"); map.put(3, "ab"); map.put(4, "ab"); map.put(4, "ab");// 和上面相同 , 会自己筛选 System.out.println(map.size()); System.out.println("第一种:通过Map.keySet遍历key和value:"); for (Integer in : map.keySet()) { //map.keySet()返回的是所有key的值 String str = map.get(in);//得到每个key多对用value的值 System.out.println(in + " " + str); } System.out.println("第二种:通过Map.entrySet使用iterator遍历key和value:"); Iterator<Map.Entry<Integer, String>> it = map.entrySet().iterator(); while (it.hasNext()) { Map.Entry<Integer, String> entry = it.next(); System.out.println("key= " + entry.getKey() + " and value= " + entry.getValue()); } // 第三种:推荐,尤其是容量大时 System.out.println("第三种:通过Map.entrySet遍历key和value"); for (Map.Entry<Integer, String> entry : map.entrySet()) { //Map.entry<Integer,String> 映射项(键-值对) 有几个方法:用上面的名字entry //entry.getKey() ;entry.getValue(); entry.setValue(); //map.entrySet() 返回此映射中包含的映射关系的 Set视图。 System.out.println("key= " + entry.getKey() + " and value= " + entry.getValue()); } System.out.println("第四种:通过Map.values()遍历所有的value,但不能遍历key"); for (String v : map.values()) { System.out.println("value= " + v); } }
* 中文乱码问题:
* get方式:tomcat 8 已经将get方式乱码问题解决了
* post方式:会乱码;* 解决:在获取参数前,设置request的编码request.setCharacterEncoding("utf-8");
public class RequestServletDemoOne extends HttpServlet { protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { // 1. 根据名称返回参数 String username = request.getParameter("username"); System.out.println(username);//22 //2. 根据名称返回参数数组 String[] hobbies = request.getParameterValues("hobby"); for (String hobby : hobbies) { System.out.println(hobby);//game study } System.out.println("----------"); Enumeration<String> parameterNames = request.getParameterNames(); while (parameterNames.hasMoreElements()){ String name = parameterNames.nextElement(); System.out.println(name); String value = request.getParameter(name); System.out.println(value); System.out.println("==========="); } /** * username * 22 * =========== * password * 33 * =========== * hobby * game * =========== */ }
public class RequestServletDemoOne extends HttpServlet { protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { Map<String, String[]> parameterMap = request.getParameterMap(); Set<String> keyName = parameterMap.keySet(); for (String name : keyName) { System.out.println(name); String[] strings = parameterMap.get(name); for (String value : strings) { System.out.println(value); } System.out.println("----------"); } /** * username * 22 * ---------- * password * 33 * ---------- * hobby * game * study * ---------- */ }
4.3.5.2. 请求转发:一种在服务器内部的资源跳转方式
设想一下,某一个浏览器请求,需要两个servlet共同完成,则这个需求在servlet进行资源跳转,而servlet的运行依赖服务器软件,因此称之为服务器内部资源跳转。
1. 步骤:
1. 通过request对象获取请求转发器对象:RequestDispatcher getRequestDispatcher(String path)【servletRequest实现的方法,可以使用,给一个要转发的路径,返回一个对象】
2. 使用RequestDispatcher对象来进行转发:forward(ServletRequest request, ServletResponse response) 将当前的request和response对象转给新的路径对象。
如上图所示,第二个Servlet的响应被发送到客户端。第一个Servlet的响应不会显示给用户。
如上图所示,第二个Servlet的响应包含在第一个Servlet的响应中,最终才发送回用户。
RequestDispatcher接口提供两种方法。他们是:
public void forward(ServletRequest request,ServletResponse response)thorws ServletException,java.io.IOException
:将请求从Servlet转发到服务器上的另一个资源(Servlet,JSP文件或HTML文件)。public void include(ServletRequest request,ServletResponse response)throws ServletException,java.io.IOException
:在响应中包含资源(servlet,JSP页面或HTML文件)的内容。
2. 特点:
1. 浏览器地址栏路径不发生变化(只显示转发前的路径,demeOne)
2. 只能转发到当前服务器内部资源中。也就是说转发给百度是不行的
3. 转发是一次请求(f12里面只看到一个请求)
public class RequestServletDemoOne extends HttpServlet { protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { System.out.println("RequestServletDemoOne访问了"); //转发 RequestDispatcher requestDispatcher = request.getRequestDispatcher("/RequestServletDemoTwo"); requestDispatcher.forward(request,response); } @WebServlet("/RequestServletDemoTwo") public class RequestServletDemoTwo extends HttpServlet { protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { System.out.println("RequestServletDemoTwo访问了"); } protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { this.doPost(request,response); } } RequestServletDemoOne访问了 RequestServletDemoTwo访问了
4.3.5.3.共享数据
一次转发请求,两个servlet对象进行数据。
共享数据:
* 域对象:一个有作用范围的对象,可以在范围内共享数据
* request域:代表一次请求的范围,一般用于请求转发的多个资源中,也就是request对象在一次请求转发中,数据共享
* 方法:
1. void setAttribute(String name,Object obj):存储数据
2. Object getAttitude(String name):通过键获取值
3. void removeAttribute(String name):通过键移除键值对
@WebServlet("/RequestServletDemoOne") public class RequestServletDemoOne extends HttpServlet { protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { System.out.println("RequestServletDemoOne访问了"); //转发 request.setAttribute("msg","hello");//先设置数数据 RequestDispatcher requestDispatcher = request.getRequestDispatcher("/RequestServletDemoTwo"); requestDispatcher.forward(request,response); } protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { this.doPost(request,response); } }
4.3.5.4.获取ServletContext:
* ServletContext getServletContext()
org.apache.catalina.core.ApplicationContextFacade@5afc7b61 ServletContext servletContext = request.getServletContext(); System.out.println(servletContext);
案例:登录案例
用户登录案例需求:
1.编写login.html登录页面
username & password 两个输入框
2.使用Druid数据库连接池技术,操作mysql,day14数据库中user表
3.使用JdbcTemplate技术封装JDBC
4.登录成功跳转到SuccessServlet展示:登录成功!用户名,欢迎您
5.登录失败跳转到FailServlet展示:登录失败,用户名或密码错误
BEANutils
他可以使用request对象,进行request对象整体封装,而不用之前的繁琐的调试。
BeanUtils的做法:简单的封装操作,他是标准的javaBean,而一个标准的javaBean具备以下特征:
- 类必须被public修饰
- 必须提供空参的构造器
- 成员变量必须使用private修饰
- 提供公共的setter和getter方法
因此一般来说,我们的实体类也就是一个javaBean类,所以后期多数情况下,我们可以认为domain/Entity下面就是javaBean类
- POJO又是什么呢?准确的说,就是:一个普通的Java对象, 它没有任何特定的规则,它不与任何特定框架(framework)的接口绑定。
- javabean严格的解释应该是: JavaBean 是一种JAVA语言写成的可重用组件。它的方法命名,构造及行为必须符合特定的约定
- EntityBean是OR映射中对应表的每行信息封装的实体类。当然它符合javabean的约定,并且一般只有属性,没有方法
JavaBean功能:用来封装数据,
BeanUtils里面基本概念:成员变量和属性
我们使用BeanUtils可以来操作这个javaBean。此处需要深化两个理解:成员变量和属性。
成员变量:private int id;private String username;private String password;
属性:(一般来说属性和成员变量没区别),但特定环境下是setter/getter截取后的产物,经过操作:getName()--Name---name这种情况下属性和成员变量一样,但是下面:hehe和gender不一样:
BeanUtils里面两个方法:
- setProperty(封装为某对象,某对象名属性名,某对象属性值):设置属性值
- getProperty(某对象名,某对象属性名):获取属性值,返回值
- pupolate(要封装为某对象,要封装的数据map集合):封装javaBean对象,将map集合的键值对信息,封装到对应的javaBean对象中(它把键当成属性名称,值当成属性值)
4.8 HTTP协议
1. 请求消息:客户端发送给服务器端的数据
* 数据格式:
1. 请求行
2. 请求头
3. 请求空行
4. 请求体
2. 响应消息:服务器端发送给客户端的数据
* 数据格式:
1. 响应行
1. 组成:协议/版本 响应状态码 状态码描述
例如 http/1.1 200 OK
2. 响应状态码:服务器告诉客户端浏览器本次请求和响应的一个状态。
1. 状态码都是3位数字
2. 分类:
1. 1xx:客户端给服务器发消息,服务器接收客户端消息,但没有接受完成,等待一段时间后,发送1xx多状态码,询问客户端还发不发。
2. 2xx:成功。代表:200
3. 3xx:重定向。代表:302(重定向),304(访问缓存),类似于请求转发。
4. 4xx:客户端错误。
* 代表:
* 404(请求路径没有对应的资源)
* 405:请求方式没有对应的doXxx方法
5. 5xx:服务器端错误。代表:500(服务器内部出现异常)
2. 响应头:
1. 格式:头名称: 值
2. 常见的响应头:待会通过response来检验响应头的价值
1. Content-Type:服务器告诉客户端本次响应体数据格式以及编码格式,页面会以指定的格式编译打开内容
2. Content-disposition:服务器告诉客户端以什么格式打开响应体数据
* 值:
* in-line:默认值,在当前页面内打开
* attachment;filename=xxx:以附件形式打开响应体。文件下载
* 响应字符串格式
HTTP/1.1 200 OK 响应行
下面是键值键值对-响应头 Content-Type: text/html;charset=UTF-8 Content-Length: 101 Date: Wed, 06 Jun 2018 07:08:42 GMT 下面是 响应体 也就是html页面内容 <html> <head> <title>$Title$</title> </head> <body> hello , response </body> </html>
5.Response
* 功能:设置响应消息(响应空行不用设置)
1. 设置响应行
1. 格式:HTTP/1.1 200 ok
2. 设置状态码:setStatus(int sc)
2. 设置响应头:setHeader(String name, String value)
3. 设置响应体:
* 使用步骤:
1. 获取输出流
* 字符输出流:PrintWriter getWriter()
* 字节输出流:ServletOutputStream getOutputStream()
2. 使用输出流,将数据输出到客户端浏览器
* 案例:
1. 完成重定向
* 重定向:资源跳转的方式
* 代码实现: //1. 设置状态码为302 response.setStatus(302); //2.设置响应头location response.setHeader("location","/day15/responseDemo2"); //简单的重定向方法 response.sendRedirect("/day15/responseDemo2");
* 重定向的特点:redirect
1. 地址栏发生变化
2. 重定向可以访问其他站点(服务器)的资源
3. 重定向是两次请求。不能使用request对象来共享数据
@Override protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { System.out.println("responseServlet被访问了"); //1.设置状态码(302) resp.setStatus(302); //2. 设置响应头 resp.setHeader("location","/day13_tomcat/responseServletDemoTwo"); //重定向简化做法: resp.sendRedirect("/day13_tomcat/responseServletDemoTwo");
* 转发的特点:forward
1. 转发地址栏路径不变
2. 转发只能访问当前服务器下的资源
3. 转发是一次请求,可以使用request对象来共享数据
* forward 和 redirect 区别-也就是有得有失。
* 路径写法:指的是“”
1. 路径分类
1. 相对路径:通过相对路径不可以确定唯一资源
* 如:./index.html(当前 路径,有个相对概念的路径)
* 不以/开头,以点.开头路径
* 规则:找到当前资源和目标资源之间的相对位置关系
* ./:当前目录
* ../:后退一级目录
2. 绝对路径:通过绝对路径可以确定唯一资源* 如:http://localhost/day15/responseDemo2 前面http://localhost是固定的, /day15/responseDemo2(简化绝对路径)
* 以/开头的路径
* 规则:判断定义的路径是给谁用的?也就是判断请求将来从哪儿发出
* 给客户端浏览器使用:需要加虚拟目录(也就是项目的访问路径)
* 建议虚拟目录动态获取:request.getContextPath()(比方说重定向 )
* <a> , <form> 重定向...
* 给服务器使用:不需要加虚拟目录(比方说请求转发),只需要给资源的路径即可-也就是webservlet里面。
* 重定向的资源路径需要虚拟目录吗?需要,因为是给浏览器使用访问另外的资源。而且由于虚拟目录是tomcat定义的,容易发生改变,因此我们建议在重定向之前,进行动态获取虚拟目录,使用getConntextPath。然后使用字符串拼接方式给予重定向方法的路径赋值。
2. 服务器输出字符数据到浏览器
* 步骤: 1. 获取字符输出流 2. 输出数据
@WebServlet("/ResponseServletDemoFour") public class ResponseServletDemoFour extends HttpServlet { protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { //1.获取字符输出流 PrintWriter writer = response.getWriter(); writer.write("hello response"); // writer.println();在原本printWriter中,会自动刷新,在本处是response创建,会自动刷新销毁,因此和write一样。 }
* 注意:
* 乱码问题:
1. PrintWriter pw = response.getWriter();获取的流默认编码是ISO8859-1
2. 设置该流的默认编码(浏览器在电脑本地安装,因此一般来说浏览器使用的我们安装的系统的编码,我们在中国因此大部分为gbk格式,而我们response和request是tomcat创建,因此response获得的getWriter输出流也是非本地的,因此要设置其编码,因为tomcat创建的对象一般是拉丁编码ISO8859.)
3. 告诉浏览器响应体使用的编码
//简单的形式,设置编码,是在获取流之前设置
response.setContentType("text/html;charset=utf-8");
setCharacterEncoding只是设置字符的编码方式
setContentType除了可以设置字符的编码方式还能设置文档内容的类型
因此考虑到前端浏览器编码方式可能会编号,也需要告诉前端以什么方式进行解析:
response.setHeader("content-type","text/html;charset=utf-8");
简化写法:
response.setContextType(“text/html;charset=utf-8”)
3. 服务器输出字节数据到浏览器
* 步骤:
1. 获取字节输出流
2. 输出数据(需要用getBytes() 获取字节数组),一般字节用来输出图片
public class ResponseServletDemoFour extends HttpServlet {
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
response.setContentType("text/html;charset=utf-8");
//1.获取字节输出流
ServletOutputStream outputStream = response.getOutputStream();
//2.输出数据
outputStream.write("你好".getBytes("utf-8"));
}
public byte[] getBytes(String charsetName) throws UnsupportedEncodingException { if (charsetName == null) throw new NullPointerException(); return StringCoding.encode(charsetName, value, 0, value.length); }
4. 验证码
1. 本质:图片
2. 目的:防止恶意表单注册
步骤:
- 创建对象,在内存中图片
- 美化同频
- 将图片输出到页面展示
@WebServlet("/checkCodeServlet") public class CheckCodeServlet extends HttpServlet { @Override protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { this.doPost(req,resp); } @Override protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { int width=100; int height=50; //1.创建图片对象,在内存中的图片 BufferedImage img = new BufferedImage(width,height, BufferedImage.TYPE_INT_RGB); //2.1美化图片-填充背景色 Graphics graphics = img.getGraphics();//画笔对象 graphics.setColor(Color.pink);//设置背景色 graphics.fillRect(0,0,width,height);//范围 //2.2 花边框 graphics.setColor(Color.blue); graphics.drawRect(0,0,width-1,height-1); //2.3 写验证码 String str="ABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890"; Random random=new Random(); //创建随机角标 for (int i = 0; i < 4; i++) { int index = random.nextInt(str.length()); char c = str.charAt(index); graphics.drawString(c+"",width/5*i,height/2); } //4.设置划线 graphics.setColor(Color.green); for (int i = 0; i < 10; i++) { int x1 = random.nextInt(width); int x2 = random.nextInt(width); int y1 = random.nextInt(height); int y2 = random.nextInt(height); graphics.drawLine(x1,y1,x2,y2); } //3. 将图片传到页面中,由于此时我们的图片是在内存中,我们需要用对象将其传送,write需要图片对象,图片后缀名,响应对象输出流 ImageIO.write(img,"jpg",resp.getOutputStream()); } }
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> <script> /** * 给超链接添加绑定事件,重新设置src属性 */ window.onload=function () { //1.获取图片对象 var elementById = document.getElementById("change"); //2.绑定单击事件 elementById.onclick=function () { var date=new Date(); href="/day13_tomcat/checkCodeServlet?"+date; } } </script> </head> <body> <form action="./login.html" method="post"> 用户名<input type="text" name="username"> 密码<input type="text" name="password"> 爱好<input type="checkbox" name="hobby" value="game">游戏 爱好<input type="checkbox" name="hobby" value="study">学习 <input type="submit" value="提交"> </form> <img id="checkCode" src="/day13_tomcat/checkCodeServlet"/> <a id="change" href="/day13_tomcat/checkCodeServlet">看不清,换一张</a> </body> </html>
## ServletContext对象:
1. 概念:代表整个web应用,可以和程序的容器(服务器)来通信,交互数据
2. 获取:
1. 通过request对象获取
request.getServletContext();
2. 通过HttpServlet获取,父类实现,他继承了,我们继承了它,也就可以使用。
this.getServletContext();
两者获取的都相同,因为这个对象创建的是web应用,独一份,因此是一样的。//结果是org.apache.catalina.core.ApplicationContextFacade@6baed7ff
@WebServlet("/ResponseServletDemoFive") public class ResponseServletDemoFive extends HttpServlet { @Override protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { this.doPost(req,resp); } @Override protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { ServletContext servletContext = req.getServletContext(); System.out.println(servletContext); String mimeType = servletContext.getMimeType(""); System.out.println(mimeType); ServletContext servletContext1 = getServletContext(); System.out.println(); } }
3. 功能:
1. 获取MIME类型:
* MIME类型:在互联网通信过程中定义的一种文件数据类型
* 格式: 大类型/小类型 text/html image/jpeg
* 获取:String getMimeType(String file)
所以未来我们要给客户端发送数据,我们需要获取发送数据的数据类型,对应前端的context-type,这里getMIMEType是根据文件的扩展名也就是后缀名进行获取的。它为什么能获取类型,是因为数据都在服务器web中存储着,因此我们这个对象又是服务器创建的,因此可以获取到。我们的项目web.xml都继承此处,这个web.xml中文件格式是全部的。
通常,我们是动态获取文件名,通过文件名来查其文件类型,此处我们定义死文件名。结果是IMAG/JPG-大类型/小类型
@Override protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { ServletContext servletContext = req.getServletContext(); System.out.println(servletContext); String fileName= "a.jpg"; String mimeType = servletContext.getMimeType(fileName); System.out.println(mimeType);//imag/jpg }
2. 域对象:共享数据
1. setAttribute(String name,Object value)
2. getAttribute(String name)
3. removeAttribute(String name)
* ServletContext对象范围:所有用户所有请求的数据,哪怕没有请求转发,也可以使用数据msg,生命周期很长,服务器启动, 它创建, 服务器关闭,它销毁。驻留在内存中时间很长,可以说对内存压力比较大。
3. 获取文件的真实(服务器)路径
首先明确我们的web项目,会放在tomcat和工作空间存在一份,我们通过浏览器会找tomcat还是本地工作空间,肯定是tomcat,因为我们会部署到tomcat上面,由此带来一个 问题:文件的真实路径对应的一个方法:servletContext里面getRealPath:
比方说读取配置文件:配置文件有3个地方可以放:
要注意,我们此前说的classloader只能获取src下面的文件,不能获取web下面目录文件。
1.在web层可以使用String filePath = request.getServletContext().getRealPath("WEB-INFO/classes/db.properties");
先获取资源文件名,然后当作文件读取。 InputStream is = new FileInputStream(filePath);
2.在DAO层中可以使用类加载器(classloader)的方式读取配置文件。
InputStream is = CurrentClass.class.getClassLoader().getResourceAsStream("db.properties");//db.properties在bin文件夹下面,这是classpath的路径。
- src下面=a.txt(最麻烦,他编译后最终会放到web-inf下面calsses下面)
/web-inf/classes/a.txt
- web下面=b.txt /b.txt
- web下面web-inf下面=c.txt /web-inf/c.txt
1. 方法:String getRealPath(String path)
String b = context.getRealPath("/b.txt");//web目录下资源访问
System.out.println(b);
String c = context.getRealPath("/WEB-INF/c.txt");//WEB-INF目录下的资源访问 System.out.println(c);
String a = context.getRealPath("/WEB-INF/classes/a.txt");//src目录下的资源访问 System.out.println(a);
## 案例:
* 文件下载需求:
1. 页面显示超链接
2. 点击超链接后弹出下载提示框
3. 完成图片文件下载
* 分析:
1. 超链接指向的资源如果能够被浏览器解析,则在浏览器中展示,如果不能解析,则弹出下载提示框。不满足需求
2. 任何资源都必须弹出下载提示框
3. 使用响应头设置资源的打开方式:
* content-disposition:attachment;filename=xxx
这个XXX是弹框显示的下载文件名称。
Content-disposition 是 MIME 协议的扩展,MIME 协议指示 MIME 用户代理如何显示附加的文件。Content-disposition其实可以控制用户请求所得的内容存为一个文件的时候提供一个默认的文件名,文件直接在浏览器上显示或者在访问时弹出文件下载对话框。
格式说明: content-disposition = "Content-Disposition" ":" disposition-type *( ";" disposition-parm )
字段说明:Content-Disposition为属性名disposition-type是以什么方式下载,如attachment为以附件方式下载disposition-parm为默认保存时的文件名服务端向客户端游览器发送文件时,如果是浏览器支持的文件类型,一般会默认使用浏览器打开,比如txt、jpg等,会直接在浏览器中显示,如果需要提示用户保存,就要利用Content-Disposition进行一下处理,关键在于一定要加上attachment:复制代码 代码如下:
Response.AppendHeader("Content-Disposition","attachment;filename=FileName.txt");
* 步骤:
1. 定义页面,编辑超链接href属性,指向Servlet,传递资源名称filename(因为不是直接显示了,而是要指向资源),页面名称是参数传递过去
2. 定义Servlet
//1.获取请求参数,也就是文件名称 就是下载的文件名称
//2.加载文件进内存:首先获取真实路径,把文件用字符或字节输入流读取进内存,
再把内存数据用response对象写到输出流中
//2.2 将响应头设置资源的打开方式:content-dispostion:
attachment;filaname=xxx;才能以附件形式或弹框形式
//3.将数据写出到response输出流中
@WebServlet("/downLoadServlet") public class DownLoadServlet extends HttpServlet { @Override protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { //1.获取请求参数,也就是文件名称 就是下载的文件名称 String filename = req.getParameter("filename"); //2.加载文件进内存:首先获取真实路径,把文件用字符或字节输入流读取进内存,再把内存数据用response对象写到输出流中 String realPath = this.getServletContext().getRealPath("/webapp/img/"+filename); System.out.println(realPath); FileInputStream fis = new FileInputStream(realPath);//关联真实路径,也就加载进内存了 //2.2 将响应头设置资源的打开方式:content-dispostion:attachment;filaname=xxx;才能以附件形式或弹框形式 String mimeType = this.getServletContext().getMimeType(filename);//获取文件的mime类型 resp.setHeader("content-type",mimeType); resp.setHeader("content-dispostion","attachment;filaname="+filename); //3.将数据写出到response输出流中 ServletOutputStream outputStream = resp.getOutputStream(); byte[] bytes = new byte[1028 * 8];//作为缓存区 int len =0;//代表读取到的个数 while((len=fis.read(bytes))!=-1){ outputStream.write(bytes); } fis.close(); }
* 问题:
* 中文文件问题(tomcat8 中前端get中文显示自动解决了,但是别的不行,考虑到不同的ie浏览器,比方说5之前就显示乱码)
* 解决思路:
1. 获取客户端使用的浏览器版本信息
2. 根据不同的版本信息,设置filename的编码方式不同,要么base64Encoder或者URLEncoder两种编码方式。
//2.2 将响应头设置资源的打开方式:content-dispostion:attachment;filaname=xxx;才能以附件形式或弹框形式 String mimeType = this.getServletContext().getMimeType(filename);//获取文件的mime类型 resp.setHeader("content-type",mimeType); // 解决中文文件名问题 // 获取user-agent请求头 String agent = req.getHeader("user-agent"); // 使用工具类方法编码文件名即r String fileName=DownloadUtils.getFileName(agent,filename); resp.setHeader("content-dispostion","attachment;filaname="+fileName);