jsp

jsp & servlet 精华一页纸

2017-04-11  本文已影响44人  轩居晨风

servlet/jsp 本质上是一种java编程协议/规范, 存在于web容器中,由web容器解析,管理其生命周期。

1、容器

一个典型简单容器的实现:

实现一个Java程序Application

使用Socket监听一个端口

解析url/解析协议

发送数据给客户端

容器处理请求的典型过程:

I、客户请求

II、Web容器接受到请求,把相关信息封装成两个对象 HttpServletRequest/HttpServletResponse

III、根据请求的URL找到具体的servlet,并为这个请求创建一个线程,把请求和响应对象传递给这个servlet线程

IV、容器调用 servlet提供的service方法(doGet、doPost)

V、servlet把处理结果设置到 响应对象中去

VI、线程结束,容器把响应翻译并返回给客户端

2、典型应用

I、部署结构

web-app

WEB-INF

web.xml

classes

html/jsp

II、web.xml

两个部分:处理的servlet  | 拦截的URL路径

servletA

xxx.xxx.ServletA 

servletA

/

首先定义 servlet 名称,并且映射到具体的代码路径

其次定义 servlet 名称,到url响应的服务名

III、Servelt实现

public class ServletA extends HttpServlet {

protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {

String c = request.getParameter("xxx"); -- 获取参数

MyObject o = new MyObject-- Model 部分

request.setAttribute("styles", o); -- View 部分

RequestDispatcher view = request.getRequestDispatcher("result.jsp");

view.forward(request, response);-- 转发请求

}

}

注意标红的这段代码, 这是很多 第三方MVC 框架(比如 Spring MVC)的核心基础, 从这里开始是第一步,基于此不断构建了一个庞大的框架体系。

3、Servlet详解

继承体系

servlet接口 -> genericservlet抽象类 -> HttpServlet

ServletRequest(接口) - HttpServletReqeust(接口)

ServletResponse(接口) - HttpServletResponse

生命周期- httpServlet 接口方法

启动 - init: servlet创建后,服务前执行

服务 - service doGet/doPost:应用覆盖执行

销毁 - destory:释放资源

数据交互

客户端请求数据 - HttpRequest : 参数 getParameter | 头部 getHeader | 缓存 getCookies ...

返回客户端数据 - HttpResponse: 头部 setHeader | 输出流 getOutputStream ...

系统全局参数 - servletConfig | servletContext

监听与全局参数

全局参数 与 的区别

是整个webApp 级别的参数,给所有Servlet使用

是Servlet级别的参数,给当前Servlet的 多个实例使用

另外,纠正网上 以讹传讹的一个错误,init-param并不是只能在 init方法中使用,而是从 init 方法的生命周期之后才能使用,具体说来就是 构造函数里不能使用,直到容器调用init方法开始才能使用

监听

web.xml

com.example.MyServletContextListener

因为监听类扩展的接口 ServletContextListener ,可以获取全局对象 ServletContext,因而可以做一些全局的准备工作,把对应的结果缓存到全局对象中;典型的就是数据库链接

几个注意点:

a、请求幂等性的问题, 非查询性的请求处理时要注意检查,防止重复执行

b、请求重定向与请求分派的区别:

请求分派在服务端发生,分派是由服务器的一个组件传递给另一个组件

重定向在客户端发生。服务器发送一个 301 状态的响应给浏览器,并在头部有一个location的参数,包含新的URL,浏览器根据新的URL重新请求,在客户端浏览器能够看到变化

4、会话管理

http协议是无状态协议 ,如何缓存状态,如何跟踪用户的访问

思考一下, 要跟踪一个客户, 首先需要标识一个客户; 其次这个标识在交互中要一直携带,如何携带

I、生成这个标识

方法一: 本地 自己生成 - 比如非浏览器的http请求

方法二:服务端 在初次访问时生成一个唯一标识 request.getSession(); session.getId();

II、携带这个标识

方法一:客户端在发送请求的 cookie中携带这个标识; 服务端在发送相应时 set-Cookie (除了设置标识外,cookie 可以存储更多的数据)-- 这部分工作是容器在处理

方法二:通过 URL 重写,response.encodeURL("SessionTest.do") -- 原理就是http请求携带一个 参数jsessionid

方法三:通过表单 hidden 域,这个已不推荐使用

会话的生命周期

创建 : 创建会话 getSession

变化 :增加属性 setAttribute | 删除属性 removeAttribute

销毁 :销毁会话 Invalidate

迁移 :如果是分布式商用容器(比如weblogic/websphere),会把一台设备上的会话钝化,然后迁移到另一台设备上

更多关于 session 的讨论,参见本人写的另一片文章 《Session 一页纸精华》

5、JSP详解 - java裸编码

jsp本质是由容器转化成Servlet来处理,目的是减少写 html 脚本的工作量。如果使用的是tomcat,可以到work目录下查找对应jsp的class文件

