>>分享孙卫琴的Java技术专稿和著作 书籍支持  卫琴直播  品书摘要  在线测试  资源下载  联系我们
发表一个新主题 开启一个新投票 回复文章 您是本文章第 13036 个阅读者 刷新本主题
 * 贴子主题:  【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面向对象编程-->Swing组件(上)
  JavaWeb开发-->开发JavaMail Web应用
  JSP与Hibernate开发-->立即检索和延迟检索策略
  Java网络编程-->XML数据处理
  精通Spring-->Vue组件开发高级技术
  Vue3开发-->CSS过渡和动画
  【Vue.js技术专题】Vue组件的数据监听
  【Vue.js技术专题】路由导航中抓取数据
  【Vue.js技术专题】组件的递归
  向小伙伴们推荐我所创作前后端编程书籍的学习顺序
  【持久化专题】为什么JPA和Hibernate的持久化方法都抛出运行...
  【持久化专题】@Access注解设定Hibernate访问类的属性的方式
  【持久化专题】Hibernate的配置文件
  【持久化专题】JPA API的基本用法
  【持久化专题】对象-关系的映射概念
  【持久化专题】FetchType.LAZY延迟检索策略
  【Java网络编程专题】用Apache HttpClients下载网上的图片等...
  【Java网络编程专题】用Java套接字访问HTTP服务器读取网页数...
  【持久化专题】比较JPA的EntityManager接口与Hibernate的Ses...
  【JavaWeb专题】在JavaWeb应用中对客户请求的异步处理
  【JavaWeb专题】选择32位或64位JDK或者Apache HTTP服务器的安...
  更多...
 IPIP: 已设置保密
树形列表:   
1页 0条记录 当前第1
发表一个新主题 开启一个新投票 回复文章


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