`
lee5593
  • 浏览: 57796 次
社区版块
存档分类
最新评论

Magic AOP:面向切面的业务日志框架设计,第二部分

阅读更多

本文是Magic AOP:面向切面的业务日志框架设计的第二部分,第一部分请见http://lee5593.iteye.com/admin/show/88163

3.1.   业务日志记录组件设计

业务日志记录组件的设计思路是通过事先定义好的业务类型和操作类型对业务方法进行两个维度的分类,利用BusinessLog注解对需要记录业务日志的业务方法进行声明,然后用AOP对声明了BusinessLog注解的业务方法进行拦截,取到注解中的参数组装业务日志消息对象,调用业务日志输出组件进行输出。

<o:p></o:p>

对一个业务方法进行业务方法进行声明的代码看起来象这样:

java 代码
  1. @BusinessLog(info="info message")   
  2.    public void anService(){…}   
  3.   
  4.    @BusinessLog(success="success message",failture="failture message",state=State.VALID)   
  5.   public void anotherService(){…}  

 对于业务日志信息中的会话信息(如用户标识、客户端机器标识),采用了ThreadLocal通过Filter获取后,直接在业务日志消息对象构造方法中进行赋值,以避免客户端开发人员手动编码。

我们推荐使用上述的方式来记录业务日志,当然你也可以将BnLog对象直接注入到你的业务对象中通过显式方法调用来记录业务日志,甚至可以直接拿到BnLogConfiguration对象来按需生成BnLog对象,一切取决于你的喜好,这里只是演示了一种Best Practice

<o:p></o:p>

业务日志记录组件中的主要接口和类清单如下:

BnLog<o:p></o:p>

     业务日志记录接口,包含了多个log方法

BnLogItem<o:p></o:p>

   业务日志对象,使用JPA进行数据库表映射,数据库相应的表名需要与对象类名相同。主要属性有业务类型、事件类型、详细信息、记录时间、用户标识、客户端机器标识、是否成功标识。如果扩展该业务日志对象,用JPA对扩展属性进行配置即可,表名需要与扩展对象类名相同。

BnLogConfiguration<o:p></o:p>

     业务日志配置类,主要负责初始化业务类型、事件类型和维护业务日志实例缓存池。初始化业务日志框架时,该类从外部的属性文件bnlog.properties中读取定义好的参数,然后分别对业务类型、事件类型以及业务日志消息对象进行初始化,bnlog.properties的格式定义如下:

java 代码
  1. #----------------------定制业务类型----------------------------------   
  2. #在这里定制业务类型,每个业务类型标识字符串必须是唯一的,且不区分大小写,不能超过50个字符,多个业务类型之间请用,进行分隔。   
  3. #默认内置general类型   
  4. bnlog.businessTypes=general   
  5.   
  6. #----------------------定制事件类型--------------------------   
  7. #在这里定制事件类型,每个事件类型标识字符串必须是唯一的,且不区分大小写,不能超过50个字符,多个事件类型之间请用,进行分隔。   
  8. #默认内置general类型   
  9. bnlog.eventTypes=general   
  10.   
  11. #-----------------------定制业务日志领域对象----------------------   
  12. #在这里定制自己扩展的业务日志领域对象,扩展的业务日志领域对象必须继承基类BnLogItem,并自行实现扩展属性的annotations。   
  13. #如果自行扩展业务日志领域对象,需要修改关系表结构脚本,使之与扩展的业务日志领域对象一致。   
  14. #默认为基类BnLogItem   
  15. bnlog.entityClass=com.relax.component.bnlog.BnLogItem  

主要的方法如下:

void init()<o:p></o:p>

   初始化方法<o:p></o:p>

<o:p></o:p>

void destory()<o:p></o:p>

     销毁方法<o:p></o:p>

<o:p></o:p>

void setBusinessTypes(String businessTypes)<o:p></o:p>

   设置业务类型  <o:p></o:p>

<o:p></o:p>

void setEventTypes(String eventTypes)<o:p></o:p>

   设置事件类型<o:p></o:p>

<o:p></o:p>

BnLog getBnLog(String name)<o:p></o:p>

   获取BnLog实例,所有的BnLog实例都从这里获取<o:p></o:p>

<o:p></o:p>

static boolean validateBusinessType(String businessType)<o:p></o:p>

验证业务类型<o:p></o:p>

<o:p></o:p>

static boolean validateEventType(String eventType)<o:p></o:p>

验证事件类型<o:p> </o:p>

BusinessLog<o:p></o:p>

   业务日志注解,用于提供方法级别的声明,配合AOP进行切面业务日志记录。<o:p></o:p>

<o:p>
java 代码
  1. @Retention(RetentionPolicy.RUNTIME)   
  2. @Target(ElementType.METHOD)   
  3. public @interface BusinessLog {   
  4.     /**  
  5.      * 业务类型,默认为内置general类型  
  6.      * @return general业务类型  
  7.      */  
  8.     String businessType() default BusinessType.GENERAL;   
  9.     /**  
  10.      * 事件类型,默认为内置general类型  
  11.      * @return general事件类型  
  12.      */  
  13.     String eventType() default EventType.GENERAL;   
  14.     /**  
  15.      * 当state状态为无效时的日志信息  
  16.      * @return 日志信息  
  17.      */  
  18.     String info() default "";   
  19.     /**  
  20.      * 当业务操作成功时的日志信息  
  21.      * @return 记录成功业务操作的日志信息  
  22.      */  
  23.     String success() default "";   
  24.     /**  
  25.      * 当业务操作失败时的日志信息  
  26.      * @return 记录失败业务操作的日志信息  
  27.      */  
  28.     String failture() default "";   
  29.     /**  
  30.      * 业务操作状态,有两种选项INVALID,VALID.
     
  31.      * 默认为INVALID  
  32.      * @return 业务操作状态  
  33.      */  
  34.     State state() default State.INVALID;   
  35. }  

 

State<o:p></o:p>

   业务操作状态枚举,配合BusinessLog注解协同工作。

java 代码
  1. public enum State {   
  2.     /**  
  3.      * 无效  
  4.      */  
  5.     INVALID,   
  6.     /**  
  7.      * 有效  
  8.      */  
  9.     VALID   
  10. }  

 

BnLogAspect<o:p></o:p>

   业务日志通用切面,采用@AspectJ风格,Spring配置文件中需引入下列元素来启用对@AspectJ的支持。<o:p></o:p>

java 代码
  1. @Aspect  
  2. public class BnLogAspect {   
  3.     private final Logger logger = Logger.getInstance(BnLogAspect.class);   
  4.   
  5.     private BnLog bnLog;   
  6.   
  7.     /**  
  8.      * 业务日志切入点:所有声明@BusinessLog的方法  
  9.      *   
  10.      */  
  11.     @Pointcut("@annotation(com.relax.component.bnlog.annotation.BusinessLog)")   
  12.     private void bnLogAnnotation() {   
  13.     }   
  14.   
  15.     /**  
  16.      * 业务操作正常返回后的业务日志切面  
  17.      *   
  18.      * @param businessLog 业务日志声明对象  
  19.      */  
  20.     @AfterReturning("bnLogAnnotation() && @annotation(businessLog)")   
  21.     public void afterReturning(BusinessLog businessLog) {   
  22.         // FIXME 正式系统应该将记录业务日志操作可能抛出的异常捕获进行处理,以避免业务日志出错对正常的业务逻辑造成干扰   
  23.         // this.log(businessLog, true);   
  24.   
  25.         try {   
  26.             this.log(businessLog, true);   
  27.         } catch (BnLogException e) {   
  28.             logger.error("业务日志切面抛出异常", e);   
  29.         }   
  30.     }   
  31.   
  32.     /**  
  33.      * 业务操作抛出异常后的业务日志切面  
  34.      *   
  35.      * @param businessLog 业务日志声明对象  
  36.      * @param e 业务方法抛出的异常  
  37.      * @throws Exception 将业务方法抛出的异常重新进行抛出  
  38.      */  
  39.     @AfterThrowing(pointcut = "bnLogAnnotation() && @annotation(businessLog)", throwing = "e")   
  40.     public void afterThrowing(BusinessLog businessLog, Exception e)   
  41.             throws Exception {   
  42.         // FIXME 正式系统应该将记录业务日志操作可能抛出的异常捕获进行处理,以避免业务日志出错对正常的业务逻辑造成干扰   
  43.         // this.log(businessLog, false);   
  44.   
  45.         try {   
  46.             this.log(businessLog, false);   
  47.         } catch (BnLogException ex) {   
  48.             logger.error("业务日志切面抛出异常", ex);   
  49.         }   
  50.         /*将异常重新抛出*/  
  51.         throw e;   
  52.     }   
  53.   
  54.     /**  
  55.      * 记录业务日志  
  56.      *   
  57.      * @param businessLog 业务日志声明对象  
  58.      * @param state 操作状态,true or false  
  59.      * @throws BnLogException 当业务日志记录失败时抛出业务日志异常  
  60.      */  
  61.     private void log(BusinessLog businessLog, boolean state)   
  62.             throws BnLogException {   
  63.         BnLogItem bnLogItem;   
  64.         bnLogItem = new BnLogItem();   
  65.         if (!BnLogConfiguration   
  66.                 .validateBusinessType(businessLog.businessType())) {   
  67.             throw new BnLogPreparationException("无效的业务类型:"  
  68.                     + businessLog.businessType());   
  69.         }   
  70.         bnLogItem.setBusinessType(businessLog.businessType());   
  71.         if (!BnLogConfiguration.validateEventType(businessLog.eventType())) {   
  72.             throw new BnLogPreparationException("无效的事件类型:"  
  73.                     + businessLog.eventType());   
  74.         }   
  75.         bnLogItem.setEventType(businessLog.eventType());   
  76.         if (State.INVALID == businessLog.state()) {   
  77.             if (businessLog.info().equals("")) {   
  78.                 throw new BnLogPreparationException(   
  79.                         "当state设置为INVALID时,info内容不能为空");   
  80.             }   
  81.             bnLogItem.setMessage(businessLog.info());   
  82.         } else {   
  83.             if (businessLog.success().equals("")   
  84.                     || businessLog.failture().equals("")) {   
  85.                 throw new BnLogPreparationException(   
  86.                         "当state设置为VALID时,success和failture内容不能为空");   
  87.             }   
  88.             if (state) {   
  89.                 bnLogItem.setMessage(businessLog.success());   
  90.             } else {   
  91.                 bnLogItem.setMessage(businessLog.failture());   
  92.             }   
  93.             bnLogItem.setState(state);   
  94.         }   
  95.   
  96.         bnLog.log(bnLogItem);   
  97.     }   
  98.   
  99.     /**  
  100.      * 设置业务日志记录对象  
  101.      * @param bnLog  
  102.      *            业务日志记录对象  
  103.      */  
  104.     public void setBnLog(BnLog bnLog) {   
  105.         this.bnLog = bnLog;   
  106.     }   
  107.   
  108. }  

3.2.   业务日志输出组件

相对于业务日志记录组件,业务日志输出组件要简单得多,我们采用了ActiveMq来对业务日志消息对象进行异步处理,以便隔离业务日志输出异常给业务操作带来的影响。你也可以不用异步处理机制,只需要实现Appender接口,重新编写一种实现来替换默认的输出机制。Appender接口非常简洁,其中只定义了一个doAppend()方法。

<o:p></o:p>

业务日志记录组件中的主要接口和类清单如下:

Appender<o:p></o:p>

     业务日志输出接口,定义了一个doAppend(BnLogItem bnLogItem)方法,实现类需实现该方法对业务日志消息对象进行输出

DefaultAppenderImpl<o:p></o:p>

     默认的业务日志输出类,实现了Appender接口,将业务日志持久化到数据库

<o:p></o:p>

BnLogMessageProducer<o:p></o:p>

     业务日志JMS消息生产者,实现了Appender接口

<o:p></o:p>

BnLogMessageConverter<o:p></o:p>

     业务日志消息转换类

<o:p></o:p>

BnLogMessageConsumer<o:p></o:p>

     业务日志消息消费者,接受注入的Appender对象对业务日志消息进行真正地输出

3.3.   业务日志查询组件

业务日志查询组件定义了一个BnLogQuery接口,包含了多个查询方法,接口定义如下:

java 代码
  1. public interface BnLogQuery {   
  2.   
  3.     public List<bnlogitem></bnlogitem> listAll();   
  4.   
  5.     public List<bnlogitem></bnlogitem> list(String businessType, String eventType);   
  6.   
  7.     public List<bnlogitem></bnlogitem> list(String businessType, String eventType,   
  8.             Boolean state);   
  9.   
  10.     public List<bnlogitem></bnlogitem> list(String businessType, String eventType,   
  11.             Boolean state, Date beginTime, Date endTime);   
  12.   
  13.     public List<bnlogitem></bnlogitem> list(String hql, Object... values);   
  14.   
  15.     public List<bnlogitem></bnlogitem> pageList(String hql, int pageNo, int pageSize,   
  16.             Object... values);   
  17.   
  18. }  

     该查询组件相对来说没有什么难度,本文不再予以详细描述。

4.   总结

    本文通过我们在实际企业级项目中应用AOP的实际经验,总结了通过AOP来进行业务日志框架设计的一些经验供大家参考,并希望借此起到抛砖引玉的效果,寻求更加优秀的AOP设计方案,使面向方面软件设计(AOSD)深入人心,来改善目前面向对象设计在某些特定问题领域的不足之处,让我们试目以待。

</o:p>
分享到:
评论
4 楼 liushilang 2013-09-10  
急需一个整体的DEMO,能否发个实例,谢谢8386513@qq.com
3 楼 zerozhengsi 2011-09-15  
文章不错,测试下
2 楼 kyk555 2008-06-02  
很不错的帖子,怎么没人来顶下呢,楼主辛苦了
1 楼 abcdzt 2007-06-30  
最近项目中需要实现日志,就找到这里了,读你的文章很是收益,思路也更加清晰了许多,能不能把你的实现给我发一,借鉴一下,email:summerwind62@hotmail.com

相关推荐

    spring-aop面向切面系统日志案例

    AOP是一种编程范式,它允许程序员定义“切面”,这些切面可以包含业务逻辑的多个部分,如方法调用前后的代码。在Spring框架中,切面通过定义“通知”(advice)和“切入点”(pointcut)来实现。通知是实际执行的...

    面向切面 aop

    2. **连接点(Join Point)**:程序执行过程中的某个特定点,如方法调用、异常处理等,是AOP框架能够插入切面的地方。 3. **通知(Advice)**:在特定连接点上执行的代码,即切面的具体实现。通知类型有前置通知、...

    spring AOP 切面日志 分层打日志

    在Spring框架中,AOP(面向切面编程)是一种强大的工具,它允许我们在不修改源代码的情况下,对程序进行横向关注点的插入,比如日志记录、事务管理、权限检查等。在这里,我们重点关注如何利用Spring AOP实现分层...

    Spring mvc mybatis plus 实现AOP 切面日志系统

    本项目“Spring MVC Mybatis Plus 实现AOP 切面日志系统”旨在提供一个基础的日志记录框架,能够自动追踪和记录应用程序的关键操作,同时支持数据持久化到数据库中,方便后期分析和问题排查。下面将详细介绍这些技术...

    aop:aspect

    标题 "aop:aspect" 指涉的是Spring框架中的面向切面编程(Aspect-Oriented Programming, AOP)的一个核心概念。在Spring AOP中,`aop:aspect`是XML配置中定义一个切面的元素,它允许我们将关注点(如日志、事务管理...

    AOP面向切面编程总结

    ### AOP面向切面编程详解 #### 一、AOP概览 AOP(Aspect-Oriented Programming,面向切面编程)是一种编程思想和技术,它作为OOP(面向对象编程)的一种补充,主要解决了OOP在处理横切关注点方面的不足。在传统的...

    spring使用动态代理面向切面编程(AOP) xml

    在Spring框架中,面向切面编程(AOP)是一种强大的设计模式,它允许开发者将关注点分离,将横切关注点(如日志、事务管理、权限检查等)与核心业务逻辑解耦。本篇文章将深入探讨如何使用Spring的动态代理机制实现AOP...

    AOP_使用spring框架进行面向切面编程

    面向切面编程(AOP)是一种编程范式,它旨在减少代码中的重复部分,特别是那些与核心业务逻辑无关但又必须处理的交叉关注点,如日志、事务管理、安全控制等。Spring框架是Java领域中实现AOP的常用工具,它通过提供...

    AOP(面向切面)的C#例子VS2012

    AOP为Aspect Oriented Programming的缩写,意为:面向切面编程(也叫面向方面),可以通过预编译方式和运行期动态代理实现在不修改源代码的情况下给程序动态统一添加功能的一种技术。AOP实际是GoF设计模式的延续,...

    Spring AOP面向切面三种实现

    在IT行业中,Spring框架是Java企业级应用开发的首选,其强大的功能之一就是AOP(面向切面编程)。本文将详细解析Spring AOP的三种实现方式,帮助你深入理解这一重要概念。 首先,理解AOP的基本概念至关重要。AOP是...

    AOP面向切面架构设计Demo

    面向切面编程(AOP,Aspect Oriented Programming)是一种编程范式,旨在将关注点分离,使得系统中的核心业务逻辑与横切关注点(如日志、事务管理、权限控制等)解耦。在传统的面向对象编程(OOP)中,这些横切关注...

    aop 面向切面编程 demo

    面向切面编程(AOP)是一种编程范式,它旨在将关注点分离,使得系统中的核心业务逻辑与系统服务(如日志、事务管理、权限控制等)可以解耦。在Android开发中,AOP的应用可以帮助我们更好地组织代码,提高可维护性和...

    揭秘AOP:切面编程的综合指南配套演示项目资源

    在软件开发中,面向切面编程(Aspect-Oriented Programming,简称AOP)是一种强大的设计模式,它允许我们把关注点分离到不同的切面,使得代码更加模块化,易于维护。Spring框架是AOP应用最广泛的平台之一,特别是...

    面向切面编程aop简介

    面向切面编程(AOP,Aspect Oriented Programming)是Spring框架的重要组成部分,它提供了一种在不修改原有业务代码的基础上,插入额外功能的编程模型。Spring AOP使得开发者能够更方便地实现如日志记录、事务管理、...

    Spring-aop面向切面编程实例

    面向切面编程(Aspect-Oriented Programming,AOP)是Spring框架的核心特性之一,它提供了一种优雅的方式来处理系统的横切关注点,如日志、事务管理、性能监控和权限控制等。在Spring中,AOP主要通过代理模式实现,...

    .net AOP 面向切面编程很好的例子

    spect Oriented Programming(AOP),面向切面编程,是一个比较热门...AOP主要实现的目的是针对业务处理过程中的切面进行提取,它所面对的是处理过程中的某个步骤或阶段,以获得逻辑过程中各部分之间低耦合性的隔离效果。

    AOP面向切面编程实例

    面向切面编程(Aspect-Oriented Programming,简称AOP)是一种编程范式,它旨在提高软件的模块化程度,通过将关注点分离到不同的“切面”中来实现。在.NET环境中,AOP通常通过拦截器(Interceptor)或动态代理...

    day39-Spring 06-Spring的AOP:带有切点的切面

    在Spring框架中,AOP(Aspect Oriented Programming,面向切面编程)是一种强大的设计模式,它允许程序员将关注点从核心业务逻辑中分离出来,如日志、事务管理等。在"day39-Spring 06-Spring的AOP:带有切点的切面...

    AOP面向切面编程.ppt

    AOP面向切面编程.ppt

Global site tag (gtag.js) - Google Analytics