zoukankan      html  css  js  c++  java
  • java中静态代理和动态代理

    一、概述

    代理是一种模式,提供了对目标对象的间接访问方式,即通过代理访问目标对象。如此便于在目标实现的基础上增加额外的功能操作,前拦截,后拦截等,以满足自身的业务需求,同时代理模式便于扩展目标对象功能的特点也为多人所用。

    按照代理的创建时期,代理类可以分为两种: 

    静态:由程序员创建代理类或特定工具自动生成源代码再对其编译。在程序运行前代理类的.class文件就已经存在了。

    动态:在程序运行时运用反射机制动态创建而成。

    我们根据加载被代理类的时机不同,将代理分为静态代理和动态代理。如果我们在代码编译时就确定了被代理的类是哪一个,那么就可以直接使用静态代理;如果不能确定,那么可以使用类的动态加载机制(反射),在代码运行期间加载被代理的类这就是动态代理,比如RPC框架和Spring AOP机制。

    二、图形描述

      

      

    三、静态代理

    静态代理的实现比较简单,代理类通过实现与目标对象相同的接口,并在类中维护一个代理对象。通过构造器塞入目标对象,赋值给代理对象,进而执行代理对象实现的接口方法,并实现前拦截,后拦截等所需的业务功能,一个典型的代理模式通常有三个角色,这里称之为**代理三要素**:共同的接口,代理对象、被代理对象

    package com.bjsxt.staticProxy;
    
    /**
     * Created by Administrator on 2019/3/10.
     */
    public class TestStaticProxy {
    
        public static void main(String[] args){
            //用法类似于线程的代理对象 new Thread(线程对象).start();
            new WeddingCompany(new You()).happyMarry();
        }
    }
    
    /**
     * 共同的接口
     */
    interface Marry{
        void happyMarry();
    }
    
    /**
     * 真实的角色,被代理角色
     */
    class You implements Marry{
        @Override
        public void happyMarry() {
            System.out.println("你结婚了。。。");
        }
    }
    
    /**
     * 代理角色,代理真实的角色
     */
    class WeddingCompany implements Marry{
    
        private Marry marry;    //代理对象内部维护一个真实的角色(需要代理的角色)
    
        public WeddingCompany(Marry marry){
            this.marry=marry;
        }
    
    
        @Override
        public void happyMarry() {
            ready();
            marry.happyMarry();
            after();
        }
    
    
        private void ready() {
            System.out.println("布置婚礼。。。。");
        }
        private void after() {
            System.out.println("闹洞房。。。。");
        }
    }

      静态代理的总结

      优点:可以做到不对目标对象进行修改的前提下,对目标对象进行功能的扩展和拦截。

      缺点:因为代理对象,需要实现与目标对象一样的接口,会导致代理类十分繁多,不易维护,同时一旦接口增加方法,则目标对象和代理类都需要维护。

    四、动态代理

      在上面的示例中,一个代理只能代理一种类型,而且是在编译器就已经确定被代理的对象。而动态代理是在运行时,通过反射机制实现动态代理,并且能够代理各种类型的对象,动态代理有两种方式jdk动态代理和cglib动态代理

      (1)使用jdk动态代理

      我们看到jdk的代理机制必须要求被代理类实现某个方法,这样在生成代理类的时候才能知道重写那些方法。(事实上jdk动态代理会动态生成一个被代理类所实现的接口的实现类,跟静态代理类似)这样一个没有实现任何接口的类就无法通过jdk的代理机制进行代理,当然解决方法是使用cglib的代理机制进行代理。

    package com.bjsxt.dynamicProxy;
    
    import java.lang.reflect.InvocationHandler;
    import java.lang.reflect.Method;
    import java.lang.reflect.Proxy;
    import java.util.Map;
    
    /**
     * JDK动态代理:使用前提(基于接口的实现)
     * JDK动态代理:本质上生产了一个代理类,该代理与被代理目标实现相同的接口,
     * 当前用户访 问代理类中的方法时,将委派InvacationHandler中的invoke()方法进行处理,
     * 在invoke()方法中添加业务处理代理和回调被代理目标中方法。
     */
    public class TestJdkProxy {
    
        public static void main(String[] args){
            You you=new You();//创建被代理类
            WeddingCompany weddingCompany=new WeddingCompany(you);
            //生成代理类
            Marry marry=(Marry)weddingCompany.createProxy();
            //通过代理类,调用被代理类的方法
            marry.happyMarry();
        }
    
    }
    
    /**
     * 共同的接口
     */
    interface Marry{
        void happyMarry();
    }
    
    /**
     * 真实的角色,被代理角色
     */
    class You implements Marry {
        @Override
        public void happyMarry() {
            System.out.println("你结婚了。。。");
        }
    }
    
    class WeddingCompany implements InvocationHandler {
    
        private Object target;     //被代理的对象
    
        public WeddingCompany(Object target){
            this.target=target;
        }
    
        /**
         * 生成代理
         * @return
         */
        public Object  createProxy(){
            //获得加载被代理类的 类加载器
            ClassLoader loader = target.getClass().getClassLoader();
            //指明被代理对象实现的接口
            Class<?>[] interfaces = target.getClass().getInterfaces();
            //this是当前类的对象,也是代理类,之后想要调用被代理类的方法时,都会委托给这个类的
            return Proxy.newProxyInstance(loader, interfaces, this);
        }
    
        /**
         *
         * @param proxy 代理对象
         * @param method    原对象被调用的方法
         * @param args  方法传递的参数
         * @return
         * @throws Throwable
         */
        @Override
        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
            ready();
            Object returnValue=method.invoke(target,args);
            after();
            return returnValue;
        }
    
    
        private void ready() {
            System.out.println("布置婚礼。。。。");
        }
        private void after() {
            System.out.println("闹洞房。。。。");
        }
    }

      jdk动态代理总结

        优点:代理对象无需实现接口,免去了编写很多代理类的烦恼,同时接口增加方法也无需再维护目标对象和代理对象,只需在事件处理器中添加对方法的判断即可。

        缺点:代理对象不需要实现接口,但是目标对象一定要实现接口,否则无法使用JDK动态代理。

      

    (2)使用cglib动态代理

      cglib是通过继承的方式做的动态代理,因此,如果某个类被标记为final,那么它是无法使用cglib做动态代理。cglib会生成的代理类继承当前的被代理类,并在代理类中对代理方法进行强化处理(前置处理、后置处理等),因此被代理类有没有实现接口都是一样的

      当然使用cglib需要添加下cglib所需的jar包:cglib-2.2.2.jar,asm-3.3.1.jar,asm-util-3.3.1.jar,ant-1.6.2.jar

      

    package com.bjsxt.dynamicCglibProxy;
    
    import net.sf.cglib.proxy.Enhancer;
    import net.sf.cglib.proxy.MethodInterceptor;
    import net.sf.cglib.proxy.MethodProxy;
    
    import java.lang.reflect.InvocationHandler;
    import java.lang.reflect.Method;
    import java.lang.reflect.Proxy;
    
    
    public class TestCglibProxy {
    
        public static void main(String[] args){
            You you=new You();//创建被代理类
            WeddingCompany weddingCompany=new WeddingCompany(you);
            //生成代理类
            You you1=(You)weddingCompany.createProxy();
            //通过代理类,调用被代理类的方法
            you1.happyMarry();
        }
    
    }
    
    
    /**
     * 真实的角色,被代理角色
     */
    class You {
    
        public void happyMarry() {
            System.out.println("你结婚了。。。");
        }
    }
    
    /**
     * 动态没生成代理对象
     * 实现 MethodInterceptor方法代理接口,创建代理类
     * 当访问代理中的方法时,委派给MethodInteceptor中的处理程序(intercept方法)进行出来,
     * 在处理程序中添加了业务逻辑和回掉了被代理目标中的方法。
     */
    class WeddingCompany implements MethodInterceptor {
    
        private Object target;     //被代理的对象
    
        public WeddingCompany(Object target){
            this.target=target;
        }
    
        /**
         * 创建代理对象
         * @return
         */
        public Object createProxy() {
            //创建加强器,用来创建动态代理类
            Enhancer enhancer = new Enhancer();
            //为加强器指定要代理的类(即:为下面生成的代理类指定父类)
            enhancer.setSuperclass(this.target.getClass());
            //设置回调:对于代理类上所有方法的调用,都会调用CallBack,而Callback则需要实现intercept()方法进行拦
            enhancer.setCallback(this);
            // 创建动态代理类对象并返回
            return enhancer.create();
        }
    
        /**
         *
         * @param proxy 生成的代理对象
         * @param method    被调用的方法
         * @param args  方法的入参
         * @param methodProxy
         * @return
         * @throws Throwable
         */
        @Override
        public Object intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
            ready();
            Object value = method.invoke(target, args);//回掉被代理对象中的方法
            after();
            return value;
        }
    
        private void ready() {
            System.out.println("布置婚礼。。。。");
        }
        private void after() {
            System.out.println("闹洞房。。。。");
        }
    }
  • 相关阅读:
    UWP中的消息提示框(二)
    UWP中的消息提示框(一)
    使用批处理编写ORACLE设置
    程序员那些事儿
    “程序已停止工作”问题的解决方法
    《第一行代码--Android》阅读笔记之数据持久化
    《第一行代码--Android》阅读笔记之广播
    《第一行代码--Android》阅读笔记之界面设计
    《第一行代码--Android》阅读笔记之Activity
    WinForm中为按钮添加键盘快捷键,超简单,亲测有效
  • 原文地址:https://www.cnblogs.com/duanrantao/p/10504471.html
Copyright © 2011-2022 走看看