SpringMVC最小依赖+Junit单元测试+log解析(log4j及log4j2)

1. junit

1.1 最小junit

<!--junit 测试-->
<dependency>
    <groupId>junit</groupId>
    <artifactId>junit</artifactId>
    <scope>test</scope>
</dependency>

测试的时候仅使用

@Test
method(){...}

直接引入junit即可。

说明:在spring中使用junit测试(如仅测试dao层),只需maven中加入两个依赖:

  • spring-test
  • junit

2. springMVC+Junit最小依赖

参考文献:http://blog.51cto.com/983836259/1864248

这里除了Spring以及其他模块所需要的jar包之外,还需要引入:

  • spring-test-4.2.3.RELEASE.jar
  • junit-4.10.jar

1.1 springmvc + junit

maven中如下即可(版本管理略):

<!--spring + springmvc 依赖-->

    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-core</artifactId>
        <exclusions>
            <exclusion>
                <groupId>commons-logging</groupId>
                <artifactId>commons-logging</artifactId>
            </exclusion>
        </exclusions>
    </dependency>

    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-context</artifactId>
    </dependency>

    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-test</artifactId>
    </dependency>

    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-webmvc</artifactId>
    </dependency>

    <dependency>
        <groupId>javax.servlet</groupId>
        <artifactId>javax.servlet-api</artifactId>
        <version>3.1.0</version>
    </dependency>
    <!-- junit单元测试 -->
    <dependency>
        <groupId>junit</groupId>
        <artifactId>junit</artifactId>
        <scope>test</scope>
    </dependency>

注意:上述排除了spring自带的common-logging,使用了log4j。关于log4j,需要桥接过来。

spring-core依赖的日志为common-logging,将其排除后,要使用common-logging接口,将其引入其他接口。

(1)对于jcl转log4j,有以下过程

Component
|
| log to Apache Commons Logging
V
(1)jcl-over-slf4j.jar — (redirect) —> SLF4j —>
(2)slf4j-log4j12-version.jar —>
(3)log4j.jar —> 输出日志

使用log4j依赖(1.x版本下示例)

<!-- 日志,属性文件放置可识别目录 -->
<!-- (1)jcl->slf4j   (2)slf4j->log4j   (3)log4j -->
<dependency>
    <groupId>org.slf4j</groupId>
    <artifactId>jcl-over-slf4j</artifactId>
    <version>1.7.25</version>
</dependency>
<dependency>
    <groupId>org.slf4j</groupId>
    <artifactId>slf4j-log4j12</artifactId>
    <version>1.7.10</version>
</dependency>
<dependency>
    <groupId>log4j</groupId>
    <artifactId>log4j</artifactId>
    <version>1.2.17</version>
</dependency>
<!--end log-->

测试代码如下:

@ContextConfiguration("classpath:spring-context.xml")
@RunWith(SpringJUnit4ClassRunner.class)
public class SomeTest {
    private static final Logger LOGGER = LoggerFactory.getLogger(XXX.class);//(1)

    @Resource
    private ***Service ***Service;

    @Test
    public void fun(){
        Set<String> set = new HashSet<String>();
        set.add("whb1");
        set.add("whb2");
        String g** = "gg";
        ...
        **Service.needTestfun(set, groupCode, g**,..);
        //在无返回时可以打印到logger
        LOGGER.info("--whb success--");
    }
}

总结:

@ContextConfiguration 基本spring配置文件路径
@RunWith(SpringJUnit4ClassRunner.class)此次使用的配置环境
@Test标注这是一个Test方法
Assert类可以用于断言,非空、相等等等。在无返回时也可以打印到log。

说明:

(1) LoggerFactory.getLogger

private static final Logger LOGGER = LoggerFactory.getLogger(com.XXX.class);

XXX.class用于绑定类名,在调试的时候会显示,如:
LOGGER.debug("日志信息");
会显示:
com...XXX: 日志信息

3. web项目配置

