关于SpringAOP的初步认识(个人理解)

PS:结合《Spring从入门到精通》(郭峰)这本书,谈谈对AOP
的一个初步认识,也向刚开始学习Spring的盆友推荐这本书,写的特别的好,适合初学者。
以下所有源代码均来自《Spring从入门到精通》这本书。

5.1 认识AOP
提到AOP,不得不说耦合性这个专业词语,耦合性就是使两种作用类型代码分离的理想,比如业务层和逻辑层,如果对耦合性不是特别的了解,可以了解一下Java基础部分中的Porxy代理模式,后续我可能也会对面向对象编程进行单元性的总结。
言归正传,AOP,从字面理解就是面向方面编程,不同于OOP(面向对象编程),举个例子来解释一下AOP的概念:基本上每个方法都要用日志进行记录(小程序一般不需要日志,大的项目为了方便以后的维护,需要写日志),如果按照面向对象的思想,就是每个对象都要有记录日志的这样一个行为,但是在每个方法中加入重复的日志代码,不符合基本的编程思想,所以在 AOP中,可以将记录日志看作是一个横切面,所有对这些方法的调用都要经过这个横切面,然后再这个横切面进行记录日志的操作,这样代码的重用率就非常高,这就是AOP的基本思想。

OOP与AOP的对比
一个应用程序分为核心关注点和横切关注点。核心关注点和具体应用的功能相关,而横切关注点存在于整个系统的范围内。

5.2 从一个输出日志的实例分析Java的代理机制
5.21 通用的日志输出方法

package Test;

import java.util.logging.Level;
import java.util.logging.Logger;

public class TimeBook implements TimeBookInterface{
    private Logger logger = Logger.getLogger(this.getClass().getName());
    public void doAuditing(String name){
        logger.log(Level.INFO, name + "开始审核数据。。");
        //审核数据的相关程序

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

代码说明:
1.在业务逻辑中log4j作为日志的输出工具。
2.doAuditing方法用来处理实际业务中的实现方法(现在他们是没有分开的,耦合度很高)。
3.参数name,用来传入是谁执行了用logger.log()方法来实现日志输出的功能。
调用logger.log()实现日志的输出功能。
编写测试程序:

package Test;

public class Test {

    public static void main(String[] args) {
        TimeBook timebook = new TimeBook();
        timebook.doAuditing("张三");
    }

}

上面的代码实现了日志输出的功能,但是多个方法就需要在每个方法中编写多个重复的日志输出语句,非常麻烦,下面就用OOP的思想来编写日志输出。

5.2.2 通过面相接口编程实现日志输出
用OOP实现的思路就是:首先把执行的方法doAuditing()作为一个接口,下面用具体的实现类来实现它。
首先定义接口:

package Test;

public interface TimeBookInterface {
    public void doAuditing(String name);
}

然后是具体的实现类TimeBook :

package Test;

import java.util.logging.Level;
import java.util.logging.Logger;

public class TimeBook implements TimeBookInterface{
    public void doAuditing(String name){
        //审核数据的相关程序
        ....
    }
}

编写一个代理类TimeBookProxy,用来实现日志的输出,在该类中针对前面的接口TimeBookInterface编程,而不是针对具体的类。

package Test;

import java.util.logging.Level;
import java.util.logging.Logger;

public class TimeBookProxy {
    private Logger logger = Logger.getLogger(this.getClass().getName());
    private TimeBookInterface tbi;
    //该类中针对前面的接口TimeBookInterface变成,而不是具体的类
    public TimeBookProxy(TimeBookInterface tbi) {
        this.tbi = tbi;
    }
    //实际业务处理
    public void doAuditing(String name){
    logger.log(Level.INFO, name + "开始审核数据。。");
    //审核数据的相关程序

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

修改测试程序,把类TimeBook当作参数传入代理类TimeBookProxy 中,从而实现对具体负责审核类TimeBook的调用:

package Test;

public class Test {

    public static void main(String[] args) {
        //针对接口进行编程
        TimeBookProxy tbp = new TimeBookProxy(new TimeBook());
        tbp.doAuditing("张三");
    }

}

和前面的对比,使用这种模式(在Java基础中叫做静态代理模式),把重用率很高的代码拿出来,用接口的实现类来编写。但是如果我不仅仅希望有人帮我把审计(auditing)的工作做了,我还希望有人帮我做其他的工作(比如翻译之类的),如果项目上线了,我怎么才能尽可能的少修改代码呢? 下面是用AOP来解决。
5.2.3 使用Java的代理机制(动态代理)进行日志的输出
上面说到,我希望还有别的事情,也交给其他人去做,现在我需要一个类似于专门服务的家政公司。
Java提供的InvocationHandler接口可以实现这个功能,首先编写一个日志信息的代理类,实现接口InvocationHandler,然后和前面一个实例类似,编写一个接口,并且实现这个接口,在实现类中编写具体的考勤审核代码,最后编写测试类。
一个“家政公司”,LogProxy类,实现InvocationHandler,可以实现对任何接口实现日志信息的输出,即不光可以用人做审计,还可以实现让人做翻译。

package Test;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.logging.Level;
import java.util.logging.Logger;

public class LogProxy implements InvocationHandler{
    private Logger logger = Logger.getLogger(this.getClass().getName());
    private Object obj;
    //绑定特定的代理对象(代理什么都行,参考之前的动态代理代码,有化妆品代理,金融代理,看前台穿过来哪一个)
    public Object bind(Object obj){
        this.obj = obj;
        return Proxy.newProxyInstance(obj.getClass().getClassLoader(), obj.getClass().getInterfaces(), (InvocationHandler) this);

    }
    //针对接口编程
    public Object invoke(Object proxy, Method method, Object[] args){
        Object result = null;

        try {
            logger.log(Level.INFO, args[0]+"开始审核数据。。。");
            result = method.invoke(obj, args);
            logger.log(Level.INFO, args[0]+"审核结束");
        } catch (IllegalAccessException | IllegalArgumentException | InvocationTargetException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
            logger.log(Level.INFO, e.toString());
        }
        return result;

    }
}

一个接口TimeBookInterface

package Test;

public interface TimeBookInterface {
    public void doAuditing(String name);
}

一个具体的实现类TimeBook:

package Test;

import java.util.logging.Level;
import java.util.logging.Logger;

public class TimeBook implements TimeBookInterface{
    public void doAuditing(String name){
        //审核数据的相关程序
    }
}

然后是实现类

package Test;

public class Test {

    public static void main(String[] args) {
        //实现了对日志类(LogProxy)的重用
        LogProxy logproxy = new LogProxy();
        TimeBookInterface tbi = (TimeBookInterface) logproxy.bind(new TimeBook());
        tbi.doAuditing("张三");
    }

}

这三种是同样的输出结果,但是很明显第三种的耦合性最低。

********************************************************
以上所有观点只是我个人的理解,不能保证全部的正确,也希望有更深更正确理解的朋友可以指出来我的错误,在此先谢过了!

你可能感兴趣的:(关于SpringAOP的初步认识(个人理解))