Spring整合redis

        最近由于手头项目不多,闲来无事,想去学习下redis。也可能是之前实习(其实现在也是在实习期大笑)找工作很多公司招聘公告上会提及redis,所以兴趣也随之而起。

       网上也有许多教学博客,但大多数都是没有贴出项目或者都是需要下载资源去下载的,所以这里给出github的地址,希望大家可以一起学习https://github.com/wx744197705/ssm-redis,话不多说,开始吧。

 pom.xml,关键就是spring-data-redis和redis-client包
<?xml version="1.0" encoding="UTF-8"?>

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
  <modelVersion>4.0.0</modelVersion>

  <groupId>com.sl.user</groupId>
  <artifactId>redis-demo</artifactId>
  <version>1.0-SNAPSHOT</version>
  <packaging>war</packaging>

  <name>redis-demo Maven Webapp</name>
  <!-- FIXME change it to the project's website -->
  <url>http://www.example.com</url>

  <properties>
    <maven.compiler.source>1.8</maven.compiler.source>
    <maven.compiler.target>1.8</maven.compiler.target>
    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
    <!-- spring版本号 -->
    <spring.version>4.2.5.RELEASE</spring.version>
    <!-- mybatis版本号 -->
    <mybatis.version>3.2.8</mybatis.version>
    <!-- mysql驱动版本号 -->
    <mysql-driver.version>5.1.29</mysql-driver.version>
    <!-- log4j日志包版本号 -->
    <slf4j.version>1.7.18</slf4j.version>
    <log4j.version>1.2.17</log4j.version>
    <kotlin.version>1.2.10</kotlin.version>
  </properties>
  <dependencies>

    <!-- spring mvc related.....start --> <!-- TODO: replace jackson with fastjson -->
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-context</artifactId>
      <version>${spring.version}</version>
    </dependency>
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-aop</artifactId>
      <version>${spring.version}</version>
    </dependency>
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-webmvc</artifactId>
      <version>${spring.version}</version>
    </dependency>
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-web</artifactId>
      <version>${spring.version}</version>
    </dependency>
    <dependency>
      <groupId>javax.servlet</groupId>
      <artifactId>jstl</artifactId>
      <version>1.2</version>
    </dependency>
    <dependency>
      <groupId>commons-logging</groupId>
      <artifactId>commons-logging</artifactId>
      <version>1.1.3</version>
    </dependency>
    <dependency>
      <groupId>org.codehaus.jackson</groupId>
      <artifactId>jackson-mapper-asl</artifactId>
      <version>1.9.13</version>
    </dependency>
    <dependency>
      <groupId>com.fasterxml.jackson.core</groupId>
      <artifactId>jackson-annotations</artifactId>
      <version>2.6.1</version>
    </dependency>
    <dependency>
      <groupId>com.fasterxml.jackson.core</groupId>
      <artifactId>jackson-core</artifactId>
      <version>2.6.1</version>
    </dependency>
    <dependency>
      <groupId>com.fasterxml.jackson.core</groupId>
      <artifactId>jackson-databind</artifactId>
      <version>2.6.1</version>
    </dependency>
    <!-- spring mvc related.....end -->

    <!-- mybatis orm related.....start -->
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-orm</artifactId>
      <version>${spring.version}</version>
    </dependency>
    <dependency>
      <groupId>org.mybatis</groupId>
      <artifactId>mybatis-spring</artifactId>
      <version>1.2.3</version>
    </dependency>
    <dependency>
      <groupId>mysql</groupId>
      <artifactId>mysql-connector-java</artifactId>
      <version>5.1.36</version>
    </dependency>
    <dependency>
      <groupId>org.mybatis</groupId>
      <artifactId>mybatis</artifactId>
      <version>3.3.0</version>
    </dependency>
    <dependency>
      <groupId>c3p0</groupId>
      <artifactId>c3p0</artifactId>
      <version>0.9.1.2</version>
    </dependency>
    <dependency>
      <groupId>org.slf4j</groupId>
      <artifactId>slf4j-log4j12</artifactId>
      <version>1.7.12</version>
    </dependency>
    <!-- mybatis orm related.....end -->

    <!-- project log related.....start -->
    <dependency>
      <groupId>log4j</groupId>
      <artifactId>log4j</artifactId>
      <version>1.2.17</version>
    </dependency>
    <!-- project log related.....end -->

    <!-- redis cache related.....start -->
    <dependency>
      <groupId>org.springframework.data</groupId>
      <artifactId>spring-data-redis</artifactId>
      <version>1.6.0.RELEASE</version>
    </dependency>
    <dependency>
      <groupId>redis.clients</groupId>
      <artifactId>jedis</artifactId>
      <version>2.7.3</version>
    </dependency>
    <!-- redis cache related.....end -->

  </dependencies>

  <build>
    <finalName>redis-demo</finalName>
    <pluginManagement><!-- lock down plugins versions to avoid using Maven defaults (may be moved to parent pom) -->
      <plugins>
        <plugin>
          <artifactId>maven-clean-plugin</artifactId>
          <version>3.0.0</version>
        </plugin>
        <!-- see http://maven.apache.org/ref/current/maven-core/default-bindings.html#Plugin_bindings_for_war_packaging -->
        <plugin>
          <artifactId>maven-resources-plugin</artifactId>
          <version>3.0.2</version>
        </plugin>
        <plugin>
          <artifactId>maven-compiler-plugin</artifactId>
          <version>3.7.0</version>
        </plugin>
        <plugin>
          <artifactId>maven-surefire-plugin</artifactId>
          <version>2.20.1</version>
        </plugin>
        <plugin>
          <artifactId>maven-war-plugin</artifactId>
          <version>3.2.0</version>
        </plugin>
        <plugin>
          <artifactId>maven-install-plugin</artifactId>
          <version>2.5.2</version>
        </plugin>
        <plugin>
          <artifactId>maven-deploy-plugin</artifactId>
          <version>2.8.2</version>
        </plugin>
      </plugins>
    </pluginManagement>
  </build>
</project>
applicationContent.xml ssm、redis配置文件
<?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:tx="http://www.springframework.org/schema/tx"
       xmlns:cache="http://www.springframework.org/schema/cache"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.0.xsd
       http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-4.0.xsd
       http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.0.xsd
       http://www.springframework.org/schema/cache http://www.springframework.org/schema/cache/spring-cache.xsd">

    <!-- 扫描dao,service -->
    <context:component-scan base-package="com.wxx.jedis.service" />
    <context:component-scan base-package="com.wxx.jedis.service.*" />
    <context:component-scan base-package="com.wxx.jedis.redis" />
    <!-- 启用注解 -->
    <context:annotation-config/>
    <!-- 启动缓存注解 -->
    <cache:annotation-driven/>

    <bean class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
        <property name="locations">
            <list>
                <value>classpath:config/jdbc.properties</value>
                <value>classpath:config/redis.properties</value>
            </list>
        </property>
    </bean>

    <!-- MyBatis start -->
    <!-- 配置dataSource DriverManagerDataSource-->
    <bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
        <property name="driverClassName" value="com.mysql.jdbc.Driver" />
        <property name="url" value="jdbc:mysql://127.0.0.1:3306/test" />
        <property name="username" value="root" />
        <property name="password" value="5415143" />
    </bean>

    <!-- MyBatis配置 SqlSessionFactoryBean -->
    <bean id="sessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
        <property name="dataSource" ref="dataSource" />
        <property name="configLocation" value="classpath:config/mybatis.xml" />
        <property name="mapperLocations" value="classpath:mapper/UserMapper.xml" />
    </bean>

    <!-- mybatis自动扫描加载Sql映射文件/接口 : MapperScannerConfigurer
               sqlSessionFactory
            basePackage:指定sql映射文件/接口所在的包(自动扫描) -->
    <bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
        <property name="sqlSessionFactory" ref="sessionFactory" />
        <property name="basePackage" value="com.wxx.jedis.dao" />
    </bean>

    <!-- 事务管理  DataSourceTransactionManager-->
    <bean id="txManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
        <property name="dataSource" ref="dataSource" />
    </bean>

    <!-- 使用声明式事务 transaction-manager:引用上面定义的事务管理器-->
    <tx:annotation-driven transaction-manager="txManager" />

    <!-- MyBatis end -->

    <!-- 配置redis部分 start -->

    <!-- 配置redis连接池  JedisPoolConfig-->
    <bean id="poolConfig" class="redis.clients.jedis.JedisPoolConfig">
        <property name="maxIdle" value="${redis.maxIdle}" />
        <property name="maxTotal" value="${redis.maxActive}" />
    </bean>

    <!-- 配置CoonnectionFactory JedisConnectionFactory-->
    <bean id="connFactory" class="org.springframework.data.redis.connection.jedis.JedisConnectionFactory">
        <property name="hostName" value="${redis.host}" />
        <property name="port" value="${redis.port}" />
        <property name="poolConfig" ref="poolConfig" />
    </bean>

    <!-- 配置redisTemplate StringRedisTemplate-->
    <bean id="redisTemplate" class="org.springframework.data.redis.core.StringRedisTemplate">
        <property name="connectionFactory" ref="connFactory"/>
    </bean>

    <bean id="cacheManager" class="org.springframework.cache.support.SimpleCacheManager">
        <property name="caches">
            <set>
                <bean class="com.wxx.jedis.redis.RedisCacheConfig">
                    <property name="redisTemplate" ref="redisTemplate" />
                    <property name="name" value="User"/>
                    <!-- User名称要在类或方法的注解中使用 -->
                </bean>
            </set>
        </property>
    </bean>

