您的位置:首页 > 其它

静态代理和动态代理

2017-11-28 18:47 162 查看
以前在学习代理这方面知识点的时候,一直都没关注过静态代理和动态代理的区别,只是知道代理是怎么一回事情,知道动态代理是什么,但是有一天学习过程中,看到静态代理这个词,突然才发现,原来不知道静态代理具体指什么,然后就又学习了一下静态代理,并且顺便把动态代理也复习了一下,学完之后感觉对代理方面的知识算是又深入了一点吧。

什么是代理

代理,也叫做Proxy,通俗一点的理解就是指当我去调用一个类的方法时,实际去调用了另一个类的方法,在另一个类的方法里面再去调用我真正想调用的那个类的方法。额,这样说着好绕,好吧,换一种说法,就是假设当我想调用类A的do()方法时,并不直接去调用,而是调用类B的do()方法,类B的do()方法内部再去调用类A的do()方法。

这样的话,就有个好处,原来直接调用A的do()方法,现在通过B再去调用A的do()方法,就可以在调用do()方法的前后加上一些其他的逻辑了,这就是AOP。

下面看个例子吧:

先定义一个Hello接口:

public interface Hello {
void say();
}


然后定义一个这个接口的实现类HelloImpl:

public class HelloImpl implements Hello {
public void say() {
System.out.println("hello world");
}
}


那么现在问题来了,如果想要在HelloImpl的System.out.println(“hello world”);前后加点逻辑,应该怎么组织代码呢?

最先考虑到的是直接在HelloImpl的say()方法里面改,在println的前后加代码,但是这样的话,代码耦合性就高了,不优雅。所以一般尽量不采用这种方式,而是定义一个代理类来实现这种功能。

静态代理

接下来定义一个StaticHelloProxy代理类:

public class StaticHelloProxy implements Hello{
private Hello hello;

public StaticHelloProxy() {
this.hello = new HelloImpl();
}

public void say() {
before();
hello.say();
after();
}

private void before() {
System.out.println("before");
}

private void after() {
System.out.println("after");
}
}


StaticHelloProxy这个代理类实现了Hello接口,并且里面定义了Hello的一个成员变量,实际new的是HelloImpl类的实例,这样在StaticHelloProxy中重写say方法的时候,就可以调用实际HelloImpl类的say方法了。

在这里,HelloImpl成为目标类,StaticHelloProxy称为代理类。

从StaticHelloProxy名字上也可以看出,这种代理,属于静态代理。一开始就把代理类写好了,在运行之前,就已经有了代理类的字节码。

静态代理也有缺点,比如,StaticHelloProxy这个代理类,在创建的时候就写死了实现Hello接口,这样每当需要一个代理类的时候,就需要新建一个类,整个项目中全是Proxy类文件,而且如果有一天需要在Hello接口中新增一个方法,那么不仅仅要修改HelloImpl,还要修改StaticHelloProxy,这样想想,也挺麻烦的。所以,接下来看看动态代理是怎么实现的。

动态代理

java提供了一个Proxy.newProxyInstance()方法,用来创建代理类的实例对象,下面是这个方法的声明:

public static Object newProxyInstance(ClassLoader loader,
Class<?>[] interfaces,
InvocationHandler h)
throws IllegalArgumentException


这个方法接收三个参数,乍一看挺迷糊的,其实想想,也就那回事,结合静态代理的创建过程,当我们创建一个静态代理类的时候,指定了这个代理类实现的那些接口,并且在代理类中定义了目标类的对象,所以,创建动态代理类的时候,也是类似的,不同点就在于,当我们自己创建一个类的时候,需要额外指定这个类的类加载器(默认是和目标类的类加载器一样就可以了)。

创建动态代理类的时候,目标类是作为参数动态的传进去的,

分析完这些,再看这个方法接收的三个参数,就比较明白了,第一个参数就是这个动态代理类的类加载器,第二个参数就是说明这个动态代理类要实现哪些接口,第三个参数就是实现了InvocationHandler接口的代理类的实例对象。

接下来,定义一个动态代理类,动态代理类要实现InvocationHandler接口。

public class DynamicProxy implements InvocationHandler {

/**
* 被代理的目标类
*/
private Object target;

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

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");
}
}


DynamicProxy中定义了private Object target,这就是被代理的目标类,每次创建动态代理类实例的时候就通过构造方法动态的传进去。并且DynamicProxy还需要实现InvocationHandler接口,重写invoke方法,并且在invoke方法里面通过反射去调用目标类的方法。

下面代码是如何使用动态代理的:

Hello hello = new HelloImpl();
DynamicProxy dynamicProxy = new DynamicProxy(hello);
Hello helloDynamicProxy = (Hello) Proxy.newProxyInstance(
hello.getClass().getClassLoader(),
hello.getClass().getInterfaces(),
dynamicProxy
);
helloDynamicProxy.say();


当调用helloDynamicPro
4000
xy.say();的时候,就会调用DynamicProxy里重写的invoke方法,这就是动态代理。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: