jsp&EL表达式&EL表达式&fn函数库&JSTL标签

JSP技术 (java server page)


该技术干嘛的
servlet先出现.只有servlet的时候,servlet又要处理逻辑又要负责输出html. 在servlet中输出 html 太痛苦. 推出了jsp技术.
专注于显示.


jsp的运行机制

.jsp (第一次访问jsp的时候)======> .java ======> .class(servlet) 

.jsp编译成.java文件和.class文件在tomcat的 work目录下.


JSP中的脚本
<%%> : 直接写java代码,出现在jsp的servlet中的service方法中.
<%!%>: 直接写java代码,出现在jsp生成的servlet的类中. 声明类中的变量, 方法.

<%=%>: 输出, 写在该脚本中的代码,最后会出现在service方法 中,并以 out.print(); 包裹.


JSP中的注释
<%-- --%> : 被注释掉的内容,不会生成到java文件中.

<!-- -->  : html注释,将html代码发送给浏览器之后,给浏览器看的.


JSP指令
三大指令:
page 页面中的一些信息(最复杂)
language: 描述当前页面使用的语言. 目前取值只有java.
buffer="8kb" (不常用) : 决定缓存的大小.
autoFlush="true"(不常用) : 如果缓存写满了.如果该属性为true,会将缓存中的自动输出到浏览器. 设置为false,将会报错.
**import="java.io.FileOutputStream" 该属性用来导包. 唯一一个可以出现多次的.
extends=""(不用) 绝对 jsp生成的java文件 继承哪个类.默认继承:org.apache.jasper.runtime.HttpJspBase.通过该属性可以改变.也必须是HTTPServlet的子类.
**pageEncoding="UTF-8"  决定服务器读取jsp时 采用什么编码读
**contentType="text/html; charset=UTF-8" 响应浏览器时 告诉浏览器用什么码表解码. 
以上两个属性,只需要指定一个,另外一个会自动指定.
errorPage=""(不常用)  当前jsp中出现了异常. 那么跳转到哪个页面.
isErrorPage="false"(不常用) 标识当前页面是否是处理错误的页面.
拓展: 错误页面可以使用如下统一配置
<error-page>
<error-code>500</error-code>
<location>/zhiling/Demo2.jsp</location>
</error-page>
session="true"(不要修改) 页面中是否 需要使用session对象.如果为false,那么session内置对象会消失. 默认为true.
include 页面包含指令(静态包含)

taglib 引入标签指令



九大内置对象.

类型   变量名称
-------------------------------------------------------------------
HttpServletResponse       response
HttpServletRequest          request (jsp四大域之一servlet中的request域)
HttpSession           session(jsp四大域之一servlet中的session域)
ServletContext             application(jsp四大域之一servlet中的application域)
Throwable   exception
ServletConfig           config
Object   page
JspWriter   out
PageContext             pageContext(jsp四大域之一)


1>page对象指向了当前servlet的实例.(一般没用)


2>JspWriter jsp中都是使用JspWriter再向外输出内容.
response.getWriter 和 JspWriter 有什么区别?
response.getWriter的输出会出现在JspWriter输出的前面.
JspWriter缓存会附加到response.getWriter缓存后.最终输出response.getWriter缓存.

注意:JSP中不要直接使用response.getWriter.


3>PageContext 对象
1.page域. 范围只在当前页面当中.(jsp技术中4个域中最小一个域).
存值
    pageContext.setAttribute("", "");
    取值
    pageContext.getAttribute("");
    删除一个值
    pageContext.removeAttribute("");
2.还可以操作其他3个域.
如何获得其他3个域代表的int值.PageContext中的常量.
    存值
    pageContext.setAttribute("name", "tom",PageContext.REQUEST_SCOPE );
    取值
    pageContext.getAttribute("name", PageContext.REQUEST_SCOPE);
    删除一个值
    pageContext.removeAttribute("name", PageContext.REQUEST_SCOPE);
    遍历所有键
    pageContext.getAttributeNamesInScope(PageContext.REQUEST_SCOPE);
3.还能获得其他8个内置对象. 
pageContext.getRequest();
   
    pageContext.getResponse();
   
    pageContext.getSession();
   
    pageContext.getServletContext();
   
    pageContext.getServletConfig();
   
    pageContext.getOut();
   
    pageContext.getException();
   

    pageContext.getPage();



JSP标签(本身库很大,有很多标签,全都不用了.)
<jsp:useBean id="u" scope="page" class="jyh.com.bean.User" ></jsp:useBean>
相当于如下java代码
User user = new User();
  pageContext.setAttribute("u", user);

<jsp:setProperty property="name" name="u" param="name" />
相当于如下java代
((User)pageContext.getAttribute("u")).setName(request.getParameter("name"));

<jsp:getProperty property="name" name="u"/>
相当于如下java代码
out.print(((User)pageContext.getAttribute("u")).getName());



JAVA BEAN

就是在写法上符合以下格式即可成为java bean

*1.所有作为属性保存的成员变量私有化 ==> java不能脱离基本特性==> 封装性
*2.有空参构造  ==>  使用内省可以帮你创建对象.
  3.属性由对应get/set方法 ==> 1.满足了封装性, 2.命名规律的话,内省就能分辩出哪些是操作属性的方法,那些不是.
 
符合上面的格式,对我们开发来说有什么好处?
可以应用JAVA的内省机制.

BeanUtils 工具类
提供一个populate方法,可以把表单提交的参数自动封装到Bean中.
并且可以自动进行类型转换.
转换范围是 8个基本数据类型.

我们也可以注册一个转换器,让BeanUtils可以转换其他类型

package com.jyh.bean;

import java.beans.BeanInfo;
import java.beans.Introspector;
import java.beans.PropertyDescriptor;
import java.io.IOException;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Map;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.apache.commons.beanutils.BeanUtils;
import org.apache.commons.beanutils.ConvertUtils;
import org.apache.commons.beanutils.Converter;

public class AServlet extends HttpServlet {

	private static final long serialVersionUID = 1L;

	public void doGet(HttpServletRequest request, HttpServletResponse response)
			throws ServletException, IOException {
		response.setContentType("text/html;charset=utf-8");
		doPost(request, response);
	}

	public void doPost(HttpServletRequest request, HttpServletResponse response)
			throws ServletException, IOException {
		response.setContentType("text/html;charset=utf-8");
		request.setCharacterEncoding("utf-8");
		User u1 = new User();
		User u2 = new User();
		User2 u3 = new User2();
		//自己通过使用内省封装参数.
		populate(request.getParameterMap(), u2);
		
		//使用BeanUtils包进行封装
		try {
			//注册一个转换器,告诉BeanUtils遇到什么样的类型该如何进行转换
			//意思意思就是说,把一个方法当做参数传进去,但是方法只能通过类或者对象调用
			//所以只能写一个类,类里面写上方法(具体的转换操作)
			ConvertUtils.register(new MyNewConverter(), Date.class);
			//这是将表单传过来的参数键值复制给u对象进行封装
			BeanUtils.populate(u1,request.getParameterMap());
		} catch (Exception e) {
			e.printStackTrace();
		}
		
		copyProperties(u1, u3);
		response.getWriter().println("BeanUtils包:" + u1 + "<br>自己编写的:" + u2 + "<br>从u1拷贝的:" + u3);
	}
	
	//将map中的内容拷贝到u中
	private void populate(Map<String,String[]> map, User u) {
		try {
			//获取所有传过来的参数键值对
			Map<String,String[]> params = map;
			BeanInfo info = Introspector.getBeanInfo(User.class);
			//获取对象的所有属性
			PropertyDescriptor[] pds = info.getPropertyDescriptors();
			//遍历属性 
			for(PropertyDescriptor pd : pds){
				//把当前遍历的属性名称当做key值在传过来的参数集合里面获取对应的值
				String[] param = params.get(pd.getName());
				//如果参数存在在将值赋给对应的属性
				//由于传过来的参数全都是String类型,而bean类中的属性有其它类型,所以就需要类型转换,这里就不再编写了
				if(param!=null && param.length>0){
					if(pd.getPropertyType().equals(String.class)){
						pd.getWriteMethod().invoke(u, param[0])	;
					}
				}
			}
		} catch (Exception e) {
			e.printStackTrace();
		}
	}

