SSM第三讲 SpringAOP开发

2020-09-27 17:11:51 浏览数 (1)

SpringAOP开发

  • AOP概述
  • AOP原理
  • 基于xml配置aop
  • 基于注解配置aop
  • 日志

一. AOP概述

1. 什么是AOP

AOP(Aspect Oriented Programming),即面向切面编程。通过预编译方式和运行期动态代理实现程序功能的统一维护的一种技术。AOP是OOP的延续,是软件系统开发中的一个热点,也是spring框架的一个重点。利用AOP可以实现业务逻辑各个部分的隔离,从而使得业务逻辑各个部分的耦合性降低,提高程序的可重用性,同时提高开发效率。

2. 传统开发模型: 纵向的编程

3. 面向切面编程:纵横配合的编程

AOP原理:

4. AOP的作用及优势

作用:在程序运行期间,不修改源码对已有方法进行增强,扩展功能。

优势:减少重复代码、提高开发效率、维护方便

5. AOP的实现原理

原理: 动态代理技术

说明:在 spring 中,框架会根据目标类是否实现了接口来决定采用哪种动态代理的方式。

如果目标对象实现接口,使用JDK代理。

目标对象没有实现接口使用CGLIB代理。

JDK动态代理

动态代理:代理类在程序运行时创建的代理方式被成为动态代理。

JDK动态代理有以下特点:

  • 代理对象的生成,是利用JDK的API,动态的在内存中构建代理对象(需要我们提供目标对象所实现的接口的字节码对象)
  • 动态代理要求目标对象一定要实现接口,所以也叫做接口代理!
回顾JDK动态代理
  • Proxy类

static Object newProxyInstance(ClassLoader loader, Class[] interfaces, InvocationHandler h) 作用:生成一个代理对象

loader

和目标对象一类的类加载器

interfaces

真实对象所有实现的接口

h

是一个接口,传一个匿名内部类做为实现类,并且重写其中的方法来实现代理的功能

返回值

返回代理对象

  • InvocationHandler接口

Object invoke(Object proxy, Method method, Object[] args) 作用:这个接口中的方法会调用多次,每个方法都会调用一次,用来实现代理方法的功能

proxy

代表生成的代理对象,不建议在方法中直接调用,不然会出现递归调用。

method

代理的方法对象

args

调用方法时传递的参数数组

返回

返回当前这个方法调用的返回值

动态代理模式的开发步骤
  1. 首先需要存在抽象角色,定义所有的功能
  2. 真实对象实现抽象角色所有的功能
  3. 通过Proxy类,创建代理对象,调用代理方法
  4. 在InvocationHandler的invoke对代理的方法有选择的修改或不修改。

动态代理模式应用案例:

  • 租房案例:

接口:

代码语言:javascript复制
interface Service{                          //接口
    void save();
    void update();
    void delete();
}

目标对象:

代码语言:javascript复制
class ServiceImpl implements Service{           //目标对象(被代理对象)

    @Override
    public void save() {
        System.out.println("保存数据");
    }

    @Override
    public void update() {
        System.out.println("修改数据");
    }

    @Override
    public void delete() {
        System.out.println("删除数据");
    }
}
  • 测试类:
代码语言:javascript复制
public class Demo {
    public static void main(String[] args) throws Exception {
        /*
        最终返回一个代理对象,此代理对象对目标对象的所有方法都进行了代理
        最终执行代理对象的任何方法都会执行InvocationHandler中的invoke方法
         */
        Service o = (Service) Proxy.newProxyInstance(
                /*
               传入目标对象的类加载器
                 */
                ServiceImpl.class.getClassLoader(),
                /*
             传入目标对象的所有实现接口的字节码对象
             代理对象就是根据此接口字节码对象来指定代理方法,实现动态方法代理
               要不然目标对象增加一个方法,动态代理需要手动添加一个代理方法
                 */
                ServiceImpl.class.getInterfaces(),
                new InvocationHandler() {
                    @Override
                    //proxy是代理对象
                    //method是目标对象的方法
                    //args是目标对象方法的参数
                    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                        System.out.println("增强的代码");
                        /*
                        因为method.invoke执行的是目标对象的具体方法,因此必须传入目标对象
                         */
                        Object invoke = method.invoke(new ServiceImpl());
                        System.out.println("增强的代码");
                        return invoke;
                    }
                }
        );
        //代理对象执行save方法,最终会执行InvocationHandler中的invoke方法
        o.save();

    }
}

小结:JDK提供的动态代理是基于接口的,把目标对象所实现的所有接口的字节码对象传入方法中,JDK能在内存中动态的帮我们创建一个对象,该对象实现的目标对象实现的所有方法。即保证目标对象的所有方法代理对象都能代理到

JDK动态代理,要求目标对象一定要实现接口,否则不能用动态代理。

如果目标对象就是没有实现接口,还想对目标对象某个方法进行扩展,怎么办?看CGLIB代理!

CGLIB代理

动态代理模式要求目标对象是实现一个接口的目标对象,但是有时候目标对象只是一个单独的对象,并没有实现任何的接口,这个时候就可以使用以目标对象子类的方式类实现代理,这种方法就叫做:Cglib代理

Cglib代理,也叫作子类代理,它是在内存中构建一个子类对象从而实现对目标对象功能的扩展.

Cglib是一个强大的高性能的代码生成包,它可以在运行期扩展java类与实现java接口.它广泛的被许多AOP的框架使用,例如Spring AOP和synaop,为他们提供方法的interception(拦截)

Spring-Core核心已经包含CGLIB功能。

代码:

Service类:

代码语言:javascript复制
package com.dfbz.entity;

public class Service {
    public void save(){
        System.out.println("保存");
    }
    
    public void update(){
        System.out.println("修改");
    }
    
    public void delete(){
        System.out.println("删除");
    }
}
代码语言:javascript复制
package com.itdfbz.spring;

import com.dfbz.entity.Service;
import org.junit.Test;
import org.springframework.cglib.proxy.Enhancer;
import org.springframework.cglib.proxy.MethodInterceptor;
import org.springframework.cglib.proxy.MethodProxy;

import java.lang.reflect.Method;

public class Demo1 {

    @Test
    public void test1() {
        //返回一个service的子类
        Service serviceProxy = (Service) Enhancer.create(Service.class, new MethodInterceptor() {
            /*
            *
            *   proxy:代理对象
            *   method:要执行的方法对象
            *   args:要执行的方法的参数
            *   methodProxy:代理对象的方法对象
            * */
            @Override
            public Object intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
                System.out.println("增强的代码");
//                Object invoke = method.invoke(new Service());
                 /*
                methodProxy:
                    代理对象的方法类,因为cglib代理对目标对象进行继承派生出子类,因此代理对象是目标对象的子类
                    此代理对象方法类可以执行本代理对象方法,也可以执行
                 */
                Object invoke = methodProxy.invoke(new Service(), args);
                System.out.println("增强的代码");
                return invoke;
            }
        });

        serviceProxy.save();
    }

}

使用CGLIB子类代理:

  • 需要引入cglib的jar文件,但是Spring的核心包中已经包括了Cglib功能,
  • 引入功能包后,就可以在内存中动态构建子类
  • 代理的类不能为final,否则报错
  • 目标对象的方法如果为final/static,那么就不会被拦截,即不会执行目标对象额外的业务方法.

二. Spring中的AOP

通过动态代理模式的实现后,我们可以定义AOP其实就是用于通过规则设置来拦截方法,加入可以统一处理的代码。

1. AOP实现原理

在spring中,框架会根据目标类是否实现了接口来决定采用哪种动态代理的方式。

AOP配置后 Spring容器返回的目标对象,实质上是 Spring利用动态代理技术生成一个代理类型。代理类重写了原目标组件方法的功能,在代理类中调用方面对象功能和目标对象功能

