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

java学习——代理模式之动静PK

2015-10-20 16:54 471 查看
由于本人之前学习过代理模式,因此在这里不在过多的对代理模式的基础知识讲解,主要说一说代理模式的应用,以及动态代理和静态代理都是什么。而且AOP的原理就是java的动态代理机制,所以本篇博客就是以代理形式给大家介绍一下java的动态机制。以后我们在学习Spring容器的时候再做详细学习。

一、代理模式

1、是什么

代理模式是对象的结构模式。代理模式给某一个对象提供一个代理对象,并由代理对象控制对原对象的引用。

所谓代理,就是一个人或者机构代表另一个人或者机构采取行动。在一些情况下,一个客户不想或者不能够直接引用一个对象,而代理对象可以在客户端和目标对象之间起到中介的作用。

2、代理模式图



需要注意的有两点

[code]代理类(GamePlayerProxy)和真类(GamePlayer)实现的接口至少有一个是相同的。


[code]代理类(GamePlayerProxy)必须调用真类(GamePlayer)来实现其真正的方法。


二、静态代理

1、调用关系图




解说:

[code]Client类:客户端类,调用代理UserManagerImplProxy实现添加


[code]UserManagerImplProxy代理类:
    在添加前打印参数
    调用正真类UserManagerImpl实现添加
    增加打印成功或失败信息


[code]真正实现类UserManagerImpl:该对象是真正实现方法的类


2、代码实现

真正实现类UserManagerImpl

[code]public class UserManagerImpl implements UserManager {

    @Override
    public void addUser(String userId, String userName) {
        System.out.println("UserManagerImpl.addUser() --->userId" +userId);
    }
}


UserManagerImplProxy代理类

[code]public class UserManagerProxy implements UserManager {

    private UserManager userManager;

    //构造方法,传入真正的实现类
    public  UserManagerProxy(UserManager userManager){
        this.userManager=userManager;
    }

    @Override
    public void addUser(String userId, String userName) {
        try {
            //代理调用开始提示
            System.out.println("start-->>addUser() userId-->>" + userId);
            //代理调用真正的实现方法
            userManager.addUser(userId, userName);
            //代理调用成功提示
            System.out.println("success-->>addUser()");
        }catch(Exception e) {
            e.printStackTrace();
            //代理调用失败提示
            System.out.println("error-->>addUser()");
        }   
    }
}


Client类

[code]public class Client {
    public static void main(String[] args)
     {
        //传入真正的实现类,创建代理对象
        UserManager userManager = new UserManagerProxy(new UserManagerImpl() );
        //通过代理调用
        userManager.addUser("0001", "张三");
    }
}


3、静态代理类优缺点

优点

业务类只需要关注业务逻辑本身,保证了业务类的重用性。这是代理的共有优点。

缺点

1)代理对象的一个接口只服务于一种类型的对象,如果要代理的方法很多,势必要为每一种方法都进行代理,静态代理在程序规模稍大时就无法胜任了。

2)如果接口增加一个方法,除了所有实现类需要实现这个方法外,所有代理类也需要实现此方法。增加了代码维护的复杂度。

三、动态代理

1、调用关系图




解说:

[code]Client类:客户端类,调用代理Java的代理类InvocationHandler(java 机制)


[code]InvocationHandler代理类由LogHandler实现:所以直接由LogHandler实现的invoke方法来调用真正实现类UserManagerImpl


[code]真正实现类UserManagerImpl:该对象是真正实现方法的类


2、代码实现

真正实现类UserManagerImpl(同上,略)

LogHandler类,实现InvocationHandler代理类

[code]public class LogHandler implements InvocationHandler {

    //我们要代理的真实对象
    private Object dynamicProxy;

    public Object getProxy(Object dynamicProxy){
        // 我们要代理哪个真实对象,就将该对象传进去,最后是通过该真实对象来调用其方法的
        this.dynamicProxy=dynamicProxy;

        /*Proxy这个类的作用就是用来动态创建一个代理对象的类,它提供了许多的方法,
        但是我们用的最多的就是 newProxyInstance 这个方法*/
        return Proxy.newProxyInstance(dynamicProxy.getClass().getClassLoader(),
                                      dynamicProxy.getClass().getInterfaces(),
                                      this);
        /*
         * 通过Proxy的newProxyInstance方法来创建我们的代理对象,我们来看看其三个参数
         * 第一个参数 dynamicProxy.getClass().getClassLoader(),
             我们这里使用dynamicProxy这个类的ClassLoader对象来加载我们的代理对象
         * 第二个参数dynamicProxy.getClass().getInterfaces(),我们这里为代理对象提供的接口是真实对象所实行的接口,
             表示我要代理的是该真实对象,
             这样我就能调用这组接口中的方法了
         * 第三个参数this,我们这里将这个代理对象关联到了上方的 InvocationHandler 这个对象上,
         * 即实现了该对象的LogHandler
         */
    }
    /**
     * 当我们通过代理对象调用一个方法的时候,
     * 这个方法的调用就会被转发为由InvocationHandler这个接口的 invoke 方法来进行调用
     * 
     * proxy:  指代我们所代理的那个真实对象
     * method:  指代的是我们所要调用真实对象的某个方法的Method对象
     * args:  指代的是调用真实对象某个方法时接受的参数
     */
    public Object invoke(Object proxy, Method method, Object[] args)
            throws Throwable {
        //代理开始调用提示
        System.out.println("Start---->"+method.getName() );
        //循环打印出所有的参数
        for(int i=0;i<args.length;i++){
            System.out.println(args[i]);
        }
        //如果有返回值,则以对象形式返回
        Object ret=null;
        try {   
             //当代理对象调用真实对象的方法时,其会自动的跳转到代理对象关联的LogHandler对象的invoke方法来进行调用
            ret=method.invoke(dynamicProxy, args);
            //代理成功调用提示
            System.out.println("Success---->"+method.getName() );
        } catch (Exception e) {
            e.printStackTrace();
            //代理失败调用提示
            System.out.println("error---->"+method.getName() );
        }
        //返回参数
        return ret;
    }
}


Client类

[code]public class Client {
    public static void main(String[] args) {
        //调用LogHandler动态创建代理对象
        UserManager userManager=(UserManager)new LogHandler().getProxy(new UserManagerImpl());
        //通过代理实现添加
        userManager.addUser("0001", "张三");
    }
}


3、动态代理优缺点

优点

动态代理与静态代理相比较,最大的好处是接口中声明的所有方法都被转移到调用处理器一个集中的方法中处理(InvocationHandler.invoke)。这样,在接口方法数量比较多的时候,我们可以进行灵活处理,而不需要像静态代理那样每一个方法进行中转。而且动态代理的应用使我们的类职责更加单一,复用性更强

缺点

Proxy 已经设计得非常优美,但是还是有一点点小小的遗憾之处,那就是它始终无法摆脱仅支持 interface 代理的制约,因为它的设计注定了这个遗憾。回想一下那些动态生成的代理类的继承关系图,它们已经注定实现接口。Java 的继承机制注定了这些动态代理类们无法实现对 class 的动态代理,原因是多继承在 Java 中本质上就行不通。动态代理还有一个比较明显的缺点就是使用到了反射机制,从而与静态代理相比,性能要差一点。

总结

至此,我们学习了静态代理与动态代理各自的实现以及他们的优缺点,还是那句话,我们在实际使用中,要根据自己的情况,选择适合自己的模式。没有哪个好哪个坏,只要找到最适合我们的就是最好的。

相关连接

java的动态代理机制详解

java静态代理和动态代理
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: