`
喻红叶
  • 浏览: 39486 次
  • 性别: Icon_minigender_1
  • 来自: 哈尔滨
社区版块
存档分类
最新评论

Java与模式-动态代理模式

 
阅读更多

代理模式

代理模式:为另一个对象提供一个替身或者占位符以控制对这个对象的访问。使用代理模式创建代理对象,让代理对象控制某个对象的访问,被代理的对象可以是远程的对象或需要安全控制的对象。我觉得,在C语言中,指针就是一个代理对象,真正存储数据的是内存中的另外一块区域。Java提供了API可以生成动态代理,所以把标准的代理模式称为静态代理模式。在静态代理模式中有三类角色,理解了这三类角色也就理解了代理模式:

a.抽象对象:定义了真实角色和抽象角色的公共接口(可以是类,也可以是接口)Subject;

b.代理角色:代理角色内部包含有对真实角色的引用,通过这个引用去执行真实角色想要完成的任务;除此之外,代理角色可以完成其他的一些功能;

c.真实角色:实际要完成任务的角色,是我们始终要引用的对象。

代理角色和真实角色均实现了(或继承了)抽象角色。

写一段代码来举例:

//抽象角色,定义公共部分
interface Subject { 
    public void request();
}

//真实角色
class RealSubject implements Subject {
    public void request() {
        do something;
   } 
}

//抽象角色,内部保存真实角色的引用
class ProxySubject implements Subject {
    private RealSubject sub;
    
    public ProxySubject(RealSubject obj) {
        this.sub = obj;//获得真实角色的实例引用
    }

   public void request() {
       sub.request();//通过真实角色的引用去执行最终要完成的任务
  }
}

public static void main(String[] args) {
		Subject proxy = new ProxySubject();
		proxy.request();
}

代理模式有很多中变形,这个很值得研究一番,不过今天我的重点是Java中的动态代理。

动态代理

上面的代理模式存在一个问题,那就是需要为不同的接口单独提供了一个代理类。现在有这样一个问题,系统中有10个类的方法在调用之前,需要进行安全验证,这10个类分别实现了不同的接口。这种情况恰好是代理模式发挥作用的地方,在真正的方法调用之前,现在代理类里面进行安全验证,如果通过了就去调用真正的方法,否则返回。这样既达到了安全验证的目的,又不违反开闭原则。可是,安全验证都是一样的,难道我们真的要去实现十个代理类,然后去做同样的安全验证吗?重复代码就意味着坏的气息啊。Java的动态代理就可以帮助我们搞定这件事情。动态代理,顾名思义就是动态的生成代理类,去代理服务。在看Java的动态代理之前,还是先分析一下如果要实现动态代理,需要哪些条件:

(1)在代理模式中,客户端实际上是在跟代理角色打交道,它以为它操作的是真实对象,但其实是代理对象,但是这种代理对客户端是透明的,客户端其实是面向接口编程,它只知有抽象接口,而不知具体的实现类。所以我们要定义抽象接口,真实角色和代理角色全部的都实现抽象接口,这样就实现了对客户端的透明代理。

(2)动态代理也只是代理,代理无非就是在真正的方法执行之前或者之后添加一些逻辑,但是主要的业务逻辑还需要真实角色去实现,所以必须要有真实角色。这个和静态代理模式是一样。我们还是需要按照业务逻辑去定义RealSubject。

(3)动态代理同样需要代理类,它和静态代理的区别是:静态的代理类是由程序员生成的,而动态代理类是动态生成的。但是,动态代理类的性质和普通的代理类是一样的,它都需要:a.实现抽象角色接口,由于它是动态生成的,所以不知道要实现哪些接口,所以它应该实现真实角色实现的所有接口;b.保存角色的引用;

前面两个条件跟静态代理模式是一样的,我们不用关心,我们需要弄明白的问题是:如果给动态代理类实现所需的接口以及它是怎样代理方法调用的?

Proxy来生成动态代理类

为了实现动态代理,Java提供了Proxy类和InvocationHandler接口,Proxy类用于生成动态代理类,InvocationHandler接口是代理真正发生的地方,它只有一个方法public Object invoke(Object proxy, Method method, Object[] args)throws Throwable,代理类所有的方法调用都会转到invoke()方法里,后面我们在说这个方法,先看Proxy类是如何生成动态代理类的。Proxy提供了如下的方法:

//返回动态代理类proxy关联的InvocationHandler
static InvocationHandler getInvocationHandler(Object proxy) 
//返回由loader加载,实现了interfaces接口的代理类
static Class<?>	getProxyClass(ClassLoader loader, Class<?>... interfaces)
//判断cl是否是代理类
static boolean	isProxyClass(Class<?> cl) 
//返回由loader加载,实现了interfaces接口的动态类的实例,其关联的InvocationHandler是h
static Object	newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h)
可以看出,Proxy提供了的4个方法全是static,它其实应嘎叫做ProxyFactory。关于以上方法具体的内容可以看JavaDoc,不过为了看清楚动态代理的实现,我决定研究一下Proxy的源码。处于篇幅的考虑,只是节选了部分源码:
 class Proxy implements java.io.Serializable {
	
	/*所有生成的代理类的名称是:$Proxy紧接一个数字,这个数字就是nextUniqueNumber++*/
	private static long nextUniqueNumber = 0;
    private final static String proxyClassNamePrefix = "$Proxy";

    /**动态代理类构造方法的参数 */
    private final static Class[] constructorParams = { InvocationHandler.class };

    /*映射每个ClassLoader所生成的代理类*/
    private static Map<ClassLoader, Map<List<String>, Object>> loaderToCache
        = new WeakHashMap<>();

    /**标记一个动态代理类正在生成 */
    private static Object pendingGenerationMarker = new Object();

    /** next number to use for generation of unique proxy class names */
    private static Object nextUniqueNumberLock = new Object();

    /** 生成的代理类,在查询一个类是否是代理类时就是通过判断是否在proxyClasses中 */
    private static Map<Class<?>, Void> proxyClasses =
        Collections.synchronizedMap(new WeakHashMap<Class<?>, Void>());

    /*关联的InvocationHandler*/
    protected InvocationHandler h;

   /*留给子类扩展的构造方法*/
    protected Proxy(InvocationHandler h) {
        doNewInstanceCheck();
        this.h = h;
    }

   /*生成代理类,这个代理类由loader加载,实现了interfaces中所有的接口,主要逻辑由getProxyClass0()实现*/
    public static Class<?> getProxyClass(ClassLoader loader,
                                         Class<?>... interfaces)
        throws IllegalArgumentException
    {
        SecurityManager sm = System.getSecurityManager();
        if (sm != null) {
            checkProxyAccess(Reflection.getCallerClass(), loader, interfaces);
        }

        return getProxyClass0(loader, interfaces);
    }


    /**
     * 真正生成代理类的方法:
     * 1.首先检查需要继承的接口是否大于65535,这是Java中能实现接口的上限
     * 2.遍历interfaces(需要实现的接口),获得每个接口的名称interfaceNames,如果:
     *   使用反射加载interfaceNames所对应的Class对象,如果与接口不一致,抛异常
     *   如果当前遍历的Class对象不是接口,抛异常
     *   如果其中有重复的接口,抛异常(interfaceSet就是用来保存已经遍历的接口,通过它可以判断是否包含当前接口)
     * 3.使用loader去loaderToCache中获得有该类加载器生成的所有代理类的map,如果没有,则创造一个空的Map放进去
     * 4.将所有需要实现的接口转换成List,去第3步中的map中查找:
     *   如果已经生成了由loader加载的且实现了interfaces所有接口的代理类,直接把该代理类返回
     *   如果正在生成有loader加载且实现了interface接口的代理类,等待,直到代理类生成//point1
     *   如果还没有生成符合条件的代理类,执行第5吧
     *   注意上面是放在一个while(true)循环中,所有point1最终都会从这里返回
     * 5.将该类加入包里,如果interfaces中由non-public的接口,则该代理类就就放在该non-public所在的包中,
     *   所以interfaces中所有的non-public接口应该在同一包中,否则就无法生成代理类了;
     *   如果全部都是public接口,则代理类默认放在com.sun.proxy包里
     * 6.代理类的名称是:$Proxy加序号
     * 7.调用本地方法生成代理类的字节码,同时将生成的代理类放入proxyClasses,方便isProxy()方法调用
     * 8.将生成的代理类放入第3步的map中,以便point1可以顺利返回,同时也把该代理类保存起来
     * @param loader
     * @param interfaces
     * @return
     */
    private static Class<?> getProxyClass0(ClassLoader loader,Class<?>... interfaces) {
    	//检查需要继承的接口是否大于65535,这是Java中能实现接口的上限
    	if (interfaces.length > 65535) {
            throw new IllegalArgumentException("interface limit exceeded");
        }
    	//需要生成的代理类
        Class<?> proxyClass = null;

        /* 所有需要实现的接口名称的数组 */
        String[] interfaceNames = new String[interfaces.length];

        // 用于检查是否有重复的接口
        Set<Class<?>> interfaceSet = new HashSet<>();

        //遍历每个接口,对应注释中的第2步
        for (int i = 0; i < interfaces.length; i++) {
        	//判断传递进来的接口与通过反射生成的接口是否相同
            String interfaceName = interfaces[i].getName();
            Class<?> interfaceClass = null;
            try {
                interfaceClass = Class.forName(interfaceName, false, loader);
            } catch (ClassNotFoundException e) {
            }
            if (interfaceClass != interfaces[i]) {
                throw new IllegalArgumentException(
                    interfaces[i] + " is not visible from class loader");
            }

           /*判断传递进来的是否是进口*/
            if (!interfaceClass.isInterface()) {
                throw new IllegalArgumentException(
                    interfaceClass.getName() + " is not an interface");
            }

            /*判断是否有重复的接口*/
            if (interfaceSet.contains(interfaceClass)) {
                throw new IllegalArgumentException(
                    "repeated interface: " + interfaceClass.getName());
            }
            interfaceSet.add(interfaceClass);

            interfaceNames[i] = interfaceName;
        }

        /*将要实现的接口组装成List,到时候作为key去map中查找,判断该动态代理类是否已经生成*/
        List<String> key = Arrays.asList(interfaceNames);

        /*获得由loader类加载器生成所有代理类的map*/
        Map<List<String>, Object> cache;
        synchronized (loaderToCache) {
            cache = loaderToCache.get(loader);
            if (cache == null) {
                cache = new HashMap<>();
                loaderToCache.put(loader, cache);
            }
        }

        synchronized (cache) {
        	/**
        	 * 下面的代码是在while(true)循环中:
        	 * (1)如果已经生成了复合条件的动态代理类,直接把该类返回,循环结束
        	 * (2)如果需要生成的动态代理类正在创建中,等待,直到创建完成,则会跳到(1),循环会结束
        	 * (3)否则,去创建该动态代理类,并标记正在创建中,创建完成后,也会跳到(1)
        	 */
            do {
                Object value = cache.get(key);
                if (value instanceof Reference) {
                    proxyClass = (Class<?>) ((Reference) value).get();
                }
                if (proxyClass != null) {
                    return proxyClass;
                } else if (value == pendingGenerationMarker) {
                    try {
                        cache.wait();
                    } catch (InterruptedException e) {
                    }
                    continue;
                } else {
                    cache.put(key, pendingGenerationMarker);
                    break;
                }
            } while (true);
        }

        try {
        	//代理类的包
            String proxyPkg = null;    

            /*所有要实现的接口有non-public的,则该代理类就和这个non-public接口在同一个包里,否而就在com.sun.proxy*/
            for (int i = 0; i < interfaces.length; i++) {
                int flags = interfaces[i].getModifiers();
                if (!Modifier.isPublic(flags)) {
                    String name = interfaces[i].getName();
                    int n = name.lastIndexOf('.');
                    String pkg = ((n == -1) ? "" : name.substring(0, n + 1));
                    if (proxyPkg == null) {
                        proxyPkg = pkg;
                    } else if (!pkg.equals(proxyPkg)) {
                        throw new IllegalArgumentException(
                            "non-public interfaces from different packages");
                    }
                }
            }

            if (proxyPkg == null) {
                proxyPkg = ReflectUtil.PROXY_PACKAGE + ".";
            }

            {
               /*获得一个序号,加到$Proxy后面的序号*/
                long num;
                synchronized (nextUniqueNumberLock) {
                    num = nextUniqueNumber++;
                }
                String proxyName = proxyPkg + proxyClassNamePrefix + num;
                /*生成代理类*/
                byte[] proxyClassFile = ProxyGenerator.generateProxyClass(
                    proxyName, interfaces);
                try {
                    proxyClass = defineClass0(loader, proxyName,
                        proxyClassFile, 0, proxyClassFile.length);
                } catch (ClassFormatError e) {
                    throw new IllegalArgumentException(e.toString());
                }
            }
            // 加到proxyClasses中,isProxy()方法就是靠它判断
            proxyClasses.put(proxyClass, null);

        } finally {
           /*将生成的代理类加入缓存中*/
            synchronized (cache) {
                if (proxyClass != null) {
                    cache.put(key, new WeakReference<Class<?>>(proxyClass));
                } else {
                    cache.remove(key);
                }
                cache.notifyAll();
            }
        }
        return proxyClass;
    }

    
    /**
     * 生成代理类的实例
     * 1.首先调用getProxyClass0()获得代理类的Class对象
     * 2.获取动态东来类的构造方法的Constructor对象
     * 3.调用Constructor.newInstance()
     * 它需要InvocationHandler参数
     */
    public static Object newProxyInstance(ClassLoader loader,
                                          Class<?>[] interfaces,
                                          InvocationHandler h)
        throws IllegalArgumentException
    {
        //获取由loader家在,且实现了interfaces接口的动态代理类
        Class<?> cl = getProxyClass0(loader, interfaces);
        final Constructor<?> cons = cl.getConstructor(constructorParams);
        final InvocationHandler ih = h;
        return newInstance(cons, ih);
    }

    //创建代理类的实例,代理对象,调用代理类的构造方法
    private static Object newInstance(Constructor<?> cons, InvocationHandler h) {  
            return cons.newInstance(new Object[] {h} );
    }
    
    /*判断cl是否是代理类,如果在proxyClasses中就是,否则就不是*/
    public static boolean isProxyClass(Class<?> cl) {
        if (cl == null) {
            throw new NullPointerException();
        }
        return proxyClasses.containsKey(cl);
    }
    
    /*获得与proxy关联的InvocationHandler,就是获得proxy.h,然后返回*/
    public static InvocationHandler getInvocationHandler(Object proxy)
            throws IllegalArgumentException
        {
            final Proxy p = (Proxy) proxy;
            final InvocationHandler ih = p.h;
            return ih;
        }
}
Proxy在生成代理类的时候主要的工作就是让代理类实现所需的接口,生成字节码的工作是ProxyGenerator做的。在生成动态代理类时,我们需要传递给它类加载器,需要实现的接口,以及与之关联的InvocationHandler对象,InvocationHandler对象要做的就是我们希望实现的代理逻辑,比如,我们希望在每个方法调用之前做安全检查,那就在InvocationHandler的invoke()方法里调用安全检查的逻辑,invoke()是代理发生的地方。我们看一下invoke()方法:
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable;


其中proxy是代理类,不用去管它。method是要真实对象要执行的方法,也就是我们要代理的方法,args是method的参数,所以如果代理需要做安全检查的话,可以这么写:
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
    if(安全检查通过)
        method.invoke(obj,args);
   else
       return null;
}

例子

说了这么多,我们还是实现一个动态代理的例子,逻辑是这样的:有三个模块,分别是添加,删除,修改,这些代码已经完成;在做实际的工作之前,需要进行安全验证,如安全验证通过,就去调用相应的方法,否则本次操作失败。首先定义各个接口以及实现:
//添加模块
interface Add {
	public void add(String s);
}

class AddImp implements Add {
	public void add(String s) {
		System.out.println(s + "已经被添加进系统");
	}
}

//删除模块
interface Delete {
	public void delete(String s);
}

class DeleteImp implements Delete {
	public void delete(String s) {
		System.out.println(s + "已经被删除");
	}
}

//修改模块
interface Update {
	public void update(String s);
}

class UpdateImp implements Update {
	public void update(String s) {
		System.out.println(s + "已经被修改");
	}
}
安全检查模块
/**
 * 安全检查模块,随机的返回检查通过或者不通过
 */
class SafeCheck {
	static Random rand = new Random(25);
	public static boolean check(Object obj) {
		if(rand.nextInt(20) > 10)
			return false;
		
		return true;
	}
}
生成动态代理,并代理执行:
/**
 * 可以把它理解为代理角色,内部保存真实对象的引用
 * 在调用真实对象的方法之前或之后执行相应的控制逻辑
 * 虽然真正的动态代理类是由Proxy生成的,但是代理的逻辑却是在这里实现的
 */
class DynamicProxy implements InvocationHandler {
	//真实对象,只能是Object
	private Object originalObj;
	
	/**
	 * 为传递进来的对象生成代理类,并实现originalObj的接口
	 */
	public Object bind(Object originalObj) {
		this.originalObj = originalObj;
		//originalObj对应的Class对象
		Class<?> clazz = originalObj.getClass();
		//自身现在就是处理器,所以把this传递给代理类
		return Proxy.newProxyInstance(clazz.getClassLoader(), clazz.getInterfaces(), this);
	}
	/**
	 * 方法的返回值是method的返回值,如果没有则返回null
	 * 代理真正发生的地方,可以在这里添加控制逻辑
	 * 注意,proxy是代理类,method是在真实对象上执行的,也就是originalObj
	 */
	@Override
	public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
		//如果通过了安全验证,就去执行真正的逻辑
		if(SafeCheck.check(originalObj))
			return method.invoke(originalObj, args);
		else 
			System.out.println(originalObj + "不符合安全要求!");
		//否则就什么也不执行
		return null;
	}
}
测试代码:
public class Client {

	public static void main(String[] args) {
		String name = "cxy";
		DynamicProxy handler = new DynamicProxy();
		//真实对象
		Add add = new AddImp();
		Update update = new UpdateImp();
		Delete delete = new DeleteImp();
		
		//生成Add的代理类
		Add pa = (Add) handler.bind(add);
		//代理执行
		pa.add(name);
		//生成update的代理类
		Update pu = (Update)handler.bind(update);
		pu.update(name);
		//生成Delete的代理类
		Delete pd = (Delete)handler.bind(delete);
		pd.delete(name);
	}
}
输出结果:
----正在给com.understanding.loaderandengine.AddImp@f0a3e8做安全检查-----
cxy已经被添加进系统
----正在给com.understanding.loaderandengine.UpdateImp@a22e0c做安全检查-----
com.understanding.loaderandengine.UpdateImp@a22e0c不符合安全要求!
----正在给com.understanding.loaderandengine.DeleteImp@1b56bda做安全检查-----
com.understanding.loaderandengine.DeleteImp@1b56bda不符合安全要求!

我们可以看到,虽然系统中有三个模块,而且各自实现不同的接口,只要他们需要代理的逻辑是相同的,那么只需要给出一个处理程序,就可以动态代理全部的模块了,这是动态代理的强大之处。这个例子已经有点AOP的味道了,安全检查以不改变侵入原来模块的方式做到了为每个模块服务,Spring中的AOP有一大部分是通过动态代理实现的。

动态代理的执行

前面已经介绍了动态代理类是如何生成的,也演示了动态代理的例子,其实我们还想看看动态代理类是如何代理服务的,这就需要看动态代理类的代码了,可以通过下面这个工具类来生成动态代理类:

import java.io.FileOutputStream;
import java.io.IOException;
import java.lang.reflect.Proxy;
import sun.misc.ProxyGenerator;

public class ProxyUtils {

	/*
	 * 将根据类信息 动态生成的二进制字节码保存到硬盘中,
	 * 默认的是clazz目录下
         * params :clazz 需要生成动态代理类的类
         * proxyName : 为动态生成的代理类的名称
        */
	public static void generateClassFile(Class clazz,String proxyName)
	{
	  //根据类信息和提供的代理类名称,生成字节码
  byte[] classFile = ProxyGenerator.generateProxyClass(proxyName, clazz.getInterfaces()); 
		String paths = clazz.getResource(".").getPath();
		System.out.println(paths);
		FileOutputStream out = null;  
        
        try {
            //保留到硬盘中
        	out = new FileOutputStream(paths+proxyName+".class");  
            out.write(classFile);  
            out.flush();  
        } catch (Exception e) {  
            e.printStackTrace();  
        } finally {  
            try {  
                out.close();  
            } catch (IOException e) {  
                e.printStackTrace();  
            }  
        }  
	}
	
}
或者在main()中加入一句来生成代理类:
System.getProperties().put("sun.misc.ProxyGenerator.saveGeneratedFiles", "true");
通过反编译,得到代理类的源码,有了源码那么一切都暴露在我们面前了,先看看为AddImp生成的代理类:
/**
 * 动态代理类,它继承了Proxy类,同时实现了Add接口
 */
public final class AddProxy extends Proxy
implements Add
{
private static Method m1;
private static Method m3;
private static Method m0;
private static Method m2;

//接收一个InvocationHandler做参数,它是代理真正发生的地方
public AddProxy(InvocationHandler paramInvocationHandler)
  throws 
{
  super(paramInvocationHandler);
}

/**
 * 实现Add接口而生成的add方法
 */
public final void add(String paramString)
  throws 
{
  try
  {
	//它其实就是去执行InvocationHandler的invoke()方法了
    this.h.invoke(this, m3, new Object[] { paramString });
    return;
  }
  catch (Error|RuntimeException localError)
  {
    throw localError;
  }
  catch (Throwable localThrowable)
  {
    throw new UndeclaredThrowableException(localThrowable);
  }
}


static
{
  try
  {
   //通过反射的反射获得add对应的Method对象,在InvocationHandler的invoke()方法中,当代理逻辑完成后,就是调用m3去执行真正的业务逻辑
    m3 = Class.forName("com.understanding.loaderandengine.Add").getMethod("add", new Class[] { Class.forName("java.lang.String") });
    m1 = Class.forName("java.lang.Object").getMethod("equals", new Class[] { Class.forName("java.lang.Object") });
    m0 = Class.forName("java.lang.Object").getMethod("hashCode", new Class[0]);
    m2 = Class.forName("java.lang.Object").getMethod("toString", new Class[0]);
    return;
  }
  catch (NoSuchMethodException localNoSuchMethodException)
  {
    throw new NoSuchMethodError(localNoSuchMethodException.getMessage());
  }
  catch (ClassNotFoundException localClassNotFoundException)
  {
    throw new NoClassDefFoundError(localClassNotFoundException.getMessage());
  }
}
}
以上代码删除了为继承Object而生成的hashCode(),toString(),equals()方法,接着再看看Delete代理类的源码,节选:
//为实现delete()代理而实现的方法
public final void delete(String paramString)
    throws 
  {
    try
    {
      this.h.invoke(this, m3, new Object[] { paramString });
      return;
    }
    catch (Error|RuntimeException localError)
    {
      throw localError;
    }
    catch (Throwable localThrowable)
    {
      throw new UndeclaredThrowableException(localThrowable);
    }
  }

//通过反射获得delte()的Method对象
m3 = Class.forName("com.understanding.loaderandengine.Delete").getMethod("delete", new Class[] { Class.forName("java.lang.String") });
Update的代码就不贴了,从上面的代码中,我们可以发现动态代理的执行过程:

(1)动态代理类需要保存一个InvocationHandler的引用;

(2)动态代理类实现被代理类实现的全部接口,并获得相应方法的Method对象,生成相应的方法

(3)在执行代理的时候,动态代理类其实转到InvocationHandler.invoke()方法中去了,这也是为什么我说InvocationHandler是代理角色的原因。

到此,我们已经知道了动态代理类是如何生成的,动态代理是如何实现的,并且举了一个小例子,这个小例子很有AOP的思想。但是Java中动态代理已经很完美了吗?如果回过来头来去看Proxy.getProxyclass)()方法,我们发现Java的动态代理是根据被代理类所实现的接口来代理的,可以说Java的动态代理是“面向接口的代理”,如果一个类没有实现接口,那么就无法生成动态代理类,这是它的缺陷,但是不得不说,Java的动态代理还是很强大的。

转载请注明:喻红叶《Java与模式-代理模式》

分享到:
评论

相关推荐

Global site tag (gtag.js) - Google Analytics