Spring 学习小记(十二)

67 篇文章 0 订阅
Spring MVC的Controller
 
本小记学习目标
  1. 基于注解的控制器使用
  2. 请求处理方法的编写
  3. Controller接收的请求参数介绍
  4. 重定向与转发的实现
  5. 其它相关的注解
 
一、基于注解的控制器使用
        在前面我们入门Spring MVC应用中,我们控制器它是通过实现Controller接口类来实现的,它的局限性在于通过这种方式实现的控制器需要在Spring的配置文件中对控制器进行映射配置并且对于控制器来说一个控制器的实现类只能编写一个处理方法。于是Spring MVC中控制器一般是使用注解的方式来指定。使用注解的控制器具有如下优点:
  1. 基于注解的控制器类可以编写多个处理方法,所以它可以处理多个请求。这样可以把相关的操作编写到同一个控制器类中做归类处理,这样可以明显地减少控制器的数量,方便以后的维护。
  2. 基于注解的控制器不需要在配置文件中进行配置。(仅需要在配文件中指定Spring的扫描包即可)
 
这里有两个重要的注解:Controller、RequestMapping
Controller注解
在Spring MVC中,它用来声明某个类的实例是一个控制器。它对应的注解类型是:org.springframework.stereotype.Controller
这个控制器的注解存在默认的value值,它是类名的首字母小写的方式
 
RequestMapping注解
在Spring MVC中,它用来在控制器为类中为每个请求编写声明处理方法,它对应的注解类型是:org.springframework.web.bind.annotation.RequestMapping
 
二、请求处理方法的编写
在controller包中新增一个Controller实现类:UserController
package com.xiaoxie.controller;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
/*
 * @Controller:表示UserController的实例就是一个控制器
 * 它相当于如下两个写法
 * @Controller(userController)/@Controller(value="userController")
 */
@Controller
@RequestMapping( "/user")
public class UserController {
       @RequestMapping( "/login")
       public String login() {
             return "login";
      }
       @RequestMapping( "/register")
       public String register() {
             return "register";
      }
}
注意:上面在类上有@RequestMapping,在具体的处理方法上也有@RequestMapping,这样做的目的就是对于类上的注解指定它是哪个模块的请求,比如上面的user,在处理的处理方法上则指定明确的请求
那行对于login()这个方法它实际请求的则是/user/login
 
在Spring的配置文件中,指定Spring扫描Controller包其子包的位置,需要添加如下配置
<!-- 指定Spring扫描Controller包及其子包 -->
< context:component-scan base-package= "com.xiaoxie.controller" />
 
请求处理方法中常见的参数介绍
  1. 如果希望在请求处理方法中使用Servlet API类型,那么可以把这些类型作为请求处理方法的参数
        比如在UserController中新增一个请求处理方法
    @RequestMapping( "/param1")
       public String param1(HttpSession session,HttpServletRequest request) {
             session.setAttribute( "session_key", "session的值");
             request.setAttribute( "request_key", "request的值");
             return "param";
      }
    这个时候返回到视图中去的时候则可以获取到这些类所绑定的参数
    2.可以使用Model类型(org.springframework.ui.Model)作参数
    比如在UserController中新增一个请求处理方法
     @RequestMapping( "/param2")
       public String param2(Model model) {
             model.addAttribute( "model_key", "model类型绑定的值");
             return "param";
      }
    这个时候在在model参数中则绑定了相应的值,在JSP视图中可以使用EL表达式来获取这个绑定的值${model_key}
    3.除了上达的参数外,参数还可以是输入/出流、表单实体类、注解类型……与Spring框架相关的类型
 
请求处理方法的返回值类型
    1.一般来说返回值类型为String,它代表了逻辑视图的名称
    2.返回值也可以是ModelAndView、Model、View或其它Java类型
 
三、Controller接收请求参数的介绍
通过实体Bean接收请求参数
通过实体Bean来接收请求参数,适用于get和post请求,这里有一个要求就是Bean的属性名称必须要与请求参数的名称保持一致
 