	//将u1中的内容拷贝到u2
	private void copyProperties(User u1, User2 u2) {
		try {
			//1 获得u1中信息
			BeanInfo info =	Introspector.getBeanInfo(u1.getClass());
			//2 获得 u1中的所有属性信息
			PropertyDescriptor[] pds = info.getPropertyDescriptors();
			//3 遍历这些属性描述
			for(PropertyDescriptor pd :pds){
					String propName = pd.getName();
					if (!propName.equals("class")) {
						//4 查看u2是否具有这些属性
						PropertyDescriptor pd2 = new PropertyDescriptor(propName, User2.class);
						
						if (pd2 != null) {
							//5 有该属性 ==> 获得该属性值,查看是否是null
							Object obj = pd.getReadMethod().invoke(u1);
							if (obj != null) {
								//6 不是null 拷贝到u2
								pd2.getWriteMethod().invoke(u2, obj);
							}
						}
					}
			}
		} catch (Exception e) {
			e.printStackTrace();
		}
	}

	//转换器
	public class MyNewConverter implements Converter{
		@SuppressWarnings("rawtypes")
		@Override
		public Object convert(Class arg0, Object arg1) {
			String birthdayStr = arg1.toString();//1990-01-01 ==> date
			SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd");
			try {
				return format.parse(birthdayStr);
			} catch (ParseException e) {
				e.printStackTrace();
			}
			return null;
		}
		
	}
}




EL表达式
封装内省的操作. 可以通过EL表达式操作JAVA BEAN.


功能: 替代页面上的代码
1.使用EL内置对象,获得数据
EL的内置对象如下:
1.requestScope
2.sessionScope
3.applicationScope
4.pageScope
通过以上4个内置对象可以对4大域进行访问
5.param 
6.paramValues  这两个对象封装了表单参数
7.header
8.headerValues 这两个对象封装了HTTP请求头
9.initParam 封装了web.xml中 配置
10.pageContex 封装了 9大内置对象中的 pageContext
11.cookie  封装了cookie信息

2.使用EL表达式可以获取JAVABEAN属性 集合项的值.
<%--
使用EL访问User对象
--%>
<%
User u = new User();
u.setName("tom");
request.setAttribute("user", u);
 %>
 ${requestScope.user.name}==><%((User)request.getAttribute("user")).getName(); %><br>
 ${requestScope.user['name']}==><%((User)request.getAttribute("user")).getName(); %><br>
<%
使用EL访问数组
String[] array = new String[]{"tom","jerry","jack","rose"};
request.setAttribute("array", array);
%>
 ${requestScope.array[2]} <br>
 <%
使用EL访问List
List<String> list = new ArrayList<String>();
list.add("jack");
list.add("rose");
request.setAttribute("list", list);
  %>
  ${requestScope.list[1]}<br>
  <%
使用EL访问Map
Map<String,String> map = new HashMap<String,String>();
map.put("birthday", "now");
map.put("haha.heihei", "hiahia");
request.setAttribute("map", map);
%>
${requestScope.map.birthday}<br>
${requestScope.map['birthday']}<br>
${requestScope.map['haha.heihei']}<br>
    
3.使用EL可以进行逻辑运算
<%
request.setAttribute("num1", 10);
request.setAttribute("num2", 20);
//EL表达式支持如下运算符.
%>
 
${num1 > num2} ==>${num1 gt num2}<br>
${num1 < num2} ==>${num1 lt num2}<br>
${num1 <= num2}==>${num1 le num2} <br>
${num1 >= num2}==>${num1 ge num2} <br>
${num1 == num2}==>${num1 eq num2} <br>
${num1 != num2}==>${num1 ne num2} <br>
${true && true}<br>
${true || true}<br>
${!true}<br>

