• 说说代理--动态代理


    动态代理:

    我们都知道,接口是不能被new的,只有类才能被new的。

    我们来看看Person接口,和实现了Person接口的Student类,到底有什么区别:

    public class Test01 {
        public static void main(String[] args) {
            Class<Person> personClass = Person.class;
            Constructor<?>[] constructors = personClass.getConstructors();
            System.out.println("===Person接口的构造器===");
            for (Constructor c:
                    constructors) {
                System.out.println(c);
            }
            System.out.println("===Person接口的方法===");
            Method[] methods = personClass.getMethods();
            for (Method m:
                    methods) {
                System.out.println(m);
            }
    
            Class<Student> studentClass = Student.class;
            Constructor<?>[] constructors2 = studentClass.getConstructors();
            System.out.println("===实现Person接口的Student类的构造器===");
            for (Constructor c:
                    constructors2) {
                System.out.println(c);
            }
            System.out.println("===实现Person接口的Student类的方法===");
            Method[] methods2 = studentClass.getMethods();
            for (Method m:
                    methods2) {
                System.out.println(m);
            }
    
        }
    }
    

      运行结果:

    Person接口,没有构造器,所以Person不能被new。

    Student类有构造器,所以可以被new。

    接口中只有2个方法

    实现类有2个方法和父类Object类的一些方法

    也就是说除了构造器之外,接口跟实现类的结构差不多。

    什么是动态代理:

    通过上一篇我们介绍完静态代理(见上文:https://www.cnblogs.com/takeyblogs/p/14035553.html)的优缺点之后,我们来用动态代理来弥补静态代理的缺点。

     通过查看API,我们发现Proxy类有一个静态方法:

     Proxy.getProxyClass(ClassLoader loader,Class<?>... interfaces):我们传入一个接口的Class对象,就可以给我返回一个代理类。

    上代码:

    public class Test {
        public static void main(String[] args) {
            Class<?> proxyClass = Proxy.getProxyClass(Person.class.getClassLoader(), Person.class);
            System.out.println(proxyClass.getName());
    
            System.out.println("-----打印代理对象的构造器-----");
            Constructor<?>[] constructors = proxyClass.getConstructors();
            for (Constructor c:
                    constructors) {
                System.out.println(c);
            }
            System.out.println("-----打印代理对象的方法-----");
            Method[] methods = proxyClass.getMethods();
            for (Method m:
                    methods) {
                System.out.println(m);
            }
        }
    }
    

      输出结果:

     通过Proxy.getProxyClass(),我们可以获得一个增强类,即包含了构造器,也包含了各种方法。与上文的对比,我们多了一个重要的构造器。

    简单梳理一下:

    1.我们一开始需要直接根据Person接口得到代理对象,但是我们发现了,接口没有构造器,没办法new出对象。

    2.我们假象,能不能构造出一个类,即有构造器,又包含Person接口的方法。

    3.我们发现java自带的Proxy类中有一个getProxyClass()方法,我们传入Person接口,它可以给我们返回一个带构造器又包含Person接口方法的Class对象。

    那我们现在获取到了代理对象了。我们能不能生成代理实例呢?

    public class Test {
        public static void main(String[] args) {
            try {
                Class<?> proxyClass = Proxy.getProxyClass(Person.class.getClassLoader(), Person.class);
                System.out.println(proxyClass.getName());
    
                Object o = proxyClass.newInstance();
                System.out.println(o);
            } catch (InstantiationException e) {
                e.printStackTrace();
            } catch (IllegalAccessException e) {
                e.printStackTrace();
            }
    
        }
    }  

    结果:

    我们发现直接去实例化是会报错的,因为newInstance()调用的是类的无参构造器,但是我们再上面看到的是,代理类的构造器是有参数的:

     那我们就给他传入一个参数:

    public class Test {
    public static void main(String[] args) throws Exception{
    Class<?> proxyClass = Proxy.getProxyClass(Person.class.getClassLoader(), Person.class);
    Constructor<?> constructor = proxyClass.getConstructor(InvocationHandler.class);
    //通过反射创建对象
    Person PersonProxy = (Person)constructor.newInstance(new InvocationHandler() {
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
    return null;
    }
    });

    System.out.println(PersonProxy.dance(1,2));
    }
    }

      至此,我们就创建出来了一个代理对象了。

    执行上面代码,结果:

    发现报了空指针异常。

    如果我们修改了invoke方法的返回值:

     我们会发现结果返回666。

    那么我们不难猜到,结果跟返回值有关系:

    我们知道,在实例化代理对象的时候,我们传入一个参数InvocationHandler,我们不难发现,代理对象中肯定有一个属性,来维护这个传入的对象,就像

    静态代理一样,我们传入一个目标对象,然后代理对象有一个属性来维护这个目标对象。

     为什么要这么设计呢?

    为了解耦,为了通用性。

    JVM生成代理对象,只生成一个空壳的方法,把具体的逻辑留给InvocationHandler去实现。

    API还有另一方法:

    public class Test {
        public static void main(String[] args) throws  Exception{
            Student student = new Student();
            Person proxy = (Person)getProxy(student);
            proxy.sing();
            proxy.dance(2,3);
        }
    
        public static Object getProxy(final Object target) throws Exception{
            Object proxy = Proxy.newProxyInstance(
                    target.getClass().getClassLoader(),
                    target.getClass().getInterfaces(),
                    new InvocationHandler() {
                        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                            System.out.println(method.getName() + "方法开始执行");
                            Object invoke = method.invoke(target, args);
                            System.out.println(invoke);
                            System.out.println(method.getName() + "方法结束");
                            return invoke;
                        }
                    }
            );
            return proxy;
        }
    }
    

      执行结果:

  • 相关阅读:
    Array
    java 设计模式
    Hashtable
    lettCode-Array
    最短路径 dijkstra
    算法:优先级队列
    7.29 DFS总结
    SZU:D89 The Settlers of Catan
    SZU:B47 Big Integer I
    7.25 RPN转换
  • 原文地址:https://www.cnblogs.com/takeyblogs/p/14046299.html
Copyright © 2020-2023  润新知