动态代理(JDK动态代理和Cglib代理)

两者区别:

1.JDK动态代理只能代理实现了接口的类,动态代理类的字节码在程序运行时由Java反射机制动态生成。
2.Cglib是可以代理没有实现接口的类,cglib是针对类来实现代理的,他的原理是对指定的目标类生成一个子类,并覆盖其中方法实现增强,所以不能对final修饰的类进行代理。底层采用ASM实现。

代理模式:

代理模式最大的特点就是代理类和实际业务类实现同一个接口(或继承同一父类),代理对象持有一个实际对象的引用,外部调用时操作的是代理对象,而在代理对象的内部实现中又会去调用实际对象的操作。

 

JDK动态代理:

JDK动态代理其实内部也是通过Java反射机制来实现的,即已知的一个对象,然后在运行时动态调用其方法,这样在调用前后作一些相应的处理。

实例:

    我们大家都知道微商代理,简单地说就是代替厂家卖商品,厂家“委托”代理为其销售商品。关于微商代理,首先我们从他们那里买东西时通常不知道背后的厂家究竟是谁,也就是说,“委托者”对我们来说是不可见的;其次,微商代理主要以朋友圈的人为目标客户,这就相当于为厂家做了一次对客户群体的“过滤”。我们把微商代理和厂家进一步抽象,前者可抽象为代理类,后者可抽象为委托类(被代理类)。通过使用代理,通常有两个优点,并且能够分别与我们提到的微商代理的两个特点对应起来

代理的优点:

优点一:可以隐藏委托类的实现;

优点二:可以实现客户与委托类间的解耦,在不修改委托类代码的情况下能够做一些额外的处理。

静态代理

代理类在程序运行前就已经存在,那么这种代理方式被成为 静态代理 ,这种情况下的代理类通常都是我们在Java代码中定义的。 通常情况下, 静态代理中的代理类和委托类会实现同一接口或是派生自相同的父类

public interface Sell { 
   void sell();
   void ad(); 
}
//Vendor代表生产厂家
public class Vendor implements Sell {
   public void sell() { 
      System.out.println("In sell method"); 
   } 
   public void ad() { 
      System,out.println("ad method") ;
   } 
} 


//BusinessAgent代表代理厂家 //静态代理可以通过聚合来实现,让代理类持有一个委托类的引用即可 public class BusinessAgent implements Sell { private Vendor mVendor; public void sell() { //可以在代理类里面做一些额外处理 System.out.println("before"); mVendor.sell(); } public void ad() { mVendor.ad(); } }

JDK动态代理

在使用动态代理时,我们需要定义一个位于代理类与委托类之间的中介类,这个中介类被要求实现InvocationHandler接口,这个接口的定义如下:

public interface InvocationHandler { 
    Object invoke(Object proxy, Method method, Object[] args); 
}

当我们调用代理类对象的方法时,这个“调用”会转送到invoke方法中,代理类对象作为proxy参数传入,参数method标识了我们具体调用的是代理类的哪个方法,args为这个方法的参数。这样一来,我们对代理类中的所有方法的调用都会变为对invoke的调用,这样我们可以在invoke方法中添加统一的处理逻辑。

中介类:

public class DynamicProxy implements InvocationHandler { 
   private Object obj; //持有一个委托类对象; 
   public DynamicProxy(Object obj) { 
      this.obj = obj;  
   } 
 
   @Override 
   public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { 
      System.out.println("before"); 
      Object result = method.invoke(obj, args); 
      System.out.println("after"); 
      return result; 
   } 
}

invoke方法里面的参数是 生成的代理对象,目标对象的方法(通过反射调用),目标对象方法的参数

以上代码中

中介类持有一个委托类对象引用,在invoke方法中调用了委托类对象的相应方法,看到这里是不是觉得似曾相识?通过聚合方式持有委托类对象引用,把外部对invoke的调用最终都转为对委托类对象的调用。这不就是我们上面介绍的静态代理的一种实现方式吗?实际上,中介类与委托类构成了静态代理关系,在这个关系中,中介类是代理类,委托类就是委托类; 代理类与中介类也构成一个静态代理关系,在这个关系中,中介类是委托类,代理类是代理类。也就是说,动态代理关系由两组静态代理关系组成,这就是动态代理的原理

动态代理生成类:

利用反射机制动态生成

public class Main { 
  public static void main(String[] args) { 
    //创建中介类实例 
    DynamicProxy inter = new DynamicProxy(new Vendor()); 
   //加上这句将会产生一个$Proxy0.class文件,这个文件即为动态生成的代理类文件 
    System.getProperties().put("sun.misc.ProxyGenerator.saveGeneratedFiles","true"); 
   //动态生成代理类
Sell sell = (Sell)(Proxy.newProxyInstance(Sell.class.getClassLoader(), new Class[] {Sell.class}, inter));
//通过代理类对象调用代理类方法,实际上会转到invoke方法调用
sell.sell();
  } 
} 

newProxyInstance():这个函数实际上就是通过反射动态生成代理类。三个参数分别是类加载器,实现的接口,中介类对象。

直接通过代理类调用方法就会转到中介类的invoke方法中再去调用委托类中的反复。

JDK动态代理只能代理实现接口的类也是因为这个函数中参数必须有实现类的接口列表,所以导致只能代理实现接口的类。

 

Cglib动态代理:cglib是针对类来实现代理的,他的原理是对指定的目标类生成一个子类,并覆盖其中方法实现增强,但因为采用的是继承,所以不能对final修饰的类进行代理。

package com.Model.CGlibProxy;

public interface AddBook {
    public void addbook();
}

public class AddBookImp implements AddBook {

    @Override
    public void addbook() {
        // TODO Auto-generated method stub
        System.out.println("添加书籍....");
    }

}
public class CglibProxy implements MethodInterceptor {

    private Object target;
    public Object getInstance(Object target) {
        this.target = target;
        Enhancer enhancer = new Enhancer();
        // 设置自己的父类
        enhancer.setSuperclass(target.getClass());
        // 关联的要使用哪个对象的回调函数 这里指向自己这个对象的回调 那么就是下面这个方面了
        enhancer.setCallback(this);
//创建代理对象
return enhancer.create(); } @Override public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable { System.out.println("调用前"); // 调用父类函数 Object result = methodProxy.invokeSuper(o, objects); System.out.println("调用后"); return null; } public static void main(String[] args) { CglibProxy cglibProxy = new CglibProxy(); AddBookImp oBookImp = (AddBookImp) cglibProxy.getInstance(new AddBookImp());//这里其实就相当于生成了一个加强的类 oBookImp.addbook(); } }

 

 参考:

http://www.apigo.cn/2018/11/27/javacore6/

posted @ 2018-12-11 09:34  LeeJuly  阅读(245)  评论(0)    收藏  举报