SSM框架之SpringMVC学习

关于SpringMVC

ssm:Spring+SpringMVC+Mybatis

  • MVC三层架构:
    • Model:模型(dao,service)
    • View:视图(jsp)
    • Controller:控制器(servlet)
  • 试问:你的项目的架构,是设计好的,还是演进的?回答:ALL FROM ONE

回顾servlet

  • 一个简单servlet

    public class helloServlet extends HttpServlet {
        @Override
        protected void doGet(HttpServletRequest req, HttpServletResponse resp)
         throws ServletException, IOException {
            //1. 获取前端参数
            String method = req.getParameter("method");
            if (method.equals("add")){
                req.getSession().setAttribute("msg","执行了add方法");
            }
            if (method.equals("delete")){
                req.getSession().setAttribute("msg","执行了delete方法");
            }
            //2. 调用业务层
    
            //3. 视图转发或者重定向
            req.getRequestDispatcher("WEB-INF/jsp/test.jsp").forward(req,resp);
        }
    
        @Override
        protected void doPost(HttpServletRequest req, HttpServletResponse resp)
         throws ServletException, IOException {
            doGet(req, resp);
        }
    }
    
  • 配置web.xml,servlet映射

    <servlet>
        <servlet-name>hello</servlet-name>
        <servlet-class>com.study.servlet.helloServlet</servlet-class>
    </servlet>
    <servlet-mapping>
        <servlet-name>hello</servlet-name>
        <url-pattern>/hello</url-pattern>
    </servlet-mapping>
    
    <!--设置超时-->
    <!--<session-config>
        <session-timeout>15</session-timeout>
    </session-config>-->
    
    <!--设置默认页面-->
    <!--<welcome-file-list>
        <welcome-file>index.jsp</welcome-file>
    </welcome-file-list>-->
    
  • jsp表单填写参数

    <form action="/hello" method="post">
        <input type="text" name="method">
        <input type="submit">
    </form>
    
  • jsp展示结果

    <body>
        ${msg}
    </body>
    

实现SpringMVC

  • 简要分析执行流程

    在这里插入图片描述
    在这里插入图片描述

    1. DispatcherServlet表示前置控制器,是整个SpringMVC的控制中心。用户发出请求,DispatcherServlet接收请求并拦截请求
      • 我们假设请求的url为:http://localhost:8080/SpringMVC/hello
      • 如上url拆分成三部分
      • http://localhost:8080:服务器域名
      • SpringMVC:部署在服务器上的web站点
      • hello:表示控制器
      • 通过分析,如上url表示为:请求位于服务器localhost:8080上的SpringMVC站点的hello控制器
    2. HandlerMapping为处理器映射。DispatcherServlet调用HandlerMapping,HandlerMapping根据请求url查找Handler
    3. HandlerExecution表示具体的Handler,其主要作用是根据url查找控制器,如上url被查找控制器为:hello
    4. HandlerExecution将解析后的信息传递给DispatcherServlet,如解析控制器映射等
    5. HandlerAdapter表示处理器适配器,其按照特定的规则去执行Handler
    6. Handler让具体的Controller执行
    7. Controller将具体的执行信息返回给HandlerAdapter ,如ModelAndView
    8. HandlerAdapter将视图逻辑名或模型传递给DispatcherServlet
    9. DispatcherServlet调用视图解析器(ViewResolver)来解析HandlerAdapter传递的逻辑视图名
    10. 视图解析器将解析的逻辑视图名传给DispatcherServlet
    11. DispatcherServlet根据视图解析器解析的视图结果,调用具体的视图
    12. 最终视图呈现给用户
  • 代码实现

    • 配置web.xml,接管所有servlet

      <!--配置DispatchServlet:这个是SpringMVC的核心; 请求分发器,前端控制器-->
      <servlet>
          <servlet-name>springmvc</servlet-name>
          <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
          <!--关联一个springmvc的配置文件:建议使用(spring配置文件名字)-servlet.xml-->
          <init-param>
              <param-name>contextConfigLocation</param-name>
              <param-value>classpath:springmvc-servlet.xml</param-value>
          </init-param>
          <!--启动级别1:服务器一启动跟着启动-->
          <load-on-startup>1</load-on-startup>
      </servlet>
      
      <!-- / 匹配所有的请求(不包括.jsp)-->
      <!-- /* 匹配所有的请求(包括.jsp) 返回jsp视图时再次进入DispatcherServlet类,
      导致找不到对应的controller报错-->
      <servlet-mapping>
          <servlet-name>springmvc</servlet-name>
          <url-pattern>/</url-pattern>
      </servlet-mapping>
      
    • springmvc-servlet.xml

      <?xml version="1.0" encoding="UTF-8"?>
      <beans xmlns="http://www.springframework.org/schema/beans"
             xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
             xsi:schemaLocation="http://www.springframework.org/schema/beans 
             http://www.springframework.org/schema/beans/spring-beans.xsd">
      
          <!--处理器映射器-->
         <bean class="org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping"/>
          <!--处理器适配器-->
        <bean class="org.springframework.web.servlet.mvc.SimpleControllerHandlerAdapter"/>
      
          <!--视图解析器:DispatcherServlet给它的ModelAndView
              1. 获取了ModelAndView的数据
              2. 解析ModelAndView的视图名字
              3. 拼接视图名字,找到对应的视图 /WEB-INF/jsp/hello.jsp
              4. 将数据渲染到视图上
              -->
          <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver" 
           id="internalResourceViewResolver">
              <!--前缀-->
              <property name="prefix" value="/WEB-INF/jsp/"/>
              <!--后缀-->
              <property name="suffix" value=".jsp"/>
          </bean>
          
          <!--BeanNameUrlHandlerMapping:需要根据bean的id来寻找-->
          <!--Handler,类似于之前的servlet映射-->
          <bean id="/hello" class="com.study.controller.helloController"/>
      
      </beans>
      
    • 实现Controller接口

      public class helloController implements Controller {
          @Override
          public ModelAndView handleRequest(HttpServletRequest httpServletRequest, 
           HttpServletResponse httpServletResponse) throws Exception {
              //ModelAndView 模型和视图
              ModelAndView mv = new ModelAndView();
              //封装对象,放在ModelAndView中.Model
              mv.addObject("msg","helloSpringMVC");
              //封装要跳转的视图,放在ModelAndView中
              mv.setViewName("hello");//拼接后:/WEB-INF/jsp/hello.jsp
              return mv;
          }
      }
      
    • jsp展示结果

      <body>
          ${msg}
      </body>
      
    • 注意:可以会出现404错误,原因是依赖没有导入项目,需要去项目结构中的Artifacts中找到Available Elements里的对应依赖放入项目

注解实现SpringMVC

  • 使用springMVC必须配置的三大件:处理器映射器、处理器适配器、视图解析器

    • 通常,我们只需要手动配置视图解析器,而处理器映射器和处理器适配器只需要开启注解驱动即可,而省去了大段的xml配置
  • 代码实现

    • 配置web.xml

      <!--配置DispatchServlet:这个是SpringMVC的核心; 请求分发器,前端控制器-->
      <servlet>
          <servlet-name>springMVC</servlet-name>
          <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
          <!--DispatcherServlet要绑定Spring-MVC的配置文件-->
          <init-param>
              <param-name>contextConfigLocation</param-name>
              <param-value>classpath:springMVC-servlet.xml</param-value>
          </init-param>
          <load-on-startup>1</load-on-startup>
      </servlet>
      
      <!--
      在SpringMVC中, / 和 /* 的区别
      /:只匹配所有的请求,不会去匹配jsp页面
      /*:匹配所有的请求,包括jsp
      -->
      <servlet-mapping>
          <servlet-name>springMVC</servlet-name>
          <url-pattern>/</url-pattern>
      </servlet-mapping>
      
    • springmvc-servlet.xml

      <?xml version="1.0" encoding="UTF-8"?>
      <beans xmlns="http://www.springframework.org/schema/beans"
             xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
             xmlns:context="http://www.springframework.org/schema/context"
             xmlns:mvc="http://www.springframework.org/schema/mvc"
             xsi:schemaLocation="http://www.springframework.org/schema/beans 
             http://www.springframework.org/schema/beans/spring-beans.xsd
              http://www.springframework.org/schema/context 
              http://www.springframework.org/schema/context/spring-context.xsd
              http://www.springframework.org/schema/mvc 
              http://www.springframework.org/schema/mvc/spring-mvc.xsd">
      
          <!--自动扫描包,让指定包下的注解生效,由IOC容器统一管理-->
          <context:component-scan base-package="com.study.controller"/>
      
          <!--让springMVC不处理静态资源 .css .js .html .mp3 .mp4 ...-->
          <mvc:default-servlet-handler/>
      
          <!--
          支持mvc注解驱动:
              在spring中一般采用@RequestMapping注解来完成映射关系
              要想使@RequestMapping注解生效
              必须向上下文中注册DefaultAnnotationHandlerMapping
              和一个AnnotationMethodHandlerAdapter实例
              这两个实例分别在类级别和方法级别处理
              而annotation-driven配置帮助我们自动完成上述两个实例的注入
          -->
          <mvc:annotation-driven/>
      
          <!--视图解析器-->
          <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver" 
           id="internalResourceViewResolver">
              <!--前缀-->
              <property name="prefix" value="/WEB-INF/jsp/"/>
              <!--后缀-->
              <property name="suffix" value=".jsp"/>
          </bean>
      </beans>
      
    • Controller类

      @Controller //表示被IOC容器接管
      //@RestController //不会被视图解析器处理,仅仅返回字符串(与@Controller选其一)
      public class helloController {
          @RequestMapping("/hello") //对应的url路径调用该方法,类似于之前的servlet映射
          public String hello(Model model) {
              //封装数据
              model.addAttribute("msg", "hello,SpringMVC");
              return "hello"; //会被视图解析器处理,表示要跳转的视图
          }
      }
      

RESTful风格

  • 使用RESTful操作资源:可以通过不同的请求方式来实现不同的效果,如下,请求地址一样,但功能可以不同

    • http://localhost:8080/item/1 查询,GET
    • http://localhost:8080/item 增加,POST
    • http://localhost:8080/item 更新,PUT
    • http://localhost:8080/item/1 删除,DELETE
  • 新建一个类,在SpringMVC中可以使用@PathVariable注解,让方法参数的值对应绑定到一个URL模板变量上

    @Controller
    public class RestFulController {
        //原来的:http://localhost:8080/springMVC_4_controller_war_exploded/add?a=1&b=2
        //RestFul:http://localhost:8080/springMVC_4_controller_war_exploded/add/1/2
        //映射访问路径
        //@RequestMapping("/add/{a}/{b}")
        //不同访问方式触发不同方法 GET
        @GetMapping("/add/{a}/{b}")
        public String test1(@PathVariable int a, @PathVariable int b, Model model) {
            int res = a + b;
            model.addAttribute("msg", "结果1为" + res);
            return "test";
        }
    
        //映射访问路径,限定必须是GET请求
        //@RequestMapping(value = "/add", method = {RequestMethod.GET})
        //不同访问方式触发不同方法 POST
        @PostMapping("/add/{a}/{b}")
        public String test2(@PathVariable int a, @PathVariable int b, Model model) {
            int res = a + b;
            model.addAttribute("msg", "结果2为" + res);
            return "test";
        }
    }
    
  • 使用路径变量的好处:

    • 使路径变得更加简洁
    • 获得参数更加方便,框架会自动进行类型转换
    • 通过路径变量的类型可以约束访问参数,如果类型不一样,则访问不到对应的请求方法,如这里访问是的路径是/add/1/a,则路径与方法不匹配,而不会是参数转换失败
  • 小结:

    • SpringMVC的@RequestMapping注解能够处理HTTP请求的方法,比如GET,PUT,POST,DELETE以及PATCH
    • 所有的地址栏请求默认都会是HTTP GET类型
    • 方法级别的注解变体有如下几个:(组合注解)
      • @GetMapping(= @RequestMapping(method = RequestMethod.GET)的一个快捷方式)
      • @PostMapping
      • @DeleteMapping
      • @PatchMapping

重定向和转发

  • 重定向

    @Controller
    public class controllerTest2 {
        @RequestMapping("/t2")
        public String test(Model model){
            //重定向,默认是转发,加了redirect:/变为重定向
            model.addAttribute("msg","controllerTest2");
            return "redirect:/index.jsp";
        }
    }
    
  • 重定向不会走视图解析器的,转发才会。并且重定向不能访问WEB-INF目录下的内容。想要通过重定向访问WEB-INF目录下的内容,可以通过另外一个方法转发跳转到jsp

接受请求参数及数据回显

  • 提交的域名称和处理方法的参数名一致

    @Controller
    @RequestMapping("/user")
    public class UserController {
        //localhost:8080/user/t1?name=xxx
        @GetMapping("/t1")
        public String test1(String name, Model model) {
            //1. 接收前端参数
            System.out.println("接收到前端的参数为:" + name);
            //2. 将返回的结果传递给前端
            model.addAttribute("msg", name);
            //3. 视图跳转
            return "test";
        }
        
        //前端接收的是一个对象:id, name, age
        /*
        * 1. 接收前端用户传递的参数,判断参数的名字,假设名字直接在方法上,可以直接使用
        * 2. 假设传递的是一个对象User,匹配User对象中的字段名:如果名字一致则OK,否则匹配不到
        * */
        @GetMapping("/t2")
        public String test2(User user) {
            System.out.println(user);
            return "test";
        }
    }
    
  • 提交的域名称和处理方法的参数名不一致

    @Controller
    @RequestMapping("/user")
    public class UserController {
        //localhost:8080/user/t1?name=xxx
        @GetMapping("/t1")
        public String test1(@RequestParam("username") String name, Model model) {
            //1. 接收前端参数
            System.out.println("接收到前端的参数为:" + name);
            //2. 将返回的结果传递给前端
            model.addAttribute("msg", name);
            //3. 视图跳转
            return "test";
        }
    }
    