${(num1 > num2)?"num1厉害":"老2厉害" }


JSTL标签库

JSTL:Java Standard Tag Libary

apache实现一套标准的标签库。(JCP.org)

由5部分组成:

*Core:核心

*Functions:EL函数(fn函数库)

Format:国际化

SQL:操作数据库

XML:操作XML


自定义EL函数和fn函数库的学习
1.什么是EL函数库,干什么用的?
简化页面中静态方法的调用,使用EL函数代替JAVA代码.
2.如何自定义EL函数库?

1>定义工具类,在类中定义静态方法


package com.jyh.bean;

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


public class ELTest{
	
	//这里是自定义EL表达式
	public static String getTime(String name){
		SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd hh:mm:ss");
		
		return simpleDateFormat.format(new Date().getTime()) + "----" + name;
	}

}


2>填写配置文件xxx.tld 放到WEB-INF下(可以直接去jstl包下找到META-INF目录下的fn.tld,复制过去改 )

<?xml version="1.0" encoding="UTF-8" ?>

<!-- 这里是自定义EL表达式注册 -->

<taglib xmlns="http://java.sun.com/xml/ns/j2ee"
  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee http://java.sun.com/xml/ns/j2ee/web-jsptaglibrary_2_0.xsd"
  version="2.0">
    
  <description>JSTL 1.1 functions library</description>
  <display-name>JSTL functions</display-name>
  <tlib-version>1.1</tlib-version>
  <!-- 为函数库命名,跟文件名一样是个好习惯 -->
  <short-name>myFn</short-name>
  <!-- 起一个包名,相当于标识,在页面中引用就靠它 -->
  <uri>http://www.jyh.com/myFn</uri>
  <function>
  	<!-- 定义表达式的名字,以后调用的时候就是函数库名后面跟着函数名 -->
    <name>getTime</name>
    <!-- 映射的类名 -->
    <function-class>com.jyh.bean.ELTest</function-class>
    <!-- 类中的方法 -->
    <function-signature>java.lang.String getTime(java.lang.String)</function-signature>
    <example>
      <c:if test="${myFn:getTime(name)}">
    </example>
  </function>

</taglib>



3>页面中先引入.
<%@ taglib prefix="myFn" uri="http://www.itcast.cn/myFn"  %>
再使用.

${myFn:getTime()}


3.学习系统自带的fn函数.
${fn:contains("hiahia", "hi")}<br> 判断是否包含
${fn:endsWith("abha", "ha")}<br>  判断是否以某字符串结尾(有BUG)
${fn:escapeXml("<font color='red'>haha</font>")}<br> 自动将html关键字符转义

其它的可以去看官方文档或者直接去项目下的jstl包中的META-INFA目录下的fn.tld,里面有所有的fn函数以及简介



JSTL标签

1.JSTL标签的作用

自定义标签是属于JSP规范的。

开发原则:JSP中不要使用<%%>(标签替换)和<%=%>(EL表达式)

自定义标签的作用:替换掉JSP中的JSP脚本(<%%>),实现一些简单的逻辑运算。

2.自定义JSTL标签

1>编写一个类,实现一个接口javax.servlet.jsp.tagext.SimpleTag.或者继承javax.servlet.jsp.tagext.SimpleTagSupport。

类一:显示时间

package com.jyh.bean;

import java.io.IOException;
import java.text.SimpleDateFormat;
import java.util.Date;

import javax.servlet.jsp.PageContext;
import javax.servlet.jsp.tagext.SimpleTagSupport;

/**
 * 自定义显示时间标签
 * @author OverrideRe
 *
 */
public class ELTest extends SimpleTagSupport{
	
	private int count;
	
	public void setCount(int count) {
		this.count = count;
	}

	//这里是自定义JSTL标签,重写父类的doTag方法
	public void doTag(){
		SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd hh:mm:ss");
		String dateStr = simpleDateFormat.format(new Date().getTime());
		PageContext pContext = (PageContext)getJspContext();
		try {
			pContext.getOut().write(count + "次" +dateStr);
		} catch (IOException e) {
			e.printStackTrace();
		}
	}
}