Spring框架采用了两种动态代理实现:

  • 利用cglib工具包 目标没有接口时采用CGLIB代理,代理类是利用继承方法生成一个目标子类
  • 利用 JDK Proxy API 目标有接口时采用JDK动态代理,代理类是用实现目标接口方法生成一个类

2. AOP相关名词(了解)

Joinpoint(连接点):

  • 在程序执行过程中,需要拦截的方法
  • 根据规则,可以指定拦截的方法,我们将每一个被拦截的方法称为连接点。如:UserService中的save()方法就是连接点

Pointcut(切入点):

  • 切入点,就是拦截方法设置的规则,连接点的一系列集合
  • 所谓切入点是指我们要对哪些Joinpoint进行拦截的定义

Advice(通知/增强):

  • 增强连接点的实现代码(也就是需要为连接点绑定的方法)
  • 所谓通知是指拦截到Joinpoint之后所要做的事情就是通知
通知的类型:
  • 前置通知(Before):执行连接点方法之前执行
  • 后置通知(After):目标方法
  • 环绕通知(Around)
  • 最终通知(After)
  • 异常通知(AfterThrowing)

target(目标对象):

  • 被代理对象,连接点的所属方法,也就是需要增强的方法

Weaving(织入):

将通知应用到连接点形成切点的过程

Proxy(代理):

一个类被AOP织入增强后,就产生一个结果代理类

Aspect(切面):

  • 我们的拦截处理类,切入点 通知

3. 基于XML配置AOP

使用spring的aop,需要加入IOC基础框架包和spring的AOP支持包。

aspectj 开源的面向切面编程的组织,SpringAop在此基础上实现的切面编程。

3.1. 搭建环境

搭建Maven项目

需求:编写一个切面类,在执行save,delete,update方法,分别在方法执行前、方法之后、异常出现后、分别方法执行前后调用编写统一处理的代码,

引入依赖(context依赖中依赖有aop包,所有我们只要引入context依赖就ok):

代码语言:javascript复制
<!--context中有依赖于aop包所有导入context依赖就行-->
<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-context</artifactId>
    <version>5.0.6.RELEASE</version>
</dependency>
<!--导入spring整合的aspects依赖-->
<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-aspects</artifactId>
    <version>5.0.6.RELEASE</version>
</dependency>

<!--spring整合junit依赖-->
<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-test</artifactId>
    <version>5.0.6.RELEASE</version>
</dependency>

<!--junit依赖-->
<dependency>
    <groupId>junit</groupId>
    <artifactId>junit</artifactId>
    <version>4.12</version>
</dependency>
编写业务层类和接口

StudentService接口

代码语言:javascript复制
package com.dfbz.service;

public class UserService  {

    public void save(){
        System.out.println("保存");
    }

}
编写切面类:
代码语言:javascript复制
package com.dfbz.aspect;

import org.aspectj.lang.ProceedingJoinPoint;

public class MyAspect {
    
    public void before(){
        System.out.println("前置通知");
    }

    public void afterReturning(){
        System.out.println("后置通知");
    }

    public Object around(ProceedingJoinPoint around) throws Throwable {         //环绕通知

        System.out.println("环绕通知之前");
        Object proceed = around.proceed();          //执行目标方法
        System.out.println("环绕通知之后");
        return proceed;                             //将目标方法的返回值返回
    }

    public void after(){
        System.out.println("最终通知");
    }

