您的位置:首页 > 编程语言

AOP那点事儿:面向切面编程(1)

2016-05-13 12:30 309 查看
今天我要和大家分享的是 AOP(Aspect-Oriented Programming)这个东西,名字与 OOP 仅差一个字母,其实它是对 OOP 编程方式的一种补充,并非是取而代之。翻译过来就是“面向方面编程”,可我更倾向于翻译为“面向切面编程”。它听起有些的神秘,为什么呢?当你看完这篇文章的时候,就就知道,我们做的很重要的工作就是去写这个“切面”。那么什么是“切面”呢?

没错!就是用一把刀来切一坨面。注意,相对于面而言,我们一定是横着来切它,这简称为“横切”。可以把一段代码想象成一坨面,同样也可以用一把刀来横切它,下面要做的就是如何去实现这把刀!

需要澄清的是,这个概念不是由 Rod Johnson(老罗)提出的。其实很早以前就有了,目前最知名最强大的 Java 开源项目就是 AspectJ 了,然而它的前身是 AspectWerkz(该项目已经在 2005 年停止更新),这才是 AOP 的老祖宗。老罗(一个头发秃得和我老爸有一拼的天才)写了一个叫做 Spring 框架,从此一炮走红,成为了 Spring 之父。他在自己的 IOC 的基础之上,又实现了一套 AOP 的框架,后来仿佛发现自己越来越走进深渊里,在不能自拔的时候,有人建议他还是集成 AspectJ
吧,他在万般无奈之下才接受了该建议。于是,我们现在用得最多的想必就是 Spring + AspectJ 这种 AOP 框架了。

那么 AOP 到底是什么?如何去使用它?本文将逐步带您进入 AOP 的世界,让您感受到前所未有的畅快!

不过在开始讲解 AOP 之前,我想有必要回忆一下这段代码:

1. 写死代码

先来一个接口:

public interface Greeting {  
 
    void sayHello(String name);  


还有一个实现类:

public class GreetingImpl implements Greeting {  
 
    @Override 
    public void sayHello(String name) {  
        before();  
        System.out.println("Hello! " + name);  
        after();  
    }  
 
    private void before() {  
        System.out.println("Before");  
    }  
 
    private void after() {  
        System.out.println("After");  
    }  


before() 与 after() 方法写死在 sayHello() 方法体中了,这样的代码的味道非常不好。如果哪位仁兄大量写了这样的代码,肯定要被你的架构师骂个够呛。

比如:我们要统计每个方法的执行时间,以对性能作出评估,那是不是要在每个方法的一头一尾都做点手脚呢?

再比如:我们要写一个 JDBC 程序,那是不是也要在方法的开头去连接数据库,方法的末尾去关闭数据库连接呢?

这样的代码只会把程序员累死,把架构师气死!

一定要想办法对上面的代码进行重构,首先给出三个解决方案:

2. 静态代理

最简单的解决方案就是使用静态代理模式了,我们单独为 GreetingImpl 这个类写一个代理类:

public class GreetingProxy implements Greeting {  
 
    private GreetingImpl greetingImpl;  
 
    public GreetingProxy(GreetingImpl greetingImpl) {  
        this.greetingImpl = greetingImpl;  
    }  
 
    @Override 
    public void sayHello(String name) {  
        before();  
        greetingImpl.sayHello(name);  
        after();  
    }  
 
    private void before() {  
        System.out.println("Before");  
    }  
 
    private void after() {  
        System.out.println("After");  
    }  


就用这个 GreetingProxy 去代理 GreetingImpl,下面看看客户端如何来调用:

public class Client {  
 
    public static void main(String[] args) {  
        Greeting greetingProxy = new GreetingProxy(new GreetingImpl());  
        greetingProxy.sayHello("Jack");  
    }  


这样写没错,但是有个问题,XxxProxy 这样的类会越来越多,如何才能将这些代理类尽可能减少呢?最好只有一个代理类。

这是我们就需要使用 JDK 提供的动态代理了。 

3. JDK 动态代理

public class JDKDynamicProxy implements InvocationHandler {  
 
    private Object target;  
 
    public JDKDynamicProxy(Object target) {  
        this.target = target;  
    }  
 
    @SuppressWarnings("unchecked")  
    public <T> T getProxy() {  
        return (T) Proxy.newProxyInstance(  
            target.getClass().getClassLoader(),  
            target.getClass().getInterfaces(),  
            this 
        );  
    }  
 
    @Override 
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {  
        before();  
        Object result = method.invoke(target, args);  
        after();  
        return result;  
    }  
 
    private void before() {  
        System.out.println("Before");  
    }  
 
    private void after() {  
        System.out.println("After");  
    }  


客户端是这样调用的:

public class Client {  
 
    public static void main(String[] args) {  
        Greeting greeting = new JDKDynamicProxy(new GreetingImpl()).getProxy();  
        greeting.sayHello("Jack");  
    }  


这样所有的代理类都合并到动态代理类中了,但这样做仍然存在一个问题:JDK 给我们提供的动态代理只能代理接口,而不能代理没有接口的类。有什么方法可以解决呢?

4. CGLib 动态代理

我们使用开源的 CGLib 类库可以代理没有接口的类,这样就弥补了 JDK 的不足。CGLib 动态代理类是这样玩的:

public class CGLibDynamicProxy implements MethodInterceptor {  
 
    private static CGLibDynamicProxy instance = new CGLibDynamicProxy();  
 
    private CGLibDynamicProxy() {  
    }  
 
    public static CGLibDynamicProxy getInstance() {  
        return instance;  
    }  
 
    @SuppressWarnings("unchecked")  
    public <T> T getProxy(Class<T> cls) {  
        return (T) Enhancer.create(cls, this);  
    }  
 
    @Override 
    public Object intercept(Object target, Method method, Object[] args, MethodProxy proxy) throws Throwable {  
        before();  
        Object result = proxy.invokeSuper(target, args);  
        after();  
        return result;  
    }  
 
    private void before() {  
        System.out.println("Before");  
    }  
 
    private void after() {  
        System.out.println("After");  
    }  


以上代码中了 Singleton 模式,那么客户端调用也更加轻松了:

public class Client {  
 
    public static void main(String[] args) {  
        Greeting greeting = CGLibDynamicProxy.getInstance().getProxy(GreetingImpl.class);  
        greeting.sayHello("Jack");  
    }  


到此为止,我们能做的都做了,问题似乎全部都解决了。但事情总不会那么完美,而我们一定要追求完美!

老罗搞出了一个 AOP 框架,能否做到完美而优雅呢?请大家继续往下看吧!

1 2

3
下一页>>查看全文

内容导航

 第 1 页:写死代码 静态(动态)代理
 第 2 页:Spring AOP:前置增强、后置增强、环绕增强
 第 3 页:Spring AOP:抛出增强
原文:AOP那点事儿:面向切面编程(1)
返回开发首页
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: