日志--门面及实现框架 自用解析
日志
概述
日志文件是用于记录系统操作时间的文件集合。可分为事件日志和消息日志。具有处理历史数据、诊断问题的追踪以及理解系统的活动等重要作用。
调试日志
开发中使用日志能够更加灵活和方便的去重现开发中的的bug问题。
系统日志
系统日志是记录系统中硬件、软件和系统问题的信息,还可以监视系统中发生的事件。可以通过它来检查错误发生的原因,或者寻找受到攻击时攻击者留下的痕迹。
java日志框架
日志门面 : JCL 、 slf4j
日志实现 : JUL 、 logback 、 log4j 、 log4j2
日志门面
借鉴于JDBC的思想,为日志系统提供一套门面,可以面向这些接口规范进行开发,避免直接依赖具体的日志框架。
用户通过控制日志门面提供的日志接口实现具体日志框架技术
SLF4J
1. 依赖
<!-- slf4j-core 使用slf4j门面核心 --><dependency><groupId>org.slf4j</groupId><artifactId>slf4j-api</artifactId><version>1.7.5</version></dependency><!-- slf4j 自带的简单日志实现 --><dependency><groupId>org.slf4j</groupId><artifactId>slf4j-simple</artifactId><version>1.7.30</version></dependency>
2. 测试代码
public class slf4jTest {public final static Logger LOGGER = LogerFactory.getLogger(slf4jTest.class);@Testpublic void test() throws Exception {LOGGER.error("error");LOGGER.warn("warn");LOGGER.info("info");LOGGER.debug("debug");LOGGER.trace("trace");//使用占位符输出日志 String name=cccq;Integer age=18;LOGGER.info("User:{},{}",name,age);}}
3. 绑定日志的实现
SLF4J附带几个SLF4J绑定的jar文件,每个绑定对应一个受支持的框架
- 绑定流程
- 添加slf4j-api的依赖
- 使用slf4j的API在项目中进行统一的日志记录
- 绑定具体的日志实现框架
- 绑定已经实现了slf4j的日志框架,直接添加对应依赖
- 绑定没有实现slf4j的日志框架,先添加日志的适配器,再添加实现类的依赖
slf4j有且仅有一个日志实现框架的绑定(如果出现多个默认使用第一个依赖日志实现)
依赖
<<!-- slf4j-core 使用slf4j门面核心 --> <dependency><groupId>org.slf4j</groupId><artifactId>slf4j-api</artifactId><version>1.7.5</version> </dependency><!-- log4j --> <dependency><groupId>org.slf4j</groupId><artifactId>slf4j-log4j12</artifactId><version>1.7.30</version> </dependency> <dependency><groupId>log4j</groupId><artifactId>log4j</artifactId><version>1.2.17</version> </dependency><!-- jul --> <dependency><groupId>org.slf4j</groupId><artifactId>slf4j-jdk14</artifactId><version>1.5.6</version> </dependency><!-- jcl --> <dependency><groupId>org.slf4j</groupId><artifactId>slf4j-jcl</artifactId><version>1.7.30</version> </dependency><!-- 不使用日志实现 --> <dependency><groupId>org.slf4j</groupId><artifactId>slf4j-nop</artifactId><version>1.7.30</version> </dependency>
要切换日志框架,只需替换类路径上的slf4j绑定。 例:从java.util.logging切换到log4j,只需将依赖 slf4j-jdk14 改为 slf4j-log4j12
4. 桥接旧的日志框架
从旧的其他日志实现升级到使用slf4j日志门面,slf4j附带了几个桥接模块,这些模块将对log4j,JCL和java.util.logging的调用重定向,就好像他们是对SLF4J一样
步骤 :
- 去除之前老的日志框架的依赖
- 添加SLF4J提供的桥接组件
- 为项目添加SLF4J的具体实现
依赖
<!-- log4j --> <dependency><groupId>org.slf4j</groupId><artifactId>log4j-over-slf4j</artifactId><version>1.7.30</version> </dependency><!-- jul --> <dependency><groupId>org.slf4j</groupId><artifactId>jul-to-slf4j</artifactId><version>1.5.6</version> </dependency><!-- jcl --> <dependency><groupId>org.slf4j</groupId><artifactId>jcl-over-slf4j</artifactId><version>1.7.30</version> </dependency>
注意问题:
- jcl-over-slf4j 和 slf4j-jcl 不能同时部署,前一个jar将导致jcl将日志系统的选择委托给SLF4J,后一个jar文件将导致SLF4J将日志系统的选择委托给JCL,从而导致死循环
- log4j-over-slf4j 和 slf4j-log4j12 不能同时出现
- jul-over-slf4j 和 slf4j-jdk14 不能同时出现
- 所有的桥接都只对Logger日志记录器对象有效,如果程序中调用了内部的配置类或者是Appender,Filter对象,将无法产生效果
5. 原理解析
- SLF4J通过LoggerFactory加载日志具体的实现对象
- LoggerFactory在初始化的过程中,会通过performInitialization()方法绑定具体的日志实现
- 在绑定具体实现的时候,通过类加载器,加载org/slf4j/impl/StaticLoggerBinder.class
- 所以,只要是一个日志实现框架,在org.slf4j.impl包中提供一个自己的StaticLoggerBinder类,在其中提供具体日志实现的LoggerFactory就可以呗SLF4J所加载
logback
由log4j的创始人设计的另一个开源日志组件,性能强于log4j
主要分为三个模块: 1. logback-core : 其他两个模块的基础模块 2. logback-classic : 是log4j的一个改良版本,同时它完整实现了slf4j API 3. logback-access : 访问模块与Servlet容器继承提供通过Http来访问日志功能
1. 依赖
<dependency><groupId>org.slf4j</groupId><artifactId>slf4j-api</artifactId><version>1.7.5</version></dependency><dependency><groupId>ch.qos.logback</groupId><artifactId>logback-core</artifactId><version>1.2.3</version></dependency><dependency><groupId>ch.qos.logback</groupId><artifactId>logback-classic</artifactId><version>1.2.3</version></dependency>
2. 配置
logback会依次读取以下类型配置文件
- logback.groovy
- logback-test.xml
- logback.xml
如果均不存在会采取默认配置
logback组件之间的关系
- Logger:日志的记录器,把它关联到应用的对应的context上后,主要用于存放日志对象,也可以定义日志类型、级别。
- Appender:用于指定日志输出的目的地,目的地可以是控制台、文件、数据库等等。
- Layout:负责把事件转换成字符串,格式化的日志信息的输出。在logback中Layout对象被封装在encoder中。
基本配置信息
<?xml version="1.0" encoding="UTF-8"?> <configuration><!--日志输出格式: %-5level 级别从左显示5个字符宽度 %d{yyyy-MM-dd HH:mm:ss.SSS} 日期 %c 类的完整名称 %M 为method %L 为行号 %thread 线程名称%m或者%msg 为信息 %n 换行 --> <property name="pattern" value="%d{yyyy-MM-dd HH:mm:ss.SSS} %c [%thread] %-5level %msg%n"/><!--Appender: 设置日志信息的去向,常用的有以下几个 ch.qos.logback.core.ConsoleAppender (控制台)ch.qos.logback.core.rolling.RollingFileAppender (文件大小到达指定尺 寸的时候产生一个新文件) ch.qos.logback.core.FileAppender (文件) --><appender name="console" class="ch.qos.logback.core.ConsoleAppender"><!--输出流对象形式 默认 System.out 改为 System.err--><target>System.err</target><!--日志格式配置--><encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder"><pattern>${pattern}</pattern></encoder></appender><!--用来设置某一个包或者具体的某一个类的日志打印级别、以及指定<appender>。<loger>仅有一个name属性,一个可选的level和一个可选的addtivity属性name:用来指定受此logger约束的某一个包或者具体的某一个类level:用来设置打印级别,大小写无关:TRACE, DEBUG, INFO, WARN, ERROR, ALL 和 OFF如果未设置此属性,那么当前logger将会继承上级的级别。additivity: 是否向上级loger传递打印信息。默认是true。<logger>可以包含零个或多个<appender-ref>元素,标识这个appender将会添加到这个 logger--><!--也是<logger>元素,但是它是根logger。默认debuglevel:用来设置打印级别,大小写无关:TRACE, DEBUG, INFO, WARN, ERROR, ALL 和 OFF,<root>可以包含零个或多个<appender-ref>元素,标识这个appender将会添加到这个 logger--><root level="ALL"><appender-ref ref="console"/></root> </configuration>
FileAppender配置
在logback.xml
<!-- 日志文件存放目录 --> <property name="log_dir" value="d:/logs"></property><!--日志文件输出appender对象--> <appender name="file" class="ch.qos.logback.core.FileAppender"><!--日志格式配置--><encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder"><pattern>${pattern}</pattern></encoder><!--日志输出路径--><file>${log_dir}/logback.log</file> </appender><!-- 生成html格式appender对象 --> <appender name="htmlFile" class="ch.qos.logback.core.FileAppender"><!--日志格式配置--><encoder class="ch.qos.logback.core.encoder.LayoutWrappingEncoder"><layout class="ch.qos.logback.classic.html.HTMLLayout"><pattern>%level%d{yyyy-MM-dd HH:mm:ss}%c%M%L%thread%m</pattern></layout></encoder><!--日志输出路径--><file>${log_dir}/logback.html</file> </appender><!--RootLogger对象--> <root level="all"> <appender-ref ref="console"/> <appender-ref ref="file"/> <appender-ref ref="htmlFile"/> </root>
RollingFileAppender配置
在logback.xml
<property name="pattern" value="%d{yyyy-MM-dd HH:mm:ss.SSS} %c [%thread] %-5level %msg%n"/><!-- 日志文件存放目录 --> <property name="log_dir" value="d:/logs"></property> <!--控制台输出appender对象--> <appender name="console" class="ch.qos.logback.core.ConsoleAppender"> <!--输出流对象 默认 System.out 改为 System.err--> <target>System.err</target> <!--日志格式配置--> <encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder"> <pattern>${pattern}</pattern> </encoder> </appender><!-- 日志文件拆分和归档的appender对象--> <appender name="rollFile" class="ch.qos.logback.core.rolling.RollingFileAppender"> <!--日志格式配置--> <encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder"> <pattern>${pattern}</pattern> </encoder> <!--日志输出路径--> <file>${log_dir}/roll_logback.log</file> <!--指定日志文件拆分和压缩规则--> <rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy"> <!--通过指定压缩文件名称,来确定分割文件方式--> <fileNamePattern>${log_dir}/rolling.%d{yyyy-MM- dd}.log%i.gz</fileNamePattern> <!--文件拆分大小--> <maxFileSize>1MB</maxFileSize> </rollingPolicy> </appender><!--RootLogger对象--> <root level="all"><appender-ref ref="console"/><appender-ref ref="rollFile"/> </root>
Filter和异步日志配置
在logback.xml
<!--异步日志--> <appender name="async" class="ch.qos.logback.classic.AsyncAppender"> <appender-ref ref="rollFile"/> </appender> <!--RootLogger对象--> <root level="all"> <appender-ref ref="console"/> <appender-ref ref="async"/> </root> <!--自定义logger additivity表示是否从 rootLogger继承配置--> <logger name="com.itheima" level="debug" additivity="false"> <appender-ref ref="console"/> </logger>
官方提供的log4j.properties转换成logback.xml
https://logback.qos.ch/translator/
3. logback-access的使用
logback-access模块与Servlet容器(如Tomcat和Jetty)集成,以提供HTTP访问日志功能。我们可以使 用logback-access模块来替换tomcat的访问日志。 1. 将logback-access.jar与logback-core.jar复制到$TOMCATHOME/lib/目录下 2. 修改$TOMCATHOME/conf/server.xml中的Host元素中添加:
<Valve className="ch.qos.logback.access.tomcat.LogbackValve" />
logback默认会在$TOMCAT_HOME/conf下查找文件 logback-access.xml
<?xml version="1.0" encoding="UTF-8"?> <configuration> <!-- always a good activate OnConsoleStatusListener --> <statusListener class="ch.qos.logback.core.status.OnConsoleStatusListener"/> <property name="LOG_DIR" value="${catalina.base}/logs"/> <appender name="FILE" class="ch.qos.logback.core.rolling.RollingFileAppender"> <file>${LOG_DIR}/access.log</file> <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy"> <fileNamePattern>access.%d{yyyy-MM-dd}.log.zip</fileNamePattern> </rollingPolicy><encoder> <!-- 访问日志的格式 --> <pattern>combined</pattern> </encoder> </appender> <appender-ref ref="FILE"/> </configuration>
官方配置: https://logback.qos.ch/access.html#configuration
log4g2
Apache Log4j 2是对Log4j的升级版,参考了logback的一些优秀的设计,并且修复了一些问题
提升 :
- 异常处理,在logback中,Appender中的异常不会被应用感知到,但是在log4j2中,提供了一些异常处理机制。
- 性能提升, log4j2相较于log4j 和logback都具有很明显的性能提升,后面会有官方测试的数据。
- 自动重载配置,参考了logback的设计,当然会提供自动刷新参数配置,最实用的就是我们在生产上可以动态的修改日志的级别而不需要重启应用。
- 无垃圾机制,log4j2在大部分情况下,都可以使用其设计的一套无垃圾机制,避免频繁的日志收集 导致的jvm gc。
1. 入门
依赖
<!-- Log4j2 门面API --> <dependency><groupId>org.apache.logging.log4j</groupId><artifactId>log4j-api</artifactId><version>2.13.0</version> </dependency> <!-- Log4j2 日志实现 --> <dependency><groupId>org.apache.logging.log4j</groupId><artifactId>log4j-core</artifactId><version>2.13.0</version> </dependency>
测试
public class Log4j2Test {// 定义日志记录器对象public static final Logger LOGGER = LogManager.getLogger(Log4j2Test.class);@Test public void testQuick() throws Exception { LOGGER.fatal("fatal"); LOGGER.error("error"); LOGGER.warn("warn"); LOGGER.info("info"); LOGGER.debug("debug"); LOGGER.trace("trace"); }} }
使用slf4j作为门面,log4j2作为实现
<!-- Log4j2 门面API --> <dependency><groupId>org.apache.logging.log4j</groupId><artifactId>log4j-api</artifactId><version>2.13.0</version> </dependency> <!-- Log4j2 日志实现 --> <dependency><groupId>org.apache.logging.log4j</groupId><artifactId>log4j-core</artifactId><version>2.13.0</version> </dependency><!-- 使用slf4j作为日志的门面,使用log4j2来记录日志 --> <dependency><groupId>org.slf4j</groupId><artifactId>slf4j-api</artifactId><version>1.7.5</version> </dependency> <!--为slf4j绑定日志实现 log4j2的适配器 --> <dependency><groupId>org.apache.logging.log4j</groupId><artifactId>log4j-slf4j-impl</artifactId><version>2.13.0</version> </dependency>
2. 配置
log4j2默认加载classpath下的 log4j2.xml 文件中的配置。
<?xml version="1.0" encoding="UTF-8"?>
<!--status="warn" 日志框架本身的输出日志级别monitorInterval="5" 自动加载配置文件的间隔时间,不低于 5 秒,实现热更新的效果
-->
<Configuration status="warn" monitorInterval="5"><!--集中配置属性进行管理使用时通过:${name}--><properties><property name="LOG_HOME">D:/logs</property></properties><!--日志处理--><Appenders><!--控制台输出 appender--><Console name="Console" target="SYSTEM_OUT"><PatternLayout pattern="%d{HH:mm:ss.SSS} [%t] [%-5level] %c{36}:%L - -- %m%n" /></Console><!--日志文件输出 appender--><File name="file" fileName="${LOG_HOME}/myfile.log"><PatternLayout pattern="[%d{yyyy-MM-dd HH:mm:ss.SSS}] [%-5level] %l %c{36} - %m%n" /></File><!--使用随机读写流的日志文件输出 appender,性能提高--><RandomAccessFile name="accessFile" fileName="${LOG_HOME}/myAcclog.log"> <PatternLayout pattern="[%d{yyyy-MM-dd HH:mm:ss.SSS}] [%-5level] %l %c{36} - %m%n" /></RandomAccessFile><!--按照一定规则拆分的日志文件的 appender--><RollingFile name="rollingFile" fileName="${LOG_HOME}/myrollog.log" filePattern="D:/logs/$${date:yyyy-MM-dd}/myrollog-%d{yyyy- MM-dd-HH-mm}-%i.log"><!--日志级别过滤器--><ThresholdFilter level="debug" onMatch="ACCEPT" onMismatch="DENY" /><!--日志消息格式--><PatternLayout pattern="[%d{yyyy-MM-dd HH:mm:ss.SSS}] [%-5level] %l %c{36} - %msg%n" /><Policies><!--在系统启动时,出发拆分规则,生产一个新的日志文件--><OnStartupTriggeringPolicy /><!--按照文件大小拆分,10MB --><SizeBasedTriggeringPolicy size="10 MB" /><!--按照时间节点拆分,规则根据filePattern定义的--><TimeBasedTriggeringPolicy /></Policies><!--在同一个目录下,文件的个数限定为 30 个,超过进行覆盖--><DefaultRolloverStrategy max="30" /></RollingFile></Appenders><!--logger 定义--><Loggers> <!--使用 rootLogger 配置 日志级别 level="trace"--><Root level="trace"> <!--指定日志使用的处理器--><AppenderRef ref="Console" /> </Root> </Loggers>
</Configuration>
3.异步日志
log4j2最大的特点就是异步日志,其性能的提升主要也是从异步日志中受益,
Log4j2提供了两种实现日志的方式,一个是通过AsyncAppender,一个是通过AsyncLogger,分别对应前面的Appender组件和Logger组件。
依赖
<!-- 异步日志依赖 --> <dependency><groupId>com.lmax</groupId><artifactId>disruptor</artifactId><version>3.4.2</version> </dependency>
AsyncAppender方式
<?xml version="1.0" encoding="UTF-8"?> <Configuration status="warn"><properties> <property name="LOG_HOME">D:/logs</property> </properties> <Appenders> <!--日志文件输出 appender--><File name="file" fileName="${LOG_HOME}/myfile.log"> <PatternLayout> <Pattern>%d %p %c{1.} [%t] %m%n</Pattern> </PatternLayout> </File><Async name="Async"> <AppenderRef ref="file"/> </Async> </Appenders><Loggers><Root level="error"><!--使用异步 appender--><AppenderRef ref="Async"/></Root></Loggers> </Configuration
AsyncLogger方式
AsyncLogger 是log4j2的重点,也是官方推荐的异步方式。它可以使得调用Logger.log返回的更快。可以有两种选择:全局异步和混合异步。
全局异步
所有的日志都异步的记录,在配置文件上不用做任何改动,只需要添加一个log4j2.component.properties 配置文件;
Log4jContextSelector=org.apache.logging.log4j.core.async.AsyncLoggerContextSelector
混合异步
可以在应用中同时使用同步日志和异步日志,这使得日志的配置方式更加灵活。
<?xml version="1.0" encoding="UTF-8"?> <Configuration status="WARN"> <properties> <property name="LOG_HOME">D:/logs</property> </properties> <Appenders> <File name="file" fileName="${LOG_HOME}/myfile.log"> <PatternLayout> <Pattern>%d %p %c{1.} [%t] %m%n</Pattern> </PatternLayout> </File> <Async name="Async"> <AppenderRef ref="file"/> </Async> </Appenders> <Loggers> <AsyncLogger name="com.cccq" level="trace" includeLocation="false" additivity="false"> <AppenderRef ref="file"/> </AsyncLogger><Root level="info" includeLocation="true"> <AppenderRef ref="file"/> </Root> </Loggers> </Configuration>
如上配置: com.cccq 日志是异步的,root日志是同步的。
4.性能
Log4j2最厉害的地方在于异步输出日志时的性能表现
Log4j2有三种模式:
- 全局使用异步模式;
- 部分Logger采用异步模式;
- 异步Appender。
无垃圾记录:
垃圾收集暂停是延迟峰值的常见原因,并且对于许多系统而言,花费大量精力来控制这些暂停。
许多日志库(包括以前版本的Log4j)在稳态日志记录期间分配临时对象,如日志事件对象,字符串,字符数组,字节数组等。这会对垃圾收集器造成压力并增加GC暂停发生的频率。
从版本2.6开始,默认情况下Log4j以“无垃圾”模式运行,其中重用对象和缓冲区,并且尽可能不分配临时对象。还有一个“低垃圾”模式,它不是完全无垃圾,但不使用ThreadLocal字段。
Log4j 2.6中的无垃圾日志记录部分通过重用ThreadLocal字段中的对象来实现,部分通过在将文本转换为字节时重用缓冲区来实现。
日志--门面及实现框架 自用解析相关推荐
- Log4j2日志框架集成Slf4j日志门面
1.说明 本文介绍使用日志门面Slf4j打印日志, 底层日志实现使用Log4j2框架, 方便以后切换底层日志实现, Log4j2可以替换成Logback等. 2.依赖管理 在pom.xml依赖管理中导 ...
- java常用日志框架日志门面及实现 SLF4J 、Jboss-logging 、JCL、Log4j、Logback、Log4j2、JUL,springboot集成 log4j、log4j2
java常用日志框架日志门面SLF4J .Jboss-logging .JCL.Log4j及实现 Logback.Log4j2.JUL,springboot集成 log4j.log4j2 .logba ...
- Java日志框架日志门面介绍
文章目录 一.日志 二.常见日志框架 历史 各大框架介绍 JUL Log4j(1999-2015) Logback(2006-?) Log4j2 Logback与Log4j2对比 三.日志门面 什么是 ...
- Java各类日志门面(slf4j,commons-logging)和日志框架(log4j,logback)联系和区别
日志门面 1.Apache通用日志接口(commons-logging.jar) Apache Commons包中的一个,包含了日志功能,必须使用的jar包.这个包本身包含了一个Simple Logg ...
- 日志门面框架Slf4j
SLF4J的使用 简单日志门面(Simple Logging Facade For Java) SLF4J主要是为了给Java日志访问提供一套标准.规范 的API框架,其主要意义在于提供接口,具体的实 ...
- Java日志框架 -- 日志框架介绍、日志门面技术、JUL日志(JUL架构、JUL入门示例、JUL日志级别、JUL日志的配置文件)
1. 日志的概念 日志文件是用于记录系统操作事件的文件集合,可分为事件日志和消息日志.具有处理历史数据.诊断问题的追踪以及理解系统的活动等重要作用. 2. Java日志框架 问题: 控制日志输出的内容 ...
- Java日志框架 -- JCL日志门面(JCL概念介绍、JCL示例)
1. JCL 全称为Jakarta Commons Logging,是Apache提供的一个通用日志API.是日志门面的一种实现方式,另外一种日志门面的实现方式是Slf4j. 它是为 "所有 ...
- 日志门面和日志框架(日志实现框架log4j2)
一.log4j2简介 Apache Log4j 2是对Log4j的升级,是最优秀的java日志框架. 二.log4j2特征 性能提升:Log4j2包含基于LMAX Disruptor库的下一代异步记录 ...
- 日志门面和日志框架(SpringBoot日志实现)
一.Springboot日志实现简介 SpringBoot是现今市场上最火爆用来简化spring开发的框架,springboot日志也是开发常用的日志系统.SpringBoot默认就是使用SLF4J作 ...
最新文章
- PowerShell攻防进阶篇:nishang工具用法详解
- 神奇的用法_续行符——反斜杠
- 2017年最新基于Bootstrap 4 的专业、多用途响应式布局的系统模板
- [转载] [SQL] patindex 详解
- 软件技术毕业论文编程方向
- 联想智能云教室安win7_用“联想智能云教室”部署系统完成后,操作卡顿?
- es服务器的cpu压力过大的调试
- 微软雅黑字体的设计理念
- [Err] 1267 - Illegal mix of collations (utf8_unicode_ci,IMPLICIT) and (utf8_general_ci,IMPLICIT)
- 使用POI 删除批注
- JAVA小游戏(国王和大臣)
- 原装linux系统装win7系统,网购本自带Linux系统,要重装win7旗舰要到店里去吗?
- 手把手教你ECSHOP去版权与标志
- ARM A35 A53
- 软件构造lab2 - 实验报告
- Python+大数据-数据分析与处理(六)-综合案例
- MAXScript脚本用于建筑可视化
- google app engine 部署 proxy
- 获取Android 设备的WIFI IPv4地址
- 2022-1-25 牛客C++ 项目 —— sigaction 函数
热门文章
- 互联网早报:腾讯小微推新功能,支持在微信中进行硬件管理和音乐分享....
- pygame基本框架
- java中OOD_Java面向对象OOD
- 企业电子名片小程序哪家?市面上哪一款名片小程序更好用?
- 这几个SpringBoot前后端分离项目(附源码),改改就能换钱。。。
- 【人教版】心田花开:七年级语文上册期末复习资料重点
- 一年级语文学习资料17.12.20
- 生产者消费者问题-代码详解(Java多线程)
- python什么工作好找女朋友_Python不能帮你找到女朋友,却能让你成为有钱的单身狗。...
- react的SPA实践