静态代理和动态代理的区别

Posted Mr_伍先生

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了静态代理和动态代理的区别相关的知识,希望对你有一定的参考价值。

什么是静态代理:

打一个最简单的比方,我现在想要学习,那么就必须得把书包拿过来,把书掏出来,准备好纸笔,然后开始学习,等学完了我还得收拾书,把书塞回书包里,还得整理一下书包,这是一个完整的学习的过程,但是我很懒,不想动弹,只想学习,那可能就得让妈妈帮我把书包拿过来,把书打开,我只管学习就好了,学完以后,妈妈再帮我把书整理好放回去.(我这是打个什么破比方...),在这里,妈妈就代表了一个代理对象,要学习的人是我,而我只管学习,这样效率才最高,至于其他的交给代理对象(妈妈)做就好了,画一个丑陋的的图表示一下:

  再联想我们最开始接触jdbc操作数据库的时候,业务层每一个方法,都需要1.打开数据库连接,2.执行我们想要的操作3.关闭数据库连接.这样就使得业务层代码不够纯粹,我的功能是查询用户数据,打开和关闭数据库连接关我毛事?我干嘛要去干这件事?这就是传统开发中存在的一个问题,现在通过代码演示一下:

复制代码
/**
 * 简单业务层接口,只有一个save方法
 */
interface UserService{
    public void saveUser();
}
//////////////////////////////////////////////////////// /** * 业务层实现类,实现save方法 */ class UserServiceImpl implements UserService{ @Override public void saveUser() { System.out.println("1:打开数据库连接"); System.out.println("2:保存用户信息"); System.out.println("3:关闭数据库连接"); } }
复制代码

我们可以看到其实这个方法的实现是有问题的,核心业务与辅助业务写在了一个方法中,不但业务冗余了不说,像开关数据库连接这样的公共操作也大量的重复,这时候就出现了代理模式的思想,我们可以使用代理模式的思想改写一下上面的代码:

复制代码
package com.wang.proxy;
/**
 * 简单业务层接口,只有一个save方法
 */
interface UserService{
    public void saveUser();
}

/**
 * 代理类
 */
class UserServiceProxy implements UserService{

    private UserService userService;
    
    public UserServiceProxy(UserService userService) {
        super();
        this.userService = userService;
    }

    public void open(){
        System.out.println("1:打开数据库连接");
    }
    public void close(){
        System.out.println("3:关闭数据库连接");
    }
    @Override
    public void saveUser() {
        this.open();
        userService.saveUser();
        this.close();
    }
    
}

/**
 * 业务层实现类,实现save方法
 */
class UserServiceImpl implements UserService{

    @Override
    public void saveUser() {
        System.out.println("2:保存用户信息");
    }
    
}
/**
 * 测试类
 */
public class TestProxy {
    
    public static void main(String[] args) {
        UserService userService =new UserServiceProxy(new UserServiceImpl());
        userService.saveUser();
    }
}
复制代码

  通过测试代码打印结果,和上面没有使用代理的代码是完全一样,但是通过修改可以清晰地看到,业务层代码变得很纯很纯的,只剩下最核心的保存用户信息的代码.通过代理模式,我们可以抽取出核心业务与辅助业务,但是问题随之而来了,我这里编写的UserServiceProxy是挺不错,可是它只能服务与UserService这个接口的对象啊,如果我有一千个业务,那岂不是要编写一千个代理类,毕竟一千个人心中就有一千个哈姆雷特啊,其实这种代理模式就是静态代理,它的缺点很明显,静态代理只能服务于一种类型的对象,不利于业务的扩展,那么我们就想了,能不能设计一个代理类可以服务于所有的业务对象呢?于是,这时候,动态代理就闪亮登场了.

  如果要实现动态代理,那么你要编写的那个代理类就需要实现一个InvocationHandle接口.这个接口所在位置是java.lang.reflect.InvocationHandler.看到reflect我们就能知道,动态代理肯定是通过反射来实现的了,这个接口中有一个方法:

    Object  invoke(Object proxy, Method method, Object[] args)    :在代理实例上处理方法调用并返回结果。

    invoke方法其实是反射里边的一个方法,在这个方法中有三个参数:

    Ojbect proxy:表示需要代理的对象

    Method method:表示要操作的方法

    Object[] args:method方法所需要传入的参数(可能没有为,null.也可能有多个)

  如果要想让代理设计真正可用,我们还必须有一个代理类对象产生,这有用到了另一个类:java.lang.reflect.Proxy.我的中文jdk文档对他的描述是:

 Proxy 提供用于创建动态代理类和实例的静态方法,它还是由这些方法创建的所有动态代理类的超类。 

  在这个类下面,我们找到了这样一个方法:

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

  该方法返回一个指定接口的代理类实例,该接口可以将方法调用指派到指定的调用处理程序.方法中有三个参数:

   参数:

     loader - 定义代理类的类加载器

     interfaces - 代理类要实现的接口列表

     h - 指派方法调用的调用处理程序

   返回:

    一个带有代理类的指定调用处理程序的代理实例,它由指定的类加载器定义,并实现指定的接口 

  ok,有了这些知识,我们就可以来编写一个万能的动态代理类了.

复制代码
class ServiceProxy implements InvocationHandler {

    private Object target=null;//保存真实业务对象
    /**
     * 返回动态代理类的对象,这样用户才可以利用代理类对象去操作真实对象
     * @param obj  包含有真实业务实现的对象
     * @return   返回代理对象
     */
    public Object getProxy(Object obj) {
        this.target=obj;//保存真实业务对象
        return Proxy.newProxyInstance(obj.getClass().getClassLoader(), obj
                .getClass().getInterfaces(), this);
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args)
            throws Throwable {
        Object result=method.invoke(target, args);//通过反射调用真实业务对象的业务方法,并且返回
        return result;
    }

}
复制代码

现在来测试一下好不好使:

复制代码
/**
 * 测试类
 */
public class TestProxy {

    public static void main(String[] args) {
            UserService service=(UserService) new ServiceProxy().getProxy(new UserServiceImpl());
            service.saveUser();
    }
}


//打印结果:
//        2.保存用户信息
复制代码

  看!!!!完美,你说什么?为什么没有打开数据库连接,和关闭数据库连接呢?  简单,我们直接在ServiceProxy中加入两个方法,open()和close(),一个放到method.invoke()上面,一个放到下面,就可以了,注意,我们现在编写的是一个万能的动态代理类了,没有和任何的业务层接口关联,所以接下来你就可以为所欲为了.

  下面来总结一下:

  动态代理和静态代理相比较,最大的好处就是接口中声明的所有的方法都被转移到一个集中的方法中去处理,就是invocke()方法.这样在接口中声明的方法比较多的情况下我们可以进行灵活处理,而不需要像静态代理那样每一个方法进行中转。

      动态代理只能代理接口,代理类都需要实现InvocationHandler类,实现invoke方法。该invoke方法就是调用被代理接口的所有方法时需要调用的,该invoke方法的返回值是被代理接口的一个实现类。

 

以上是关于静态代理和动态代理的区别的主要内容,如果未能解决你的问题,请参考以下文章

关于动态代理和静态代理的区别

静态代理和动态代理的区别,什么场景使用?

静态代理和动态代理区别

代理模式(静态代理动态代理)代码实战(详细)

Java静态代理和动态代理的区别

代理模式(动态)