代理模式

200 阅读5分钟

1.代理模式

​ 为目标对象生成个替身,以控制对这个目标对象的访问。通过代理对象访问目标对象的好处是:可以在目标对象实现的基础上,增强额外的功能,即扩展目标对象的功能。

graph LR
 Client -->ProxyObject -->TargetObject

2.静态代理

静态代理在使用时,需要定义接口或者父类,被代理对象(目标对象)与代理对象需要一起实现相同的接口或者继承相同父类。
  1. 定义一个接口:IteachDao给被代理对象以及代理对象实现。

  2. 被代理对象实现TeacherDao实现接口IteachDao。

  3. 使用静态队里方式,就需要代理对象TeachserDaoProxy中也实现IteachDao接口。

  4. 调用的时候通过调用代理对象的方法来调用方法。

  5. 特别提醒:代理对象与目标对象要实现相同的接口,然后通过调用相同的方法来调用目标对象的方法。 思路分析图解(类图)

    48def7a27f7e4c37aba0da385e1ee710

    IteachDao类

    //接口给代理对象以及被代理对象实现
    public interface IteachDao(){
        void teach();
    }
    

    Client类

    public class Client{
        public static void main(String[] args){
            //创建被代理对象
            TeacherDao target = new TeacherDao();
            //创建代理对象,并通过构造方法把被代理对象传递代理对象
            TeacherDaoProxy proxy = new TeahcerDaoProxy(target);
            //通过代理对象调用被代理对象方法
            //即:执行代理对象的方法,代理对象再去执行被代理对象的方法
            proxy.teacher();
        }
    }
    

    TeacherDao类

    public class TeacherDao implements IteachDao(){
        @Override
        public void teach(){
            System.out.Println("老师授课中。。。");
        }
    }
    

    TeacherDaoProxy类

    public class TeacherDaoProxy implements IteacherDao(){
        private IteachDao target;
        //构造器
        public TeacherDaoProxy(IteachDao taeget){
            this.target = target;
        }
        
        @Override
        public void teach(){
            //增强功能,扩展
            System.out.Println("开始代理,完成课前操作");
            target.teach();
            //增强功能,扩展
            System.out.Println("结束代理,收尾操作");
        }
    }
    
    优缺点
    1. 优点:在不修改目标对象的前提下,通过代理对象对目标功能进行扩展。
    2. 缺点:因为代理对象需要与目标对象实现一样的接口,所以会有很多代理类,一旦接口增加方法,代理对象和目标对象都需要修稿。

3.cglib和jdk动态代理

代理对象不需要实现接口,但目标对象需要实现接口,代理对象的生成是利用jdk的API,动态的内存中构建代理对象。

jdk代理

  1. 代理类所在包:java.lang.reflect.Proxy

  2. JDK实现代理只需要使用newProxyInstance方法,该方法接受三个参数:static Object newProxyInstance(ClassLoader loader,Class<?>[] interfaces,InvocationHandel handel)

f57c3ff5880145fb96aef3e3b73283cb

IteacherDao类

//接口
public interface IteacherDao(){
    void teach();
    void sayHello(String name);
}

ProxyFactory类

public class ProxyFactory(){
    //维护一个目标对象
    private Object targer;
    //构造器 对target初始化
    public ProxyFactory(Object target){
        this.target = target;
    }
    
    //给目标对象生成一个代理对象
    public Object getProxyInstance(){
        /*
		 *  public static Object newProxyInstance(ClassLoader loader,
                                          Class<?>[] interfaces,
                                          InvocationHandler h)

            //1. ClassLoader loader : 指定当前目标对象使用的类加载器, 获取加载器的方法固定
            //2. Class<?>[] interfaces: 目标对象实现的接口类型,使用泛型方法确认类型
            //3. InvocationHandler h : 事情处理,执行目标对象的方法时,会触发事情处理器方法, 会把当前执行的目标对象方法作为参数传入
		 */
        return Proxy.newProxyInstance(taget.getCalss().getClassLoader(),target.getClass().getInterfaces(),new InvocationHandel(){
            @Override
            public Object invoke(Object proxy,Method method,Object[] args) throws Throwable{
                System.out.Println("Jdk动态代理开始~~");
                Object returnValue = method.invoke(target,args);
                System.out.Println("JDK代理提交");
                return returnValue;
            }
        });
    }
}

TeacherDao类

public class TeacherDao implements IteacherDao{
    @Override
    public void teache(){
        System.out.Println("老师授课中。。。");
    }
    
    @Override
    public void sayHello(String name){
        System.out.Println("hello "+name);
    }
}

Client类

public class Client{
  public static void main(String[] args){
    //创建目标对象
    TeacherDao target =new TeacherDao();
    //给目标对象创建代理对象可以转换成IteacherDao
    IteacherDao proxy = (IteacherDao)new ProxyFactory(target).getProxyInstance;
    // proxyInstance=class com.sun.proxy.$Proxy0 内存中动态生成了代理对象
		System.out.println("proxyInstance=" + proxyInstance.getClass());
    proxy.sayHello("lilei");
  }
}

cglib代理

  1. 静态代理和动态代理都需要目标对象实现一个接口,但有时候目标对象只是个简单的对象,并没有实现接口,这时候可以使用目标对象子类来实现代理这就是cglib代理。

  2. cglib代理也叫子类代理,他是在内存中构建个子类对象来实现对目标对象的扩展功能。

  3. cglib是一个高性能的代码生成包,可以在代码运行期扩展java类与实现java接口,它广泛的被许多Aop框架使用,例如Spring Aop实现方法拦截。

  4. 在Aop编程中如何选择代理:

    1. 目标对象需要实现接口:JDK代理

    2. 目标对象不需要实现接口:cglib代理

  5. cglib底层是通过使用字节码处理框架ASM来转换字节码并生成新的类。

  6. 需要引入新的maven

    <dependency>
        <groupId>cglib</groupId>
        <artifactId>cglib</artifactId>
        <version>3.3.0</version>
    </dependency>
    
  7. 在内存中构建类,注意不能代理final修饰的类,否则报错,抛出java.lang.IllegalArgumentException

  8. 目标对象的方法如果是static/final修饰,则不会被拦截,既不会执行目标方法之外额外的业务方法。

    be71513553b54345ab12e13a67a92412

TeacherDao类

public class teacherDao{
  public String teach(){
    System.out.Println("老师授课中 cglib代理,不需要实现任何接口。。。");
      return "hello";
  };
}

ProxyFactory类

public class ProxyFacotry implements MethodInterceptor{
  //维护一个目标对象
  private Object target;
  //构造器,传入一个被代理对象
  public ProxyFactory(Object target){
    this.target = target;
  }
  //返回一个代理对象
  public Object getProxyInstance(){
    //1.创建个工具类
    Enhancer enhancer = new Enhancer();
    //2.设置父类
    enhancer.setSuperClass(target.getClass());
    //3.设置回调函数
    enhancer.setCallback(this);
    //4.创建子类对象,即代理对象
    return enhancer.create();
  }
  
  //重写intercpt方法 会调用目标对象的方法
  @Override
  public Object intercpt(Object arg0,Method method,Object[] args,MethodProxy arg3){
    System.out.prinln("cglib代理开始~");
    Object returnValue =method.invake(target,args);
    System.out.println("cglib代理结束~");
  }
}

Client类

public class Client{
  public static void manin(String[] args){
    //创建目标对象
    TeacherDao target =new TeahcerDao();
    //传入目标对象,获取代理对象
    TeacherDao  proxy =(TeahcerDao) new ProxyFactory(target).getProxyInstance();
    //执行代理对象的方法,触发intercpt方法,从而实现对目标对象的调用
    String res = proxy.teach();
    System.out.println(res);
  }
}