新增一个pojo实体类:UserForm,它的目的是用来接收请求过来的参数(注意:UserForm中的属性需要与提交的请求参数名称保持一致)
package com.xiaoxie.pojo;
public class UserForm {
       private String uname;
       private String upass;
       private String reupass;
       public String getUname() {
             return uname;
      }
       public void setUname(String uname) {
             this. uname = uname;
      }
       public String getUpass() {
             return upass;
      }
       public void setUpass(String upass) {
             this. upass = upass;
      }
       public String getReupass() {
             return reupass;
      }
       public void setReupass(String reupass) {
             this. reupass = reupass;
      }
}
 
改造UserController类中的登录请求处理及注册请求处理方法(改造后的UserController类如下)
package com.xiaoxie.controller;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpSession;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;
import com.xiaoxie.pojo.UserForm;
/*
 * @Controller:表示UserController的实例就是一个控制器
 * 它相当于如下两个写法
 * @Controller(userController)/@Controller(value="userController")
 */
@Controller
@RequestMapping( "/user")
public class UserController {
       //使用记录日志的对象
       private static final Log logger = LogFactory. getLog(UserController. class);
      
       /**
       * 处理用户登录请求
       * @param user UserForm对象,接收登录页面提交的请求参数
       * @param session
       * @param model
       * @return
       */
       @RequestMapping( "/login")
       public String login(UserForm user,HttpSession session,Model model) {
             //对提交的请求参数进行判断
             if( "xiaoxie".equals( user.getUname()) && "123456".equals( user.getUpass())) {
                   session.setAttribute( "login_user", user);
                   logger.info( "登录成功!");
                   return "main";     //登录成功后跳转到main.jsp
            } else {
                   logger.info( "登录失败!");
                   model.addAttribute( "error_tips", "用户名或密码错误!");
                   return "login";
            }
      }
      
       /**
       * 处理用户注册请求
       * @param user user UserForm对象,接收注册页面提交的请求参数
       * @param model
       * @return
       */
       @RequestMapping( "/register")
       public String register(UserForm user,Model model) {
             //对注册提交的信息进行后端校验
             if( "xiaoxie".equals( user.getUname()) && "123456".equals( user.getUpass())) {
                   logger.info( "注册成功!");
                   return "login";    //注册成功后跳转到login.jsp
            } else {
                   logger.info( "注册失败!");
                   model.addAttribute( "error_tips", "用户名:" + user.getUname() + "注册失败!");
                   return "register";
            }
      }
      
       @RequestMapping( "/param1")
       public String param1(HttpSession session,HttpServletRequest request) {
             session.setAttribute( "session_key", "session的值");
             request.setAttribute( "request_key", "request的值");
             return "param";
      }
      
       @RequestMapping( "/param2")
       public String param2(Model model) {
             model.addAttribute( "model_key", "model类型绑定的值");
             return "param";
      }
}
 
对index.jsp页面进行设计,这里主要是提供两个连接可以跳转到login.jsp页面和register.jsp页面
<%@ page language= "java" contentType= "text/html; charset=UTF-8"
    pageEncoding= "UTF-8" %>
<! 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 >
      未注册用户,请 < a href= "${pageContext.request.contextPath } /register" >注册 </ a >< br />
      已注册用户,请 < a href= "${pageContext.request.contextPath } /login" >登录 </ a >
       < hr />
       Servlet API类型作为Controller中处理方法的参数: < a href= "${pageContext.request.contextPath } /user/param1" >访问,Servlet_API参数 </ a >< br >
       < hr />
      Model类型作为Controller中处理方法的参数: < a href= "${pageContext.request.contextPath } /user/param2" >访问,Model参数 </ a >
</ body >
</ html >
 