web.xml 中指出 spring配置文件,log配置文件(固定名称按序加载,略)放在资源目录下自动加载即可。
web.xml:

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
         version="4.0">
    <display-name>pro</display-name>
    <context-param>
        <param-name>webPro</param-name>
        <param-value>whbweb.root</param-value>
    </context-param>

    <!-- Spring MVC 转发Servlet -->
    <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:spring-context.xml
            </param-value>
        </init-param>
        <load-on-startup>1</load-on-startup>
    </servlet>

    <servlet-mapping>
        <servlet-name>springmvc</servlet-name>
        <url-pattern>/api/*</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>
        <servlet-name>springmvc</servlet-name>
    </filter-mapping>

</web-app>

注意:关于日志的属性文件不需要在初始化时配置。上述classpath处只写spring-context.xml即可。(由spring容器加载)

4. spring-context.xml 及 log4j.properties

均放在资源目录下即可。值得一提的是,log4j属性文件不需要额外指出,直接放在资源目录下即可!

(1)spring-context.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">

    <context:component-scan base-package="cn.whbing.pro.web.*"/>
    <mvc:annotation-driven enable-matrix-variables="true" />

</beans>

(2)log4j.properties(1.x版本以后不再介绍)

### 设置###
#log4j.rootLogger = debug,stdout,D,E
log4j.rootLogger = info,stdout,D,E
#log4j.rootLogger = error,stdout,D,E

### 输出信息到控制台 ###
log4j.appender.stdout = org.apache.log4j.ConsoleAppender
log4j.appender.stdout.Target = System.out
log4j.appender.stdout.layout = org.apache.log4j.PatternLayout
log4j.appender.stdout.layout.ConversionPattern = [%-5p] %d{yyyy-MM-dd HH:mm:ss,SSS} method:%l%n%m%n

### 输出DEBUG 级别以上的日志到=/home/**/logs/debug.log ###
### 相对目录是相对于整个项目而言的
log4j.appender.D = org.apache.log4j.DailyRollingFileAppender
log4j.appender.D.File = logs/debug.log
log4j.appender.D.Append = true
log4j.appender.D.Threshold = DEBUG
log4j.appender.D.layout = org.apache.log4j.PatternLayout
log4j.appender.D.layout.ConversionPattern = %-d{yyyy-MM-dd HH:mm:ss}  [ %t:%r ] - [ %p ]  %m%n

### 输出ERROR 级别以上的日志到=/home/admin/logs/error.log ###
log4j.appender.E = org.apache.log4j.DailyRollingFileAppender
log4j.appender.E.File =logs/error.log
log4j.appender.E.Append = true
log4j.appender.E.Threshold = ERROR
log4j.appender.E.layout = org.apache.log4j.PatternLayout
log4j.appender.E.layout.ConversionPattern = %-d{yyyy-MM-dd HH:mm:ss}  [ %t:%r ] - [ %p ]  %m%n

目录结构如下:(说明:log4j 1版本以log4j.properties名称即可)

src
 |-main
    |-java
    |-resources
         |- log4j.properties
    |-webapp
         |-WEB-INF
             |-web.xml

工具:

  • pom文件处,在idea中通过右键Diagrams查看依赖关系图如下:

    通过diagrams可以查看是否存在重复的包对一个接口实现等。

  • 在maven project中查看dependency可以查看包中的包含关系,以便排除多余的依赖,或者减少已经引入进来的依赖。如:spring-context依赖中实际上已经有了spring-core

上述log4j 1.x在spring中使用配置完毕。


【springweb+log4j中注意点】

web.xml中初始化classpath中不要写log配置文件路径!

log4j.xml等配置文件不用指明,自动识别。


5. log4j2详解

log4j与log4j2

1.x与2.x坐标和名称都有所变化
log4j:(不再维护)

 <dependency>
    <groupId>log4j</groupId>
     <artifactId>log4j</artifactId>
     <version>1.2.17</version>
 </dependency>

log42:(主要依赖有两个core与api)

<!-- log4j2 -->
<dependency>
    <groupId>org.apache.logging.log4j</groupId>
    <artifactId>log4j-core</artifactId>
    <version>2.5</version>
</dependency>

<dependency>
    <groupId>org.apache.logging.log4j</groupId>
    <artifactId>log4j-api</artifactId>
    <version>2.5</version>
</dependency>

关于slf4j、log4j、log4j2

由于java日志框架众多(common-logging,log4j,slf4j,logback等),引入jar包的时候,就要为其添加对应的日志实现。。

不同的jar包,可能用了不同的日志框架,那引用了之后就得给不同的日志框架添加配置,这个是比较麻烦的。

slf4j就是为了解决这个麻烦事的。

slf4j全称为Simple Logging Facade for JAVA,java简单日志门面。类似于Apache Common-Logging,是对不同日志框架提供的一个门面封装,可以在部署的时候不修改任何配置即可接入一种日志实现方案。但是,他在编译时静态绑定真正的Log库。使用SLF4J时,如果你需要使用某一种日志实现,那么你必须选择正确的SLF4J的jar包的集合(各种桥接包)。

这里写图片描述

【slj4j常用依赖】
jcl-over-slf4j.jar  -->  (jcl -> slf4j)     将Jakarta Commons Logging日志框架到 slf4j 的桥接

log4j-over-slf4j.jar-->  (log4j -> slf4j)   将log4j 的日志,桥接到slf4j,所以这个包不能和log4j-over-slf4j.jar同时用,否则会死循环!!  
slf4j-log4j12.jar   -->  (slf4j -> log4j)   slf4j 转接到 log4j,所以这个包不能和log4j-over-slf4j.jar同时用,否则会死循环!!  

slf4j-api.jar       -->                     slf4j 的api接口jar包  
slf4j-ext.jar       -->                     扩展功能  
slf4j-jcl.jar       -->  (slf4j -> jcl)     slf4j 转接到 Jakarta Commons Logging日志输出框架  

其他依赖包:
jul-to-slf4j.jar    -->  (juc  -> slf4j)    将java.util.logging的日志桥接到 slf4j  
osgi-over-slf4j.jar -->  (osgi   -> slf4j)  将osgi环境下的日志,桥接到slf4j  
slf4j-android.jar   -->  (android-> slf4j)  将android环境下的日志,桥接到slf4j  
slf4j-jdk14.jar     -->  (slf4j -> jul )    slf4j 转接到 java.util.logging,所以这个包不能和jul-to-slf4j.jar同时用,否则会死循环!!  

slf4j-migrator.jar  -->                     一个GUI工具,支持将项目代码中 JCL,log4j,java.util.logging的日志API转换为slf4j的写法  
slf4j-nop.jar       -->  (slf4j -> null)          slf4j的空接口输出绑定,丢弃所有日志输出  
slf4j-simple.jar    -->  (slf4j -> slf4j-simple ) slf4j的自带的简单日志输出接口  

说明:上述有部分包,如相互引用,将出现死循环。

【log4j2常用依赖】

一般还会有log4j和log4j2混用的问题。
因为log4j项目已经停止更新了,官方建议用log4j2。
log4j2里也提供了对各类log的桥接支持,这里就只列举相关的几个jar包说明。

log4j-1.2-api.jar   -->  (log4j -> log4j2)    将log4j 的日志转接到log4j2日志框架  
log4j-api.jar       -->                       log4j2的api接口jar包  
log4j-core.jar      -->  (log4j2 )            log4j2的日志输出核心jar包  

log4j-slf4j-impl.jar-->  (slf4j  -> log4j2)   slf4j 转接到 log4j2 的日志输出框架  (不能和 log4j-to-slf4j同时用)  
log4j-to-slf4j.jar  -->  (log4j2 -> slf4j)    将 log4j2的日志桥接到 slf4j  (不能和 log4j-slf4j-impl 同时用)  

【说明】

slf4j转log4j2时,slf4j包中提供了slf4j-log4j12.jar 将slf4j 转接到 log4j2;同时log4j包中也提供了log4j-slf4j-impl.jar将slf4j 转接到 log4j2。两个依赖不能同时出现,否则报重复bindings错误:

SLF4J: Class path contains multiple SLF4J bindings.
SLF4J: Found binding in [jar:file:/Users/didi/.m2/repository/org/apache/logging/log4j/log4j-slf4j-impl/2.5/log4j-slf4j-impl-2.5.jar!/org/slf4j/impl/StaticLoggerBinder.class]
SLF4J: Found binding in [jar:file:/Users/didi/.m2/repository/org/slf4j/slf4j-log4j12/1.7.10/slf4j-log4j12-1.7.10.jar!/org/slf4j/impl/StaticLoggerBinder.class]

因此在spring中排除掉common-logging后,jcl->slf4j->log4j2过程如下

        <!-- 日志,属性文件放置可识别目录 -->
        <!-- (1)jcl->slf4j   (2)slf4j->log4j   (3)log4j -->
        <dependency>
            <groupId>org.slf4j</groupId>
            <artifactId>jcl-over-slf4j</artifactId>
            <version>1.7.25</version>
        </dependency>
        <dependency>
            <groupId>org.slf4j</groupId>
            <artifactId>slf4j-api</artifactId>
            <version>${dependency.version.slf4j}</version>
        </dependency>

        <!--任选其一-->
        <dependency>
            <groupId>org.apache.logging.log4j</groupId>
            <artifactId>log4j-slf4j-impl</artifactId>
            <version>2.5</version>
        </dependency>
        <!--任选其一-->
        <!--<dependency>
            <groupId>org.slf4j</groupId>
            <artifactId>slf4j-log4j12</artifactId>
            <version>1.7.10</version>
        </dependency>-->

log4j2依赖:

