【设计模式】代理模式,静态代理和动态代理区别以及原理,jDK底层动态代理生成源码解析

概念

代理模式一般涉及到的角色有

  • 抽象角色:声明真实对象和代理对象的共同接口
  • 代理角色:代理对象角色内部含有对真实对象的引用,从而可以操作真实对象,同时代理对象提供与真实对象相同的接口以便在任何时刻都能代替真实对象。同时,代理对象可以在执行真实对象操作时,附加其他的操作,相当于对真实对象进行封装
  • 真实角色:代理角色所代表的真实对象,是我们最终要引用的对象

代理模式

1.建立一个抽象角色

/**
 * 抽象角色
 */
public abstract class Subject
{
	public abstract void request();
}


2.建立一个真实角色

/**
 * 真实角色
 */
public class RealSubject extends Subject
{
	@Override
	public void request()
	{
		System.out.println("From real subject.");
	}
}

3.建立一个代理角色

/**
 * 代理角色
 */
public class ProxySubject extends Subject
{
	//一个真实角色必须有个代理角色
	private RealSubject realSubject; //代理角色内部引用的真实角色
	
	@Override
	public void request()  //中介去调用真实角色的方法
	{
		this.preRequest(); //调用前代理角色需要干的事情
		
		if(null == realSubject)
		{
			realSubject = new RealSubject();
		}

		realSubject.request(); //调用真实角色方法
		
		this.postRequest(); //调用后代理角色需要干的事情
	}
	
	private void preRequest()
	{
		System.out.println("pre request");
	}
	
	private void postRequest()
	{
		System.out.println("post request");
	}
}

4.建立一个客户

/**
 *  客户
 */
public class Client
{
	public static void main(String[] args)
	{
		Subject subject = new ProxySubject();
		
		subject.request();
	}
}

总结

代理模式相当于客客户端去执行真实角色某个方法需要中间一个媒介代理对象去调用该方法,代理对象与真是对象存在联系,而客户端没有联系。当代理对象去调用真实对象时可以在在调用的前后执行自己的逻辑。(类比客户找房通过中介与房东签合同,中间都是中介做媒介,事后收取双方佣金 客户和房东签合同前无联系,而中介和房东存在联系)

代理模式存在的问题

如果要按照上述的方法使用代理模式,那么真实角色必须是事先已经存在的,并将其作为代理对象的内部属性。但是实际使用时,一个真实角色必须对应一个 代理角色,如果大量使用会导致类的急剧膨胀;此外,如果事先并不知道真实角色,该如何使用代理呢?这个问题可以通过Java的动态代理类来解决

动态代理

Java动态代理类位于java.lang.reflect包下,一般主要涉及到以下两个类:

  1. Interface InvocationHandler
    该接口中仅定义了一个方法
/*
	invoke为动态代理类的实现逻辑
*/
 public Object invoke(Object proxy, Method method, Object[] args)
        throws Throwable;

• 在实际使用时,第一个参数proxy一般是指代理类,method是被代理的方法,如上例中的request(),args为该方法的参数数组。 这个抽象方法在代理类中动态实现。

  1. Proxy:该类即为动态代理类,作用类似于上例中的ProxySubject,其中主要包含以下静态方法
  • 构造函数,用于给内部的h赋值。
 protected Proxy(InvocationHandler h) {
        Objects.requireNonNull(h);
        this.h = h;
    }

  • 获得一个代理类,其中loader是类装载器,interfaces是真实类拥有全部接口的数组,生成的代理类
    就宣称实现了改全部接口
 @CallerSensitive
    public static Class<?> getProxyClass(ClassLoader loader,
                                         Class<?>... interfaces)
        throws IllegalArgumentException
    {
        final Class<?>[] intfs = interfaces.clone();
        final SecurityManager sm = System.getSecurityManager();
        if (sm != null) {
            checkProxyAccess(Reflection.getCallerClass(), loader, intfs);
        }

        return getProxyClass0(loader, intfs);
    }
  • 返回代理类的一个实例,返回后的代理类可以当作被代理类使用(可使用被代理类的在Subject接口中声明过的方法)
/*
	返回指定接口的代理类实例,该接口将方法调用分派到指定的调用处理程序。
*/
 @CallerSensitive
    public static Object newProxyInstance(ClassLoader loader,
                                          Class<?>[] interfaces,//实现类的接口
                                          InvocationHandler h)
        throws IllegalArgumentException
    {
    //判断Handler是否为空
        Objects.requireNonNull(h);
		//克隆
        final Class<?>[] intfs = interfaces.clone();
        //安全管理 返回该安全管理器 否则为null
        final SecurityManager sm = System.getSecurityManager();
        if (sm != null) {//安全
        	//检查创建代理类所需的权限。
            checkProxyAccess(Reflection.getCallerClass(), loader, intfs);
        }

        /*
         * 查找或生成指定的代理类。  有返回没有就创建
         */
        Class<?> cl = getProxyClass0(loader, intfs);

        /*
         * Invoke its constructor with the designated invocation handler.
         */
        try {
            if (sm != null) {
            //检测新的代理权限
                checkNewProxyPermission(Reflection.getCallerClass(), cl);
            }
			//获取代理Class对象的构造方法
            final Constructor<?> cons = cl.getConstructor(constructorParams);
            //赋值hander
            final InvocationHandler ih = h;
            //判断修饰词是否存在privte 有打破封装
            if (!Modifier.isPublic(cl.getModifiers())) {
                AccessController.doPrivileged(new PrivilegedAction<Void>() {
                    public Void run() {
                        cons.setAccessible(true);
                        return null;
                    }
                });
            }
            //返回构造方法生成的动态对象实例 参数为new Object[]{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.新建个一个抽象对象

/**
 * 抽象角色
 */
public interface Subject
{
	public void request();
}

2.创建一个真实对象


/**
 * 真实角色
 */
public class RealSubject implements Subject
{
	@Override
	public void request()
	{
		System.out.println("From real subject!");
	}

}

3.构建动态代理类的执行方法 创建一个执行器 必须实现InvocationHandler 重写invoke 执行方法去包装真实类的方法

public class DynamicSubject implements InvocationHandler
{
	private Object sub;
	
	public DynamicSubject(Object obj)
	{
		this.sub = obj;
	}
	
	@Override
	public Object invoke(Object proxy, Method method, Object[] args)
			throws Throwable
	{
		System.out.println("before calling: " + method);
		//执行真实类的被代理方法
		method.invoke(sub, args);
		
		System.out.println(args == null);
		
		System.out.println("after calling: " + method);
		
		return null;
	}

4.客户端调用

/**
*客户端
*/
public class Client
{
	public static void main(String[] args)
	{
		RealSubject realSubject = new RealSubject();
		//代理对象包装真实类
		InvocationHandler handler = new DynamicSubject(realSubject);

		Class<?> classType = handler.getClass();

		/*
		返回动态实例 class com.sun.proxy.$Proxy0 仅仅是生成动态代理对象
		生成的动态实例特点实现了真实类的所有接口(包括Subject) 的这些接口(不包括realSubject)
		 该实例是在运行期间动态生成的既不属于 
		realSubject 也不属于DynamicSubject 
		*/
		Subject subject = (Subject) Proxy.newProxyInstance(classType
				.getClassLoader(), realSubject.getClass().getInterfaces(),
				handler);
//        Subject subject2 = (Subject) Proxy.newProxyInstance(classType
//                        .getClassLoader(), new Class[]{Subject.class}, //可以动态制定需要实现的接口类
//                handler);
//        subject.request();
		//任何借口的调用都会由InvocationHandler 来接管
		subject.request();

		System.out.println(subject.getClass());

	}
}

打印结果

before calling: public abstract void com.shengsiyuan.javase.dynamicproxy.Subject.request()
From real subject!
true
after calling: public abstract void com.shengsiyuan.javase.dynamicproxy.Subject.request()
class com.sun.proxy.$Proxy0

总结

与静态代理原理类似,动态代理类必须的实现InvocationHandler ,通过Proxy.newProxyInstance()方法去动态的获取动态代理对象实例。
所谓Dynamic Proxy是这样一种class:它是在运行时生成的class,在生成它时你必须提供一组interface给它(静态代理则一个真实对应一个代理),然后该class(动态代理对象)就宣称它实现了这些 interface。你当然可以把该class的实例当作这些interface中的任何一个来用。当然,这个DynamicProxy其实就是一个Proxy(动态代理类),它不会替你作实质性的工作,在生成它的实例时你必须提供一个handler,由它接管实际的工作

public class VectorProxy implements InvocationHandler
{
	private Object proxyObj;

	public VectorProxy(Object obj)
	{
		this.proxyObj = obj;
	}

	public static Object factory(Object obj)
	{
		Class<?> classType = obj.getClass();

		return Proxy.newProxyInstance(classType.getClassLoader(),
				classType.getInterfaces(), new VectorProxy(obj));
	}
	/*
	1.proxy代表代理对象当前被代理出来的对象 该对象如果method.invoke(proxy,args)就会陷入死循环(aop里一样),我们的自定义传进来对应的对象
	2.method被代理接口的method方法
	3.方法对应的参数
参考:https://blog.csdn.net/codeydj/article/details/107236027
	*/
	@Override
	public Object invoke(Object proxy, Method method, Object[] args)
			throws Throwable
	{
		System.out.println("before calling: " + method);
		
		if(null != args)
		{
			for(Object obj : args)
			{
				System.out.println(obj);
			}
		}
		
		Object object = method.invoke(proxyObj, args);
		
		System.out.println("after calling: " + method);
		
		return object;
	}
	
	public static void main(String[] args)
	{
		List v = (List)factory(new Vector());
		
		System.out.println(v.getClass().getName());
		
		v.add("New"); //调用代理对象的add方法
		v.add("York");
		
		System.out.println(v);
		
		v.remove(0);
		System.out.println(v);
		
	}

本质上 jvm在运行期间给你动态生成了类
此处类的结构就是extends Proxy implements 指定接口
此处的h就是自己对应的InvocationHandler,本质上都是调用(String)proxy.h.invoke(this, m2, (Object[])null); 去增强你的接口方法注意此处传的是this 是代理对象 我们在handler中要自定义想要的对象去执行对应method 不能使用handler中invoke提供的proxy 不然会陷入死循环
在这里插入图片描述

//此处类的结构就是extends Proxy implements 指定接口
//从这个地方也能 明白为什么jdk代理只能代理接口(因为Java是单继承,而jdkProxy生成的代理类对象必然会继承Proxy,因此,只能代理接口)
//不过如果是一个无实现接口无继承类的类,调用Proxy.newProxyInstance,会生成一个extends Proxy implements 自身的一个代理类,但在实际的使用中,这样的类是无法转换成我们想要的东西的,且也无法使用(没发进行方法调用的)
public final class class extends Proxy implements Interface {
    private static Method m1;
    private static Method m3;
    private static Method m2;
    private static Method m0;

    public class(InvocationHandler var1) throws  {
        super(var1);
    }

    public final boolean equals(Object var1) throws  {
        try {
            return (Boolean)super.h.invoke(this, m1, new Object[]{var1});
        } catch (RuntimeException | Error var3) {
            throw var3;
        } catch (Throwable var4) {
            throw new UndeclaredThrowableException(var4);
        }
    }

    public final void print() throws  {
        try {
            super.h.invoke(this, m3, (Object[])null);
        } catch (RuntimeException | Error var2) {
            throw var2;
        } catch (Throwable var3) {
            throw new UndeclaredThrowableException(var3);
        }
    }

    public final String toString() throws  {
        try {
            return (String)super.h.invoke(this, m2, (Object[])null);
        } catch (RuntimeException | Error var2) {
            throw var2;
        } catch (Throwable var3) {
            throw new UndeclaredThrowableException(var3);
        }
    }

    public final int hashCode() throws  {
        try {
            return (Integer)super.h.invoke(this, m0, (Object[])null);
        } catch (RuntimeException | Error var2) {
            throw var2;
        } catch (Throwable var3) {
            throw new UndeclaredThrowableException(var3);
        }
    }

    static {
        try {
            m1 = Class.forName("java.lang.Object").getMethod("equals", Class.forName("java.lang.Object"));
            m3 = Class.forName("com.ydj.netty.proxy.ProxyTest3$Interface").getMethod("print");
            m2 = Class.forName("java.lang.Object").getMethod("toString");
            m0 = Class.forName("java.lang.Object").getMethod("hashCode");
        } catch (NoSuchMethodException var2) {
            throw new NoSuchMethodError(var2.getMessage());
        } catch (ClassNotFoundException var3) {
            throw new NoClassDefFoundError(var3.getMessage());
        }
    }
}
————————————————
版权声明:本文为CSDN博主「codeydj」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/codeydj/article/details/107236027

打印结果

com.sun.proxy.$Proxy0 //动态代理class对象
before calling: public abstract boolean java.util.List.add(java.lang.Object)
New //调用一次动态代理对象 会执行invoke方法
after calling: public abstract boolean java.util.List.add(java.lang.Object)
before calling: public abstract boolean java.util.List.add(java.lang.Object)
York //打印的参数
after calling: public abstract boolean java.util.List.add(java.lang.Object)
before calling: public java.lang.String java.lang.Object.toString()
after calling: public java.lang.String java.lang.Object.toString()
[New, York] //此时调用tostring方法 没有参数无打印
before calling: public abstract java.lang.Object java.util.List.remove(int)
0 //调用动态代理的remove方法
after calling: public abstract java.lang.Object java.util.List.remove(int)
before calling: public java.lang.String java.lang.Object.toString()
after calling: public java.lang.String java.lang.Object.toString()
[York]  //此时调用tostring方法

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值