Aop的基本思想
开发人员在编写应用程序时,通常包含两种代码:一种是和业务系统有关系的代码,一种是和业务系统关系不大的代码,例如日志,事务处理,权限处理等等。以前的程序中,这两种代码是写在一起的。这样一来,程序中到处充满着相同或类似的代码,不利于维护。而Aop的目标就是使这两种代码分离解耦,这样程序员就能专心于业务的处理,而且达到维护和重用的目的。
拿日志记录这个例子来看。每个方法都需要进行日志记录,不使用Aop的时候,每个方法中都要包含日志记录的代码,这样就会产生重复,而且日志记录和业务代码紧紧耦合,不利于维护。使用AOP的思想,可以把日志看成是一个切面,所有需要日志记录的方法都要经过这个切面。这样就可以把日志记录的代码封装,当方法经过切面的时候执行,这样就实现了代码重用。
Aop和oop的关系
一个程序分为核心关注点和横切关注点,oop关注核心,aop关注横切。在aop中每个关注点并不知道是否有其他关注点关注它,这是oop和aop的区别。Oop和aop是互补的思想,而不是对立。
Aop和java的代理机制
Aop是一种思想,他和具体的实现技术无关。任何符合aop思想的技术实现都可以看做是Aop的实现。
Jdk 1.3之后java提供动态代理机制。通过java的动态代理可以轻松实现Aop。事实上spring的Aop也是建立在java的动态代理机制之上的。要理解spring,就要先理解java的动态代理机制。
通过日志记录的实例分析AOP思想
(Log4j下载地址列表:http://www.apache.org/dyn/closer.cgi/logging/log4j/1.2.17/log4j-1.2.17.zip )
//关于log4j的使用请参看本博客另一篇文章《log4j使用快速入门 》,配置文件和log4j.jar在本例源码中有。
日志记录的三种实现:
1.不使用aop思想的实现
/** * 客户端程序 * @author yanwushu * */ public class Browser { public static void main(String[] args) { new Register().register("张三"); } } |
/** * * 注册机类 * @author yanwushu * */ public class Register { //一个日志记录器成员 Logger logger = Logger.getLogger(this.getClass().getName()); /** * 注册方法 */ public void register(String name ){ logger.info(name+"开始审核"); System.out.println("正在注册..."); logger.info(name+"注册完毕"); } } |
这样的方式,每个需要日志记录服务的方法都必须重复logger.info()这样的代码,且这些和业务逻辑无关的代码和业务逻辑紧紧耦合不利于维护,让程序员不能集中精力在业务的处理上。
2.使用静态代理模式实现Aop来实现日志服务
/** * 客户端 * @author yanwushu * */ public class Browser { //使用静态代理模式实现Aop public static void main(String[] args) { Register register = new RegisterImpl(); RegisterProxy proxy = new RegisterProxy(register); proxy.register("张三"); } } |
/** * 注册机接口 * @author yanwushu * */ public interface Register { //定义注册服务 public void register(String name); } |
/** * 注册机实现类 * @author yanwushu * */ public class RegisterImpl implements Register { @Override public void register(String name) { System.out.println(name+"正在注册"); } } |
/** * 注册代理类 */ public class RegisterProxy { //一个日志记录器成员 Logger logger = Logger.getLogger(this.getClass().getName()); //一个注册接口的引用 private Register register; public RegisterProxy(Register register){ this.register = register; } public void register(String name){ logger.debug("正在审核...."); register.register(name); logger.debug("注册完毕"); } } |
这种实现的好处是把业务代码和与业务无关的记录日志的代码解耦。这样有利于代码的维护和程序员精力集中在业务的处理上。这样就已经实现了Aop的思想。在这里面向的切面就是日志记录这个横切关注点。不过使用静态代理的方式由一个坏处(也是静态代理模式的短板)就是当被代理类数量扩展的时候不得不扩展响应的代理类,进而造成代理类泛滥。
3.使用jdk 1.3之后的动态代理机制来实现Aop
/** * 客户端代码 * @author yanwushu * */ public class Browse { public static void main(String[] args) { //实例化日志代理类 LogProxy logProxy = new LogProxy(new RegisterImpl()); //动态获取代理实例 //参数:注册类的类装载器,被代理的接口,日志代理的实例 Register re = (Register)Proxy.newProxyInstance(RegisterImpl.class.getClassLoader(), RegisterImpl.class.getInterfaces(),logProxy); re.register("张三"); } } |
/** * 日志代理类,他可以代理一切需要日志输出服务的类 * 它实现了调用处理者接口 * @author yanwushu * */ public class LogProxy implements InvocationHandler{ //一个日志记录器成员 private Logger logger = Logger.getLogger(this.getClass().getName()); //被代理者成员 private Object beProxy; public LogProxy(Object beProxy){ this.beProxy = beProxy; } //实现调用方法 @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { Object result ; logger.debug("正在审核"); result = method.invoke(beProxy, args); logger.debug("注册完毕"); return result; } } |
/** * 注册机接口 * @author yanwushu * */ public interface Register { //定义注册服务 public void register(String name); } |
/** * 注册机实现类 * @author yanwushu * */ public class RegisterImpl implements Register { @Override public void register(String name) { System.out.println(name+"正在注册"); } } |
Java 1.3之后的动态代理机制(参看java.lang.reflect.Proxy,java.lang.reflect.InvocationHandler)可以动态生成代理类(内部使用java的反射机制,参看java.lang.reflect包)。这样就弥补了静态代理的先天不足。不用担心代理类泛滥了。