新增login.jsp、register.jsp、main.jsp,它们都在WEB-INF/jsp下
login.jsp
<%@ page language= "java" contentType= "text/html; charset=UTF-8"
    pageEncoding= "UTF-8" %>
<! 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 >
       < form action= "${pageContext.request.contextPath } /user/login" method= "post" >
             < table border= "1" bgcolor= "lightblue" align= "center" >
                   < tr >
                         < td >姓名: </ td >
                         < td >< input type= "text" name= "uname" ></ td >
                   </ tr >
                   < tr >
                         < td >密码: </ td >
                         < td >< input type= "password" name= "upass" ></ td >
                   </ tr >
                   < tr >< td colspan= "2" align= "center" >< input type= "submit" value= "登录" ></ td ></ tr >
             </ table >
             < span style=" color: red;" >${error_tips } </ span >
       </ form >
</ body >
</ html >
 
register.jsp
<%@ page language= "java" contentType= "text/html; charset=UTF-8"
    pageEncoding= "UTF-8" %>
<! 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 >
       < form action= "${pageContext.request.contextPath } /user/register" method= "post" >
             < table border= "1" bgcolor= "lightblue" align= "center" >
                   < tr >
                         < td >姓名: </ td >
                         < td >< input type= "text" name= "uname" ></ td >
                   </ tr >
                   < tr >
                         < td >密码: </ td >
                         < td >< input type= "password" name= "upass" ></ td >
                   </ tr >
                   < tr >
                         < td >确认密码: </ td >
                         < td >< input type= "password" name= "reupass" ></ td >
                   </ tr >
                   < tr >< td colspan= "2" align= "center" >< input type= "submit" value= "注册" ></ td ></ tr >
             </ table >
             < span style=" color: red;" >${error_tips } </ span >
       </ form >
</ body >
</ html >
 
main.jsp
<%@ page language= "java" contentType= "text/html; charset=UTF-8"
    pageEncoding= "UTF-8" %>
<! 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 >
      欢迎管理员:${login_user.uname }
</ body >
</ html >
 
通过处理方法的形参来接收请求参数
这种方式就是直接把请求参数写到控制器类相应请求处理方法的形参中,这里要求形参的名称要与请求参数的名称完全一致。适用于get和post请求。
把上面的login处理方法进行改造
@RequestMapping( "/login")
public String login(String uname,String upass,HttpSession session,Model model) {
             //对提交的请求参数进行判断
             if( "xiaoxie".equals( uname) && "123456".equals( upass)) {
                   session.setAttribute( "login_user", uname);
                   logger.info( "登录成功!");
                   return "main";     //登录成功后跳转到main.jsp
            } else {
                   logger.info( "登录失败!");
                   model.addAttribute( "error_tips", "用户名或密码错误!");
                   return "login";
            }
      }
第二个改造的地方就是main.jsp,因为这个时候当登录成功时login_user中绑定的不是一个pojo对象而是一个字符串的值
在<body>标签中直接展示登录成功的名称
欢迎管理员:${login_user }
 
通过HttpServletRequest接收请求参数
这种方式支持get和post的请求,它直接是使用HttpServletRequest来作为参数接收请求的信息
把login处理方法再做如下改造
      @RequestMapping( "/login")
       public String login(HttpServletRequest request,Model model) {
             //获取请求参数
            String uname = request.getParameter( "uname");
            String upass = request.getParameter( "upass");
             //对提交的请求参数进行判断
             if( "xiaoxie".equals( uname) && "123456".equals( upass)) {
                   request.getSession().setAttribute( "login_user", uname);
                   logger.info( "登录成功!");
                   return "main";     //登录成功后跳转到main.jsp
            } else {
                   logger.info( "登录失败!");
                   model.addAttribute( "error_tips", "用户名或密码错误!");
                   return "login";
            }
      }
 
