JavaWeb-Servlet2-Method

本文通过使用Servlet优化新闻管理功能,介绍了如何在一个Servlet中处理多种操作,利用反射机制动态选择调用方法,并实现了错误页面的处理。

话说:
在JavaWeb-Servlet-News(CURD)这篇博客里面,我们用Servlet改造了新闻(News),在JavaWeb-Servlet-Properties、JavaWeb-Servlet-Reflect两篇里面,分别做了优化。今天继续走在优化的道路上吧!
目标


1、优化多个Servlet的局面

1)根据参数选择不同的Servlet
2)利用反射机制,动态选择调用方法

2、错误页面处理(404、500、400)


1、优化多个Servlet的局面

1)根据参数选择不同的Servlet

细心的读者会发现,之前笔者用Servlet来写CURD的时候,每实现一个功能,就建立了一个对应的Servlet,如图:

这里写图片描述

这样很麻烦,一个项目里面,有几十个功能,那都这么写……..会哭的。所以嘛,我们希望可以再一个Servlet里面写多个Servlet,或者实现类似的功能。我们创建一个NewsServlet,希望用它来达到一劳永逸的效果。

上代码:
在Servlet包下面新建一个NewsServlet类:

package com.hmc.jdbc.news.servlet;
import com.hmc.jdbc.news.dao.NewsDao;
import com.hmc.jdbc.news.model.News;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.List;

/**
 * User:Meice
 * 2017/10/5
 */
public class NewsServlet extends HttpServlet {
    NewsDao nd = new NewsDao();
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        doPost(req,resp);
    }

    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
       //设置编码
        req.setCharacterEncoding("utf-8");
        resp.setCharacterEncoding("utf-8");

        //根据传递参数决定方法跳转 op代表你访问页面传递的参数operation之含义
        String op = req.getParameter("op");
        if(op !=null && !"".equals(op)) {
            if("list".equals(op)) {
                System.out.println("调用newsShow()方法");
            }else if("add".equals(op)) {
                System.out.println("调用newsAdd()方法");
            }else  if("update".equals(op)) {
               System.out.println("调用newsUpdate()方法");


            }else if("del".equals(op)) {
                System.out.println("调用newsDel()方法");


            }else {
                System.out.println("参数传递有误");
            }
        }else{
            System.out.println("参数缺失");
        }

    }

}

配置web.xml如下:
(为了保持思维连贯性,把整个web.xml配置都贴了出来,请看最后那一组配置)

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee
         http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd"
         version="3.1">

   <!--news Add Servlet-->

    <servlet>
        <servlet-name>newsAdd</servlet-name>
        <servlet-class>com.hmc.jdbc.news.servlet.NewsAddServlet</servlet-class>
    </servlet>

    <servlet-mapping>
        <servlet-name>newsAdd</servlet-name>
        <url-pattern>/newsAdd.do</url-pattern>
    </servlet-mapping>


    <!--news Show Servlet-->

    <servlet>
        <servlet-name>newsShow</servlet-name>
        <servlet-class>com.hmc.jdbc.news.servlet.NewsShowServlet</servlet-class>
    </servlet>

    <servlet-mapping>
        <servlet-name>newsShow</servlet-name>
        <url-pattern>/newsShow.do</url-pattern>
    </servlet-mapping>

    <!-- news Update Servlet-->
    <servlet>
        <servlet-name>newsUpdate</servlet-name>
        <servlet-class>com.hmc.jdbc.news.servlet.NewsUpdateServlet</servlet-class>
    </servlet>

    <servlet-mapping>
        <servlet-name>newsUpdate</servlet-name>
        <url-pattern>/newsUpdate.do</url-pattern>
    </servlet-mapping>

    <!--news Update Do Servlet-->
    <servlet>
        <servlet-name>newsUpdateDo</servlet-name>
        <servlet-class>com.hmc.jdbc.news.servlet.NewsUpdateDoServlet</servlet-class>
    </servlet>
    <servlet-mapping>
        <servlet-name>newsUpdateDo</servlet-name>
        <url-pattern>/newsUpdateDo.do</url-pattern>
    </servlet-mapping>

    <!--news Del Servlet-->
    <servlet>
        <servlet-name>newsDel</servlet-name>
        <servlet-class>com.hmc.jdbc.news.servlet.NewsDelServlet</servlet-class>
    </servlet>

    <servlet-mapping>
        <servlet-name>newsDel</servlet-name>
        <url-pattern>/newsDel.do</url-pattern>
    </servlet-mapping>


    <!--news Servlet 这个Servlet可以处理所有操作;只要你给我一个参数-->
    <servlet>
        <servlet-name>news</servlet-name>
        <servlet-class>com.hmc.jdbc.news.servlet.NewsServlet</servlet-class>
    </servlet>

    <servlet-mapping>
        <servlet-name>news</servlet-name>
        <url-pattern>/news.do</url-pattern>
    </servlet-mapping>

</web-app>


这里为了简单阐述清楚访问的情况,我们先简单的输出结果,看是否如此:
假如你在页面访问:
localhost:8080/JavaWeb_Servlet/news.do?op=list
localhost:8080/JavaWeb_Servlet/news.do?op=add
localhost:8080/JavaWeb_Servlet/news.do?op=update
localhost:8080/JavaWeb_Servlet/news.do?op=del

,控制台就输出调用对应的方法。因此,我们只需要把之前其他类似NewsAddServlet、NewsDelServlet…中的代码拷贝到对应位置即可,对跳转做酌情修改。

为了让界面更加整洁,我就补充两个功能的代码:list和del;补充后代码如下:

package com.hmc.jdbc.news.servlet;
import com.hmc.jdbc.news.dao.NewsDao;
import com.hmc.jdbc.news.model.News;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.List;

/**
 * User:Meice
 * 2017/10/5
 */
public class NewsServlet extends HttpServlet {
    NewsDao nd = new NewsDao();
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        doPost(req,resp);
    }

    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
       //设置编码
        req.setCharacterEncoding("utf-8");
        resp.setCharacterEncoding("utf-8");

        //根据传递参数决定方法跳转 op代表你访问页面传递的参数operation之含义
        String op = req.getParameter("op");
        if(op !=null && !"".equals(op)) {
            if("list".equals(op)) {
               // System.out.println("调用newsShow()方法");
                //1 处理编码(抽离出来了,在doPost()一开始统一处理)
                //2 处理业务逻辑,调用方法
                List<News> list = nd.list();
                //3 存储到req中
                req.setAttribute("list",list);
                req.getRequestDispatcher("newsShow.jsp").forward(req,resp);


            }else if("add".equals(op)) {
               // System.out.println("调用newsAdd()方法");
            }else  if("update".equals(op)) {
               System.out.println("调用newsUpdate()方法");
            }else if("del".equals(op)) {
               // System.out.println("调用newsDel()方法");
                //1 接受参数
                int id = 0;
                String strId = req.getParameter("id");
                if(strId != null && !"".equals(strId)) {
                    id = Integer.valueOf(strId);
                }
                //2 业务逻辑处理(按照id删除对应新闻)
                int result =  nd.newsDel2(id);
                //3 页面跳转
                if(result >0 ) {
                    req.getRequestDispatcher("newsShow.do").forward(req,resp);
                }else {
                    req.getRequestDispatcher("newsShow.do").forward(req,resp);
                }


            }else {
                System.out.println("参数传递有误");
            }
        }else{
            System.out.println("参数缺失");
        }

    }

}

总结:
以上代码,把设置编码抽离了出来,统一在doPost()方法里面设置,只用设置一次;NewsDao实例化也是在类一开始就实例化了,因为后面的方法基本都要用到,未避免多次实例化,占用内存和资源,只实例化一次。

然鹅:如果功能一多起来,看起来还是混乱,不便于整理。所以,我们把相关功能方法封装起来,在判断的时候直接调用方法。改进如下:

package com.hmc.jdbc.news.servlet;
import com.hmc.jdbc.news.dao.NewsDao;
import com.hmc.jdbc.news.model.News;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.List;

/**
 * User:Meice
 * 2017/10/5
 */
