WEB-JSPELJSTL
day12-JSP&EL&JSTL
学习目标
-[ ] 了解JSP的作用
-[ ] jsp常用内置对象
-[ ] EL表达式学习
-[ ] JSTL标签库学习
第一章 JSP
1、概述
1.1 JSP引入
在前几天的登录案例中,登录失败后为了能够响应登录的错误信息。我们特意创建了一个LoginErrorServlet用来动态地拼接错误信息。
【代码如下】
- LoginErrorServlet:登录失败,手动拼接login.html页面,并将错误信息动态地添加进去。
//3.给浏览器生成响应信息boolean result = service.login(username,password);String msg = result ? "用户登陆成功" : "用户登陆失败";request.setAttribute("msg",msg);if (result){//登陆成功response.sendRedirect("/success.html");}else {//登陆失败//转发到处理错误的servlet中request.getRequestDispatcher("/loginErrorServlet").forward(request,response);}
- LoginErrorServlet:手动拼接html页面,动态展示登录错误信息。
@Overrideprotected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {//响应给用户response.setContentType("text/html;charset=utf-8");//接收错误的信息String msg = (String) request.getAttribute("msg");//获取输出流PrintWriter writer = response.getWriter();//拼接登录的html页面response.getWriter().print("<html>");response.getWriter().print("<body>");response.getWriter().print("<form action='loginInterServlet' method='post'>");response.getWriter().print(" <font style='color: red'>"+msg+"</font><br/>");response.getWriter().print("用户名:<input type='text' name='username'/><br/>");response.getWriter().print("密码:<input type='password' name='password'/><br/>");response.getWriter().print("<input type='submit' value='登录'/>");response.getWriter().print("</form>");response.getWriter().print("</body>");response.getWriter().print("</html>");}
说明:上述虽然能够达到我们的需求。但是他有以下两个缺点:
1) 拼接麻烦开发效率低;
2) 阅读性差,代码难以维护;
问题:既然以前在servlet有上述缺点,为什么还要使用拼接呢?
因为如果我们想在html页面显示错误的信息,我们只能采用这种方式,html页面不能够书写java代码。而我们既不采用上述拼接标签的方式,还想实现简单的效果。我们可以使用jsp技术,在jsp中可以书写java代码同时还可以书写html标签。这样 就能够使用jsp技术在客户端和服务端直接进行数据交互了。
1.2JSP是什么
JSP全名为Java Server Pages,中文名叫java服务器页面,其本质是一个Servlet ,它是由Sun 公司倡导、许多公司参与一起建立的一种动态网页 技术标准。其实就是一个既能书写Servlet代码又能书写HTML代码的文件。
1.3 创建JSP文件
在web目录下,新建一个jsp文件,点击鼠标右键New --- JSP---给这个文件取个名字。
【图1 创建JSP】
文件内容如下:
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head><title>Title</title>
</head>
<body></body>
</html>
1.4 在JSP中书写代码
jsp中既可以书写java代码,同时还可以书写html标签。我们完成练习如何在jsp页面中书写java代码。
【需求】
- 在jsp页面上获取当前日期,并将其格式化成字符串"yyyy-MM-dd HH:mm:ss";
- 将这个字符串展示在浏览器上;
说明:
1)jsp中的注释:<%–注释内容–%>,添加注释的快捷键是:ctrl+/;
2)在jsp中书写代码有三种方式,我们这里先介绍一种,格式:
<%java代码%>
【参考代码】demo01.jsp
<%@ page import="java.util.Date" %>
<%@ page import="java.text.SimpleDateFormat" %><%--Created by IntelliJ IDEA.User: tiansuoDate: 2018-01-09Time: 19:15To change this template use File | Settings | File Templates.
--%>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head><title>Title</title>
</head>
<body><%--jsp中的注释【需求】1. 在jsp页面上获取当前日期,并将其格式化成字符串"yyyy-MM-dd HH:mm:ss";2. 将这个字符串展示在浏览器上;--%><%//获取当前系统时间Date d = new Date(); //创建日期格式化解析器对象SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");//使用日期格式化解析器对象调用方法格式化日期对象dString s_date = sdf.format(d);//控制台输出System.out.println("s_date = " + s_date);//将这个字符串展示在浏览器上response.getWriter().print(s_date);%>
</body>
</html>
【运行结果】启动tomcat,并且访问这个jsp页面。
在浏览器看到 format格式的日期:
【控制台输出】
s_date = 2018-01-09 19:28:22
2、JSP的运行原理
通过上面的jsp初体验, 我们看到jsp中既可以编写java代码也可以直接编写html代码,相对servlet更加方便.并且我们通过浏览器访问到的jsp页面,最后在浏览器中看到了响应到浏览器中的结果。那么整个过程的执行流程是怎样的呢,我们可以参考如下图:
通过上面的执行流程,我们可以找到jsp生成的.java源文件,这样就可以知道jsp的真正原理,同时可以看下jsp为什么是servlet。
如何查看生成的.java源文件呢?
我们可以到tomcat中查看一下上面案例中的jsp页面是怎么样的一种存在。IDEA借助tomcat发布web项目的机制:动态发布(为每一个web项目创建一个单独的发布文件)。我们可以通过tomcat其中日志中的CATALINA_BASE路径找到我们当前这个JSP页面在tomcat中的位置:
【图1 IDEA发布项目的路径】
找到这个目录后,会看到以下3个文件夹:
【图2 文件目录】
我们打开C:\Users\admin.IntelliJIdea2017.3\system\tomcat\Tomcat_8_5_311_day05目录发现有两个文件:
【图3 JSP被翻译后的代码】
打开demo01_jsp.java文件后,代码如下所示:
我们可以看到当前的jsp文件被翻译成了一个类,这个类继承HttpJspBase类,那么这个HttpJspBase类又是什么?
注意:jsp的翻译由服务器完成,HttpJspBase类一定也是tomcat服务器的内容,所以我们需要找到org.apache.jasper.runtime.HttpJspBase这个类,这个类可以到tomcat的安装目录下的lib文件夹下找到jasper.jar,将其解压,找如下目录:org\apache\jasper\runtime,找到HttpJspBase.class。
使用反编译工具打开,如下所示:
通过观察源码,根据我们所学习的继承关系,我们发现JSP其实底层就是一个servlet。通过观察源码,我们发现我们刚刚编写的所有代码都在该Servlet里面的service方法内部。
public final class demo01_jsp extends org.apache.jasper.runtime.HttpJspBase {public void _jspService(final javax.servlet.http.HttpServletRequest request, final javax.servlet.http.HttpServletResponse response)throws java.io.IOException, javax.servlet.ServletException {try {response.setContentType("text/html;charset=UTF-8");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;out.write('\r');out.write('\n');out.write("\r\n");out.write("\r\n");out.write("<html>\r\n");out.write("<head>\r\n");out.write(" <title>Title</title>\r\n");out.write("</head>\r\n");out.write("<body>\r\n");out.write(" ");out.write("\r\n");out.write(" ");//获取当前系统时间Date d = new Date();//创建日期格式化解析器对象SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");//使用日期格式化解析器对象调用方法格式化日期对象dString s_date = sdf.format(d);//控制台输出System.out.println("s_date = " + s_date);//将这个字符串展示在浏览器上response.getWriter().print(s_date);out.write("\r\n");out.write("</body>\r\n");out.write("</html>\r\n");} catch (java.lang.Throwable t) {。。。。}
}
总结:
1. <% %>中书写的代码被直接解析成java代码;
2.jsp之所以可以编写html代码,其实本质上也是类似我们使用Servlet直接输出的。也就是说底层还是像我们之前做法一样进行标签拼接。html部分都被out.write("")方法以字符串的形式拼接,然后响应给浏览器;
3.在这个java文件中有个_jspService,这个方法有两个参数request,response;
4. 由于我们自己编写的代码全部都落入到了service方法内部一开始就已经声明了request,response,session,application(ServletContext)等对象了,这些对象成为之jsp内置对象。
3、JSP中书写java代码的三种方式
在JSP页面的body标签中,可以直接书写html代码和JS代码。但是,如果在JSP页面中书写java代码。必须遵循固定的格式,才能够生效;JSP页面中书写java代码有三种方式:1.脚本片段;2.脚本声明;3.脚本表达式;
方式一:脚本片段
脚本片段指的是一段java代码。书写格式:<% java 代码 %>
【示例】 demo02.jsp
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head><title>Title</title>
</head>
<body>
<%//书写java代码,向浏览器输出数据response.getWriter().println("hello jsp");
%>
</body>
</html>
【被翻译后的源码】
out.write("\r\n");out.write(" ");response.getWriter().println("hello jsp");out.write("\r\n");
【页面结果】:
方式二:脚本声明
脚本片段虽然可以嵌入java代码,但是如果,我们要给当前的jsp中定义一些成员方法或者成员变量,就需要一个新的技术——脚本声明.
说明:通过查看jsp的源代码我们发现,脚本片段的代码都会存在service方法中,而方法中是不可以定义一个方法的。
脚本声明的格式:<%! 书写Java代码 %>
【示例一】声明成员变量 demo03.jsp
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head><title>Title</title>
</head>
<body>
<%!int num = 10;public void method(){}
%>
</body>
</html>
【被翻译后的代码】成员变量
方式三:脚本表达式
虽然脚本声明和脚本片段已经可以书写Java代码了,但是如果我们要使用java代码向页面输出一些内容,还是需要 使用原来的response对象,比较繁琐,因此,我们需要一个更加简便的方式,可以代替response**向页面输出内容 ** ——这个就是脚本表达式。
脚本表达式的格式:<%= 向浏览器输出的内容 %> 等价于:out.print( 向浏览器输出的内容)
【示例】在页面输出信息
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head><title>Title</title>
</head>
<body>
<%="传智播客"%>
<%="上海黑马"%>
<%
response.getWriter().print("response");
%>
</body>
</html>
【浏览器页面结果】:
【被翻译后的代码】:
注意:
上面代码中,我们书写代码的顺序是先:
<%="传智播客"%>
<%="上海黑马"%>
再:
response.getWriter().print("response");
可是浏览器查看数据的结果是
response 传智播客 上海黑马
原因:response.getWriter()获取的输出流是:PrintWriter。而脚本表达式<%=%> 等价于:out.print( 向浏览器输出的内容).并且这种方式获取的输出流是:JspWriter。其实导致上述输出结果前后顺序就是PrintWriter和JspWriter。
JspWriter:将输出的内容先放到jsp内置的缓冲区中,然后再刷新输出。
PrintWriter:是不使用jsp内置的缓冲区,直接将内容写到网页中。
总结:
1.不要同时使用脚本表达式和response同时往页面输出信息,会出现顺序不一致。现象:response会出现在最前面。使用一种输出即可。
2.脚本表达式<%= str %>
:在页面输出内容,在service方法中起作用;
3.脚本片段<% %>
:在service方法中,原样输出的代码片段;属于局部为,放在_jspService方法中
4.脚本声明:<%! String str = "黑马程序员" %>
:定义成员变量;
4、JSP页面常用对象
我们发现,我们在JSP页面中书写的代码最终都会翻译到_jspService()方法中,我们发现这个方法中有两个形参:HttpServletRequest对象 ,HttpServletResponse对象 。所以说,JSP的本质就是一个Servlet。我们可以直接在JSP页面上使用这两个对象。
【需求】登录页面改造
改造登录后的页面,用户登陆失败以后,跳转到login.jsp,在jsp页面中动态显示用户登陆错误中的信息。
【思路】
- 登录失败后直接转发到一个jsp页面;
- 在jsp页面上使用request对象获取request中的值;
【LoginServlet代码】
说明:LoginServlet中,登录失败之后,直接转发到login.jsp页面。
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {//解决中文乱码问题request.setCharacterEncoding("utf-8");//获取用户名和密码String username = request.getParameter("username");String password = request.getParameter("password");UserServiceInter service = new UserServiceInterImpl();boolean result = service.login(username,password);String msg = result ? "用户登陆成功" : "用户登陆失败";request.setAttribute("msg",msg);if (result){response.sendRedirect("/success.html");}else {//转发到login.jsp页面request.getRequestDispatcher("/login.jsp").forward(request,response);}}
【login.jsp页面】
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<!DOCTYPE html>
<html lang="en">
<head><meta charset="utf-8"><meta http-equiv="X-UA-Compatible" content="IE=edge"><meta name="viewport" content="width=device-width, initial-scale=1"><title>登录页面</title><link href="css/bootstrap.min.css" rel="stylesheet"><link href="css/login.css" rel="stylesheet"><script src="js/jquery.js"></script><script src="js/bootstrap.js"></script>
</head>
<body>
<%String msg = (String) request.getAttribute("msg");
%>
<div class="container text-center"><form class="form-signin" action="/loginInterServlet" method="get"><h2 class="form-signin-heading">登录页面</h2><font color="red"><%=msg%></font><input type="text" name="username" class="form-control" placeholder="用户名" required autofocus><input type="password" name="password" class="form-control" placeholder="密码" required><button class="btn btn-lg btn-primary btn-block" type="submit">登录</button></form>
</div>
</body>
</html>
说明:上述代码中:
<font color="red"><%=msg%></font>
能够使用msg获取数据,因为编译之后存在于同一个类中,编译jsp如下所示:
效果图:浏览器访问login.html页面
登录密码输入错误:
小结:
- JSP作用:给浏览器生成响应信息;
- JSP特点:动态网页,html+java,由服务器来运行的。本质上是一个Servlet;
- 书写Java代码的三种方式:
- 脚本片段:java代码片段,在service方法中起作用。格式:<% System.out.println(“hello”) %>;
- 脚本表达式:直接在页面输出内容,在service方法中起作用,等价于out.print(内容)。格式:<%= “传智播客”%>
- 脚本声明:定义成员变量,方法。在当前类中都起作用。<%!int a=10;%>
- JSP页面常用的对象:request,response;
第二章 EL表达式
1、EL概述
1.1 引入
问题:
按照上述代码实现方式,如果用户登录失败,我们不用再单独书写一个LoginErrorServlet,直接新创键一个login.jsp页面即可。
但是我们先访问login.html,然后错误在跳转到login.jsp页面,那么我们是否可以直接访问login.jsp页面,而不用再使用login.html页面。这样更为简单。
在浏览器中直接访问login.jsp页面:
我们发现最开始访问jsp页面,页面显示null,原因是最开始访问request中的msg没有任何数据,所以是null。那么有的同学就会想在login.jsp页面中使用java代码对request中的msg进行判断。虽然可以实现,但是脚本表达式在JSP页面上书写起来比较麻烦。而在页面上取值在开发中使用的又比较多,所以Sun公司为了简化在页面上的取值操作。我们这里有更为简单的方案,就是我们下面要讲解的EL表达式。
【需求】使用EL表达式改造login.jsp页面的取值操作
提示:EL表达式从request域对象中取值:${request域对象中的key}
温馨提示:
原来JSP页面中的脚本表达式<%=msg%>被EL表达式取代。
【代码示例】login.jsp
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<!DOCTYPE html>
<html lang="en">
<head><meta charset="utf-8"><meta http-equiv="X-UA-Compatible" content="IE=edge"><meta name="viewport" content="width=device-width, initial-scale=1"><title>登录页面</title><link href="css/bootstrap.min.css" rel="stylesheet"><link href="css/login.css" rel="stylesheet"><script src="js/jquery.js"></script><script src="js/bootstrap.js"></script>
</head>
<body>
<%
// String msg = (String) request.getAttribute("msg");
%>
<div class="container text-center"><form class="form-signin" action="/loginInterServlet" method="get"><h2 class="form-signin-heading">登录页面</h2><%--<font color="red"><%=msg%></font>--%><font color="red">${msg}</font><input type="text" name="username" class="form-control" placeholder="用户名" required autofocus><input type="password" name="password" class="form-control" placeholder="密码" required><button class="btn btn-lg btn-primary btn-block" type="submit">登录</button></form>
</div></body>
</html>
说明:
1)在jsp页面中使用EL表达式可以简化Java代码。
2)EL表达式从域对象取值,如果域对象中有值则获取,没有值则不获取。
1.2 EL简介
EL全称: Expression Language 中文的意思是EL表达式。
作用:代替jsp中脚本表达式<%=输出内容%>的功能,简化对java代码的操作,从【域对象】中取值。 EL表达式简化<%= %>方式取值
EL语法表达式的格式:${域对象中的key}
2、EL取值
2.1 JSP的四大域对象
JSP的四大域对象指的是:page域,request域,session域,application域。我们通常使用EL表达式从这4个域对象用取值。以下是这4个域对象的详细信息:
域对象 | 在EL中的对象名称 | 在servlet中的对象名 | 说明 |
---|---|---|---|
page域 | pageScope | pageContext,属于javax.servlet.jsp.PageContext类的对象 | page域指的是当前JSP页面,其中存储的数据只在当前页面有效,因为jsp本质是servlet,所以page域只在一个servlet中有效。 |
request域 | requestScope | request,属于javax.servlet.http.HttpServletRequest类的对象 | request域:一次请求或请求链中request域 |
session域 | sessionScope | session,属于javax.servlet.http.HttpSession接口的对象 | session域:一次会话过程中,session域 |
application域 | applicationScope | application,属于javax.servlet.ServletContext类的对象 | application域:服务启动后整个项目对应的ServletContext域 |
2.2 EL表达式从四大域中取值
EL表达式从指定的域中取值的方式如下:
域对象 | 取值方式 |
---|---|
page域 |
${pageScope.key}
|
request域 |
${requestScope.key}
|
session域 |
${sessionScope.key}
|
application域 |
${applicationScope.key}
|
【需求】
- 在一个JSP页面中,使用脚本片段分别向page域、request域,session域,application域和中存储数据;
- 使用EL表达式从这4个域中取值;
【参考代码】
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head><title>Title</title>
</head>
<body><%--EL表达式 page request session application 四大域对象【需求】1. 在一个JSP页面中,使用脚本表达式分别向page域、request域,session域,application域和中存储数据;2. 使用EL表达式从这4个域中取值;--%><%//page域,对一servlet中的pageContext对象pageContext.setAttribute("pageValue","page的值");//request域request.setAttribute("requestValue","request的值");//session域request.getSession().setAttribute("sessionValue","session的值");//application域,属于ServletContext类的对象application.setAttribute("applicationValue","application的值");%><%--EL表达式来域对象中取值--%>page中取值:${pageScope.pageValue}<br>request中取值:${requestScope.requestValue}<br>session中取值${sessionScope.sessionValue}<br>application中取值${applicationScope.applicationValue}<br>
</body>
</html>
2.3 EL表达式搜索数据
EL表达式取值的时候也可以不指定域,如果取值的时候不指定域对象。就会按照从page域—>request域—>session域—>application域从小到大逐级根据key值查找。
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head><title>Title</title>
</head>
<body><%--EL表达式 page request session application 四大域对象【需求】1. 在一个JSP页面中,使用脚本表达式分别向page域、request域,session域,application域和中存储数据;2. 使用EL表达式从这4个域中取值;--%><%//page域,对一servlet中的pageContext对象pageContext.setAttribute("pageValue","page的值");//request域request.setAttribute("requestValue","request的值");//session域request.getSession().setAttribute("sessionValue","session的值");//application域,属于ServletContext类的对象application.setAttribute("applicationValue","application的值");//向域对象中存储值,key值一样,则先从小向大的域中查找pageContext.setAttribute("value","page的值");request.setAttribute("value","requst的值");%><%--EL表达式来域对象中取值--%>page中取值:${pageScope.pageValue}<br>request中取值:${requestScope.requestValue}<br>session中取值${sessionScope.sessionValue}<br>application中取值${applicationScope.applicationValue}<br>相同的key:${value}
</body>
</html>
2.4 EL表达式从cookie中取值
之前我们在学习cookie的时候,做过记住用户名和密码的案例,但是前端页面我们并没有实现。因为我们还没有学习使用什么技术在前端如何将cookie中的数据取出,而今天我们学习了EL表达式,我们就可以实现前端记住用户名和密码的案例了。
new Cookie(name,value)
使用EL表达式获取cookie中的值格式:(掌握)
cookie.cookie的name名.value
例如:Cookie cookieName = new Cookie("username","锁哥");cookie.username.value获取的值是锁哥。
创建一个登陆的jsp页面:
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head><title>Title</title>
</head>
<body><form action="/loginServlet" method="post"><%--${cookie.username.value} 表示从浏览器存储的cookie中获取值cookie表示对象名username 表示cookie中的名,看服务器端给的什么名字,这里就写什么名字,不固定。可以理解为keyvalue属于固定属性。表示获取值--%>用户名:<input type="text" name="username" value="${cookie.username.value}"><br>密码:<input type="password" name="password" value="${cookie.password.value}"><br><input type="submit" value="提交"></form>
</body>
</html>
在servlet生成cookie LoginServlet.java
@WebServlet("/loginServlet")
public class LoginServlet extends HttpServlet {protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {doGet(request, response);}protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {//获取用户名和密码String username = request.getParameter("username");String password = request.getParameter("password");//创建cookieCookie cookieName = new Cookie("username",username);Cookie cookiePsw = new Cookie("password",password);response.addCookie(cookieName);response.addCookie(cookiePsw);}
}
温馨提示:要去访问Java代码LoginServlet,才能将cookie存储到浏览器中
效果图:
先访问:http://localhost:8080/login.jsp也面,点击登录按钮,访问了LoginServlet,在LoginServlet中 生成cookie。
再访问login.jsp页面,使用EL表达式获取用户名和密码
http://localhost:8080/login.jsp
3、EL运算符
EL表达式获取到值之后可以直接通过运算符进行运算。EL表达式的运算符主要有以下几类:算术运算,关系运算,逻辑运算,三元运算,empty运算符。
【素材】页面数据准备demo08.jsp
<%request.setAttribute("n1",40);request.setAttribute("n2",30);request.setAttribute("n3","20");request.setAttribute("n4","10");
%>
3.1 算术运算
顾名思义,算术运算是进行算术运算的符号,和java中的一致。主要包括:加,减,乘,除,取余。具体使用如下表:
运算符 | 说明 | 使用示例 | 结果 |
---|---|---|---|
+ | 加 |
${n1+n2}
|
70 |
- | 减 |
${n1-n2}
|
10 |
* | 乘 |
${n1*n2}
|
1200 |
/或div | 除 |
${n1/n4}
|
4 |
%或mod | 取余 | ${n1%n4} | 0 |
【代码示例】demo08.jsp代码片段
算数运算:<br>n1+n2:${n1 + n2}<br>n1-n3:${n1 - n3}<br>n3-n4:${n3-n4}<br>n2*n3:${n2 * n3}<br>
效果图:
注意:在进行算术运算的时候,EL表达式会对字符串中的内容进行判断,如果都是数值,那么会转化为数值再进行算术运算,如果含有非数字类型,则会报异常。
3.2 关系运算
<%request.setAttribute("n1",40);request.setAttribute("n2",30);request.setAttribute("n3","20");request.setAttribute("n4","10");
%>
关系运算符是判断两个数据的大小关系的,关系运算符有:==,!=,<,<=,>,>=。具体使用方法如下:
运算符 | 说明 | 使用示例 | 结果 |
---|---|---|---|
== 或 eq
|
等于 equal |
${n1 == n2}
|
false |
!= 或ne
|
不等于 not equal |
${n1 != n2}
|
true |
> 或 gt
|
大于 greater than |
${n1 > n2}
|
true |
>= 或ge
|
大于等于 greater than or equal |
${n1 >= n2}
|
true |
< 或 lt
|
小于 less than |
${n1 < n2}
|
false |
<= 或le
|
小于等于 less than or equal |
${n1 <= n2}
|
false |
【代码示例】demo08.jsp
关系运算符:<br>
n1==n2:${n1==n2}<br>
n3>=n4:${n3>=n4}<br>
n1!=n2:${n1 ne n2}<br>
效果:
3.3 逻辑运算
逻辑运算符包括:&& ,||,! 使用方法如下:
运算符 | 说明 | 使用示例 | 结果 |
---|---|---|---|
&& 或 and | 逻辑与,一假即假 |
${true && false}
|
false |
|| 或 or | 逻辑或,一真即真 |
${true || false}
|
true |
! 或 not | 非,取反 |
${!false}
|
true |
参考代码demo08.jsp
逻辑运算符:<br>
false && true:${false && true}<br>
false || true:${false || true}<br>
!false:${!false}<br>
3.4 三元运算
EL表达式也支持三元运算符:如,判断n1是否大于等于n2,可以写成如下:参考代码demo08.jsp
<%--表达式1?表达式2:表达式3
--%>
三元运算符:<br>
${n1>=n2?"正确":"错误!"}<br>
3.5 empty运算
empyt运算符表示判断数据是否为空,如果为空返回true。对以下数据运算返回true:
- 字符串:"";
- 空集合(size=0):List list = new ArrayList();
- 空对象(null):Student stu = null;String s=null;Object obj=null;
【代码示例】 demo08.jsp
<%String str = "";Student student=null;List<String> list = new ArrayList<>();%>empty:<br>${empty str}<br>${empty student}<br><%--not empty list :表示对empty的结果取反,由于list.size()等于0,所以empty list的结果是true,但是这里加了一个not,结果变为了false--%>${not empty list}<br>
页面效果:
demo08.jsp完整代码
<%@ page import="java.util.ArrayList" %>
<%@ page import="java.util.List" %>
<%@ page import="com.itheima.sh.el.cookie_01.Student" %><%--Created by IntelliJ IDEA.User: tiansuoDate: 2018-01-15Time: 8:59To change this template use File | Settings | File Templates.
--%>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head><title>运算符</title>
</head>
<body><%//向request域对象中存储数据request.setAttribute("n1",40);request.setAttribute("n2",30);request.setAttribute("n3","20");request.setAttribute("n4","10");%>算数运算:<br>n1+n2:${n1 + n2}<br>n1-n3:${n1 - n3}<br>n3-n4:${n3-n4}<br>n2*n3:${n2 * n3}<br>关系运算符:<br>n1==n2:${n1==n2}<br>n3>=n4:${n3>=n4}<br>n1!=n2:${n1 ne n2}<br>逻辑运算符:<br>false && true:${false && true}<br>false || true:${false || true}<br>!false:${!false}<br><%--表达式1?表达式2:表达式3--%>三元运算符:<br>${n1>=n2?"正确":"错误!"}<br><%String str = "";Student student=null;List<String> list = new ArrayList<>();%>empty:<br>${empty str}<br>${empty student}<br><%--not empty list :表示对empty的结果取反,由于list.size()等于0,所以empty list的结果是true,但是这里加了一个not,结果变为了false--%>${not empty list}<br>
</body>
</html>
效果图:
【注】not empty可以用在EL表达式中,判断非空的情况。
EL表达式小结:
- EL:Expression Language;
- EL语法:${ }
- 作用:简化脚本表达式的取值,简化<%= request.getAttribute(“name”) %> ===> ${name}
- jsp的四大域对象
- page域:pageContext pageContext.setAttrubute() pageContext.getAttribute() JSP特有 作用范围:当前的JSP页面
- requst域:request request.setAttribute() request.getAttribute() 作用范围:一次请求和响应之间
- session域: session session.setAttribute() session.getAttribute() 作用范围:会话期间(多次请求和响应)
- servletContext域:application application.setAttribute() application.getAttribute() 作用范围:整个项目;
- jsp从四大域中取值(指定域对象):
- ${pageScope.name}
- ${requestScope.name}
- ${sessionScope.name}
- ${applicationScope.name}
- JSP搜索域对象中的值:page — request — session — servletContext
- ${name}
- 运算
- 算术运算
- 关系运算
- 逻辑运算:&& || !
- 三元运算
- empty运算:empty not empty
- 空字符串:""
- 空对象:null
- 空集合:list.size = 0;
第三章 JSTL标签库
1、JSTL概述
1.1 、JSTL引入
【案例】在页面中遍历数组:demo09.jsp
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head><title>JSTL</title>
</head>
<body>
<%int[] arr = {123,456,789};for (int i : arr) {response.getWriter().print("<h1>"+i+"</h1>");}
%>
</body>
</html>
1.2、JSTL是什么
从JSP 1.1规范开始JSP就支持使用自定义标签,使用自定义标签大大降低了JSP页面的复杂度,同时增强了代码的重
用性,因此自定义标签在WEB应用中被广泛使用。许多WEB应用厂商都开发出了自己的一套标签库提供给用户使用,这导致出现了许多功能相同的标签,令网页制作者无所适从,不知道选择哪一家的好。为了解决这个问题,
Apache Jakarta小组归纳汇总了那些网页设计人员经常遇到的问题,
开发了一套用于解决这些常用问题的自定义标签库,
这套标签库被SUN公司定义为标准标签库(The JavaServer Pages Standard Tag Library),简称JSTL。使用JSTL可以解决用户选用不同WEB厂商的自定义标签时的困惑,JSP规范同时也允许WEB容器厂商按JSTL标签库的
标准提供自己的实现,以获取最佳性能
注意:由于jstl标签属于第三方的,使用的时候必须导入核心jar包。并且在使用标签的jsp页面导入核心jar包中的类。
1.3、JSTL核心标签库
标签名称 | 作用 |
---|---|
<c:out>(掌握) | 通常用于输出一段文本内容到客户端浏览器 |
<c:set> | 用于设置各种Web域中的属性 |
<c:remove> | 用于删除各种Web域中的属性 |
<c:catch> | 用于捕获嵌套在标签体中的内容抛出的异常 |
<c:if>(掌握) | java代码if(){}语句功能 |
<c:choose>(掌握) | 用于指定多个条件选择的组合边界,它必须与c:when和c:otherwise标签一起使用 |
<c:forEach>(掌握) | 代替java代码for循环语句 |
<c:forTokens> | 迭代操作String字符 |
<c:param> | 给请求路径添加参数 |
<c:url> | 重写url,在请求路径添加sessionid |
<c:import> | 用于在JSP页面中导入一个URL地址指向的资源内容 |
<c:redirect> | 用于将当前的访问请求转发或重定向到其他资源 |
2、JSTL标签应用
2.1 安装JSTL
我们已经知道JSTL标签是将一段java代码功能封装成一个标签来使用。所以,我们使用JSTL标签之前必须导入被封装的java代码—jar包。JSTL标签库主要依赖以下两个jar包:
我们在IDEA的web项目的WEB-INF目录下创建一个lib文件夹,然后将jar包复制到这个文件夹里。选中这些jar包,右键—add as library即可将jar包导入到项目中。
2.2 将标签库资源引入JSP页面
在JSP页面中通过以下标签,通过taglib标签引入JSTL资源:
<%@taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
【注意】
- prefix:是jstl标签在使用的时候的前缀;
- uri:是标签库的资源路径;
2.3 在JSP页面中使用JSTL标签
在JSP页面中书写"<c: ",如果出现如下图的提示则说明标签库引入成功,我们可以根据提示信息使用自己需要的标签。
【代码示例】demo10.jsp
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head><title>JSTL标签库</title>
</head>
<body>
<c:out value="传智播客,黑马程序猿"></c:out>
</body>
</html>
效果:
3、常用JSTL标签
3.1、c:if标签
3.1.1 作用
<c:if test="判断条件"></c:if>
标签的作用相当于java中的if判断语句。
3.1.2 属性
test:if的判断条件
3.1.3 使用示例
【案例】使用java代码在jsp页面向request域对象中存储一个对象,使用<c:if>
标签判断该对象是否为空。
【代码实现】demo11.jsp
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<%@ page import="com.itheima.sh.el.cookie_01.Student" %>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head><title>Title</title>
</head>
<body><%--if(判断条件){判断条件是真,执行代码}<c:if test="判断条件"></c:if>--%>
<%//创建学生对象Student s=null;//将学生对象s存储到request域对象中request.setAttribute("s",s);
%>
<%--判断 这里需要获取request域对象中的s.所以需要使用EL表达式--%>
<c:if test="${s == null}"><c:out value="学生为空"></c:out>
</c:if>
<c:if test="${s != null}"><c:out value="学生不为空"></c:out>
</c:if>
</body>
</html>
3.2 c:foreach标签
3.2.1 c:foreach标签的作用
<c:foreach>
标签的作用相当于java中的for循环。主要是对数组和集合进行遍历。
3.2.2 c:foreach标签的属性
var:在不循环对象的时候,保存的是控制循环的变量;在循环对象的时候,保存的是被循环对象中的数据。就是集合或者数组中的数据。******
items:指定要循环的对象。就是数组或者集合名字。******
varStatus:保存了当前循环过程中的信息(循环的开始、结束、步长、次数等)
begin:设置循环的开始。起始索引,例如 int i=0;
end:设置循环的结束.就是结束的值。例如:i<=arr.length-1中的arr.length-1。
step:设置步长——间隔几次循环,执行一次循环体中的内容。例如:i++.
在使用<c:forEach>标签时,需要注意如下几点说明:
如果指定begin属性,其值必须大于或等于零;
如果指定步长(step属性),其值必须大于或等于1;
如果items属性的值为null,则要处理的集合对象为空,这时不执行迭代操作;
如果指定的begin属性的值大于或等于集合对象的长度,不执行迭代操作;
如果指定的end属性的值小于begin属性的值,不执行迭代操作;
3.2.3 c:foreach标签的使用
【案例】c:foreach遍历案例
【需求】
- 遍历普通数组;
- 遍历map集合;
- 遍历实体类对象list集合;
【需求一】遍历普通数组.
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head><title>遍历数组</title>
</head>
<body><%//定义数组String[] arr={"篮球","足球","排球"};//增强for循环for (String s : arr) {System.out.println(s);}//将数组存入到域对象中request.setAttribute("arr",arr);%><%--items 相当于 arr,将要迭代的数组对象arrvar="item" 相当于s,数组中的数据--%><c:forEach items="${arr}" var="item">${item}<br/></c:forEach>
</body>
</html>
【需求二】遍历map集合.
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<%@ page import="java.util.HashMap" %>
<%@ page import="java.util.Map" %>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head><title>Title</title>
</head>
<body><%Map map = new HashMap();map.put("one","火箭");map.put("two","湖人");map.put("thr","马刺");map.put("four","猛龙");request.setAttribute("map",map);%><%--使用jstl遍历mapitem 表示键值对对象${item.key} 通过键值对象调用属性key获取map集合中的键${item.value} 通过键值对象调用属性value获取map集合中的值--%><c:forEach items="${map}" var="item">map集合中的key:${item.key},map集合中的value:${item.value}<br/></c:forEach>
</body>
</html>
效果:
【需求三】遍历实体类对象list集合.
我们已经学习完jsp技术,接下来我们先看下jsp在企业流程开发中扮演怎样的角色,然后可以根据流程完成一个小的案例。
【创建一个商品的实体类:】
public class Product {//属性private String pname;//商品名private double price;//商品价格//构造方法public Product(String pname, double price) {this.pname = pname;this.price = price;}//get set方法public String getPname() {return pname;}public void setPname(String pname) {this.pname = pname;}public double getPrice() {return price;}public void setPrice(double price) {this.price = price;}
}
【商品的servlet:】
@WebServlet("/productServlet")
public class ProductServlet extends HttpServlet {protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {doGet(request, response);}protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {//去数据库查询商品信息Product product1 = new Product("布娃娃",9.9);Product product2 = new Product("苹果手机",99);Product product3 = new Product("外星人电脑",12000);Product product4 = new Product("鼠标",8);Product product5 = new Product("抹茶",20);List<Product> list = new ArrayList<>();list.add(product1);list.add(product2);list.add(product3);list.add(product4);list.add(product5);//将数据存储在域对象中request.setAttribute("list",list);//使用转发 list.jsprequest.getRequestDispatcher("/list.jsp").forward(request,response);}
}
【显示商品页面:】
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head><title>Title</title>
</head>
<body><%--取出商品信息.使用EL表达式和JSTL将数据遍历出来,放在标签里面--%><c:forEach items="${list}" var="product">商品名:<font color="green">${product.pname}</font>,惊爆价:<font color="red">${product.price}</font><br/></c:forEach>
</body>
</html>
说明:上述使用 product.pname ,product.price。底层是调用对应的getPname()和getPrice()方法。
访问路径:http://localhost:8080/produceServlet
效果图:
3.3 choose标签
3.3.1 choose标签作用
<c:choose>标签用于指定多个条件选择的组合边界,它必须与<c:when>和<c:otherwise>标签一起使用。和java中的switch多分支语句一样的。
3.3.2 choose标签子标签
介绍 <c:when>,相当于switch语句的case
<c:when>标签含有test属性,进行判断。 <c:otherwise>,相当于default。
3.3.3 choose标签注意事项
三个标签必须组合使用,一组标签中不能出现两个<c:otherwise>
3.3.4 choose标签演示
【SwitchServlet代码:】
@WebServlet("/switchServlet")
public class SwitchServlet extends HttpServlet {protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {doGet(request, response);}protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {//实现switch功能int day = 7;switch (day){case 1:System.out.println("星期一");break;case 2:System.out.println("星期二");break;case 3:System.out.println("星期三");break;case 4:System.out.println("星期四");break;case 5:System.out.println("星期五");break;case 6:System.out.println("星期六");break;case 7:System.out.println("星期日");break;default:System.out.println("你输入错误!");}//转发到servletrequest.setAttribute("day",day);//转发到choose.jsprequest.getRequestDispatcher("/choose.jsp").forward(request,response);}
}
【choose.jsp页面:】
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head><title>Title</title>
</head>
<body><%--switch (day){case 1:System.out.println("星期一");break;default:System.out.println("你输入错误!");}<c:when test="${day == 1}"> 相当于 case 1<c:otherwise> 相当于 default --%><c:choose><c:when test="${day == 1}"><c:out value="星期一"></c:out></c:when><c:when test="${day == 2}"><c:out value="星期二"></c:out></c:when><c:when test="${day == 3}"><c:out value="星期三"></c:out></c:when><c:when test="${day == 4}"><c:out value="星期四"></c:out></c:when><c:when test="${day == 5}"><c:out value="星期五"></c:out></c:when><c:when test="${day == 6}"><c:out value="星期六"></c:out></c:when><c:when test="${day == 7}"><c:out value="星期日"></c:out></c:when><c:otherwise><c:out value="你输入有误"></c:out></c:otherwise></c:choose>
</body>
</html>
说明:
switch (day){case 1:System.out.println("星期一");break;default:System.out.println("你输入错误!");}<c:when test="${day == 1}"> 相当于 case 1<c:otherwise> 相当于 default
访问URL:http://localhost:8080/switchServlet
效果图:
第四章 MVC模式和JSP的发展
1、MVC设计模式
MVC设计模式: Model-View-Controller简写。
MVC是软件工程中的一种软件架构模式,它是一种分离业务逻辑与显示界面的设计方法。它把软件系统分为三个基本部分:模型(Model)、视图(View)和控制器(Controller)。
- 控制器Controller:对请求进行处理,负责请求转发;
- 视图View:界面设计人员进行图形界面设计;
- 模型Model:编写程序应用的功能(实现算法等等)、数据库管理;
如下图所示:
MVC可对程序的后期维护和扩展提供了方便,并且使程序某些部分的重用提供了方便。而且MVC也使程序简化,更加直观。
注意,MVC不是Java的特有的,几乎现在所有B/S结构的软件都采用了MVC设计模式。
2、JSP发展
当SUN公司推出JSP后,同时也提供相应的开发模式.
2.1、model1时代—一个JSP页面搞定前端和后台
JSP技术主要用来简化动态网页的开发过程,由于它在形式上和html文档比较相似,因此与Servlet相比,用JSP来编写动态网页更加直观。但是服务器端只有JSP页面,所有的操作都在JSP页面中,连访问数据库的API也在JSP页面中完成。也就是说,所有的东西都在一起,对后期的维护和扩展极为不利。
2.2、model2时代—弱化在JSP页面书写java代码
但是,当网页非常复杂时,JSP文件中的大量的html标记和java程序片段混杂在一起,会大大削弱JSP代码的可读性和可维护性,而且会增加调试JSP文件的难度。于是,出现了EL表达式帮助我们在JSP页面上简化从容器中取值的操作。【EL表达式】—简化取值
如果是在JSP页面上处理一些业务逻辑,JSTL标签库给我们提供了一个解决思路。将java代码封装成标签。直接在JSP页面书写标签,如<c:if>
即可完成相应逻辑。【JSTL标签库】—将java代码封装成标签.
Model2使用到的技术有:Servlet、JSP、JavaBean。Model2 是MVC设计模式在Java语言的具体体现。
- JSP:视图层,用来与用户打交道。负责接收用户的数据,以及显示数据给用户;
- Servlet:控制层,负责找到合适的模型对象来处理业务逻辑,转发到合适的视图;
- JavaBean:模型层,完成具体的业务工作,例如:转账等。
3、三层架构(补充)
JSP模式是理论基础,但实际开发中,我们常将服务器端程序,根据逻辑进行分层。一般比较常见的是分三层,我们称为:经典三层体系架构。三层分别是:表示层、业务逻辑层、数据访问层。区分层次的目的即为了“高内聚,低耦合”的思想。高内聚:开发人员可以只关注整个结构中的其中某一层,各层功能明确,代码结构标准化,程序员集中对每一层的实现;低耦合:可以降低层与层之间的依赖,利于各层逻辑的复用,在后期维护的时候,极大地降低了维护成本和维护时间;扩展性强:可以很容易的用新的实现来替换原有层次的实现。
- 表示层:又称为 web层,与浏览器进行数据交互的。
- 业务逻辑层:又称为service层,专门用于处理业务数据的。
- 数据访问层:又称为dao层,与数据库进行数据交换的。将数据库的一条记录与JavaBean进行对应。
面向接口编程思想:
为什么要使用接口编程?
1.现有代码还是现有业务
面向接口编程就是先把客户的业务逻辑线提取出来,作为接口,业务具体实现通过该接口的实现类来完成。
2.编程原则
其遵循的思想是:对扩展开放,对修改关闭。其恰恰就是遵循的是使用接口来实现。当各个类变化时,不需要对已经编写的系统进行改动,添加新的实现类就可以了,不在担心新改动的类对系统的其他模块造成影响。
3.降低耦合性,解耦
耦合就是联系 ,耦合越强,联系越紧密。在程序中紧密的联系并不是一件好的事情,因为两种事物之间联系越紧密,你更换其中之一的难度就越大,扩展功能和debug的难度也就越大。
4.程序扩展性
可扩展性是指当需要为程序添加新的功能时,对其他模块的影响和添加的代价。
WEB-JSPELJSTL相关推荐
- 在k8s中使用gradle构建java web项目镜像Dockerfile
在k8s中使用gradle构建java web项目镜像Dockerfile FROM gradle:6-jdk8 AS build COPY --chown=gradle:gradle . /home ...
- 手动将web项目的class文件打成jar包,手动打jar包,java -cvf,IDE打包底层指令
手动将web项目的class文件打成jar包. 我们的项目在使用IDE进行编译后,在项目的target目录下将会生成class文件.我们可以将class文件打成jar包. 使用的到命令为: 在targ ...
- 将Eclipse中Web项目打成war包
将Eclipse中Web项目打成war包 请按如下操作即可:
- Docker安装Apache与运行简单的web服务——httpd helloworld
Docker运行简单的web服务--httpd helloworld目录[阅读时间:约5分钟] 一.Docker简介 二.Docker的安装与配置[CentOS环境] 三.Docker运行简单的web ...
- 【Golang源码分析】Go Web常用程序包gorilla/mux的使用与源码简析
目录[阅读时间:约10分钟] 一.概述 二.对比: gorilla/mux与net/http DefaultServeMux 三.简单使用 四.源码简析 1.NewRouter函数 2.HandleF ...
- 基于Golang的简单web服务程序开发——CloudGo
基于Golang的简单web服务程序开发--CloudGo[阅读时间:约10分钟] 一.概述 二.系统环境&项目介绍 1.系统环境 2.项目的任务要求 (1)基本要求 (2)扩展要求 三.具体 ...
- CentOS Docker安装配置部署Golang web helloworld
目录[阅读时间:约5分钟] 一.Docker简介 二.Docker的安装与配置[CentOS环境] 三.Docker部署Golang web helloworld 四.Docker与虚拟机的区别 五. ...
- ideal如何创建dynamic web project
步骤如下 ① file -> new -> project ② 选择 Java Enterprise -> next ③ create project from template - ...
- eclipse运行maven web项目
说在前面:Maven项目对象模型(POM),可以通过一小段描述信息来管理项目的构建,报告和文档的软件项目管理工具.现在好多公司都在使用.有时候我们想在eclipse启动maven项目,怎么操作呢,下面 ...
- Eclipse+Maven创建web项目
有时候我们想用eclipse+maven创建web项目,怎么操作呢,下面我来给大家来分享一下方法 所需工具 已经安装了maven插件的elipse 步骤 1.打开eclipse,右键new--othe ...
最新文章
- 独家 | Kaggle 大神Dan Becker与你分享他的数据科学之旅!
- 前端学习(3132):react-hello-react之react中事件处理
- Android 分享功能大全
- Qt杂记-QQuick之Android隐藏状态栏以及状态栏透明(QQuick项目)
- 【Level 08】U07 Mixed Feelings L6 State of the Nation
- iOS及Android 启动页面(即欢迎页面),引导页面,及广告页面的加载
- 计算机控制系统的输入输出信号,谈谈自动控制系统的输入输出信号
- Content type ‘application/json;charset=UTF-8‘ not supp...
- 数据降维:主成分分析法(PCA)
- html文本域 高度自适应,Javascript 文本域根据输入内容自适应高度
- 一个计算机爱好者的不完整回忆(十)插播游戏
- MQTT学习笔记——MQTT协议使用
- 5G时代对IDC数据中心提出了怎样的要求?
- python numpy 二维数组reshape成三维数组
- 【调剂】中国航空研究院631所2022年调剂信息
- 记2018年北航夏令营+机试面试心得体会
- 深度学习(自监督:SimSiam)——Exploring Simple Siamese Representation Learning
- 在线获取美女写真壁纸采集源码
- 什么是闭包?它的优点与缺点是?
- 微信小程序直播系统有哪些优势?
热门文章
- 鸽巢排序Pigeonhole Sort----(排序算法八)
- Windows10 使用Win32.Mak 进行makefile编译
- 量化交易 米筐 策略评价指标
- fastlane二开java_Fastlane 使用手册(二)
- total variation、global variation、local variation
- WMS入库作业_核心业务流程
- 电脑开的热定手机连不上解决方法
- 探索 AIX 6:新特性概述(上)(转自IBM)
- 今日头条极速版自动寻找-阅读惊喜奖励
- 营销/网赚/seo/淘宝资料收集下载