Java Web MVC和JSTL

MVC:模型 视图 控制器本质上是组织代码的编程范式

MVC的核心是引入控制器代码,将用户界面的代码和业务逻辑的代码隔离(解耦)

MVC的设计理念,将Model细分为业务模型(Service)、数据访问模型(DAO)、数据模型(JavaBean)

对于Service类,通用技术是java SE

对于DAO模型,通用技术是JDBC

数据模型负责持有业务数据,通用技术是JavaBean

数据模型是既要持有Form表单中的数据,又要持有数据库表中的数据

EntityBean持有数据库表的数据,使用FormBean持有表单数据,这两者都是属于JavaBean

Service和DAO之间的关系

一个业务逻辑对应Service类的一个方法。

为了完成一个业务逻辑需要执行多个数据库访问操作

将业务逻辑与数据库操作解耦

通常做法是在Service和数据库之间引入DAO

设计DAO的基本原则是DAO只封装了增删改查等最基本的数据库元操作,DAO中不会包含与业务逻辑相关的代码

设计Service的基本原则是,Service通常需要调用多个DAO才能完成一个业务

JavaBean命名属性的特殊情况

1.命名属性的惰性计算

利用缓存等记忆化特性,暂存程序的执行结果

如:

private List userName = null;
public List getUserNames(){
    if(userNames == null){
        userNames = fetchUserNamesFromDatabase();
    }
    return userNames;
}

 这样第一次调用getter方法时,需要从数据库中提取数据。时间成本较高,但第二次调用getter方法会,直接返回第一次缓存结果,降低了时间成本

2.合成属性

一般JavaBean中的命名属性就是JavaBean的私有成员变量,但也有例外,合成属性是JavaBean命名属性的另一种特殊形式。

就比如说JavaBean中有一个Person类,定义一个IDNumber命名属性和三个合成属性(birthday、sex、age)

结论:仅可以通过无参的 getter 方法和 is 方法,就可以定义JavaBean属性

小露身手:创建数据模型JavaBean和业务逻辑Service

准备工作:创建Dynamic Web Project,Servlet-api.jar包导入 Web 项目,jstl项目部署到Tomcat中

Java Resources找到src然后右键选择class,包名为bean,类名为User

package bean;

import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.Date;

public class User {
	private int userID;
	private String userName;
	private String birthday;
	private boolean admin;
	//为User类添加带有参数的构造方法,右击代码空白区域选择Source-Generate Constructor using Fields
	//constructor,选中所有成员变量,然后点击确定按钮
	public User(int userID, String userName, String birthday, boolean admin) {
		super();
		this.userID = userID;
		this.userName = userName;
		this.birthday = birthday;
		this.admin = admin;
	}
	//将成员变量定义为只读命名变量,右键空白,Source-Generate getter and setters,选中四个成员变量的
	//getter方法和is方法
	public int getUserID() {
		return userID;
	}
	public String getUserName() {
		return userName;
	}
	public String getBirthday() {
		return birthday;
	}
	public boolean isAdmin() {
		return admin;
	}
	//添加合成属性age的getter方法,通过出生日期计算年龄
	public int getAge() throws Exception{
		SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd");
		Date date = null;
		date = dateFormat.parse(birthday);
		Calendar cal = Calendar.getInstance();//当前时间
		int yearNow = cal.get(Calendar.YEAR);
		int monthNow = cal.get(Calendar.MONTH);
		int dayOfMonthNow = cal.get(Calendar.DAY_OF_MONTH);
		cal.setTime(date);
		int yearBirth = cal.get(Calendar.YEAR);
		int monthBirth = cal.get(Calendar.MONTH);
		int dayOfMonthBirth = cal.get(Calendar.DAY_OF_MONTH);
		int age = yearNow - yearBirth;//计算整岁
		if(monthNow <= monthBirth) {
			if(monthNow == monthBirth) {
				if(dayOfMonthNow < dayOfMonthBirth) {
					age--;//当前日期在生日之前,年龄-1
				}
			}else {
				//当前月份在年龄之前,年龄-1
				age--;
			}
		}
		return age;
	}
}

如法炮制:包名为service,类名为UserService,输入以下代码

package service;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Random;

