Java动态代理

代理模式英语:Proxy Pattern)是程序设计中的一种设计模式。

代理模式是对其他对象提供一种代理以控制对这个对象的访问。代理模式的思想是为了提供额外的处理或者不同的操作而在实际对象与调用者之间插入一个代理对象。这些额外的操作通常需要与实际对象进行通信。

代理可分为静态代理动态代理

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

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

代理角色:代理对象角色内部含有对真实对象的引用,从而可以操作真实对象,同时代理对象提供与真实对象相同的接口以便在任何时刻都能代替真实对象。同时,代理对象可以在执行真实对象操作时,附加其他的操作,相当于对真实对象进行封装。

真实角色:代理角色所代表的真实对象,是我们最终要引用的对象。

 静态代理实例:

抽象角色

public abstract class Subject {
	public abstract void request();
}
<span style="color: rgb(255, 0, 0); font-family: Arial;"><span style="color: rgb(255, 0, 0); font-family: Arial; font-size: 14px; line-height: 26px; text-indent: 28px;">真实角色</span></span>
<pre name="code" class="java" style="font-size: 14px; line-height: 26px; text-indent: 28px; color: rgb(51, 51, 51);"><span style="font-family: Arial;">public class RealSubject extends Subject {</span>
@Overridepublic void request() {// TODO Auto-generated method stub}}

 
 
<span style="font-family: Arial;"></span><pre name="code" class="java" style="font-size: 14px; line-height: 26px; text-indent: 28px;"><span style="color:#ff0000;">代理角色</span>
 
 
public class ProxySubject extends Subject {
	
	private RealSubject realSubject = null;

	@Override
	public void request() {
		preRequest();  //真实角色操作前的附加操作
		
		if(realSubject == null){
			realSubject =  new RealSubject();
		}
		realSubject.request();
		
		postRequest();  //真实角色操作后的附加操作
	}

	private void postRequest() {
		// TODO Auto-generated method stub
		
	}

	private void preRequest() {
		// TODO Auto-generated method stub
		
	}

}

public class Main {
	public static void main(String[] args) {
		Subject subject = new ProxySubject();
		subject.request();  //代理者代替真实者做事情
	}
}

<span style=" font-family: Arial; background-color: rgb(255, 255, 255);"><strong>动态代理</strong></span>
 

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

(1). Interface InvocationHandler:

Object invoke(Object proxy,
              Method method,
              Object[] args)
              throws Throwable
在代理实例上处理方法调用并返回结果。在与方法关联的代理实例上调用方法时,将在调用处理程序上调用此方法。

参数:
proxy - 在其上调用方法的代理实例
method - 对应于在代理实例上调用的接口方法的 Method 实例。 Method 对象的声明类将是在其中声明方法的接口,该接口可以是代理类赖以继承方法的代理接口的超接口。
args - 包含传入代理实例上方法调用的参数值的对象数组,如果接口方法不使用参数,则为 null。基本类型的参数被包装在适当基本包装器类(如 java.lang.Integerjava.lang.Boolean)的实例中。
返回:
从代理实例的方法调用返回的值。如果接口方法的声明返回类型是基本类型,则此方法返回的值一定是相应基本包装对象类的实例;否则,它一定是可分配到声明返回类型的类型。如果此方法返回的值为 null 并且接口方法的返回类型是基本类型,则代理实例上的方法调用将抛出 NullPointerException。否则,如果此方法返回的值与上述接口方法的声明返回类型不兼容,则代理实例上的方法调用将抛出 ClassCastException。 
 

(2).Proxy:该类即为动态代理类,作用类似于上例中的ProxySubject,其中主要包含以下内容:

Protected Proxy(InvocationHandler h):构造函数,

protected Proxy(InvocationHandler h)
使用其调用处理程序的指定值从子类(通常为动态代理类)构建新的 Proxy 实例。

参数:
h - 此代理实例的调用处理程序

public static Class<?> getProxyClass(ClassLoader loader,
                                     Class<?>... interfaces)
                              throws IllegalArgumentException
返回代理类的 java.lang.Class 对象,并向其提供类加载器和接口数组。该代理类将由指定的类加载器定义,并将实现提供的所有接口。如果类加载器已经定义了具有相同排列接口的代理类,那么现有的代理类将被返回;否则,类加载器将动态生成并定义这些接口的代理类。

对可以传递给 Proxy.getProxyClass 的参数有以下几个限制:

  • interfaces 数组中的所有 Class 对象必须表示接口,而不能表示类或基本类型。
  • interfaces 数组中的两个元素不能引用同一 Class 对象。
  • 所有接口类型的名称通过特定的类加载器必须可见。换句话说,对于类加载器 cl 和所有接口 i,以下表达式必须为 true:
    Class.forName(i.getName(), false, cl) == i
  • 所有非公共接口必须位于同一包中;否则,该代理类将不可能实现所有的接口,无论它在哪一个包中定义。
  • 对于有相同签名的指定接口中任何成员方法集:
    • 如果任何方法的返回类型是基本类型或 void,那么所有的方法必须具有与此相同的返回类型。
    • 否则,该方法之一必须是返回类型,它可以指派给该方法其余的所有返回类型。
  • 得到的代理类必须不超过虚拟机在类上施加的任何限制。例如,虚拟机可以限制某一类实现至多 65535 的接口数;在这种情况下,interfaces 数组的大小必须不超过 65535。

如果违反了这些限制,Proxy.getProxyClass 将抛出 IllegalArgumentException。如果 interfaces 数组参数或其任何元素为 null,则将抛出 NullPointerException

注意,指定的代理接口的顺序非常重要:对接口组合相同但顺序不同的代理类的两个请求会导致两个不同的代理类。

参数:
loader - 定义代理类的类加载器
interfaces - 代理类要实现的接口列表
返回:
用指定的类加载器定义的代理类,它可以实现指定的接口

public static Object newProxyInstance(ClassLoader loader,
                                      Class<?>[] interfaces,
                                      InvocationHandler h)
                               throws IllegalArgumentException
返回一个指定接口的代理类实例,该接口可以将方法调用指派到指定的调用处理程序。此方法相当于:
Proxy.getProxyClass(loader, interfaces).
         getConstructor(new Class[] { InvocationHandler.class }).
         newInstance(new Object[] { handler });

Proxy.newProxyInstance 抛出 IllegalArgumentException,原因与 Proxy.getProxyClass 相同。

参数:
loader - 定义代理类的类加载器
interfaces - 代理类要实现的接口列表
h - 指派方法调用的调用处理程序
返回:
一个带有代理类的指定调用处理程序的代理实例,它由指定的类加载器定义,并实现指定的接口 

动态代理类(以下简称为代理类)是一个实现在创建类时在运行时指定的接口列表的类,该类具有下面描述的行为。 代理接口 是代理类实现的一个接口。 代理实例 是代理类的一个实例。 每个代理实例都有一个关联的调用处理程序 对象,它可以实现接口 InvocationHandler。通过其中一个代理接口的代理实例上的方法调用将被指派到实例的调用处理程序的 Invoke 方法,并传递代理实例、识别调用方法的 java.lang.reflect.Method 对象以及包含参数的 Object 类型的数组。调用处理程序以适当的方式处理编码的方法调用,并且它返回的结果将作为代理实例上方法调用的结果返回。 

动态代理实例:

与上例类似只修改代理对象:

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;

public class DynamicSubject implements InvocationHandler {

	private Object sub; // 真实对象的引用

	public DynamicSubject(Object sub) {
		this.sub = sub;
	}

	@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("after calling " + method); 
        return null; 
	}

}
main():

import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Proxy;

public class Main {
	public static void main(String[] args) throws Throwable {
		RealSubject rs = new RealSubject();
		InvocationHandler handler = 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[]{handler});
		subject.request();
	}
}







你可能感兴趣的:(Java动态代理)