public class NewsServlet extends HttpServlet {
    NewsDao nd = new NewsDao();
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        doPost(req,resp);
    }

    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
       //设置编码
        req.setCharacterEncoding("utf-8");
        resp.setCharacterEncoding("utf-8");

        //根据传递参数决定方法跳转 op代表你访问页面传递的参数operation之含义
        String op = req.getParameter("op");
        if(op !=null && !"".equals(op)) {
            if("list".equals(op)) {
               // System.out.println("调用newsShow()方法");
                list(req,resp);

            }else if("add".equals(op)) {
               // System.out.println("调用newsAdd()方法");
            }else  if("update".equals(op)) {
               System.out.println("调用newsUpdate()方法");
            }else if("del".equals(op)) {
               // System.out.println("调用newsDel()方法");
                Del(req,resp);


            }else {
                System.out.println("参数传递有误");
            }
        }else{
            System.out.println("参数缺失");
        }

    }

    //未避免代码混乱,把显示新闻列表封装成方法
    private void list(HttpServletRequest req,HttpServletResponse resp) {
        List<News> list = nd.list();
        //3 存储到req中
        req.setAttribute("list",list);
        try {
            req.getRequestDispatcher("newsShow.jsp").forward(req,resp);
        } catch (ServletException e) {
        } catch (IOException e) {
        }

    }

    //同理,把删除封装成方法
    private  void Del(HttpServletRequest req,HttpServletResponse resp) {
        //1 接受参数
        int id = 0;
        String strId = req.getParameter("id");
        if(strId != null && !"".equals(strId)) {
            id = Integer.valueOf(strId);
        }
        //2 业务逻辑处理(按照id删除对应新闻)
        int result =  nd.newsDel2(id);
        //3 页面跳转
        try {
            if(result >0 ) {
                req.getRequestDispatcher("newsShow.do").forward(req,resp);
            }else {
                req.getRequestDispatcher("newsShow.do").forward(req,resp);
            }
        } catch (ServletException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }



}

然鹅,这表面看起来简洁了些,本质还不是需要自己去调用,下面我们利用反射机制,利用Method类来实现动态选择方法(自动选择)

2)利用反射机制,动态选择调用方法

要实现的目标就是避免用if () else{}来判断调用哪个方法。你参数传递的是什么方法名(?op= ),我就调用与参数op同名的方法。

package com.hmc.jdbc.news.servlet;
import com.hmc.jdbc.news.dao.NewsDao;
import com.hmc.jdbc.news.model.News;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.List;

/**
 * User:Meice
 * 2017/10/5
 */
public class NewsServlet extends HttpServlet {
    NewsDao nd = new NewsDao();
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        doPost(req,resp);
    }

    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
       //设置编码
        req.setCharacterEncoding("utf-8");
        resp.setCharacterEncoding("utf-8");

        //根据传递参数决定方法跳转 op代表你访问页面传递的参数operation之含义
        String op = req.getParameter("op");
        if(op !=null && !"".equals(op)) {

            try {
                //获取Method对象;这里和我们前面写过的this.getClass().getDeclaredField类似
                Method method = this.getClass().getDeclaredMethod(op,HttpServletRequest.class,HttpServletResponse.class);
                /**
                 * 调用invoke()方法;属于java.lang.reflect下面的类
                 * 自动调用指定的方法 参数1:方法所在的类 参数2:调用方法的参数
                 */
                method.invoke(this,req,resp);

            } catch (NoSuchMethodException e) {
                e.printStackTrace();
            } catch (IllegalAccessException e) {
                e.printStackTrace();
            } catch (InvocationTargetException e) {
                e.printStackTrace();
            }


        }else{
            System.out.println("参数缺失");
        }

    }

    //未避免代码混乱,把显示新闻列表封装成方法
    private void list(HttpServletRequest req,HttpServletResponse resp) {
        List<News> list = nd.list();
        //3 存储到req中
        req.setAttribute("list",list);
        try {
            req.getRequestDispatcher("newsShow.jsp").forward(req,resp);
        } catch (ServletException e) {
        } catch (IOException e) {
        }

    }

    //同理,把删除封装成方法
    private  void Del(HttpServletRequest req,HttpServletResponse resp) {
        //1 接受参数
        int id = 0;
        String strId = req.getParameter("id");
        if(strId != null && !"".equals(strId)) {
            id = Integer.valueOf(strId);
        }
        //2 业务逻辑处理(按照id删除对应新闻)
        int result =  nd.newsDel2(id);
        //3 页面跳转
        try {
            if(result >0 ) {
                req.getRequestDispatcher("newsShow.do").forward(req,resp);
            }else {
                req.getRequestDispatcher("newsShow.do").forward(req,resp);
            }
        } catch (ServletException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }



}

总结:


1、获取Method类的对象
用Class的方法来获取Method对象,前提是给我一个和想要调用方法名相同的String,和参数的类。
后面的各种框架,就很简单了,调用什么方法加注解,而且对post 、get方法区分很明确。
2、调用反射类Method的invoke()方法
调用方法也很简单,给方法所在的类,和方法包含的参数即可。之所以知道调用方法名和op相同的方法,是因为你创建了Method这个对象,这个对象就根据你传过来的参数op底层映射成了对应名称相等的方法。


这样就简单了,想用什么方法NewsUpdate、NewsAdd等方法直接接着写既可以啦。

这里还存在一个问题,假如我有不同的业务类,需要调用,那么遇到的问题和我们优化查最后一步相似,我们也需要写重复类似调用Method类的方法吗?
所以,封装起来。新建一个BaseServlet类,把共性代码封装,然后这个NewsServlet的实例extends一下就好。这里我们不用重写doGet()
doPost(),我们重写Service,不论什么请求,都可以处理。

BaseServlet

package com.hmc.jdbc.news.servlet;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;

/**
 * User:Meice
 * 2017/10/5
 */
public class BaseServlet extends HttpServlet {

    @Override
    protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        //设置编码
        req.setCharacterEncoding("utf-8");
        resp.setCharacterEncoding("utf-8");

        //根据传递参数决定方法跳转 op代表你访问页面传递的参数operation之含义
        String op = req.getParameter("op");
        if(op !=null && !"".equals(op)) {

            try {
                //获取Method对象;这里和我们前面写过的this.getClass().getDeclaredField类似
                Method method = this.getClass().getDeclaredMethod(op,HttpServletRequest.class,HttpServletResponse.class);
                /**
                 * 调用invoke()方法;属于java.lang.reflect下面的类
                 * 自动调用指定的方法 参数1:方法所在的类 参数2:调用方法的参数
                 */
                //如果不取消访问权限检查,会报IllegalAccessException异常
                method.setAccessible(true);
                method.invoke(this,req,resp);

            } catch (NoSuchMethodException e) {
                e.printStackTrace();
            } catch (IllegalAccessException e) {
                e.printStackTrace();
            } catch (InvocationTargetException e) {
                e.printStackTrace();
            }

        }else{
            System.out.println("参数缺失");
        }



    }
}

NewsServlet改造后变成这样:

package com.hmc.jdbc.news.servlet;
import com.hmc.jdbc.news.dao.NewsDao;
import com.hmc.jdbc.news.model.News;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.List;

/**
 * User:Meice
 * 2017/10/5
 */
public class NewsServlet extends BaseServlet {
    NewsDao nd = new NewsDao();


        //未避免代码混乱,把显示新闻列表封装成方法
        private void list (HttpServletRequest req, HttpServletResponse resp){
            List<News> list = nd.list();
            //3 存储到req中
            req.setAttribute("list", list);
            try {
                req.getRequestDispatcher("newsShow.jsp").forward(req, resp);
            } catch (ServletException e) {
            } catch (IOException e) {
            }

        }

        //同理,把删除封装成方法

        private void Del(HttpServletRequest req,HttpServletResponse resp) {
            //1 接受参数
            int id = 0;
            String strId = req.getParameter("id");
            if (strId != null && !"".equals(strId)) {
                id = Integer.valueOf(strId);
            }
            //2 业务逻辑处理(按照id删除对应新闻)
            int result = nd.newsDel2(id);
            //3 页面跳转
            try {
                if (result > 0) {
                    req.getRequestDispatcher("newsShow.do").forward(req, resp);
                } else {
                    req.getRequestDispatcher("newsShow.do").forward(req, resp);
                }
            } catch (ServletException e) {
                e.printStackTrace();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }


}

这里只写了CURD的查,只要查没问题,其他直接把方法搬过来即可。
需要注意,如果没有设置method.setAccessible(true)的话,因为我们的NewsServlet中的方法是Private的,所以会报访问权限检查异常。

java.lang.IllegalAccessException: Class com.hmc.jdbc.news.servlet.BaseServlet can not access a member of class com.hmc.jdbc.news.servlet.NewsServlet with modifiers "private"

这里写图片描述

这个异常很熟悉了,之前在优化BaseDao实例化Field对象的时候,遇到过。因为是反射类,所以他们一般都容易报这样的异常。可以把Private变为public?这样确实就不会访问检查了。不过,这样就得不偿失了,我们目的就是要Private,违背了封装的本意,因小失大。

至此,我们的Servlet优化告一段落。优化的过程,如同修行啊,嘻嘻;
温故而知新。

然鹅,今天的内容还没有结束,下午出去吃了一顿好的,所以在来点产出把!也请感兴趣滴读者有耐心看下去。以下内容比较好玩。

2、错误页面处理(404、500、400)

一旦访问不小心,访问到了我们没有写的方法,比如
localhost:8080/JavaWeb_Servlet?op=list6666
我们就没有list6666()这样的方法,怎么调用?所以界面什么也不显示,多尴尬!同时,后台报错:

java.lang.NoSuchMethodException: com.hmc.jdbc.news.servlet.NewsServlet.list34(javax.servlet.http.HttpServletRequest, javax.servlet.http.HttpServletResponse)

找不到方法丫!所以,为了避免这样,我们起码给客户一个反馈,出现一个页面,来提示一下。以挽回我们编程人员的一点点面子。奥,报错啊。我知道的,你看看那个错误页面,就是我写的,一切都在掌握之中,哈哈。
不让程序抛出异常,而是访问我们事先准备好的错误页面,有2种方法。


法1:在try() {}代码块中写错误页面跳转代码
法2:在web.xml中做全局错误页面配置


法1:在try() {}代码块中写跳转代码
1)先准备一个错误显示页面,以及显示图片。