    public void afterThrowing(){
        System.out.println("异常抛出通知");
    }

}
编写Spring配置文件
代码语言:javascript复制
<?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:aop="http://www.springframework.org/schema/aop"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
       http://www.springframework.org/schema/beans/spring-beans.xsd
       http://www.springframework.org/schema/aop
       http://www.springframework.org/schema/aop/spring-aop.xsd">


    <bean id="userService" class="com.dfbz.service.UserService"></bean>
    <bean id="myAspect" class="com.dfbz.aspect.MyAspect"></bean>
    <!--aop核心配置-->
    <aop:config>
        <!--
            配置切点
                id:切点名称
                expression:切点表达式
        -->
        <aop:pointcut id="myPoint" expression="execution(public void com.dfbz.service.UserService.save(..))"/>

        <!--
            配置切面
            before:前置通知
            after-returning:后置通知
            around:环绕通知
            after:最终通知
            after-throwing:异常通知
        -->
        <aop:aspect ref="myAspect">
            <!--【前置通知】-->
            <aop:before method="before" pointcut-ref="myPoint"/>
            <!--【环绕通知】-->
            <aop:around method="around" pointcut-ref="myPoint"/>
            <!--【后置通知(后置返回通知)】-->
            <aop:after-returning method="afterReturning" pointcut-ref="myPoint"/>
            <!--【异常通知】-->
            <aop:after-throwing method="afterThrowing" pointcut-ref="myPoint"/>
            <!--【最终通知】-->
            <aop:after method="after" pointcut-ref="myPoint"/>
        </aop:aspect>
    </aop:config>
</beans>
编写测试代码
代码语言:javascript复制
package com.dfbz.spring;

import com.dfbz.service.UserService;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:application.xml")
public class Demo1 {

    @Autowired
    private UserService userService;

    @Test
    public void test1(){

        userService.save();

    }
}

测试结果:

aop配置注意事项

在我们开发中通知顺序应该为:

  1. 前置通知
  2. 环绕通知之前
  3. 目标方法执行
  4. 环绕通知之后
  5. 后置通知
  6. 最终通知

但是如果在application.xml配置顺序如果不一样则会导致不同的情况,因此必须严格控制在spring配置文件中配置的通知顺序

3.2. 切入点表达式说明

3.2.1 execution

匹配方法的执行(常用)

execution(表达式)表达式语法:

execution([修饰符] 返回值类型 包名.类名.方法名(参数))

全匹配方式:

代码语言:javascript复制
public void com.dfbz.service.UserService.save(..)

访问修饰符可以省略

代码语言:javascript复制
void com.dfbz.service.UserService.save()

返回值可以使用**号,表示任意返回值*

代码语言:javascript复制
* com.dfbz.service.UserService.save()

包名可以使用*号,表示任意包,但是有几级包,需要写几个

代码语言:javascript复制
* *.*.*.*.UserService.save()

使用…来表示当前包,及其子包

代码语言:javascript复制
* com..UserService.save()

方法名匹配UserService的所有方法都能找到(但是方法必须无参)

代码语言:javascript复制
* com..UserService.*();

方法参数匹配,Userservice所有方法都能找到,不管方法是否有参

代码语言:javascript复制
* com..UserService.*(..)

类名可以使用 * 号,表示任意类*

代码语言:javascript复制
* com..*.save()
3.2.2within

按类匹配:

被匹配的类中的所有方法作为目标方法,使用within来实现

语法within(包名.类名)

匹配到具体类

代码语言:javascript复制
<aop:pointcut id="myPoint" expression="within(com.dfbz.service.UserService)"/>

匹配到包下的类(不包含子包)

代码语言:javascript复制
com.dfbz.service.*

找所有的service包下的类匹配到包和子包下的类

代码语言:javascript复制
within(com..*)

注意所有的该包和子包的类都会切入切面

*:通配的是任何的字符串 … :如果通配包的时候,就是匹配包以及它子包的类,如果通配是方法参数,就是不限定任何参数

3.4. 常用标签

aop:config

作用:aop的核心配置标签

aop:aspect

作用:用于配置切面。

属性:

id:给切面提供一个唯一标识。

ref:引用配置好的通知类bean的id。

aop:pointcut

作用:

用于配置切入点表达式

属性:

expression:用于定义切入点表达式。

id:用于给切入点表达式提供一个唯一标识。

