8月6日

前面介绍了Commons Logging和Log4j这一对好基友,它们一个负责充当日志API,一个负责实现日志底层,搭配使用非常便于开发。

为什么要使用s l f4j

我们自己的系统中使用了logback这个日志系统
我们的系统使用了A.jar,A.jar中使用的日志系统为log4j
我们的系统又使用了B.jar,B.jar中使用的日志系统为slf4j-simple

这样,我们的系统就不得不同时支持并维护logback、log4j、slf4j-simple三种日志框架,非常不便。

解决这个问题的方式就是引入一个适配层,由适配层决定使用哪一种日志系统,而调用端只需要做的事情就是打印日志而不需要关心如何打印日志,slf4j或者commons-logging就是这种适配层,slf4j是本文研究的对象。

从上面的描述,我们必须清楚地知道一点:slf4j只是一个日志标准,并不是日志系统的具体实现。理解这句话非常重要,slf4j只做两件事情:

  • 提供日志接口
  • 提供获取具体日志对象的方法
1
2
3
4
5
6
7
8
9
10
11
12
13
#下面是配置将日志信息插入数据库,
#配置输出目标为数据库(假如要将日志在控制台输出,配置为log4j.appender. stdout =org.apache.log4j.ConsoleAppender;将日志写入文件,配置为log4j.appender.logfile=org.apache.log4j.DailyRollingFileAppender
#这样的配置在许多地方都要有,需要可查有关资料),当然你也可以自己扩展org.apache.log4j.jdbc.JDBCAppender这个类,只需要在这里配置就可以了例如我们配置我自己扩展的MyJDBCAppender,配置为#log4j.appender.db=com.neam.commons.MyJDBCAppender
log4j.appender.datasource=org.apache.log4j.jdbc.JDBCAppender
log4j.appender.datasource.layout=org.apache.log4j.PatternLayout
log4j.appender.datasource.driver=com.mysql.jdbc.Driver
#定义什么级别的错误将写入到数据库中
log4j.appender.datasource.BufferSize=1
#设置缓存大小,就是当有1条日志信息是才忘数据库插一次,我设置的数据库名和表名均为user
log4j.appender.datasource.URL=jdbc\:mysql\://localhost\:3306/user?characterEncoding\=UTF8&zeroDateTimeBehavior\=convertToNull
log4j.appender.datasource.user=root
log4j.appender.datasource.password=root
log4j.appender.datasource.sql=insert into user (class,method,create_time,log_level,log_line,msg) values ('%C','%M','%d{yyyy-MM-dd HH:mm:ss}','%p','%l','%m')
1
同义词在去重校验逻辑中的业务编码为2

@Query注解:
 这种查询可以声明在 Repository 方法中,摆脱像命名查询那样的约束,将查询直接在相应的接口方法中声明,结构更为清晰,这是 Spring data 的特有实现。

如果是 @Query 中有 LIKE 关键字,后面的参数需要前面或者后面加 %,这样在传递参数值的时候就可以不加 %

还可以使用@Query来指定本地查询,只要设置nativeQuery为true

简明扼要的介绍一下@Data注解的功能与使用方法
注解功能
1、@Data可以为类提供读写功能,从而不用写get、set方法。
2、@Data提供 equals()、hashCode()、toString() 方法。

Lombok项目是一种自动接通你的编辑器和构建工具的一个Java库。不用再一次写额外的getter或者equals方法。由此可以看出,lombok会帮我们自动生成getter和euqals方法,但是更有意思的是,当我们的变量发生改变时,我们不再需要修改对的getter、setter方法,lombok帮我们在运行的过程中自动生成上述方法,编码更灵活.

所以,使用lombok的优点: 1、简化long冗余的javabean代码;

​ 2、提高执行效率

lombok中的常用注解

  • @Setter :在JavaBean或类JavaBean中使用,使用此注解会生成对应的setter方法;
  • @Getter:在JavaBean或类JavaBean中使用,使用此注解会生成对应的getter方法;
  • @ToString:在JavaBean或类JavaBean中使用,使用此注解会自动重写对应的toStirng方法;
  • @NoArgsConstructor:在JavaBean或类JavaBean中使用,使用此注解会生成对应的无参构造方法;
  • @HashCode:
  • @Equals:
  • @CanEqual:
  • @Data:在JavaBean或类JavaBean中使用,这个注解包含范围最广,它包含上述注解,即当使用当前注解时,会自动生成包含的所有方法;
  • @AllArgsConstructor:在JavaBean或类JavaBean中使用,使用此注解会生成对应的有参构造方法;
  • @Log(这是一个泛型注解,具体有很多种形式)
  • @EqualsAndHashCode:在JavaBean或类JavaBean中使用,使用此注解会自动重写对应的equals方法和hashCode方法;
  1. 此注解会生成equals(Object other)hashCode()方法。
  2. 它默认使用非静态,非瞬态的属性
  3. 可通过参数exclude排除一些属性
  4. 可通过参数of指定仅使用哪些属性
  5. 它默认仅使用该类中定义的属性且不调用父类的方法
  6. 可通过callSuper=true解决上一点问题。让其生成的方法中调用父类的方法。
  7. @Slf4j:在需要打印日志的类中使用,当项目中使用了slf4j打印日志框架时使用该注解,会简化日志的打印流程,只需调用info方法即可;

@ToString(exclude=”column”)

意义:排除column列所对应的元素,即在生成toString方法时不包含column参数;

@ToString(exclude={“column1”,”column2”})

意义:排除多个column列所对应的元素,其中间用英文状态下的逗号进行分割,即在生成toString方法时不包含多个column参数;
@ToString(of=”column”)

意义:只生成包含column列所对应的元素的参数的toString方法,即在生成toString方法时只包含column参数;;

@ToString(of={“column1”,”column2”})

意义:只生成包含多个column列所对应的元素的参数的toString方法,其中间用英文状态下的逗号进行分割,即在生成toString方法时只包含多个column参数;

Java中的::是Java8的新特性的应用。

foreach是属于Java集合的一个方法,准确来说,集合中Java8拥有一个stream方法,可以得到一个流对象,这个对象拥有很多方法,这些方法可以很方便的对集合进行例如排序,分组,计数,遍历,转换等操作,而遍历是比较常见的一种,forEach就是用来做这个的,这里的forEach就是stream的forEach。

java此时还有另外一个特性叫做lambda表达式和函数式接口,仅仅有一个未实现方法的接口,可以直接写作**(参数列表)** -> {方法体}这种形式。

省去了之前需要专为他编写一个实现类或者匿名内部类的代码,直接对接口进行实现。

forEach方法提供一个某种类型的Object,(具体是什么类型是要看Stream类的泛型参数的,不过一般就是这个集合提供的那种类型),

而System.out.println可以接受一个Object,因此,forEach提供的参数和System.out.println的参数类型是一致的,可以进行这种简写。

具体来说就是:原本应该写为:

.forEach(element -> {

System.out.println(element)

})

但是System.out.println的参数传递的参数element 的类型完全匹配,所以这样的时候就可以简化为:

.forEach(System.out::println)

即forEach将会使用System.out对象println方法进行接下来的操作。

1
2
3
4
5
List<String> list = new ArrayList<>();
list.add("a");
list.add("b");
list.add("c");
list.forEach(System.out::println);

其中list.forEach可以改写成以下代码:

1
2
3
for(int i = 0; i < list.size(); i++) {
System.out.println(list.get(i));
}

或者等于以下代码:

1
2
3
for(String s : list) {
System.out.println(s);
}

实际场景

有一个集合:List users = getList(); //从数据库查询的用户集合

现在想获取User的身份证号码;在后续的逻辑处理中要用,

常用的方法我们大家都知道,用for循环

1
2
3
4
5
6
7
List idcards=new ArrayList();//定义一个集合来装身份证号码

for(int i=0;i<users.size();i++){

idcards.add(users.get(i).getIdcard());

}

这种方法要写好几行代码,有没有简单点的,有,java8 API能一行搞定:

1
List idcards= users.stream().map(User::getIdcard).collect(Collectors.toList());

Arrays.stream()

为什么需要 Stream

Stream 作为 Java 8 的一大亮点,它与 java.io 包里的 InputStream 和 OutputStream 是完全不同的概念。Java 8 中的 Stream 是对集合(Collection)对象功能的增强,它专注于对集合对象进行各种非常便利、高效的聚合操作(aggregate operation),或者大批量数据操作 (bulk data operation)。

Stream API 借助于同样新出现的 Lambda 表达式,极大的提高编程效率和程序可读性。同时它提供串行并行两种模式进行汇聚操作,并发模式能够充分利用多核处理器的优势,使用 fork/join 并行方式来拆分任务和加速处理过程。

通常编写并行代码很难而且容易出错,但使用 Stream API 无需编写一行多线程的代码,就可以很方便地写出高性能的并发程序。所以说,Java 8 中首次出现的 java.util.stream 是一个函数式语言+多核时代综合影响的产物。

什么是流

Stream 不是集合元素,它不是数据结构并不保存数据,它是有关算法和计算的,它更像一个高级版本的 Iterator。原始版本的 Iterator,用户只能显式地一个一个遍历元素并对其执行某些操作;

高级版本的 Stream,用户只要给出需要对其包含的元素执行什么操作,比如 “过滤掉长度大于 10 的字符串”、“获取每个字符串的首字母”等,Stream 会隐式地在内部进行遍历,做出相应的数据转换。

Stream 就如同一个迭代器(Iterator),单向,不可往复,数据只能遍历一次,遍历过一次后即用尽了,就好比流水从面前流过,一去不复返。

而和迭代器又不同的是,Stream 可以并行化操作,迭代器只能命令式地、串行化操作。顾名思义,当使用串行方式去遍历时,每个 item 读完后再读下一个 item。而使用并行去遍历时,数据会被分成多个段,其中每一个都在不同的线程中处理,然后将结果一起输出。

Java 的并行 API 演变历程基本如下:

  1. 1.0-1.4 中的 java.lang.Thread
  2. 5.0 中的 java.util.concurrent
  3. 6.0 中的 Phasers 等
  4. 7.0 中的 Fork/Join 框架
  5. 8.0 中的 Lambda