Struts2OGNL简单使用、contextMap数据操作、ValueStack存数据、s:property取数据、valueStack的其它方法、struts对EL取值顺序的改变-day05下

Struts2 专栏收录该内容
9 篇文章 0 订阅

第一节 Struts2的OGNL

1.1 OGNL简介

  • OGNL是Object Graphic Navigation Language(对象图导航语言)的缩写
  • 它是一个单独的开源项目。 Struts2框架使用OGNL作为默认的表达式语言。
  • OGNL是struts2整合的一个开源项目,所以在struts2中,要想使用OGNL表达式,必须要使用Struts2的标签库
  • OGNL相当于EL表示式,从作用域取数据

1.2 OGNL的简单使用

  • s:property类型于JSP的表达式,把value的值直接输出到页面
  • jsp表达式:【<jsp:setProperty property="" name=""/>
  • value的值就是一个OGNL表达式,它不是一个字符串
  • 如果想把value的值当字符串输出,加单引号即可
  1. index.jsp:
<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<%@ taglib uri="/struts-tags" prefix="s"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Insert title here</title>
</head>
<body>
<%-- OGNL表达式
	首先:要在jsp页面导入<%@ taglib uri="/struts-tags" prefix="s"%>
	OGNL的作用:
	1.取值,或者说输入数据到jsp页面
		s:property的value不写单引号,代表要从作用域取值,如果写了单引号,则代表它就是一个字符串
	2.调用方法
	3.访问静态属性/方法
	  默认情况下,strut2把静态属性的访问禁止,如果要使用,在struts.xml中配置
	  <constant name="struts.ognl.allowStaticMethodAccess" value="true"></constant>
	4.封装List数据,数组用花括号
	  <s:radio list="{'',''}" name="gender" label="性别"/>
	5.封装Map数据,用#{'key1':'value1','key2':'value2'} 
--%>
 访问对象方法:<br>
 <s:property value="'java-struts2'"/>的长度为:
 <s:property value="'java-struts2'.length()"/><br>
 访问静态属性:<br>
 int的最大值:<s:property value="@java.lang.Integer@MAX_VALUE"/><br>
 访问静态方法:<br>
 随机数:<s:property value="@java.lang.Math@random()"/><br>
 封装lsit数据:<br>
 <s:radio list="{'',''}" name="gender" label="性别"></s:radio><br>
 封装Map数据:<br>
 <s:radio list="#{'male':'','female':''}" name="gender1" label="性别1"></s:radio><br>
</body>
</html>

效果:
在这里插入图片描述

1.3 OGNL的功能

  1. 访问对象方法
    如上面index.jsp中字符串的length方法
  2. 访问静态属性(index.jsp)
  3. 访问静态方法(index.jsp)
    默认OGNL是禁止静态方法访问的,在default.properties中有个struts.ognl.allowStaticMethodAccess=false,如想使用,需要在struts.xml文件中配置允许访问静态方法(index.jsp)
  4. 封装list数据(index.jsp)
  5. 封装Map数据(index.jsp)
  6. struts.xml中写OGNL
    1. day05上中的文件下载案例就是一个OGNL表达式
      在这里插入图片描述
    2. 修改DownloadAction.java
      在这里插入图片描述
    3. 修改struts.xml
      在这里插入图片描述
    4. 效果
      在这里插入图片描述

第二节 contextMap

2.1 动作类的生命周期

  • struts使用contextMap存数据
  • 动作类是多例的,每次动作访问,动作类都会实例化,所以是线程安全的。
  • 与Struts1的区别是,struts1的动作类是单例的。
    例:
  1. 写一个TestAction.java
package com.it.web.action;

import com.opensymphony.xwork2.ActionSupport;

public class TestAction extends ActionSupport{

	@Override
	public String execute() throws Exception {
		System.out.println(this);
		//org.apache.struts2.dispatcher.ng.filter.StrutsPrepareAndExecuteFilter
		return SUCCESS;
	}
}
  1. struts.xml
<package name="p1" extends="struts-default">
	<action name="test" class="com.it.web.action.TestAction">
	</action>
</package>
  1. 多次访问:http://localhost:8080/day05_struts2_demo2-ognl/test
    发现每次地址都不同,避免多线程访问同一个变量,所以是线程安全的。
    在这里插入图片描述

2.2 请求数据的存放

  • 每次请求时,都会产生一些请求数据,这些数据存放到哪里去了?
  • 在每次动作执行前,核心控制器StrutsPrepareAndExecuteFilter都会创建一个ActionContextValueStack对象,且每次动作访问都会创建。
  • 这两个对象存储了整个动作访问期间用到的数据。
  • 并且把数据绑定到了线程局部变量(ThreadLocal)上了,所以是线程安全的。
    在这里插入图片描述
    在这里插入图片描述

2.3 contextMap存储数据

  • Struts2的官方文档对contextMap的说明:
    在这里插入图片描述
contextMap(=ActionContext)中存放的主要内容
KeyValue说明
value stack (root)java.util.List 没有root这个key。它是一个list。
applicationjava.util.Map<String,Object>ServletContext中的所有属性
session java.util.Map<String,Object> HttpSession中的所有属性。
request java.util.Map<String,Object> ServletRequest中的所有属性。
parameters java.util.Map参数
attr java.util.Map 把页面、请求、会话、应用范围内的所有属性放到一起。

root栈:继承了List接口,又称之为对象栈
map栈:实现了Map接口,又可以称之为上下文栈(context)
除了value stack之外,全是map,而contextMap也是一个map。
其实就是Map中又封装的Map。(很像dbutils中KeyedHandler封装数据的结构,只是封装数据的结构)

  • 使用s:debug查看contextMap的数据
    在这里插入图片描述
    在这里插入图片描述

2.4 测试存储数据

  1. test.jsp
<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<%@ taglib uri="/struts-tags" prefix="s"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Insert title here</title>
</head>
<body>
<%session.setAttribute("username", "shu"); %>
<s:debug></s:debug>
</body>
</html>

这里要先存数据,再debug显示,不然不显示

  1. struts.xml
<package name="p1" extends="struts-default">
	<action name="test" class="com.it.web.action.TestAction">
		<result>/test.jsp</result>
	</action>
</package>
  1. 效果
    在这里插入图片描述

第三节 contextMap中的数据操作

3.1 存数据

在这里插入图片描述

利用ActionContext存数据

  1. Test1Action.java
package com.it.web.action;

import java.util.Map;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpSession;

import org.apache.struts2.ServletActionContext;

import com.opensymphony.xwork2.ActionContext;
import com.opensymphony.xwork2.ActionSupport;

public class Test1Action extends ActionSupport{

	@Override
	public String execute() throws Exception {
		// TODO Auto-generated method stub
		//往cotextMap存数据
		//1.获取ActionContext
		ActionContext contextMap = ActionContext.getContext();
		
		//2.往cotextMap存数据
		contextMap.put("username", "shu01");
		contextMap.put("password", "shu01");
		
		//3.往cotextMap的session存数据
		Map<String, Object> sessionMap = contextMap.getSession();
		//也可以通过 HttpSession httpSession = ServletActionContext.getRequest().getSession();
		sessionMap.put("username", "shu02");
		sessionMap.put("password", "shu02");
		
		//4.往cotextMap的request存数据
		HttpServletRequest request = ServletActionContext.getRequest();
		request.setAttribute("username", "shu03");
		
		//5.往cotextMap的application存数据
		//application = ServletContext 【ServletActionContext.getServletContext()】
		Map<String, Object> applicationMap = contextMap.getApplication();
		applicationMap.put("username", "shu04");
	
		return SUCCESS;
	}
}

  1. struts.xml
 	<!-- 开发模式 -->
	<constant name="struts.devMode" value="true"></constant>
	
	<!-- 配置允许OGNL表达式调用静态方法、属性 -->
	<constant name="struts.ognl.allowStaticMethodAccess" value="true"></constant>
	
	<package name="p1" extends="struts-default">
		<action name="test1" class="com.it.web.action.Test1Action">
			<result>/test1.jsp</result>
		</action>
	</package>
  1. test1.jsp