类二:字符串处理

package com.jyh.bean;

import java.io.IOException;
import java.io.StringWriter;

import javax.servlet.jsp.JspException;
import javax.servlet.jsp.tagext.JspFragment;
import javax.servlet.jsp.tagext.SimpleTagSupport;
/**
 * 自定义处理字符串标签(转大写)
 * @author OverrideRe
 *
 */
public class ELDemo extends SimpleTagSupport{

	//这里是自定义EL标签,重写父类的doTag方法
		public void doTag(){
			JspFragment jf = getJspBody();//获取标签之间的文本内容
			StringWriter writer = new StringWriter();//带缓存的输出流,在使用上的差别就是不需要参数(目标文件)
			try {
				jf.invoke(writer);//将获得的文本内容写入到带缓冲的输出流
				String str = writer.toString();//将输出流里面的内容输出到字符串上
				str = str.toUpperCase();//将文本内容转换成大写
				getJspContext().getOut().write("转大写格式:" + str);
			} catch (JspException e) {
				e.printStackTrace();
			} catch (IOException e) {
				e.printStackTrace();
			}
		}
}


类三:自定义循环


package com.jyh.bean;

import java.io.IOException;
import java.lang.reflect.Array;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.Set;

import javax.servlet.jsp.JspException;
import javax.servlet.jsp.PageContext;
import javax.servlet.jsp.tagext.SimpleTagSupport;

/**
 * 自定义for循环标签标签
 * @author OverrideRe
 *
 */
public class ForEachSimpleTag extends SimpleTagSupport{
	
	private Object list;
	private String item;
	private Collection<Object> collection;
	

	@SuppressWarnings({ "unchecked", "rawtypes" })
	@Override
	public void doTag() throws JspException, IOException {
		PageContext pc = (PageContext) getJspContext();
		if(list instanceof List){
			collection = (Collection<Object>) list;
		}else if(list instanceof Set){
			collection = (Collection<Object>) list;
		}else if(list instanceof Map){
			collection = ((Map)list).entrySet();
//		}else if(list instanceof Object[]){//引用类型数组转换,如果不是引用类型则还需要另写一个,麻烦
//			collection = Arrays.asList(list);
//		}另一个利用反射的方法:
		}else if(list.getClass().isArray()){//判断是不是数组
			int len = Array.getLength(list);//获取长度
			for(int i = 0; i < len; i ++){
				collection.add(Array.get(list, i));
			}
		}
		for (Object obj : collection) {
			pc.setAttribute(item, obj);//将数组里面的每个元素塞入到页面的PageContext域中去,页面通过EL表达式取值
			getJspBody().invoke(null);//将从页面获取的文本内容打印到页面中去,即‘${S}’,打印在页面之后页面解析这个EL表达式
		}
		pc.removeAttribute(item);
	}
}



2>在WEB-INF目录下,建立一个扩展名为tld(Tag Libary Definition)的xml文件。

注:tld如果在WEB-INF或者在jar包的META-INF目录下,都能自动找到。

<?xml version="1.0" encoding="UTF-8"?>

<!-- 这里是自定义EL标签注册 -->

