热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

5.2从一个输出日志的实例分析Java的代理机制

5.2从一个输出日志的实例分析Java的代理机制上面讲到,要了解Spring的AOP,先来了解Java的代理机制。本节主要通过一个输出日志的实例来分析

 

5.2  从一个输出日志的实例分析Java的代理机制

上面讲到,要了解Spring的AOP,先来了解Java的代理机制。本节主要通过一个输出日志的实例来分析Java的代理机制。首先介绍以前写日志的时候是怎么实现的,然后讲解使用Java的代理机制怎么实现日志的输出,接着讲解怎样通过Java的动态代理机制把这个日志输出改成通用的,最后引出AOP的几个关键点。

5.2.1  通用的日志输出方法

在笔者使用Spring以前开发的程序中,不管是使用Java自动的日志工具,还是使用Log4j,或是自己编写的日志工具,都要在每一个业务逻辑方法里编写记录日志的代码。使用AOP就可以使业务逻辑和记录日志这两件事情分离开。这个输出日志实例的实现思路是:首先给出原来在程序中编写日志的方法,然后编写测试程序,查看输出结果,最后对这种方法进行总结,指出这种方法的缺点。具体编写步骤如下:

(1)打开Eclipse,在com.gc.action包中建立一个Java文件TimeBook.java,用来模拟实际业务中考勤审核的业务逻辑。

(2)原来在程序中编写日志时,都要在每一个业务逻辑方法里编写记录日志的代码。TimeBook.java的示例代码如下:

//******* TimeBook.java**************

import org.apache.log4j.Level;

import org.apache.log4j.Logger;

public class TimeBook {

         private Logger logger = Logger.getLogger(this.getClass().getName());

         //审核数据的相关程序

         public void doAuditing(String name) {

                  logger.log(Level.INFO, name + " 开始审核数据....");

                  //审核数据的相关程序

                  ……   

                  logger.log(Level.INFO, name + " 审核数据结束....");

         }

}

代码说明:

  ●       在业务逻辑中使用log4j作为日志输出的工具。

  ●       doAuditing()方法用来处理实际业务中的考勤审核。

  ●       参数name,用来传入是谁执行了类TimeBook中的doAuditing()方法。

  ●       在审核代码的前后添加了用logger.log()方法实现日志输出的功能。

(3)编写测试程序,继续在以前的测试程序TestHelloWorld的基础上进行修改,TestHelloWorld.java的示例代码如下:

//******* TestHelloWorld.java**************

package com.gc.test;

import com.gc.action.TimeBook;

public class TestHelloWorld {

         public static void main(String[] args) {

                   TimeBook timeBook = new TimeBook();

timeBook.doAuditing("张三");

    }

}

代码说明:timeBook.doAuditing("张三")表示该程序的执行人是“张三”。

(4)运行测试程序,查看通过TimeBook类输出的日志信息,如图5.1所示。

图5.1  通过TimeBook类输出日志信息

在上面的示例中,笔者把日志信息添加在了具体的业务逻辑中,假如程序中其他的代码都需要日志输出的功能,那么每个程序就都要添加和上面类似的代码。这样,在程序中,就会存在很多类似的日志输出代码,造成了很大的耦合,通过什么方法可以使业务逻辑和输出日志的代码分离呢?通过面向接口编程可以改进这个问题。

5.2.2  通过面向接口编程实现日志输出

通过前面的示例程序,读者可以了解到以前添加日志信息方法的缺点。下面主要通过面向接口编程来改进这个缺点。其实现思路是:首先把执行考勤审核的doAuditing()方法提取出来成为接口,然后通过一个实体类来实现这个方法,在这个方法里编写具体的考勤审核的业务逻辑,接着通过一个代理类来进行日志输出,最后编写测试程序,查看输出结果。具体步骤如下:

(1)在com.gc.impl包中,建立一个接口TimeBookInterface。TimeBookInterface.java的示例代码如下:

//******* TimeBookInterface.java**************

package com.gc.impl;

import org.apache.log4j.Level;

//通过面向接口编程实现日志输出

public interface TimeBookInterface {

         public void doAuditing(String name);

}

(2)在com.gc.action包中,使前面已经建立好的类TimeBook实现接口TimeBookInterface,在doAuditing()方法中编写具体的考勤审核代码。TimeBook.java的示例代码如下:

//******* TimeBook.java**************

package com.gc.action;

import com.gc.impl.TimeBookInterface;

public class TimeBook implements TimeBookInterface {

public void doAuditing(String name) {

//审核数据的相关程序

……   

}

}

(3)编写一个代理类,用来实现日志的输出,在该类中针对前面的接口TimeBookInterface编程,而不针对具体的类,从而实现具体业务逻辑与日志输出代码。TimeBookProxy.java的示例代码如下:

//******* TimeBookProxy.java**************

package com.gc.action;

import org.apache.log4j.Level;

import org.apache.log4j.Logger;

import com.gc.impl.TimeBookInterface;

public class TimeBookProxy {

private Logger logger = Logger.getLogger(this.getClass().getName());

private TimeBookInterface timeBookInterface;

     //在该类中针对前面的接口TimeBookInterface编程,而不针对具体的类

     public TimeBookProxy(TimeBookInterface timeBookInterface) {

        this.timeBookInterface = timeBookInterface;

     }

          //实际业务处理

          public void doAuditing(String name) {

                   logger.log(Level.INFO, name + " 开始审核数据....");

                   timeBookInterface.doAuditing(name);   

                   logger.log(Level.INFO, name + " 审核数据结束....");

          }

}

(4)修改测试程序TestHelloWorld,把类TimeBook当作参数传入代理类TimeBookProxy中,从而实现对具体负责考勤审核类TimeBook的调用。TestHelloWorld.java的示例代码如下:

//******* TestHelloWorld.java**************

package com.gc.test;

import com.gc.action.TimeBook;

import com.gc.action.TimeBookProxy;

public class TestHelloWorld {

         public static void main(String[ ] args) {

           //这里针对接口进行编程

TimeBookProxy timeBookProxy  = new TimeBookProxy(new TimeBook());

timeBookProxy .doAuditing("张三");

    }

}

(5)运行测试程序,可以得到通过TimeBookProxy类输出日志信息,如图5.2所示。

图5.2  通过TimeBookProxy类输出日志信息

和前面一个日志输出做对比,可以看到,在这个示例中,具体负责考勤审核的业务逻辑代码和日志信息的代码分离开了,并且以后只要实现了接口TimeBookInterface的类,都可以通过代理类TimeBookProxy实现日志信息的输出,而不用再每个类里面都写日志信息输出的代码,从而实现了日志信息的代码重用。

5.2.3  使用Java的代理机制进行日志输出

前面的代码虽然有了一些改进,但是仍然有一定局限性,因为要使用代理类,就必须要实现固定的接口,有没有一种通用的机制,不管是不是实现这个接口,都可以实现日志信息的输出呢?

Java提供的InvocationHandler接口可以实现这种功能,首先编写一个日志信息的代理类,这个代理类实现了接口InvocationHandler,然后和前面一个实例类似,编写一个接口,并实现这个接口,在实现类中编写具体的考勤审核代码,最后针对接口编写测试类,查看测试结果。具体步骤如下:

(1)编写一个日志信息的代理类LogProxy,这个代理类实现了接口InvocationHandler,可以对任何接口实现日志信息的输出。LogProxy.java的示例代码如下:

//******* LogProxy.java**************

package com.gc.action;

import java.lang.reflect.InvocationHandler;

import java.lang.reflect.Method;

import java.lang.reflect.Proxy;

import org.apache.log4j.Level;

import org.apache.log4j.Logger;

//代理类实现了接口InvocationHandler

public class LogProxy implements InvocationHandler {

         private Logger logger = Logger.getLogger(this.getClass().getName());

    private Object delegate;

    //绑定代理对象

    public Object bind(Object delegate) {

        this.delegate = delegate;

        return Proxy.newProxyInstance(delegate.getClass().getClassLoader(), delegate.getClass().
getInterfaces(), this);

    }

    //针对接口编程

    public Object invoke(Object proxy, Method method, Object[ ] args) throws Throwable {

        Object result = null;

        try {

                            //在方法调用前后进行日志输出

logger.log(Level.INFO, args[0] + " 开始审核数据....");

              result = method.invoke(delegate, args);

                            logger.log(Level.INFO, args[0] + " 审核数据结束....");

        } catch (Exception e){

            logger.log(Level.INFO, e.toString());

        }

        return result;

    }

}

(2)使用com.gc.impl包中的接口TimeBookInterface。TimeBookInterface.java的示例代码如下:

//******* TimeBookInterface.java**************

package com.gc.impl;

import org.apache.log4j.Level;

//针对接口编程

public interface TimeBookInterface {

         public void doAuditing(String name);

}

(3)使用com.gc.action包中的类TimeBook,doAuditing()方法中编写具体的考勤审核代码。TimeBook.java的示例代码如下:

//******* TimeBook.java**************

package com.gc.action;

import com.gc.impl.TimeBookInterface;

public class TimeBook implements TimeBookInterface {

public void doAuditing(String name) {

//审核数据的相关程序

……   

}

}

(4)修改测试代码TestHelloWorld,使用日志代理类LogProxy 实现日志的输出。TestHelloWorld.java的示例代码如下:

//******* TestHelloWorld.java**************

package com.gc.test;

import com.gc.action.TimeBook;

import com.gc.action.TimeBookProxy;

import com.gc.impl.TimeBookInterface;

import com.gc.action.LogProxy;

public class TestHelloWorld {

         public static void main(String[ ] args) {

           //实现了对日志类的重用

           LogProxy logProxy  = new LogProxy();

TimeBookInterface timeBookProxy = (TimeBookInterface)logProxy.bind(new TimeBook());

timeBookProxy.doAuditing("张三");

    }

}

(5)运行测试程序,可以得到通过LogProxy类输出日志信息,如图5.3所示。

图5.3  通过LogProxy类输出日志信息

这种方式,对于其他的类也同样适用,这样就真正地实现了业务逻辑和输出日志信息代码的分离。

5.2.4  对这3种实现方式进行总结

第一种方式,需要在每个类里都增加对输出日志信息的代码;第二种方式,虽然实现了业务逻辑与输出日志信息代码的分离,但还是必须依赖于固定的接口;第三种方式,真正实现了对输出日志信息代码的重用,并且不依赖于固定的接口实现。

从第三种方式中,也可以看出Java动态代理机制的强大,而Spring的AOP正是建立在Java动态代理的基础上的,当读者通过上面的示例一步一步地对Java的动态代理机制有了一定的了解后,接下来就可以逐渐地进入AOP了。


推荐阅读
  • Java如何导入和导出Excel文件的方法和步骤详解
    本文详细介绍了在SpringBoot中使用Java导入和导出Excel文件的方法和步骤,包括添加操作Excel的依赖、自定义注解等。文章还提供了示例代码,并将代码上传至GitHub供访问。 ... [详细]
  • 利用Dubbo的SPI扩展容器
    在实际开发中,很多Dubbo的服务提供者都会运行在web容器上,如果提供者服务上同时对外 ... [详细]
  • Java太阳系小游戏分析和源码详解
    本文介绍了一个基于Java的太阳系小游戏的分析和源码详解。通过对面向对象的知识的学习和实践,作者实现了太阳系各行星绕太阳转的效果。文章详细介绍了游戏的设计思路和源码结构,包括工具类、常量、图片加载、面板等。通过这个小游戏的制作,读者可以巩固和应用所学的知识,如类的继承、方法的重载与重写、多态和封装等。 ... [详细]
  • Java自带的观察者模式及实现方法详解
    本文介绍了Java自带的观察者模式,包括Observer和Observable对象的定义和使用方法。通过添加观察者和设置内部标志位,当被观察者中的事件发生变化时,通知观察者对象并执行相应的操作。实现观察者模式非常简单,只需继承Observable类和实现Observer接口即可。详情请参考Java官方api文档。 ... [详细]
  • Spring学习(4):Spring管理对象之间的关联关系
    本文是关于Spring学习的第四篇文章,讲述了Spring框架中管理对象之间的关联关系。文章介绍了MessageService类和MessagePrinter类的实现,并解释了它们之间的关联关系。通过学习本文,读者可以了解Spring框架中对象之间的关联关系的概念和实现方式。 ... [详细]
  • 面向对象之3:封装的总结及实现方法
    本文总结了面向对象中封装的概念和好处,以及在Java中如何实现封装。封装是将过程和数据用一个外壳隐藏起来,只能通过提供的接口进行访问。适当的封装可以提高程序的理解性和维护性,增强程序的安全性。在Java中,封装可以通过将属性私有化并使用权限修饰符来实现,同时可以通过方法来访问属性并加入限制条件。 ... [详细]
  • 大数据Hadoop生态(20)MapReduce框架原理OutputFormat的开发笔记
    本文介绍了大数据Hadoop生态(20)MapReduce框架原理OutputFormat的开发笔记,包括outputFormat接口实现类、自定义outputFormat步骤和案例。案例中将包含nty的日志输出到nty.log文件,其他日志输出到other.log文件。同时提供了一些相关网址供参考。 ... [详细]
  • SpringBoot简单日志配置
     在生产环境中,只打印error级别的错误,在测试环境中,可以调成debugapplication.properties文件##默认使用logbacklogging.level.r ... [详细]
  • java日志框架详解
    Java日志框架详解1.常用日志框架1.1Java常用日志框架类别1.2Java常用日志框架历史1.3两大日志接口阵营1.3.1基于CommonsLogging接口实现的常用日志框 ... [详细]
  • log4j相关
    Log4j的类图Logger-日志写出器,供程序员输出日志信息Appender-日志目的地,把格式化好的日志信息输出到指定的地方去ConsoleAppe ... [详细]
  • -1{baos.write;baos.flush(;}responseDatabaos.toByteArray(;}catch{thrownewIO ... [详细]
  • springboot项目组引入JMeter的实现步骤
    本文主要介绍了springboot项目组引入JMeter的实现步骤,文中通过示例代码介绍的非常详细,具有一定的参考价值,感兴趣的 ... [详细]
  • import?java.io.IOException;import?java.io.InputStream;import?java.util.Properties;impor ... [详细]
  • 开发笔记:MyBatis03:ResultMap及分页
    篇首语:本文由编程笔记#小编为大家整理,主要介绍了MyBatis03:ResultMap及分页相关的知识,希望对你有一定的参考价值。 ... [详细]
  • 本文介绍了如何在给定的有序字符序列中插入新字符,并保持序列的有序性。通过示例代码演示了插入过程,以及插入后的字符序列。 ... [详细]
author-avatar
Mr_JJwonG05
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有