精华内容
下载资源
问答
  • 此资源为文章中《代理设计模式:静态代理和动态代理的理解、实现与区别(优缺点)》与《 AOP动态代理声明式的3种配置方式过程与区别》的案例代码,可下载参考学习。
  • 静态代理和动态代理

    2019-03-06 01:31:55
    博文链接:https://congpeixue.iteye.com/blog/159173
  • 静态代理和动态代理简单实现代码.Proxy.其中静态代理我写了两个demo,有一个是最原始的,还有一个放在程杰的大化设计模式的代理模式写的,纯属娱乐。
  • 资源列举了设计模式中的静态代理和动态代理的简单java实现,jdk1.8版本经过测试验证,对于想学习设计模式的童靴应该有所帮助
  • JAVA静态代理和动态代理
  • NULL 博文链接:https://daxiaoli123.iteye.com/blog/940784
  • 静态代理和动态代理 根据加载被代理类的时机不同,将代理分为静态代理和动态代理。 编译时就确定了被代理的类是哪一个,那么就可以直接使用静态代理; 运行时才确定被代理的类是哪个,那么可以使用类动态代理。 1...

    静态代理和动态代理

    根据加载被代理类的时机不同,将代理分为静态代理和动态代理。

    • 编译时就确定了被代理的类是哪一个,那么就可以直接使用静态代理;
    • 运行时才确定被代理的类是哪个,那么可以使用类动态代理。

    1、静态代理

    public interface Subject {
        public void sayGoodBye();
        public void sayHello(String str);
    }
    

    2、 定义被代理类(原来功能类)并实现被代理类的功能逻辑:

    public class RealSubject implements Subject {
        @Override
        public void sayGoodBye() {
            System.out.println("RealSubject sayGoodBye");
        }
        @Override
        public void sayHello(String str) {
            System.out.println("RealSubject sayHello  " + str);
        }
    }
    

    3、 定义静态代理类(功能增加类),这个代理类也必须要实现和被代理类相同的Subject接口,便于对原有功能的增强:

    public class ProxySubject implements Subject {
        private Subject subject;
        public ProxySubject(Subject subject) {
            this.subject = subject;
        }
        @Override
        public void sayGoodBye() {
            //代理类,功能的增强
            System.out.println("ProxySubject sayGoodBye begin");
            //在代理类的方法中 间接访问被代理对象的方法
            subject.sayGoodBye();
            System.out.println("ProxySubject sayGoodBye end");
        }
        @Override
        public void sayHello(String str) {
            //代理类,功能的增强
            System.out.println("ProxySubject sayHello begin");
            //在代理类的方法中 间接访问被代理对象的方法
            subject.sayHello(str);
            System.out.println("ProxySubject sayHello end");
        }
    }
    

    4、使用:

    public static void main(String[] args) {
        //被代理的对象,某些情况下 我们不希望修改已有的代码,我们采用代理来间接访问
        RealSubject realSubject = new RealSubject();
        //代理类对象
        ProxySubject proxySubject = new ProxySubject(realSubject);
        //调用代理类对象的方法
        proxySubject.sayGoodBye();
        System.out.println("******");
        proxySubject.sayHello("Test");
    }
    

    总结: 静态代理(传统代理模)的实现方式比较暴力直接,需要将所有被代理类的所有方法都写一遍,并且一个个的手动转发过去。有点累

    二、动态代理

    1、在java的动态代理机制中,有两个重要的类或接口
    • 一个是 InvocationHandler(Interface)
    • 另一个则是Proxy(Class)
    • 这一个类和接口是实现我们动态代理所必须用到的
    2、 InvocationHandler
    public interface InvocationHandler {
        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable;
    }
    
    • proxy:指代生成的代理对象;
    • method:指代的是我们所要调用真实对象的某个方法的Method对象;
    • args:指代的是调用真实对象某个方法时接受的参数;
    • 每一个代理实类例的invocation handler都要实现InvocationHandler这个接口。并且每个代理类的实例都关联到了一个handler,当我们通过代理对象调用一个方法的时候,这个方法的调用就会被转发为由InvocationHandler这个接口的invoke 方法来进行调用
    3、 Proxy这个类的 newProxyInstance 这个方法

    JDK动态代理需要借助接口来实现,如果我们要代理的对象功能没有抽成任何接口,那么我们就无法通过JDK动态代理的方式来实现。

    public static Object newProxyInstance(ClassLoader loader,
                                              Class<?>[] interfaces,
                                              InvocationHandler h)
    
    • loader:一个ClassLoader对象,定义了由哪个ClassLoader对象来对生成的代理对象进行加载
    • interfaces:一个Interface对象的数组,表示的是我将要给我需要代理的对象提供一组什么接口,如果我提供了一组接口给它,那么这个代理对象就宣称实现了该接口(多态),这样我就能调用这组接口中的方法了
    • 一个InvocationHandler对象,表示的是当我这个动态代理对象在调用方法的时候,会关联到哪一个InvocationHandler对象上。

    第一步:定义一个接口

    public interface Subject {
        public void sayGoodBye();
        public void sayHello(String str);
    }
    

    第二步:定义真是对象(被代理类):

    public class RealSubject implements Subject {
        @Override
        public void sayGoodBye() {
            System.out.println("RealSubject sayGoodBye");
        }
        @Override
        public void sayHello(String str) {
            System.out.println("RealSubject sayHello  " + str);
        }
    }
    

    第三步: 定义一个InvocationHandler, 相当于一个代理处理器
    SubjectInvocationHandler并不是真正的代理类,而是用于定义代理类需要扩展、增强那些方法功能的类。在invoke函数中,对代理对象的所有方法的调用都被转发至该函数处理。在这里可以灵活的自定义各种你能想到的逻辑。

    public class SubjectInvocationHandler implements InvocationHandler {
        //这个就是我们要代理的真实对象
        private Object subject;
        //构造方法,给我们要代理的真实对象赋初值
        public SubjectInvocationHandler(Object subject) {
            this.subject = subject;
        }
        @Override
        public Object invoke(Object object, Method method, Object[] args) throws Throwable {
            //在代理真实对象前我们可以添加一些自己的操作
            System.out.println("before Method invoke");
            System.out.println("Method:" + method);
            //当代理对象调用真实对象的方法时,其会自动的跳转到代理对象关联的handler对象的invoke方法来进行调用
            method.invoke(subject, args);
            //在代理真实对象后我们也可以添加一些自己的操作
            System.out.println("after Method invoke");
            return null;
        }
    }
    

    第四步:调用

    public static void main(String[] args) {
           //被代理类
           Subject realSubject = new RealSubject();
           //我们要代理哪个类,就将该对象传进去,最后是通过该被代理对象来调用其方法的
           SubjectInvocationHandler handler = new SubjectInvocationHandler(realSubject);
           //生成代理类
           Subject subject = (Subject) Proxy.newProxyInstance(handler.getClass().getClassLoader(),
                                                              realSubject.getClass().getInterfaces(), handler);
           //输出代理类对象
           System.out.println("Proxy : "+ subject.getClass().getName());
           System.out.println("Proxy super : "+ subject.getClass().getSuperclass().getName());
           System.out.println("Proxy interfaces : "+ subject.getClass().getInterfaces()[0].getName());
           //调用代理类sayGoodBye方法
           subject.sayGoodBye();
           System.out.println("--------");
           //调用代理类sayHello方法
           subject.sayHello("Test");
       }
    
    Proxy : com.sun.proxy.$Proxy0
    Proxy super : java.lang.reflect.Proxy
    Proxy interfaces : com.company.ha.Subject
    before Method invoke
    Method:public abstract void com.company.ha.Subject.sayGoodBye()
    RealSubject sayGoodBye
    after Method invoke
    --------
    before Method invoke
    Method:public abstract void com.company.ha.Subject.sayHello(java.lang.String)
    RealSubject sayHello  Test
    after Method invoke
    

    总结:
    与静态代理相比,动态代理具有如下的优点:

    • 代理转发的过程自动化了,实现自动化搬砖;
    • 代理类的代码逻辑和具体业务逻辑解耦,与业务无关;

    首先来看看 $Proxy0 这东西,这个东西就是真正的代理类对象,我们定义SubjectInvocationHandler类则是用于添加对代理类的功能扩展!而 $Proxy0类继承java.lang.reflect.Proxy类 并实现Subject接口 ,因此它的类声明方式如下:

    同时我们一定要记住,通过 Proxy.newProxyInstance 创建的代理对象是在jvm运行时动态生成的一个对象,它并不是我们的InvocationHandler类型,也不是我们定义的那组接口的类型,而是在运行是动态生成的一个对象,并且命名方式都是这样的形式,以$开头,proxy为中,最后一个数字表示对象的标号。

    如果想添加功能可以:

    @Override
    public Object invoke(Object object, Method method, Object[] args) throws Throwable {
        if (method.getName().equals("sayGoodBye")) {//在调用sayGoodBye方法的时候 对返回值进行处理
            int result = (int) method.invoke(subject, args);
            return result + 10;
        } else {//其他方法一律不处理
            return method.invoke(subject, args);
        }
    }
    
    展开全文
  • 主要介绍了 Spring AOP里的静态代理和动态代理用法详解,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
  • 为其他对象提供一种代理以控制这个对象的访问,在某些情况下一个对象不能直接访问那个对象时,代理就起到了客户端代理对象(委托类)中介作用。 代理委托类都有同样接口。 好处:可以不用动原来类的逻辑,...

    控制对象访问权限

    • 概念
    • 类图
    • 代码实例
    • 和装饰者区别

    概念

    为其他对象提供一种代理以控制这个对象的访问,在某些情况下一个对象不能直接访问那个对象时,代理就起到了客户端和被代理对象(委托类)中介作用。 代理类和委托类都有同样接口。

    好处:可以不用动原来类的逻辑,再次增加一些功能,符合开闭原则。真正的业务还是交给被代理对象处理的,因此在其委托被代理对象处理业务前后实现一些公共逻辑服务,例如加入缓存或日志等功能,无须修改原来的类就可以使用代理进行实现。

    类图

    在这里插入图片描述

    Subject: 代理类和被代理类实现同样的接口

    Proxy:代理类,里面有被代理类,具体逻辑委托被代理类进行处理

    RealSubject:被代理类,可以在其内做一些访问权限控制,额外的业务处理

    Client:看到的是代理类,并不知道具体处理业务逻辑的类,降低耦合性

    代码实例

    UserDAO 代理和被代理的公共的接口(Subject)

    /**
     * @author duanyimiao
     * @create 2018-09-22 10:47 AM
     * @description 用户数据持久化逻辑接口
     **/
    public interface UserDAO {
    
        boolean insert(String name,int age);
    }
    

    UserDAOImpl 被代理类(RealSubject)

    /**
     * @author duanyimiao
     * @create 2018-09-22 10:47 AM
     * @description 用户数据持久具体实现
     **/
    public class UserDAOImpl implements UserDAO {
    
        @Override
        public boolean insert(String name, int age) {
            System.out.println("insert to database name="+name +" age="+age);
            return true;
        }
    }
    

    UserDAOProxyByInterface 代理类,通过实现接口方式实现代理方式(Proxy)

    /**
     * @author duanyimiao
     * @create 2018-09-22 10:50 AM
     * @description 用户持久数据的代理类(和被代理类实现同个接口方式)
     **/
    public class UserDAOProxyByInterface implements UserDAO {
        private UserDAOImpl conreteUserDAO;
    
        public UserDAOProxyByInterface() {
    
        }
        public UserDAOProxyByInterface(UserDAOImpl conreteUserDAO) {
            this.conreteUserDAO = conreteUserDAO;
        }
        @Override
        public boolean insert(String name, int age) {
            System.out.println("before insert handle some logic by interface");
            return conreteUserDAO.insert(name, age);
        }
    }
    

    UserDAOProxyByExtend 代理类,通过继承被代理类实现的代理方式(Proxy)

    /**
     * @author duanyimiao
     * @create 2018-09-22 10:50 AM
     * @description 用户持久数据的代理类(继承被代理类方式)
     **/
    public class UserDAOProxyByExtend extends UserDAOImpl {
        private UserDAOImpl conreteUserDAO;
    
        public UserDAOProxyByExtend() {
    
        }
        public UserDAOProxyByExtend(UserDAOImpl conreteUserDAO) {
            this.conreteUserDAO = conreteUserDAO;
        }
        @Override
        public boolean insert(String name, int age) {
            System.out.println("before insert handle some logic by extend");
            return conreteUserDAO.insert(name, age);
        }
    }
    

    Client 获取代理对象客户端(Client)

    /**
     * @author duanyimiao
     * @create 2018-09-22 10:52 AM
     * @description
     **/
    public class Client {
        public static void main(String[] args) {
            UserDAOImpl conreteUserDAO = new UserDAOImpl();
            UserDAOProxyByInterface userDAOProxyByInterface = new UserDAOProxyByInterface(conreteUserDAO);
            //和被代理类实现同个接口方式进行代理
            userDAOProxyByInterface.insert("dynamo", 18);
    
            //通过继承被代理类方式进行代理
            UserDAOProxyByExtend userDAOProxyByExtend = new UserDAOProxyByExtend(conreteUserDAO);
            userDAOProxyByExtend.insert("dynamo", 18);
    
        }
    }
    

    输出结果:

    before insert handle some logic by interface
    insert to database name=dynamo age=18
    before insert handle some logic by extend
    insert to database name=dynamo age=18
    

    和装饰者模式区别

    代理模式关注的是控制对对象的访问,一般具体的被代理对象对Client是不可见的,直接在创建代理对象时就创建了被代理对象,也就是编译时就确定了。 但是装饰者模式:更关注的是在一个对象方法上动态添加修饰逻辑,但是这些逻辑修饰在运行时才能确定的,通过构造方法传入具体的装饰者对象,执行时递归调用修饰对象的方法。

    动态代理

    静态代理和动态代理:

    1、静态代理:代理类由程序员创建的然后编译成.class文件。但是其中缺点是,具有重复代码,灵活性不好,例如在执行接口A中所有方法之前加上日志逻辑,那么使用静态代理的话,在代理类中每个方法都得加,如果我想add* 开头方法加上一种逻辑,select* 开头方法加上另一种逻辑,那么就很难去实现和维护了,想解决以上困惑就要使用动态代理了。

    2、动态代理:是在运行的时候,通过jvm中的反射进行动态创建对象,生成字节码对象(构造方法参数 InvocationHandler h类型),传入由我们实现InvocationHandler接口的对象,通过反射创建代理对象。 然后当调用代理对象的任何方法都会调用h中的 invoke(Object proxy,Method method,Object[] args)传入当前代理对象、当前调用的方法、方法参数值。

    JDK动态代理实现原理

    要求被代理类必须要实现接口,因为JDK动代理实现是通过实现接口方式来实现的。需要实现 InvocationHandler接口(在invoke方法中实现一些额外的逻辑,添加一些新功能),通过Proxy.newProxyInstance(ClassLoader classLoader,Class<?> interfaces,InvocationHandler h)。

    实现步骤

    1.定义一个接口ProxyObj

    2.编写该接口I的实现类ProxyObjImpl

    3.编写InvocationHandler接口的实现类InvokcationInvokeHandler,构造h类对象的时候可以把要代理的对象target传入,target完成实际的动作。在里面的invoke方法里编写自己想实现的逻辑,然后再调用实际要完成的动作就可以。

    4.调用Proxy.newProxyInstance方法,传递的三个参数分别是代理类的类加载器(可以用Impl实例的getClass().getClassLoader())
    、代理类要实现的接口列表(可以用Impl实例getClass().getInterfaces())、InvocationHandler实现类的实例。

    这样就生成了 P r o x y 0 类 的 对 象 , 由 于 Proxy0类的对象,由于 Proxy0Proxy0类实现了ProxyObj接口,所以可以将对象强制转型成ProxyObj。

    再说一下Proxy.newProxyInstance方法的实际过程:

    1.使用传入的InvocationHandler实例参数将Proxy类的h实例初始化,注意,如果传入空对象的话,会抛出空指针错误,即h不能为空。

    2.运行时生成代理Class,即$Proxy0

    3.利用上面动态生成的$Proxy0类,构造出该类的对象,并返回。

    实现代码

    ProxyObj 代理类和被代理类实现的公共接口

    public interface ProxyObj {
    
     public  void setName(String name);
    }
    

    ProxyObjImpl 被代理类

    public class ProxyObjImpl implements  ProxyObj,ProxyObj1{
    
      private String name;
    
      public String getName() {
        return name;
      }
    
      public void setName(String name) {
        System.out.println("set name="+name);
        this.name = name;
      }
    
      @Override
      public void setAge(Integer age) {
        System.out.println("set age="+age);
      }
    }
    

    InvokcationInvokeHandler 实现InvocationHandler接口

    public class InvokcationInvokeHandler implements InvocationHandler {
        //真实的对象
        private ProxyObj proxyPbj;
    
        public InvokcationInvokeHandler(ProxyObj proxy) {
            this.proxyPbj = proxy;
        }
    
        /**
         *
         * @param proxy jvm生成的动态代理对象 $Proxy0
         * @param method 当前代理对象调用的方法对象,是代理对象实现接口中的方法
         * @param args 调用对象传入的参数
         * @return
         * @throws Throwable
         */
        @Override
        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
            System.out.println("before call set proxy" + proxy.getClass()+" method="+method+" args="+ Arrays.toString(args));
            method.invoke(proxyPbj, args);
            //((ProxyObj)proxy).setName("2"); 会导致invoke方法循环调用导致StackoverflowError
            return null;
        }
    }
    

    ProxyFactory 获取代理对象的工厂

    public class ProxyFactory {
    
      //获取代理对象
      public static ProxyObj getProxy(ProxyObj proxyObj) {
        /**
         * loader 指定加载jvm运行时动态生成的代理对象的加载器
         *
         * interface 真实对象实现的所有接口
         *
         * h 实现InvocationHandler接口对象
         */
        return (ProxyObj) Proxy.newProxyInstance(Thread.currentThread().getContextClassLoader(),
            proxyObj.getClass().getInterfaces(), new InvokcationInvokeHandler(proxyObj));
      }
    
      public static void main(String[] args) {
        ProxyFactory.getProxy(new ProxyObjImpl()).setName("hh");
      }
    }
    

    输出结果

    before call set proxyclass com.sun.proxy.$Proxy0 method=public abstract void com.dynamo.proxy.dynamicproxy.jdk.ProxyObj.setName(java.lang.String) args=[hh]
    set name=hh
    

    流程图:

    在这里插入图片描述

    通过javap可以看到具体代理类为:

    import java.lang.reflect.InvocationHandler;   
    import java.lang.reflect.Method;  
    import java.lang.reflect.Proxy;   
    import java.lang.reflect.UndeclaredThrowableException;  
       
    public final class $Proxy0 extends Proxy implements UserManager {  
        private static Method m1;  
        private static Method m0;  
        private static Method m3;  
        private static Method m2;  
      
        static {  
            try {  
                m1 = Class.forName("java.lang.Object").getMethod("equals",  
                        new Class[] { Class.forName("java.lang.Object") });  
                m0 = Class.forName("java.lang.Object").getMethod("hashCode",  
                        new Class[0]);  
                m3 = Class.forName("cn.edu.jlu.proxy.UserManager").getMethod("addUser",  
                        new Class[0]);  
                m2 = Class.forName("java.lang.Object").getMethod("toString",  
                        new Class[0]);  
            } catch (NoSuchMethodException nosuchmethodexception) {  
                throw new NoSuchMethodError(nosuchmethodexception.getMessage());  
            } catch (ClassNotFoundException classnotfoundexception) {  
                throw new NoClassDefFoundError(classnotfoundexception.getMessage());  
            }  
        }  
      
        public $Proxy0(InvocationHandler invocationhandler) {  
            super(invocationhandler);  
        }  
      
        @Override  
        public final boolean equals(Object obj) {  
            try {  
                return ((Boolean) super.h.invoke(this, m1, new Object[] { obj }))  
                        .booleanValue();  
            } catch (Throwable throwable) {  
                throw new UndeclaredThrowableException(throwable);  
            }  
        }  
      
        @Override  
        public final int hashCode() {  
            try {  
                return ((Integer) super.h.invoke(this, m0, null)).intValue();  
            } catch (Throwable throwable) {  
                throw new UndeclaredThrowableException(throwable);  
            }  
        }  
      
        @Override  
        public final String toString() {  
            try {  
                return (String) super.h.invoke(this, m2, null);  
            } catch (Throwable throwable) {  
                throw new UndeclaredThrowableException(throwable);  
            }  
        }  
      
        @Override  
        public void addUser() {  
            try {  
                super.h.invoke(this, m3, null);  
                return;  
            } catch (Error e) {  
            } catch (Throwable throwable) {  
                throw new UndeclaredThrowableException(throwable);  
            }  
      
        }  
    } 
    
    

    为什么无论调用JDK的动态代理对象还是CGLIB的代理对象的toString方法会出现栈溢出?

    通过阅读JDK中代理类的源码 $Proxy0 extends Proxy implements I1,I2 看到,其中该代理类通过反射获取到这么几种Method对象, Object类中的方法对象:m2 = Class.forName(“java.lang.Object”).getMethod(“toString”,new Class[0]);
    除了toString,还有 equals 和 hashCode方法。

    还有在Proxy.newProxInstance()传入的Class[] interfaces 所有接口中的方法。当这些方法调用的时候都会触发父类Proxy中的InvocationHandler对象中的invoke方法

    ((String)super.h.invoke(this, m2, null));

    所以如果在 实现InvocationHandler对象中的invoke方法中 调用代理对象中的 toString hashCode equals 和 其中被代理类的方法时都会导致栈溢出。
    从上述的流程和代码可以看到如果调用代理类中的toString equals hashCode 和实现被代理类方法时都会导致方法栈溢出。

    CGLIB动态代理实现原理

    CGLIB(Code Generation Library)实现动态代理,并不要求被代理类必须实现接口,底层采用asm字节码生成框架生成代理类字节码(该代理类继承了被代理类)。所以被代理类一定不能定义为final class并且对于final 方法不能被代理。

    实现步骤

    Ehancer enhancer = new Enhancer() //Enhancer为字节码增强器,很方便对类进行扩展
    enhancer.setSuperClass(被代理类.class);
    enhancer.setCallback(实现MethodInterceptor接口的对象)
    enhancer.create()//返回代理对象,是被代理类的子类
    

    MethodInterceptor接口的intercept方法

    /**
    *obj 代理对象
    *method 委托类方法
    *arg 方法参数
    *MethodProxy 代理方法MethodProxy对象,每个方法都会对应有这样一个对象 
    *通过methodProxy.invokeSuper(obj,arg)方法调用委托类的方法
    */
    public Object intercept(Object obj, Method method, Object[] arg, MethodProxy proxy)
    

    实现代码

    Query 被代理类

    /**
     * @author duanyimiao
     * @create 2018-09-22 11:12 AM
     * @description 被代理类
     **/
    public class Query {
    
        public boolean query(String name){
            System.out.println("Query query"+name);
            return true;
        }
    
        /**
         * cglib 代理通过继承被代理类,所有final方法不能被重写,所以定义的final方法不能被代理
         * @param name
         * @return
         */
        public final  boolean query1(String name){
            System.out.println("Query query1"+name);
            return true;
        }
    }
    

    CglibProxy 方法拦截器

    /**
     * @author duanyimiao
     * @create 2018-09-22 11:04 AM
     * @description 目标方法拦截,在进行具体业务逻辑前后做一些额外逻辑
     **/
    public class CglibProxy implements MethodInterceptor {
    
        /**
         *cglib代理是通过继承被代理类的方式进行代理的
         *
         * @param o 目标实例对象(继承于传入的被代理类)
         * @param method 目标方法对象
         * @param args 方法参数
         * @param proxy 代理对象实例
         * @return
         * @throws Throwable
         */
        @Override
        public Object intercept(Object o, Method method, Object[] args, MethodProxy proxy) throws Throwable {
            //o.toString o.equals o.hashCode方法调用都会导致方法栈溢出
            System.out.println("o="+o.getClass()+" method="+method+" args="+ Arrays.toString(args)+" proxy="+proxy);
            System.out.println("before call add extera logic");
            //调用父类(被代理类)的方法
            boolean result = ((Boolean) proxy.invokeSuper(o,args)).booleanValue();
            System.out.println("after call add extera logic");
            return result;
        }
    }
    

    CglibProxyFactory 产生代理对象的工厂

    /**
     * @author duanyimiao
     * @create 2018-09-22 11:04 AM
     * @description
     **/
    public class CglibProxyFactory {
        private Enhancer enhancer = new Enhancer();
    private CglibProxy cglibProxy = new CglibProxy();
        public Object getProxyObject(Class cls) {
            enhancer.setSuperclass(cls);
            enhancer.setCallback(cglibProxy);
            return enhancer.create();
        }
    }
    

    Client 获取代理对象的Client

    /**
     * @author duanyimiao
     * @create 2018-09-22 11:18 AM
     * @description
     **/
    public class Client {
        public static void main(String[] args) {
            CglibProxyFactory cglibProxyFactory = new CglibProxyFactory();
            Query query = (Query)cglibProxyFactory.getProxyObject(Query.class);
            query.query("dynamo");
            //该方法不能进过代理对象,因为query1方法定义为final,因此不能被代理类重写,因此直接调用被代理类的query1方法
            query.query1("dynamo1");
        }
    }
    

    流程图

    在这里插入图片描述

    cglib动态生成的代理类

    import java.lang.reflect.Method;
    import net.sf.cglib.core.ReflectUtils;
    import net.sf.cglib.core.Signature;
    import net.sf.cglib.proxy.Callback;
    import net.sf.cglib.proxy.Factory;
    import net.sf.cglib.proxy.MethodInterceptor;
    import net.sf.cglib.proxy.MethodProxy;
    
    public class HelloServiceImpl$$EnhancerByCGLIB$$82ef2d06
      extends HelloServiceImpl
      implements Factory
    {
      private boolean CGLIB$BOUND;
      private static final ThreadLocal CGLIB$THREAD_CALLBACKS;
      private static final Callback[] CGLIB$STATIC_CALLBACKS;
      private MethodInterceptor CGLIB$CALLBACK_0;
      private static final Method CGLIB$sayHello$0$Method;
      private static final MethodProxy CGLIB$sayHello$0$Proxy;
      private static final Object[] CGLIB$emptyArgs;
      private static final Method CGLIB$finalize$1$Method;
      private static final MethodProxy CGLIB$finalize$1$Proxy;
      private static final Method CGLIB$equals$2$Method;
      private static final MethodProxy CGLIB$equals$2$Proxy;
      private static final Method CGLIB$toString$3$Method;
      private static final MethodProxy CGLIB$toString$3$Proxy;
      private static final Method CGLIB$hashCode$4$Method;
      private static final MethodProxy CGLIB$hashCode$4$Proxy;
      private static final Method CGLIB$clone$5$Method;
      private static final MethodProxy CGLIB$clone$5$Proxy;
      
      static void CGLIB$STATICHOOK1()
      {
        CGLIB$THREAD_CALLBACKS = new ThreadLocal();
        CGLIB$emptyArgs = new Object[0];
        Class localClass1 = Class.forName("proxy.HelloServiceImpl$$EnhancerByCGLIB$$82ef2d06");
        Class localClass2;
        Method[] tmp95_92 = ReflectUtils.findMethods(new String[] { "finalize", "()V", "equals", "(Ljava/lang/Object;)Z", "toString", "()Ljava/lang/String;", "hashCode", "()I", "clone", "()Ljava/lang/Object;" }, (localClass2 = Class.forName("java.lang.Object")).getDeclaredMethods());
        CGLIB$finalize$1$Method = tmp95_92[0];
        CGLIB$finalize$1$Proxy = MethodProxy.create(localClass2, localClass1, "()V", "finalize", "CGLIB$finalize$1");
        Method[] tmp115_95 = tmp95_92;
        CGLIB$equals$2$Method = tmp115_95[1];
        CGLIB$equals$2$Proxy = MethodProxy.create(localClass2, localClass1, "(Ljava/lang/Object;)Z", "equals", "CGLIB$equals$2");
        Method[] tmp135_115 = tmp115_95;
        CGLIB$toString$3$Method = tmp135_115[2];
        CGLIB$toString$3$Proxy = MethodProxy.create(localClass2, localClass1, "()Ljava/lang/String;", "toString", "CGLIB$toString$3");
        Method[] tmp155_135 = tmp135_115;
        CGLIB$hashCode$4$Method = tmp155_135[3];
        CGLIB$hashCode$4$Proxy = MethodProxy.create(localClass2, localClass1, "()I", "hashCode", "CGLIB$hashCode$4");
        Method[] tmp175_155 = tmp155_135;
        CGLIB$clone$5$Method = tmp175_155[4];
        CGLIB$clone$5$Proxy = MethodProxy.create(localClass2, localClass1, "()Ljava/lang/Object;", "clone", "CGLIB$clone$5");
        tmp175_155;
        Method[] tmp223_220 = ReflectUtils.findMethods(new String[] { "sayHello", "()V" }, (localClass2 = Class.forName("proxy.HelloServiceImpl")).getDeclaredMethods());
        CGLIB$sayHello$0$Method = tmp223_220[0];
        CGLIB$sayHello$0$Proxy = MethodProxy.create(localClass2, localClass1, "()V", "sayHello", "CGLIB$sayHello$0");
        tmp223_220;
        return;
      }
      
      final void CGLIB$sayHello$0()
      {
        super.sayHello();
      }
      
     	public final void sayHello()
    	  {
    	    MethodInterceptor tmp4_1 = this.CGLIB$CALLBACK_0;
    	    if (tmp4_1 == null)
    	    {
    	      tmp4_1;
    	      CGLIB$BIND_CALLBACKS(this);
    	    }
    	    if (this.CGLIB$CALLBACK_0 != null) {
    	     tmp17_14.intercept(this, CGLIB$sayHello$3$Method, CGLIB$emptyArgs, CGLIB$sayHello$3$Proxy);
    	    }
    	    super.sayHello();
    	  }
      
      final void CGLIB$finalize$1()
        throws Throwable
      {
        super.finalize();
      }
      
      protected final void finalize()
        throws Throwable
      {
        MethodInterceptor tmp4_1 = this.CGLIB$CALLBACK_0;
        if (tmp4_1 == null)
        {
          tmp4_1;
          CGLIB$BIND_CALLBACKS(this);
        }
        if (this.CGLIB$CALLBACK_0 != null) {
          return;
        }
        super.finalize();
      }
      
      final boolean CGLIB$equals$2(Object paramObject)
      {
        return super.equals(paramObject);
      }
      
      public final boolean equals(Object paramObject)
      {
        MethodInterceptor tmp4_1 = this.CGLIB$CALLBACK_0;
        if (tmp4_1 == null)
        {
          tmp4_1;
          CGLIB$BIND_CALLBACKS(this);
        }
        MethodInterceptor tmp17_14 = this.CGLIB$CALLBACK_0;
        if (tmp17_14 != null)
        {
          Object tmp41_36 = tmp17_14.intercept(this, CGLIB$equals$2$Method, new Object[] { paramObject }, CGLIB$equals$2$Proxy);
          tmp41_36;
          return tmp41_36 == null ? false : ((Boolean)tmp41_36).booleanValue();
        }
        return super.equals(paramObject);
      }
      
      final String CGLIB$toString$3()
      {
        return super.toString();
      }
      
      public final String toString()
      {
        MethodInterceptor tmp4_1 = this.CGLIB$CALLBACK_0;
        if (tmp4_1 == null)
        {
          tmp4_1;
          CGLIB$BIND_CALLBACKS(this);
        }
        MethodInterceptor tmp17_14 = this.CGLIB$CALLBACK_0;
        if (tmp17_14 != null) {
          return (String)tmp17_14.intercept(this, CGLIB$toString$3$Method, CGLIB$emptyArgs, CGLIB$toString$3$Proxy);
        }
        return super.toString();
      }
      
      final int CGLIB$hashCode$4()
      {
        return super.hashCode();
      }
      
      public final int hashCode()
      {
        MethodInterceptor tmp4_1 = this.CGLIB$CALLBACK_0;
        if (tmp4_1 == null)
        {
          tmp4_1;
          CGLIB$BIND_CALLBACKS(this);
        }
        MethodInterceptor tmp17_14 = this.CGLIB$CALLBACK_0;
        if (tmp17_14 != null)
        {
          Object tmp36_31 = tmp17_14.intercept(this, CGLIB$hashCode$4$Method, CGLIB$emptyArgs, CGLIB$hashCode$4$Proxy);
          tmp36_31;
          return tmp36_31 == null ? 0 : ((Number)tmp36_31).intValue();
        }
        return super.hashCode();
      }
      
      final Object CGLIB$clone$5()
        throws CloneNotSupportedException
      {
        return super.clone();
      }
      
      protected final Object clone()
        throws CloneNotSupportedException
      {
        MethodInterceptor tmp4_1 = this.CGLIB$CALLBACK_0;
        if (tmp4_1 == null)
        {
          tmp4_1;
          CGLIB$BIND_CALLBACKS(this);
        }
        MethodInterceptor tmp17_14 = this.CGLIB$CALLBACK_0;
        if (tmp17_14 != null) {
          return tmp17_14.intercept(this, CGLIB$clone$5$Method, CGLIB$emptyArgs, CGLIB$clone$5$Proxy);
        }
        return super.clone();
      }
      
      public static MethodProxy CGLIB$findMethodProxy(Signature paramSignature)
      {
        String tmp4_1 = paramSignature.toString();
        switch (tmp4_1.hashCode())
        {
        case -1574182249: 
          if (tmp4_1.equals("finalize()V")) {
            return CGLIB$finalize$1$Proxy;
          }
          break;
        }
      }
      
      public HelloServiceImpl$$EnhancerByCGLIB$$82ef2d06()
      {
        CGLIB$BIND_CALLBACKS(this);
      }
      
      public static void CGLIB$SET_THREAD_CALLBACKS(Callback[] paramArrayOfCallback)
      {
        CGLIB$THREAD_CALLBACKS.set(paramArrayOfCallback);
      }
      
      public static void CGLIB$SET_STATIC_CALLBACKS(Callback[] paramArrayOfCallback)
      {
        CGLIB$STATIC_CALLBACKS = paramArrayOfCallback;
      }
      
      private static final void CGLIB$BIND_CALLBACKS(Object paramObject)
      {
        82ef2d06 local82ef2d06 = (82ef2d06)paramObject;
        if (!local82ef2d06.CGLIB$BOUND)
        {
          local82ef2d06.CGLIB$BOUND = true;
          Object tmp23_20 = CGLIB$THREAD_CALLBACKS.get();
          if (tmp23_20 == null)
          {
            tmp23_20;
            CGLIB$STATIC_CALLBACKS;
          }
          local82ef2d06.CGLIB$CALLBACK_0 = (// INTERNAL ERROR //
    

    其中该代理会实现所有被代理类非final的方法,并且每个方法有两种形式:例如被代理类有sayHello()方法,那么代理类会有两个该方法

    final void CGLIB$sayHello$0()//该方法是通过在MethodInterceptor对象中methodProxy.invokeSuper(obj,args)进行调用的
      {
        super.sayHello();
      }
    
    public final void sayHello()
      {
        MethodInterceptor tmp4_1 = this.CGLIB$CALLBACK_0;
        if (tmp4_1 == null)
        {
          tmp4_1;
          CGLIB$BIND_CALLBACKS(this);
        }
        if (this.CGLIB$CALLBACK_0 != null) {
         tmp17_14.intercept(this, CGLIB$sayHello$3$Method, CGLIB$emptyArgs, CGLIB$sayHello$3$Proxy);
        }
        super.sayHello();
      }
    

    一个是重写了父类的sayHello方法,如果callback不为null就会调用 其中实现MethodInterprector接口的对象的invoke方法。

    实现MethodInterceptor接口的实例的intercept方法

        @Override
        public Object intercept(Object o, Method method, Object[] args, MethodProxy proxy) throws Throwable {
            //o.toString o.equals o.hashCode方法调用都会导致方法栈溢出
            System.out.println("o="+o.getClass()+" method="+method+" args="+ Arrays.toString(args)+" proxy="+proxy);
            System.out.println("before call add extera logic");
            //调用父类(被代理类)的方法
            boolean result = ((Boolean) proxy.invokeSuper(o,args)).booleanValue();
            System.out.println("after call add extera logic");
            return result;
        }
    

    代理类为每个委托方法都会生成两个方法
    在这里插入图片描述

    其中如果想要调用被代理类中的方法就需要使用 methodProxy.invokeSuper(obj,arg) 其中每个父类中非final的方法都会对应一个MethodProxy对象,下面看看其中该类中的invokeSuper方法:

    在这里插入图片描述

    和JDK动态代理最大区别就是,JDK是通过反射进行调用被代理对象的方法,而cglib是直接对被代理方法进行调用的。 通过类FastClassInfo存放了该被代理类和代理类对象,f1指向委托类对象,f2指向代理类对象,i1和i2分别代表着sayHello方法以及CGLIB$sayHello$0方法在类信息数组中的下标。其中

    在这里插入图片描述

    FastClass对Class对象进行特别的处理,比如将会用数组保存method的引用,每次调用方法的时候都是通过一个index下标来进行对类中某个方法调用。其中FastClassInfo中的f1、f2 i1、i2通过init方法进行初始化的,i1 和 i2是通过MethodProxy的getIndex方法就是通过方法签名来获得方法在存储了Class信息的数组中的下标。

    在这里插入图片描述

    从上面的源码分析可以看到invokeSuper方法调用实际上就是调用代理类的CGLIB$sayHello$0()方法然后该方法又调用其父类的sayHello方法。

    代理方式

    1、远程代理

    例如:客户端有个接口A 里面有多种方法,客户端想和通过接口A和服务端通讯,给服务端发送请求。客户端并不需要知道实现A接口具体实现,只管调用就行,和请求本地接口一样。 这里A接口的实现就可以看作访问服务端的代理。这个代理就帮你发送请求了,并获取结果吐给客户端。

    2、虚拟代理

    例如浏览器加载大图片时会很慢,加载文字就很快。所以通过虚拟代理进行优化,代理对象会记录下图片的url和尺寸,然后一并返回未加载出来图片,等到访问到该图片的位置再去请求该页面。这个时候就不会出现下面的加载很快的文字一直等待上面图片加载出来才显示。

    3、安全代理

    在访问具体对象的时候做一些权限的校验,这样更好的控制对象的访问。

    4、智能指引

    在访问真实对象时候做一些额外逻辑处理,例如记录对象访问次数和引用次数,该对象第一次访问加入缓存中,在访问一个实际对象前,检查是否已经锁定它。

    展开全文
  • 【设计模式之代理模式】—静态代理和动态代理

    千次阅读 热门讨论 2018-07-31 18:01:31
    前言 ...今天咱们来说下代理模式。 正文 代理模式 &amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;为另一个对象提供一个替身或占位符以控制这个对象的访问。 &

    前言

        很多优秀的框架中,巧妙的用到了各种设计模式,在解读源码的过程中,肯定非常需要再熟悉下设计模式。今天咱们来说下代理模式。

    正文

    代理模式

        为另一个对象提供一个替身或占位符以控制这个对象的访问。
         代理就是找个人帮你做事情,好比你要买房子,代理就相当于房地产中介,帮你去寻找合适的房源,我们需要把自己对房源的需求告诉房地产中介,中介在找房源的过程中还会和我们不断的沟通。

    为什么要有代理模式?

        为了解决直接操作目标对象给系统带来的不必要的复杂性。

    代理模式的种类

        根据代理创建时期的不同分为静态代理和动态代理,静态代理由开发人员或特定工具自动生成源代码,再对其编译,在程序运行之前 ,代理类已经生成了;动态代理在程序运行时,利用java的反射机制动态创建而成。

    静态代理

    UML 类图

    这里写图片描述

    demo

    //接口:声明目标对象需要让代理对象帮忙做的事情
    public interface Subject {
        public void buyHouse();   
    }
    /*********************************************************/
    //创建目标对象(真实对象)
    public class RealSubject implements Subject {
    
        @Override
        public void buyHouse() {
            System.out.println("我想买一个房子");
        }
    
    }
    /*********************************************************/
    //创建代理对象类,并通过代理类创建真实对象实例并访问其方法
    public class Proxy implements Subject
    {
    
        @Override
        public void buyHouse() {
            //真实对象买房之前的操作
            this.preBuyHouse();
            // 引用并创建真实对象实例  
            RealSubject realSubject = new RealSubject();
            //调用真实对象的购买方法
            realSubject.buyHouse();
           //真实对象买房之后的操作
           this.postBuyHouse();
    
        }
    
        public void preBuyHouse(){
            System.out.println("买房之前需要交中介费");
        }
        public void postBuyHouse(){
            System.out.println("买房之后的一些操作");
        }
    }
    /*********************************************************/
    //客户端测试
    public class Proxytest {
       public static void main(String[] args){
    
           Subject proxy = new Proxy();
           proxy.buyHouse();
    
       }
    }

        输出结果:
    这里写图片描述
        静态代理在使用时必须事先知道真实对象的存在,并将其作为代理对象的内部属性;一个真实对象对应一个代理对象,如果真实对象很多,那么代理对象的大量使用将是个灾难;此外,如果事先不知道真实对象是谁,    该怎么使用代理呢? 下面的动态代理就解决了这个问题。

    动态代理

    UML 类图

    这里写图片描述

    demo

    package dynamicproxy;
    
    import java.lang.reflect.InvocationHandler;
    import java.lang.reflect.Method;
    
    
    /**
     * 通过实现InvocationHandler 接口创建自己的调用处理器
     * @author zhang
     *
     */
    public class DynamicSubject implements InvocationHandler{
    
        //真实对象的引用
        private Object sub;
        public DynamicSubject(Object sub){
            this.sub = sub;
         }
    
        @Override
        //该方法集中处理动态代理类上的所有方法调用,第一个参数既是代理类实例,第二个参数是被调用的方法对象,第三个参数是调用参数,调用处理器根据这三个参数进行预处理或分派到委托类示例上反射执行
        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
           //方法调用之前
            System.out.println("方法调用之前:买房之前需要交中介费" + method);       
            //执行方法
            method.invoke(sub, args);
            //方法调用之后
            System.out.println("方法调用之后:买房之后的一些操作" + method);
            return null;
    
        }
    }
    /*********************************************************/
    //接口:声明目标对象需要让代理对象帮忙做的事情
    public interface Subject {
        public void buyHouse();   
    }
    /*********************************************************/
    //创建目标对象(真实对象)
    public class RealSubject implements Subject {
        @Override
        public void buyHouse() {
            System.out.println("我想买一个房子");
        }
    
    }
    /*********************************************************/
    //客户端测试
    public class Proxytest {
       public static void main(String[] args) throws Throwable{
    
           RealSubject realSubject = new RealSubject();
           InvocationHandler handler = new DynamicSubject(realSubject);
           //Proxy提供了静态方法为指定类装载器、一组接口以及调用处理器生成动态代理实例
           Subject subject = (Subject)Proxy.newProxyInstance(clazz.getClassLoader(), clazz.getInterfaces(), handler);
           /*  newProxyInstance 封装的步骤
            //通过为Proxy 类指定Classloader对象和一组Interface来创建动态代理类
           Class c = Proxy.getProxyClass(clazz.getClassLoader(),clazz.getInterfaces());
           //通过反射机制获得动态代理类的构造函数,其唯一参数类型是调用处理类接口类型
           Constructor ct = c.getConstructor(new Class[]{InvocationHandler.class});
           //通过构造函数创建动态代理类实例,构造时调用处理器对象作为参数被传入
           Subject subject = (Subject)ct.newInstance(new Object[]{handler});
           */
           subject.buyHouse();
    
       }
    }
    

        输出结果:
    这里写图片描述
        可以看出,JDK 动态代理使用反射实现的,反射中Proxy可以根据传入参数的不同(handler)在运行时产生不同的代理类,该代理类必须是实现了被代理类的接口,并且有个参数为InvocationHandler的构造函数,所以jdk动态代理的缺点是:只能代理接口定义的方法。
        为了解决这个问题,出现了动态代理的第二种实现方式—CGlib
        Cglib 是针对类来实现代理的,原理是对指定的业务类生成一个子类,并覆盖其中业务方法实现代理,因为采用的是继承,所以不能对final修饰的类进行代理。

    demo

    package cglibproxy;
    
    //定义业务类,不是必须要实现接口
    //创建目标对象(真实对象)
    public class RealSubject {
        public void buyHouse() {
            System.out.println("我想买一个房子");
        }
    }
    /*********************************************************/
    package cglibproxy;
    import java.lang.reflect.Method;
    
    import net.sf.cglib.proxy.Enhancer;
    import net.sf.cglib.proxy.MethodInterceptor;
    import net.sf.cglib.proxy.MethodProxy;
    
    /*
     * 通过实现MethodInterceptor方法代理接口,创建代理类
     */
    
    public class CglibProxy implements MethodInterceptor{
        // 创建加强器,用来创建动态代理类
        private Enhancer enhancer = new Enhancer();
    
        //传进去的clazz 为业务类对象,供代理方法中进行真正的业务方法调用
        public Object getProxy(Class clazz){
            //为加强器指定要代理的业务类(为此方法生成的代理类指定父类)
            enhancer.setSuperclass(clazz);
            //设置回调:对于代理类上所有方法的调用,都会调用callback,而callback则需要实现intercept()方法进行拦截
            enhancer.setCallback(this);
            //创建动态代理类对象并返回
            return enhancer.create();
        }
    
    
        //实现回调方法
        @Override
        public Object intercept(Object arg0, Method method, Object[] args, MethodProxy proxy) throws Throwable {
    
             //方法调用之前
            System.out.println("方法调用之前:买房之前需要交中介费" + method);       
            //执行方法,调用业务类(父类)中的方法
            Object res = proxy.invokeSuper(arg0, args);
            //方法调用之后
            System.out.println("方法调用之后:买房之后的一些操作" + method);
            return res;
    
        }  
    
    }
    /*********************************************************/
    package cglibproxy;
    
    import java.lang.reflect.Constructor;
    import java.lang.reflect.InvocationHandler;
    import java.lang.reflect.Proxy;
    
    //客户端测试
    public class Proxytest {
       public static void main(String[] args) {
    
           CglibProxy proxy = new CglibProxy();
           RealSubject rs  = (RealSubject) proxy.getProxy(RealSubject.class);
           rs.buyHouse();
    
    
       }
    }
    

    输出结果:

    这里写图片描述

    总结

    • JDK动态代理需要代理类和InvocationHandler接口
    • JDK 动态代理的目标类必须实现某个接口,如果目标类没有实现某个接口就不能生成代理对象。
    • CGlib 需要指定父类和回调方法
    • CGlib 原理是针对目标类生成一个子类,覆盖其中的所有方法,所以目标类和方法不能声明为private final类型。

    适用场景

    • 远程代理
          为一个对象在不同的地址空间提供局部代表。
    • 虚拟代理
          当需要创建开销非常大的对象时,使用一个小对象代理一个大对象。
    • 保护代理
          当需要控制对原始对象的访问时,给不同的用户提供不同的访问权限。
    • 智能引用代理
          当需要在访问对象时附加额外操作时,在不影响对象类的情况下,在访问对象时进行更多的操作。

    总结

        将代理模式复习了一遍,有些地方有疑问,对于CGlib动态代理方式接触的不多,在项目中慢慢体会吧。

    展开全文
  • 静态代理和动态代理的讲解和案例,有详细的注释
  • NULL 博文链接:https://wmwork2010.iteye.com/blog/631978
  • 静态代理和动态代理的区别和联系

    千次阅读 2019-06-13 22:46:58
    静态代理和动态代理的区别和联系 代理Proxy: Proxy代理模式是一种结构型设计模式,主要解决的问题是:在直接访问对象时带来的问题 代理是一种常用的设计模式,其目的就是为其他对象提供一个代理以控制对某个...

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

    代理Proxy:

     

    Proxy代理模式是一种结构型设计模式,主要解决的问题是:在直接访问对象时带来的问题

     

    代理是一种常用的设计模式,其目的就是为其他对象提供一个代理以控制对某个对象的访问。代理类负责为委托类预处理消息,过滤消息并转发消息,以及进行消息被委托类执行后的后续处理。

     

                          

     

     

    为了保持行为的一致性,代理类和委托类通常会实现相同的接口,所以在访问者看来两者没有丝毫的区别。通过代理类这中间一层,能有效控制对委托类对象的直接访问,也可以很好地隐藏和保护委托类对象,同时也为实施不同控制策略预留了空间,从而在设计上获得了更大的灵活性。

     

    更通俗的说,代理解决的问题当两个类需要通信时,引入第三方代理类,将两个类的关系解耦,让我们只了解代理类即可,而且代理的出现还可以让我们完成与另一个类之间的关系的统一管理,但是切记,代理类和委托类要实现相同的接口,因为代理真正调用的还是委托类的方法。

     

    使用场合举例:

    如果需要委托类处理某一业务,那么我们就可以先在代理类中统一处理然后在调用具体实现类

     

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

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

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

     

    下面分别用静态代理与动态代理演示一个示例:

    添加打印日志的功能,即每个方法调用之前和调用之后写入日志

     

    静态代理:

     

    具体用户管理实现类

     

    [java] view plain copy

     

     print?

    1. public class UserManagerImpl implements UserManager {  
    2.   
    3.     @Override  
    4.     public void addUser(String userId, String userName) {  
    5.         System.out.println("UserManagerImpl.addUser");  
    6.     }  
    7.   
    8.     @Override  
    9.     public void delUser(String userId) {  
    10.         System.out.println("UserManagerImpl.delUser");  
    11.     }  
    12.   
    13.     @Override  
    14.     public String findUser(String userId) {  
    15.         System.out.println("UserManagerImpl.findUser");  
    16.         return "张三";  
    17.     }  
    18.   
    19.     @Override  
    20.     public void modifyUser(String userId, String userName) {  
    21.         System.out.println("UserManagerImpl.modifyUser");  
    22.   
    23.     }  
    24. }  

     

    代理类--代理用户管理实现类

     

    [java] view plain copy

     

     print?

    1. public class UserManagerImplProxy implements UserManager {  
    2.   
    3.     // 目标对象  
    4.     private UserManager userManager;  
    5.     // 通过构造方法传入目标对象  
    6.     public UserManagerImplProxy(UserManager userManager){  
    7.         this.userManager=userManager;  
    8.     }  
    9.     @Override  
    10.     public void addUser(String userId, String userName) {  
    11.         try{  
    12.                 //添加打印日志的功能  
    13.                 //开始添加用户  
    14.                 System.out.println("start-->addUser()");  
    15.                 userManager.addUser(userId, userName);  
    16.                 //添加用户成功  
    17.                 System.out.println("success-->addUser()");  
    18.             }catch(Exception e){  
    19.                 //添加用户失败  
    20.                 System.out.println("error-->addUser()");  
    21.             }  
    22.     }  
    23.   
    24.     @Override  
    25.     public void delUser(String userId) {  
    26.         userManager.delUser(userId);  
    27.     }  
    28.   
    29.     @Override  
    30.     public String findUser(String userId) {  
    31.         userManager.findUser(userId);  
    32.         return "张三";  
    33.     }  
    34.   
    35.     @Override  
    36.     public void modifyUser(String userId, String userName) {  
    37.         userManager.modifyUser(userId,userName);  
    38.     }  
    39.   
    40. }  

     

    客户端调用

     

    [java] view plain copy

     

     print?

    1. public class Client {  
    2.   
    3.     public static void main(String[] args){  
    4.         //UserManager userManager=new UserManagerImpl();  
    5.         UserManager userManager=new UserManagerImplProxy(new UserManagerImpl());  
    6.         userManager.addUser("1111", "张三");  
    7.     }  
    8. }  

     

    静态代理类优缺点

     

    优点:

     

    代理使客户端不需要知道实现类是什么,怎么做的,而客户端只需知道代理即可(解耦合),对于如上的客户端代码,newUserManagerImpl()可以应用工厂将它隐藏,如上只是举个例子而已。

     

    缺点:

    1)代理类和委托类实现了相同的接口,代理类通过委托类实现了相同的方法。这样就出现了大量的代码重复。如果接口增加一个方法,除了所有实现类需要实现这个方法外,所有代理类也需要实现此方法。增加了代码维护的复杂度。

    2)代理对象只服务于一种类型的对象,如果要服务多类型的对象。势必要为每一种对象都进行代理,静态代理在程序规模稍大时就无法胜任了。如上的代码是只为UserManager类的访问提供了代理,但是如果还要为其他类如Department类提供代理的话,就需要我们再次添加代理Department的代理类。

     

    举例说明:代理可以对实现类进行统一的管理,如在调用具体实现类之前,需要打印日志等信息,这样我们只需要添加一个代理类,在代理类中添加打印日志的功能,然后调用实现类,这样就避免了修改具体实现类。满足我们所说的开闭原则。但是如果想让每个实现类都添加打印日志的功能的话,就需要添加多个代理类,以及代理类中各个方法都需要添加打印日志功能(如上的代理方法中删除,修改,以及查询都需要添加上打印日志的功能)

    即静态代理类只能为特定的接口(Service)服务。如想要为多个接口服务则需要建立很多个代理类。

     

    引入动态代理:

     

    根据如上的介绍,你会发现每个代理类只能为一个接口服务,这样程序开发中必然会产生许多的代理类

    所以我们就会想办法可以通过一个代理类完成全部的代理功能,那么我们就需要用动态代理

     

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

     

    Java中要想实现动态代理机制,需要java.lang.reflect.InvocationHandler接口和 java.lang.reflect.Proxy 类的支持

     

    java.lang.reflect.InvocationHandler接口的定义如下:

     

    [java] view plain copy

     

     print?

    1. //Object proxy:被代理的对象  
    2. //Method method:要调用的方法  
    3. //Object[] args:方法调用时所需要参数  
    4. public interface InvocationHandler {  
    5.     public Object invoke(Object proxy, Method method, Object[] args) throws Throwable;  
    6. }  

     

    java.lang.reflect.Proxy类的定义如下:

     

    [java] view plain copy

     

     print?

    1. //CLassLoader loader:类的加载器  
    2. //Class<?> interfaces:得到全部的接口  
    3. //InvocationHandler h:得到InvocationHandler接口的子类的实例  
    4. public static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h) throws IllegalArgumentException  

     

     

     

    动态代理:

     

    具体实现类

     

    [java] view plain copy

     

     print?

    1. public class UserManagerImpl implements UserManager {  
    2.   
    3.     @Override  
    4.     public void addUser(String userId, String userName) {  
    5.         System.out.println("UserManagerImpl.addUser");  
    6.     }  
    7.   
    8.     @Override  
    9.     public void delUser(String userId) {  
    10.         System.out.println("UserManagerImpl.delUser");  
    11.     }  
    12.   
    13.     @Override  
    14.     public String findUser(String userId) {  
    15.         System.out.println("UserManagerImpl.findUser");  
    16.         return "张三";  
    17.     }  
    18.   
    19.     @Override  
    20.     public void modifyUser(String userId, String userName) {  
    21.         System.out.println("UserManagerImpl.modifyUser");  
    22.   
    23.     }  
    24.   
    25. }  

     

     

    动态创建代理对象的类

     

    [java] view plain copy

     

     print?

    1. //动态代理类只能代理接口(不支持抽象类),代理类都需要实现InvocationHandler类,实现invoke方法。该invoke方法就是调用被代理接口的所有方法时需要调用的,该invoke方法返回的值是被代理接口的一个实现类  
    2.      
    3. public class LogHandler implements InvocationHandler {  
    4.   
    5.     // 目标对象  
    6.     private Object targetObject;  
    7.     //绑定关系,也就是关联到哪个接口(与具体的实现类绑定)的哪些方法将被调用时,执行invoke方法。              
    8.     public Object newProxyInstance(Object targetObject){  
    9.         this.targetObject=targetObject;  
    10.         //该方法用于为指定类装载器、一组接口及调用处理器生成动态代理类实例    
    11.         //第一个参数指定产生代理对象的类加载器,需要将其指定为和目标对象同一个类加载器  
    12.         //第二个参数要实现和目标对象一样的接口,所以只需要拿到目标对象的实现接口  
    13.         //第三个参数表明这些被拦截的方法在被拦截时需要执行哪个InvocationHandler的invoke方法  
    14.         //根据传入的目标返回一个代理对象  
    15.         return Proxy.newProxyInstance(targetObject.getClass().getClassLoader(),  
    16.                 targetObject.getClass().getInterfaces(),this);  
    17.     }  
    18.     @Override  
    19.     //关联的这个实现类的方法被调用时将被执行  
    20.     /*InvocationHandler接口的方法,proxy表示代理,method表示原对象被调用的方法,args表示方法的参数*/  
    21.     public Object invoke(Object proxy, Method method, Object[] args)  
    22.             throws Throwable {  
    23.         System.out.println("start-->>");  
    24.         for(int i=0;i<args.length;i++){  
    25.             System.out.println(args[i]);  
    26.         }  
    27.         Object ret=null;  
    28.         try{  
    29.             /*原对象方法调用前处理日志信息*/  
    30.             System.out.println("satrt-->>");  
    31.               
    32.             //调用目标方法  
    33.             ret=method.invoke(targetObject, args);  
    34.             /*原对象方法调用后处理日志信息*/  
    35.             System.out.println("success-->>");  
    36.         }catch(Exception e){  
    37.             e.printStackTrace();  
    38.             System.out.println("error-->>");  
    39.             throw e;  
    40.         }  
    41.         return ret;  
    42.     }  
    43.   
    44. }  

     

     

    被代理对象targetObject通过参数传递进来,我们通过targetObject.getClass().getClassLoader()获取ClassLoader对象,然后通过targetObject.getClass().getInterfaces()获取它实现的所有接口,然后将targetObject包装到实现了InvocationHandler接口的LogHandler对象中。通过newProxyInstance函数我们就获得了一个动态代理对象。

     

    客户端代码

    [java] view plain copy

     

     print?

    1. public class Client {  
    2.   
    3.     public static void main(String[] args){  
    4.         LogHandler logHandler=new LogHandler();  
    5.         UserManager userManager=(UserManager)logHandler.newProxyInstance(new UserManagerImpl());  
    6.         //UserManager userManager=new UserManagerImpl();  
    7.         userManager.addUser("1111", "张三");  
    8.     }  
    9. }  

     

     

    可以看到,我们可以通过LogHandler代理不同类型的对象,如果我们把对外的接口都通过动态代理来实现,那么所有的函数调用最终都会经过invoke函数的转发,因此我们就可以在这里做一些自己想做的操作,比如日志系统、事务、拦截器、权限控制等。这也就是AOP(面向切面编程)的基本原理。

     

    插曲:

     

    AOP(AspectOrientedProgramming):将日志记录,性能统计,安全控制,事务处理,异常处理等代码从业务逻辑代码中划分出来,通过对这些行为的分离,我们希望可以将它们独立到非指导业务逻辑的方法中,进而改变这些行为的时候不影响业务逻辑的代码---解耦。

     

    针对如上的示例解释:

     

    我们来看上面的UserManagerImplProxy类,它的两个方法System.out.println("start-->addUser()")和System.out.println("success-->addUser()"),这是做核心动作之前和之后的两个截取段,正是这两个截取段,却是我们AOP的基础,在OOP里,System.out.println("start-->addUser()")、核心动作、System.out.println("success-->addUser()")这个三个动作在多个类里始终在一起,但他们所要完成的逻辑却是不同的,如System.out.println("start-->addUser()")里做的可能是权限的判断,在所有类中它都是做权限判断,而在每个类里核心动作却各不相同,System.out.println("success-->addUser()")可能做的是日志,在所有类里它都做日志。正是因为在所有的类里,核心代码之前的操作和核心代码之后的操作都做的是同样的逻辑,因此我们需要将它们提取出来,单独分析,设计和编码,这就是我们的AOP思想。一句话说,AOP只是在对OOP的基础上进行进一步抽象,使我们的类的职责更加单一。

     

    动态代理优点:

     

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

     

    总结:

     

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

     

    代理对象就是把被代理对象包装一层,在其内部做一些额外的工作,比如用户需要上facebook,而普通网络无法直接访问,网络代理帮助用户先FQ,然后再访问facebook。这就是代理的作用了。

     

    纵观静态代理与动态代理,它们都能实现相同的功能,而我们看从静态代理到动态代理的这个过程,我们会发现其实动态代理只是对类做了进一步抽象和封装,使其复用性和易用性得到进一步提升而这不仅仅符合了面向对象的设计理念,其中还有AOP的身影,这也提供给我们对类抽象的一种参考。关于动态代理与AOP的关系,个人觉得AOP是一种思想,而动态代理是一种AOP思想的实现!

    展开全文
  • 包含静态代理和动态代理demo代码
  • 之前本人在设计模式中有写过静态代理和动态代理的相关代码测试,可以看下。 今天我们主要学一下理论相关知识。 静态代理动态代理动态代理模式主要由四个元素共同组成: 接口:定义具体实现的方法 被代理类:...
  • 静态代理(Static Proxy)和动态代理(Dynamic Proxy)静态代理jdk动态代理CGLIB动态代理 静态代理 静态代理要先抽象出一个接口,并且写一个实现类实现这个接口。 //主业务接口 public interface SomeService { ...
  • 在某些情况下,一个对象不想或者不能直接引用另一个对象,而代理对象可以再客户端目标对象之间起到中介的作用. (2)什么时候使用代理? 设计模式中有一个设计原则是开闭原则,是说对修改关闭对扩展开放,我们在工作中...
  • Java动态代理,Java静态代理,Java代理方式详解
  • 对于最近博主最近写博客的兴致大发,我也在思考:为什么而写博客?在互联网时代,无论你是牛人大咖,还是小白菜鸟,都有发表自己看法的权利。无论你是对的还是错的,都会在这个...同样,了解技术是一个深入拓展的...
  • java静态代理和动态代理

    千次阅读 2018-03-27 22:00:13
    代理委托类有共同的父类或父接口,这样在任何使用委托类对象的地方都可以用代理对象替代。代理类负责请求的预处理、过滤、将请求分派给委托类处理、以及委托类执行完请求后的后续处理。图1:代理模式从图中可以...
  • 一、代理设计模式 首先,要明确什么是代理。通俗的说,就是日常生活中的中介。比如你要租房子,找了中介,中介负责打理你的一切事务(找房、签约等),这些事务你都不用做,你就负责住房就可以。代理同理,如果你想...
  • 1 什么是代理 代理模式的定义:对其他对象提供一种代理以控制对这个对象的访问。举个例子,如A对象有若干个方法,这时A对象对B对象进行委托授权,B对象便成了A对象的代理方,因此B对象便可对A对象进行访问并调用A...
  • Java中的代理模式--静态代理和动态代理 Java中的代理模式--静态代理和动态代理
  • 简单介绍一下动态代理的基本原理实现过程,并不涉及代理对象如何自动生成的问题
  • https://blog.csdn.net/fangqun663775/article/details/78960545
  • 代理模式 代理模式(Proxy Pattern),又叫委托模式,是指为其他对象提供一种代理,以控制对这个对象的访问,属于结构型设计模式 目的是为了保护目标对象或增强目标对象 抽象角色-Subject:可以是抽象类,也可以是...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 193,041
精华内容 77,216
关键字:

静态代理和动态代理

友情链接: WinsockTransfer.rar