国产 无码 综合区,色欲AV无码国产永久播放,无码天堂亚洲国产AV,国产日韩欧美女同一区二区

手動開發(fā)-實現(xiàn)SpringMVC底層機制--小試牛刀

這篇具有很好參考價值的文章主要介紹了手動開發(fā)-實現(xiàn)SpringMVC底層機制--小試牛刀。希望對大家有所幫助。如果存在錯誤或未考慮完全的地方,請大家不吝賜教,您也可以點擊"舉報違法"按鈕提交疑問。

在這里說的底層機制的實現(xiàn)主要是指:前端控制器、Controller、Service注入容器、對象自動裝配、控制器方法獲取參數(shù)、視圖解析、返回json數(shù)據(jù)。

前端控制器

前端控制器就是核心控制器。在這里我們可以設(shè)計一個Servlet來充當(dāng)核心控制器:LingDispatcherServlet.java.這個控制器的作用主要是接收響應(yīng)前端過來的Http請求和Response響應(yīng)。一開始需要在web.xml中配置好控制器的 請求路徑,還要配置好SpringMVC的xml文件: lingspringmvc.xml.

lingspringmvc.xml

<?xml version="1.0" encoding="UTF-8" ?>
<beans>
    <component-scan base-package="com.linghu.controller"></component-scan>
</beans>

有了這個xml文件就好辦了,里面寫了Controller層的類路徑,只需要通過dom4j技術(shù)將類路徑讀出來,就可以輕松的將該文件下的類文件進行讀取遍歷,再去分析他們是否加了我們設(shè)計的注解,如果加了就保留類路徑,甚至加入到我們自己設(shè)計的ioc容器中。在讀取xml文件里的內(nèi)容的時候,可以單獨寫一個工具類XMLParser.java

package com.linghu.springmvc.xml;

import org.dom4j.Attribute;
import org.dom4j.Document;
import org.dom4j.DocumentException;
import org.dom4j.Element;
import org.dom4j.io.SAXReader;

import java.io.InputStream;

/**
 * @author linghu
 * @date 2023/9/11 14:22
 */
public class XMLParser {
    public static String getBasePackage(String xmlFile){
        SAXReader saxReader = new SAXReader();
        InputStream resourceAsStream =
                XMLParser.class.getClassLoader().getResourceAsStream(xmlFile);

        try {
            Document document = saxReader.read(resourceAsStream);
            Element rootElement = document.getRootElement();
            Element componentScanElement =
                    rootElement.element("component-scan");
            Attribute attribute = componentScanElement.attribute("base-package");
            String basePackage = attribute.getText();
            System.out.println("basePackage="+basePackage);
            return basePackage;
        } catch (DocumentException e) {
            e.printStackTrace();
        }
        return null;
    }
}

Controller注解

在Controller層,我們會在類上標(biāo)注Controller注解,容器就會將這個類路徑掃描加入到我們的容器中。在這里我們將自己設(shè)計一個自己的注解Controller注解。

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Controller {
    String value() default "";
}

RequestMapping注解

前端發(fā)出請求的時候,請求地址為:IP+端口+URI。RequestMapping注解可以規(guī)定我們請求的URI。同樣我們需要自己設(shè)計一個屬于自己的RequestMapping注解:

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface RequestMapping {
    String value() default "";
}

前端發(fā)出請求的時候,如果攜帶了參數(shù),后端接收的時候就要標(biāo)注接收的參數(shù)字段,會用到注解 RequestMapping注解。

自定義容器LingWebApplicationContext

需要自己設(shè)計一個容器,將我們從lingspringmvc.xml中讀取到的類路徑下的.class文件的路徑列表全部存起來。具體來說 就是:

<?xml version="1.0" encoding="UTF-8" ?>
<beans>
    <component-scan base-package="com.linghu.controller"></component-scan>
</beans>

需要將上面com.linghu.controller這個路徑下的類文件的路徑進行存儲。以后這個掃描的包不光有controller,還會有service,dao等等。

這個容器里最重要的就是要保存我們掃描包和子包下的全類路徑,所以我們定義了一個 classFullPathList集合用來存儲。

private ArrayList<String> classFullPathList=new ArrayList<>();