</beans>
mybatis.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>

    <!-- 实体类,简称 -设置别名 -->
    <typeAliases>
        <typeAlias alias="User" type="com.wxx.jedis.domain.User" />
    </typeAliases>

</configuration>
springMVC.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-4.0.xsd
       http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.0.xsd
       http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-4.0.xsd">

    <mvc:annotation-driven/>
    <!-- 启用spring mvc 注解 -->
    <context:annotation-config/>
    <!-- 设置使用注解的类所在的jar包 -->
    <context:component-scan base-package="com.wxx.jedis.controller" />

    <!-- 对模型视图名称的解析,即在模型视图名称添加前后缀 -->
    <bean id="viewResolver" class="org.springframework.web.servlet.view.InternalResourceViewResolver">
        <property name="viewClass" value="org.springframework.web.servlet.view.JstlView"/>
        <property name="prefix" value="/views/"/>
        <property name="suffix" value=".jsp"/>
    </bean>

    <bean class="org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter">
        <!-- JSON转换器 -->
        <property name="messageConverters">
            <list>
                <bean class="org.springframework.http.converter.json.MappingJackson2HttpMessageConverter">
                    <property name="supportedMediaTypes">
                        <list>
                            <value>application/json;charset=utf-8</value>
                            <value>text/json;charset=utf-8</value>
                        </list>
                    </property>
                </bean>
            </list>
        </property>
    </bean>

</beans>
log4j.properties
# Set root category priority to INFO and its only appender to CONSOLE.
log4j.rootCategory=ALL, CONSOLE
#log4j.rootCategory=INFO, CONSOLE, LOGFILE

# CONSOLE is set to be a ConsoleAppender using a PatternLayout.
log4j.appender.CONSOLE=org.apache.log4j.ConsoleAppender
log4j.appender.CONSOLE.Threshold=DEBUG
log4j.appender.CONSOLE.layout=org.apache.log4j.PatternLayout
log4j.appender.CONSOLE.layout.ConversionPattern=%d{yyyy-MM-dd HH\:mm\:ss} %p - %m%n

log4j.logger.java.sql.Connection=DEBUG
log4j.logger.java.sql.Statement=DEBUG
log4j.logger.java.sql.PreparedStatement=DEBUG
log4j.logger.java.sql.ResultSet=DEBUG

redis.properties
# Redis settings
redis.host=127.0.0.1
redis.port=6379
redis.dbIndex=0
redis.expiration=3000
redis.maxIdle=300
redis.maxActive=600
redis.maxWait=1000
redis.testOnBorrow=true
User.java 实体类,必须要实现Serializable接口,因为这个坑,纠结了好久,一直抛java.io.StreamCorruptedException异常
package com.wxx.jedis.domain;

import java.io.Serializable;

public class User implements Serializable{

    private static final long serialVersionUID = 1L;

    private Long id;
    private String username;
    private String password;
    private int age;

    public Long getId() {
        return id;
    }

    public void setId(Long id) {
        this.id = id;
    }

    public String getUsername() {
        return username;
    }

    public void setUsername(String username) {
        this.username = username;
    }

    public String getPassword() {
        return password;
    }

    public void setPassword(String password) {
        this.password = password;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

}
userMapper.xml 写sql的配置文件
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd" >
<mapper namespace="com.wxx.jedis.dao.UserMapper" >
    <resultMap id="userResult" type="User">
        <result column="id" property="id"/>
        <result column="username" property="username"/>
        <result column="password" property="password"/>
        <result column="age" property="age"/>
    </resultMap>

    <insert id="addUser" parameterType="User">
        insert into t_user(username,password,age) values(#{username},#{password},#{age})
    </insert>
    <select id="getUser" parameterType="java.lang.Integer" resultMap="userResult" >
        select * from t_user where 1 = 1
        <if test="username != null and username != ''">
            AND username = #{username}
        </if>
    </select>
</mapper>
userMapper.java 数据库接口
package com.wxx.jedis.dao;

import com.wxx.jedis.domain.User;
import org.apache.ibatis.annotations.Param;

import java.util.List;

public interface UserMapper {

    void addUser(User user);

    List<User> getUser(@Param("username") String username);

}
UserService.java 服务类
package com.wxx.jedis.service;

import com.wxx.jedis.domain.User;
import java.util.List;

public interface UserService {

    void addUser(User user);

    List<User> getUser(String username);

}
UserServiceImpl.java 服务实现类,关键的redis缓存就是在这个类里面执行的
package com.wxx.jedis.service.impl;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;

import com.wxx.jedis.dao.UserMapper;
import com.wxx.jedis.service.UserService;
import com.wxx.jedis.domain.User;

import java.util.List;

@Service("userService")
public class UserServiceImpl implements UserService{

    @Autowired
    private UserMapper userMapper;

    /**
     * 新增数据直接进入缓存
     *
     * */
    @Override
    @Cacheable(value = "User",key = "'addUser'")
    public void addUser(User user){
        userMapper.addUser(user);
    }

    /**
     * 第一次查询会从数据库取数据,随后进入redis缓存
     * 这里的value的我们在xml文件里面定义的name
     *
     * */
    @Override
    @Cacheable(value="User",key = "'getUser'")
    public List<User> getUser(String username) {
        return userMapper.getUser(username);
    }

}

UserController.java 控制层
package com.wxx.jedis.controller;

import java.util.List;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;

import com.wxx.jedis.service.UserService;
import com.wxx.jedis.domain.User;

@Controller
@RequestMapping("/userCtrl")
public class UserController {

    @Autowired
    private UserService userService;

    @RequestMapping("/addUser")
    @ResponseBody
    public String addUser(User user){
        userService.addUser(user);
        return "success";
    }

    @ResponseBody
    @RequestMapping("/getUser")
    public List<User> getUser(String username){
        return userService.getUser(username);
    }

}
RedisCacheConfig.java applicationContent.xml文件里面可以找到次类,执行redis缓存的中间类,实现了Spring的缓存接口
package com.wxx.jedis.redis;
import java.io.*;

import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.cache.Cache;
import org.springframework.cache.support.SimpleValueWrapper;
import org.springframework.dao.DataAccessException;
import org.springframework.data.redis.connection.RedisConnection;
import org.springframework.data.redis.core.RedisCallback;
import org.springframework.data.redis.core.RedisTemplate;

public class RedisCacheConfig implements Cache{
    private final Logger logger = LoggerFactory.getLogger(getClass());

    private RedisTemplate<String, Object> redisTemplate;
    private String name;
    public RedisTemplate<String, Object> getRedisTemplate() {
        return redisTemplate;
    }

    public void setRedisTemplate(RedisTemplate<String, Object> redisTemplate) {
        this.redisTemplate = redisTemplate;
    }

    public void setName(String name) {
        this.name = name;
    }

    @Override
    public String getName() {
        return this.name;
    }

    @Override
    public Object getNativeCache() {
        return this.redisTemplate;
    }

    /**
     * 从缓存中获取key
     */
    @Override
    public ValueWrapper get(Object key) {
        logger.info("-----------------------------从redis缓存中读取数据-----------------------------");
        final String keyf =  key.toString();
        Object object;
        object = redisTemplate.execute((RedisCallback<Object>) connection -> {
            byte[] key1 = keyf.getBytes();
            byte[] value = connection.get(key1);
            if (value == null) {
                return null;
            }
            return toObject(value);
        });
        return (object != null ? new SimpleValueWrapper(object) : null);
    }

    @Override
    public <T> T get(Object key, Class<T> type){

        logger.info("-----------------------------从redis缓存中读取数据-----------------------------");
        final String keyf =  key.toString();
        T object;
        object = redisTemplate.execute((RedisCallback<T>) connection -> {
            byte[] key1 = keyf.getBytes();
            byte[] value = connection.get(key1);
            if (value == null) {
                return null;
            }
            return readToObject(value,type);
        });
        return object;
    }

    private ObjectMapper objectMapper = new ObjectMapper();

    private <T> byte[] writeToByte(T object) {
        try {
            return objectMapper.writeValueAsBytes(object);
        } catch (JsonProcessingException e) {
            e.printStackTrace();
            return null;
        }
    }

    private <T> T readToObject(byte[] bytes, Class<T> clazz) {
        try {
            return objectMapper.readValue(bytes, clazz);
        } catch (IOException e) {
            e.printStackTrace();
            return null;
        }
    }

    /**
     * 将一个新的key保存到缓存中
     * 先拿到需要缓存key名称和对象,然后将其转成ByteArray
     */
    @Override
    public void put(Object key, Object value) {
        logger.info("-----------------------------将数据放入redis缓存-----------------------------");
        final String keyf = key.toString();
        final Object valuef = value;
        final long liveTime = 86400;
        redisTemplate.execute((RedisCallback<Long>) connection -> {
            byte[] keyb = keyf.getBytes();
            byte[] valueb = toByteArray(valuef);
            connection.set(keyb, valueb);
            connection.expire(keyb, liveTime);
            return 1L;
        });
    }

    private byte[] toByteArray(Object obj) {
        byte[] bytes = null;
        ByteArrayOutputStream bos = new ByteArrayOutputStream();
        try {
            ObjectOutputStream oos = new ObjectOutputStream(bos);
            oos.writeObject(obj);
            oos.flush();
            bytes = bos.toByteArray();
            oos.close();
            bos.close();
        }catch (IOException ex) {
            ex.printStackTrace();
        }
        return bytes;
    }

    private Object toObject(byte[] bytes) {
        Object obj = null;
        try {
            ByteArrayInputStream bis = new ByteArrayInputStream(bytes);
            ObjectInputStream ois = new ObjectInputStream(bis);
            obj = ois.readObject();
            ois.close();
            bis.close();
        } catch (IOException | ClassNotFoundException ex) {
            ex.printStackTrace();
        }
        return obj;
    }

    /**
     * 删除key
     */
    @Override
    public void evict(Object key) {
        System.out.println("del key");
        final String keyf = key.toString();
        redisTemplate.execute((RedisCallback<Long>) connection -> connection.del(keyf.getBytes()));
    }

    /**
     * 清空key
     */
    @Override
    public void clear() {
        System.out.println("clear key");
        redisTemplate.execute((RedisCallback<String>) connection -> {
            connection.flushDb();
            return "ok";
        });
    }


    @Override
    public ValueWrapper putIfAbsent(Object key, Object value) {
        return null;
    }

}
index.jsp
<html>
<body>
<h2>
    <form action="${pageContext.request.contextPath}/userCtrl/getUser">
        <input type="submit" value="getUser">
    </form>
    <hr>
    <form action="${pageContext.request.contextPath}/userCtrl/addUser" method="post">
        username:<input type="text" name="username">
        password:<input type="password" name="password">
        age:<input type="text" name="age">
        <input type="submit" value="addUser">
    </form>
</h2>
</body>
</html>
最后,来测试一下把
以查询为例,第一次执行会从数据库读取



执行后,再次刷新当前页面


可以看到,没有再执行sql了,说明缓存起到了作用

最后,补充下注解:

@Cacheable 参数 ->value:必须,是我们配置在配置文件里面的那么,我这里配置了User。

                               key:非必须,指定此方法或者类的缓存的key

                               condition:触发缓存的条件,只有满足此条件才会返回缓存的结果

@CacheEvict 参数 ->value,key,condition同上

                                allEntries:默认false,只清除key的值,调用evict方法,为true时,清除所有缓存,忽略key,调用clear 方法。

一般@CacheEvict用在需要对数据库有写操作,恰巧此这部分数据又在缓存中的时候,更新缓存              

初学者,有错多包涵

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值