<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<%@ taglib uri="/struts-tags" prefix="s"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Test1</title>
</head>
<body>
<s:debug></s:debug>
</body>
</html>
  1. 效果
    1.往cotextMap存数据
    在这里插入图片描述
    在这里插入图片描述
    2.往cotextMap的session存数据
    在这里插入图片描述
    3.往cotextMap的request存数据
    在这里插入图片描述
    4.往cotextMap的application存数据
    在这里插入图片描述

利用ValueStack存数据

三种获取ValueStack的方法

  1. 先提供一个User.java,username、password的getter、setter方法、有参构造
  2. Test2Action.java
package com.it.web.action;

import java.util.Map;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpSession;

import org.apache.struts2.ServletActionContext;

import com.opensymphony.xwork2.ActionContext;
import com.opensymphony.xwork2.ActionSupport;
import com.opensymphony.xwork2.util.ValueStack;

public class Test2Action extends ActionSupport{

	@Override
	public String execute() throws Exception {
		// TODO Auto-generated method stub
		//往ValueStack【值栈】存数据
		
		//1.第一种获取“值栈”的方法【推荐使用,方便简单】
		//获取“值栈”对象的引用
		ValueStack vs1 = ActionContext.getContext().getValueStack();
		//通过值栈存储信息
		vs1.push(new User("shu01", "123"));
		
		//2.第二种获取“值栈”的方法
		@SuppressWarnings("unchecked")
		Map<String,Object> requestMap = (Map<String,Object>) ActionContext.getContext().get("request");
		ValueStack vs2 = (ValueStack) requestMap.get("struts.valueStack");
		
		//3.第三种获取“值栈”的方法
		ValueStack vs3 = (ValueStack) ServletActionContext.getRequest().getAttribute("struts.valueStack");
		System.out.println(vs1);
		System.out.println(vs2);
		System.out.println(vs3);
		
		return SUCCESS;
	}
}

  1. struts.xml在上面的p1包下添加
<action name="test2" class="com.it.web.action.Test2Action">
		<result>/test2.jsp</result>
</action>
  1. 效果
    在这里插入图片描述
    在这里插入图片描述

3.2 取数据

  • 用Struts2的标签(OGNL表达式)在JSP上(用的最多)

使用s:property取数据

  • 取contextMap中的数据,需使用#+key
  • 取contextMap里面ValueStack中对象的属性:直接写属性名key
  1. Test3Action.java
package com.it.web.action;

import java.util.Map;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpSession;

import org.apache.struts2.ServletActionContext;

import com.opensymphony.xwork2.ActionContext;
import com.opensymphony.xwork2.ActionSupport;
import com.opensymphony.xwork2.util.ValueStack;

public class Test3Action extends ActionSupport{

	private String username = "shu action属性";
	//如果在Action的属性中有get方法,它会把这个数据存在ValueStack
	public String getUsername() {
		return username;
	}

	@Override
	public String execute() throws Exception {
		// TODO Auto-generated method stub
		//1.往ValueStack【值栈】存数据
		ValueStack vs1 = ActionContext.getContext().getValueStack();
		vs1.push(new User("shu05", "123"));
		
		//获取ActionContext
		ActionContext contextMap = ActionContext.getContext();
		//2.往cotextMap存数据
		contextMap.put("username", "shu01");
		
		//3.往cotextMap的session存数据
		Map<String, Object> sessionMap = contextMap.getSession();
		sessionMap.put("username", "shu02");
		
		//4.往cotextMap的request存数据
		HttpServletRequest request = ServletActionContext.getRequest();
		request.setAttribute("username", "shu03");
		
		//5.往cotextMap的application存数据
		Map<String, Object> applicationMap = contextMap.getApplication();
		applicationMap.put("username", "shu04");
		
		return SUCCESS;
	}
}

  1. test3.jsp