aop:before

作用:用于配置前置通知

属性:

method:指定通知中方法的名称。

pointct:定义切入点表达式

pointcut-ref:指定切入点表达式的引用

aop:after-returning

作用:用于配置后置通知,如果出了异常就一定不会调用切面的方法

属性:

method:指定通知中方法的名称。

pointct:定义切入点表达式

pointcut-ref:指定切入点表达式的引用

aop:after-throwing

作用:用于配置异常通知,只有出了异常才会调用切面对应的方法

属性:

method:指定通知中方法的名称。

pointct:定义切入点表达式

pointcut-ref:指定切入点表达式的引用

aop:after

作用:用于配置最终通知,不管出不出异常,调用的切面的方法

属性:

method:指定通知中方法的名称。

pointct:定义切入点表达式

pointcut-ref:指定切入点表达式的引用

aop:around

作用:用于配置环绕通知

属性:

method:指定通知中方法的名称。

pointct:定义切入点表达式

pointcut-ref:指定切入点表达式的引用

4. 基于注解配置AOP

4.1. 搭建环境

4.1.1. 第一步:创建一个Maven项目

引入依赖

代码语言:javascript复制
<!--context中有依赖于aop包所有导入context依赖就行-->
<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-context</artifactId>
    <version>5.0.6.RELEASE</version>
</dependency>
<!--导入spring整合的aspects依赖-->
<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-aspects</artifactId>
    <version>5.0.6.RELEASE</version>
</dependency>

<!--spring整合junit依赖-->
<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-test</artifactId>
    <version>5.0.6.RELEASE</version>
</dependency>

<!--junit依赖-->
<dependency>
    <groupId>junit</groupId>
    <artifactId>junit</artifactId>
    <version>4.12</version>
</dependency>
4.1.2. 第二步:编写业务层类

UserService

代码语言:javascript复制
package com.dfbz.service;

public class UserService  {

    public void save(){
        System.out.println("保存");
    }

}
编写切面类:
代码语言:javascript复制
package com.dfbz.aspect;

import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.springframework.stereotype.Component;

@Aspect     //标注此类是个注解类
@Component
public class AnnoMyAspect {

    @Pointcut("execution(* com.dfbz.service.UserService.save())")
    public void pt(){}

//    @Before("execution(* com.dfbz.service.UserService.save())")     //单独定义切点
    @Before("pt()")             //引用切点
    public void before(){
        System.out.println("前置通知");
    }

    @AfterReturning("pt()")
    public void afterReturning(){
        System.out.println("后置通知");
    }

    @Around("pt()")
    public Object around(ProceedingJoinPoint around) throws Throwable {         //环绕通知

        System.out.println("环绕通知之前");
        Object proceed = around.proceed();          //执行目标方法
        System.out.println("环绕通知之后");
        return proceed;                             //将目标方法的返回值返回
    }

    @After("pt()")
    public void after(){
        System.out.println("最终通知");
    }

    @AfterThrowing("pt()")
    public void afterThrowing(){
        System.out.println("异常抛出通知");
    }
}
4.1.3. 第三步:编写Spring配置文件
代码语言:javascript复制
<?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:aop="http://www.springframework.org/schema/aop"
       xmlns:context="http://www.springframework.org/schema/context"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
       http://www.springframework.org/schema/beans/spring-beans.xsd
       http://www.springframework.org/schema/aop
       http://www.springframework.org/schema/aop/spring-aop.xsd 	http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">


    <context:component-scan base-package="com.dfbz" />
    <bean id="userService" class="com.dfbz.service.UserService" />
    <!--开启aop注解支持-->
    <aop:aspectj-autoproxy />

</beans>
4.1.4. 第四步:编写测试代码
代码语言:javascript复制
package com.dfbz.spring;

import com.dfbz.service.UserService;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:application.xml")
public class Demo1 {

    @Autowired
    private UserService userService;

    @Test
    public void test1(){
        userService.save();
    }
}