import bean.User;
public class UserService {
	public static User createRandomUser() {
		Random random = new Random();
		int rand = random.nextInt(9000)+1000;
		User user = new User(rand, "用户名"+rand, "2008-8-8", false);
		return user;
	}
	public static List<User> getUserList(){
		List<User> userList = new ArrayList<User>();
		for(int i=0;i<=10;i++) {
			User user = createRandomUser();
			userList.add(user);
		}
		User admin = new User(1, "admin", "2000-8-8", true);
		userList.add(admin);
		return userList;
	}
	public static Map<String, User> getUserMap() {
		Map<String, User> userMap = new HashMap<String, User>();
		for(int i=0;i<=10;i++) {
			User user = createRandomUser();
			userMap.put(Integer.toString(user.getUserID()), user);
		}
		User admin = new User(1, "admin", "2000-8-8", true);
		userMap.put("1", admin);
		return userMap;
	}
	//至此创建了一个业务逻辑模型UserService,以备视图层(JSP+EL+JSTL)测试使用
}

JavaBean的分类

分为:FormBean和EntityBean

使用FormBean后,Servlet从表单中获取请求参数的时候,不需要再调用request.getParameter()方法,直接调用FormBean的getter方法即可

FormBean显著特点如下:

FormBean通常提供一组表单验证的方法(validate())

FormBean的成员变量通常与Form表单的表单控件一一对应

FormBean可以借助第三方MVC架构实现,如SpringMVC 、Struts 、JSF等

EntityBean的成员变量通常和数据库表中的字段一一对应

EntityBean需要提供EntityBean与EntityBean之间的各种映射关系


JSP的内置对象

前三个已经学过,request、response和session

另外三个为out、application、pageContext

内置对象out

out与response.getWriter()的执行流程不同

response.getWriter().print()方法的执行流程:将字符数据直接添加到 response 缓存中。

out.print()方法的执行流程:首先将字符数据写入out内置对象的缓存中,out内置对象缓存中的数据再择机写入response的缓存中

例如

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<%="a"%>
b
<%
    out.write("c");
%>
<%
    response.getWriter().write("d");
%>

程序执行的结果是d a b c

因为abc都被加入out的缓存中,而d直接添加到response缓存中

对此,我们可以使用out.flush()方法控制缓存的刷新时间,继而控制字符数据的输出顺序,下面的程序在3个文本型数据a、b、c后各添加了一个out.flush()方法,程序执行的结果是,“abcd”通过采用了3次强制刷新的方法,虽然可以控制字符数据的输出顺序,但是效率低下

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<%="a"%>
<% out.flush(); %>
b
<% out.flush(); %>
<%
    out.write("c");
    out.flush();
%>
<%
    response.getWriter().write("d");
%>

内置对象application和Servlet中的ServletContext

JSP的内置对象application实际上是Servlet中servletContext类的实例化对象

这个对象全局唯一,而且工程内部的所有servlet都共享这个对象。所以叫全局应用程序共享对象。

servletContext实例化对象功能主要两个:

1、获取Web项目部署后的绝对物理路径

request.getServletContext.getRealPath("")方法可以获取Web项目部署后的绝对物理路径

等同于application.getRealPath("")

2、作为域对象

(1)向ServletContext对象绑定属性,绑定后全局共享当然也可以换成application.setAttribute(name,object)

(2)获取ServletContext对象绑定的属性,application也可以

内置对象pageContext

JSP的内置对象pageContext提供了当前JSP页面的容器,常用功能有两个

1. 获取其他内置对象(注意是当前的JSP页面的session、request response之类的)

2.作为域对象绑定和解绑属性

pageContext的典型应用是:使用JSTL遍历集合(Collection或者Map)遍历过程中产生的当前元素作为属性被绑定在pageContext域对象上

request针对当前请求有效,作为域对象,主要有请求包含和请求转发

session针对当前的session会话有效,作为域对象,主要用于权限控制

application是对整个Web项目有效,统计在线人数经常使用


EL表达式语言

EL语法格式:以$符号开头,表达式的内容包括在{}之中

EL的功能:在JSP页面中,获取并输出对象的属性值(attribute属性或者JavaBean的property命名属性)通过:“.”运算符或者“[]”运算符获取

例如${user.userName}或者${user["userName"]}

1.“.”运算符后面的属性名,必须符合Java变量名的命名规范,否则必须使用“[]”运算符

2.使用[]运算符,引号不能省略,但可以是单引号

EL的功能和优点

EL作为JSP规范的

一部分,通常用于获取变量的值并输出变量的值,而不用于设置变量值。

例如以红色字体,显示Session会话中的用户名

<font color='red'>
<%
String userName = (String)session.getAttribute("userName");
if(userName == null){
    userName = "";
}else{
    userName = (String)session.getAttribute("userName");
}
out.print(userName);
%>
</font>

如果替换成EL则为

<font color='red'>${sessionScope.userName}</font> 

