代理模式 静态代理与动态代理 绝对通俗易懂

静态代理:

假设现在有一个人(person接口),他的名字叫A(class A implement person),小A 40岁了必须要找对象,但是呢自己比较害羞不好意思开口,这样就需要一个媒婆来帮他找对象(MeiPo 代理类),至此就是静态代理模式,下面我们用带来实现下。

Person:

public interface Person {
	void findLove();
}

A:

public class XiaoA implements Person{
	@Override
	public void findLove() {
        //个人条件
		System.out.println("高富帅");
		System.out.println("有房有车的");
	}
}

媒婆:

public class Meipo2 implements Person {
	
	private XiaoA xiaoA;
	
	public Meipo2(XiaoA xa) {
		// TODO Auto-generated constructor stub
		this.xiaoA = xa;
	}
	
	 @Override
	public void findLove() {
		// TODO Auto-generated method stub
		System.out.println("我是媒婆,要帮小A找对象,小A的条件为:");
		xiaoA.findLove();
		System.out.println("寻找中...");
		System.out.println("寻找到仙女一枚!");
	}

}

Main方法:

public class TestFindLove {
	public static void main(String[] args) {
		Meipo2 meipo2 = new Meipo2(new XiaoA());
		meipo2.findLove();
	}
}

上面的静态代理的基础代码,下面我们考虑一个问题,这时候B也来找媒婆了,说我也要找对象,这时候由于class meipo2代理着A的找对象的任务,无法再代理B,所以我们不得不再创建一个meipo3来代理B,如果有1000个人来就得创建1000个meipo。这时候可能有人说了一个媒婆可以代理多个people啊。但是这样的话,需要修改接口与类,违背了开闭原则。

动态代理:

基于JDK的动态代理:

上面的静态代理是每来一个人就创建一个媒婆代理他,这样的话工作过于繁琐,这时我们为何我开一个媒婆公司呢?将找对象的人交给媒婆公司,让媒婆公司创建媒婆代理他传宗接代的任务!

贴下代码:

增加一个xiaoB:

public class XiaoB implements Person{
	@Override
	public void findLove() {
		System.out.println("矮穷矬");
		System.out.println("屌丝一枚");
	}
}

媒婆 改为 媒婆公司:

public class MeipoCompany implements InvocationHandler {
	
	private Person target; //被代理对象的引用作为一个成员变量保存下来了
	
	//获取被代理人的个人资料
	public Object getInstance(Person target) throws Exception{
		this.target = target;
		Class clazz = target.getClass();
		System.out.println("被代理对象的class是:"+clazz);
		return Proxy.newProxyInstance(clazz.getClassLoader(), clazz.getInterfaces(), this);
	}
	

	@Override
	public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
		
		System.out.println("我是媒婆公司:" + "得给你找个异性才行");
		System.out.println("说下你的条件:");
		System.out.println("------------");
		//反射调用方法
		method.invoke(this.target, args);
		System.out.println("------------");
		System.out.println("开始进行海选...");
		System.out.println("找到啦");
		
		return null;
	}

}

Main方法:

public class TestFindLove {
	public static void main(String[] args) {
		try {
 			Person A = (Person)new MeipoCompany().getInstance(new XiaoA());
 			System.out.println("代理类:"+A.getClass());
 			A.findLove();
 			System.out.println("******************************************************");
 			Person B = (Person)new MeipoCompany().getInstance(new XiaoB());
 			System.out.println("代理类:"+B.getClass());
 			B.findLove();
		} catch (Exception e) {
			e.printStackTrace();
		}
	}
}

打印结果:

这样当小C再来时,只需新建一个小C,然后Person C = (Person)new MeipoCompany().getInstance(new XiaoC());即可代理小C

JDK静态代理是通过直接编码创建的,而JDK动态代理是利用反射机制在运行时创建代理类的。

那么问题来了,jdk是怎么实现动态代理的呢?

首先我们把jdk创建的动态代理类的字节码输出到一个class文件中:(需要引入rt.jar)

byte[] data = ProxyGenerator.generateProxyClass("$Proxy0", new Class[]{Person.class});
			FileOutputStream os = new FileOutputStream("D:/$Proxy0.class");
			os.write(data);
			os.close();

然后反编译一下,反编译后的代码: (这个代码会报错,但是无伤大雅我们只看下其中的逻辑)


