代理模式初探

代码地址:https://git.oschina.net/wizards/jdk1.8Test.gitjava

简介

代理,proxy;生活中例子很常见,好比房产中介,租客和房主实际上不须要见面,只须要中介就能完成租房活动.这样租客和房主都能节省时间,保障人身安全;固然,他们须要付一些中介费.但只要中介的费用在接受范围以内,一切仍是值得的. 在java开发中会用一些状况,须要咱们采用这种模式.即屏蔽掉全部与咱们目标不相关的方法,属性,常量,只获取全部与目标相关的方法和属性.git

简单构造以下:spring

package com.wangge.proxy.simple;

public class SimpleProxy {
  private Shop shop = new Shop();
  public void sell() {
    shop.sellFood();
  }
}

class Shop {
  void sellFood() {
    System.out.println("卖出一个吃的");
  }
}

这是一个简单的代理模式,也实现了功能的封装.数组

抽象角色:声明真实对象和代理对象的共同接口;安全

代理角色:代理对象角色内部含有对真实对象的引用,从而能够操做真实对象,同时代理对象提供与真实对象相同的接口以便在任什么时候刻都能代替真实对象。同时,代理对象能够在执行真实对象操做时,附加其余的操做,至关于对真实对象进行封装。ide

真实角色:代理角色所表明的真实对象,是咱们最终要引用的对象函数

目前的代理通常分为静态代理和动态代理;性能

静态代理

静态代理也叫接口代理.此时代理的角色是接口.this

代码以下;.net

package com.wangge.proxy.interfaceCase3;

public interface Waiter {
  public void service(int sum);
}

package com.wangge.proxy.interfaceCase3;

public class HotelWaiter implements Waiter {
  
  @Override
  public void service(int xx) {
    System.out.println("我要订" + xx+"个房间!!");
  }
  
}


package com.wangge.proxy.interfaceCase3;

public class RestaurantWaiter implements Waiter{

  @Override
  public void service(int xx) {
    System.out.println("给你"+xx+"道菜!!");
  }
  
}


package com.wangge.proxy.interfaceCase3;

import java.util.HashMap;
import java.util.Map;

/**
 * 标准代理模式<br/>
 * date: 2016年11月10日 上午9:51:40 <br/>
 * 
 * @author yangqc
 * @version
 * @since JDK 1.8
 */
public class WaiterProxy implements Waiter {
  private Waiter waiter;
    
  public WaiterProxy(Waiter waiter) {
    super();
    this.waiter = waiter;
  }

  @Override
  public void service(int sum) {
    waiter.service(sum);
    
  }
  
  public static void main(String[] args) {
    Waiter waiter=new WaiterProxy(new HotelWaiter());
    waiter.service(2);
  }
}

接口Waiter,Waiter的两个实现类. WaiterProxy是Waiter的代理类.经过Waiter的实现类来建立WaiterProxy的代理类,经过代理类来实现接口的方法.

动态代理

Java动态代理类位于Java.lang.reflect包下,通常主要涉及到如下两个类:

(1). Interface InvocationHandler:该接口中仅定义了一个方法Object:invoke(Object obj,Method method, Object[] args)。在实际使用时,第一个参数obj通常是指代理类,method是被代理的方法,如上例中的request(),args为该方法的参数数组。 这个抽象方法在代理类中动态实现。

(2).Proxy:该类即为动态代理类,做用相似于上例中的ProxySubject,其中主要包含如下内容: Protected Proxy(InvocationHandler h):构造函数,估计用于给内部的h赋值。

Static Class getProxyClass (ClassLoader loader, Class[] interfaces):得到一个代理类,其中loader是类装载器,interfaces是真实类所拥有的所有接口的数组。

Static Object newProxyInstance(ClassLoader loader, Class[] interfaces, InvocationHandler h):返回代理类的一个实例,返回后的代理类能够看成被代理类使用(可以使用被代理类的在Subject接口中声明过的方法)。

所谓 Dynamic Proxy是这样一种class:它是在运行时生成的class,在生成它时你必须提供一组interface给它,而后该class就宣称它实现了这些 interface。你固然能够把该class的实例看成这些interface中的任何一个来用。固然啦,这个Dynamic Proxy其实就是一个Proxy,它不会替你做实质性的工做,在生成它的实例时你必须提供一个handler,由它接管实际的工做。(参见文献3)

在使用动态代理类时,咱们必须实现InvocationHandler接口,以第一节中的示例为例:

代码: //抽象角色(以前是抽象类,此处应改成接口):

public interface Subject{
     public void request();
} 

//具体角色RealSubject:实现了Subject接口的request()方法。
public class RealSubject implements Subject{ 
     public RealSubject(){

     }
     public void request(){ 
           System.out.println("From real subject."); 
     } 
}

//代理角色:
import java.lang.reflect.Method;
import java.lang.reflect.InvocationHandler;
public class DynamicSubject implements InvocationHandler{
     private Object sub; 
     public DynamicSubject(Object sub){            
           this.sub = sub;
     }
     public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
         System.out.println("before calling " + method); 
         method.invoke(sub,args); 
         System.out.println("after calling " + method); 
         return null; 
     }
}

客户端代码以下

mport java.lang.reflect.InvocationHandler;
import java.lang.reflect.Proxy;
import java.lang.reflect.Constructor;
import java.lang.reflect.Method;
public class Client{ 
  static public void main(String[] args) throws Throwable{
  RealSubject rs = new RealSubject(); //在这里指定被代理类
  InvocationHandler ds = new DynamicSubject(rs); //初始化代理类
  Class cls = rs.getClass();
  //如下是分解步骤
  /*
  Class c = Proxy.getProxyClass(cls.getClassLoader(),cls.getInterfaces());
  Constructor ct=c.getConstructor(new Class[]{InvocationHandler.class});
  Subject subject =(Subject) ct.newInstance(new Object[]{ds});
*/

//如下是一次性生成

  Subject subject = (Subject) Proxy.newProxyInstance(cls.getClassLoader(),cls.getInterfaces(),ds);
  subject.request();
}

经过这种方式,被代理的对象(RealSubject)能够在运行时动态改变,须要控制的接口(Subject接口)能够在运行时改变,控制的方式(DynamicSubject类)也能够动态改变,从而实现了很是灵活的动态代理关系。

可是,JDK的动态代理依靠接口实现,若是有些类并无实现接口,则不能使用JDK代理,这就要使用cglib动态代理了。

Cglib动态代理

JDK的动态代理机制只能代理实现了接口的类,而不能实现接口的类就不能实现JDK的动态代理,cglib是针对类来实现代理的,他的原理是对指定的目标类生成一个子类,并覆盖其中方法实现加强,但由于采用的是继承,因此不能对final修饰的类进行代理。不能重写final,static,private,protected,default 修饰的方法;

package com.wangge.proxy.cglibProxy;

public class Sayhello {
  public void hello() {
    System.out.println("say hello!!");
  }
  
  final public void sayBye() {
    System.out.println("Bye Bye!!");
  }
  
  static void eat() {
    System.out.println("eating food !!");
  }
  
  public static void main(String[] args) {
    CglibProxy proxy = new CglibProxy();
    Sayhello say = (Sayhello) proxy.getProxy(Sayhello.class);
    say.hello();
    //子类没法重写父类的final方法和static方法
    say.sayBye();//不会执行代理逻辑
    say.eat();//不会执行代理逻辑
    //没法继承final类,抛出异常:java.lang.IllegalArgumentException
//    SayBye bye = (SayBye) proxy.getProxy(SayBye.class);
//    bye.sayBye();
  }
}

package com.wangge.proxy.cglibProxy;

public final class SayBye {
  public void sayBye(){
    System.out.println("I can ever leave !");
  }
}


package com.wangge.proxy.cglibProxy;

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 CglibProxy implements MethodInterceptor{  
  private Enhancer enhancer = new Enhancer();  
  public Object getProxy(Class<?> clazz){  
   //设置须要建立子类的类  
   enhancer.setSuperclass(clazz);  
   enhancer.setCallback(this);  
   //经过字节码技术动态建立子类实例  
   return enhancer.create();  
  }  
  //实现MethodInterceptor接口方法  
  @Override
  public Object intercept(Object obj, Method method, Object[] args,  
    MethodProxy proxy) throws Throwable {  
   System.out.println("前置代理");  
   //经过代理类调用父类中的方法  
   Object result = proxy.invokeSuper(obj, args);  
   System.out.println("后置代理");  
   return result;  
  }
  
 }

CGLib建立的动态代理对象性能比JDK建立的动态代理对象的性能高很多,可是CGLib在建立代理对象时所花费的时间却比JDK多得多,因此对于单例的对象,由于无需频繁建立对象,用CGLib合适,反之,使用JDK方式要更为合适一些。同时,因为CGLib因为是采用动态建立子类的方法,对于final方法,没法进行代理。

使用场景

代理模式的其余应用: 1,远程代理,为一个对象在不一样的地址空间提供局部表明。这样能够隐藏一个对象存在于不一样地址空间的事实。

2,虚拟代理,根据须要建立开销很大的对象。经过它来存放实例化须要很长时间的真实对象。例如,网页中在图片出来之前现出来文字。

3,安全代理,用来控制真实对象访问时的权限。

4,智能代理,是指当调用真实的对象时,代理处理另一些事。如AOP 5.与bean做用域相关,看成用域时间长的bean引用做用域引用时间短的bean,须要使用代理. 详见https://my.oschina.net/u/1590027/blog/731904文中Scoped beans as dependencies段.

相关文章
相关标签/搜索