通过@PathVariable接收URL中的请求参数
对login的处理方法做如下改造
@RequestMapping(value= "/login/{uname}/{upass}",method=RequestMethod. GET)
       public String login( @PathVariable String uname, @PathVariable String upass,HttpSession session, Model model) {
             //获取请求参数
             //String uname = request.getParameter(" uname ");
             //String upass = request.getParameter(" upass ");
             //对提交的请求参数进行判断
             if( "xiaoxie".equals( uname) && "123456".equals( upass)) {
                   session.setAttribute( "login_user", uname);
                   logger.info( "登录成功!");
                   return "main";     //登录成功后跳转到main.jsp
            } else {
                   logger.info( "登录失败!");
                   model.addAttribute( "error_tips", "用户名或密码错误!");
                   return "login";
            }
      }
这里要注意是:@RequestMapping中对于请求的地址需要把请求的参数做模板配置上,并且需要指定method
这个时候请求类似于:http://localhost:8088/SSM_12/login/xiaoxie/123456
 
通过@RequestParam接收请求
它可以支持get和post请求
@RequestMapping( "/login")
       public String login( @RequestParam String uname, @RequestParam String upass,HttpSession session, Model model) {
             //获取请求参数
             //String uname = request.getParameter(" uname ");
             //String upass = request.getParameter(" upass ");
             //对提交的请求参数进行判断
             if( "xiaoxie".equals( uname) && "123456".equals( upass)) {
                   session.setAttribute( "login_user", uname);
                   logger.info( "登录成功!");
                   return "main";     //登录成功后跳转到main.jsp
            } else {
                   logger.info( "登录失败!");
                   model.addAttribute( "error_tips", "用户名或密码错误!");
                   return "login";
            }
      }
这里要注意,如果我们在指定@RequestParam的时候形参与请求时提交的参数名称不一样会报404的错误,而如果我们直接使用形参来接收请求时提交的参数当名称不一致时只是接收到不请求的信息并不会报404的错误
 
通过@ModelAttribute接收请求参数
当把它放到处理方法的形参上的时候,会把多个请求封装为一个实体对象,并且会自己动暴露模型数据,不需要使用model.addAttribute,可以直接在jsp中使用EL表达式获取到模型对象的数据
改造login的处理方法
@RequestMapping( "/login")
       public String login( @ModelAttribute( "user") UserForm user) {
             //获取请求参数
             //String uname = request.getParameter(" uname ");
             //String upass = request.getParameter(" upass ");
             //对提交的请求参数进行判断
             if( "xiaoxie".equals( user.getUname()) && "123456".equals( user.getUpass())) {
                   //session.setAttribute("login_user", uname1);
                   logger.info( "登录成功!");
                   return "main";     //登录成功后跳转到main.jsp
            } else {
                   logger.info( "登录失败!");
                   //model.addAttribute("error_tips", "用户名或密码错误!");
                   return "login";
            }
      }
在登录失败后在jsp页面中显示失败的提示,所以改造login.jsp如下
<%@ page language= "java" contentType= "text/html; charset=UTF-8"
    pageEncoding= "UTF-8" %>
<%@ taglib uri= "http://java.sun.com/jsp/jstl/core" prefix= "c" %>
<! 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 >
       < form action= "${pageContext.request.contextPath } /user/login" method= "get" >
             < table border= "1" bgcolor= "lightblue" align= "center" >
                   < tr >
                         < td >姓名: </ td >
                         < td >< input type= "text" name= "uname" ></ td >
                   </ tr >
                   < tr >
                         < td >密码: </ td >
                         < td >< input type= "password" name= "upass" ></ td >
                   </ tr >
                   < tr >< td colspan= "2" align= "center" >< input type= "submit" value= "登录" ></ td ></ tr >
             </ table >
             <%-- <span style="color: red;">用户:${user.uname }登录失败!</span> --%>
             < c:if test= "${user.uname!=null and user.uname!='' } " >
                   < span style=" color: red;" >用户:${user.uname }登录失败! </ span >
             </ c:if >
            
       </ form >
</ body >
</ html >
注意:这里使用了jstl的标签,需要依赖相应的标签库jar包,在pom.xml中添加相应的依赖
< dependency >
    < groupId >javax.servlet </ groupId >
    < artifactId > jstl </ artifactId >
    < version >1.2 </ version >
</ dependency >
 
四、重定向与转发
重定向:把当前处理请求定向一另一个视图或处理请求,之前的请求中存放的信息将全部失效,并进入到一个新的Request作用域。
转发:把当前处理请求转给到另一个视图或处理请求,之前的请求reques中存放的信息不会失效。
重定向是客户端的行为,转为则是服务器行为。
 
转发的过程:
浏览器---->http请求---->Web服务器---->调用内部方法在容器内完成请求处理和转发动作---->处理后的目标资源发送给客户
注意:转发是在容器内完成的,必须是同一个web容器下的URL,不可以转发到其它的Web路径上去(这其中请求的信息会是第一次请求上的request),在外在来看客户的浏览器地址栏中显示的信息一值为保存为第一次访问的路径,也就是说客户端是不感知的它是服务器内容的行为。
 
重定向的过程:
浏览器---->http请求---->web服务器接收请求后发送302状态码响应和对应的新的location给浏览器---->浏览器发现302响应,则会自动发送一个新的http请求---->web服务器根据这个新的请求寻找资源返回给到浏览器
注意:这里新的location要以是任意的URL不局限在web容器内部,重定向后浏览器地址栏也会做相应的变化,浏览器在这个过程中至少做了两次请求。
 
前面的例子中我们在Spring MVC中Controller中请求处理方法return的都是默认使用转发来实现的,如果我们需要指明是转发还是重定向则需要在retrun返回的字符串前加缀加上 forward: 或 redirect:
如:return "forward:/user/login";    //转发到一个请求方法
    return "redirect:/user/register";    //重定向到一个请求方法
注意:上述的转发和重定向中的内容都会受到配置视图解析器的处理,如果需要直接转到到一个资源(如:某个静态资源页面)而不需要DispatcherServlet的处理,则需要在配置文件中使用mvc:resource配置,其配置如下:
     <!-- css 目录下的所有文件可见 -->
       < mvc:resources location= "/css/" mapping= "/css/**" />
       <!-- html 目录下的所有文件可见 -->
       < mvc:resources location= "/html/" mapping= "/html/**" />
       <!-- images目录下的所有文件可见 -->
       < mvc:resources location= "/images/" mapping= "/images/**" />
 
五、其它相关注解
 
@Autowired注解
在前面所有的例子及说明中我们看到了MVC中的V、和C,这里的M层由控制一并充当了,这样的设计是不够合理的,应该把M层的实际作用从C层中剥离出来。这个时候可以使用Spring的依赖注入的优点,把相应的M层定义出来并自动注入到C层中,这样的话则M层的逻辑则从C层中剥离出来了,只保留了他们之前的依赖关系。
使用@Service注解来注解一个M,同时要在Spring的配置文件中指定自动扫描包<context:component-scan base-package="包路径"/>(这个时候包路径及其子包都会被spring自动扫描)
 
新增service的接口及实现类com.xiaoxie.service.UserService、com.xiaoxie.service.impl.UserserviceImpl
package com.xiaoxie.service;
import com.xiaoxie.pojo.UserForm;
/*
 * 这里service的接口:指定M层提供的服务有哪些
 */
public interface UserService {
       boolean login(UserForm user);
       boolean register(UserForm user);
}
 
package com.xiaoxie.service.impl;
import org.springframework.stereotype.Service;
import com.xiaoxie.pojo.UserForm;
import com.xiaoxie.service.UserService;
//@Service指定这是一个服务M层
@ Service
public class UserServiceImpl implements UserService {
       /**
       * 用户登录服务
       */
       @Override
       public boolean login(UserForm user) {
             if( "xiaoxie".equals( user.getUname()) && "123456".equals( user.getUpass())) {
                   return true;
            } else {
                   return false;
            }
      }
       /**
       * 用户注册服务
       */
       @Override
       public boolean register(UserForm user) {
             if( "xiaoxie".equals( user.getUname()) && "123456".equals( user.getUpass())) {
                   return true;
            } else {
                   return false;
            }
      }
}
这里注意使用了@Service注解指定实现类为一个service服务M层
 
