JavaWeb学习笔记(三)
8、JSP
8.1、什么是JSP
Java Server Pages : Java服务器端页面,也和Servlet一样,用于动态Web技术!
特点:
- 写JSP就像在写HTML
- 区别:
- HTML只给用户提供静态的数据
- JSP页面中可以嵌入JAVA代码,为用户提供动态数据
8.2、JSP原理
浏览器向服务器发送请求,不管访问什么资源,其实都是在访问Servlet!
JSP最终也会被转换成为一个Java类!
JSP 本质上就是一个Servlet
//初始化
public void _jspInit() {
}
//销毁
public void _jspDestroy() {
}
//JSPService
public void _jspService(.HttpServletRequest request,HttpServletResponse response)
-
判断请求
-
内置一些对象
final javax.servlet.jsp.PageContext pageContext; //页面上下文 javax.servlet.http.HttpSession session = null; //session final javax.servlet.ServletContext application; //applicationContext final javax.servlet.ServletConfig config; //config javax.servlet.jsp.JspWriter out = null; //out final java.lang.Object page = this; //page:当前 HttpServletRequest request //请求 HttpServletResponse response //响应
-
输出页面前增加的代码
response.setContentType("text/html"); //设置响应的页面类型 pageContext = _jspxFactory.getPageContext(this, request, response, null, true, 8192, true); _jspx_page_context = pageContext; application = pageContext.getServletContext(); config = pageContext.getServletConfig(); session = pageContext.getSession(); out = pageContext.getOut(); _jspx_out = out;
以上的这些个对象我们可以在JSP页面中直接使用!
8.3、JSP基础语法
- jsp表达式
<%--JSP表达式
作用:用来将程序的输出,输出到客户端
<%= 变量或者表达式%>
--%>
<%= new java.util.Date()%>
- jsp声明(全局变量和全局函数),默认生成在_jspService()中
<%!
static {
System.out.println("该类已被访问");
}
private int globalVar = 0;
public int getGlobalVar(){
return globalVar;
}
%>
- jsp脚本片段
<%--jsp脚本片段--%>
<%for (int i = 0; i < 5; i++) {
out.println(getGlobalVar());
%>
<h1>hello world <%= i%></h1>
<% } %>
- JSP的注释,不会在客户端显示,HTML就会!
<%--注释--%>
8.4、JSP指令
<%@page args.... %>
<%@include file=""%>
<%--@include会将两个页面合二为一--%>
<%@include file="common/header.jsp"%>
<h1>网页主体</h1>
<%@include file="common/footer.jsp"%>
<hr>
<%--jSP标签
jsp:include:拼接页面,本质还是三个
--%>
<jsp:include page="/common/header.jsp"/>
<h1>网页主体</h1>
<jsp:include page="/common/footer.jsp"/>
8.4、9大内置对象和其作用域
- 9大内置对象
PageContext 存东西
Request 存东西
Response
Session 存东西
Application 【SerlvetContext】 存东西
config 【SerlvetConfig】
out
page ,不用了解
exception
- 四大作用域
public static final int PAGE_SCOPE = 1;
public static final int REQUEST_SCOPE = 2;
public static final int SESSION_SCOPE = 3;
public static final int APPLICATION_SCOPE = 4;
- 存数据
<%--存数据--%>
<%
pageContext.setAttribute("pageContextName","pageContext");//保存的数据在当前页面有效
request.setAttribute("requestName","request");//保存的数据在一次请求中有效,请求转发也会携带这个数据
session.setAttribute("sessionName","session");//保存的数据在一次会话中有效,从打开浏览器到关闭浏览器
application.setAttribute("applicationName","application");//保存的数据在服务器中有效
%>
//源码
public void setAttribute (String name,Object attribute,int scope){
switch (scope) {
case PAGE_SCOPE:
mPage.put (name, attribute);
break;
case REQUEST_SCOPE:
mRequest.put (name, attribute);
break;
case SESSION_SCOPE:
mSession.put (name, attribute);
break;
case APPLICATION_SCOPE:
mApp.put (name, attribute);
break;
default:
throw new IllegalArgumentException ("Bad scope " + scope);
}
}
- 取数据,从作用域最小的开始找,如果存的时候有Key是同名的,数据将会取不出来
<%
String pageContextName = (String) pageContext.findAttribute("pageContextName");
String requestName = (String) request.getAttribute("requestName");
String sessionName = (String) session.getAttribute("sessionName");
String applicationName = (String) application.getAttribute("applicationName");
%>
//源码
//从作用域最小的开始找
public Object findAttribute (String name){
if (mPage.containsKey (name)) {
return mPage.get (name);
}
else if (mRequest.containsKey (name)) {
return mRequest.get (name);
}
else if (mSession.containsKey (name)) {
return mSession.get (name);
}
else if (mApp.containsKey (name)) {
return mApp.get (name);
}
else {
return null;
}
}
8.5、JSTL标签、EL表达式
- 相关依赖
<!-- JSTL表达式的依赖 -->
<dependency>
<groupId>javax.servlet.jsp.jstl</groupId>
<artifactId>jstl-api</artifactId>
<version>1.2</version>
</dependency>
<!-- standard标签库 -->
<dependency>
<groupId>taglibs</groupId>
<artifactId>standard</artifactId>
<version>1.1.2</version>
</dependency>
- EL表达式: ${ }
- 获取数据
- 执行运算
- 获取web开发的常用对象
- JSP标签
<%--jsp:include--%>
<%--
http://localhost:8080/jsptag.jsp?name=thomas&age=18
--%>
<jsp:forward page="/jsptag2.jsp">
<jsp:param name="name" value="thomas"></jsp:param>
<jsp:param name="age" value="18"></jsp:param>
</jsp:forward>
- JSTL标准标签库
JSTL标签库的使用就是为了弥补HTML标签的不足;它自定义许多标签,可以供我们使用,标签的功能和Java代码一样
- JSTL标签库使用步骤
- 引入对应的 taglib
- 使用其中的方法
- 在Tomcat 也需要引入 jstl的包,否则会报错:JSTL解析错误
- 测试代码
- c:if
<h4>if测试</h4>
<hr>
<form action="coreif.jsp" method="get">
<%--
EL表达式获取表单中的数据
${param.参数名}
--%>
<input type="text" name="username" value="${param.username}">
<input type="submit" value="登录">
</form>
<%--判断如果提交的用户名是管理员,则登录成功--%>
<c:if test="${param.username=='admin'}" var="isAdmin">
<c:out value="管理员欢迎您!"/>
</c:if>
<%--自闭合标签--%>
<c:out value="${isAdmin}"/>
- c:choose c:when
<body>
<%--定义一个变量score,值为85--%>
<c:set var="score" value="55"/>
<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:choose>
</body>
- c:forEach
<%
ArrayList<String> people = new ArrayList<>();
people.add(0,"张三");
people.add(1,"李四");
people.add(2,"王五");
people.add(3,"赵六");
people.add(4,"田六");
request.setAttribute("list",people);
%>
<%--
var , 每一次遍历出来的变量
items, 要遍历的对象
begin, 哪里开始
end, 到哪里
step, 步长
--%>
<c:forEach var="people" items="${list}">
<c:out value="${people}"/> <br>
</c:forEach>
<hr>
<c:forEach var="people" items="${list}" begin="1" end="3" step="1" >
<c:out value="${people}"/> <br>
</c:forEach>
9、JavaBean
实体类
JavaBean有特定的写法:
- 必须要有一个无参构造
- 属性必须私有化
- 必须有对应的get/set方法;
一般用来和数据库的字段做映射 ORM;
ORM :对象关系映射
- 表—>类
- 字段–>属性
- 行记录---->对象
详细参考JavaBean菜鸟教程
10、MVC
10.1、mvc三层架构如下
- Model
- 业务处理 :业务逻辑(Service)
- 数据持久层:CRUD (Dao - 数据持久化对象)
- View(JSP)
- 展示数据
- 提供链接发起Servlet请求 (a,form,img…)
- Controller (Servlet)
- 接收用户的请求 :(req:请求参数、Session信息….)
- 交给业务层处理对应的代码
- 控制视图的跳转
10.2、以登录页面到首页为例子,流程如下
11、Filter
Filter:过滤器 ,用来过滤网站的数据
- 处理中文乱码
- 登录验证….
11.1、工作流程
11.2、Filter的使用
- 让java类实现javax.servlet.Filter接口
public class CharacterEncodingFilter implements Filter {
//服务器启动时就执行对filter进行初始化,随时等待过滤特有的链接
public void init(FilterConfig filterConfig) throws ServletException {
System.out.println("CharacterEnCodingFilter已经启动");
}
//Chain : 链
/*
1. 过滤中的所有代码,在过滤特定请求的时候都会执行
2. 必须要让过滤器继续同行
chain.doFilter(request,response);
*/
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
servletRequest.setCharacterEncoding("utf-8");
servletResponse.setCharacterEncoding("utf-8");
servletResponse.setContentType("text/html;charset=utf-8");
//让请求继续走下去,可能不止一个filter
filterChain.doFilter(servletRequest,servletResponse);
}
//服务器关闭时执行
public void destroy() {
System.out.println("CharacterEnCodingFilter已经销毁");
}
}
- 在web.xml文件中配置filter
<filter>
<filter-name>charsetFilter</filter-name>
<filter-class>com.iandf.filter.CharacterEnCodingFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>charsetFilter</filter-name>
<!--只要是/servlet/....的请求就会经过这个过滤器-->
<url-pattern>/servlet/*</url-pattern>
</filter-mapping>
12、监听器
- 编写一个监听器,实现监听器的接口
//统计网站在线人数 : 统计session
public class OnlineCountListener implements HttpSessionListener {
//创建session监听: 看你的一举一动
//一旦创建Session就会触发一次这个事件!
public void sessionCreated(HttpSessionEvent se) {
ServletContext ctx = se.getSession().getServletContext();
System.out.println(se.getSession().getId());
Integer onlineCount = (Integer) ctx.getAttribute("OnlineCount");
if (onlineCount==null){
onlineCount = new Integer(1);
}else {
int count = onlineCount.intValue();
onlineCount = new Integer(count+1);
}
ctx.setAttribute("OnlineCount",onlineCount);
}
//销毁session监听
//一旦销毁Session就会触发一次这个事件!
public void sessionDestroyed(HttpSessionEvent se) {
ServletContext ctx = se.getSession().getServletContext();
Integer onlineCount = (Integer) ctx.getAttribute("OnlineCount");
if (onlineCount==null){
onlineCount = new Integer(0);
}else {
int count = onlineCount.intValue();
onlineCount = new Integer(count-1);
}
ctx.setAttribute("OnlineCount",onlineCount);
}
/*
Session销毁:
1. 手动销毁 getSession().invalidate();
2. 自动销毁
*/
}
- web.xml中注册监听器
<!--注册监听器-->
<listener>
<listener-class>com.thomas.listener.OnlineCountListener</listener-class>
</listener>
13、Servlet基于注解实现文件上传和下载
13.1、文件的上传
- 表单的设置:设置表单的enctype,提交方式必须为post
<form action="up" method="post" enctype="multipart/form-data">
<input type="file" name="myfile">
<input type="submit" value="上传文件">
</form>
- 服务端接受
servlet3.0版本以前需要使用第三方的jar包commons-fileupload来实现上传文件,servlet3.0版本自带了上传文件的api,无需第三方jar包
servlet3.0将上传的文件保存在Part实例中,可以通过request对象根据上传的表单控件的name获取对应的part对象,同时也支持多个文件上传
通过part对象可直接得到文件的输入流inputStream,在通过outputStream写到本地即可
比较麻烦的地方在于文件名的获取,由于保存文件不能以上传时的文件名来保存,
因为怕多人上传相同文件名而被覆盖,所以一般需要设置一个随机的不能重复的文件名,我们可以使UUID来作为文件名,至于后缀名就需要根据上传时的文件后缀来获取了,part对象里本身并没有存储上传文件的名称,
我们需要从请求头信息里去获取文件名,浏览器发送上传文件请求时将文件名存储在Request Head里的Content-Disposition里,但Content-Disposition得值里除了文件名信息还有一些其他信息,所以只能通过字符串截取的方式获取文件的后缀名。Content-Disposition:form-data; name=“file”; filename=“test.txt”
文件存储在服务器当前项目的根目录下的upload文件夹中,通过req.getServletContext().getRealPath()
获取服务器的绝对路径再将上传的文件写入。
@WebServlet("/up")
@MultipartConfig //使用MultipartConfig注解标注改servlet能够接受文件上传的请求
public class UploadServlet extends HttpServlet {
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException {
Part part = req.getPart("myfile");
String disposition = part.getHeader("Content-Disposition");
String suffix = disposition.substring(disposition.lastIndexOf("."),disposition.length()-1);
//随机的生存一个32的字符串
String filename = UUID.randomUUID()+suffix;
//获取上传的文件名
InputStream is = part.getInputStream();
//动态获取服务器的路径
String serverpath = req.getServletContext().getRealPath("upload");
FileOutputStream fos = new FileOutputStream(serverpath+"/"+filename);
byte[] bty = new byte[1024];
int length =0;
while((length=is.read(bty))!=-1){
fos.write(bty,0,length);
}
fos.close();
is.close();
}
}
13.2、文件下载
web项目大部分文件下载不需要写代码实现,只需通过超链接即可实现,就是通过超链接,在连接地址里写上文件的路径,浏览器会自动打开该文件,如果是普通的文本,图片等浏览器能直接显示内容的浏览器都能直接打开并显示,但如果是浏览器无法打开的文件,比如rar,docx,exe等等,那么浏览器就会提示你下载改文件或者使用当前系统自带的工具打开该文件
<a href="test.docx">下载</a>
如果设置了不询问,就会自动下载不弹出提示
如果是文本文件或者图片,浏览器就会直接打开,想要得到内容可以直接复制,或者右键另存为实现下载,
如果希望所有的文件都能够直接下载而不是打开文件,那么需要通过代码实现通过servlet的响应设置可以设置服务器响应文件流给客户端实现文件下载,思路是,客户端发送请求给服务端告诉服务端需要下载的文件,服务端读取该文件,转换为输入流,在通过outputstream响应给客户端,不过需要设置response的头信息
public class DownServlet extends HttpServlet {
public void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
String file = request.getParameter("file"); //客户端传递的需要下载的文件名
String path = request.getServletContext().getRealPath("")+"/"+file; //默认认为文件在当前项目的根目录
FileInputStream fis = new FileInputStream(path);
response.setCharacterEncoding("utf-8");
response.setHeader("Content-Disposition", "attachment; filename="+file);
ServletOutputStream out = response.getOutputStream();
byte[] bt = new byte[1024];
int length = 0;
while((length=fis.read(bt))!=-1){
out.write(bt,0,length);
}
out.close();
}
}
其实文件下载与上传原理一样,一个是将客户端的文件通过流写到服务端,一个是将服务端的文件通过流写到客户端。
14、@WebServlet注解
14.1、Servlet的传统配置方式
web.xml中的servlet配置
<web-app>
<servlet>
<servlet-name>HelloWorld</servlet-name>
<servlet-class>HelloWorld</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>HelloWorld</servlet-name>
<url-pattern>/HelloWorld</url-pattern>
</servlet-mapping>
</web-app>
写一个Servlet,都要在web.xml中配置Servlet才能够使用,就比较麻烦。所以Servlet3.0之后提供了注解(annotation)
14.2、使用注解方式配置Servlet
@WebServlet注解用于标注在一个继承了HttpServlet类之上,属于类级别的注解。
例如:
@WebServlet("/RegistServlet")
public class RegistServlet extends HttpServlet{
//处理 GET 方法请求的方法
public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {}
//处理POST方法请求的方法
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {}
}
该注解的作用等价于 在web.xml中配置的该servlet的<servlet-mapping>
元素中<url-pattern>
的配置
14.3、@WebServlet注解源码
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface WebServlet {
String name() default "";
String[] value() default {};
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 "";
}
14.4、@WebServlet注解属性
属性名 | 类型 | 描述 |
---|---|---|
name | String | 指定Servlet 的 name 属性,等价于 <servlet-name> 。如果没有显式指定,则该 Servlet 的取值即为类的全限定名 |
value | String[] | 该属性等价于 urlPatterns 属性。两个属性不能同时使用 |
urlPatterns | String[] | 指定一组 Servlet 的 URL 匹配模式。等价于<url-pattern> 标签 |
loadOnStartup | int | 指定 Servlet 的加载顺序,等价于 <load-on-startup> 标签 |
initParams | WebInitParam[] | 指定一组 Servlet 初始化参数,等价于<init-param> 标签 |
asyncSupported | boolean | 声明 Servlet 是否支持异步操作模式,等价于<async-supported> 标签 |
description | String | 该 Servlet 的描述信息,等价于 <description> 标签 |
displayName | String | 该 Servlet 的显示名,通常配合工具使用,等价于 <display-name> 标签 |
-
loadOnStartup属性:标记容器是否在启动应用时就加载Servlet,默认不配置或数值为负数时表示客户端第一次请求Servlet时再加载;0或正数表示启动应用就加载,正数情况下,数值越小,加载该Servlet的优先级越高
-
name属性:可以指定也可以不指定,通过getServletName()可以获取到,若不指定,则为Servlet的完整类名
-
urlPatterns/value属性: String[]类型,可以配置多个映射,如:urlPatterns={"/user/test", “/user/example”}
-
urlPatterns的常用规则:
-
/*或者/:拦截所有
-
*.do:拦截指定后缀
-
/user/test:拦截路径
-