1.SLF4J(Simple logging Facade for Java) 意思为简单日志门面,它是把不同的日志系统的实现进行了具体的抽象化,只提供了统一的日志使用接口,使用时只需要按照其提供的接口方法进行调用即可,由于它只是一个接口,并不是一个具体的可以直接单独使用的日志框架,所以最终日志的格式、记录级别、输出方式等都要通过接口绑定的具体的日志系统来实现,这些具体的日志系统就有log4j,logback,java.util.logging等,它们才实现了具体的日志系统的功能。 如何使用SLF4J? 既然SLF4J只是一个接口,那么实际使用时必须要结合具体的日志系统来使用,我们首先来看SLF4J和各个具体的日志系统进行绑定时的框架原理图: 其实slf4j原理很简单,他只提供一个核心slf4j api(就是slf4j-api.jar包),这个包只有日志的接口,并没有实现,所以如果要使用就得再给它提供一个实现了些接口的日志包,比 如:log4j,common logging,jdk log日志实现包等,但是这些日志实现又不能通过接口直接调用,实现上他们根本就和slf4j-api不一致,因此slf4j又增加了一层来转换各日志实现包的使 用,当然slf4j-simple除外。其结构如下: slf4j-api(接口层) | 各日志实现包的连接层( slf4j-jdk14, slf4j-log4j) | 各日志实现包 所以,结合各日志实现包使用时提供的jar包情况为: SLF4J和logback结合使用时需要提供的jar:slf4j-api.jar,logback-classic.jar,logback-core.jar SLF4J和log4j结合使用时需要提供的jar:slf4j-api.jar,slf4j-log412.jar,log4j.jar SLF4J和JDK中java.util.logging结合使用时需要提供的jar:slf4j-api.jar,slf4j-jdk14.jar SLF4J和simple(SLF4J本身提供的一个接口的简单实现)结合使用时需要提供的jar:slf4j-api.jar,slf4j-simple.jar 当然还有其他的日志实现包,以上是经常会使用到的一些。 注意,以上slf4j和各日志实现包结合使用时最好只使用一种结合,不然的话会提示重复绑定日志,并且会导致日志无法输出。 slf4j-api.jar:对外提供统一的日志调用接口,该接口具体提供的调用方式和方法举例说明: public class Test { private static final Logger logger = LoggerFactory.getLogger(Tester.class); //通过LoggerFactory获取Logger实例 public static void main(String[] args) { //接口里的统一的调用方法,各具体的日志系统都有实现这些方法 logger.info("testlog: {}", "test"); logger.debug("testlog: {}", "test"); logger.error("testlog: {}", "test"); logger.trace("testlog: {}", "test"); logger.warn("testlog: {}", "test"); } } 如果系统中之前已经使用了log4j做日志输出,想使用slf4j作为统一的日志输出,该怎么办呢? 如果之前系统中是单独使用log4j做为日志输出的,这时再想使用slf4j做为日志输出时,如果系统中日志比较多,此时更改日志输出方法肯定是不太现实的,这个时候就可以使用log4j-over-slf4j.jar将使用log4j日志框架输出的日志路由到slf4j上来统一采用slf4j来输出日志。 为什么要使用SLF4J? slf4j是一个日志接口,自己没有具体实现日志系统,只提供了一组标准的调用api,这样将调用和具体的日志实现分离,使用slf4j后有利于根据自己实际的需求更换具体的日志系统,比如,之前使用的具体的日志系统为log4j,想更换为logback时,只需要删除log4j相关的jar,然后加入logback相关的jar和日志配置文件即可,而不需要改动具体的日志输出方法,试想如果没有采用这种方式,当你的系统中日志输出有成千上万条时,你要更换日志系统将是多么庞大的一项工程。如果你开发的是一个面向公众使用的组件或公共服务模块,那么一定要使用slf4的这种形式,这有利于别人在调用你的模块时保持和他系统中使用统一的日志输出。 slf4j日志输出时可以使用{}占位符,如,logger.info("testlog: {}", "test"),而如果只使用log4j做日志输出时,只能以logger.info("testlog:"+"test")这种形式,前者要比后者在性能上更好,后者采用+连接字符串时就是new 一个String 字符串,在性能上就不如前者。 2.log4j(log for java) Log4j是Apache的一个开源项目,通过使用Log4j,我们可以控制日志信息输送的目的地是控制台、文件、GUI组件,甚至是套接口服务器、NT的事件记录器、UNIX Syslog守护进程等;我们也可以控制每一条日志的输出格式;通过定义每一条日志信息的级别,我们能够更加细致地控制日志的生成过程。最令人感兴趣的就是,这些可以通过一个配置文件来灵活地进行配置,而不需要修改应用的代码。 如何使用? 引入jar,使用log4j时需要的jar为:log4j.jar。 定义配置文件log4j.properties或log4j.xml 在具体的类中进行使用: 在需要日志输出的类中加入:private static final Logger logger = Logger.getLogger(Tester.class); //通过Logger获取Logger实例 在需要输出日志的地方调用相应方法即可:logger.debug(“System […]
View Details一、说明 如果是使用slf4j规范的,请先引用:
1 2 3 4 5 6 |
<!-- https://mvnrepository.com/artifact/org.apache.logging.log4j/log4j-slf4j-impl --> <dependency> <groupId>org.apache.logging.log4j</groupId> <artifactId>log4j-slf4j-impl</artifactId> <version>2.11.0</version> </dependency> |
二、测试类
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
import org.junit.Test; import org.slf4j.Logger; import org.slf4j.LoggerFactory; public class LogTest { @Test public void test() { Logger log = LoggerFactory.getLogger(LogTest.class); log.warn("this is message {}", 1); Exception ex = new Exception("this is a message."); log.error("a new exeception", ex); log.trace("trace message."); log.info("info message."); for (int i = 0; i < 120000; i++) log.debug("debug message:{}={}", "line", i); } } |
三、配置 在Maven项目的resources目录下,或者Java项目的src下,新建log4j2.xml文件。这里要注意,如果是使用的log4j1版本,请添加log4j.properties文件并配置,但是在log4j2中已经废弃了log4j.properties文件的使用,使用的是log4j2.xml。参考如下内容:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
<?xml version="1.0" encoding="UTF-8"?> <configuration status="ON"> <appenders> <Console name="Console" target="SYSTEM_OUT"> <PatternLayout pattern="%d{HH:mm:ss.SSS} [%t] %-5level %logger{36} - %msg%n"/> </Console> <RollingFile name="RollingFile" fileName="logs/app.log" filePattern="logs/$${date:yyyy-MM}/app-%d{MM-dd-yyyy}-%i.log.gz"> <PatternLayout pattern="%d{yyyy.MM.dd 'at' HH:mm:ss z} %-5level %class{36} %L %M - %msg%xEx%n"/> <SizeBasedTriggeringPolicy size="5 MB" /> </RollingFile> </appenders> <loggers> <root level="DEBUG"> <appender-ref ref="Console"/> <appender-ref ref="RollingFile"/> </root> </loggers> </configuration> |
说明:上面配置的最低Level为DEBUG,同时使用了Console和RollingFile两种输出方式。所以输出的日志,一方面输出到控制台,一方面输出到logs/app.log中,当app.log达到5MB时,就会自动生成gz压缩文件到/logs下,并清空app.log,继续输出到app.log,以此循环。 四、运行并输出日志 宋兴柱(Sindrol):转载内容,请标明出处,谢谢!源文来自 宝贝云知识分享:https://www.dearcloud.cn from:https://www.cnblogs.com/songxingzhu/p/8867817.html
View DetailsMap 接口中键和值一一映射. 可以通过键来获取值。 给定一个键和一个值,你可以将该值存储在一个 Map 对象。之后,你可以通过键来访问对应的值。 当访问的值不存在的时候,方法就会抛出一个 NoSuchElementException 异常。 当对象的类型和 Map 里元素类型不兼容的时候,就会抛出一个 ClassCastException 异常。 当在不允许使用 Null 对象的 Map 中使用 Null 对象,会抛出一个 NullPointerException 异常。 当尝试修改一个只读的 Map 时,会抛出一个 UnsupportedOperationException 异常。 序号 方法描述 1 void clear( ) 从此映射中移除所有映射关系(可选操作)。 2 boolean containsKey(Object k) 如果此映射包含指定键的映射关系,则返回 true。 3 boolean containsValue(Object v) 如果此映射将一个或多个键映射到指定值,则返回 true。 4 Set entrySet( ) 返回此映射中包含的映射关系的 Set 视图。 5 boolean equals(Object obj) 比较指定的对象与此映射是否相等。 6 Object get(Object k) 返回指定键所映射的值;如果此映射不包含该键的映射关系,则返回 null。 7 int hashCode( ) 返回此映射的哈希码值。 8 boolean isEmpty( ) 如果此映射未包含键-值映射关系,则返回 true。 9 Set keySet( ) 返回此映射中包含的键的 Set 视图。 10 Object put(Object k, Object v) 将指定的值与此映射中的指定键关联(可选操作)。 11 void putAll(Map m) […]
View Details版权声明:这可是本菇凉辛辛苦苦原创的,转载请一定带上我家地址,不要忘记了哈 . https://blog.csdn.net/u011314442/article/details/90140532
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
import tk.mybatis.mapper.entity.Example; import com.github.pagehelper.PageHelper; ... @Override public List<RepaymentPlan> listRepaymentPlan(Integer start) { Example example = new Example(RepaymentPlan.class); // 排序 example.orderBy("id"); // 条件查询 example.createCriteria() .andNotEqualTo("repayStatus", 3) .andLessThanOrEqualTo("shouldRepayDate", new Date()); // 分页 PageHelper.startPage(start, 20); // 每次查询20条 return repaymentPlanMapper.selectByExample(example); } |
2. PageHelper 使用详解见文章:分页插件pageHelpler的使用(ssm框架中)服务器端分页 3. 更多关于 Example 的使用说明见文章: java 查询功能实现的八种方式 MyBatis : Mapper 接口以及 Example 使用实例、详解 4. 当只是查询数据,不需要返回总条数时可选择此方法:
1 |
PageHelper.startPage(第几页, 20,false); // 每次查询20条 |
当数据量极大时,可以快速查询,忽略总条数的查询,减少查询时间。 以下是该方法原码实现: ————————————————- 2019.5.13 后记 : 1)分页的写法 下图中黄框中的写法运行 比红框中 快,不知道是不是插件本身也会有费时: 2)再补充一种分页方式,mybatis 自带的 RowBounds:
1 2 3 4 5 6 7 8 9 10 |
public List<RepayPlan> listRepayPlan(int start) { // 查询所有未还款结清且应还日期小于当前时间的账单 Example example = new Example(RepayPlan.class); example.orderBy("id "); // 按id排序 example.createCriteria() .andNotEqualTo("repayStatus", 3) .andLessThanOrEqualTo("shouldRepayDate", new Date()); RowBounds rowBounds = new RowBounds(start, 20); // 每次查询20条 return epaymentPlanMapper.selectByExampleAndRowBounds(example,rowBounds); } |
推荐用 RowBounds :mybatis 自带的,且速度快 。个人运行,后 2 种分页明显比 PageHelper 快。 from:https://cloud.tencent.com/developer/article/1433161
View Details在开发过程中遇到一个问题,服务器经过排序返回后的字符串数据使用fastjson解析后,数据顺序发生变化,引起业务异常。 解决办法: 1、解析时增加参数不调整顺序 JSONObject respondeBodyJson = JSONObject.parseObject(jsonStr, Feature.OrderedField); 2、初始化json对象为有序对象: JSONObject retObj = new JSONObject(true); 这样生成的json对象就与放入数据时一致。 3、使用Gson解析 JsonObject returnData = new JsonParser().parse(replyString).getAsJsonObject(); ———————————————— 版权声明:本文为CSDN博主「long2010110」的原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接及本声明。 原文链接:https://blog.csdn.net/long2010110/article/details/81537820
View Details场景 两个不同的类,其中一部分的属性相同。 要把其中一个对象的一些属性赋值给另一个对象。 最原始的方式是依次调用两个对象的set和get方法,挨个赋值。 但是spring提供了BanUtils的方法copyPrpperties可以实现。 注: 博客: https://blog.csdn.net/badao_liumang_qizhi 关注公众号 霸道的程序猿 获取编程相关电子书、教程推送与免费下载。 实现 引入包
1 |
import org.springframework.beans.BeanUtils; |
然后调用
1 |
BeanUtils.copyProperties(kqDkszJl,kqDksz); |
其中左边是要取值的对象,右边是要赋值的对象。 博客园: https://www.cnblogs.com/badaoliumangqizhi/ 关注公众号 霸道的程序猿 获取编程相关电子书、教程推送与免费下载。 from:https://www.cnblogs.com/badaoliumangqizhi/p/13786384.html
View Details转换字符串示例:
1 2 |
String array2 = "{'i':'2','b':'3'}"; JSONObject parseObject = JSON.parseObject(array2); |
结果:
1 |
{"b":"3","i":"2"} |
我们会发现顺序与原来的字符串顺序不一致。 通过DEBUG去com.alibaba.fastjson.parser.DefaultJSONParser的下述方法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 |
public Object parse(Object fieldName) { final JSONLexer lexer = this.lexer; switch (lexer.token()) { case SET: lexer.nextToken(); HashSet<Object> set = new HashSet<Object>(); parseArray(set, fieldName); return set; case TREE_SET: lexer.nextToken(); TreeSet<Object> treeSet = new TreeSet<Object>(); parseArray(treeSet, fieldName); return treeSet; case LBRACKET: JSONArray array = new JSONArray(); parseArray(array, fieldName); if (lexer.isEnabled(Feature.UseObjectArray)) { return array.toArray(); } return array; case LBRACE: //重点就是此行的lexer.isEnabled(Feature.OrderedField)=false JSONObject object = new JSONObject(lexer.isEnabled(Feature.OrderedField)); return parseObject(object, fieldName); // case LBRACE: { // Map<String, Object> map = lexer.isEnabled(Feature.OrderedField) // ? new LinkedHashMap<String, Object>() // : new HashMap<String, Object>(); // Object obj = parseObject(map, fieldName); // if (obj != map) { // return obj; // } // return new JSONObject(map); // } case LITERAL_INT: Number intValue = lexer.integerValue(); lexer.nextToken(); return intValue; case LITERAL_FLOAT: Object value = lexer.decimalValue(lexer.isEnabled(Feature.UseBigDecimal)); lexer.nextToken(); return value; |
重点就是此行的lexer.isEnabled(Feature.OrderedField)=false,打开JSONObject的源码构造方法可以发现当ordered参数值为false时使用的是HashMap存放数据。
1 2 3 4 5 6 7 8 9 10 11 12 13 |
public JSONObject(int initialCapacity, boolean ordered){ if (ordered) { map = new LinkedHashMap<String, Object>(initialCapacity); } else { map = new HashMap<String, Object>(initialCapacity); } } |
hashmap是数组加链表结构,根据key的hash算法确定在数组中的位置,当发生hash冲突的时候,根据二叉树或者红黑树构成链表。所以是有序的,key确定,位置也就确定了。 如果要实现转换前的数据顺序与转换后的数据顺序一致,可以使用如下方式:
1 2 3 |
String array2 = "{'i':'2','b':'3'}"; JSONObject parseObject = JSON.parseObject(array2, Feature.OrderedField); |
此时会使用LinkedHashMap,LinkedHashMap的内部维持了一个双向链表,保存了数据的插入顺序,遍历时,先得到的数据便是先插入的。 from:https://blog.csdn.net/h363659487/article/details/103880710
View Detailsspring-boot继承mybatis启动时,警告如下: 2018-09-10 15:00:14.721 WARN tk.mybatis.spring.mapper.ClassPathMapperScanner --No MyBatis mapper was found in '[com.kevin]' package. Please check your configuration. 使用的tk的开源项目进行mybatis集成,百度了很多解决方案,最终看到一位前辈介绍:doScan()会扫描启动类同级目录下的mapper接口,但是合理的目录结果绝对不允许所有的mapper都在启动类目录下,所以在启动类目录下添加了一个伪mapper,如下: 再重新启动服务,就不会出现如上warn信息了…… from:https://my.oschina.net/kevin2kelly/blog/2046324 ========================================================================================= 光子:通过以上兄弟的方法,我的警告确实没有了。但经过dev-tools热启动后还是会提醒: 无法获取实体类com.w3cnet.doctoradvice.entity.HisvMzbrJzxx对应的表名 …… 还需要安装一下:Mapper Spring Boot Starter ,贴上地址:
1 2 |
// https://mvnrepository.com/artifact/tk.mybatis/mapper-spring-boot-starter compile group: 'tk.mybatis', name: 'mapper-spring-boot-starter', version: '2.1.5' |
折腾了几次,终于完美解决…… 参考:https://blog.csdn.net/zwrlj527/article/details/91824220
View Details内容简介 本文主要说明在Java8及以上版本中,使用stream().filter()来过滤一个List对象,查找符合条件的对象集合。 List对象类(StudentInfo)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 |
public class StudentInfo implements Comparable<StudentInfo> { //名称 private String name; //性别 true男 false女 private Boolean gender; //年龄 private Integer age; //身高 private Double height; //出生日期 private LocalDate birthday; public StudentInfo(String name, Boolean gender, Integer age, Double height, LocalDate birthday){ this.name = name; this.gender = gender; this.age = age; this.height = height; this.birthday = birthday; } public String toString(){ String info = String.format("%s\t\t%s\t\t%s\t\t\t%s\t\t%s",this.name,this.gender.toString(),this.age.toString(),this.height.toString(),birthday.toString()); return info; } public static void printStudents(List<StudentInfo> studentInfos){ System.out.println("[姓名]\t\t[性别]\t\t[年龄]\t\t[身高]\t\t[生日]"); System.out.println("----------------------------------------------------------"); studentInfos.forEach(s->System.out.println(s.toString())); System.out.println(" "); } @Override public int compareTo(StudentInfo ob) { return this.age.compareTo(ob.getAge()); //return 1; } public String getName() { return name; } public void setName(String name) { this.name = name; } public Boolean getGender() { return gender; } public void setGender(Boolean gender) { this.gender = gender; } public Integer getAge() { return age; } public void setAge(Integer age) { this.age = age; } public Double getHeight() { return height; } public void setHeight(Double height) { this.height = height; } public LocalDate getBirthday() { return birthday; } public void setBirthday(LocalDate birthday) { this.birthday = birthday; } } |
测试数据
1 2 3 4 5 6 |
//测试数据,请不要纠结数据的严谨性 List<StudentInfo> studentList = new ArrayList<>(); studentList.add(new StudentInfo("李小明",true,18,1.76,LocalDate.of(2001,3,23))); studentList.add(new StudentInfo("张小丽",false,18,1.61,LocalDate.of(2001,6,3))); studentList.add(new StudentInfo("王大朋",true,19,1.82,LocalDate.of(2000,3,11))); studentList.add(new StudentInfo("陈小跑",false,17,1.67,LocalDate.of(2002,10,18))); |
输出Students列表
1 2 |
//输出List StudentInfo.printStudents(studentList); |
输出结果如下图: 使用filter()过滤List
1 2 3 4 |
//查找身高在1.8米及以上的男生 List<StudentInfo> boys = studentList.stream().filter(s->s.getGender() && s.getHeight() >= 1.8).collect(Collectors.toList()); //输出查找结果 StudentInfo.printStudents(boys); |
结果如下图: from:https://www.cnblogs.com/codecat/p/10912454.html
View DetailsAPI: https://www.runoob.com/java/java8-streams.html
1 2 3 4 |
mylist.stream() .map(myfunction->{ return item; }).collect(Collectors.toList()); |
说明: steam():把一个源数据,可以是集合,数组,I/O channel, 产生器generator 等,转化成流。 forEach():迭代流中的每个数据。以下代码片段使用 forEach 输出了10个随机数.
1 2 |
Random random = new Random(); random.ints().limit(10).forEach(System.out::println); |
map():用于映射每个元素到对应的结果。以下代码片段使用 map 输出了元素对应的平方数:
1 2 3 |
List<Integer> numbers = Arrays.asList(3, 2, 2, 3, 7, 3, 5); // 获取对应的平方数 List<Integer> squaresList = numbers.stream().map( i -> i*i).distinct().collect(Collectors.toList()); |
filter():filter 方法用于通过设置的条件过滤出元素。以下代码片段使用 filter 方法过滤出空字符串:
1 2 3 4 5 6 7 8 |
List<String>strings = Arrays.asList("abc", "", "bc", "efg", "abcd","", "jkl"); // 获取空字符串的数量 int count = strings.stream().filter(string -> string.isEmpty()).count(); limit limit 方法用于获取指定数量的流。 以下代码片段使用 limit 方法打印出 10 条数据: Random random = new Random(); random.ints().limit(10).forEach(System.out::println); |
sorted(): 用于对流进行排序。以下代码片段使用 sorted 方法对输出的 10 个随机数进行排序:
1 2 3 4 5 6 7 8 9 |
Random random = new Random(); random.ints().limit(10).sorted().forEach(System.out::println); 并行(parallel)程序 parallelStream 是流并行处理程序的代替方法。以下实例我们使用 parallelStream 来输出空字符串的数量: List<String> strings = Arrays.asList("abc", "", "bc", "efg", "abcd","", "jkl"); // 获取空字符串的数量 int count = strings.parallelStream().filter(string -> string.isEmpty()).count(); 我们可以很容易的在顺序运行和并行直接切换。 |
Collectors(): 类实现了很多归约操作,例如将流转换成集合和聚合元素。Collectors 可用于返回列表或字符串:
1 2 3 4 5 6 |
List<String>strings = Arrays.asList("abc", "", "bc", "efg", "abcd","", "jkl"); List<String> filtered = strings.stream().filter(string -> !string.isEmpty()).collect(Collectors.toList()); System.out.println("筛选列表: " + filtered); String mergedString = strings.stream().filter(string -> !string.isEmpty()).collect(Collectors.joining(", ")); System.out.println("合并字符串: " + mergedString); |
from:https://blog.csdn.net/shine_guo_star/article/details/94383319
View Details