C/S模式的软件 客户端/服务器 比如QQ,或者一些大型游戏
用户需要先下载软件的客户端,然后才可以使用.
B/S模式的软件 浏览器/服务器 我们上网时候所访问网站的基本全是B/S模式的项目.
用户不需要下载任何东西,只需要用到浏览器就可以访问到这个项目.
我们在java中要学习的web项目,就是B/S架构的项目.
web项目中,其实就是俩个软件之间的信息交换,客户端机 器中的浏览器软件和服务器端机器中的web服务器软件之间的交换,比如在我们的学习中,就是客户端的浏览器和服务器端的tomcat进行信息交换。
浏览器和tomcat是俩个不同的软件,但是在开发这俩个软件的时候,都是加入对http协议的支持,所有它们俩个进行信息交换的时候,是通过http协议规定的方式进行交换的:客户端先发送一个请求,然后服务器端接收到请求后再返回给客户端一个响应,并且把客户端所请求的资源返回给客户端.
我们要做的时候就是,开发一个web项目,然后把这个web项目放到tomcat里面指的的位置,然后再运行这个tomcat软件,在tomcat运行过程中,其他计算机里面,只要是网络和我们这个服务器是通的,那么都可以通过浏览器来访问我们这个web项目。
在用浏览器访问tomcat的时候,tomcat会按照浏览器发送的这个请求里面的内容,把浏览器想访问的页面从web项目中找到并返回给这个浏览器,或者是tomcat去调用web项目所写的某些java类中的方法(其实就调用servlet中的方法)
注意:web项目中,任何一个类里面都不需要写main方法,整个项目的运行,对象的创建,方法的调用,都是由tomcat处理的,我们不需要去管. tomcat根据客户端的要求,会调用某个servlet中的指定方法,然后在这个指定方法里面,我们可以写上调用我们自己写的某个类的方法,类似的这样方法相互调用下去,最后调用到我们sql语句的执行,这样我们的一个功能就走完了,比如说登录或者注册功能。
一个web项目最基本的项目结构:
项目名字:WebTest
1.文件夹WebTest,项目名字是随便起的
2.WebTest文件中有文件WEB-INF,WEB-INF这个名字是固定的
3.WEB-INF文件夹中:classes文件夹,lib文件夹,web.xml文件,这个名字都是固定的。
web.xml文件在tomcat目录中的conf目录里面有一个web.xml模板.
注意:
WebTest文件夹下面还可以放页面,css样式、js文件、图片等等.
classes文件夹中放的是编译后的.class文件
lib文件夹中放的是当前项目中运行所需要的jar包
tomcat服务器:
bin 目录:启动、关闭tomcat相关的命令。
conf目录:tomcat的配置文件
lib 目录:tomcat中可以同时部署多个项目,多个项目都需要用到的相同jar包,就可以放在这个目录。
logs目录:记录tomcat日常运行的情况或者错误信息。
temp目录:tomcat运行期间可能会产生一些临时文件,这写临时文件就会放在这个目录.
webapps目录:我们开发好的web项目就是需要放到这个指定的目录下面,默认里面已经有一些tomcat自带的项目样例了.
work目录:编译jsp页面时候需要用到的一个目录.
webapps
部署
访问tomcat中项目里面的资源的方式:
http://IP:port/项目名字/项目在资源
http://127.0.0.1:8002/WebTest/hello.html
http://172.16.4.81:8002/WebTest/hello.html
手写一个servlet并且放到tomcat中去运行:
package com.briup.test;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.ServletException;
import java.io.IOException;
import java.io.PrintWriter;
public class FristServletTest extends HttpServlet{
public void doGet(HttpServletRequest req,HttpServletResponse resp)throws ServletException,IOException{
PrintWriter out = resp.getWriter();
out.println("hello world");
out.flush();
out.close();
}
}
servlet-api.jar在tomcat的lib目录中
servlet-api.jar和FristServletTest.java都在桌面
编译java文件所在的命令窗口的路径也在桌面
javac -cp servlet-api.jar -d . FristServletTest.java
编译完之后,把这个.class文件已经包目录,放到项目中的classes目录里面,然后再项目中的web.xml文件进行配置,配置的目的是:想把一个路径映射成我们自己一个servlet,这个我们在浏览器里面就可以用个这个映射的路径来来访问这个servlet了.
浏览器的地址栏中只能提供类似于这样的信息:
主要是以路径的形式提供访问的信息:
/.../.../.../...
http://172.16.4.81:8002/WebTest/hello.html
但是我们需要的是这样的信息:
要运行servlet至少要有servlet的对象,所有我们需要这个类的package.类名
com.briup.test.FristServletTest
所以我们需要把一个路径映射成一个servlet:
比如:把/first路径映射成FristServletTest这个servlet,将来用户在浏览器中输入/first路径之后,就相当于告诉服务器,需要运行FristServletTest这个servlet.
例如:
web.xml文件中加入:
<servlet>
<servlet-name>firstservlet</servlet-name>
<servlet-class>com.briup.test.FristServletTest</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>firstservlet</servlet-name>
<url-pattern>/first</url-pattern>
</servlet-mapping>
<servlet>中的<servlet-name>子标签:
给你要描述的servlet起一个名字,这个是名字是随便起的,目的是为了在下面做映射的时候能够再找到这个servlet
<servlet>中的<servlet-class>子标签:
这个servlet的package.类名
<servlet-mapping>中的<servlet-name>子标签:
指明这个映射是给哪一个servlet的做的映射,通过之前随便起的servlet的名字找。
<servlet-mapping>中的<url-pattern>子标签:
这个就是我们要映射的路径,将来浏览器里面输入这个路径就相当于要访问我们这个servlet。
注意:这个路径一定是以/开头的:/a /a/b /a/b/hello
-----------------------------------------------------------
servlet
1.什么是servlet
servlet其实就是java里面一种java类,只不过这种java类有一些特殊的作用,所以我们就叫它servlet.
浏览器地址栏输入地址或者是点击超链接或者是点击表单的提交按钮等操作,向服务器发送一个请求,这个请求的目的是要调用服务器中web项目里面的一个指定类的对象(servlet对象)中的指定方法(service方法)(同时还可以把页面上的相关参数传过来)
如果说随便一个类的对象中的方法都可以这么来调用的话,那么后台的代码程序就会很混乱,因为随便一个访问地址url就能访问到一个方法
所有在j2ee平台中,规定了只有一种类的对象可以被这么来访问,就是servlet
2.servlet的作用
可以让浏览器直接通过一个地址(URL)去访问。
(一般的java类是做不到这一点的)
3.怎么写一个类,能成为一个servlet
三种方式:
a.实现一个接口:javax.servlet.Servlet
b.继承一个抽象类:javax.servlet.GenericServlet
c.继承一个抽象类javax.servlet.http.HttpServlet
这种是之后一直要用的方法.
注意:Servlet接口中,有五个抽象方法,其实最重要的一个方法是:service(ServletRequestreq,ServletResponse res)
抽象类GenericServlet实现了接口Servlet,但是留了一个抽象方法没有实现,就是上面所提到的service方法,除此之外,GenericServlet里面还增加了一些新的方法.
抽象类HttpServlet,继承了GenericServlet,但是HttpServlet中没有任何抽象方法,除了从父类继承过来的方法之外,里面还有很多新的方法:doXxx方法,另外需要注意的是,HttpServlet里面有俩个service方法,但是俩个方法的参数类型不同.
i.Servlet接口中的service方法.
因为无论我们使用哪一种方式去写出来的一个servlet类的中,一定会有一个方法叫做service,这个方法是Servlet接口中的定义的,tomcat服务器接收到客户端请求后,帮我们调用servlet中的方法的时候,它只会调用这一个service方法.
注意这个service方法中参数的类型:
service(ServletRequestreq,ServletResponse res)
ii.GenericServlet中的俩个init方法
带参数的init(ServletConfig config)方法是从Servlet接口中实现的,还有一个是这个类直接定义的,无参的init()方法.
tomcat服务器创建servlet对象的时候,会帮我们去调用init(ServletConfig config)进行servlet类中的初始化工作,所以如果我们想在servlet类中初始化自己的一些相关数据的话,需要去重写这个init方法。有参数的init方法中调用了无参的init方法,所以将来我们只需要重写这个无参的init方法就可以了。这样我们就不需要再专门对这个init(ServletConfig config)方法中的参数进行处理了。
iii.HttpServlet中的俩个service方法
这个是从Servlet接口中实现的方法.
service(ServletRequest req, ServletResponse res){
//强转参数类型
HttpServletRequest request = (HttpServletRequest)req;
HttpServletResponse response = (HttpServletResponse)res;
//调用新定义的service方法
service(request,response);
}
HttpServlet中新定义的方法
service(HttpServletRequest req, HttpServletResponse resp){
//拿到当前请求的类型 get/post
String method = req.getMethod();
//判断请求的方法 然后去调用doXxx方法
if(method.equals("GET")){
doGet(req,resp);
}
if(method.equals("POST")){
doPost(req,resp);
}
...
..
}
http://ip:port/项目名字/资源路径
http://127.0.0.1:8002/web_test/servlet1
4.servlet的生命周期
servlet的对象是单例:在web项目整个运行期间,每一个servlet类只会有一个对象存在.默认情况下,这一个对象是在servlet第一次被访问的时候才会创建的,而且之后不管再访问多少次这个servlet,也不会再去创建它的的对象的.(我们可以设置某一个servlet的对象在服务器启动的时候就被创建出来)
这些操作(方法的调用)都是由服务器来做的:
1.默认情况下,第一次被访问的时候tomcat创建servlet对象(调用无参构造器)
2.在调用完构造器创建对象后,tomcat还会调
用init(ServletConfig config)方法,对servlet对象进行初始化,所以我们可以重写init方法来进行对象的初始化.
3.客户端第一次访问的时候,在tomcat创建servlet
对象之后,tomcat接着会调用service(ServletRequest req,ServletResponse res)方法,在方法中可以接收客户端传过来的参数以及给客户端写回去相应的数据或者页面.
4.从这之后,客户端再这个servlet,那么
tomcat就会直接调用servlet对象中的service方法
5.在销毁servlet对象的时候会tomcat调用destroy()方法.
通过web.xml文件中设置,可以让servlet的对象是在服务器启动的时候被创建出来.
web.xml:
<servlet>
<servlet-name>LifeServletTest</servlet-name>
<servlet-class>com.briup.servlet.LifeServletTest</servlet-class>
<load-on-startup>0</load-on-startup>
</servlet>
加入<load-on-startup>标签,表示这个servlet需要在启动服务器的时候就被创建对象.其中,标签可以要放一个数字,这个数字的大小就决定了多个servlet对象被创建的顺序,数字越小越先创建.(如果我们配置了多个servlet在启动服务器的时候被创建对象的话)
5.servlet的线程安全问题
问题产生的原因:
1.servlet是单例,一个servlet类只有一个对象在项目运行期间。
2.web项目项目本身就有多线程的特点,虽然我们在代码中没有写和线程相关的东西,但是这个多线程的特点是由服务器给我们提供出来的,一个客户端发送了一个请求,服务器接收到请求后就会建一个线程去处理这个请求。
所以就有可能出现这样的情况:俩个线程同时去访问同一个servlet对象.
如何解决/防止
1.加锁synchronized
2.尽量少的定义成员变量
因为只有成员变量才会产生线程安全的问题(在多个线程访问同一个对象的时候),方法中的局部变量是没有这样的问题的.
3.其他(实现一些安全性的接口)
6.客户端向服务器发请求并且传参(get/post)
客户端向服务器发送请求可以是get方式也可以是post方式.所以传参也分为get方式下传参和post方式下传参.
1.哪些方式的请求属于get/post方式
get方式:
a.浏览器中输入地址(URL)然后回车
b.超链接
c.页面中引入的css样式文件
d.页面中引入的js的文件(javascript)
e.<img src="image/a.jpg" />
f.form表单中method="get"
g.ajax中可以设置异步提交请求的方式为get
f.其他
post方式:
a.form表单中method="post"
b.ajax中可以设置异步提交请求的方式为post
c.其他
2.get和post的特点及其区别
它们各自的特点及其区别主要是体现在所传递的参数上面。
a.get方式参数
参数是直接放在要请求url后面的
例如:
url?参数名=参数值&参数名=参数值
要请求的url为:
http://ip:port/WebTest/firstServlet
传参:
.../firstServlet?name=tom
.../firstServlet?name=tom&age=20
这种方式传参的特点:
1.参数直接放在url后面
2.从浏览器的地址栏里面可以直接看到所传的参数
3.参数的长度有限制,不能把一个很长的数据通过get方式传参.(与浏览器种类有关)
b.post方式传参
参数是放在请求的体部的。
(浏览器发送的请求可以大致的分为请求头+请求体)
这种方式传参的特点:
1.参数放在请求的体部而不是url后面.
2.浏览器的地址栏中看不到所传的参数.
3.因为参数不用出现在地址栏里面,所有参
数长度是没有限制的.
7.servlet中接收客户端传过来的参数。
客户端都是以这样的形式传参的:
参数名字=参数值
所有在servlet中我们需要用参数名字来拿到参数值:
String value = request.getParameter("key");
其中key就是参数名字,value是参数值,不管传的值本身是什么类型,servlet中接收到以后只能是字符串类型或者字符串类型数组.
如果客户端用的多选框,这个时候传过来的参数就要用一个字符串类型数组来接收:String[] like = req.getParameterValues("like");
在表单中,参数值就是用户所填写的内容或者是所选的选项的value属性值,参数名字就是每一个input或者其他输入元素中的name属性的值.
例如:
<input type="text" name="username">
参数名字就是name属性的值:username
参数的值就是将来用户所填内容.
在servlet中,不管是get方式提交的时候还是post方式提交的时候,来到servlet里面以后,都是用相同的方式来取得数据。
String name=request.getParameter("key");
String[] str=request.getParameterValues("key");
8.中文乱码
a.get方式提交数据,servlet拿到后乱码
需要修改tomcat中的配置文件,然后重新启动tomcat服务器.
server.xml:在这个文件中找到修改端口号的那个标签,然后加入一个新的属性URIEncoding="UTF-8",或者是写GBK、GB2312
例如:
<Connector connectionTimeout="20000" port="8002" protocol="HTTP/1.1" redirectPort="8443"
URIEncoding="GBK"/>
b.post方式提交数据,servlet拿到后乱码
在代码中,我们去接收数据之前,也就是调用getParameter方法之前,需要先转换一下接收数据的编码:
req.setCharacterEncoding("GBK");
里面的值可以是GBK、UTF-8、GB2312
注意:其实不管是get方式或者是post方式提交数据,我们最后是在Eclipse/MyEclipse中打印了出来,所以我们最后还要看看工具中是用什么编码显示的我们的数据的.点击一个文件或者项目名字后右键,properties选项中可以看到这个编码.
c.servlet向浏览器返回数据,浏览器显示乱码.
在代码中,获得输出流之前,我们要先设置输出流是用什么编码把数据写回去的:
resp.setCharacterEncoding("GBK");
同时我们还可以通知浏览器我们向它传输的数据是用的什么样的编码:
resp.setContentType("text/html;charset=GBK");
注意:在我们使用的浏览器中,也是可以调整编码的设置的,就是我们可以自己选择一种编码来显示当前页面的内容,同时浏览器也会有自己一个默认的显示编码.当浏览器不知道服务器传过来的的数据是用什么编码的时候,浏览器会用默认的编码来显示.
9.servlet中的跳转.
a.服务器内部跳转
1.跳转到一个页面
2.跳转到另外一个servlet
page="页面/servlet"
//获得一个跳转对象(服务器内部跳转)
RequestDispatcher rd = req.getRequestDispatcher(page);
//跳转
rd.forward(req,resp);
特点:
1.不管servlet服务器内部跳转多少次,浏览器地址栏中显示的地址都是第一
次访问到的servlet的地址
2.服务器内部跳转其实就是把request和response对象进行转发,转
发到另外一个服务器内部的一个资源中,如果转发的对象是一个页面
,那么就把这个页面返回给浏览器,如果转发的对象是另外一个servlet,
那么会调用到这个servlet里面的service方法,然后把之前那个request
和response当做传送传进来.
3.在服务器内部跳转的过程中,每一个环节中所用到的request和response对
象都是同一个request和response对象.
b.客户端重定向
1.跳转到一个页面
2.跳转到另外一个servlet
String page = "页面/servlet";
resp.sendRedirect(page);
特点:
1.跳转之后,浏览器中显示的是跳转到的那个资源(页面/servlet)的
地址,而不是第一次访问的servlet地址.
2.客服端重定向,其实就是让客户端浏览器重新再发一次新的请求
到要跳转的资源。所以在客户端重定向中我们使用的对象是response,
因为response这个对象的作用就是向客户端传递数据传递消息用的。
3.在客户端重定向的过程中,每个环节中所用到的request,response
对象都是浏览器新发出的。
10.web项目中的路径问题
路径WebRoot/path/ 下面
俩个页面:
pathA.html
A页面中有超链接,链接到B页面
<a href="pathB.html">pathB.html</a>
pathB.html
一个servlet的映射路径:
/PathServletTest
这个servlet里面服务器内部直接跳转到A页面
注意: 相对路径,指的是相对于当前路径,当前路径指的是当前
地址栏里面的路径.
正常情况:
直接访问A页面:
...web_test/path/pathA.html
点击超链接:href="pathB.html"
...web_test/path/pathB.html
404的情况:
先访问servlet,再跳转到A页面:
...web_test/PathServletTest
这时候页面显示的是A页面,因为servlet跳转了.
点击超链接:href="pathB.html"
...web_test/pathB.html
因为要访问到pathB.html需要这样的路径:...
web_test_servlet/path/pathB.html
所以上面的这种情况就报错了 404
11.路径中最左边的/
例如 :一个servlet的映射路径
/servlet/test/myfristservlet
这里的第一个/就是我们要讨论的.
路径中其他/都是表示路径的分割。
注意:如果路径最左没有/,那么路径就是要按照当前路径算,
如果最左边有/,那么这个路就要按照这个/所代表的含义来算。
这个/一般会有俩种含义:
http://ip:port/
1.代表地址栏中端口号后的那个/
例如:在这种情况下
/test.html
相当于:
http://ip:port/test.html
http://ip:port/项目名字/
2.代表地址栏中项目名字后的那个/
例如:在这种情况下
/test.html
相当于:
http://ip:port/项目名字/test.html
注意:这里指的都是路径最左面的/
特点:和客户端相关的路径中的/
http://ip:port/
a. html页面中的/代表地址栏中端口号后的那个/
b. 客户端重定向中的/代表地址栏中端口号后的那个/
特点:和服务器端相关的路径中的/
http://ip:port/项目名字/
c. web.xml文件中的/代表地址栏中项目名字后的那个/
d. 服务器内部跳转中的/代表地址栏中项目名字后的那个/
12.request session application
这三个对象可以在服务器内部帮我们保存数据以及传递数据.
类型 对象名
HttpServletRequest request
HttpSession session
ServletContext application
request:
保存数据:
key必须是String,value是Object类型
request.setAttribute("key", value);
取数据:
通过key拿到value值,需要的话可以强转
Object o = request.getAttribute("key");
作用范围:request的存/取数据只能在一次请求之内起作用.比如服
务器内部跳转的时候(因为内部跳转客户端只发一次请求).
注意:客户端重定向是不行的,因为客户端重定向会发出多个请求.
session:
保存数据和取数据:
和上面request一模一样的操作,只是对象的名字变化了,上面的
加request,而这个叫session.
如何获得:
HttpSession session = req.getSession();
作用范围:在一个会话之中起作用.一个会话可以包括很多个request
请求,所以放在session中的数据,可以被多个请求所共享.
application:
保存数据和取数据:
和上面request也是一模一样的操作
如何获得:
ServletContext application =
req.getSession().getServletContext();
作用范围:整个项目在运行期间,只有一个application对象,
所以这个对象是所有人所有会员共享的,大家都可以向这个对
象里面存值,也可以再拿出来.因为 这个对象一个项目中只有一个.
作用范围: request<session<application
(page<request<session<application 这page是之后我们在jsp中
要学习的另一个范围,页面范围,只在一个页面中起作用。)
注意:getParameter("key")方法和getAttribute("key")方法,
getParameter是接收客户端传过来的值,getAttribute方法是取到
之前调用setAttribute方法保存到这个对象里面的值.
13.会话追踪技术 session cookie
http协议的访问是无状态的访问,就是说当前这次访问是不会知道之
前访问的状态的.(http协议的访问是不会帮我们保存访问的记录/痕迹的).
有些我们的访问是不需要知道之前访问的状态的.比如我们访问一些静态
的页面,在访问一个校园网站的时候,第一次访问点击了页面中的校园风
采,第二次访问点击了学生作品,这俩次访问完全可以没有任何关系,也
不需要知道各自访问的状态.
但是有些访问我们是一定要知道之前几次访问的状态的.比如在购物网
站的时候,第一次访问点击购买了一个手机,第二次访问点击购买了一
个电脑,第三次访问点击购物车结算,这个时候我们就必须知道前俩次访
问的时候购买了什么,要不然就没有方法去结算。
所以我们就有了会话追踪技术来解决这个访问无状态的问题
session和cookie
session是保存在服务器端的一个对象.客户端是没有session的.
cookie在客户端和服务器端都会有。但是存在的形式不一样.在客户端cookie是以本地文件(浏览器管理的文件)的形式存在的,在服务器端是以java对象的形式存在.
cookie的作用:和session一样,是用来保存用户访问的信息数据的。但是session是把数据保存在服务器端的内存里面,cookie是把数据保存在客户端的文件里面.
客户端访问服务器的时候,服务器会给这个客户端创建一个会话,也就是一个session对象,服务器端的内存里面同时可能有好多个session对象,分别对应的不同的客户端,每一个session对象都有一个唯一的id值,叫做JSESSIONID。
服务器端给客户端产生一个session对象后,会通过这次访问的response对象把这个session的JSESSIONID的值返回给浏览器,浏览器接收到后会把这个值以一个cookie文件的形式保存起来.
之后,这个浏览器再发生请求到服务器的时候,就会把之前保存在cookie文件中的JSESSIONID的值传给服务器,服务器通过这个JESSIONID的值就能够知道服务器内存中是哪一个session对象和当前这个客户端对应.
这样以来,最后就能到达一个效果,我们客户端的每一次访问,在服务器端都能够拿到相同的一个session对象,从而实现不同请求之间通过相同的session对象进行数据的共享.
如何从服务器端向浏览器写回cookie
//创建cookie对象
Cookie c1 = new Cookie("name","tom");
Cookie c2 = new Cookie("msg","hello");
//设置cookie的有效时间
c1.setMaxAge(60*60*24*365);
c2.setMaxAge(60*60*24*365*10);
//把cookie放到response里面
resp.addCookie(c1);
resp.addCookie(c2);
最后response对象会把cookie带回到浏览器,并又浏览器把cookie对象中的内容保存到对应的一个cookie的文件中。
注意:如果没有设置cookie生效的时间,那么这个cookie就是会话cookie,当会话结束(关闭浏览器)的时候cookie就是失效了。
如何在servlet中拿到从浏览器发送过来的cookie文件中的数据
//从request中拿到一个cookie数组
//如果没任何cookie数据则返回null
Cookie[] cookies = req.getCookies();
//遍历数组 拿出key和value
for(Cookie c:cookies){
String key = c.getName();
String value = c.getValue();
}
注意:session对象的创建时间。
14.URL重写
也属于会话追踪技术的一种.
URL重写解决了这样一个问题:
当前浏览器把cookie禁用之后,浏览器在发请求的时候,就不会把cookie带到服务器端了(其中最重要的也包括JSESSIONID),因为禁用cookie之后浏览器拒绝一切站点向浏览器写入cookie的(注意再禁用之前是否已经有一些已经存在的cookie了),这样的话,多个请求就不能在服务器端拿到同一个session对象了(因为发送请求的时候没有把JSESSIONID的值传给服务器)。
把JSESSIONID从客户端传给服务器,有俩种方式:
1.JSESSIONID保存在cookie文件中,浏览器发送请求的时候把这个cookie文件中的数据带给服务器(cookie).
2.通过传参的方式,把JSESSIONID的值通过要访问的URL传给服务器.(URL重写)
如何实现URL重写:
String url = resp.encodeURL("..");
这个方法参数就是我们要访问的URL,这个方法会把重写后的URL以字符串的形式返回.
例如:在一个超链接中,本来要访问的URL是:<a href="GetDataFromSession">
重写后:
<a href="GetDataFromSession;jsessionid=5480EF9016295A73DC56731A2F123246">
15.Filter(过滤器)
作用:在一个请求去访问某个资源的时候,filter可以在这个请求访问到这个资源之前,把请求拦下,然后做出一系列的处理或者判断(比如编码的转换,信息的过滤、权限的判断、是否已经登录的验证等等),最后filter再决定是否要让这个请求去访问那个资源.
如何写一个filter:
写一个java类,然后实现javax.Servlet.Filter接口
这个接口中有三个方法:
init destroy doFilter
init:这个过滤器类被服务器创建对象的时候会调用到这个方法。
destroy:过滤器对象被销毁的时候会调用这个方法。
doFilter:当过滤器拦截到请求的时候,会调用这个doFilter.
注意:
public void doFilter(ServletRequest req, ServletResponse res,
FilterChain chain)
这个方法有三个参数,第三个参数表示的一个过滤器链对象,因为同一个请求有可能要依次的通过俩个或者多个过滤器,在web中把这样多个过滤器看做一个过滤器链条对象,就是用这个FilterChain类型的对象来表示。
chain.doFilter(req,res)表示把当前的req和res传给这个过滤器链条中的下一个过滤器进行过滤,如果说链条中已经没有下一个过滤器,那么就把这次访问放行,让它去访问它真正要访问的资源.
注意:如果这次访问没有符合过滤器中的条件,那么我们就不用调用chain.doFilter(req,res)这个方法把这次访问放行了,而是可以直接进行跳转(服务器内部跳转或者客户端重定向),跳转到一个页面,页面中提示用户一下,为什么这次不让他去访问,比如说 还没有登录、权限不够等等原因。
最后还需要在web.xml文件中进行配置:
例如:
<filter>
<filter-name>encodingFilter</filter-name>
<filter-class>com.briup.filter.EncodingFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>encodingFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
这个配置和servlet的配置很相似。
<url-pattern>/*</url-pattern>
表示当前这个过滤器,要拦截的路径是/*,表示项目中所有的资源。
<url-pattern>/servlet/*</url-pattern>
表示当前这个过滤器,要拦截的路径是/servlet/*,也就是项目下面的servlet路径下面的所有资源.
<url-pattern>/firstServlet</url-pattern>
表示当前这个过滤器,要拦截的路径是/firstServlet,也就是说这个过滤器只会拦截这一个路径.
如果要拦截的路径有俩个,我们可以配置俩个<filter-mapping>标签分别都去和同一个<filter>标签对应。
注意:1.这里的/代表地址栏中项目名字后的/
2.某一个资源是不是会被拦截,要看地址栏中会不会出现我们在web.xml文件所配置的要拦截的路径.
16.监听器(Listener)
作用:监听web中的一些事件的发生,如果某些事件一旦发生了,那么这个监听器就会调用某些方法进行处理.
比如:在web中可以监听request对象的创建和销毁.
如何去写一个监听器:
1.写一个类,实现一个特点的接口。
2.在web.xml文件中进行配置。
web.xml:
<listener>
<listener-class>com.briup.listener.RequestListener</listener-class>
</listener>
比如:
监听request对象的创建和销毁要实现接口ServletRequestListener
监听session对象的创建和销毁要实现接口HttpSessionListener
监听application对象的创建和销毁要实现接口
-------------------------------------------------------------------------
JSP
属于动态网页技术的一种.
(servlet、jsp、PHP、asp等等)
1.jsp是什么
java server page(JSP)
后缀名以.jsp结尾的页面文件.
.html文件是静态页面.
.jsp 文件是动态页面.
2.jsp页面中可以写哪些内容
1.html标签
2.css
3.javascript
4.java代码
5.EL表达式
6.jstl标签库
3.jsp是如何工作的以及jsp的特点.
1.jsp页面其实就是一个servlet。
2.jsp页面的运行需要服务器的支持。
3.服务器中的jsp引擎可以帮我们去运行jsp页面。(注意并不是所有服务器都有这样的引擎的.引擎其实就是别人写的支持jsp页面运行的jar包或者代码)
4.jsp页面在运行之前,要经过几个步骤:首先jsp页面要被翻译成.java文件,然后再编译成.class文件,最后再运行这个.class文件.(创建这个类的对象,调用指定方法_jspService,方法中把页面里面要显示的内容用io流一行行的写给浏览器)
5.jsp翻译成的.java文件中,其实就是写了一个servlet,在这个类中的方法里面,用io流,把jsp页面中的内容一行一行的输出给了浏览器。因为这是在java类中的方法里面做的事情,所有很多数据我们都可以用变量来表示,同时也可以调用其他类中的方法.(在这里,jsp动态页面的效果也就体现出来.)
6.运行jsp页面过程中,jsp引擎帮我们去翻译或者编译成的.java文件和.class文件都保存在了tomcat中的work目录里面。
7.通过上述jsp的特点可知,写完一个项目之后,第一次部署运行的时候,整个项目的运行jsp速度会慢一些,因为第一次访问运行jsp的时候,需要先翻译成.java文件然后再编译成.class文件,最后再运行,这个过程会耗费一些时间,但是第二访问运行的时候就会比较快了.
8.访问项目中的一个jsp页面的时候,服务器首先会检查你所访问的这个jsp页面是否存在,如果不存在,服务器直接给你返回404,如果存在,服务器会进一步检查有没有和这个jsp页面对应的.class文件,如果有的话就直接运行这个.class,如果没有的话,则先把这个jsp页面翻译成.java,然后再编译成.class,最后再运行这个.class文件.
9.jsp页面其实就是在html页面中直接写上java代码.但是,在一个jsp页面中,可以没有任何html的代码而只有java代码,也可以没有任何java代码只有html的代码.
10.servlet能做的事情jsp全能做。
4.如何写一个jsp页面以及在页面中如何写java代码.
jsp页面中主要写的东西分为三部分:
1.jsp的脚本元素
1.1表达式(expression)
形式:<%= %>
例如:<%="hello" %>
<%=1+1 %>
<%=s.getName() %>
将来翻译到java文件中的位置:
_jspService方法中的
out.print(..)里面的参数.
例如上面那几个例子会翻译成
out.print("hello");
out.print(1+1);
out.print(s.getName());
所以System.out.prntln()这个方法的参数可以写什么,那么我们这个jsp页面中表达式里面就可以写什么.
注意:在jsp中的表达式里面不需要加;号。
1.2脚本(scriptlet)
形式:<% ... %>
<%
....
%>
例如:
<%
Student s = new Student();
String name = "tom";
String username = s.getName();
List<String> list = new ArrayList<String>();
list.add("hello")
%>
将来翻译到java文件中的位置:
脚本中的代码将来会被直接翻译到_jspService这个方法中.
在一个方法中我们可以写什么样的代码,那么在脚本中就可以写什么样的代码.
注意:在脚本中所写的代码,代码的最后是要加上;号的.因为我们在一个方法里面所写的没一句代码后面都是要加;号。
在脚本声明的变量,我们是可以在表达式里面使用的,但是要注意要先声明变量再使用变量.只要查看脚本和表达式分别翻译到java文件中的位置,就可以很清楚的认识到这一点.
1.3声明(declaration)
形式:<%!
.....
%>
例如:
<%!
private String address;
public String go(){
System.out.println("hello world jd1307");
return "this is go()";
}
%>
将来翻译到java文件中的位置:
直接将声明中的代码翻译到java文件里面所定义的类中。
所以我们直接可以在一个类中写什么,就可以在声明中写什么.(一般在声明中会去定义一些类中的成员变量或者方法)
注意:这里面的代码,定义变量的时候要加;号,定义方法的时候不用加;号,这是和我们写一个java类语法是一样的。
2.jsp的指令元素
jsp的指令是给jsp引擎看的,让jsp引擎在翻译jsp页面成java文件的时候,知道需要注意哪些地方的设置.比如页面中的编码、页面中脚本里面所用的编程语言、翻译的java文件中需要引入哪些其他包下的java类等等.
写法:
<%@指令名字 属性="值" .. %>
page指令
//表示当前页面中的编程语言是java,目前这个属性值只能写java
language="java"
//在当前页面中要引入哪些包下的类.
import="java.util.HashMap"
import="java.util.HashMap,java.sql.Connection"
//设置jsp页面文件保存时候所用的编码
pageEncoding="UTF-8"
//设置服务器将来使用io把jsp页面内容一行一行的输出给浏览器的时候,使用什么编码向浏览器输出.
contentType="text/html; charset=UTF-8"
//设置jsp页面被翻译成java文件的时候,java文件中的类要继承那个父类.这个属性不用设置,jsp引擎会给它一个默认的父类去继承的.
extends=""
//设置当前这个jsp页面是否支持session对象的使用.默认是支持的.
session="true"
//设置jsp页面是否是线程安全的.
isThreadSafe="true"
//如果a.jsp页面中设置了errorPage="b.jsp",那么a.jsp页面在运行的时候一旦出错,就会自动跳转到b.jsp里面.
errorPage=""
//如果一个页面中设置了isErrorPage="true",那么就表示这个页面是用来专门显示错误信息的页面.然后在这个页面中就能够使用到隐藏对象exception来显示出错误的信息了.
isErrorPage=""
include指令
<%@include file="" %>
作用:在当前页面中使用include指令可以把另外一个页面的内容引入到当前页面。
一个页面包含/引入另外一个页面有俩种方式:静态包含 动态包含。
这个include指令就属于静态包含
静态包含特点:例如a.jsp页面中静态包含了b.jsp页面,那么在翻译期间,jsp引擎在翻译a.jsp成为一个a_jsp.java文件的时候,发现jsp页面中有include指令,这时候jsp引擎就会把被包含的页面b.jsp中内容原封不动的拿到a_jsp.java中,然后用io输出出去.
taglib指令
作用:在当前jsp页面中引入一些特殊的标签库.比如jstl标签库、struts2标签库等等.
3.jsp的动作元素
<jsp:useBean id="s" class="com.briup.bean.Student" scope="page"></jsp:useBean>
相当于代码:
<%
Student s = null;
s = (Student)pageContext.getAttribute("s");
if(s==null){
s = new Student();
pageContext.setAttribute("s",s);
}
%>
<jsp:setProperty property="name" value="tom" name="s"/>
相当于代码:
<%
Student s = (Student)pageContext.setAttribute("s");
s.setName("tom");
%>
<jsp:getProperty property="name" name="s"/>
相当于代码:
<%
Student s = (Student)pageContext.getAttribute("s");
out.write(s.getName());
%>
//页面跳转
<jsp:forward page="target.jsp"></jsp:forward>
//跳转的同时还可以传参数
<jsp:forward page="target.jsp?name=tom"></jsp:forward>
或者
<jsp:forward page="target.jsp">
<jsp:param value="tom" name="name"/>
</jsp:forward>
//这个动作元素专门是传参数使用的
<jsp:param value="tom" name="name"/>
//动态包含
<jsp:include page="foot2.jsp?name=tom"></jsp:include>
动态包含特点:在把jsp文件翻译成java文件的期间,动态包含并不会去把被包含的页面原封不动的拿过来,而是会把动态包含这个标签翻译成一个方法的调用,将来运行页面调用到这个方法的时候才会去拿被包含页面的内容.同时还可以给动态包含的页面传参数.静态包含是不能传参数的。
注意:总结和对比后,分析出动态包含和静态包含各自的特点,以及哪一个执行的效率更快一些.
//使用jsp的动作元素向浏览器输出一个标签
<jsp:element name="font">
<jsp:attribute name="color">blue</jsp:attribute>
<jsp:body>hello world</jsp:body>
</jsp:element>
这个效果相当于在页面代码中直接写上<font color="blue">hello world</font>
或者:
<%
out.println("<font color='blue'>hello world</font>");
%>
5.在jsp页面代码中可以直接使用的对象
一共有9个内置对象可以直接使用.
类型 名字
PageContext pageContext
HttpServletRequest request
HttpSession session
ServletContext application
Object page
HttpServletResponse response
JspWriter out
ServletConfig config
Throwable exception
注意:为什么这个写对象可以直接使用,因为他们都是在_jspService这个方法中默认声明了出来.而我们在表达式和脚本中所写的java代码将来是要翻译到_jspService方法中的,所以我们在表达式和脚本中写java代码的时候可以直接使用这些对象.
四个范围对象,在一定范围内可以存取数据:
//页面范围(只能在同一个页面中起作用)
pageContext
request
session
application
//虽然名字叫page,但是这个并不是页面范围对象,它是Object类型的对象,表示当前这个页面本身.
page
response
//用于向浏览器输出内容的输出流.
out
//用于获得和servlet相关的信息.
config
//这个对象其实我们并不能直接使用,需要相关设置后才能使用,这个可以算是一个隐藏对象.这个对象表示将来这个jsp页面运行出错的时候所抛出的异常对象.
exception
6.jsp页面中的注释
第一种:
<!-- html/xml中的注释方式 -->
特点:
1.用户在浏览器中右键查看源代码 [能] 看到这个注释。
2.在服务器端,这个jsp页面被翻译成的java中 [能] 看到这个注释.
注意: jsp动作元素 放在这种注释里面是不起作用(注释不起作用,失效了)
第二种:
<%--
jsp中的注释方式(隐藏注释)
--%>
特点:
1.用户在浏览器中右键查看源代码 [不能] 看到这个注释。
2.在服务器端,这个jsp页面被翻译成的java中 [不能] 看到这个注释.
第三种:
java中的注释方式,但是这种注释方式只能在jsp的脚本或者声明中使用。
//String name = "tom";
/*
int b = 40;
*/
/**
int a = 20;
*/
特点:
1.用户在浏览器中右键查看源代码 [不能] 看到这个注释。
2.在服务器端,这个jsp页面被翻译成的java中 [能] 看到这个注释.
7.jsp页面中的路径
一般情况下,jsp中路径问题是和我们之前在servlet中讨论的html里面的路径问题是一样的,但是在jsp中有一种情况是要特殊对待的。
如果在jsp页面的上面写了这样一个脚本:
<%
String path = request.getContextPath();
String basePath = request.getScheme()+"://"+request.getServerName()+":"+request.getServerPort()+path+"/";
%>
并且再<head>标签中加入了一个子标签:
<base href="<%=basePath%>" />
如果在jsp页面里面有上面说提到的俩个东西,那么在这个jsp页面中,我们再去写上一个相对路径(最左边没有加/的那种路径),它就不是相对于地址栏中的当前路径了,而是要相对于这个basePath变量所代表的这个路径.
<base href="..">这个标签的作用:
在没有这个标签的情况下,页面中相对路径的跳转,都要参照地址栏中的当前路径来跳转,但是页面中加上了这个<base>标签后,页面中的相对路径的跳转都要参照这个标签中所写的路径来跳转。
注意:这里说的相对路径的跳转指的是最左边没有/的那种路径的跳转.
8.EL表达式
形式:${ }
作用:从一个范围里面取值或者从一个对象中取值或是向页面输出值.
1.接收客户端参数.
${param.name1 }
2.指定范围并取值
${pageScope.name2 }
${requestScope.name3 }
${sessionScope.name4 }
${applicationScope.name5 }
3.可以不指定范围再去取值
${name}
这时候会按照pageContext request session application这样一个顺序依次的去找有没有一个叫name的值存在,一旦找到了就输出出来,最终没有找到那么就什么都不输出。
4.取出一个对象中的属性值.
${requestScope.student.id}
${requestScope.student.name}
${requestScope.student.age}
或者
${student.id}
${student.name}
${student.age}
或者
${student["id"]}
${student["name"]}
${student["age"]}
注意:比如 ${student.id}表示是要调用student对象中的getId方法,至于对象中有没有id属性对这个操作没有任何影响.
如果Student类中一个方法是getAddress,返回一个Address类的对象,Address类中有一个方法getCity,这个时候我们就可以这样写去拿到city属性的值.
${student.address.city}
5.输出字符串
${"hello"}
6.输出运算结果或者boolean表达式
${1+1 }
${(1+2)*3-4+5*3 }
${1<3 }
//为空的话返回true
${empty "" }
${empty "hello" }
//取否 不为空的话返回true
${not empty "hello" }
${! empty "hello" }
${param.score >50 }
${param.score >60?"good":"bad" }
7.输出数组、集合中的元素
<%
String[] str = {"hello","world"};
List<String> list = new ArrayList<String>();
list.add("zhangsan");
list.add("lisi");
Map<String,Integer> map = new HashMap<String,Integer>();
map.put("a",100);
map.put("b",200);
map.put("c",300);
request.setAttribute("str",str);
request.setAttribute("list",list);
request.setAttribute("map",map);
%>
${str[0] }<br>
${list[1] }<br>
${map["c"] }<br>
9.JSTL标签库
JSP Standard Tag Library(JSTL)
1.让web项目支持JSTL标签库
在myeclipse中,建一个web项目的时候,在对话框下面会有提示在当前项目是否需要加入JSTL标签库的支持.(J2EE5.0是默认会加入对JSTL的支持的)
在eclipse中,建一个文本项目,默认都是不支持JSTL,所以需要我们自己把JSTL的jar包导入到项目中(复制粘贴到项目中的lib目录):jstl.jar standard.jar
2.把JSTL标签库导入到某一个jsp页面中
使用jsp中的taglib指令:
<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
prefix="c"相当于给这个标签库起一个别名,将来在页面中就是用以c开头的标签来使用标签库中的标签。这个别名也可以叫其他的名字。
<c:forEach>标签:
遍历List集合:
students是放进request对象中的一个List集合,集合中存放的是Student类型的对象.
items=""属性值是要遍历的集合
var="" 属性值是每次遍历到的对象用什么名字的变量去接收。
<c:forEach items="${students}" var="stu">
<tr>
<td>${stu.id }</td>
<td>${stu.name }</td>
<td>${stu.age }</td>
</tr>
</c:forEach>
遍历Map集合:
map是一个Map类型的集合,放到了request对象中,entry是我们顶一个的一个变量,用做接收每次遍历到的集合中的一组键值对,我们可以通过entry.key entry.value分别拿到这次遍历到的key值和value值
<c:forEach items="${map}" var="entry">
${entry.key }-->${entry.value.id } ${entry.value.name } ${entry.value.age }<br>
</c:forEach>
<c:out>标签:
向页面输出内容
<c:out value="hello"></c:out>
<c:out value="${5+5}"></c:out>
//students是放在request中的List集合,集合里面是Student对象
<c:out value="${students[2].id}"></c:out>
<c:set>标签:
向某一个范围对象中存放一个值。
<c:set var="name" value="zhangsan" scope="request"></c:set>
<c:remove>标签:
从某个范围对象中把某个值给移除掉.
<c:remove var="name" scope="request"/>
<c:if>标签:
条件判断
<%
request.setAttribute("score",40);
%>
<c:if test="${score>85 }">
<font color="red">你的分数超过了85分</font>
</c:if>
<c:if test="${score>95 }">
<font color="red">你的分数超过了95分</font>
</c:if>
这样写相当于:
if(score>85){
...
}
if(score>95){
...
}
<c:choose>标签
<c:when>标签
<c:otherwise>标签
例如:
<c:choose>
<c:when test="${score>=90 }">优</c:when>
<c:when test="${score>=80 }">良</c:when>
<c:when test="${score>=70 }">中</c:when>
<c:when test="${score>=60 }">及格</c:when>
<c:otherwise>差</c:otherwise>
</c:choose>
相当于:
if(score>=90){
}else if(score>=80){
}else if(score>=70){
}eles if(score>=60){
}else{
}