<!-- log4j2 -->
        <dependency>
            <groupId>org.apache.logging.log4j</groupId>
            <artifactId>log4j-core</artifactId>
            <version>2.5</version>
        </dependency>

        <dependency>
            <groupId>org.apache.logging.log4j</groupId>
            <artifactId>log4j-api</artifactId>
            <version>2.5</version>
        </dependency>

        <!-- end log4j2 -->
【关于几种log包的总结】

【log4j】

这个最简单,单独使用jar包就一个,已经停止更新,当前最新版是 log4j-1.2.17.jar
只有输出功能,没有转接功能。

【log4j2】

同时有日志输出和转接功能。
单独使用时,jar包是 log4j-api-2.x.x.jar 和 log4j-core-2.x.x.jar
配置文件在资源目录下即可。

【log4j -> log4j2 桥接】

官网
去掉 log4j 1.x jar,添加log4j-1.2-api.jar,配合 log4j-api-2.x.x.jar 和 log4j-core-2.x.x.jar 即可,依赖如下

log4j-1.2-api.jar
    log4j-api-2.x.x.jar
        log4j-core-2.x.x.jar

【log4j2 -> log4j 桥接】
不建议。
本来log4j在2015年停止更新后,就建议转向log4j2,并提供了到log4j2的桥接接口。
所以反过来log4j2到log4j是不建议这么做的,log4j2也没有提供直接支持。
但理清了上面的jar包作用,就会发现,可以通过 log4j2 -> slf4j -> log4j 的方式来实现。

需要的jar包,根据依赖关系分别是:

log4j-api-2.x.x.jar
    log4j-to-slf4j.jar
        slf4j-api-x.x.x.jar
            slf4j-log4j12-x.x.x.jar
                log4j-1.2.17.jar

【slf4j】
同时有日志输出和转接功能。
核心jar包是 slf4j-api-x.x.x.jar
因为一般slf4j 只作为桥接用,如果要搭配 slf4j 自带的简单日志输出,那么就加上 slf4j-simple.jar

【log4j -> slf4j】
将代码中的log4j日志桥接到 slf4j,需要如下jar包

log4j-api-2.x.x.jar
    log4j-to-slf4j-2.x.x.jar
        slf4j-api-x.x.x.jar

【slf4j -> log4j】
将slf4j日志,采用log4j实现进行输出,需要如下jar包

slf4j-api-x.x.x.jar
    slf4j-log4j12.jar
        log4j-1.2.17.jar

【slf4j -> log4j2】
将slf4j日志,采用log4j2实现进行输出,需要如下jar包

slf4j-api-x.x.x.jar
    log4j-slf4j-impl.jar
        log4j-api.jar
            log4j-core.jar

slf4j的代理绑定和输出组合起来,就实现了从一种日志框架,转到另一种日志实现框架的效果。
建议在这三种日志混用的情况下,采用如下方案

log4j -> log4j2
slf4j -> log4j2

参考:jsl4j与log4j与log4j2那些事

配置文件结构:

src
 |-main
    |-java
    |-resources
         |- log4j2.xml
    |-webapp
         |-WEB-INF
             |-web.xml

使用:

import org.slf4j.LoggerFactory;
import org.slf4j.Logger;
private static final Logger LOGGER = LoggerFactory.getLogger(ReadSpecialLineServiceImpl.class);

log4j2.xml配置

xml方式log4j配置

可以通过以下四种方式配置log4j2

  • 通过一个格式为xml或json的配置文件
  • 以编程方式,通过创建一个ConfigurationFactory工厂和Configuration实现
  • 以编程方式,通过api暴露在配置界面添加组件的博人方式
  • 以编程方式,通过调用Logger内部类上的方法
    配置文件优先级及存放位置
    log4j2不在支持log2j1中的.properties后缀的文件配置方式。log4j2版本配置文件后缀只能为xml、json或jsn。

系统选择配置的文件的优先级如下:

  1. classpath下的名为log4j2-test.json或者log4j-test.jsn文件
  2. classpath下名为log4j2-test.xml的文件
  3. classpath下名为log4j2.json或者log4j2.jsn的文件
  4. classpath下名为log4j2.xml的文件

我们一般默认使用log4j2.xml进行命名。如果本地测试,可以先把log4j2-test.xml放到classpath,而正式环境使用log4j2.xml,则在打包部署的时候不要打包log4j2-test.xml

简单配置如下,详解参考:

log4j2配置详解

<?xml version="1.0" encoding="UTF-8"?>
<configuration status="info">
    <appenders>
        <Console name="Console" target="SYSTEM_OUT">
            <PatternLayout pattern="%d{HH:mm:ss.SSS} [%t] %-5level %logger{36} - %msg%n"/>
        </Console>
    </appenders>
    <loggers>
        <root level="error">
            <appender-ref ref="Console"/>
        </root>
    </loggers>
