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("张三");
}
}
这三种是同样的输出结果,但是很明显第三种的耦合性最低。
【********************************************************】
以上所有观点只是我个人的理解,不能保证全部的正确,也希望有更深更正确理解的朋友可以指出来我的错误,在此先谢过了!