<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<%@ taglib uri="/struts-tags" prefix="s"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Test3</title>
</head>
<body>
<%-- 取值 <s:property value=""/>
	 value写的是OGNL表达
	 如果要取contextMap,写#+key名字
	 如果是取ValueStack,直接key名字 --%>
	 
<!-- 如果遇有对象属性重名,可以通过OGNL表达式,选择查找的起始位置-->
取ValueStack的username:<s:property value="[0].username"/><br>
取ValueStack的username:<s:property value="[1].username"/><br>

<!-- 这里的username、password不是字符串而是OGNL表达式 -->
取ValueStack的username:<s:property value="username"/><br>
取ValueStack的password:<s:property value="password"/><br>

取contextMap的:<s:property value="#username"/><br>
取contextMap的session的:<s:property value="#session.username"/><br>
取contextMap的request的:<s:property value="#request.username"/><br>
取contextMap的application的:<s:property value="#application.username"/><br>

<!-- 当s:property不给定value属性时,默认取栈顶对象。 -->
栈顶<s:property/>
<s:debug></s:debug>
</body>
</html>
  1. struts.xml添加
<action name="test3" class="com.it.web.action.Test3Action">
		<result>/test3.jsp</result>
</action>
  1. 效果
    在这里插入图片描述
  2. 解释
    1. private String username = “shu action属性”;需要提供get方法,ValueStack中才有它的值
      在这里插入图片描述
      2.在jsp页面取值时,可以通过索引取,也可以注释vs1.push(new User(“shu05”, “123”));,这时username就为shu action属性,如果不注释取的是User里的shu05
      在这里插入图片描述
      在这里插入图片描述
      注释vs1.push(new User(“shu05”, “123”));后:
      在这里插入图片描述
      3.当s:property不给定value属性时,默认取栈顶对象。
      在这里插入图片描述
      在这里插入图片描述
      4.存储2个username,后面的username为什么不替换前一个?
      它们所属的Action不同,一个是vs1.push(new User(“shu05”, “123”));属于User,一个是private String username = “shu action属性”;属于Test3Action
      在这里插入图片描述

3.3 valueStack的其它方法

  • 先提供一个User.java包含username、password的getter、setter、有参构造

put方法:往栈顶添加元素

  1. demo01.jsp导入struts标签库,配置debug
<%@ taglib uri="/struts-tags" prefix="s"%>
<s:debug></s:debug>
  1. Demo01Action.java
package com.it.web.action;

import com.it.model.User;
import com.opensymphony.xwork2.ActionContext;
import com.opensymphony.xwork2.ActionSupport;
import com.opensymphony.xwork2.util.ValueStack;

public class Demo01Action extends ActionSupport{
	
	@Override
	public String execute() throws Exception {
		// TODO Auto-generated method stub
		//往值栈存数据
		ValueStack vs = ActionContext.getContext().getValueStack();
	
		//1.push方法:往栈顶添加数据
		vs.push(new User("shu01", "123"));
		vs.push(new User("shu02", "124"));
		
		return SUCCESS;
	}
}

  1. struts.xml常规配置
<constant name="struts.devMode" value="true"></constant>

	<package name="p1" extends="struts-default">
		<action name="demo01" class="com.it.web.action.Demo01Action">
			<result>/demo01.jsp</result>
		</action>
	</package>
  1. 效果:它不会替换,会压栈,先进后出
    在这里插入图片描述

setValue方法:更改栈顶元素的值

  1. 修改Demo01Action.java的代码,注释一个push,留一个做栈顶,其它不变
	//1.push方法:往栈顶添加数据
	vs.push(new User("shu01", "123"));
	//vs.push(new User("shu02", "124"));
	
	//2.setValue方法:更改栈顶的username的值
	vs.setValue("username", "shu03");
  1. 效果
    在这里插入图片描述
  2. 注意,如果栈里没有setValue所修改的key,会报错
//栈中没有key为username2,会报错
vs.setValue("username2", "shu03");
//Error setting expression 'username2' with value 'shu03' - [unknown location]

在这里插入图片描述
4. 其它
注释put,添加一个private String username = “shu”;此时User不再是栈顶,Demo01Action中的username为栈顶

private String username = "shu";
	public String getUsername() {
		return username;
	}
		//1.push方法:往栈顶添加数据
		//vs.push(new User("shu01", "123"));
		//vs.push(new User("shu02", "124"));
		

在这里插入图片描述
再使用setValue修改栈顶值,还需要提供userneme的setter方法,否则报错

		//2.setValue方法:更改栈顶的username的值
		vs.setValue("username", "shu03");

在这里插入图片描述
设置setter后:

public class Demo01Action extends ActionSupport{
	private String username = "shu";
	public String getUsername() {
		return username;
	}
	public void setUsername(String username) {
		this.username = username;
	}
	@Override
	public String execute() throws Exception {
		// TODO Auto-generated method stub
		//往值栈存数据
		ValueStack vs = ActionContext.getContext().getValueStack();
		//1.push方法:往栈顶添加数据
		//vs.push(new User("shu01", "123"));
		//vs.push(new User("shu02", "124"));
		
		//2.setValue方法:更改栈顶的username的值
		vs.setValue("username", "shu03");
		return SUCCESS;
	}
}

在这里插入图片描述

setValue方法:往contextmap存数据

  1. 继续在Demo01Action.java中添加以下代码
	//3.setValue往contextmap存数据
	vs.setValue("#username3", "shu03");
  1. 效果
    在这里插入图片描述

set方法:往值栈栈顶存map数据

  1. Demo01Action.java
package com.it.web.action;

import com.it.model.User;
import com.opensymphony.xwork2.ActionContext;
import com.opensymphony.xwork2.ActionSupport;
import com.opensymphony.xwork2.util.ValueStack;

public class Demo01Action extends ActionSupport{

	private String username = "shu";
	public String getUsername() {
		return username;
	}

	public void setUsername(String username) {
		this.username = username;
	}

	@Override
	public String execute() throws Exception {
		// TODO Auto-generated method stub
		//往值栈存数据
		ValueStack vs = ActionContext.getContext().getValueStack();
		
		//1.push方法:往栈顶添加数据
		vs.push(new User("shu01", "123"));
		//vs.push(new User("shu02", "124"));
		
		//2.setValue方法:更改栈顶的username的值
		vs.setValue("username", "shu03");
		/**
		 * 如果栈里没有这key会报错,vs.setValue("username2", "shu03");
		 * Error setting expression 'username2' with value 'shu03' - [unknown location]
		 */
		
		//3.往contextmap存数据
		vs.setValue("#username3", "shu03");
		
			
		//4.set方法 :往值栈存map数据
		vs.set("user", new User("shu04", "125"));
		
		return SUCCESS;
	}
}

  1. 数据未显示,在jsp页面使用findValue方法取出栈顶map元素的内容
    在这里插入图片描述

findValue方法:取出栈顶map元素的内容

  1. 在上面的基础上使用findValue方法修改demo01.jsp
<%@page import="com.opensymphony.xwork2.util.ValueStack"%>
<%@page import="com.opensymphony.xwork2.ActionContext"%>
<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<%@ taglib uri="/struts-tags" prefix="s"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Demo01.jsp</title>
</head>
<body>
<!-- 用来看contextMap数据 -->
获取栈顶map的数据
<%
	ValueStack vs = ActionContext.getContext().getValueStack();
	String value = (String)vs.findValue("user.username");
	out.write(value);
%>
栈顶:<s:property/>
<s:debug></s:debug>
</body>
</html>
  1. 效果,成功取到了map中存的 shu04
    在这里插入图片描述
  2. 简便方法,推荐使用第二种,简单方便
获取栈顶map的数据方法1:
<%
	ValueStack vs = ActionContext.getContext().getValueStack();
	String value = (String)vs.findValue("user.username");
	out.write(value);
%>
<br>
获取栈顶map的数据方法2:<s:property value="user.username"/><br>
栈顶:<s:property/>
<s:debug></s:debug>

在这里插入图片描述

3.4 Struts中对EL取值顺序的改变

默认情况下EL查找顺序

  • 表达式取值必须是servlet四大作用域,由小到大排列为:
  • pageContext【page】 -> request -> session -> servletContext【application】

在Struts中EL表达式的查找顺序的改变

  • page –> request -> ValueStack(根中)-> contextMap -> session -> application

案例

  1. Demo02Action.java
package com.it.web.action;

import org.apache.struts2.ServletActionContext;
import org.apache.struts2.dispatcher.StrutsRequestWrapper;

import com.it.model.User;
import com.opensymphony.xwork2.ActionContext;
import com.opensymphony.xwork2.ActionSupport;
import com.opensymphony.xwork2.util.ValueStack;

public class Demo02Action extends ActionSupport{

	@Override
	public String execute() throws Exception {
		//StrutsRequestWrapper srw;//源码
		// TODO Auto-generated method stub
		//使用struts后EL表达式的取数据顺序
		/**
		 * Servlet/JSP:存数据 page -> request -> session -> application
		 * Struts:存数据contextMap(Actioncontext),ValueStack
		 * 
		 */
		
		//1.往contextMap(Actioncontext)
		ActionContext.getContext().put("username", "shu-contextMap");
		
		//2.往值栈存数据
		ValueStack vs = ActionContext.getContext().getValueStack();
		vs.push(new User("shu-valueStack", null));
		
		//3.往request
		ServletActionContext.getRequest().setAttribute("username", "shu-request");
		
		//4.往session
		ServletActionContext.getRequest().getSession().setAttribute("username", "shu-session");
		return SUCCESS;
	}
}

  1. struts.xml中添加
<action name="demo02" class="com.it.web.action.Demo02Action">
		<result>/demo02.jsp</result>
</action>
  1. demo02.jsp
<%@page import="com.opensymphony.xwork2.util.ValueStack"%>
<%@page import="com.opensymphony.xwork2.ActionContext"%>
<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<%@ taglib uri="/struts-tags" prefix="s"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Demo01.jsp</title>
</head>
<body>
<!-- 使用EL表达式来取ActionContext,ValueStack数据
	  研究它的取值顺序:page -> request -> valueStack -> contextMap -> session -> applaction
 	  以前用Servlet的时候,在jsp中取数据从request中取
 	  如果使用struts2,在jsp中取数据,一般从ValueStack中取
 -->
${username}
<s:debug></s:debug>
</body>
</html>
  1. 运行结果
    在这里插入图片描述
    注释request后
    在这里插入图片描述
    再注释valueStack后
    在这里插入图片描述
    再注释contextMap后
    在这里插入图片描述

源码

在这里插入图片描述

内容补充

在这里插入图片描述

  • 2
    点赞
  • 0
    评论
  • 0
    收藏
  • 打赏
    打赏
  • 扫一扫,分享海报

参与评论 您还未登录,请先 登录 后发表或查看评论
©️2022 CSDN 皮肤主题:游动-白 设计师:白松林 返回首页

打赏作者

SYY96

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

¥2 ¥4 ¥6 ¥10 ¥20
输入1-500的整数
余额支付 (余额:-- )
扫码支付
扫码支付:¥2
获取中
扫码支付

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

打赏作者

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

抵扣说明:

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

余额充值