</configuration>

如下配置:

<?xml version="1.0" encoding="UTF-8"?>
<configuration status="off" monitorInterval="1800">
    <!--
    status : 这个用于设置log4j2自身内部的信息输出,可以不设置,当设置成trace时,会看到log4j2内部各种详细输出 ,默认OFF
    monitorInterval : Log4j能够自动检测修改配置文件和重新配置本身, 设置间隔秒数。

    %d{yyyy-MM-dd HH:mm:ss, SSS} : 日志生产时间
    %p : 日志输出格式
    %c : logger的名称
    %m : 日志内容,即 logger.info("message")
    %n : 换行符
    %C : Java类名
    %L : 日志输出所在行数
    %M : 日志输出所在方法名
    %t:表示当前线程名称
    %-5level:输出日志级别,-5表示左对齐并且固定输出5个字符
    %l:输出语句所在的包名、类名、函数名、行数
    hostName : 本地机器名 hostAddress : 本地ip地址

    <Async>属性用于是否异步,暂时忽略
    -->
    <Properties>
        <!-- 配置日志文件输出目录,此配置将日志输出到tomcat根目录下的指定文件夹 -->
        <!--<Property name="LOG_HOME">log4j2-test</Property>-->
        <Property name="LOG_HOME">log4j2-pro2</Property>
    </Properties>
    <appenders>

        <!--这个输出控制台的配置 -->
        <Console name="Console" target="SYSTEM_OUT">
            <!--控制台只输出level及以上级别的信息(onMatch),其他的直接拒绝(onMismatch)-->
            <ThresholdFilter level="info" onMatch="ACCEPT" onMismatch="DENY"/>
            <!--<PatternLayout pattern="%d{HH:mm:ss.SSS}  [%t] %-5level %logger{36} - %msg%n"/>-->
            <PatternLayout pattern="%d{DEFAULT}  [%t] %-5level %logger{36} - %msg%n"/>
        </Console>

        <!-- INFO级别日志 -->
        <RollingRandomAccessFile name="info_appender"
                                 immediateFlush="true" fileName="${LOG_HOME}/info/info.log"
                                 filePattern="${LOG_HOME}/info/info - %d{yyyy-MM-dd HH_mm_ss}.log.gz">
            <PatternLayout>
                <pattern>%5p [%t] %d{yyyy-MM-dd HH:mm:ss} (%F:%L) %m%n</pattern>
            </PatternLayout>
            <Policies>
                <!-- 日志压缩触发条件 -->
                <!-- 每次日志超过size配置的大小,按filePattern的配置的格式进行压缩-->
                <SizeBasedTriggeringPolicy size="25MB"/>
                <!-- 此处是按时间进行压缩 根据filePattern中的时间格式 每隔一个单位进行压缩 此处是按天压缩-->
                <TimeBasedTriggeringPolicy interval="1" modulate="true"/>

            </Policies>
            <Filters>
                <!--如果是warn级别拒绝-->
                <ThresholdFilter level="warn" onMatch="DENY" onMismatch="NEUTRAL"/>
                <ThresholdFilter level="info" onMatch="ACCEPT" onMismatch="DENY"/>
            </Filters>
        </RollingRandomAccessFile>

        <!-- ERROR级别日志 -->
        <RollingRandomAccessFile name="error_appender"
                                 immediateFlush="true" fileName="${LOG_HOME}/error/error.log"
                                 filePattern="${LOG_HOME}/error/error - %d{yyyy-MM-dd HH_mm_ss}.log.gz">
            <PatternLayout>
                <pattern>%5p [%t] %d{yyyy-MM-dd HH:mm:ss} (%F:%L) %m%n</pattern>
            </PatternLayout>
            <Policies>
                <!-- 日志压缩触发条件 -->
                <!-- 每次日志超过size配置的大小,按filePattern的配置的格式进行压缩-->
                <SizeBasedTriggeringPolicy size="25MB"/>
                <!-- 此处是按时间进行压缩 根据filePattern中的时间格式 每隔一个单位进行压缩 此处是按天压缩-->
                <TimeBasedTriggeringPolicy interval="1" modulate="true"/>

            </Policies>
            <Filters>
                <ThresholdFilter level="error" onMatch="ACCEPT" onMismatch="DENY"/>
            </Filters>
        </RollingRandomAccessFile>

    </appenders>

    <!--定义logger,只有定义了logger并引入的appender,appender才会生效 -->
    <loggers>
        <root level="info">
            <appender-ref ref="Console"/>
            <appender-ref ref="info_appender"/>
        </root>
    </loggers>
</configuration>
  • 1
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值