乱码问题解决

  • 加过滤器(自己写)

    • 编写过滤器类

      public class EncodingFilter implements Filter {
          @Override
          public void init(FilterConfig filterConfig) throws ServletException {
          }
      
          @Override
          public void doFilter(ServletRequest servletRequest,
           ServletResponse servletResponse, FilterChain filterChain)
           throws IOException, ServletException {
              servletRequest.setCharacterEncoding("utf-8");
              servletResponse.setCharacterEncoding("utf-8");
              filterChain.doFilter(servletRequest, servletResponse);
          }
      
          @Override
          public void destroy() {
          }
      }
      
    • web.xml里加入过滤器配置

      <filter>
          <filter-name>encoding</filter-name>
          <filter-class>com.study.filter.EncodingFilter</filter-class>
      </filter>
      <filter-mapping>
          <filter-name>encoding</filter-name>
          <!--拦截所有文件-->
          <url-pattern>/*</url-pattern>
      </filter-mapping>
      
  • SpringMVC提供的过滤器

    • web.xml里加入配置

      <filter>
          <filter-name>encoding</filter-name>
          <filter-class>
          	org.springframework.web.filter.CharacterEncodingFilter
          </filter-class>
          <init-param>
              <param-name>encoding</param-name>
              <param-value>utf-8</param-value>
          </init-param>
      </filter>
      <filter-mapping>
          <filter-name>encoding</filter-name>
          <url-pattern>/*</url-pattern>
      </filter-mapping>
      

Controller返回JSON数据

什么是JSON

  • JSON(JavaScript Object Notation),JS对象标记,是一种轻量级的数据交换格式,目前使用特别广泛

  • 采用完全独立于编程语言的文本格式来存储和表示数据

  • 简洁和清晰的层次结构使得JSON成为理想的数据交换语言

  • 易于人阅读和编写,同时也易于机器解析和生成,并有效地提升网络传输效率

  • JSON键值对是用来保存JavaScript对象的一种方式,和JavaScript对象的写法也大同小异,键/值对组合中的键名写在前面并用双引号包裹,使用冒号分隔,然后写值:

    {"name" : "cbc"}
    {"age" : "3"}
    {"sex" : "男"}
    
  • json和js对象互转

    <script type="text/javascript">
        //编写一个JavaScript对象
        let user = {
            name: "cbc",
            age: 3,
            sex: "男"
        };
        //将js对象转换为json对象
        let json = JSON.stringify(user);
        console.log(json);
        //将json对象转换为js对象
        let obj = JSON.parse(json);
        console.log(obj);
        //console.log(user);
    </script>
    

Jackson使用

  • Jackson应该是目前比较好的json解析工具了

  • 当然工具不止这一个,比如还有阿里巴巴的fastjson等等

  • 我们这里使用Jackson,使用它需要导入它的jar包

    <!-- https://mvnrepository.com/artifact/com.fasterxml.jackson.core/jackson-databind -->
    <dependency>
        <groupId>com.fasterxml.jackson.core</groupId>
        <artifactId>jackson-databind</artifactId>
        <version>2.12.1</version>
    </dependency>
    
  • 发现乱码,需要设置一下编码格式utf-8,以及它的返回值类型

    • 通过@RequestMapping的produces属性来实现(原生解决方法)

      //produces:指定响应体返回类型和编码
      @RequestMapping(value = "/j1", produces = "application/json;charset=utf-8")
      
    • 我们可以在SpringMVC的配置文件上添加一段消息StringHttpMessageConverter转换配置

      <!--JSON乱码问题配置-->
      <mvc:annotation-driven>
          <mvc:message-converters>
              <bean class="org.springframework.http.converter.StringHttpMessageConverter">
                  <constructor-arg value="UTF-8"/>
              </bean>
              <bean 
      class="org.springframework.http.converter.json.MappingJackson2HttpMessageConverter">
                  <property name="objectMapper">
                      <bean 
          class="org.springframework.http.converter.json.Jackson2ObjectMapperFactoryBean">
                          <property name="failOnEmptyBeans" value="false"/>
                      </bean>
                  </property>
              </bean>
          </mvc:message-converters>
      </mvc:annotation-driven>
      
  • 传对象和对象集合(类上事先加好@RestController,或者在方法上加@ResponseBody)

    @RequestMapping("/j1")
    public String json1() throws JsonProcessingException {
        //用jackson的ObjectMapper类处理对象
        ObjectMapper mapper = new ObjectMapper();
        //创建一个对象
        User user = new User("cbc",3,"男");
        //把对象参数和属性转换成字符串
        String string = mapper.writeValueAsString(user);
        return string;
    }
    
    @RequestMapping("/j2")
    public String json2() throws JsonProcessingException {
        ObjectMapper mapper = new ObjectMapper();
        List<User> users = new ArrayList<>();
        User user0 = new User("cbc0",3,"男");
        User user1 = new User("cbc1",3,"男");
        User user2 = new User("cbc2",3,"男");
        User user3 = new User("cbc3",3,"男");
        User user4 = new User("cbc4",3,"男");
        User user5 = new User("cbc5",3,"男");
        users.add(user0);
        users.add(user1);
        users.add(user2);
        users.add(user3);
        users.add(user4);
        users.add(user5);
        String string = mapper.writeValueAsString(users);
        return string;
    }
    
  • 传时间

    @RequestMapping("/j3")
    public String json3() throws JsonProcessingException {
        //ObjectMapper,时间解析后的默认格式为:Timestamp,时间戳
        ObjectMapper mapper = new ObjectMapper();
        Date date = new Date();
        //自定义日期格式
        SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        //方法2:使用ObjectMapper来格式化输出,关闭时间戳,设置日期格式
        /*mapper.configure(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS, false);
        mapper.setDateFormat(simpleDateFormat);
        return mapper.writeValueAsString(date);*/
        //方法1:直接定义日期格式处理date
        return mapper.writeValueAsString(simpleDateFormat.format(date));
    }
    
封装思想:写json工具类
  • 工具类

    public class JsonUtils {
        //重载
        public static String getJson(Object object) {
            //不要傻乎乎的重复造轮子,巧妙偷懒
            return getJson(object, "yyyy-mm-dd hh:mm:ss");
        }
    
        public static String getJson(Object object, String dateFormat) {
            //由于重载方法偷了点懒,所以此处只能用方法2处理日期格式,用方法1会报错
            ObjectMapper mapper = new ObjectMapper();
            mapper.configure(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS,false);
            SimpleDateFormat simpleDateFormat = new SimpleDateFormat(dateFormat);
            mapper.setDateFormat(simpleDateFormat);
            try {
                return mapper.writeValueAsString(object);
            } catch (JsonProcessingException e) {
                e.printStackTrace();
            }
            return null;
        }
    }
    
  • 使用工具类

    @RequestMapping("/j4")
    public String json4(){
        Date date = new Date();
        return JsonUtils.getJson(date,"yyyy-mm-dd hh:mm:ss");
    }
    
    @RequestMapping("/j5")
    public String json5() throws JsonProcessingException {
        List<User> users = new ArrayList<>();
        User user0 = new User("cbc0",3,"男");
        User user1 = new User("cbc1",3,"男");
        User user2 = new User("cbc2",3,"男");
        User user3 = new User("cbc3",3,"男");
        User user4 = new User("cbc4",3,"男");
        User user5 = new User("cbc5",3,"男");
        users.add(user0);
        users.add(user1);
        users.add(user2);
        users.add(user3);
        users.add(user4);
        users.add(user5);
        return JsonUtils.getJson(users);
    }
    

FastJson使用

  • fastjson.jar是阿里开发的一款专门用于Java开发的包,可以方便的实现json对象与JavaBean对象的转换,实现JavaBean对象与json字符串的转换,实现json对象与json字符串的转换。实现json的转换方法很多,最后的实现结果都是—样的

  • fastjson的pom依赖

    <!-- https://mvnrepository.com/artifact/com.alibaba/fastjson -->
    <dependency>
        <groupId>com.alibaba</groupId>
        <artifactId>fastjson</artifactId>
        <version>1.2.75</version>
    </dependency>
    
  • fastjson三个主要类:

    • JSONObject:代表json对象
      • JSONObject实现了Map接口,猜想JSONObject底层操作是由Map实现的
      • JSONObject对应json对象,通过各种形式的get()方法可以获取json对象中的数据,也可利用诸如size(),isEmpty()等方法获取"键/值"对的个数和判断是否为空。其本质是通过实现Map接口并调用接口中的方法完成的
    • JSONArray:代表json对象数组
      • 内部是有List接口中的方法来完成操作的
    • JSON:代表JSONObject和JSONArray的转化
      • JSON类源码分析与使用
      • 仔细观察这些方法,主要是实现json对象,json对象数组,javabean对象,json字符串之间的相互转化
  • fastjson使用

    @RequestMapping("/j6")
    @ResponseBody
    public String json6() throws JsonProcessingException {
        List<User> users = new ArrayList<>();
        User user0 = new User("cbc0",3,"男");
        User user1 = new User("cbc1",3,"男");
        User user2 = new User("cbc2",3,"男");
        User user3 = new User("cbc3",3,"男");
        User user4 = new User("cbc4",3,"男");
        User user5 = new User("cbc5",3,"男");
        users.add(user0);
        users.add(user1);
        users.add(user2);
        users.add(user3);
        users.add(user4);
        users.add(user5);
        String string = JSON.toJSONString(users);
        return string;
    }
    
  • fastjson常用方法

    • 解析为java对象(支持深度解析)
      • T t = JSON.parseObject(jsonstr, T.class); //解析{...}为对象
      • List<t> list = JSON.parseArray(jsonstr, T.class); //解析[{},{}...]为集合
    • JSON字符串解析为JSON对象
      • 如果不知道json字符串具体是JSONObject还是JSONArray可以使用
        • Object obj = JSON.parse(jsonstr); // obj可能为String、JSONArray、JSONObject
      • 如果确定字符串为JSONObject类型
        • JSONObject parseObject = JSON.parseObject(jsonstr);
      • 如果确定字符串为JSONArray类型
        • JSONArray jsonarray = JSON.parseArray(jsonstr);
    • java对象解析为JSON对象
      • JSONObject json = (JSONObject) JSON.toJSON(bean);
    • JSON对象解析为java对象
      • Bean bean = JSON.toJavaObject(jsonobject, Bean.class);

SSM整合

  1. 导入依赖

    <dependencies>
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.12</version>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>5.1.46</version>
        </dependency>
        <!--数据库连接池:c3p0-->
        <dependency>
            <groupId>com.mchange</groupId>
            <artifactId>c3p0</artifactId>
            <version>0.9.5.2</version>
        </dependency>
    
        <!--Servlet JSP-->
        <dependency>
            <groupId>javax.servlet.jsp</groupId>
            <artifactId>jsp-api</artifactId>
            <version>2.2</version>
            <scope>provided</scope>
        </dependency>
        <dependency>
            <groupId>javax.servlet</groupId>
            <artifactId>servlet-api</artifactId>
            <version>2.5</version>
            <scope>provided</scope>
        </dependency>
        <dependency>
            <groupId>javax.servlet</groupId>
            <artifactId>jstl</artifactId>
            <version>1.2</version>
        </dependency>
    
        <!--Mybatis-->
        <dependency>
            <groupId>org.mybatis</groupId>
            <artifactId>mybatis</artifactId>
            <version>3.5.3</version>
        </dependency>
        <dependency>
            <groupId>org.mybatis</groupId>
            <artifactId>mybatis-spring</artifactId>
            <version>2.0.5</version>
        </dependency>
    
        <!--Spring-->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-webmvc</artifactId>
            <version>5.3.4</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-jdbc</artifactId>
            <version>5.3.4</version>
        </dependency>
    </dependencies>
    
    <!--静态资源导出问题-->
    <!--<build>
        <resources>
            <resource>
                <directory>src/main/resources</directory>
                <includes>
                    <include>**/*.properties</include>
                    <include>**/*.xml</include>
                </includes>
                <filtering>true</filtering>
            </resource>
            <resource>
                <directory>src/main/java</directory>
                <includes>
                    <include>**/*.properties</include>
                    <include>**/*.xml</include>
                </includes>
                <filtering>true</filtering>
            </resource>
        </resources>
    </build>-->
    
  2. 编写database.properties用于关联数据库

    jdbc.driver=com.mysql.jdbc.Driver
    jdbc.url=jdbc:mysql://localhost:3306/ssmbuild?useSSL=false&useUnicode=true&characterEncoding=UTF-8&serverTimezone=UTC
    jdbc.username=root
    jdbc.password=123456
    
  3. 创建spring配置文件applicationContext.xml,编写mybatis-config.xml配置文件

    <?xml version="1.0" encoding="UTF-8" ?>
    <!DOCTYPE configuration
            PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
            "http://mybatis.org/dtd/mybatis-3-config.dtd">
    <configuration>
    
        <!--配置数据源,现在交给spring去做-->
        
        <!--开启mybatis自带日志-->
        <settings>
            <setting name="logImpl" value="STDOUT_LOGGING"/>
        </settings>
        
        <!--别名-->
        <typeAliases>
            <package name="com.pp.pojo"/>
        </typeAliases>
    
        <!--映射mapper.xml-->
        <mappers>
            <mapper resource="dao/BookMapper.xml"/>
        </mappers>
    
    </configuration>
    
  4. 建好必要的包,编写实体类

    public class Books {
        private int bookID;
        private String bookName;
        private int bookCounts;
        private String detail;
    
  5. 编写dao层接口

    public interface BookMapper {
    
        //增加一本书
        int addBook(Books books);
    
        //删除一本书
        int deleteBookById(@Param("bookId") int id);
    
        //更新一本书
        int updateBook(@Param("books") Books books, @Param("bookId") int id);
    
        //查询一本书
        Books queryBookById(@Param("bookId") int id);
    
        //查询全部书
        List<Books> queryAllBook();
    }
    
  6. 编写mapper.xml

    <?xml version="1.0" encoding="UTF-8" ?>
    <!DOCTYPE mapper
            PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
            "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
    <mapper namespace="com.pp.dao.BookMapper">
    
        <insert id="addBook" parameterType="Books">
            insert into books (bookName, bookCounts, detail)
            values (#{bookName}, #{bookCounts}, #{detail});
        </insert>
    
        <delete id="deleteBookById" parameterType="int">
            delete
            from books
            where bookID = #{bookId}
        </delete>
    
        <update id="updateBook">
            update books
            set bookName=#{books.bookName},
                bookCounts=#{books.bookCounts},
                detail=#{books.detail}
            where bookID = #{bookId};
        </update>
    
        <select id="queryBookById" parameterType="int" resultType="Books">
            select *
            from books
            where bookID = #{bookId};
        </select>
    
        <select id="queryAllBook" resultType="Books">
            select *
            from books;
        </select>
    
    </mapper>
    
  7. 编写service层接口

    public interface BookService {
        //增加一本书
        int addBook(Books books);
    
        //删除一本书
        int deleteBookById(@Param("bookId") int id);
    
        //更新一本书
        int updateBook(@Param("books") Books books, @Param("bookId") int id);
    
        //查询一本书
        Books queryBookById(@Param("bookId") int id);
    
        //查询全部书
        List<Books> queryAllBook();
    }
    
  8. 编写service接口实现类

    public class BookServiceImpl implements BookService {
    
        //service调dao层:组合DAO
        private BookMapper bookMapper;
    
        public void setBookMapper(BookMapper bookMapper) {
            this.bookMapper = bookMapper;
        }
    
        @Override
        public int addBook(Books books) {
            return bookMapper.addBook(books);
        }
    
        @Override
        public int deleteBookById(int id) {
            return bookMapper.deleteBookById(id);
        }
    
        @Override
        public int updateBook(Books books, int id) {
            return bookMapper.updateBook(books, id);
        }
    
        @Override
        public Books queryBookById(int id) {
            return bookMapper.queryBookById(id);
        }
    
        @Override
        public List<Books> queryAllBook() {
            return bookMapper.queryAllBook();
        }
    }
    
  9. 编写spring-dao.xml配置文件

    <?xml version="1.0" encoding="UTF-8"?>
    <beans xmlns="http://www.springframework.org/schema/beans"
           xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
           xmlns:context="http://www.springframework.org/schema/context"
           xsi:schemaLocation="http://www.springframework.org/schema/beans 
           http://www.springframework.org/schema/beans/spring-beans.xsd
           http://www.springframework.org/schema/context
           https://www.springframework.org/schema/context/spring-context.xsd">
    
        <!--1. 关联数据库配置文件-->
        <context:property-placeholder location="classpath:database.properties"/>
    
        <!--2. 连接池
            dbcp:半自动化操作,不能自动连接
            c3p0:自动化操作(自动化的加载配置文件,并且可以自动设置到对象中)
            druid hikari
        -->
        <bean class="com.mchange.v2.c3p0.ComboPooledDataSource" id="dataSource">
            <property name="driverClass" value="${jdbc.driver}"/>
            <property name="jdbcUrl" value="${jdbc.url}"/>
            <property name="user" value="${jdbc.username}"/>
            <property name="password" value="${jdbc.password}"/>
    
            <!--c3p0连接池私有属性-->
            <!--<property name="maxPoolSize" value="30"/>
            <property name="minPoolSize" value="10"/>
            &lt;!&ndash;关闭连接后不自动commit&ndash;&gt;
            <property name="autoCommitOnClose" value="false"/>
            &lt;!&ndash;获取连接超时时间&ndash;&gt;
            <property name="checkoutTimeout" value="10000"/>
            &lt;!&ndash;当获取连接失败重试次数&ndash;&gt;
            <property name="acquireRetryAttempts" value="2"/>-->
        </bean>
    
        <!--3. sqlSessionFactory 这里的id必须跟下面sqlSessionFactoryBeanName的值相同-->
        <bean class="org.mybatis.spring.SqlSessionFactoryBean" id="sqlSessionFactory">
            <property name="dataSource" ref="dataSource"/>
            <!--绑定mybatis的配置文件-->
            <property name="configLocation" value="classpath:mybatis-config.xml"/>
        </bean>
    
        <!--4. 配置dao接口扫描包,动态的实现了dao接口注入到spring容器中-->
        <!--MapperScannerConfigurer会扫描包中的所有接口,把每个接口都执行一次getMapper()方法,
        得到每个接口的对象-->
        <bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
            <!--注入sqlSessionFactory 属性的值必须与上面SqlSessionFactoryBean的id相同-->
            <property name="sqlSessionFactoryBeanName" value="sqlSessionFactory"/>
            <!--要扫描的dao包-->
            <property name="basePackage" value="com.pp.dao"/>
        </bean>
    
    </beans>
    
  10. 编写spring-service.xml配置文件

    <?xml version="1.0" encoding="UTF-8"?>
    <beans xmlns="http://www.springframework.org/schema/beans"
           xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
           xmlns:context="http://www.springframework.org/schema/context"
           xsi:schemaLocation="http://www.springframework.org/schema/beans 
           http://www.springframework.org/schema/beans/spring-beans.xsd 
           http://www.springframework.org/schema/context 
           https://www.springframework.org/schema/context/spring-context.xsd">
    
        <!--1. 扫描service下的包-->
        <context:component-scan base-package="com.pp.service"/>
    
        <!--2. 将所有业务类注入到spring,可以通过配置或注解-->
        <bean class="com.pp.service.BookServiceImpl" id="bookServiceImpl">
            <property name="bookMapper" ref="bookMapper"/>
        </bean>
    
        <!--3. 声明式事务配置-->
        <bean class="org.springframework.jdbc.datasource.DataSourceTransactionManager" 
        	id="transactionManager">
            <!--注入数据源-->
            <property name="dataSource" ref="dataSource"/>
        </bean>
    
        <!--4. aop横切事务支持-->
    
    
    </beans>
    
  11. 把有关spring的所有配置文件整合到applicationContext.xml中

    <?xml version="1.0" encoding="UTF-8"?>
    <beans xmlns="http://www.springframework.org/schema/beans"
           xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
           xsi:schemaLocation="http://www.springframework.org/schema/beans 
           http://www.springframework.org/schema/beans/spring-beans.xsd">
    
        <import resource="classpath:spring-dao.xml"/>
        <import resource="classpath:spring-service.xml"/>
        <!--spring-mvc.xml在后续编写-->
        <import resource="classpath:spring-mvc.xml"/>
    
    </beans>
    
  12. 添加web框架支持,配置web.xml

    <!--配置DispatchServlet-->
    <servlet>
        <servlet-name>springmvc</servlet-name>
        <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
        <init-param>
            <param-name>contextConfigLocation</param-name>
            <param-value>classpath:applicationContext.xml</param-value>
        </init-param>
        <load-on-startup>1</load-on-startup>
    </servlet>
    <servlet-mapping>
        <servlet-name>springmvc</servlet-name>
        <url-pattern>/</url-pattern>
    </servlet-mapping>
    
    <!--乱码过滤-->
    <filter>
        <filter-name>encodingFilter</filter-name>
        <filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
        <init-param>
            <param-name>encoding</param-name>
            <param-value>utf-8</param-value>
        </init-param>
    </filter>
    <filter-mapping>
        <filter-name>encodingFilter</filter-name>
        <url-pattern>/*</url-pattern>
    </filter-mapping>
    
    <!--Session-->
    <session-config>
        <session-timeout>15</session-timeout>
    </session-config>
    
  13. 编写spring-mvc.xml配置文件

    <?xml version="1.0" encoding="UTF-8"?>
    <beans xmlns="http://www.springframework.org/schema/beans"
           xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
           xmlns:mvc="http://www.springframework.org/schema/mvc"
           xmlns:context="http://www.springframework.org/schema/context"
           xsi:schemaLocation="http://www.springframework.org/schema/beans 
           http://www.springframework.org/schema/beans/spring-beans.xsd 
           http://www.springframework.org/schema/mvc 
           http://www.springframework.org/schema/mvc/spring-mvc.xsd 
           http://www.springframework.org/schema/context 
           https://www.springframework.org/schema/context/spring-context.xsd">
    
        <!--1. 注解驱动-->
        <mvc:annotation-driven/>
        <!--2. 静态资源过滤-->
        <mvc:default-servlet-handler/>
        <!--3. 扫描包:controller-->
        <context:component-scan base-package="com.pp.controller"/>
        <!--4. 视图解析器-->
        <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
            <property name="prefix" value="/WEB-INF/jsp/"/>
            <property name="suffix" value=".jsp"/>
        </bean>
    
    </beans>
    

Ajax技术

  • 简介:
    • AJAX = Asynchronous JavaScript and XML(异步的JavaScript和XML)
    • Ajax是一种在无需重新加载整个网页的情况下,能够更新部分网页的技术
    • Ajax不是一种新的编程语言,而是一种用于创建更好更快以及交互性更强的Web应用程序的技术
    • 在2005年,Google通过其Google Suggest使AJAX变得流行起来。Google Suggest能够自动帮你完成搜索单词
    • Google Suggest使用AJAX创造出动态性极强的web界面:当您在谷歌的搜索框输入关键字时,JavaScript 会把这些字符发送到服务器,然后服务器会返回一个搜索建议的列表

jQuery.ajax

  • 纯JS原生实现Ajax我们不去讲解这里,直接使用jquery提供的,方便学习和使用,避免重复造轮子,有兴趣可以去了解下JS原生XMLHttpRequest

  • Ajax的核心是XMLHttpRequest对象(XHR)。XHR为向服务器发送请求和解析服务器响应提供了接口。能够以异步方式从服务器获取新数据

  • jQuery提供多个与AJAX有关的方法

  • 通过jQuery AJAX方法,您能够使用HTTP Get和 HTTP Post从远程服务器上请求文本、HTML、XML或JSON同时您能够把这些外部数据直接载入网页的被选元素中

  • jQuery不是生产者,而是大自然搬运工,它是一个库,含有大量js函数(方法)

  • jQuery Ajax本质就是XMLHttpRequest,对他进行了封装,方便调用

  • 实现ajax

    • 导入jQuery.js,spring配置文件中一定要加上静态资源过滤配置

    • 编写controller

      @RequestMapping("/test")
      @ResponseBody
      public String t1(Model model){
          List<User> users = new ArrayList<>();
          User user0 = new User("cbc0", 3, "男");
          User user1 = new User("cbc1", 3, "男");
          User user2 = new User("cbc2", 3, "男");
          User user3 = new User("cbc3", 3, "男");
          User user4 = new User("cbc4", 3, "男");
          User user5 = new User("cbc5", 3, "男");
          users.add(user0);
          users.add(user1);
          users.add(user2);
          users.add(user3);
          users.add(user4);
          users.add(user5);
          model.addAttribute("msg",users);
          return "test2";
      }
      
    • 编写test.jsp

      <%@ page contentType="text/html;charset=UTF-8" language="java" %>
      <html>
      <head>
          <title>Title</title>
          <script src="${pageContext.request.contextPath}/static/js/jquery-3.6.0.js">
          </script>
          <script>
              //加这个$(function){}是为了让dom结构加载完后再执行代码
              $(function () {
                  $("#btn").click(function () {
                      /*
                      * $.post(url, param可省略, success)
                      * */
                      $.post("${pageContext.request.contextPath}/a2", function (data) {
                          let html;
                          for (let i = 0; i < data.length; i++) {
                              html += "<tr>" +
                                  "<td>" + data[i].name + "</td>" +
                                  "<td>" + data[i].age + "</td>" +
                                  "<td>" + data[i].sex + "</td>" +
                                  "</tr>"
                          }
                          $("#content").html(html);
                      })
                  })
              });
              /*function x() {
                  $.post("${pageContext.request.contextPath}/a2", function (data) {
                      let html;
                      for (let i = 0; i < data.length; i++) {
                          html += "<tr>" +
                              "<td>" + data[i].name + "</td>" +
                              "<td>" + data[i].age + "</td>" +
                              "<td>" + data[i].sex + "</td>" +
                              "</tr>"
                      }
                      $("#content").html(html);
                  })
              }*/
          </script>
      </head>
      <body>
      <div>
          <input type="button" value="加载数据" id="btn" <%--οnclick="x()"--%>>
          <table>
              <tr>姓名</tr>
              <tr>年龄</tr>
              <tr>性别</tr>
              <tbody id="content">
              </tbody>
          </table>
      </div>
      </body>
      </html>
      

拦截器

  • SpringMVC的处理器拦截器类似于Servlet开发中的过滤器Filter,用于对处理器进行预处理和后处理。开发者可以自己定义一些拦截器来实现特定的功能

  • 过滤器与拦截器的区别:拦截器是AOP思想的具体应用

  • 过滤器

    • servlet规范中的一部分,任何java web工程都可以使用
    • 在url-pattern中配置了/*之后,可以对所有要访问的资源进行拦截
  • 拦截器

    • 拦截器是SpringMVC框架自己的,只有使用了SpringMVC框架的工程才能使用
    • 拦截器只会拦截访问的控制器(controller)方法,如果访问的是jsp/html/css/image/js是不会进行拦截的
  • 自定义拦截器

    • 想要自定义拦截器,必须实现HandlerInterceptor接口

    • 编写拦截器

      public class MyInterceptor implements HandlerInterceptor {
      
          //return true; 执行下一个拦截器,放行
          //return false; 不执行下一个拦截器,拦截
          @Override
          public boolean preHandle(HttpServletRequest request,
           HttpServletResponse response, Object handler) throws Exception {
              System.out.println("========处理前=======");
              return true;
          }
      
          @Override
          public void postHandle(HttpServletRequest request,
           HttpServletResponse response, Object handler, ModelAndView modelAndView)
           throws Exception {
              System.out.println("========处理后=======");
          }
      
          @Override
          public void afterCompletion(HttpServletRequest request,
           HttpServletResponse response, Object handler, Exception ex) throws Exception {
              System.out.println("========清理=======");
          }
      }
      
    • 编写拦截器配置

      <!--拦截器配置-->
      <mvc:interceptors>
          <mvc:interceptor>
              <!-- /** 包括这个请求下面的所有请求-->
              <mvc:mapping path="/**"/>
              <!--执行下面这个拦截器-->
              <bean class="com.study.config.MyInterceptor"/>
          </mvc:interceptor>
      </mvc:interceptors>
      

登录判断验证

  • 编写拦截器

    public class LoginInterceptor implements HandlerInterceptor {
        @Override
        public boolean preHandle(HttpServletRequest request, HttpServletResponse response,
         Object handler) throws Exception {
            HttpSession session = request.getSession();
            //放行:判断什么情况下登录
            if (session.getAttribute("userLoginInfo") != null) {
                return true;
            }
            //登录提交放行
            if (request.getRequestURI().contains("login")) {
                return true;
            }
            //跳转登录页面放行
            if (request.getRequestURI().contains("goLogin")) {
                return true;
            }
            //这个地方不能用重定向,重定向是客户端级别的,不能走WEB-INF里的页面
            request.getRequestDispatcher("/WEB-INF/jsp/login.jsp").forward(request, response);
            return false;
        }
    }
    
    
  • 编写拦截器配置

    <!--拦截器配置-->
    <mvc:interceptors>
        <mvc:interceptor>
            <!-- /** 包括这个请求下面的所有请求-->
            <mvc:mapping path="/User/**"/>
            <!--执行下面这个拦截器-->
            <bean class="com.study.config.LoginInterceptor"/>
        </mvc:interceptor>
    </mvc:interceptors>
    
  • 编写Controller

    @Controller
    @RequestMapping("/User")
    public class LoginController {
    
        @RequestMapping("/login")
        public String login(HttpSession session, String username, String password,
         Model model) {
            //把用户的信息存在session中
            session.setAttribute("userLoginInfo", username);
            model.addAttribute("username", username);
            return "main";
        }
    
        @RequestMapping("/main")
        public String main(){
            return "main";
        }
    
        @RequestMapping("/goLogin")
        public String goLogin(){
            return "login";
        }
    
        @RequestMapping("goOut")
        public String goOut(HttpSession session){
            session.removeAttribute("userLoginInfo");
            return "redirect:/index.jsp";
        }
    }
    
  • 编写默认页面、登录页面、主页

    <body>
    <a href="${pageContext.request.contextPath}/User/goLogin">登录页面</a>
    <a href="${pageContext.request.contextPath}/User/main">主页</a>
    </body>
    
    <body>
    <%--在WEB-INF下的所有页面或者资源,只能通过controller,或者servlet进行访问--%>
    <h1>登录页面</h1>
    <form action="${pageContext.request.contextPath}/User/login" method="post">
        用户名:<input type="text" name="username">
        密码:<input type="text" name="password">
        <input type="submit" value="提交">
    </form>
    </body>
    
    <body>
    <h1>主页</h1>
    <span>${username}</span>
    <p>
        <a href="${pageContext.request.contextPath}/User/goOut">注销</a>
    </p>
    </body>
    

文件上传和下载

  • 导入依赖

    <!-- https://mvnrepository.com/artifact/commons-fileupload/commons-fileupload -->
    <dependency>
        <groupId>commons-fileupload</groupId>
        <artifactId>commons-fileupload</artifactId>
        <version>1.4</version>
    </dependency>
    <!-- https://mvnrepository.com/artifact/javax.servlet/javax.servlet-api -->
    <dependency>
        <groupId>javax.servlet</groupId>
        <artifactId>javax.servlet-api</artifactId>
        <version>4.0.1</version>
        <scope>provided</scope>
    </dependency>
    
  • 文件上传

    • 编写xml配置

      <!--文件上传配置 id只能用multipartResolver-->
      <bean class="org.springframework.web.multipart.commons.CommonsMultipartResolver" 
       id="multipartResolver">
          <!--请求的编码格式,必须和jsp的pageEncoding属性一致,以便正确读取表单的内容,
          默认为ISO-8859-1-->
          <property name="defaultEncoding" value="utf-8"/>
          <!--上传文件大小上限,单位为字节 10485760=10M-->
          <property name="maxUploadSize" value="10485760"/>
          <property name="maxInMemorySize" value="40960"/>
      </bean>
      
    • 编写Controller

      @RestController
      public class FileController {
      
          @RequestMapping("/upload")
          public void fileUpload(@RequestParam("file") CommonsMultipartFile file,
           HttpServletRequest request) throws IOException {
              //上传路径保存设置
              String path = request.getServletContext().getRealPath("/upload");
              File realPath = new File(path);
              if (!realPath.exists()) {
                  realPath.mkdir();
              }
              //上传文件地址
              System.out.println("上传文件保存地址" + realPath);
              //通过CommonsMultipartFile的方法直接写文件
              file.transferTo(new File(realPath + "/" + file.getOriginalFilename()));
          }
      }
      
  • 文件下载

    • 正常情况用a标签也能实现

    • 使用io流方法:

      • 编写Controller

        @RequestMapping("/download")
        public void fileDownload(HttpServletResponse response,
         HttpServletRequest request) throws Exception {
            //要下载的图片地址
            String path = request.getServletContext().getRealPath("/upload");
            String fileName = "1.jpg";
            //1. 设置response响应头
            response.reset();//设置页面不缓存,清空buffer
            response.setCharacterEncoding("UTF-8");//字符编码
            response.setContentType("multipart/form-data");//二进制传输数据
            //设置响应头
            response.setHeader("Content-Disposition", "attachment;fileName=" + 
             URLEncoder.encode(fileName, "UTF-8"));
            File file = new File(path, fileName);
            //2. 读取文件--输入流
            FileInputStream input = new FileInputStream(file);
            //3. 写出文件--输出流
            ServletOutputStream out = response.getOutputStream();
            byte[] buff = new byte[1024];
            int index = 0;
            //4. 执行写出操作
            while ((index = input.read(buff)) != -1) {
                out.write(buff, 0, index);
                out.flush();
            }
            out.close();
            input.close();
        }
        
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值