孙卫琴的《精通Spring》的学习笔记:用AOP和SLF4J输出日志的范例

本文选自孙卫琴的《精通Spring:Java Web开发技术详解》清华大学出版社出版

技术支持网址为:​​  www.javathinker.net/spring.jsp​

​本书对应的直播和录播课:​​   www.javathinker.net/zhibo.jsp​

孙卫琴的QQ学习答疑群:915851077

Spring AOP依赖AspectJ软件包来实现。AspectJ软件包由Eclipse公司提供,它的下载网址为:https://www.eclipse.org/aspectj/downloads.php

把AspectJ软件包的类库文件拷贝到helloapp/WEB-INF/lib目录下。

org.aspectj.lang.JoinPoint类,以及@Aspect和@Poincut等注解都来自于AspectJ类库。

在本范例中,把输出日志的代码块作为增强代码块。需要插入该增强代码块的原始方法是控制器类中所有用@LogAnnotation注解标识的请求处理方法。@LogAnnotation注解是自定义的注解,例程1是它的源程序

例程1  LogAnnotation.java

package mypack; 
import java.lang.annotation.Documented; 
import java.lang.annotation.ElementType; 
import java.lang.annotation.Retention; 
import java.lang.annotation.RetentionPolicy; 
import java.lang.annotation.Target; 
 
//指定注解可用来标识类的方法 
@Target({ElementType.METHOD})
 
//指定注解的生命周期 
@Retention(RetentionPolicy.RUNTIME) 
public @interface LogAnnotation { 
  //为注解定义了一个desc属性,指定原始方法的描述信息
  Stringdesc() default "
无描述信息"; 
}
 

以下例程2的LogTesterController类的logtest()请求处理方法用@LogAnnotation注解来标识。

例程2 LogTesterController.java

@Controller
public class LogTesterController{
 
 @RequestMapping("/logtest")

  @LogAnnotation(desc="logtest():测试日志方法")
  publicString logtest( 

            @RequestParam(required=false,defaultValue="1")intnum,

            Modelmodel)  {

    System.out.println("from logtest():begin");

    model.addAttribute("output",100/num);

    System.out.println("from logtest():end");

     return"result";

  }
}
 

以下例程3的SystemLogAscpect类是切面类,它有一个切点“logPointCut()”,这个切点指定需要增强功能的原始方法为所有用@LogAnnotation注解标识的方法。

例程3  SystemLogAspect.java

@Aspect 
@Component 
public class SystemLogAspect { 
 
  //日志记录对象 
  privatestatic final Logger logger = LoggerFactory 

        .getLogger(SystemLogAspect.class); 

 
  /** 声明切点:所有用LogAnnotation注解标识的方法 */
 @Pointcut("@annotation(mypack.LogAnnotation)") 

  public voidlogPointCut() {} 

 
  /** 声明原始方法执行前的增强代码块  */
  @Before(value = "logPointCut()") 
  public voiddoBefore(JoinPoint joinPoint) { 

   System.out.println("from SystemLogAspect.doBefore()");

  } 
 
  /** 声明原始方法正常退出后的增强代码块  */
  @AfterReturning(value = "logPointCut()")

  public voiddoAfter(JoinPoint joinPoint) { 

   System.out.println("from SystemLogAspect.doAfter()");

   handleLog(joinPoint, null); 

  } 
 
  /** 声明原始方法出现异常时的增强代码块 */
  @AfterThrowing(value = "logPointCut()",throwing = "e") 

  public voiddoError(JoinPoint joinPoint, Exception e) { 

   System.out.println("from SystemLogAspect.doError()");

   handleLog(joinPoint, e); 

  } 
 
  /** 输出日志*/
  privatevoid handleLog(JoinPoint joinPoint,Exception e) { 

    //从joinPoint获得LogAnnotation注解 
   LogAnnotation logAnnotation = getLogAnnotation(joinPoint); 

   if(logAnnotation == null) 

      return;

 
   HttpServletRequest request = 

          ((ServletRequestAttributes) RequestContextHolder

          .getRequestAttributes())

          .getRequest(); 

    Stringip=request.getRemoteAddr();

    //获得LogAnnotation注解所标识的原始方法的描述信息 
    Stringdesc = logAnnotation.desc(); 

   if(e==null)

     logger.info(desc+";IP:"+ip);

    else
     logger.error(desc+";IP:"+ip+";
异常:"+e.getMessage());
  } 
 
  /** 获得原始方法的LogAnnotation注解
*/
  privatestatic LogAnnotation getLogAnnotation(JoinPoint joinPoint) { 

    //获得连接点的原始方法签名
   MethodSignature methodSignature = 

              (MethodSignature) joinPoint.getSignature(); 

    //获得连接点的原始方法
    Methodmethod = methodSignature.getMethod(); 

 
    if(method!= null) 

      returnmethod.getAnnotation(LogAnnotation.class); 

    else
      returnnull; 

  } 
}
 

SystemLogAspect类用@Component注解来标识,表明它属于Spring MVC的Bean组件,Spring MVC框架会自动创建SystemLogAspect Bean组件。

在Spring MVC的配置文件中,需要配置AspectJ自动代理,来启用AOP的代码增强功能:

<beans xmlns ="http://www.springframework.org/schema/beans"

  ……
 xmlns:aop="http://www.springframework.org/schema/aop"

 xsi:schemaLocation =
"http://www.springframework.org/schema/beans     

 http://www.springframework.org/schema/beans/spring-beans-3.0.xsd

  ……
  http://www.springframework.org/schema/aop
 http://www.springframework.org/schema/aop/spring-aop.xsd"
 >
 
  ……
  <aop:aspectj-autoproxy /> 
</beans>
 

通过浏览器访问http://localhost:8080/helloapp/logtest,LogTesterController类的logtest()方法正常执行,在Tomcat控制台会输出logtest()方法的打印信息,以及SystemLogAspect切面类的增强代码块输出的日志信息:

​from SystemLogAspect.doBefore()

from logtest():begin

from logtest():end

from SystemLogAspect.doAfter()

INFO [http-nio-8080-exec-9] mypack.SystemLogAspect(handleLog:63)

- logtest():测试日志方法;IP:0:0:0:0:0:0:0:1​

从以上打印结果可以看出,执行logtest()方法以及SystemLogAspect类的增强代码块的先后循序如下:

(1)SystemLogAspect.doBefore()

(2)LogTesterController.logtest()

(3)SystemLogAspect.doAfter()

通过浏览器访问http://localhost:8080/helloapp/logtest?num=0,LogTesterController类的logtest()方法会抛出ArithmeticException异常,在Tomcat控制台打印以下信息:

from SystemLogAspect.doBefore()

from logtest():begin

from SystemLogAspect.doError()

ERROR [http-nio-8080-exec-1] mypack.SystemLogAspect(handleLog:65)

- logtest():测试日志方法;IP:0:0:0:0:0:0:0:1;异常:/by zero

从以上打印结果可以看出,执行logtest()方法以及SystemLogAspect类的增强代码块的先后循序如下:

(1)SystemLogAspect.doBefore()

(2)LogTesterController.logtest()

(3)SystemLogAspect.doError()

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Java技术集锦

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值