初识AOP

AOP百科

AOP为Aspect Oriented Programming的缩写,意为:面向切面编程,通过预编译方式和运行期动态代理实现程序功能的统一维护的一种技术。AOP是OOP的延续,是软件开发中的一个热点,也是Spring框架中的一个重要内容,是函数式编程的一种衍生范型。利用AOP可以对业务逻辑的各个部分进行隔离,从而使得业务逻辑各部分之间的耦合度降低,提高程序的可重用性,同时提高了开发的效率。

为什么要代理模式

现在有一个IHello接口,一个Hello实现类如下,现在我们需要统计方法的耗时

package seven.com.seven.aop;

public interface IHello {

    void say();

}
public class Hello implements IHello {

    @Override
    public void say() {
        System.out.println("hello word");
    }
}
package seven.com;

import seven.com.seven.aop.Hello;
import seven.com.seven.aop.IHello;

public class App 
{
    public static void main( String[] args )
    {
        IHello hello=new Hello();
        hello.say();
    }
}

可以通过修改Hello类来完成

public class Hello implements IHello {

    @Override
    public void say() {
        long star = System.currentTimeMillis();
        System.out.println("hello word");
        long end = System.currentTimeMillis();
        System.out.println(String.format("method cost %s", (end - star)));
    }
}
缺点:

1、工作量特别大,如果项目中有多个类,多个方法,则要修改多次

2、违背了设计原则:开闭原则(OCP),对扩展开放,对修改关闭,而为了增加功能把每个方法都修改了,也不便于维护

3、违背了设计原则:单一职责(SRP),每个方法除了要完成自己本身的功能,还要计算耗时、延时;每一个方法引起它变化的原因就有多种

开闭原则

开闭原则(OCP)是面向对象设计中“可复用设计”的基石,是面向对象设计中最重要的原则之一,其它很多的设计原则都是实现开闭原则的一种手段。对于扩展是开放的,对于修改是关闭的,这意味着模块的行为是可以扩展的。当应用的需求改变时,我们可以对模块进行扩展,使其具有满足那些改变的新行为。也就是说,我们可以改变模块的功能。对模块行为进行扩展时,不必改动模块的源代码或者二进制代码。模块的二进制可执行版本,无论是可链接的库、DLL或者.EXE文件,都无需改动

单一职责原则

单一职责原则(SRP:Single responsibility principle)又称单一功能原则,面向对象五个基本原则(SOLID)之一。它规定一个类应该只有一个发生变化的原因。该原则由罗伯特·C·马丁(Robert C. Martin)于《敏捷软件开发:原则、模式和实践》一书中给出的。马丁表示此原则是基于汤姆·狄马克(Tom DeMarco)和Meilir Page-Jones的著作中的内聚性原则发展出的。
所谓职责是指类变化的原因。如果一个类有多于一个的动机被改变,那么这个类就具有多于一个的职责。而单一职责原则就是指一个类或者模块应该有且只有一个改变的原因。

静态代理

package seven.com.seven.aop;

public class HelloProxy implements IHello {

    private Hello target;

    public HelloProxy(Hello target) {
        this.target = target;
    }

    @Override
    public void say() {

        long star = System.currentTimeMillis();
        target.say();
        long end = System.currentTimeMillis();
        System.out.println(String.format("method cost %s", (end - star)));
    }
}
package seven.com;

import seven.com.seven.aop.Hello;
import seven.com.seven.aop.HelloProxy;
import seven.com.seven.aop.IHello;

public class App 
{
    public static void main( String[] args )
    {
        Hello hello=new Hello();
        IHello proxy=new HelloProxy(hello);
        proxy.say();
    }
}

动态代理

JDK 实现
package seven.com.seven.aop;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

public class DynamicJDKProxy  implements InvocationHandler {

    /**
     * 被代理对象
     */
    private Object target;

    /**
     * 通过JDK的Proxy动态创建代理对象
     * @param target
     * @return
     */
    public Object getProxy(Object target) {
        this.target = target;
        return Proxy.newProxyInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces(), this);
    }

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

        long star = System.currentTimeMillis();
        Object result = method.invoke(target, args);
        long end = System.currentTimeMillis();
        System.out.println(String.format("method cost %s", (end - star)));
        return result;
    }
}
package seven.com;

import seven.com.seven.aop.DynamicProxy;
import seven.com.seven.aop.Hello;
import seven.com.seven.aop.IHello;

public class App {
    public static void main(String[] args) {
        Hello hello = new Hello();
        IHello proxy = (IHello) new DynamicJDKProxy().getProxy(hello);
        proxy.say();
    }
}
CGLIb 实现
package seven.com.seven.aop;

import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;

import java.lang.reflect.Method;

public class DynamicCGLibProxy  implements MethodInterceptor {

    /**
     * 被代理对象
     */
    private Object target;

    public Object getProxy(Object target){
        this.target=target;
        /**
         * 动态代码生成器
         */
        Enhancer enhancer=new Enhancer();
        enhancer.setSuperclass(target.getClass());
        enhancer.setCallback(this);

        /**
         * 动态生成字节码并返回代理对象
         */
        return enhancer.create();
    }

    @Override
    public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
        long star = System.currentTimeMillis();
        Object result = method.invoke(target, args);
        long end = System.currentTimeMillis();
        System.out.println(String.format("method cost %s", (end - star)));
        return result;
    }
}

package seven.com;

import seven.com.seven.aop.DynamicCGLibProxy;
import seven.com.seven.aop.Hello;
import seven.com.seven.aop.IHello;

public class App {
    public static void main(String[] args) {
        Hello hello = new Hello();
        IHello proxy = (IHello) new DynamicCGLibProxy().getProxy(hello);
        proxy.say();
    }
}

总结

优点:

1、自定生成代理类,免去手动代理的工作量,接口变动,代理类无需关注

2、符合开闭原则,通过代理实现新功能,没有侵入到原有代码,也不用破坏原来的代码结构,完美的扩展方案

JDK 和 CGLIB 区别:

1、JDK内置的代理模式,被代理的类必须实现接口,没有实现接口则不能完成代理

2、CGLIB是通过拼字节码的方式实现,更为灵活,没有接口的限制,但是由于是通过继承的方式,所以被代理类不能是final类

关于JDK和CGLIB的实现原理,将在后续的文章中介绍。

点赞