页面error.jsp

<%--
  User: Meice
  Date: 2017/10/5
  Time: 21:33
--%>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>找小美处理吧</title>
</head>
<body>
    <h2>嘻嘻,页面不见了,请不要慌张,不要失望</h2>

    <img src="images/error.jpg" alt="请找程序猿小美处理奥">

</body>
</html>

修改后,整体布局是这样的:

这里写图片描述

修改抛出异常代码:
请只用关注: resp.sendRedirect(“error.jsp”);这个位置就好。可惜的是MarkDown不能标记颜色。

package com.hmc.jdbc.news.servlet;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;

/**
 * User:Meice
 * 2017/10/5
 */
public class BaseServlet extends HttpServlet {

    @Override
    protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        //设置编码
        req.setCharacterEncoding("utf-8");
        resp.setCharacterEncoding("utf-8");

        //根据传递参数决定方法跳转 op代表你访问页面传递的参数operation之含义
        String op = req.getParameter("op");
        if(op !=null && !"".equals(op)) {

            try {
                //获取Method对象;这里和我们前面写过的this.getClass().getDeclaredField类似
                Method method = this.getClass().getDeclaredMethod(op,HttpServletRequest.class,HttpServletResponse.class);
                /**
                 * 调用invoke()方法;属于java.lang.reflect下面的类
                 * 自动调用指定的方法 参数1:方法所在的类 参数2:调用方法的参数
                 */
                //如果不取消访问权限检查,会报IllegalAccessException异常
                method.setAccessible(true);
                method.invoke(this,req,resp);

            } catch (NoSuchMethodException e) {
                //这里,我们来掌控,不要让它抛出异常即可。因为不需要携带什么参数,有请重定向上场!
                //e.printStackTrace();
                resp.sendRedirect("error.jsp");

            } catch (IllegalAccessException e) {
                e.printStackTrace();
            } catch (InvocationTargetException e) {
                e.printStackTrace();
            }

        }else{
            System.out.println("参数缺失");
        }



    }
}

有个小细节需要注意下:
resp.sendRedirect(“error.jsp”);如果加上/的话,直接从根目录开始访问。比如我们访问路径:
http://localhost:8080/JavaWeb_Servlet/news.do?op=list6666
直接变成这样:
http://localhost:8080/error.jsp
所以,应该这么访问,就会默认带上上下文路径(项目路径):
resp.sendRedirect(“error.jsp”);

提示页面如下:
这里写图片描述

法2:在web.xml中做全局错误页面配置

除了在代码里面写异常处理跳转,web.xml还提供了直接进行页面跳转的方式:
配置web.xml错误页面:

  <!--错误页面配置-->
    <error-page>
        <!--表示页面错误类型404 400 500之类的-->
        <error-code>404</error-code>
        <!--这里配置错误页面位置-->
        <location>/error.jsp</location>
        <!--这个配置是配置错误类型类似:NullPointException -->
       <!-- <exception-type></exception-type>-->
    </error-page>

注意,这里节点也是有顺序的。同时,要确保你页面错误是404,(页面找不到)这样的错误,才会出现以上界面奥。

好了,今天更新到这里,我也累了。

冰雪聪明的你,喜欢那种方式呢?

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值