4.3. 常用注解

@Aspect

作用:把当前类声明为切面类。

@Before

作用:把当前方法看成是前置通知。

属性:

value:用于指定切入点表达式,还可以指定切入点表达式的引用。

@AfterReturning

作用:把当前方法看成是后置通知。报异常,就不执行

属性:

value:用于指定切入点表达式,还可以指定切入点表达式的引用。

@AfterThrowing

作用:把当前方法看成是异常通知。只有报异常才执行

属性:

value:用于指定切入点表达式,还可以指定切入点表达式的引用。

@After

作用:把当前方法看成是最终通知。不管报不报异常都执行

属性:

value:用于指定切入点表达式,还可以指定切入点表达式的引用。

@Around

作用:把当前方法看成是环绕通知。

属性:

value:用于指定切入点表达式,还可以指定切入点表达式的引用。

@Pointcut

作用:指定切入点表达式

属性:

value:指定表达式的内容

4.4. 纯注解配置方式

使用配置类来替换配置文件

代码语言:javascript复制
package com.dfbz.app;

import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.EnableAspectJAutoProxy;

@Configuration                  //标注此类是个
@ComponentScan("com.dfbz")      //包扫描
@EnableAspectJAutoProxy         //开启aop注解支持
public class Application {
}
测试
代码语言:javascript复制
package com.dfbz.spring;

import com.dfbz.app.Application;
import com.dfbz.service.UserService;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = Application.class)
public class Demo2 {
    @Autowired
    private UserService userService;

    @Test
    public void test1(){
        userService.save();
    }
}

三、日志

1.开发步骤

A.导入log4j的jar包和配置文件

B.设置配置文件的日志输出配置

C.在spring的切面组件类中实现记录日志功能

log4j是Apache的一个开放源代码项目,通过使用Log4j,我们可以控制日志信息输送的目的地是控制台、文件、GUI组件,甚至是套接口服务器、NT的事件记录器、UNIX Syslog守护进程等;我们也可以控制每一条日志的输出格式;通过定义每一条日志信息的级别,我们能够更加细致地控制日志的生成过程。这些可以通过一个配置文件来灵活地进行配置,而不需要修改应用的代码。

2、log4j.properties 的使用详解

在实际应用中,要使Log4j在系统中运行须事先设定配置文件。配置文件事实上也就是对Logger、Appender及Layout进行相应设定。Log4j支持两种配置文件格式,一种是XML格式的文件,一种是properties属性文件。下面以properties属性文件为例介绍log4j.properties的配置。

2.1.配置根

配置根Logger:

log4j.rootLogger = [ level ] , appenderName1, appenderName2, …

level :设定日志记录的最低级别,可设的值有OFF、FATAL、ERROR、WARN、INFO、DEBUG、ALL或者自定义的级别,Log4j建议只使用中间四个级别。通过在这里设定级别,您可以控制应用程序中相应级别的日志信息的开关,比如在这里设定了INFO级别,则应用程序中所有DEBUG级别的日志信息将不会被打印出来。

ERROR、WARN、INFO、DEBUG

ERROR 为严重错误 主要是程序的错误

WARN 为一般警告,比如session丢失

INFO 为一般要显示的信息,比如登录登出

DEBUG 为程序的调试信息

appenderName:就是指定日志信息要输出到哪里。可以同时指定多个输出目的地,用逗号隔开。

例如:log4j.rootLogger=INFO,A1,B2,C3

2.2.配置日志信息输出目的地

log4j.appender.appenderName = appender类全限定名

appender类全限定名可以取值如下:

org.apache.log4j.ConsoleAppender(控制台)

org.apache.log4j.FileAppender(文件)

org.apache.log4j.DailyRollingFileAppender(每天产生一个日志文件)

org.apache.log4j.RollingFileAppender(文件大小到达指定尺寸的时候产生一个新的文件)

org.apache.log4j.WriterAppender(将日志信息以流格式发送到任意指定的地方)

2.3.配置日志信息的格式

log4j.appender.appenderName.layout = fully.qualified.name.of.layout.class

org.apache.log4j.HTMLLayout(以HTML表格形式布局),

org.apache.log4j.PatternLayout(可以灵活地指定布局模式),

org.apache.log4j.SimpleLayout(包含日志信息的级别和信息字符串),

org.apache.log4j.TTCCLayout(包含日志产生的时间、线程、类别等等信息)

2.4.输出目的地选项(了解)

控制台ConsoleAppender选项

Threshold=DEBUG:指定日志消息的输出最低层次。

ImmediateFlush=true:默认值是true,意谓着所有的消息都会被立即输出。

Target=System.err:默认情况下是:System.out,指定输出控制台

FileAppender 选项

Threshold=DEBUF:指定日志消息的输出最低层次。

ImmediateFlush=true:默认值是true,意谓着所有的消息都会被立即输出。

File=D:/logs/mylog.txt:指定消息输出到mylog.txt文件。

Append=false:默认值是true,即将消息增加到指定文件中,false指将消息覆盖指定的文件内容。

RollingFileAppender 选项

Threshold=DEBUG:指定日志消息的输出最低层次。

ImmediateFlush=true:默认值是true,意谓着所有的消息都会被立即输出。

File=mylog.txt:指定消息输出到mylog.txt文件。

Append=false:默认值是true,即将消息增加到指定文件中,false指将消息覆盖指定的文件内容。

MaxFileSize=100KB: 后缀可以是KB, MB 或者是 GB. 在日志文件到达该大小时,将会自动滚动,即将原来的内容移到mylog.log.1文件。

MaxBackupIndex=2:指定可以产生的滚动文件的最大数。

log4j.appender.A1.layout.ConversionPattern=%-4r %-5p %d{yyyy-MM-dd HH:mm:ssS} %c %m%n

DailyRollingFileAppender选项:

Threshold=WARN:指定日志信息的最低输出级别,默认为DEBUG。

ImmediateFlush=true:表示所有消息都会被立即输出,设为false则不输出,默认值是true。

Append=false:true表示消息增加到指定文件中,false则将消息覆盖指定的文件内容,默认值是true。

File=D:/logs/logging.log4j:指定当前消息输出到logging.log4j文件中。

DatePattern=’.'yyyy-MM:每月滚动一次日志文件,即每月产生一个新的日志文件。当前月的日志文件名为logging.log4j,前一个月的日志文件名为logging.log4j.yyyy-MM。

另外,也可以指定按周、天、时、分等来滚动日志文件,对应的格式如下:

1)’.'yyyy-MM:每月

2)’.'yyyy-ww:每周

3)’.'yyyy-MM-dd:每天

4)’.'yyyy-MM-dd-a:每天两次

5)’.'yyyy-MM-dd-HH:每小时

6)’.'yyyy-MM-dd-HH-mm:每分钟

日志信息格式中几个符号所代表的含义:(了解)

-X号: X信息输出时左对齐;

%p: 输出日志信息优先级,即DEBUG,INFO,WARN,ERROR,FATAL,

%d: 输出日志时间点的日期或时间,默认格式为ISO8601,也可以在其后指定格式,比如:%d{yyy MMM dd HH:mm:ss,SSS},输出类似:2002年10月18日 22:10:28,921

%r: 输出自应用启动到输出该log信息耗费的毫秒数

%c: 输出日志信息所属的类目,通常就是所在类的全名

%t: 输出产生该日志事件的线程名

%l: 输出日志事件的发生位置,相当于%C.%M(%F:%L)的组合,包括类目名、发生的线程,以及在代码中的行数。举例:Testlog4.main (TestLog4.java:10)

%x: 输出和当前线程相关联的NDC(嵌套诊断环境),尤其用到像java servlets这样的多客户多线程的应用中。

%%: 输出一个"%"字符