EL的核心功能就是:获取变量的值,判断是否为null,强制转换类型为字符串,最后输出到HTML页面

不要在EL中试图调用对象的方法

EL内置对象

El一共11个内置对象,无需创建即可使用。这11个内置对象中有10个是Map类型,最后是pageContext对象和intiParam

scope是域对象,param、header和cookie是HTTP请求对象

pageScope:获取pageContext域属性,相当于pageContext.getAttribute("xxx")

requestScope:获取request域属性,相当于request.getAttribute("xxx")

sessionScope:获取session域属性,相当于session.getAttribute("xxx")

applicationScope:获取application域属性,相当于application.getAttribute("xxx")

param:对应参数,它是一个Map,其中key是参数,value是参数值,适用于单值的参数,相当于request.getParameter("xxx")

paramValues:对应参数,她是一个Map,其中key是参数,value是多个参数值,适用于多值的参数,相当于request.getParameterValues("xxx")

header:对应请求头,它是一个Map,其中key表示头名称,value是单个头值,适用于单值的请求头,相当于request.getHeader("xxx")

headerValues:对应请求头,它是一个Map,其中key表示头名称,value是多个头值,适用于多值的请求头,相当于request.getHeaders("xxx")

initParam:获取web.xml中<context-param>内的参数,${ initParam.xxx},xxx就是<param-name>标签内的值,进而得到<param-value>中的值

cookie:用于获取cookie,Map<String,Cookie>,其中key是cookie的name,value是cookie对象,例如${cookie.JSESSIONID.value }就是获取sessionId

pageContext:可以获取JSP九大内置对象,相当于使用该对象调用getxxx()方法,例如pageContext.getRequest()可以写为${pageContext.request)

小露身手:EL的使用

创建controller的JSTLServlet

package controller;

import java.io.IOException;

import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
@WebServlet("/JSTLServlet")
public class JSTLServlet extends HttpServlet {
	protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
		String action = request.getParameter("action");
		response.setContentType("text/html;charset=UTF-8");
		if("".equals(action) || action == null) {
			response.getWriter()
			.append("本页面是程序首页,为浏览器用户提供了若干功能选项<br/>")
			.append("在URL后添加<font color='red'>查询字符串</font>,测试EL表达式语言各内置对象<br/>")
			.append("?action=scope 使用EL获取并输出4个域对象的属性值<br/>")
			.append("?action=param 使用EL获取并输出请求参数的值<br/>")
			.append("?action=bean 使用EL获取JavaBean命名属性的值<br/>")
			.append("?action=pageContext 使用EL获取pageContext对象<br/>")
			.append("?action=header 使用EL获取HTTP请求头和Cookie请求头<br/>")
			.append("?action=empty empty运算符的使用<br/>")
			.append("?action=foreach JSTL循环的使用<br/>")
			;
			return;
		}	
		//所有新增的功能代码添加到此处		
		if("scope".equals(action)) {
			request.setAttribute("userName", "张三request");
			javax.servlet.http.HttpSession httpSession = request.getSession();
			httpSession.setAttribute("userName", "张三session");
			javax.servlet.ServletContext servletContext =  request.getServletContext();
			servletContext.setAttribute("userName", "张三application");
			request.getRequestDispatcher("/scope.jsp").include(request, response);
			return;
		}
		if("param".equals(action)) {
			String name = java.net.URLEncoder.encode("张三","UTF-8");
			String paramPath = "/param.jsp?userName=" + name + "&hobby=shopping&hobby=music";
			request.getRequestDispatcher(paramPath).include(request, response);
			return;
		}
		if("bean".equals(action)) {
			bean.User user = service.UserService.createRandomUser();		
			request.setAttribute("user", user);
			java.util.List<bean.User> userList = service.UserService.getUserList();
			request.setAttribute("userList", userList);
			java.util.Map<String, bean.User> userMap = service.UserService.getUserMap();
			request.setAttribute("userMap", userMap);
			request.getRequestDispatcher("/bean.jsp").include(request, response);
			return;
		}
		if("pageContext".equals(action)) {
			request.getRequestDispatcher("/pageContext.jsp").include(request, response);
			return;
		}
		if("header".equals(action)) {			
			Cookie userNameCookie = new Cookie("userName","userNameCookie");
			response.addCookie(userNameCookie);
			response.sendRedirect(request.getContextPath()+"/header.jsp");
			return;
		}
		if("empty".equals(action)) {
			request.setAttribute("emptyString", "");
			request.setAttribute("nullValue", null);
			request.setAttribute("emptyList", new java.util.ArrayList());
			request.setAttribute("emptyMap", new java.util.HashMap());
			request.setAttribute("emptySet", new java.util.HashSet());			
			request.setAttribute("zeroInt", 0);
			request.setAttribute("zeroFloat", 0.0);
			request.setAttribute("falseValue", false);
			request.setAttribute("blank", " ");
			request.getRequestDispatcher("/empty.jsp").include(request, response);
			return;
		}
		if("three".equals(action)) {
			String paramPath = "/three.jsp?sex=m";
			request.getRequestDispatcher(paramPath).include(request, response);
			return;
		}
		if("foreach".equals(action)) {
			bean.User user = service.UserService.createRandomUser();		
			request.setAttribute("user", user);
			java.util.List<bean.User> userList = service.UserService.getUserList();
			request.setAttribute("userList", userList);
			java.util.Map<String, bean.User> userMap = service.UserService.getUserMap();
			request.setAttribute("userMap", userMap);
			request.getRequestDispatcher("/foreach.jsp").include(request, response);
			return;
		}
		if("remove".equals(action)) {
			request.setAttribute("userName", "zhangsan");
			request.getRequestDispatcher("/remove.jsp").include(request, response);
			return;
		}
		//通知浏览器用户,更多功能尚待开发。
		response.setContentType("text/html;charset=UTF-8");
		response.getWriter().print("该功能尚未提供,期待您的开发升级!<br/>");
	}
}

创建scope.jsp

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<h3>使用EL获取并输出4个域对象的属性值</h3>
${ userName }<br/>
${ requestScope.userName }<br/>
${ sessionScope.userName }<br/>
${ applicationScope.userName }<br/>
<% pageContext.setAttribute("userName", "张三pageContext"); %>
${ userName }<br/>
${ pageScope.userName }<br/>

COOKIE和SESSION:

服务器存有sessionID,当你浏览器的Cookie的session与之吻合即可 ?

ps.关闭服务器session默认还能坚持20分钟

与上述地址结合自行理解一下

结论:如果不指定作用范围EL将按照pageContext request session ServletContext域对象的顺序,依次查找被绑定的属性

我出现了

 javax.servlet.ServletException: 文.件[/product/productList.jsp] 未找到错误,只需要清理一下缓存即可

这一个param.jsp是获取和输出请求参数的值

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<h3>使用EL获取并输出请求参数的值</h3>
${param}<br/>
${paramValues}<br/>
${param.userName}<br/>
${paramValues.hobby}<br/>
${paramValues.hobby[0]}<br/>
${paramValues.hobby[1]}<br/>

下一个是获取javaBean命名属性的值

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<h3>使用EL获取JavaBean命名属性的值</h3>
${requestScope.user}<br/>
${requestScope.user.userID}<br/>
${requestScope.user.userName}<br/>
${requestScope.user.birthday}<br/>
${requestScope.user.age}<br/>
${requestScope.user.admin}<br/>
<h3>List对象</h3>
${requestScope.userList}<br/>
${requestScope.userList[3].userID}<br/>
${requestScope.userList[3].userName}<br/>
${requestScope.userList[3].birthday}<br/>
${requestScope.userList[3].age}<br/>
${requestScope.userList[3].admin}<br/>
<h3>Map对象</h3>
${requestScope.userMap}<br/>

本场景中,由于Map中的键是随机数,EL无法通过键获取Map元素,但可以通过jstl遍历的方式,获取map中的元素。


使用EL获取pageContext对象

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<h3>使用EL获取pageContext对象</h3>
${pageContext['request']}<br/> 
${pageContext.session}<br/>
${pageContext['servletContext']}<br/>
${pageContext.request.servletContext}<br/>
${pageContext.response}<br/>
${pageContext.request.contextPath}<br/>
<%= pageContext.getRequest().getServletContext().getContextPath() %><br/>
<%= pageContext.getServletContext().getContextPath() %><br/>

作为EL的内置对象,最常用的还是在jsp页面互殴Web项目虚拟路径${pageContext.request.contextPath}


使用EL获取HTTP请求头和Cookie请求头

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<h3>使用EL获取HTTP请求头和Cookie请求头</h3>
${header}<br/><br/>
${headerValues}<br/><br/>
${header['user-agent']}<br/><br/>
${header.cookie}<br/><br/>
${cookie}<br/><br/>
${cookie.userName}<br/>
${cookie.userName.name}<br/>
${cookie.userName.value}<br/>
${cookie.userName.maxAge}<br/>
${cookie.userName.secure}<br/>