public final class Proxy0 extends Proxy
implements Person
{
private static Method m1;
private static Method m3;
private static Method m2;
private static Method m0;

public Proxy0()
  throws 
{
  super(paramInvocationHandler);
}

public final boolean equals()
  throws 
{
  try
  {
    return ((Boolean)this.h.invoke(this, m1, new Object[] { paramObject })).booleanValue();
  }
  catch (RuntimeException localRuntimeException)
  {
    throw localRuntimeException;
  }
  catch (Throwable localThrowable)
  {
    throw new UndeclaredThrowableException(localThrowable);
  }
}

public final void findLove()
  throws 
{
  try
  {
    this.h.invoke(this, m3, null);
    return;
  }
  catch (RuntimeException localRuntimeException)
  {
    throw localRuntimeException;
  }
  catch (Throwable localThrowable)
  {
    throw new UndeclaredThrowableException(localThrowable);
  }
}

public final String toString()
  throws 
{
  try
  {
    return ((String)this.h.invoke(this, m2, null));
  }
  catch (RuntimeException localRuntimeException)
  {
    throw localRuntimeException;
  }
  catch (Throwable localThrowable)
  {
    throw new UndeclaredThrowableException(localThrowable);
  }
}

public final int hashCode()
  throws 
{
  try
  {
    return ((Integer)this.h.invoke(this, m0, null)).intValue();
  }
  catch (RuntimeException localRuntimeException)
  {
    throw localRuntimeException;
  }
  catch (Throwable localThrowable)
  {
    throw new UndeclaredThrowableException(localThrowable);
  }
}

static
{
  try
  {
    m1 = Class.forName("java.lang.Object").getMethod("equals", new Class[] { Class.forName("java.lang.Object") });
    m3 = Class.forName("com.bzy.proxy.jdk.Person").getMethod("findLove", new Class[0]);
    m2 = Class.forName("java.lang.Object").getMethod("toString", new Class[0]);
    m0 = Class.forName("java.lang.Object").getMethod("hashCode", new Class[0]);
    return;
  }
  catch (NoSuchMethodException localNoSuchMethodException)
  {
    throw new NoSuchMethodError(localNoSuchMethodException.getMessage());
  }
  catch (ClassNotFoundException localClassNotFoundException)
  {
    throw new NoClassDefFoundError(localClassNotFoundException.getMessage());
  }
}
}

上面的proxy0就是JDK生成的代理类。然后我们分析下,他是如何调用到XiaoA的findLove方法的呢

首先调用proxy0的findLove:

然后findLOVE调用了this.h,这个h就是invocationHandler是父类(proxy)中的成员变量

然后调用了h.invoke,那么问题来了,InvocationHandler是一个接口,是在何时实例化接口的呢?即实例化MeipoConpany的呢

我们把目光转向MeipoCompany中的

Proxy.newProxyInstance(clazz.getClassLoader(), clazz.getInterfaces(), this)

我们看下newProxyInstance方法: (都在里面解释了)

public static Object newProxyInstance(ClassLoader loader,
                                          Class<?>[] interfaces,
                                          InvocationHandler h)
        throws IllegalArgumentException
    {
		//检查h不为空
        Objects.requireNonNull(h);
		//安全检查
        final Class<?>[] intfs = interfaces.clone();
        final SecurityManager sm = System.getSecurityManager();
        if (sm != null) {
            checkProxyAccess(Reflection.getCallerClass(), loader, intfs);
        }

        /*
         * 动态代理核心,生成新类proxy0,拿到proxy0的class对象
         */
        Class<?> cl = getProxyClass0(loader, intfs);

        /*
         * Invoke its constructor with the designated invocation handler.
         */
        try {
            if (sm != null) {
                checkNewProxyPermission(Reflection.getCallerClass(), cl);
            }
			//反射调用proxy0的构造方法,获取参数为constructorParams的构造方法,
			//其实就是获取参数为InvocationHandler.class的构造方法
            final Constructor<?> cons = cl.getConstructor(constructorParams);
            final InvocationHandler ih = h;
			//cl.getModifiers()获取构造方法修饰符,判断是不是public
            if (!Modifier.isPublic(cl.getModifiers())) {
                AccessController.doPrivileged(new PrivilegedAction<Void>() {
                    public Void run() {
                        cons.setAccessible(true);
                        return null;
                    }
                });
            }
			//返回proxy0的对象,并将InvocationHandler的实现类传入,这里proxy0的构造方法
			//调用了 super(paramInvocationHandler); 也就是调用了父类构造方法将InvocationHandler的实现类传入父类,
			//在父类中通过 this.h = h; 将值赋给了父类中的h,这样就完成了父类中h的实例化
       
    }
            return cons.newInstance(new Object[]{h});
        } catch (IllegalAccessException|InstantiationException e) {
            throw new InternalError(e.toString(), e);
        } catch (InvocationTargetException e) {
            Throwable t = e.getCause();
            if (t instanceof RuntimeException) {
                throw (RuntimeException) t;
            } else {
                throw new InternalError(t.toString(), t);
            }
        } catch (NoSuchMethodException e) {
            throw new InternalError(e.toString(), e);
        }
    }

总结:

1.调用newProxyInstance方法然后proxy创建代理对象proxy0,proxy0继承了proxy实现了person接口

2.反射调用proxy0的构造方法,将invocationHandler实现类传入,在proxy0的构造方法中super调用proxy的构造方法,将invocationHandler实现类传入proxy并复制给成员变量。

3.proxy0.findLove时,调用 this.h.invoke(this, m3, null); 调用到父类invocationHandler的实例化对象,再调用到实例化对象的invoke,实现代理

猜你喜欢

转载自blog.csdn.net/qq_37113604/article/details/89944816