>>分享孙卫琴的Java技术专稿和著作 书籍支持  卫琴直播  品书摘要  在线测试  资源下载  联系我们
发表一个新主题 开启一个新投票 回复文章 您是本文章第 13037 个阅读者 刷新本主题
 * 贴子主题:  【Spring专题】用AOP和SLF4J输出日志的范例 回复文章 点赞(0)  收藏  
作者:sunweiqin    发表时间:2021-10-09 11:39:39     消息  查看  搜索  好友  邮件  复制  引用

本文参考《精通Spring:JavaWeb开发技术详解》,作者:孙卫琴,清华大学出版社出版

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属性,指定原始方法的描述信息
  String desc() default "无描述信息";
}

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

例程2 LogTesterController.java

@Controller
public class LogTesterController{
  @RequestMapping("/logtest")
  @LogAnnotation(desc="logtest():测试日志方法")
  public String logtest(
            @RequestParam(required=false,defaultValue="1")int num,
            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 {
  //日志记录对象
  private static final Logger logger = LoggerFactory
        .getLogger(SystemLogAspect.class);

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

  /** 声明原始方法执行前的增强代码块  */
  @Before(value = "logPointCut()")
  public void doBefore(JoinPoint joinPoint) {
    System.out.println("from SystemLogAspect.doBefore()");
  }

  /** 声明原始方法正常退出后的增强代码块  */
  @AfterReturning(value = "logPointCut()")
  public void doAfter(JoinPoint joinPoint) {
   System.out.println("from SystemLogAspect.doAfter()");

   handleLog(joinPoint, null);
  }

  /** 声明原始方法出现异常时的增强代码块 */
  @AfterThrowing(value = "logPointCut()",throwing = "e")
  public void doError(JoinPoint joinPoint, Exception e) {
    System.out.println("from SystemLogAspect.doError()");
    handleLog(joinPoint, e);
  }

  /** 输出日志*/
  private void handleLog(JoinPoint joinPoint,Exception e) {
    //从joinPoint获得LogAnnotation注解
   LogAnnotation logAnnotation = getLogAnnotation(joinPoint);

   if(logAnnotation == null)
      return;

   HttpServletRequest request =
          ((ServletRequestAttributes) RequestContextHolder
          .getRequestAttributes())
          .getRequest();

    String ip=request.getRemoteAddr();

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

    if(e==null)
      logger.info(desc+";IP:"+ip);
    else
      logger.error(desc+";IP:"+ip+";异常:"+e.getMessage());
  }

  /** 获得原始方法的LogAnnotation注解*/
  private static LogAnnotation getLogAnnotation(JoinPoint joinPoint) {

    //获得连接点的原始方法签名
   MethodSignature methodSignature =
              (MethodSignature) joinPoint.getSignature();

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

    if(method!= null)
      return method.getAnnotation(LogAnnotation.class);
    else
      return null;
  }
}

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&#8203;

从以上打印结果可以看出,执行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()


程序猿的技术大观园:www.javathinker.net
  Java面向对象编程-->数据类型
  JavaWeb开发-->访问数据库(Ⅱ)
  JSP与Hibernate开发-->通过JPA API检索数据
  Java网络编程-->RMI框架
  精通Spring-->绑定CSS样式
  Vue3开发-->Vue Router路由管理器
  【Vue.js技术专题】插槽slot的基本用法
  【Vue.js技术专题】组件的递归
  【Vue.js技术专题】在Vue项目中使用Axios
  【Spring Cloud Alibaba专题】Seata的架构
  【Java基础编程专题】使用和创建JavaDoc文档
  【Spring专题】通过JPA API实现Repository接口
  【Spring专题】@Query和@Modifying注解的增删改操作
  【Vue专题】Vue3的计算属性实用范例:实现购物车
  【持久化专题】映射一对多双向关联关系
  【持久化专题】Hibernate的配置文件
  【持久化专题】FetchType.LAZY延迟检索策略
  【持久化专题】JPA的事件处理API的用法
  【持久化专题】通过JPA API调用存储过程
  【Java编程基础专题】用Scanner类读取用户在控制台输入的数据
  【Java基础编程专题】用static修饰符进行静态导入的作用和语...
  更多...
 IPIP: 已设置保密
楼主      
1页 0条记录 当前第1
发表一个新主题 开启一个新投票 回复文章


中文版权所有: JavaThinker技术网站 Copyright 2016-2026 沪ICP备16029593号-2
荟萃Java程序员智慧的结晶,分享交流Java前沿技术。  联系我们
如有技术文章涉及侵权,请与本站管理员联系。