目录
📢:哈喽~☀️欢迎加入程序🐒大家庭,快点开始✒️自己的黑客帝国吧 ~🌊🌊
内容简述:Http协议、Servlet工作原理、DAO、Servlet生命周期、JSP基础、cookie、Session、过滤器、监听器、线程安全、JSTL、自定义标签、MVC。
一、Servlet基础
1.什么是Servlet?
- sun公司制订的一种用来扩展web服务器功能的组件规范。
扩展web服务器功能
-
a. web服务器通常只能处理
静态资源
(即需要事先将html文件写好并存放在服务器上)的请求, -
不能够处理
动态资源
(即需要计算,动态生成相应的页面)的请求。 -
b. 早期,开发者会使用CGI程序来扩展web服务器功能。
了解:CGI程序指的是使用c,perl等语言开发的符合CGI(Common Gateway Interface)标准的程序。因为CGI程序开发繁琐,并且可移值性不好,所以用得越来越少了。
-
c. 可以使用Servlet来扩展web服务器功能。
补充:当web服务器收到请求之后,如果是动态资源的请求,可以调用servlet来处理。
组件规范
-
a. 什么是组件?
- 符合规范,具有一定功能,并且需要部署到相应的容器上才能运行的软件模块。
- Servlet就是一个符合Servlet规范的组件,需要部署到Servlet容器里面才能运行。
-
b. 什么是容器?
- 符合规范,提供组件的运行环境的程序。
- Servlet容器(比如Tomcat)提供Servlet运行环境。
2.如何开发一个Servlet?
-
step1. 写一个java类,实现Servlet接口或者继承HttpServlet类。
-
(通常我们选择继承HttpServlet类)
-
step2. 编译。
-
step3. 打包。
- 需要创建一个具有如下结构的文件夹:
- appname (名称可以自定义) 应用名
- WEB-INF
- classes (存放.class文件)
- lib(可选,用来存放.jar文件)
- web.xml (部署描述文件)
- 需要创建一个具有如下结构的文件夹:
-
step4. 部署。
-
将step3创建好的文件夹拷贝到容器指定的位置。
(也可以将step3创建好的文件夹使用 jar命令压缩成.war结尾的文件,然后拷贝)
-
-
step5. 启动容器
- 打开浏览器,在地址栏输入:http://ip:port/appname/url-pattern
3.安装Tomcat(Tomcat是一个相对标准的容器)
创建servlet测试项目:
1)创建一个maven项目;
2)设置响应的名称并且把打包方式改为war;
3)问题:创建后的day01项目上有一个小红叉;
解决办法:在javaEE模式下,右键Deployment选择Generate Deployment.,然后小红叉消失了。
原因:之所以有小红叉是因为没有生成部署描述文件(web.xml),操作之后,在src文件夹下main文件夹下的webapp中会出现一个WEB-INF文件夹,里面有web.xml文件。
4)右键day01项目,选择最后一项Properties,然后选择Targeted Runtimes,在右侧选中tomcat7.0
原因:这一步是为了将想要运行的tomcat 的相关jar包导入我们的项目当中;
5)在Java Resources中的src/main/java中创建Serlvet类:
public class TimeServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException {
// 1.使用request接收请求数据
// 1)请求行(3组数据)
System.out.println("协议类型:" + req.getProtocol());
System.out.println("访问路径:" + req.getServletPath());
System.out.println("请求方式:" + req.getMethod());
// 2)消息头(N组数据)
// 该数据是按照键值对的方式存储
// Enumeration是一个古老的迭代器
// 其用法和Iterator相似.
Enumeration<String> headerNames = req.getHeaderNames();
while(headerNames.hasMoreElements()) {
String key = headerNames.nextElement();
String value = req.getHeader(key);
System.out.println(key + ":" + value);
}
// 3)实体内容
// 获取服务器的时间
Date date = new Date();
SimpleDateFormat sdf = new SimpleDateFormat("HH:mm:ss");
String now = sdf.format(date);
// 将时间拼到一个网页里给浏览器返回
// 告诉浏览器向它发送的是什么类型的内容
resp.setContentType("text/html");
// 设置编码格式,不然中文不能正常显示
resp.setCharacterEncoding("utf-8");
// 获取输出流,该流指向的目标是浏览器
PrintWriter w = resp.getWriter();
// 此处偷懒,拼一个简化版的网页
w.println("<lable>服务器时间:</lable>");
w.println("<p>" + now + "</p>");
// 注意,不用关闭输出流,servlet会自动关闭
}
}
输出结果
doGet()方法和service()方法的区别:
- doGet()方法只处理Get请求;
- doPost()方法只处理Post请求;
- service()方法即处理Get请求,也处理Post请求;
- 官方文档上建议尽量避免使用service()方法。
6)在web.xml中配置serlvet
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi=**"http://www.w3.org/2001/XMLSchema-instance"** xmlns=**"http://java.sun.com/xml/ns/javaee"** xsi:schemaLocation=**"http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"** version=**"2.5"**>
<servlet>
<servlet-name>helloServlet</servlet-name>
<!-- 要写完整的类名(要注意大小写) -->
<servlet-class>web.HelloServlet</servlet-class>
</servlet>
<servlet-mapping>
<!-- 两个name要一致 -->
<servlet-name>helloServlet</servlet-name>
<!-- 告诉容器,如何访问该Servlet -->
<url-pattern>/hello</url-pattern>
</servlet-mapping>
</web-app>
7)运行:在项目处右键→Run as→Run on Server→选中Tomcat 7.0→点next→点finish→重启Server→完成
4.Servlet是如何运行的?
比如,在浏览器地址栏输入:http://ip:port/day01/hello
step1.浏览器依据ip和port建立与服务器之间的连接 (比如,建立与tomcat之间的连接)。
step2.浏览器将相关数据打包 (即按照http协议的要求,创建请求数据包)。
step3.浏览器将请求数据包发送给服务器。
step4.服务器解析请求数据包的内容,并且将解析到的数据添加到request对象里面,同时创建一个response对象。
step5.服务器将对应的Servlet(比如HelloServlet)
实例化,接下来调用Servlet实例的service方法。
注意:
- 服务器会将request和response作为参数传递给service方法,可以通过request对象获取请求数据(比如一些请求参数值),然后通过response对象写入处理结果。
step6.服务器通过response对象获取处理结果,然后创建响应数据包。
step7.服务器发送响应数据包给浏览器。
step8.浏览器解析响应数据包,并且生成相应的页面。
5.常见的错误及解决方式
(1) 404
-
404 Not Found,表示服务器依据请求地址找不到对应的资源。
-
错误原因:
a.请求地址写错了;
b.应用没有部署成功。
(2)500
-
500 Internal Server Error,表示服务器端的程序运行出错。
-
错误原因:
a.没有继承HttpServlet或者实现Servlet接口;
b.web.xml配置错误,比如servlet-class写错;
c.程序写得不够严谨,异常报错,比如将一个非数字转换成数字。
(3)405
- 405 Method Not Allowed(GET请求和POST接口对接时就会报405)
- 错误原因:
- service()方法签名不符合要求(方法名,参数类型,异常类型要符合规范–两个异常)。
课堂练习
练习1:
- 写一个DateServlet,输出当前的系统日期, 比如在浏览器地址栏输入 http://ip:port/day01-lab/date 显示 2018-02-06
- 建议新建工程,从零开始写。
练习2:
- 写一个BmiServlet,计算一个人的bmi指数。
- bmi指数 = 体重(公斤) / 身高(米) / 身高(米)
- http://ip:port/day01-lab/bmi?weight=80&height=1.5
二、HTTP协议、抓包、Servlet工作原理
1.http协议
什么是http协议?
- 一种网络应用层协议,规定了浏览器与web服务器之间如何通信以及通信过程当中所使用的数据格式。
http协议如何通信
-
step1.建立连接;
-
step2.发送请求;
-
step3.发送响应;
-
step4.关闭连接
-
即“一次请求,一次连接”,如果要发送新的请求,需要重新建立新的连接。
http协议的优点:
- web服务器可以使用尽可能少的连接为更多的请求服务。
2.数据格式
抓包:
1)在Eclipse中Windows→Show View→Others→Debug→TCP/IP Monitor
2)在最左上角的框中右键,点击Properties
3)点击Add,在Local monitoring port处填写监视器的端口号(比如8888)
- 在Host name处填写想要拦截的地址(localhost);
- 在Port处填写想要拦截的端口号(8080)如图:
4) 在浏览器输入网页的网址,只不过网址的端口号改为监视器的端口号
5)在Eclipse的监视器的框中便会显示抓到的信息(第二行,左边是浏览器发送给服务器的请求内容,有边是服务器发送给浏览器的响应内容)
1)请求数据包
-
请求行 (请求方式 请求资源路径 协议类型和版本)
-
若干消息头
-
消息头是一些键值对(使用":"隔开),可以用来传递一些特定的信息;
比如,浏览器可以发送 user-agent消息头告诉服务器,浏览器的类型和版本。
-
-
实体内容
- 只有请求类型为post方式时,实体内容才会有数据。
2)响应数据包
-
状态行 (协议类型和版本 状态码 状态描述)
-
状态码是一个三位数字,表示服务器处理请求的一种状态,200(正常),500(系统出错),404(找不到对应的资源)。
-
若干消息头
-
服务器也可以发送一些消息头给浏览器;
比如:发送 content-type 消息头,告诉浏览器,服务器返回的数据类型。
-
-
实体内容
- 程序返回的处理结果,浏览器会解析出来,并生成相应的页面。
(3)请求方式
-
GET 请求
-
浏览器在什么情况下会发送get请求?
a.在地址栏直接输入某个地址。
b.点击链接。
c.表单默认提交方式。
-
GET 请求的特点:
a.会将请求参数显示在浏览器地址栏,不安全。比如,路由器会记录请求地址。
b.会将请求参数添加到请求行里面,只能提交少量的数据(整个请求行大概能存放2k左右的数据)。
-
-
POST 请求
-
浏览器在什么情况下会发送post请求?
a.将表单的提交方式设置为post。
-
post请求的特点:
a.不会将请求参数显示在浏览器地址栏,相对安全。
- 并不会对请求参数进行加密处理,所以,对于敏感数据,需要加密(使用https协议)
b.会将请求参数添加到实体内容里面,可以提交大量的数据。
-
Servlet输出中文,要注意什么?
- (1)为什么会有乱码?
- out默认会使用iso-8859-1来编码。
- (2)如何解决?
- response.setContentType(“text/html;charset=utf-8”);
如何获得请求参数值?
-
(1) String request.getParameter(String paramName)
- a.paramName是请求参数名。
- b.如果paramName写错,则获得null值。
- c.表单提交时,如果没有填写任何数据,会获得空字符串。
-
(2) String[] request.getParameterValues(String paramName)
- a.当有多个请求参数名相同时,使用该方法。
- b.对于多选框,如果没有选择任何选项,则获得null值。
表单提交时,如何读取中文参数值?
-
(1) 为什么会有乱码?
-
表单提交时,浏览器会对中文参数值进行编码(会使用打开表单所在的页面时的字符集来编码)。而服务器端默认会使用iso-8859-1来解码。
-
(2) 如何解决?
-
- POST 请求
request.setCharacterEncoding(String charset)
- 这行代码要添加到所有的getParameter方法的前面。只针对post请求有效。
-
- GET 请求
配置 URIEncoding=“utf-8”。
- 只针对get请求有效。
-
课堂练习
练习1:注册用户
参考代码如下:
public class RegServlet extends HttpServlet {
private static final long serialVersionUID = 1L;
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOExceptio {
// 采用方案三解决POST请求乱码问题
request.setCharacterEncoding("utf-8");
// 处理请求的一般流程
// 1.接收参数
String username = request.getParameter("username");
String password = request.getParameter("password");
String[] hobby = request.getParameterValues("hobby");
// 采用方案一解决乱码问题
// byte[] bs = username.getBytes("iso8859-1");
// username = new String(bs, "utf-8");
// 2.处理业务
System.out.println("username:" + username)
System.out.println("password:" + password);
System.out.println("hobby:");
for (String str : hobby) {
System.out.println(" " + str);
}
// 3.发送响应
response.setContentType("text/html;charset=utf-8");
PrintWriter pw = response.getWriter();
pw.println("<h1>恭喜,亲爱的" + username + ",您已注册成功!</h1>");
}
}
练习2:添加用户
CREATE TABLE t_user(
id INT PRIMARY KEY AUTO_INCREMENT,
username varchar(50) unique,
password varchar(20),
phone varchar(20)
);
SELECT e.ename,d.dname
FROM EMP e
JOIN Dept d ON e.deptno=d.deptno
WHERE e.sal = (SELECT max(sal) FROM EMP);
三、DAO、重定向
1.DAO (Data Access Object 数据访问对象)
(1)什么是DAO?
- 封装了数据访问逻辑的对象。
(2)如何写一个DAO?
-
step1.写一个实体类(一个java类,里面有字段、get()、set()、toString()方法)
-
将查询到的记录存放到一个java对象里面,就需要设计一个对应的java类(即实体类),该类与要访问的表要一致(字段与属性要一一对应)。
-
比如,要访问的表是:
t_user(id,username,password,phone) 就可以设计 User类(id,uname,pwd,phone)
-
-
step2.写一个DAO类
- 封装数据访问逻辑,比如,将查询到的记录中的信息存放到一个实体类的实例里面。
2.重定向
(1)什么是重定向?
-
服务器通过发送302状态码及Location消息头(该消息头的值是一个地址,一般称之为重定向地址)
通知浏览器访问一个新的地址(即重定向地址)。
(2)如何重定向?
- response.sendRedirect(String url);
- url为重定向地址;
- 重定向之前,容器会先清空response对象上存放的所有数据,然后再重定向。
(3)特点
- a.重定向地址是任意的。
- b.重定向之后,浏览器地址栏的地址会发生变化。
练习:
- 实现添加员工、员工列表功能
-- 员工表:
create table t_emp(
id int primary key auto_increment,
ename varchar(50),
salary double,
age int
);
四、Servlet生命周期、JSP基础
1.Servlet生命周期
(1)什么是Servlet的生命周期?
- 容器如何创建Servlet实例,如何对其进行初始化,
- 如何调用其方法来处理请求,以及如何销毁其实例的整个过程。
(2)生命周期分成哪几个阶段?(4个阶段–实例化、初始化、调用、销毁)
-
1.实例化
-
a.什么是实例化?
容器调用Servlet的构造器,创建相应的对象。
-
b.什么时候实例化?
情形1:容器收到请求之后才创建实例。(默认)
情形2:容器启动之后,立即创建实例。(需要额外配置)
-
<servlet>
<servlet-name>someServlet</servlet-name>
<servlet-class>web.SomeServlet</servlet-class>
<!--
配置启动加载(即立即创建其实例)
注:
其值是一个大于零的整数,越小,
优先级越高(其先被创建)
-->
<load-on-startup>1</load-on-startup>
</servlet>
( 注:容器只会创建一个实例,单例模式。)
-
2.初始化
-
a.什么是初始化?
容器调用Servlet实例的init方法。( 注:该方法只会执行一次!)
-
b.GenericServlet的init方法?
将容器传递过来的ServletConfig对象保存下来了,并且提供了一个getServletConfig方法用来获得该对象。
-
c.如何实现自已的初始化处理逻辑?
重写 GenericServlet提供的init()即可。(不带参的那个方法)
-
d.初始化参数
-
step1.先配置初始化参数
-
step2.调用ServletConfig提供的 getInitParameter方法
@Override public void init(servletconfig config) throws ServletException { // 保存容器传递过来的 ServletConfig对象 this.config = config; // 便于开发人员去扩展自已的初始化逻辑 // (只需要 override下面这个init方法即可)。 this.init(); }
-
-
-
3.调用(就绪)
-
a.什么是就绪?
容器调用Servlet实例的service方法来处理请求。
-
b.HttpServlet的service方法
依据请求类型调用对应的doXXX方法。
比如:get请求会调用doGet方法,post请求会调用doPost方法。
注意:doGet,doPost方法只是简单的抛出了一个异常。需要开发人员去重写(override)。
我们要写一个Servlet,既可以重写HttpServlet的service方法,也可以重写其doGet方法和doPost方法。
-
-
4.销毁
-
a.什么是销毁?
容器在删除Servlet实例之前,会调用该实例的 destroy() 方法。(该方法只会执行一次)
-
b.如何实现自已的销毁处理逻辑?
只需要override GenericServlet的destroy方法即可。
-
相关的接口与类
- Servlet接口
- init()
- service()
- destroy()
- GenericServlet抽象类
- 实现了Servlet接口中的init和destroy方法。
- HttpServlet抽象类
- 继承了GenericServlet抽象类,实现了 service() 方法。
2.JSP
(1)什么是jsp?
-
sun公司制订的一种服务器端动态页面技术规范。
- a.虽然直接使用Servlet也可以生成动态页面,但是,过于繁琐(需要使用out.println方法输出),也不利于页面的维护(需要修改java代码),所以,sun才制订了jsp技术规范。
- b.jsp是一个以.jsp为后缀的文件(主要内容是html和少量的java代码),容器会将这个文件转换成相应的Servlet然后执行。
- jsp的本质是Servlet。
注意:jsp文件在运行之后需要访问才会生成.class文件
(2)如何写一个jsp文件?
-
step1. 添加一个以.jsp为后缀的文件。
-
step2. 在文件当中,可以添加如下内容:
-
1)html(包括css,javascript) 直接写即可。
-
2)java代码
- 方式一: java代码片断 <% java代码 %>
-
方式二 :jsp表达式 <%= java表达式 %>
-
3)隐含对象
-
a.什么是隐含对象?
在jsp文件当中可以直接使用的对象(比如out,request,response)。
-
b.为什么可以直接使用这些隐含对象?
容器会自动生成获得这些对象的代码。
-
-
4)指令
-
a.什么是指令?
告诉容器,在将jsp文件转换成Servlet的时候,做一些额外的处理,比如导包。
-
b.语法 <%@ 指令名 属性=值 %>
-
c.page指令
import属性:导包,比如:(多个包用","隔开)
<%page import="java.util.*,java.util.*"%>
-
-
(3)jsp是如何执行的?
-
step1. 容器将jsp文件转换成一个Servlet
a. html(css,js) ----> 在service方法里,使用out.write输出。
b. <% %> ----> 在service方法里,照搬。
c. <%= %> ----> 在service方法里,使用out.print输出。
-
step2.容器调用该Servlet。
五、page指令、转发、JSP开发常见问题
1.page指令
- contentType属性:设置response.setContentType的值。
pageEncoding属性:告诉容器,在读取jsp文件的内容时,使用指定的字符集去解码。
注意:charset和pageEncoding的值可以不一样,比如:
上图是可以运行的,因为charset是给浏览器说明的编码格式,pageEncoding是读jsp文件的时候需要的编码格式,只要是中文就可以读取,但是我们一般使用相同的编码格式。
2.转发
(1)什么是转发?
-
一个web组件将未完成的处理转交给另外一个web组件继续做。
web组件(servlet或者是jsp),大多数情况下,是一个servlet获得数据之后,转发给jsp来展现这些数据。
(2)如何转发?
-
step1.绑订数据到request对象上。
request.setAttribute(String name,Object obj)
- name 绑订名。
- obj 绑订值。
- Object request.getAttribute(String name)
-
step2.获得转发器
RequestDispatcher rd = request.getRequestDispatcher(String uri);
- uri:转发的目的地地址,通常是一个jsp。
- RequestDispatcher:是一个接口,getRequestDispatcher方法会返回该接口的实现。
-
step3.转发
rd.forward(request,response)
- 详细步骤:
1:在浏览器地址栏输入地址后回车发送listUser请求
2:创建request对象
3:就绪,调用service()方法
4:在ListUserServlet中向request对象中绑定数据
5:调用forward()方法转发后,通知容器
6:让web容器去调用另外一个组件的service()方法
7:另一个组件再从request对象中获得之前绑定的数据
注意:转发的本质是通知容器调用(即执行service方法)另外一个web组件。
(3)特点
-
a.转发的目的地有限制(同一个应用)。
-
b.转发之后,浏览器地址栏的地址不变。
3.比较转发与重定向
- a.能否共享request和response?
- 转发可以,而重定向不行。
- 分析:当请求到达容器,容器创建request和response,当响应发送完毕,容器会销毁这两个对象,也就是说,request 和 response 的生存时间是一次请求和响应期间存在。
- 转发是一次请求,重定向是两次请求。
- b.浏览器地址栏地址有无变化?
- 转发无变化,重定向有变化。
- c.目的地有无限制?
- 转发有限制(同一个应用),重定向无限制。
如何用Eclipse创建Servlet:
-
step1:右键“src/main/java”,新建一个Servlet
-
step2:点击next,可以自定义url-mapping地址名
-
step3:点击next,可以选择默认生成的方法,我们一般只选择默认生成service()方法
4.web容器
什么是web容器,其作用是? 尽可能写出你知道的容器名称
- web容器给处于其中的应用程序组件(JSP、SERVLET)提供一个环境;
- 作用:使JSP、SERVLET直接跟容器中的环境变量接口交互,不必关注其它系统问题;
- 例如:TOMCAT、WEBLOGIC、WEBSPHERE等都为web容器。
容器如何处理请求资源路径?
-
比如:http://ip:port/day05/abc.html
-
step1.容器默认认为访问的是一个servlet
如果地址名和HTML等文件名重复的话先访问servlet,会查看配置文件(web.xml)有没有一个匹配的servlet。
-
step2.如果没有匹配的servlet,则查找对应位置的文件,然后执行(如果找不到,则返回404)。
配置规则
-
(1)精确匹配:
- 要求url-pattern的值必须等于"/abc.html"。
-
(2)通配符匹配:
- 使用 * 号匹配任意的零个或者多个字符,比如:
<url-pattern>/*</url-pattern>
<url-pattern>/demo/*</url-pattern>
- (3)后缀匹配:(注意:后缀匹配不能用/开头)
- 使用"*."开头,后接一个后缀,比如:
<url-pattern>*.do</url-pattern>
<!-- 用来匹配所有以.do结尾的请求 -->
如何让一个servlet处理多种请求?
- step1.采用后缀匹配。比如:
<url-pattern>*.do</url-pattern>
- step2.分析请求资源路径,然后进行不同的处理。
六、路径问题
链接:
<a href="">
表单提交:
<form action="">
重定向:
response.sendRedirct("");
转发:
request.getRequestDispatcher("");
相对路径
- 不以/开头的路径。
绝对路径
- 以/开头的路径。
- 如何写绝对路径?
- 链接,表单提交,重定向从应用名开始写;转发从应用名之后开始写。
注意:
不要直接将应用名写在路径里面,而应该使用 request.getContextPath()来获取。
(应该尽量避免硬编码----把应用名写死)
建议使用绝对路径,好写,并且更好维护。
综合练习:登录
需求:
- 用户填写用户名和密码并提交;
- 服务器端查询数据库,看是否有匹配的记录(即用户名和密码都匹配);
- 如果有,则登录成功(返回用户列表);
- 如果没有,则登录失败(在登录页面当中,提示用户名或密码错误)。
提示:
- step1.给UserDAO添加一个方法
public User findByUsername(String uname)
-
step2.添加一个登录页面 login.jsp
-
step3.给ActionServlet添加一个处理 “/login” 的分支,用于处理登录请求。
request.setAttribute("login_failed", "用户名或密码错误");
request.getRequestDispatcher("login.jsp").forward(request,response);
七、状态管理-cookie
1.状态管理
(1) 什么是状态管理?
- 将浏览器与web服务器之间多次交互做为一个整体来处理,将多次交互所涉及的数据(即状态)保存下来。
(2) 如何进行状态管理?
-
方式一: 将状态保存在浏览器端,通常使用cookie技术;
-
方式二: 将状态保存在服务器端,通常使用session技术。
2.cookie
(1) 什么是cookie?
- 服务器临时存放在浏览器端的少量数据。
(2) cookie工作原理
- 浏览器访问服务器时,服务器会将一些数据以set-cookie消息头的形式发送给浏览器,
- 浏览器会将这些数据临时保存下来;
- 当浏览器再次访问服务器时,会将之前存放的数据以cookie消息头的形式发送给服务器。
(3) 添加cookie
Cookie c = new Cookie(String name,String value);
response.addCookie(c);
注意:cookie的名称和值都必须是String。
(4) 读取cookie
// 有可能返回null
Cookie[] request.getCookies();
String cookie.getName()
String cookie.getValue();
(5) cookie的生存时间
-
a.默认情况下,cookie会被浏览器保存在内存里面。即,浏览器只要不关闭,cookie就会一直存在。
-
b.setMaxAge(int seconds)
- 单位是秒。
- seconds > 0: 浏览器会将cookie保存在硬盘上,当超过指定时间,浏览器会删除cookie。
- seconds < 0: 默认值(即保存在内存)
- seconds = 0: 删除cookie。
-
比如,要修改名称为username的cookie的值:
- 再发送一个Cookie,名为username,值为想要修改成的值
Cookie c = new Cookie("username", "");
- 比如,要删除名称为username的cookie:
Cookie c = new Cookie("username", "");
c.setMaxAge(0);
response.addCookie(c);
注意:如果Cookie的路径不一样是不能被覆盖的。
6)编码问题
cookie只能保存合法的ascii字符,如果要保存中文,
要将中文转换成相应的ascii字符的形式。
String URLEncoder.encode(String str,String charset);
String URLDecoder.decode(String str,String charset);
注:建议,添加cookie时,最好对保存的字符统一编码处理。
7)cookie的路径问题
a.什么是cookie的路径问题?
当浏览器访问服务器上的某个地址时,会比较
该地址是否匹配cookie的路径,只有匹配的cookie
才会发送给服务器。
b.cookie的默认路径
等于添加该cookie的web组件的路径。
c.匹配规则
要访问的地址必须等于cookie的路径或者
是其子路径。
比如 cookie的路径是"/day07/biz01",则
/day07/findCookie1.jsp no
/day07/biz01/findCookie2.jsp yes
/day07/biz01/sub/findCookie3.jsp yes
d.修改cookie的路径
cookie.setPath(String path);
8)cookie的限制
a.cookie可以被用户禁止。
b.cookie不安全。
对于敏感数据,一定要加密。
c.cookie只能保存少量的数据。
大约4k左右
d.cookie的数量也有限制。
总的数量大约是几百个,对于某个网站,数量
也有限制(大约是二十个左右)。
e.cookie只能保存字符串。
练习:写一个Servlet(比如CountServlet),记录
用户访问的次数(要求使用cookie技术)。
八、状态管理-Session
1.session 会话
(1) 什么是session?
- 服务器端为了保存状态而创建的一个特殊的对象。
(2) session的工作原理
- 浏览器访问服务器时,服务器创建一个session对象(该对象有一个唯一的id,称之为sessionId);
- 然后将sessionId以cookie的方式发送给浏览器(意味着浏览器重启的话,sessionId将会更新)(是在request.getSession()方法中实现的);
- 当浏览器再次访问服务器时,会将sessionId发送过来,服务器可以利用sessionId找到之前的session对象。
(3) 如何获得session对象?
-
方式一:
HttpSession session = request.getSession(boolean flag);
- a. HttpSession是一个接口。
- b. 当flag为true时,先查看请求当中是否有sessionId,
- 如果没有,则创建一个session对象;
- 如果有,则依据sessionId查找对应的session对象,
- 如果找到了,则返回,找不到,则创建一个新的session对象。
- c. 当flag为false时,先查看请求当中是否有sessionId,
- 如果没有,则返回null;
- 如果有,则依据sessionId查找对应的session对象,
- 如果找到了,则返回,找不到,返回null。
-
方式二:
HttpSession session = request.getSession();
- 等价于 request.getSession(true)。
request.getSession();
(4) 保存状态相关的几个方法
- setAttribute(String name,Object obj);–绑定数据
- Object getAttribute(String name);–获取数据
- removeAttribute(String name);–解除绑定
(5) session超时
-
什么是session超时?
- 服务器会将空闲时间过长的session对象删除掉。
- 注:这样可以节省内存空间。一般空闲时间长度为半个小时左右。
-
修改超时时间长度
-
方式一:
在Servers的web.xml文件中修改以下参数(单位是分钟):(一般没有必要去修改!)
<session-config> <session-timeout>30</session-timeout> </session-config>
-
方式二:
session.setMaxInactiveInterval(int seconds);
- 两次请求之间的间隔如果超过指定的时间(单位是秒),则session对象被销毁。
-
(6) 删除session
- session.invalidate();(invalidate–失效)
session验证(案例)
- step1.登录成功之后,将一些数据绑订到session对象之上,比如:
session.setAttribute("user",user);
- step2.如果请求涉及到需要保护的资源(即只有登录成功之后才能访问的资源,比如用户列表),则进行session验证。比如:
Object obj = session.getAttribute("user");
if(obj == null){
response.sendRedirect("login.jsp");
}
九、过滤器(上)
1.比较cookie与session
-
cookie的优点是不占用服务器端的内存资源,
-
session的优点是安全,并且可以保存更丰富的数据类型。
2.验证码
protected void service(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
System.out.println("CheckcodeServlet's service()");
/*
* 绘图
*/
//step1.创建一个内存映像对象(画布)
//三个参数为,画布的宽度,高度,类型
BufferedImage image=new BufferedImage(80,30,BufferedImage.TYPE_INT_RGB);
//step2.获得画笔
//导包的时候导awt的包
Graphics g=image.getGraphics();
//step3.给笔设置颜色
g.setColor(new Color(255,255,255));
//step4.给画布设置背景颜色(左上角的左边距,左上角的右边距,背景宽,背景高)
g.fillRect(0, 0, 80, 30);
//step5.重新给笔设置颜色和字体大小
Random r=new Random();
g.setColor(new Color(r.nextInt(255),r.nextInt(255),r.nextInt(255)));
//Font(字体类型,风格,大小)
g.setFont(new Font(null, Font.ITALIC|Font.BOLD, 24));
//step6.生成number(验证码)
String number=""+r.nextInt(10)+r.nextInt(10)+r.nextInt(10)+r.nextInt(10)+r.nextInt(10);
//step7.将验证码添加到图片里面(字符串,左下角的左边距,左下角的上边距)
g.drawString(number, 5, 25);
//step8.加一些干扰线
for(int i=0;i<8;i++) {
//设置干扰线颜色
g.setColor(new Color(r.nextInt(255),r.nextInt(255),r.nextInt(255)));
//设置干扰线位置
g.drawLine(r.nextInt(80), r.nextInt(30), r.nextInt(80), r.nextInt(30));
}
/*
* 将图片压缩,发送给浏览器
*/
//step1.设置MEMI类型(告诉浏览器,
//服务器返回的数据类型是什么,这里返回
//的是一张jpeg格式的图片)
response.setContentType("image/jpeg");
//step2.获得字节输出流
OutputStream output=response.getOutputStream();
//step3.压缩图片并输出
//(原始图片对象,压缩成的格式,输出流)
javax.imageio.ImageIO.write(image, "jpeg", output);
}
3.过滤器
(1) 什么是过滤器?
- servlet规范当中定义的一种特殊的组件,用来拦截容器的调用过程。
- 容器收到请求之后,会调用过滤器,再调用servlet。
(2) 如何写一个过滤器?
-
step1.写一个java类,实现Filter接口。
-
step2.在doFilter方法里面,实现拦截处理逻辑。
-
step3.配置过滤器。(web.xml)
练习:写一个过滤器,检查评论的字数,如果超过 20个字,则提示“字数过多”。
十、过滤器(下)、监听器
(3) 过滤器的优先级
- 当有多个过滤器都满足拦截要求,则依据配置的先后顺序来执行。
(4) 初始化参数
-
step1.配置初始化参数。
-
step2.调用FilterConfig对象的 getInitParameter() 方法来获取初始化参数值。
<!-- 配置初始化参数 --> <init-param> <param-name>illegal</param-name> <param-value>猫</param-value> </init-param>
(5) 过滤器的优点
-
a.可以在不修改原有代码的基础上,增加新的功能。
-
b.可以将多个组件相同的逻辑集中写在一个类里面(即过滤器里面),方便代码的维护。(比如session验证功能)
2.监听器
(1) 什么是监听器?
- servlet规范当中定义的一种特殊的组件,用来监听容器产生的事件并进行相应的处理。
容器主要会产生两大类事件:
-
a.生命周期相关的事件:
容器创建或者销毁了request,session,servlet上下文时产生的事件。
-
b.绑订数据相关的事件:
调用了request,session,servlet上下文的setAttribute,removeAttribute时产生的事件。
(2) servlet上下文
- 容器启动之后,会为每一个应用创建唯一的一个符合ServletContext接口要求的对象,该对象一般称之为servlet上下文。
- 特点:
- a.唯一性:一个应用对应一个servlet上下文;
- b.持久性:只要容器不关闭,应用没有被卸载,则servlet上下文会一直存在。
- 如何获取servlet上下文?
- GenericServlet、ServletConfig、FilterConfig、HttpSession都提供了一个方法:getServletContext()
- servlet上下文的作用:
- a.绑订数据(类似于request、session)
- setAttribute(String name,Object obj)
- Object getAttribute(Sting name)
- removeAttribute(String name)
- b.读取全局的初始化参数。
- a.绑订数据(类似于request、session)
注1:
request,session和servlet上下文都提供了绑订数据相关的方法,
从生存时间的长度来看:servlet上下文 > session >request;
在满足使用条件的情况下,应该优先使用生命周期短的。
注2:如果数据需要所有用户共享,应该绑订到servlet上下文对象上。
注3:同一个应用中的所有组件(servlet,filter)都可以访问的初始化参数。
- step1.配置初始化参数;
- step2.调用ServletContext提供的getInitParameter方法来读取初始化参数值。
<!-- 配置全局的初始化参数 -->
<context-param>
<param-name>company</param-name>
<param-value>A公司</param-value>
</context-param>
(3) 如何写一个监听器?
-
step1.写一个java类,实现相应的监听器接口。
注:要依据监听的事件类型来选择实现相应的接口,比如,要监听session对象的创建和销毁,可以实现HttpSessionListener接口。
-
step2.在接口方法当中,实现监听处理逻辑。
-
step3.配置监听器。(web.xml)(没有先后顺序)
统计在线人数:
/**
* session对象创建之后,容器会调用
* sessionCreated方法。
* se:事件对象
*/
public void sessionCreated(HttpSessionEvent se) {
System.out.println("sessionCreated()");
//获得servlet上下文
ServletContext sctx=se.getSession().getServletContext();
//通过上下文获得绑定人数
Integer count=(Integer)sctx.getAttribute("count");
if(count==null) {
//第一个用户
count=1;
}else {
//不是第一个用户,人数加一
count++;
}
//绑定最新的人数到上下文
sctx.setAttribute("count", count);
}
/**
* session对象销毁之后,容器会调用
* sessionDestroyed方法。
*/
public void sessionDestroyed(HttpSessionEvent se) {
System.out.println("sessionDestroyed()");
ServletContext sctx=se.getSession().getServletContext();
Integer count=(Integer)sctx.getAttribute("count");
count--;
sctx.setAttribute("count", count);
}
3.实现一个简单的web缓存 (扩展)
基本思路:
-
将一些常用的、不怎么发生变化,并且数据量不大的数据从数据库中查询出来,并存放到内存里面。
-
可以写一个监听器,实现servletContextListener接口,在上下文创建时,查询数据库,并且将查询到的数据绑订到上下文上。
-
step1.建表 t_role
create table t_role(
id int primary key,
name varchar(20)
);
insert into t_role values(1,'管理员')
insert into t_role values(2,'版主');
insert into t_role values(3,'用户');
-
step2.实体类 Role
-
step3.DAO类 RoleDAO
List findAll();
- step4.监听器类 CacheListener
/* 伪代码 */
public class CacheListener implements ServletContextListener {
contextInitialized(){
List list = RoleDAO.findAll...
sctx.setAttribute("",list)
}
}
注意:可以绑定数据的4个作用域里分别是
pageContext、request、session、ServletContext
十一、Servlet线程安全、jsp基础、EL
1.Servlet线程安全问题
(1) 为什么说Servlet会有线程安全问题?
- a.容器只会创建一个Servlet实例。
- b.容器收到一个请求,就会启动一个线程来处理该请求,这样,就可能会有多个线程同时调用同一个对象,就有可能产生线程安全问题(比如,这些线程同时去修改某个属性)。
(2) 如何解决?
- 将有可能产生线程安全问题的代码使用synchronized加锁。
- a.使用synchronized加锁对性能会有一些影响。不要对整个方法加锁,而是对有可能产生线程安全问题的代码块加锁。
- b.尽量不要修改属性。
2.JSP基础
(1) jsp是什么?
- JSP全称Java Server Pages,是一种动态网页开发技术。它使用JSP标签在HTML网页中插入Java代码。
(2) 如何写jsp?
-
step1.写一个以.jsp为后缀的文件。
-
step2.添加:
-
- html (css,js) 直接写即可。
-
- java代码。
- a.java代码片断 <% java代码 %>
- b.jsp表达式 <%= java表达式 %>
- c. jsp声明 (a1.jsp) <%! 声明变量或者方法 %>
-
- 指令
-
a.什么是指令?
告诉容器,在将jsp转换成servlet时,做一些额外的处理,比如导包。
-
b.语法 <%@ 指令名 属性=值 %>
-
c.page指令
import属性:导包
pageEncoding属性:jsp文件的编码。
contentType属性:设置setContentType的内容。
session属性:缺省值(默认值)是true,如果值是false,则不能够使用session隐含对象。 (a2.jsp)
errorPage属性:指定一个异常处理页面。
(注: 当jsp运行发生了异常,则容器会调用异常处理页面。(a3.jsp))
isErrorPage属性:缺省值是false,如果值为true,则可以使用exception隐含对象。
- 使用exception.getMessage()获得错误信息。(a4.jsp)
-
d.include指令
告诉容器,在将jsp转换成servlet时,将file属性指定的文件的内容插入到该指令所在的位置。
-
e. taglib指令
jsp标签技术中,用来引入某个标签。
-
- 隐含对象
a.什么是隐含对象? 可以直接使用的对象。
b.为什么可以直接使用这些隐含对象? 容器会自动添加获得这些对象的代码。
c.有哪些隐含对象?
-
out,request,response
-
session,application
-
exception
-
pageContext: 页面上下文
容器会为每一个jsp实例创建唯一的一个符合PageContext接口要求的对象,该对象会一直存在,除非jsp实例被删除。
-
作用1:绑订数据 (a6.jsp,a7.jsp) setAttribute、getAttribute、removeAttribute
(注:绑订到pageContext上的数据,只有对应的jsp实例能访问。)
-
作用2:获得其它所有隐含对象
(注:该对象提供了获得其它隐含对象的方法。)
-
-
config: 相当于ServletConfig,通过它来读取初始化参数 (a5.jsp)
-
page: jsp实例本身(过度设计,从来不用)
(注: jsp对应的那个servlet实例。)
-
- 注释 (a8.jsp)
a. 如果注释的内容是java代码,java代码会执行,但是不会显示。
b. <%-- 注释内容 --%> 如果注释的内容是java代码,不会执行。
-
(3) JSP是如何执行的?
-
step1. JSP要转换成一个servlet
- html -----> service方法里,使用out.write输出。
- <% %> —> service方法里,照搬。
- <%= %> --> service方法里,使用out.print
- <%! %> —> 增加新的属性和方法。
-
step2.调用servlet。
3.jsp标签和el表达式
(1) jsp标签是什么?
- jsp标签语法类似于html标签,用于替换jsp文件中的java代码。
- a.因为直接在jsp文件当中写java代码,不利于jsp文件的维护(比如,将带有java代码的jsp文件交给美工去修改就很不方便)。所以,sun才制订了jsp标签技术规范。
- b.使用jsp标签来替换java代码,一方面jsp文件会变得简洁,利于代码的维护与复用,另外一方面,也利于美工去修改。
(2) el表达式是什么?
- 一套简单的运算规则,用于给jsp标签的属性赋值,也可以脱离jsp标签直接运行。
(3) el表达式的使用
1) 访问bean的属性
补充:一个类,满足如下几个条件,就可以称之为一个javabean:
要求:
public class
有无参构造器
实现序列化接口(不是强制要求)
有一些属性
有对应的get/set方法
-
方式一:(e1.jsp)
- ${user.username}
- a.执行过程:容器依次从pageContext-> request -> session -> application 中查找绑订名为"user"的对象 (即getAttribute),如果找到了,则调用该对象的"getUsername"方法并输出。
- b.优点:会将null转换成""(空字符串)输出。如果找不到对应的对象,不会报空指针异常,而是输出""(空字符串)。但是不能把方法名写错,否则会报错。
- c.指定查找范围:可以使用pageScope,requestScope、sessionScope、applicationScope 指定查找范围,比如:${sessionScope.user.username}
-
方式二:
-
${user[‘username’]} 等价于 ${user.username}
-
[]里面允许出现绑订名,比如 ${user[str]}。找不到则输出空字符串"" ${user[requestScope.str]}
-
[]里面还可以出现从0开始的下标,用于访问实体类属性数组中的某个元素。例:
<%request.setAttribute("str", "username");%> username:${user[str] }
-
2) 做一些简单的运算 (e2.jsp)
-
运算的结果可以用来给jsp标签的属性赋值,也可以直接输出。
-
a.算术运算 +,-,*,/,%,其中"+"只能求和。不能做字符串链接,但是可以计算”1”+”1”(结果为2)
-
b.关系运算 >,>=,<,<=,==,!=(结果为true或false)
==可以用来判断字符串是否相同。如果不指定查找范围就会依次查找。
-
c.逻辑运算 &&,||,!
-
d.empty运算 empty:判断是否为一个空字符串或者是否为一个空的集合。例:
<% request.setAttribute("s2", ""); %> 空字符串:${empty s2 }<br/><!-- true --> 空的集合:${empty list1 }<br/><!-- true --> 值为null:${empty null}<br/><!-- true --> 找不到对应的值:${empty abc }<br/><!-- true -->
-
十二、JSTL、自定义标签
3) 读取请求参数值 (e1.jsp)
- ${param.username} 等价于 request.getParameter(“username”);
- ${paramValues.interest} 等价于 request.getParameterValues(“interest”);
jstl (java standard tag lib 即JAVA标准标签库)
(1)jstl是什么?
- apache开发的一套通用的jsp标签,后来捐献给了sun,sun将其命名为jstl。
(2) 如何使用jstl?
-
step1.导入jstl相关的jar包。
-
step2.使用taglib指令引入要使用的jsp标签。
uri属性:指定要引入的jsp标签的命名空间。
- 命名空间:为了区分同名的元素而在元素前添加的一个限定(通常是一个域名)。
prefix属性:命名空间的别名。
(3) 几个核心标签
1) if标签(e3.jsp)
<c:if test="" var="" scope="">标签体</c:if>
- 当
test属性
值为true时,容器会执行标签体的内容。 test属性
,可以使用el表达式赋值来作为绑定值。var属性
,用来指定绑订名。scope属性
用来指定绑订范围(“page”,“request”,“session”,“application”)。例:
性别:
<c:if test="${user.gender=='m' }" var="rs" scope="page">男</c:if>
<c:if test="${!rs }">女</c:if>
2) choose标签 (e4.jsp)
<c:choose>
<c:when test="">
</c:when> <!--可以出现1到多次-->
<c:otherwise>
</c:otherwise> <!--可以出现0或一次-->
</c:choose>
- when可以出现1次或者多次,表示一个分支;
- 当test属性值为true时,执行标签体的内容;
- otherwise可以出现0次或者1次,表示例外。
注意:这两个标签不能独立使用,必须要嵌套在choose标签中才有意义!
例:
<c:choose>
<c:when test="${u.gender=='m'}">男</c:when>
<c:when test="${u.gender=='f'}">女</c:when>
<c:otherwise>保密</c:otherwise>
</c:choose>
3) forEach标签 (e5.jsp)
-
用于遍历集合或者数组。
-
a.语法:
<c:forEach items="" var="" varStatus=""> </c:forEach>
-
b.items属性用来指定要遍历的集合或者数组,可以使用el表达式来赋值。
-
c.var属性用来指定绑订名(绑订范围固定是pageContext,该标签每次从集合或者数组当中取一个元素,然后绑订到pageContext上)。
-
d.varStatus属性也是用来指定绑定名(绑订范围固定是pageContext,绑订值是一个特殊对象,该对象由该标签创建,提供了几个方法用来获得当前遍历的状态,比如,
- getIndex():获得当前正在被遍历的元素的下标(从0开始)
- getCount():获得当前是第几次被遍历(从1开始)例:
<c:forEach items="${users }" var="u" varStatus="s">
<tr class="row${s.index%2+1 }">
<td>${u.id }</td>
<td>${u.uname }</td>
<td>${u.pwd }</td>
<td>${u.phone }</td>
<td><a href="delUser.do?id=${u.id }"
οnclick="return confirm('确定删除${u.uname}吗?')">删除
</a> </td>
</tr>
</c:forEach>
补充:容器依据标签的命名空间找到标签的描述文件(.tld文件),然后依据标签的名称找到对应的标签类,然后将该类实例化,并且调用该实例对应的方法。
注意:如果是更改项目名,要记得右键项目名称,然后更改web project settings的值为相应的项目名称。这个web project settings是你访问服务器上你部署的应用的应用名默认情况下 不指定这个的话应用名和你的工程名是一样的指定了之后 就是以这个名字为准了。
2.自定义标签
(1) 编程步骤
-
step1.写一个java类,继承SimpleTagSupport类。
- 简单标签技术(new),继承SimpleTagSupport类来开发。
- 复杂标签技术(old)
-
step2.override doTag方法,在该方法里面,编写处理逻辑。
-
step3.描述标签(.tld文件)
<body-content>JSP</body-content>
- body-content的值可以是
empty
、scriptless
、JSP
。 empty
: 该标签没有标签体。scriptless
: 该标签可以有标签体,但是标签体的内容不能够出现java代码(<% %>,<%= %>,<%! %>)JSP
:该标签可以有标签体,并且标签体可以出现java代码。只有复杂标签技术才支持该值。
- body-content的值可以是
案例:
-
java标签类:
/** * 自定义标签,用来输出系统的当前时间 * * @author ACGkaka */ public class SysdateTag extends SimpleTagSupport { //设置默认格式 private String format="yyyy/MM/dd HH:mm:ss"; //设置format的get方法 public String getFormat() { return this.format; } //设置format的set方法 public void setFormat(String format) { this.format=format; } //重写doTag方法来实现具体的功能 @Override public void doTag() throws JspException, IOException { //创建服务器时间 Date date=new Date(); //格式化时间 SimpleDateFormat sdf=new SimpleDateFormat(format); String now=sdf.format(date); //将时间输出给浏览器 //PageContext extends JspContext //该方法声明返回JspContext //但在实现时返回的是PageContext //所以可以将其强转为PageContext //从而获得其他8个隐含对象 PageContext ctx=(PageContext)getJspContext(); JspWriter out=ctx.getOut(); out.println(now); //注意:此处一定不能关闭流,因为其他的标签 //也要用这个流 super.doTag(); } }
-
s.tld文件:
<?xml version="1.0" encoding="UTF-8" ?> <taglib xmlns="http://java.sun.com/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-jsptaglibrary_2_1.xsd" version="2.1"> <description>这是我自己的标签库</description> <display-name>My tag</display-name> <tlib-version>3.1</tlib-version> <short-name>s</short-name> <uri>/my-tag</uri> <tag> <description>用来输出服务器的时间</description> <name>sysdate</name> <tag-class>web.SysdateTag</tag-class> <!-- 声明该标签可以包含哪些内容 --> <!-- empty: 该标签没有标签体。 scriptless: 该标签可以有标签体,但是标签体 的内容不能够出现java代码(<% %>, <%= %>,<%! %>)。 JSP:该标签可以有标签体,并且标签体可以出现 java代码。只有复杂标签技术才支持该值 --> <body-content>empty</body-content> <!-- 以下是标签的属性设置 --> <attribute> <description>用来设置时间的格式</description> <name>format</name> <!-- 设置是否必须给这个属性赋值 --> <required>false</required> <!-- 设置是否可以用EL表达式给次属性赋值 --> <rtexprvalue>true</rtexprvalue> </attribute> </tag> </taglib>
-
应用:
<%@ taglib uri="/my-tag" prefix="s" %> <s:sysdate format="yyyy-MM-dd" />
十三、MVC
1.MVC (Model 模型,View 视图,Controller 控制器)
(1) MVC模式
- 是一个经典的设计模式,是软件的分层思想:
- M:Model,即业务层,用来处理业务;
- V:View,即视图层,用来处理数据;
- C:Controller,即控制层,是业务层和视图层的桥梁。
- 它可以降低软件中代码的耦合度,便于团队开发及维护。
(2) 如何使用MVC?
- 在web开发当中,我们经常使用Servlet充当控制器,jsp充当视图,java充当模型。它们的关系如下图所示:
(3) MVC的优点
-
a.模型返回的处理结果,可以使用不同的视图来展现。
-
b.方便测试
- 比如将业务逻辑写在java类里面,可以直接测试。
- 如果写在Servlet里面,需要部署之后才能测试。
-
c.方便代码的维护,修改模型或者视图,彼此不受影响。
注意:
使用MVC,会增加代码量,会增加软件的成本,也会增加设计的难度。所以,只有一定规模的软件才需要使用MVC。
案例:
NETCTOSS–中国电信运营支持系统-网络版
注意:浏览器访问服务器获得网页,以及加载网页的过程中包含多次请求
2.实现一个简单的MVC框架
(1) 基本架构
十四、smartmvc
1.smartmvc架构
2.使用smartmvc
-
step1.导包 dom4j
-
step2.将base包下面的common,web包拷贝到工程里面。(这两个包是smartmvc的核心包)。
-
step3.配置DispatcherServlet。
(注:主要是指定配置文件的位置。)
-
step4.添加处理器类,比如,添加LoginController
-
a.方法前添加@RequestMapping。
-
b.方法的返回值是一个字符串(即视图名,默认会转发),如果是重定向,前面要加"redirect:"。
-
-
step5.在配置文件(比如context.xml)中添加处理器的配置信息。
(注:要写完整的类名。)
-
step6.添加jsp文件。
- a.要添加到WEB-INF下。
- b.文件名要等于视图名。