<taglib xmlns="http://java.sun.com/xml/ns/j2ee"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee http://java.sun.com/xml/ns/j2ee/web-jsptaglibrary_2_0.xsd"
    version="2.0">
    <tlib-version>1.0</tlib-version>
    <short-name>tag</short-name>
    <uri>http://www.jyh.com/taglib</uri>
    <tag>
        <name>showTime</name>
        <tag-class>com.jyh.bean.ELTest</tag-class>
        <!-- 标签之间有没有文本,没有就算empty -->
        <body-content>empty</body-content>
        <!-- 设置属性 -->
        <attribute>
        	<name>count</name><!-- 属性名 -->
        	<required>true</required><!-- 是否必填 -->
        	<rtexprvalue>true</rtexprvalue><!-- 是否支持表达式EL表达式和Jsp表达式 -->
        </attribute>
    </tag>
    
    <tag>
        <name>getText</name>
        <tag-class>com.jyh.bean.ELDemo</tag-class>
       <!-- empty:没有主题内容时使用
        	scriptless:有主题内容时使用,给简单标签用的
        	JSP:有主体内容时使用,给传统标签用的
        	tagdependent:有主体内容时使用,把主体内容当做普通字符串对待
        -->
        <body-content>scriptless</body-content>
    </tag>
    
    <tag>
        <name>forEach</name>
        <tag-class>com.jyh.bean.ForEachSimpleTag</tag-class>
        <body-content>scriptless</body-content>
        <!-- 设置属性 -->
        <attribute>
        	<name>list</name>
        	<required>true</required>
        	<rtexprvalue>true</rtexprvalue>
        </attribute>
        <attribute>
        	<name>item</name>
        	<required>true</required>
        	<rtexprvalue>false</rtexprvalue>
        </attribute>
    </tag>
</taglib>

3>(可选:tld不在WEB-INF或jar的META-INF目录下)修改web.xml,对tld和url进行一个映射


<jsp-config>
  	<taglib>
  		<taglib-uri>http://www.jyh.com/taglib</taglib-uri>
  		<taglib-location>/tag.tld</taglib-location>
  	</taglib>
  </jsp-config>


4>在JSP中引入

<%@page import="com.jyh.bean.ELTest"%>
<%@ page language="java" import="java.util.*" pageEncoding="UTF-8"%>
<%@taglib prefix="myFn" uri="http://www.jyh.com/myFn"%>
<%@taglib prefix="tag" uri="http://www.jyh.com/taglib"%>
<%
String path = request.getContextPath();
String basePath = request.getScheme()+"://"+request.getServerName()+":"+request.getServerPort()+path+"/";
%>

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
  <head>
    <base href="<%=basePath%>">
    
    <title>My JSP 'EL.jsp' starting page</title>
    
	<meta http-equiv="pragma" content="no-cache">
	<meta http-equiv="cache-control" content="no-cache">
	<meta http-equiv="expires" content="0">    
	<meta http-equiv="keywords" content="keyword1,keyword2,keyword3">
	<meta http-equiv="description" content="This is my page">
	<!--
	<link rel="stylesheet" type="text/css" href="styles.css">
	-->

  </head>
  
  <body>
  	<%request.setAttribute("name", "requestTom"); %>
    ${requestScope.name}<br>
    <%session.setAttribute("name", "sessionTom"); %>
    ${sessionScope.name}<br>
    <%application.setAttribute("name", "applicationTom"); %>
    ${applicationScope.name}<br>
    <%pageContext.setAttribute("name", "pageContextTom"); %>
    ${pageScope.name}<br>
    ${2 + 2}
    <hr>
    <%=ELTest.getTime("tom")%><br>
    ${myFn:getTime("myFn")}自定义fn函数
    <hr>
    <%
    	pageContext.setAttribute("p1", " ");
    	List list = new ArrayList();
    	list.add("p2");
    	pageContext.setAttribute("p2", list);
    %>
    ${empty p1}<br>
    ${empty p2}<br>
    <hr>
    自定义EL标签:<br>
    <tag:showTime count="3"/>WEB-INF目录下的<br>
    <tags:showTime/>非WEN-INF和META-INF目录下的,路径通过web.xml映射<br>
    <tag:getText>这里是文本啊abcd</tag:getText><br>
    循环标签:
    <%
    	List arrayList = new ArrayList();
    	arrayList.add("aaa");
    	arrayList.add("bbb");
    	arrayList.add("ccc");
    	pageContext.setAttribute("list", arrayList);
     %>
    <tag:forEach item="s" list="${list}">
    	${s}
    </tag:forEach>
  </body>
</html>


3.学习系统自带的JSTL标签(核心标签库)

查看官方文档或者查看jstl包下的META-INF目录下的c.tld文件



  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值