接下來就是利用工具類 XMLParser去讀取springmvc.xml配置文件里的包路徑,接著利用文件和IO的知識去掃描這個包路徑下的文件和目錄,將這個路徑下的所有.class文件的后綴.class裁剪掉,將路徑名和文件名進行拼接就得到了類文件的類路徑了,我們將他們存儲到classFullPathList集合中即可。如下:

手動開發(fā)-實現(xiàn)SpringMVC底層機制--小試牛刀,SpringMvc,狀態(tài)模式

這個類路徑就對應(yīng)下圖:

手動開發(fā)-實現(xiàn)SpringMVC底層機制--小試牛刀,SpringMvc,狀態(tài)模式

這里目前只討論controller注解的類,因為springmvc.xml掃描的包只寫了controller這個包。

利用文件和IO的知識去掃描這個包路徑下的文件和目錄是重點,在掃描以后,我們要將這個路徑下的所有.class文件的后綴.class裁剪掉,將路徑名和文件名進行拼接。這兩步是最重要的。scanPackage()函數(shù)可以完成這兩個重要的東西。

  public void init(){
        String basePackage = XMLParser.getBasePackage("lingspringmvc.xml");
        String[] packages = basePackage.split(",");
        if (packages.length>0){
            for (String pack :packages) {
                scanPackage(pack);
            }
        }
        System.out.println("classFullPathList="+classFullPathList);
    }

    public void scanPackage(String pack){
        URL url = this.getClass().
                getClassLoader().
                getResource("/" + pack.replaceAll("\\.", "/"));
        String path = url.getFile();//獲取所有文件的目錄
        File dir = new File(path);
        for (File f :dir.listFiles()) {
            if (f.isDirectory()){
                scanPackage(pack+"."+f.getName());
            }else {
                String classFullPath=pack+"."+f.getName().replaceAll(".class","");
                classFullPathList.add(classFullPath);
            }
        }

    }

將classFullPathList存放的掃描的全類路徑文件的類名提出來,將第一個字母小寫,作為bean對象的名稱,類似于以前xml配置bean對象的時候,聲明的bean的id。然后將類路徑進行反射創(chuàng)建對象,同時將beanName和反射創(chuàng)建好的對象放到ioc容器中。如下:

手動開發(fā)-實現(xiàn)SpringMVC底層機制--小試牛刀,SpringMvc,狀態(tài)模式

 public void executeInstance(){
        if (classFullPathList.size()==0){
            return;
        }

        try {
            for (String  classFullPath:classFullPathList) {
                Class<?> clazz = Class.forName(classFullPath);
                if (clazz.isAnnotationPresent(Controller.class)){
                    String beanName=clazz.getSimpleName().substring(0,1).toLowerCase()+
                            clazz.getSimpleName().substring(1);
                    //同時將beanName和反射創(chuàng)建好的對象放到ioc容器中
                    ioc.put(beanName,clazz.newInstance());
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        }

    }

設(shè)計handlerList

Controller層會寫很多接口,規(guī)范化請求的地址,用注解RequestMapping進行了標(biāo)識,RequestMapping的Value值其實就是請求的地址,我們把它取出來單獨命名成url,在把RequestMapping標(biāo)識的方法的方法名取出來命名成method,最后把當(dāng)前方法所在的類,也就是Controller標(biāo)識的這個類的對象命名成controller。我們現(xiàn)在有了url,controller,method。其實就已經(jīng)拿到了url和method的映射關(guān)系了,現(xiàn)在將其封裝保存到handerList集合中。這樣做的好處是,當(dāng)前端發(fā)來請求的時候,我們可以取出請求的url,通過url在handerList中找到對應(yīng)的調(diào)用的方法名,實現(xiàn)調(diào)用的映射。

先設(shè)計一個hander,用來封裝url,controller,method:

package com.linghu.springmvc.handler;

import java.lang.reflect.Method;

/**
 * @author linghu
 * @date 2023/9/12 9:23
 */
public class LingHandler {
    private String url;
    private Object controller;
    private Method method;

    public LingHandler() {
    }

    public LingHandler(String url, Object controller, Method method) {
        this.url = url;
        this.controller = controller;
        this.method = method;
    }

    @Override
    public String toString() {
        return "LingHandler{" +
                "url='" + url + '\'' +
                ", controller=" + controller +
                ", method=" + method +
                '}';
    }

    public String getUrl() {
        return url;
    }

    public void setUrl(String url) {
        this.url = url;
    }

    public Object getController() {
        return controller;
    }

    public void setController(Object controller) {
        this.controller = controller;
    }

    public Method getMethod() {
        return method;
    }

    public void setMethod(Method method) {
        this.method = method;
    }
}

  private void initHandlerMapping(){
        if (lingWebApplicationContext.ioc.isEmpty()){
            return;
        }
        for (Map.Entry<String,Object> entry :lingWebApplicationContext.ioc.entrySet()) {
            Class<?> clazz = entry.getValue().getClass();
            if (clazz.isAnnotationPresent(Controller.class)){
                Method[] declaredMethods = clazz.getDeclaredMethods();
                for (Method method :declaredMethods) {
                    if (method.isAnnotationPresent(RequestMapping.class)){
                        RequestMapping requestMappingAnnotation =
                                method.getAnnotation(RequestMapping.class);
                        String url = requestMappingAnnotation.value();
                        LingHandler lingHandler = new LingHandler(url,entry.getValue(),method);
                        handlerList.add(lingHandler);
                    }
                }

            }

        }

    }

initHandlerMapping()會將容器中的對象,也就是之前 我們通過類路徑反射創(chuàng)建的對象進行遍歷,遍歷過程中篩選出被Controller注解標(biāo)識過的類,將類里的方法再全部進行遍歷,遍歷過程中將被RequestMapping注解標(biāo)識的方法選出來,將這些方法的方法名命名成method,注解RequestMapping的value值取出來命名成url,將當(dāng)前類命名成controller,最后將url,controller,method放到LingHandler對象中,在將LingHandler對象放到handerList集合中。

手動開發(fā)-實現(xiàn)SpringMVC底層機制--小試牛刀,SpringMvc,狀態(tài)模式

手動開發(fā)-實現(xiàn)SpringMVC底層機制--小試牛刀,SpringMvc,狀態(tài)模式

完成分發(fā)請求

前端發(fā)送一個請求過來,無論是get,post請求都要經(jīng)過我的servlet,這個時候可以取出請求request信息里的uri,這樣我就得到了前端想要請求的Controller層的具體方法,其實拿到這個方法我們就可以利用反射進行調(diào)用了。所以完成分發(fā)請求的還是我們的servlet,也就是文章一開篇就說的前端控制器,核心控制器,它就是我們的整個大腦核心,負責(zé)接收請求,完成請求分發(fā),分發(fā)到具體要執(zhí)行的Controller層的方法去。

前端過來的請求,我們可以讓它統(tǒng)一都走post請求。

LingDispatcherServlet.java:

   @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 {
        System.out.println("LingDispatcherServlet doPost");
        executeDispatch(req, resp);
    }

executeDispatch(req, resp)便是攜帶著前端request請求的具體執(zhí)行分發(fā)請求的方法。

 public void executeDispatch(HttpServletRequest request,
                                HttpServletResponse response){
        LingHandler lingHandler = getLingHandler(request);
        try {
            if (lingHandler==null){
                response.getWriter().print("<h1>404</h1>");
            }else {
                lingHandler.getMethod().invoke(lingHandler.getController(),request,response);
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

lingHandler.getMethod().invoke(lingHandler.getController(),request,response);便是反射調(diào)用,是完成分發(fā)的核心。在這里lingHandler.getMethod()本身就是Method類型的對象,所以可以進行調(diào)用。getLingHandler(request)的具體過程如下:

 public LingHandler getLingHandler(HttpServletRequest request){
        String requestURI = request.getRequestURI();
        for (LingHandler lingHandler :handlerList) {
            if (requestURI.equals(lingHandler.getUrl())){
                return lingHandler;
            }
        }
        return null;
    }

getLingHandler完成的便是查看handlerList集合中有沒有前端請求的方法url,有的話就直接把handler對象返回,handler對象里保存著前端請求url和調(diào)用后端方法名的映射關(guān)系,通過映射關(guān)系我們可以查到具體要調(diào)用的方法是誰。

Service注解和AutoWired注解

這兩個注解的實現(xiàn)其實和Controller注解差不多的流程三板斧。先通過元注解定義好這兩個注解,在掃描全類路徑的時候,去判斷有沒有Service注解,有的話就獲取文件下的所有接口名,對接口名字首字母變小寫,然后拼接得到新的beanName,最后通過反射創(chuàng)建對象,將beanName和對象加入到ioc中。

 } else if (clazz.isAnnotationPresent(Service.class)) {
                    Service serviceAnnotation = clazz.getAnnotation(Service.class);
                    String beanName = serviceAnnotation.value();
                    if ("".equals(beanName)){
                        Class<?>[] interfaces = clazz.getInterfaces();
                        for (Class<?> anInterface :interfaces) {
                            String beanName2=anInterface.getSimpleName().substring(0,1).toLowerCase()+
                                    anInterface.getSimpleName().substring(1);
                            ioc.put(beanName2,clazz.newInstance());
                        }
                    }else {
                        ioc.put(beanName,clazz.newInstance());
                    }
                }

設(shè)計Controller注解的時候,我們是直接獲取的 類名,在這里設(shè)計Service注解的時候我們獲取的是接口名字,是因為接口內(nèi)部裝滿了所有實現(xiàn)類,而我們的Service注解又是寫在這些實現(xiàn)類上面 的,我們通過獲取接口,就有機會遍歷到這些實現(xiàn)類,如果不通過獲取接口,直接獲取實現(xiàn)類代價要大點。

AutoWired注解的作用是將dao層和業(yè)務(wù)層對象注入到ioc中,方便業(yè)務(wù)層或者控制層調(diào)用dao層和業(yè)務(wù)層。掃描什么的其實不難,就是全文掃描帶AutoWired注解的屬性,將其加入到容器中。加入的這個過程放在一個函數(shù)executeAutoWired()中。

    public void executeAutoWired(){
        if (ioc.isEmpty()){
            throw new RuntimeException("容器中沒有可以裝配的bean");
        }
        for (Map.Entry<String,Object> entry :ioc.entrySet()) {
            String key = entry.getKey();
            Object bean = entry.getValue();
            Field[] declaredFields = bean.getClass().getDeclaredFields();
            for (Field declaredField :declaredFields) {
                if (declaredField.isAnnotationPresent(AutoWired.class)){
                    AutoWired autoWiredAnnotation =
                            declaredField.getAnnotation(AutoWired.class);
                    String beanName = autoWiredAnnotation.value();
                    if ("".equals(beanName)){
                        Class<?> type = declaredField.getType();
                        beanName= type.getSimpleName().substring(0,1).toLowerCase()+
                                        type.getSimpleName().substring(1);

                    }
                    declaredField.setAccessible(true);

                    try {
                        if (ioc.get(beanName)==null){
                            throw new RuntimeException("容器中沒有注入該bean");
                        }
                        declaredField.set(bean,ioc.get(beanName));
                    } catch (IllegalAccessException e) {
                        e.printStackTrace();
                    }
                }
            }
        }
    }

RequestParam注解

前端在發(fā)送請求的時候,會攜帶一些參數(shù)過來,這個時候后端要接收請求的時候處理好參數(shù)字段的對應(yīng)關(guān)系。我們可以用requestparam注解標(biāo)識前端對應(yīng)過來的參數(shù)字段。

@Target(ElementType.PARAMETER)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface RequestParam {
    String value() default "";
}
 public void executeDispatch(HttpServletRequest request,
                                HttpServletResponse response){
        LingHandler lingHandler = getLingHandler(request);

        try {
            if (lingHandler==null){
                response.getWriter().print("<h1>404</h1>");
            }else {
//                lingHandler.getMethod().invoke(lingHandler.getController(),request,response);
                Class<?>[] parameterTypes =
                        lingHandler.getMethod().getParameterTypes();
                Object [] params = new Object[parameterTypes.length];

                for (int i=0;i< parameterTypes.length;i++){
                    Class<?> parameterType=parameterTypes[i];
                    if ("HttpServletRequest".equals(parameterType.getSimpleName())){
                        params[i]=request;
                    } else if ("HttpServletResponse".equals(parameterType.getSimpleName())) {
                        params[i]=response;
                    }
                }
                Map<String,String[]> parameterMap =
                        request.getParameterMap();

                for (Map.Entry<String,String[]> entry :parameterMap.entrySet()) {
                    String name = entry.getKey();
                    String value = entry.getValue()[0];
                    System.out.println("請求的參數(shù):"+name+"----"+value);
                    int indexRequestParamIndex=
                            getIndexRequestParamIndex(lingHandler.getMethod(),name);

                    if (indexRequestParamIndex!=-1){
                        params[indexRequestParamIndex]=value;
                    }else {

                    }
                }
                lingHandler.getMethod().invoke(lingHandler.getController(),params);
            }
        } catch (Exception e) {
            e.printStackTrace();
        }

    }

    public int getIndexRequestParamIndex(Method method,String name){
        Parameter[] parameters = method.getParameters();
        for (int i=0;i<parameters.length;i++){
            Parameter parameter=parameters[i];
            boolean annotationPresent =
                    parameter.isAnnotationPresent(RequestParam.class);
            if (annotationPresent){
                RequestParam requestParamAnnotation =
                        parameter.getAnnotation(RequestParam.class);
                String value = requestParamAnnotation.value();
                if (name.equals(value)){
                    return i;
                }
            }
        }
        return -1;
    }

完整代碼

《實現(xiàn)SpringMVC底層機制》文章來源地址http://www.zghlxwxcb.cn/news/detail-712419.html

到了這里,關(guān)于手動開發(fā)-實現(xiàn)SpringMVC底層機制--小試牛刀的文章就介紹完了。如果您還想了解更多內(nèi)容,請在右上角搜索TOY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!

本文來自互聯(lián)網(wǎng)用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務(wù),不擁有所有權(quán),不承擔(dān)相關(guān)法律責(zé)任。如若轉(zhuǎn)載,請注明出處: 如若內(nèi)容造成侵權(quán)/違法違規(guī)/事實不符,請點擊違法舉報進行投訴反饋,一經(jīng)查實,立即刪除!

領(lǐng)支付寶紅包贊助服務(wù)器費用

相關(guān)文章

  • 【Spring】手動實現(xiàn)Spring底層機制-問題的引出

    【Spring】手動實現(xiàn)Spring底層機制-問題的引出

    ??歡迎來到@邊境矢夢°的csdn博文?? ??本文主要梳理手動實現(xiàn)Spring底層機制-問題的引出??? ??我是邊境矢夢°,一個正在為秋招和算法競賽做準備的學(xué)生?? ??喜歡的朋友可以關(guān)注一下 ?????? ,下次更新不迷路?? Ps: 月亮越亮說明知識點越重要 (重要性或者難度越大)??

    2024年02月09日
    瀏覽(22)
  • 從零開始搭建flink流式計算項目-2小試牛刀-物聯(lián)網(wǎng)場景下,如何實現(xiàn)設(shè)備采集參數(shù)監(jiān)控報警功能

    從零開始搭建flink流式計算項目-2小試牛刀-物聯(lián)網(wǎng)場景下,如何實現(xiàn)設(shè)備采集參數(shù)監(jiān)控報警功能

    * 設(shè)備ID */ private Integer deviceId; * 監(jiān)控的變量名稱 */ private String varName; * 最小值 */ private Double min; * 最大值 */ private Double max; } /** * 報警消息 */ @Data public class AlarmMessage { * 設(shè)備 */ private Integer deviceId; * 報警時間 */ private Long timestamp; /** * 觸發(fā)報警的采集變量名稱 */ private String ala

    2024年04月11日
    瀏覽(21)
  • 網(wǎng)頁學(xué)習(xí)-小試牛刀

    網(wǎng)頁學(xué)習(xí)-小試牛刀

    分為三大部分: HTML 、 CSS 和 JavaScript 。 HTML(Hyper Text Markup Language,即超文本標(biāo)記語言),網(wǎng)頁骨架。 CSS(Cascading Style Sheets,層疊樣式表),使頁面變得美觀、優(yōu)雅,網(wǎng)頁皮膚。 JavaScript(簡稱JS,是一種腳本語言),實現(xiàn)實時、動態(tài)、交互的頁面功能,網(wǎng)頁肌肉。 學(xué)習(xí)目的

    2023年04月22日
    瀏覽(46)
  • Mapreduce小試牛刀(1)

    Mapreduce小試牛刀(1)

    1.與hdfs一樣,mapreduce基于hadoop框架,所以我們首先要啟動hadoop服務(wù)器 --------------------------------------------------------------------------------------------------------------------------------- 2.修改hadoop-env.sh位置JAVA_HOME配置,在JAVA_HOME前面加上export,重啟主虛擬機,最好也把另外兩個節(jié)點同位置的

    2024年02月04日
    瀏覽(23)
  • 運維Shell腳本小試牛刀(二)

    運維Shell腳本小試牛刀(二)

    運維Shell腳本小試牛刀(一) 運維Shell腳本小試牛刀(二) 運維Shell腳本小試牛刀(三)::$(cd $(dirname $0); pwd)命令詳解 [root@www shelldic]# cat checkpass.sh? #!/bin/bash - #================================================================================================================== # # # ? ? ? ? ? ? ? ? ? ? ? ? ?

    2024年02月10日
    瀏覽(28)
  • 快速上手kettle(二)小試牛刀

    快速上手kettle(二)小試牛刀

    目錄 一 、前言 二 、兩個小目標(biāo) 三、 kettle核心概念介紹 3.1 轉(zhuǎn)換 3.1.1 步驟(Step) 3.1.2 跳(Hop) 3.1.3 元素據(jù) 3.1.4 數(shù)據(jù)類型 3.1.5 并發(fā)執(zhí)行 3.2 作業(yè) 四、實踐操作 4.1 案例1 將csv文件轉(zhuǎn)換成excel文件 4.1.1 在kettle中新建一個轉(zhuǎn)換 4.1.2選擇輸入控件并設(shè)置 4.1.3 選擇輸出控件并設(shè)置 4.

    2024年02月06日
    瀏覽(25)
  • 運維Shell腳本小試牛刀(一)

    運維Shell腳本小試牛刀(一)

    運維Shell腳本小試牛刀(一) 運維Shell腳本小試牛刀(二) 運維Shell腳本小試牛刀(三)::$(cd $(dirname $0); pwd)命令詳解 運維Shell腳本小試牛刀(四): 多層嵌套if...elif...elif....else fi_蝸牛楊哥的博客-CSDN博客 Cenos7安裝小火車程序動畫 運維Shell腳本小試牛刀(五):until循環(huán) 運維Shell腳本小試牛刀

    2024年02月11日
    瀏覽(26)
  • 【LED子系統(tǒng)】八、小試牛刀

    個人主頁:董哥聊技術(shù) 我是董哥,高級嵌入式軟件開發(fā)工程師,從事嵌入式Linux驅(qū)動開發(fā)和系統(tǒng)開發(fā),曾就職于世界500強公司! 創(chuàng)作理念:專注分享高質(zhì)量嵌入式文章,讓大家讀有所得!

    2024年02月06日
    瀏覽(22)
  • verilator——牛刀小試

    verilator——牛刀小試

    安裝verilator可見:https://blog.csdn.net/qq_40676869/article/details/132648522?spm=1001.2014.3001.5501 編寫一個異或的電路模塊如下: top.v 編寫C++測試文件 tb_top.cpp 編譯并運行 –cc 將.v文件翻譯成c++ –exe 創(chuàng)建可執(zhí)行文件 –build verilator自動進行make –trace 記錄波形 波形如下: github鏈接:https:/

    2024年02月10日
    瀏覽(23)

覺得文章有用就打賞一下文章作者

支付寶掃一掃打賞

博客贊助

微信掃一掃打賞

請作者喝杯咖啡吧~博客贊助

支付寶掃一掃領(lǐng)取紅包,優(yōu)惠每天領(lǐng)

二維碼1

領(lǐng)取紅包

二維碼2

領(lǐng)紅包