简单理解AOP --- 基础知识之代理模式

代理模式

代理类(A) 代理了 被代理类(B)的一部分方法 

在我们的代码中需要 使用 B.a() 或者B.b() 地方  我们可以使用A.a(),A.b()来代替。

现实中的例子就是:
    
你想租房子,你找来了房屋中介代理老宋,在你通过房屋中介租房子的过程中,都是房屋中介在操办出租房子的相关事项,也就是在出租房子这件事上,房屋中介老宋的行为等于房东行为 ,但是这里房屋代理人并不具有房东老王的其他行为,比如下班之后,回老王家,抱老王老婆孩子。

但是对于你来说,最后的结果就是成功的租了房东老王的房子。

使用代理类这么做的好处是 代理类可以在  被代理的B.a()或者B.b()方法执行之前或者之后加一些小动作,就像房屋中介在出租老王的房子之后打算偷偷得NTR他。

话说回来,这里的小动作可以有

比如监控B.a()方法执行次数、比如打印B.a()执行的日志、比如控制B.a()的访问、
比如开启事务。没错,聪明如你肯定会发现使用代理模式就是 面向切面(AOP) 能够实现的关键之一。

我们在之后也会继续深入讨论AOP的相关内容

动态代理模式   动态代理并不需要我们编写 被代理对象的 具体代理类, 而是在运行期动态生成。

java中动态代理可以分为 jdk动态代理(java.lang.relect.Proxy) 和 cglib(Code Generation Library)。

两者的区别就是CGlib创建实例的时间比前者多,但是运行性能比前者快。具体的区别以后再说吧(≧▽≦)

下面来一个网上随处可见的jdk动态代理的例子。

public interface Service {  
    //目标方法 
    public abstract void add();  
} 

public class UserServiceImpl implements Service {  
    public void add() {  
        System.out.println("This is add service");  
    }  
}

2、利用java.lang.reflect.Proxy类和java.lang.reflect.InvocationHandler接口定义代理类的实现。

class MyInvocatioHandler implements InvocationHandler {
    private Object target;

    public MyInvocatioHandler(Object target) {
        this.target = target;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("-----before-----");
        Object result = method.invoke(target, args);
        System.out.println("-----end-----");
        return result;
    }
    
}

3、使用动态代理

public class ProxyTest {
    public static void main(String[] args) {
        Service service = new UserServiceImpl();
        MyInvocatioHandler handler = new MyInvocatioHandler(service);
        ClassLoader loader = Thread.currentThread().getContextClassLoader();
        // 生成代理对象
        Service serviceProxy = (Service) Proxy.newProxyInstance(loader, service.getClass().getInterfaces(), handler);
        serviceProxy.add();
    }
}

执行结果:

-----before-----
This is add service
-----end-----

下面重点讲一下使用jdk的注意事项:

1、被代理对象UserServiceImpl必须把自己需要被代理的方法add()抽象成一个接口 并实现他,这里是 Service.add()方法。

2、 Proxy.newProxyInstance(loader, service.getClass().getInterfaces(), handler);会在运行期动态返回一个 Proxy 对象,就是UserServiceImpl的代理对象。

3、interfaces 就是 代理类需要代理的方法。并且Proxy对象的实例的类型 Service。 所以 你只能把Proxy强转成Service接口的类型,这就限定了代理类只能代理UserServiceImpl.add()方法。

4、InvocatioHandler 接口是实现动态代理的重要组件 顾名思义 Invocation 调用  Handler 处理者,也就是 调用代理类的方法的处理者。
    
看一下这个接口的 invoke(Object proxy, Method method, Object[] args) 方法。

     @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("-----before-----");
        Object result = method.invoke(target, args);
        System.out.println("-----end-----");
        return result;
    }

在执行 method.invoke(target,args)前后,就是代理对象的target.add()方法前后,我们输出了两行字符串,于是就实现了动态代理。

这个时候,肯定有同学问了 为什么时候的Invocation的invoke方法呢?

问的好,看代理的源码呗,由于动态代理类的具体实现我们是看不到的

于是我们把 Proxy.newProxyInstance(loader, service.getClass().getInterfaces(), handler) 获得的对象 通过ObjectStream输出,

然后在反编译一下,于是就的得到了这个java动态生成的java代码:

public final class $proxy1 extends Proxy implements Service {
 public $proxy1(InvocationHandler invocationhandler) {
        super(invocationhandler);
    }

   public final void add() {
        try {
            super.h.invoke(this, m3, null);
            return;
        }
        catch(Error _ex) { }
        catch(Throwable throwable) {
            throw new UndeclaredThrowableException(throwable);
        }
    }
    private static Method m3;
    static {
        try {
               m3 = Class.forName("zzzzzz.Service").getMethod("add", new Class[0]);
        }
        catch(NoSuchMethodException nosuchmethodexception) {
            throw new NoSuchMethodError(nosuchmethodexception.getMessage());
        }
        catch(ClassNotFoundException classnotfoundexception) {
            throw new NoClassDefFoundError(classnotfoundexception.getMessage());
        }
    }
}

我们看到了动态生成的proxy1.add()方法,在调用它的时候,调用了父类Proxy.invocationhandler属性的invoke方法。

这里值得注意的是

通过静态代码块,我们看到了 m3 在代理类被加载的就获得了Service.add()方法,清晰明了,最后invoke方法会把Service.add()方法传给invocationhandler的invoke处理。

进一步说明了动态生成的代理类 只能生成

Proxy.newProxyInstance(loader, service.getClass().getInterfaces(), handler)

中第二个参数所指定的接口中的方法

jdk动态代理使用的局限性

通过反射类Proxy和InvocationHandler回调接口实现的jdk动态代理,要求委托类必须实现一个接口,但事实上并不是所有类都有接口,对于没有实现接口的类,便无法使用该方式实现动态代理。

参考:  

http://www.cnblogs.com/chinajava/p/5880870.html

http://www.cnblogs.com/fenglie/articles/4097759.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值