关于jfinal包slf4j包的学习
1. jfinal包
1.1 jfinal简介
公司的就项目使用的是JFinal框架,那么为了读懂并维护公司的项目,则需要对此框架进行学习、了解。
JFinal项目开源代码地址:https://gitee.com/jfinal/jfinal/tree/master
JFinal官网地址:https://jfinal.com/project/1
一个基于 Java 语言的极速 WEB + ORM + AOP + Template Engine 框架,其核心设计目标是开发迅速、代码量少、学习简单、功能强大、轻量级、易扩展、Restful。
JFinal 是基于 Java 语言的极速 WEB + ORM 框架,其核心设计目标是开发迅速、代码量少、学习简单、功能强大、轻量级、易扩展、Restful。在拥有Java语言所有优势的同时再拥有 ruby、python 等动态语言的开发效率!为您节约更多时间,去陪恋人、家人和朋友 ;)
1.2 JFinal包的如下特点
- MVC 架构,设计精巧,使用简单
- 遵循 COC 原则,支持零配置,无 XML
- 独创 Db + Record 模式,灵活便利
- ActiveRecord 支持,使数据库开发极致快速
- 极简、强大、高性能模板引擎 Enjoy,十分钟内掌握 90% 用法
- 自动加载修改后的 Java 文件,开发过程中无需重启服务
- AOP支持,拦截器配置灵活,功能强大
- Plugin 体系结构,扩展性强
- 多视图支持,支持 Enjoy、FreeMarker、JSP
- 强大的 Validator 后端校验功能
- 功能齐全,拥有传统 SSH 框架的绝大部分核心功能
- 体积小仅 832 KB,并且无第三方依赖
1.3 Maven 坐标
<dependency>
<groupId>com.jfinal</groupId>
<artifactId>jfinal</artifactId>
<version>5.0.6</version>
</dependency>
1.4 model类的简单使用
Model是ActiveRecord中最重要的组件之一,它充当MVC模式中的Model部分。以下是Model定义示例代码:
public class User extends Model<User> {
public static final User dao = new User().dao();
}
以下为Model的一些常见用法:
// 创建name属性为James,age属性为25的User对象并添加到数据库
new User().set("name", "James").set("age", 25).save();
// 删除id值为25的User
User.dao.deleteById(25);
// 查询id值为25的User将其name属性改为James并更新到数据库
User.dao.findById(25).set("name", "James").update();
// 查询id值为25的user, 且仅仅取name与age两个字段的值
User user = User.dao.findByIdLoadColumns(25, "name, age");
// 获取user的name属性
String userName = user.getStr("name");
// 获取user的age属性
Integer userAge = user.getInt("age");
// 查询所有年龄大于18岁的user
List<User> users = User.dao.find("select * from user where age>18");
// 分页查询年龄大于18的user,当前页号为1,每页10个user
Page<User> userPage = User.dao.paginate(1, 10, "select *", "from user where age > ?", 18);
1.5. Db类的简单使用
Jfinal提供了两种操作数据库的组件,分别是Model类和DB类,可以极大地减少代码量,提高开发效率。
Db类提供了在Model类之外更丰富的的数据库操作能力,使用Db类以及嵌套的Record类时,无需对数据库表进行映射,Record类相当于一个通用的Model。
以下为Db+Record的常见用法:
-
没有创建 Model,不得不使用
-
由于Active Record 限制一个model对象对应一条记录,当需要批量操作表中的记录时需要使用Db + Record,例如,需要对 account 表中的 cash 字段统一加上 100 块钱:Db.update("update account set cash=cash+100"),这种情况下 Model 只能一条一条地处理数据,而 Db 可以利用 sql 去批量处理
-
在某些非常需要高度灵活性、通用性的系统之下使用 Db.find(...),例如做一个报表系统,其中绝大部分业务都是跨多表的联合查询的数据展现,用 Db.find(sql) 更加方便,以及拥有更大自由度。比如:
1 List<A> list = A.dao.find("select A.a,B.b from A left join (select a,b from B) B on A.a = B.a")
这个时候如果需要对list中的数据进行处理,list.get(index).get("b")将会出错,因为list初始化是A类型的,而实体类A即数据库A中没有字段b。
这个时候采用Db类:
1 List<Record> list = Db.use(configname).find("select A.a,B.b from A left join (select a,b from B) B on A.a = B.a")
- 在一些删除、更新语句使用的时候,比如:
a. Model类在执行删除语句的时候可以使用deleteById()方法,该方法的使用需要数据库字段具有主键(当然,好的数据库设计每张表都具有一个主键)。如果没有主键,Model方法执行删除语句就无法进行了。当然我们可能会想到利用model.dao.find(sql)方法去执行delete操作,这样是不可以的。这个时候可以使用Db类的update方法:
1 Db.use("User").update("delete from tablename where para = ?", para);
"User"是数据库配置configname:
public static final String CONFIG_NAME = "User";
b.批量插入、更新、删除时,可以使用Db类的batch方法:
public static int[] batch(java.lang.String sql,
java.lang.String columns,
java.util.List modelOrRecordList,
int batchSize)
- sql 参数是指 insert into 或者 update 语句,动态参数使用问号占位,例如:String sql = "insert into user(name, cash) values(?, ?)";上面这个 sql 其中要插入 name、cash 两个字段,而 values 中用了两个问号,这两个问号中的值从后续的 modelOrRecordList 中获取
- columns 是指前面 sql 中问号占位的地方的参数名称,jfinal 在填充字段值的时候会根据这个名称去 modelOrRecordList 中去取数据
- modelOrRecordList 是指 List < User > 或者 List < Record> 或者这种列表
- batchSize 是指多少条数据写一次数据库
1 Db.use("User").batch("delete from tablename where para = ?", new Object[][] {{para}}, 1);
2. slf4j包
2.1 什么是LSF4J(简介)
官网:https://www.slf4j.org/manual.html
(1)简单日记门面(simple logging Facade for Java)SLF4J是为各种loging APIs提供一个简单统一的接口。
(2)slf4j并不是一种具体的日志系统,而是一个用户日志系统的facade。
(3)在部署时,选择不同的日志系统包,即可自动转换到不同的日志系统上。
如:选择JDK自带的日志系统,则只需要将slf4j-api-XXX.jar和slf4j-jdkXXX.jar放置到classpath中即可,若想换成log4j的日志系统,仅需要用slf4j-log4jXXx.jar替换slf4j-jdkXXX.jar即可
(4)slf4j和log4j比较:
①log4j看成是一个完整的日志库;而slf4j是一个日志库的规范接口,可以根据不同的日志包使用不同的日志库。
②日志中需要传入参数时,log4j一般是使用字符串进行拼接的方式;
slf4j使用占位符,比字符串拼接更加高效。如logger.error("sql为 {} ",sql)。
(5)slf4j日志级别
Slf4j日志级别,级别由低到高,设置的级别约低,打印的日志越多
①trace: 一般不会使用,在日志里边也不会打印出来,最低的一个日志级别。
②debug: 一般放于程序的某个关键点的地方,用于打印一个变量值或者一个方法返回的信息之类的信息
③info 一般处理业务逻辑的时候使用,就跟 system.err打印一样,用于说明此处是干什么的。
④warn:警告,不会影响程序的运行,但是值得注意。
⑤error: 用户程序报错,必须解决的时候使用此级别打印日志。
2.2 使用教程
-
添加依赖
<!--slf4j依赖--> <dependency> <groupId>org.slf4j</groupId> <artifactId>slf4j-api</artifactId> <version>1.7.32</version> </dependency> <!-- logback 依赖 --> <dependency> <groupId>ch.qos.logback</groupId> <artifactId>logback-classic</artifactId> <version>1.2.3</version> </dependency>
-
在springBoot项目中添加一段controller代码
package com.example.springb_web.controller; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.ResponseBody; @Controller public class VisualController { private Logger logger = LoggerFactory.getLogger(VisualController.class); @RequestMapping(value="/test") @ResponseBody public String test(){ String msg = "fucking good"; logger.info("slf4j print info msg:{}",msg); logger.debug("slf4j print debug msg:{}",msg); return msg; } }
这里通过http://localhost:8080/test访问可以看到控制台输出如下,可以看到info的输出了,debug没有,因为springboot内部集成了slf4j,默认是info级别:
-
只在控制台打印,application.yml文件配置如下:
# slf4j日志配置 logging: # 配置级别 level: root: info #分包配置级别,即不同的目录下可以使用不同的级别 com.example.springb_web.controller: debug
-
将日志打到文件中
logging: file: D://log.log #输出到文件 level: root: info
-
当需要什么级别输出到什么地方时。要加上xml配置。
#yml配置 logging: config: classpath:log/logback-spring.xml path: D:/log level: root: info
-
xml配置,取名logback-spring.xml
<?xml version="1.0" encoding="UTF-8"?> <!-- 日志级别从低到高分为TRACE < DEBUG < INFO < WARN < ERROR < FATAL,如果设置为WARN,则低于WARN的信息都不会输出 --> <!-- scan:当此属性设置为true时,配置文件如果发生改变,将会被重新加载,默认值为true --> <!-- scanPeriod:设置监测配置文件是否有修改的时间间隔,如果没有给出时间单位,默认单位是毫秒。当scan为true时,此属性生效。默认的时间间隔为1分钟。 --> <!-- debug:当此属性设置为true时,将打印出logback内部日志信息,实时查看logback运行状态。默认值为false。 --> <configuration scan="true" scanPeriod="10 seconds"> <!--<include resource="org/springframework/boot/logging/logback/base.xml" />--> <contextName>logback</contextName> <!-- name的值是变量的名称,value的值时变量定义的值。通过定义的值会被插入到logger上下文中。定义变量后,可以使“${}”来使用变量。 --> <property name="logging.file.path" value="D:/log" /> <property name="maxHistory" value="30" /> <property name="maxFileSize" value="100MB" /> <!-- 彩色日志(IDE下载插件才可以生效) --> <!-- 彩色日志依赖的渲染类 --> <conversionRule conversionWord="clr" converterClass="org.springframework.boot.logging.logback.ColorConverter" /> <conversionRule conversionWord="wex" converterClass="org.springframework.boot.logging.logback.WhitespaceThrowableProxyConverter" /> <conversionRule conversionWord="wEx" converterClass="org.springframework.boot.logging.logback.ExtendedWhitespaceThrowableProxyConverter" /> <!-- 彩色日志格式 --> <property name="CONSOLE_LOG_PATTERN" value="${CONSOLE_LOG_PATTERN:-%clr(%d{yyyy-MM-dd HH:mm:ss.SSS}){faint} %clr(${LOG_LEVEL_PATTERN:-%5p}) %clr(${PID:- }){magenta} %clr(---){faint} %clr([%15.15t]){faint} %clr(%-40.40logger{39}){cyan} %clr(:){faint} %m%n${LOG_EXCEPTION_CONVERSION_WORD:-%wEx}}"/> <!--输出到控制台--> <appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender"> <!--此日志appender是为开发使用,只配置最底级别,控制台输出的日志级别是大于或等于此级别的日志信息--> <filter class="ch.qos.logback.classic.filter.ThresholdFilter"> <level>info</level> </filter> <encoder> <Pattern>${CONSOLE_LOG_PATTERN}</Pattern> <!-- 设置字符集 --> <charset>UTF-8</charset> </encoder> </appender> <!--输出到文件--> <!-- 时间滚动输出 level为 DEBUG 日志 --> <appender name="DEBUG_FILE" class="ch.qos.logback.core.rolling.RollingFileAppender"> <!-- 正在记录的日志文件的路径及文件名 --> <!-- <file>${logging.file.path}/log_debug.log</file>--> <!--日志文件输出格式--> <encoder> <pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50} - %msg%n</pattern> <charset>UTF-8</charset> <!-- 设置字符集 --> </encoder> <!-- 日志记录器的滚动策略,按日期,按大小记录 --> <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy"> <!-- 日志归档 --> <fileNamePattern>${logging.file.path}/debug/log-debug-%d{yyyy-MM-dd}.%i.log</fileNamePattern> <timeBasedFileNamingAndTriggeringPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP"> <maxFileSize>${maxFileSize}</maxFileSize> </timeBasedFileNamingAndTriggeringPolicy> <!--日志文件保留天数--> <maxHistory>${maxHistory}</maxHistory> </rollingPolicy> <!-- 此日志文件只记录debug级别的 --> <filter class="ch.qos.logback.classic.filter.LevelFilter"> <level>debug</level> <onMatch>ACCEPT</onMatch> <onMismatch>DENY</onMismatch> </filter> </appender> <!-- 时间滚动输出 level为 INFO 日志 --> <appender name="INFO_FILE" class="ch.qos.logback.core.rolling.RollingFileAppender"> <!-- 正在记录的日志文件的路径及文件名 --> <!-- <file>${logging.file.path}/log_info.log</file>--> <!--日志文件输出格式--> <encoder> <pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50} - %msg%n</pattern> <charset>UTF-8</charset> </encoder> <!-- 日志记录器的滚动策略,按日期,按大小记录 --> <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy"> <!-- 每天日志归档路径以及格式 --> <fileNamePattern>${logging.file.path}/info/log-info-%d{yyyy-MM-dd}.%i.log</fileNamePattern> <timeBasedFileNamingAndTriggeringPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP"> <maxFileSize>${maxFileSize}</maxFileSize> </timeBasedFileNamingAndTriggeringPolicy> <!--日志文件保留天数--> <maxHistory>${maxHistory}</maxHistory> </rollingPolicy> <!-- 此日志文件只记录info级别的 --> <filter class="ch.qos.logback.classic.filter.LevelFilter"> <level>info</level> <onMatch>ACCEPT</onMatch> <onMismatch>DENY</onMismatch> </filter> </appender> <!-- 时间滚动输出 level为 WARN 日志 --> <appender name="WARN_FILE" class="ch.qos.logback.core.rolling.RollingFileAppender"> <!-- 正在记录的日志文件的路径及文件名 --> <!-- <file>${logging.file.path}/log_warn.log</file>--> <!--日志文件输出格式--> <encoder> <pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50} - %msg%n</pattern> <charset>UTF-8</charset> <!-- 此处设置字符集 --> </encoder> <!-- 日志记录器的滚动策略,按日期,按大小记录 --> <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy"> <fileNamePattern>${logging.file.path}/warn/log-warn-%d{yyyy-MM-dd}.%i.log</fileNamePattern> <timeBasedFileNamingAndTriggeringPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP"> <maxFileSize>${maxFileSize}</maxFileSize> </timeBasedFileNamingAndTriggeringPolicy> <!--日志文件保留天数--> <maxHistory>${maxHistory}</maxHistory> </rollingPolicy> <!-- 此日志文件只记录warn级别的 --> <filter class="ch.qos.logback.classic.filter.LevelFilter"> <level>warn</level> <onMatch>ACCEPT</onMatch> <onMismatch>DENY</onMismatch> </filter> </appender> <!-- 时间滚动输出 level为 ERROR 日志 --> <appender name="ERROR_FILE" class="ch.qos.logback.core.rolling.RollingFileAppender"> <!-- 正在记录的日志文件的路径及文件名 --> <!-- <file>${logging.file.path}/log_error.log</file>--> <!--日志文件输出格式--> <encoder> <pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50} - %msg%n</pattern> <charset>UTF-8</charset> <!-- 此处设置字符集 --> </encoder> <!-- 日志记录器的滚动策略,按日期,按大小记录 --> <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy"> <fileNamePattern>${logging.file.path}/error/log-error-%d{yyyy-MM-dd}.%i.log</fileNamePattern> <timeBasedFileNamingAndTriggeringPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP"> <maxFileSize>${maxFileSize}</maxFileSize> </timeBasedFileNamingAndTriggeringPolicy> <!--日志文件保留天数--> <maxHistory>${maxHistory}</maxHistory> </rollingPolicy> <!-- 此日志文件只记录ERROR级别的 --> <filter class="ch.qos.logback.classic.filter.LevelFilter"> <level>ERROR</level> <onMatch>ACCEPT</onMatch> <onMismatch>DENY</onMismatch> </filter> </appender> <!-- <logger>用来设置某一个包或者具体的某一个类的日志打印级别、 以及指定<appender>。<logger>仅有一个name属性, 一个可选的level和一个可选的addtivity属性。 name:用来指定受此logger约束的某一个包或者具体的某一个类。 level:用来设置打印级别,大小写无关:TRACE, DEBUG, INFO, WARN, ERROR, ALL 和 OFF, 还有一个特俗值INHERITED或者同义词NULL,代表强制执行上级的级别。 如果未设置此属性,那么当前logger将会继承上级的级别。 addtivity:是否向上级logger传递打印信息。默认是true。 --> <!--<logger name="org.springframework.web" level="info"/>--> <!--<logger name="org.springframework.scheduling.annotation.ScheduledAnnotationBeanPostProcessor" level="INFO"/>--> <!-- 使用mybatis的时候,sql语句是debug下才会打印,而这里我们只配置了info,所以想要查看sql语句的话,有以下两种操作: 第一种把<root level="info">改成<root level="DEBUG">这样就会打印sql,不过这样日志那边会出现很多其他消息 第二种就是单独给dao下目录配置debug模式,代码如下,这样配置sql语句会打印,其他还是正常info级别: --> <!-- root节点是必选节点,用来指定最基础的日志输出级别,只有一个level属性 level:用来设置打印级别,大小写无关:TRACE, DEBUG, INFO, WARN, ERROR, ALL 和 OFF, 不能设置为INHERITED或者同义词NULL。默认是DEBUG 可以包含零个或多个元素,标识这个appender将会添加到这个logger。 --> <!--开发环境:打印控制台 <springProfile name="dev"> <logger name="com.nmys.view" level="debug"/> </springProfile>--> <root level="info"> <appender-ref ref="CONSOLE" /> <appender-ref ref="DEBUG_FILE" /> <appender-ref ref="INFO_FILE" /> <appender-ref ref="WARN_FILE" /> <appender-ref ref="ERROR_FILE" /> </root> <!--生产环境:输出到文件--> <!--<springProfile name="pro">--> <!--<root level="info">--> <!--<appender-ref ref="CONSOLE" />--> <!--<appender-ref ref="DEBUG_FILE" />--> <!--<appender-ref ref="INFO_FILE" />--> <!--<appender-ref ref="ERROR_FILE" />--> <!--<appender-ref ref="WARN_FILE" />--> <!--</root>--> <!--</springProfile>--> </configuration>