对UserController类中的login及register方法进行调整,调整后的类如下
package com.xiaoxie.controller;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpSession;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;
import com.xiaoxie.pojo.UserForm;
import com.xiaoxie.service.UserService;
/*
 * @Controller:表示UserController的实例就是一个控制器
 * 它相当于如下两个写法
 * @Controller(userController)/@Controller(value="userController")
 */
@Controller
@RequestMapping( "/user")
public class UserController {
       //使用记录日志的对象
       private static final Log logger = LogFactory. getLog(UserController. class);
       @Autowired
       public UserService userService;
       /**
       * 处理用户登录请求
       * @param user UserForm对象,接收登录页面提交的请求参数
       * @param session
       * @param model
       * @return
       */
       @RequestMapping( "/login")
       public String login(UserForm user, HttpSession session, Model model) {
             if( userService.login( user)) {
                   //验证成功
                   session.setAttribute( "login_user", user);
                   logger.info( "登录成功!");
                   return "main";     //转发到main.jsp
            } else {
                   logger.info( "登录失败!");
                   model.addAttribute( "error_tips", "用户名:" + user.getUname() + "登录失败!");
                   return "login";
            }
      }
      
       /**
       * 处理用户注册请求
       * @param user user UserForm对象,接收注册页面提交的请求参数
       * @param model
       * @return
       */
       @RequestMapping( "/register")
       public String register(UserForm user,Model model) {
             //对注册提交的信息进行后端校验
             if( userService.register( user)) {
                   logger.info( "注册成功!");
                   return "login";    //转发到login.jsp
            } else {
                   logger.info( "注册失败!");
                   model.addAttribute( "error_tips", "用户名:" + user.getUname() + "注册失败!");
                   return "register";      
            }
      }
      
       @RequestMapping( "/param1")
       public String param1(HttpSession session,HttpServletRequest request) {
             session.setAttribute( "session_key", "session的值");
             request.setAttribute( "request_key", "request的值");
             return "param";
      }
      
       @RequestMapping( "/param2")
       public String param2(Model model) {
             model.addAttribute( "model_key", "model类型绑定的值");
             return "param";
      }
}
这里注意:使用了@Autowired把service的实现类自动注入进来
同时在配置文件中需要指定扫描service包
<context:component-scan base-package= "com.xiaoxie.service"/>
 
在实验过程中不小心把工程src目录下的内容删除掉了,解决办法
在硬盘中找到项目的目录,在目录下的WebContent/WEB-INF/jsp下有对应最新的src目录,拷贝到项目目录上,同时在Eclipse中刷新工程即可。
 
@ModelAttribute注解
这个注解有两个功能:
第一,当把它放到处理方法的形参上的时候,会把多个请求封装为一个实体对象,并且会自己动暴露模型数据,不需要使用model.addAttribute,可以直接在jsp中使用EL表达式获取到模型对象的数据(在前面已经说过)
第二,被它注解的方法会在每次调用控制器类之前先调用这个方法,这样就可以在这个方法中处理一些共用的需地前置处理的功能(比如:权限的检查)
 
比如:新增一个BaseController类
package com.xiaoxie.controller;
import org.springframework.web.bind.annotation.ModelAttribute;
public class BaseController {
       @ModelAttribute
       public void checkAndInit() {
            System. out.println( "----------加载初始化资源--------");
            System. out.println( "----------检查执行权限---------");
      }
}
 
在UserController类上加上extends BaseController,这样在每次执行UserController中的请求处理方法之前都会在控掉台打印出上面的两个语句。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值