%F: 输出日志消息产生时所在的文件名称

%L: 输出代码中的行号

%m: 输出代码中指定的消息,产生的日志具体信息

%n: 输出一个回车换行符,Windows平台为"/r/n",Unix平台为"/n"输出日志信息换行

可以在%与模式字符之间加上修饰符来控制其最小宽度、最大宽度、和文本的对齐方式。

如:

  1. c:指定输出category的名称,最小的宽度是20,如果category的名称小于20的话,默认的情况下右对齐。
  2. %-20c:指定输出category的名称,最小的宽度是20,如果category的名称小于20的话,"-"号指定左对齐。
  3. %.30c:指定输出category的名称,最大的宽度是30,如果category的名称大于30的话,就会将左边多出的字符截掉,但小于30的话也不会有空格。
  4. .30c:如果category的名称小于20就补空格,并且右对齐,如果其名称长于30字符,就从左边较远输出的字符截掉。

练习:

将当前程序添加日志系统,将info级别的信息输出到e盘,并且以info.log命名,并同时以网页形式输出到e盘,以info.html命名

2.5 log4j.properties的配置

配置步骤

  1. 把log4j-*.*jar引入项目中
  2. 编写log4j.properties文件,并放入src/main/resources下
  3. Logger调用

在每一个要产生日志的类上面加上这句,MonitorMessageController为该类的名字。

private final Logger log= Logger.getLogger(MonitorMessageController.class);

在需要记录日志信息的位置:

log.info(“日志信息”):记录info级别信息

log.error(“日志信息”):记录error级别信息

O8601,也可以在其后指定格式,比如:%d{yyy MMM dd HH:mm:ss,SSS},输出类似:2002年10月18日 22:10:28,921

%r: 输出自应用启动到输出该log信息耗费的毫秒数

%c: 输出日志信息所属的类目,通常就是所在类的全名

%t: 输出产生该日志事件的线程名

%l: 输出日志事件的发生位置,相当于%C.%M(%F:%L)的组合,包括类目名、发生的线程,以及在代码中的行数。举例:Testlog4.main (TestLog4.java:10)

%x: 输出和当前线程相关联的NDC(嵌套诊断环境),尤其用到像java servlets这样的多客户多线程的应用中。

%%: 输出一个"%"字符

%F: 输出日志消息产生时所在的文件名称

%L: 输出代码中的行号

%m: 输出代码中指定的消息,产生的日志具体信息

%n: 输出一个回车换行符,Windows平台为"/r/n",Unix平台为"/n"输出日志信息换行

可以在%与模式字符之间加上修饰符来控制其最小宽度、最大宽度、和文本的对齐方式。

如:

  1. c:指定输出category的名称,最小的宽度是20,如果category的名称小于20的话,默认的情况下右对齐。
  2. %-20c:指定输出category的名称,最小的宽度是20,如果category的名称小于20的话,"-"号指定左对齐。
  3. %.30c:指定输出category的名称,最大的宽度是30,如果category的名称大于30的话,就会将左边多出的字符截掉,但小于30的话也不会有空格。
  4. .30c:如果category的名称小于20就补空格,并且右对齐,如果其名称长于30字符,就从左边较远输出的字符截掉。

练习:

将当前程序添加日志系统,将info级别的信息输出到e盘,并且以info.log命名,并同时以网页形式输出到e盘,以info.html命名

2.5 log4j.properties的配置

配置步骤

  1. 把log4j-*.*jar引入项目中
  2. 编写log4j.properties文件,并放入src/main/resources下
  3. Logger调用

在每一个要产生日志的类上面加上这句,MonitorMessageController为该类的名字。

private final Logger log= Logger.getLogger(MonitorMessageController.class);

在需要记录日志信息的位置:

log.info(“日志信息”):记录info级别信息

log.error(“日志信息”):记录error级别信息

通过以上的配置,即可在运行的过程中在日志输出。

0 人点赞