引子- 既然是java代码, 那按照java代码的过程和元素,描述一下 jsp的使用

a、引入 包 -- page 指令

<%@ page import="java.util.*,java.io.*" %> 多个包用逗号分隔

b、定义Servlet类

-- 因为 类是自动生成的,所以这一步是容器生成的,跳过

c、定义域、属性、全局变量 -- 声明

在 _jspService之前执行

<%!

int count = 0;

%>

d、使用表达式片段 - scriptlet

因为所有的jsp最终都翻译成 _jspService 方法调用, 所以一般在 scriptlet中不能定义方法,如果非要定义的话,可以在声明里面定义,因为这部分内容不在方法内

<%

out.println(new Counter().getCount());

%>

表达式 -- 容器会调用表达式返回值(必须有返回值),通过out输出

<%=new Counter().getCount()%>

e、注释

<%-- --%>

最常用的例子,表格取数据迭代

<% for(int i = 0 ; i < 5; i++){ %>

<%=i%>

<% } %>

最终在 html页面中,就可以灵活插入各个java代码执行的片段,把其中的html变成 outputStream的输出

生命周期

创建 : 容器接受请求,翻译成 servlet的源代码,然后加载,实例化,最终变成一个Servlet

处理 : 执行service方法

销毁 : 容器执行 _jspDestroy方法

部署与全局(隐式)参数

部署和Servlet一样,只是Servlet-class 换成了 /testjsp.jsp

同Servlet一样, jsp 也拥有Servlet所有的全局参数, request | session | config | application(对应ServletContext) | out 等等对应Servlet各个部分

6、JSP进阶 - JavaBean + EL表达式

从上面table的例子可以看到,直接Java编程是非常low的事情,逻辑混乱,而且不利于解耦,前端和业务人员无法界定。所以JSP又演进了一步,不写Java的Scriptlet代码

I、使用JavaBean绑定

name:

ID#:

-- 因为JavaBean属性和表单名称

--一致,使用了通配符

II、再加上 EL 表达式

${xxx.xxx} -- 左边是隐式(param,scope,cookie..)对象、map或者bean,右边是属性

${xxx["xxx"} --可以适用于map,数组,或者bean等对象

[] 方式可以嵌套表达式,括号中的内容如果没有使用引号,容器会进行计算,否则会可能成键值等

比如request里面有 map和array两个对象

request.setAttribute("map", map);

request.setAttribute("array", array);

EL:${map[array[0]]}

tag标签 - 可以通过表达式使用 java代码中的静态方法

a、提供java静态方法

b、在WEB-INF中提供 *.tld 文件,标注方法和参数

c、在jsp中引用静态方法

<%@ taglib prefix="mine" uri="DiceFunctions" %> --mine只是一个别名而已

${mine:rollIt()}

可以设置模版值,替换另一个文件的表达式,以达到模板化的目的。

EL表达式和JavaBean绑定只解决了取值问题,虽然动态标记可以解决一些逻辑控制问题,但都是固化的模式,并没有能灵活使用。怎么解决Scriptlet代码在页面中的控制问题?

答案是 JSTL - 具体参加本人博客 《标签模版技术 JSTL 一页纸精华》

7、过滤器

过滤器是和Servlet平级的另一种处理过程,在请求到达处理Servlet之前经过一些列过滤,这里体现了 面向切面的思想,虽然没有采用AOP的技术。

I、典型例子

a、声明过滤器

在web.xml(DD)中定义,定义方法和Servlet类似

FilterA

com.example.web.FilterA

BeerRequest

/ -- 也可以指定servlet SessionTest

REQUEST -- 可以指定拦截的资源类型,REQUEST 客户端请求,INCLUDE通过include调用分派的请求,

FORWARD 通过forward调用分派来的请求,ERROR 错误处理器调用的请求

b、编写过滤器

public class FilterA implements Filter {

public void doFilter(ServletRequest req, ServletResponse resp,

FilterChain chain) throws IOException, ServletException {

处理代码

chain.doFilter(req, resp);

处理代码

}

}

II、生命周期

启动 : init 提供FilterConfig对象

服务 : doFilter()中处理,提供三个对象 ServletRequest/ServletRequest/FilterChain

销毁 : destroy 方法

III、过滤器的概念栈

a、调用第一个过滤器a的doFilter方法,直到 chain.doFilter方法

b、调用第n个过滤器n的doFilter方法,直到 chain.doFilter方法

c、调用真实Servlet的 service 方法,执行完返回

d、容器把控制器返回第n个过滤器的doFilter方法,chain.doFilter之后的内容

e、容器把控制器返回第1个过滤器的doFilter方法,chain.doFilter之后的内容

f、容器完成整个流程响应

这里,有一个最有名的应用,struts2 - 基于过滤器拦截, 进行的流程处理,而没有使用servlet这种经典流程

上一篇下一篇

猜你喜欢

热点阅读