${header['user-agent']}不能写成${header.user-agent},这是因为“user-agent”中包含短横线,这不是有效的Java变量名

我们知道EL的header以Map<String,String> 获取并输出HTTP所有请求头

而headerValues以Map<String,String[]>获取和输出HTTP所有请求头

Cookie以Map<String,String> 获取并输出Cookie所有请求头

${header.cookie}将请求头中的Cookie以字符串的形式输出 


empty运算符的使用

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<h3>empty运算符的使用</h3>
${empty requestScope.emptyString}<br/>
${empty requestScope.nullValue}<br/>
${empty requestScope.emptyList}<br/>
${empty requestScope.emptyMap}<br/>
${empty requestScope.emptySet}<br/>
${empty requestScope.notExist}<br/>
${empty requestScope.zeroInt}<br/>
${empty requestScope.zeroFloat}<br/>
${empty requestScope.falseValue}<br/>
${empty requestScope.blank}<br/>
${"1" + "2"}

EL关键字有如下:

and、eq、gt、true、instanceof、or、ne、le、false、empty、not、lt、ge、null、div、mod

注意事项有两点:

1.         0不代表空

2.         加号表示算术运算符

EL支持+、-、* 、/(或者div)、%(或者mod)、<(或者lt)等等运算符,还支持三目运算符就是?:

JSTL

jsp标准标签库

JSTL不是jsp的规范,所以要下载资源放入WEB项目的lib下

<%@taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c"%>

必须先声明JSTL核心标签库,才能够在JSP页面中使用jstl核心标签库,标签的前缀定义为c(core单词的首字母)

小露身手:JSTL核心标签库的使用

创建if.jsp

<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<%@taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c"%>
<c:if test="${empty param.sex}">
性别:未知
</c:if>
<c:if test="${param.sex=='m'}">
性别:男
</c:if>
<c:if test="${param.sex=='f'}">
性别:女
</c:if>

<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<%@taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c"%>
<c:choose>
	<c:when test="${empty param.score}">
	成绩未提供
	</c:when>
	<c:when test="${param.score>=90 and param.score<=100}">
	优秀
	</c:when>
	<c:when test="${param.score>=80 and param.score<90}">
	良好
	</c:when>
	<c:when test="${param.score==85}">
	85
	</c:when>
	<c:when test="${param.score>=60 and param.score<80}">
	及格
	</c:when>
	<c:when test="${param.score<60 and param.score>=0}">
	不及格
	</c:when>
	<c:otherwise>
	成绩超出范围!
	</c:otherwise>
</c:choose>

localhost:8080/jstl/choose.jsp?score=85


<c:forEach>标签 的使用

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<%@taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
<h3>JSTL循环的使用</h3>
<hr/>
<c:forEach items="${requestScope.userList}" var="user" varStatus="status" >
${status.count}->${user.userID}->${user.userName}->${user.birthday}->${user.age}
<c:if test="${user.admin==true}">是管理员</c:if>
<c:if test="${user.admin!=true}">不是管理员</c:if>
<br/>
</c:forEach>
<hr/>
<c:forEach items="${requestScope.userMap}" var="map" varStatus="status" >
${status.count}->${map.key}->${map.value.userID}->${map.value.userName}->
${map.value.birthday}->${map.value.age}
<c:if test="${map.value.admin==true}">是管理员</c:if>
<c:if test="${map.value.admin!=true}">不是管理员</c:if>
<br/>
</c:forEach>
<hr/>
<c:forEach var="i" begin="1" end="10" step="2">
${i}
</c:forEach>

该标签有两种用法

语法1:是遍历集合(collection或者map)

<c:forEach [var = "item"] items="collection" [varStatus="varStatusName"]>

[]表示可选属性

items的属性值collection用于指定被遍历的集合,注意这个不是字符串啊

var属性的item用于存放遍历过程中的当前元素,item作为属性名,被绑定在了pageContext域对象,作用对象是pageScope

varStatus的属性值varStatusName是一个JavaBean的对象,记录了遍历的状态

varStatusName.index

varStatusName.count

语法2:循环指定的次数

< c: forEach var="每个变量名字"   items="要迭代的list"   varStatus="每个对象的状态"

                           begin="循环从哪儿开始"    end="循环到哪儿结束"    step="循环的步长">


<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<%@taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c"%>
${requestScope.userName}
<c:remove var="userName" scope="request"/>
${requestScope.userName}

这个标签的作用是在JSP页面中实现解除绑定作用域中的属性的功能

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

可靠的成年男子

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值