zoukankan      html  css  js  c++  java
  • 设计模式-代理模式

    当年不做笔记,现在不会了,要重学框架,先把代理模式敲一遍。

    1.概念

    为其他对象提供一种代理以控制对这个对象的访问。代理对象起到中介作用,可去掉功能服务或增加额外的服务。有动态代理和静态代理两种实现方式,动态代理是Spring框架AOP思想的核心。

    2.涉及到的角色

    抽象角色:提供 一个接口或者抽象类 让真实/代理角色 实现或者继承

    真实/目标角色:实现抽象类,实现需要,被代理

    代理角色:在自己的方法里调用真实角色的方法,还可以补充扩展,充当中介作用。

    3.静态代理

    程序运行前就存在的代理类,即手写的

    public class StaticProxyTest {
        public static void main(String[] args) {//静态代理
            Producer producer=new Store();
            ProducerProxy pp=new ProducerProxy(producer);
            producer.sell();
            pp.sell();
        }
    }
    
    interface Producer {
        public void sell();
    }
    
    class Store implements Producer{
        public void sell() {
            System.out.println("商店在卖货!");
        }
    }
    
    class ProducerProxy implements Producer{
        private Producer producer;
        public ProducerProxy(Producer producer) {
            this.producer=producer;
        }
        public void sell() {
            System.out.print("代理");//代理类对被代理类的方法增添的功能
            producer.sell();//在代理类里调用 被代理类的方法
        }
    }

    4.JDK动态代理

    代理对象不需要实现接口,但是真实对象需要实现所有接口,

    import java.lang.reflect.Proxy;
    
    public class JDKProxyTest {
        public static void main(String[] args) {//JDK动态代理
            Producer producer=new Store();
            Producer pp=(Producer)Proxy.newProxyInstance(producer.getClass().getClassLoader(), 
                    producer.getClass().getInterfaces(), (proxy,method,args1)->{
                        Object invoke=method.invoke(producer, args1);
                        return invoke;
                    });
            pp.sell();
        }
    }
    
    interface Producer {
        public void sell();
    }
    
    class Store implements Producer{
        public void sell() {
            System.out.println("商店在卖货!");
        }
    }

    static Object newProxyInstance(ClassLoader loader, 类<?>[] interfaces, InvocationHandler h);

    //java.lang.reflect.Proxy的方法,API原话是“返回指定接口的代理类的实例,该接口将方法调用分派给指定的调用处理程序”。说白了就是创建一个代理类的对象,可以使用真实类的方法。

    对参数涉及到的几个方法了解一下

    (1)ClassLoader getClassLoader();

    获取类加载器

    (2)类<?>[] getInterfaces();

    Class的方法,获取 该对象表示的类 实现的接口

    (3)InvocationHandler的实例化需要实现invoke()方法,这个方法很陌生,通过下面的代码来理解。

    Object invoke(Object object,Object... args);//Method类的方法,在具有指定参数的 方法对象上调用此 方法对象表示的底层方法

    import java.lang.reflect.Method;
    
    public class Demo {
        
        public static void main(String[] args) throws Exception {
            Class c=Class.forName("demo.Test");//获取类
            Test test=(Test)c.newInstance();//创建Test类的实例对象test
            Method method = c.getMethod("run",String.class,Integer.class);//获取名叫"run"并且参数类型分别是String和Integer的方法
            method.invoke(test, null,336);//对象test调用run方法,参数可以0-无限多个
            //getMethod方法的名字、参数个数要正确,并且调用的invoke方法参数也要正确
        }
    }
    
    class Test {
        public void run(String s,Integer i) {
            System.out.println("s:" + s + " i:" + i);
        }
    }
    /*输出
    s:null i:336
    */

     

    较详细一些的JDK动态代理

    import java.lang.reflect.InvocationHandler;
    import java.lang.reflect.Method;
    import java.lang.reflect.Proxy;
    
    public class JDKProxyTest {
        
        public static void main(String[] args) {//JDK动态代理
            Producer producer=new Store();
            ClassLoader classLoader=producer.getClass().getClassLoader();
            Class<?>[] c=producer.getClass().getInterfaces();
            InvocationHandler h=new InvocationHandler(){
                public Object invoke(Object proxy,Method method,Object[] args) throws Exception {
                    System.out.println("发财1");
                    Object res=method.invoke(producer,args);//args是把原来的若干个参数都弄进数组
                    System.out.println("发财2");
                    return res;
                }
            };
            Producer pp=(Producer)Proxy.newProxyInstance(classLoader, c,h);
            System.out.println("发财3");
            pp.sell();//代理对象pp通过invoke方法调用真实类的方法
            System.out.println("发财4");
        }
    }
    
    interface Producer {
        public void sell();
    }
    
    class Store implements Producer{
        public void sell() {
            System.out.println("商店在卖货!");
        }
    }
    /*输出:
    发财3
    发财1
    商店在卖货!
    发财2
    发财4
    */

    5.Cglib动态代理

    真实对象可以没有抽象类,是单独一个类

    需要导入Spring-core包,使用某些类

    import java.lang.reflect.Method;
    
    import org.springframework.cglib.proxy.Enhancer;
    import org.springframework.cglib.proxy.MethodInterceptor;
    import org.springframework.cglib.proxy.MethodProxy;
    
    public class CglibProxyTest {
    
        public static void main(String[] args){
            Store store=new Store();
            Store p=new CgLibProxy(store).proxy();
            p.sell();
        }
    }
    
    class Store{
        public void sell() {
            System.out.println("商店在卖货!");
        }
    }
    
    class CgLibProxy implements MethodInterceptor {
        private Store store;
        
        public CgLibProxy(Store store){
            this.store=store;
        }
        
        public Store proxy() {
            Enhancer enhancer = new Enhancer();//Enhancer是一个字节码增强器,可以用来为无接口的类创建代理。
            enhancer.setSuperclass(Store.class);//设置代理的目标类
            enhancer.setCallback(this);//照抄,全网都没有解释
            return (Store)enhancer.create();
        }
        
        @Override //接口要实现的方法
        public Object intercept(Object object, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
            System.out.println("发财1");
            Object res=method.invoke(store, args);//和JDK代理一样,通过invoke调用方法
            System.out.println("发财2");
            return res;
        }
    }
    /*输出:
    发财1
    商店在卖货!
    发财2
    */

    CglibProxy类就像是一个代理工厂,写一个proxy()方法来生产真实类的代理对象。

     

    6.小结

    • 静态代理:可以做到在不修改目标对象的功能前提下,对目标功能扩展;代理对象需要与目标对象实现一样的接口,所以会有很多代理类,类太多.同时,一旦接口增加方法,目标对象与代理对象都要维护。
    • JDK动态代理:代理对象不需要实现接口;但目标类一定要实现接口
    • Cglib动态代理:目标类不需要实现接口,可以是单独的类。

    7.JDK代理 vs Cglib代理

    JDK动态代理使用Java的反射技术生成代理类,只能代理实现了接口的类,没有实现接口的类不能实现动态代理,CGLib会在运行时动态的生成一个被代理类的子类,子类重写了被代理类中所有非final的方法,在子类中采用方法拦截的技术拦截所有父类方法的调用,不需要被代理类对象实现接口,从而CGLIB动态代理效率比Jdk动态代理反射技术效率要高


    参考&引用

    https://www.jianshu.com/p/bacaafb5d02d

  • 相关阅读:
    排序算法<No.3>【桶排序】
    排序算法<No.2>【快速排序】
    排序算法<No.1> 【计数排序】
    排序问题思考(要求时间和空间复杂度尽可能的低)【Part 1】
    elasticsearch【cat API,系统数据】指令汇总
    netty研究【1】:编译源代码
    D3树状图给指定特性的边特别显示颜色
    zabbix3.0安装之图形界面显示异常【server】
    计算一维组合数的java实现
    zabbix3.0安装【server】
  • 原文地址:https://www.cnblogs.com/shoulinniao/p/12825034.html
Copyright © 2011-2022 走看看