动态代理

动态代理:

好处:
①可以用代理替代委托者,实现延迟加载。
②可以在代理的方法中织入新的逻辑,完成方法调用前后应有的处理。
③许多框架的核心就是动态代理和反射,如,Hibernate的延迟加载和Spring的AOP。

静态代理的类图:

动态代理_第1张图片

相较于静态代理的好处:

①动态代理和静态代理在结构原则上是一致的,但将Proxy的生成交给了第三方(JDK/CGLib),这个代理将被放在内存中,减少了不必要的代码量;

②由于是运行时通过黑科技(改字节码)来实现的,因此灵活得多。


JDK实现的动态代理:

原理:
为接口A,B...生成一个代理$ProxyN,让代理出现在本应该由A/B..出现的位置,拦截对这些接口的调用,实际的请求会传给A/B..,并在A/B..上被调用。
类图:
动态代理_第2张图片
调用流程:
①Proxy中持有对InvocationHandler的引用;
②InvocationHandler中持有对委托人(接口A/B..的实现类)的引用;
③$ProxyN上的方法调用会转给InvocationHandler(继承自Proxy的属性)的invoke方法;
④invoke方法由编码人员自己实现,一般会将请求转给委托人(method.invoke(target,args))。

关于动态代理的设计,一些常见疑问:
①为什么只能实现对接口的代理?  Java不支持多继承,你已经看到,$ProxyN继承了Proxy,就算不那样,也无法像接口能代理多个。
②为什么非要继承或者实现委托类?  ..代理模式的精髓,就是用一个代理来拦截对真实对象的调用,如果他们类型都不一致会很麻烦。
③加入InvacationHandler作为中继器,而不直接向$Proxy0添加委托类引用的原因这个问题有点意思,从2个方面回答。
(1最致命的一点,$ProxyN在运行时通过字节码生成,给他添加一个没有模板的引用,我想会很麻烦;
(2代理很多情况下是为了能在方法调用前后织入一些逻辑而使用的。没有了InvocationHandler,那么你在其中实现的before,after等方法将会被放到$ProxyN中,首先他他不符合我们的审美,其次在实现了多个接口时,会逻辑混乱。因此添加一个中继器是很有必要的。

编码:
 1. 创建调用中继器,实现InvocationHandler接口,并实现其invoke方法。一般在该对象上需持有委托类的实例。
 2.创建代理类:使用Proxy.newProxyInstance(ClassLoader cl,Class[]  interfaces,InvocationHandler h)即可。
 3.将此方法返回的对象强转,即可直接调用代理,代理会通过中继器将请求转给给委托对象

一个例子:
MyInvocationHandler持有两个接口——Hello,jlu实现类的引用,invoke方法通过调用时的方法名来实现对不同target(client/client1)的调用。
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;


//实现自己的请求中继器
public class  MyInvocationHandler implements InvocationHandler{

	private Object client;//委托人
	private Object client1;//委托人
	
	MyInvocationHandler(Object c,Object c1) {
		client = c;
		client1 = c1;
	}
	
	@Override
	public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
		System.out.println("before...");
		//对带有参数...的client调用此method对象表示的底层方法
		if(method.getName()=="hello")
		method.invoke(client1);
		if(method.getName()=="school")
		method.invoke(client);
		System.out.println("after...");
		return null;
	}

	public static void main(String[] args) {
		Hello hTest = new HelloImpl();
		jlu sTest = new jluImpl();
		MyInvocationHandler handlerTest = new MyInvocationHandler(sTest,hTest);
		//创建代理类
		jlu o = (jlu)Proxy.newProxyInstance(jlu.class.getClassLoader(), 
				new Class[]{Hello.class,jlu.class}, handlerTest);
		o.school();
		Hello h = (Hello)o;
		h.hello();
		try {
			Method m = Hello.class.getMethod("hello",new Class[]{});
			try {
				System.out.println(666666666);
				m.invoke(hTest);
			} catch (IllegalAccessException | IllegalArgumentException | InvocationTargetException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		} catch (NoSuchMethodException | SecurityException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}
}


class HelloImpl implements Hello {
	public void hello() {
		System.out.println("HelloImpl");
	}
	
}

class jluImpl implements jlu {
	public void school() {
		System.out.println("School");
	}
}
输出:
before...
School
after...
before...
HelloImpl
after...
666666666
HelloImpl   

其他动态代理:
CGLib可以实现对非接口的代理,也是通过修改字节码实现的。有兴趣的话可以搜索相关资料。

你可能感兴趣的:(Java,设计模式,动态代理)