1动态代理和静态代理的角色是一样的(真是角色,抽象角色,代理类)
2动态代理的代理类是动态生成的。
3分为两类:一类基于接口动态代理和基于类的动态代理
1):基于接口动态代理:jdk动态代理
2):基于类的动态代理:cglib
现在都用javasist来生成动态代理
4jdk动态代理---proxy类和InvocationHandler接口
InvocationHandler
是代理实例的调用处理程序 实现的接口。
每个代理实例都具有一个关联的调用处理程序。对代理实例调用方法时,将对方法调用进行编码并将其指派到它的调用处理程序的 invoke
方法
invoke
Object invoke(Object proxy, Method method, Object[] args) throws Throwable
在代理实例上处理方法调用并返回结果。在与方法关联的代理实例上调用方法时,将在调用处理程序上调用此方法。
参数:
proxy
- 在其上调用方法的代理实例
method
- 对应于在代理实例上调用的接口方法的 Method
实例。Method
对象的声明类将是在其中声明方法的接口,该接口可以是代理类赖以继承方法的代理接口的超接口。
args
- 包含传入代理实例上方法调用的参数值的对象数组,如果接口方法不使用参数,则为 null
。基本类型的参数被包装在适当基本包装器类(如 java.lang.Integer
或 java.lang.Boolean
)的实例中。
返回:
从代理实例的方法调用返回的值。如果接口方法的声明返回类型是基本类型,则此方法返回的值一定是相应基本包装对象类的实例;否则,它一定是可分配到声明返回类型的类型。如果此方法返回的值为 null
并且接口方法的返回类型是基本类型,则代理实例上的方法调用将抛出 NullPointerException
。否则,如果此方法返回的值与上述接口方法的声明返回类型不兼容,则代理实例上的方法调用将抛出 ClassCastException
。
抛出:
Throwable
- 从代理实例上的方法调用抛出的异常。该异常的类型必须可以分配到在接口方法的 throws
子句中声明的任一异常类型或未经检查的异常类型 java.lang.RuntimeException
或 java.lang.Error
。如果此方法抛出经过检查的异常,该异常不可分配到在接口方法的 throws
子句中声明的任一异常类型,代理实例的方法调用将抛出包含此方法曾抛出的异常的
-----from jdkAPI1.6
Proxy
Proxy
提供用于创建动态代理类和实例的静态方法,它还是由这些方法创建的所有动态代理类的超类。
创建某一接口 Foo
的代理:
InvocationHandler handler = new MyInvocationHandler(...); Class proxyClass = Proxy.getProxyClass( Foo.class.getClassLoader(), new Class[] { Foo.class }); Foo f = (Foo) proxyClass. getConstructor(new Class[] { InvocationHandler.class }). newInstance(new Object[] { handler });
或使用以下更简单的方法:
Foo f = (Foo) Proxy.newProxyInstance(Foo.class.getClassLoader(), new Class[] { Foo.class }, handler);
动态代理类(以下简称为代理类)是一个实现在创建类时在运行时指定的接口列表的类,该类具有下面描述的行为。 代理接口 是代理类实现的一个接口。 代理实例 是代理类的一个实例。 每个代理实例都有一个关联的调用处理程序 对象,它可以实现接口 InvocationHandler
。通过其中一个代理接口的代理实例上的方法调用将被指派到实例的调用处理程序的 Invoke
方法,并传递代理实例、识别调用方法的 java.lang.reflect.Method
对象以及包含参数的 Object
类型的数组。调用处理程序以适当的方式处理编码的方法调用,并且它返回的结果将作为代理实例上方法调用的结果返回。
代理类具用以下属性:
- 代理类是公共的、最终的,而不是抽象的。
- 未指定代理类的非限定名称。但是,以字符串
"$Proxy"
开头的类名空间应该为代理类保留。 - 代理类扩展
java.lang.reflect.Proxy
。 - 代理类会按同一顺序准确地实现其创建时指定的接口。
- 如果代理类实现了非公共接口,那么它将在与该接口相同的包中定义。否则,代理类的包也是未指定的。注意,包密封将不阻止代理类在运行时在特定包中的成功定义,也不会阻止相同类加载器和带有特定签名的包所定义的类。
- 由于代理类将实现所有在其创建时指定的接口,所以对其
Class
对象调用getInterfaces
将返回一个包含相同接口列表的数组(按其创建时指定的顺序),对其Class
对象调用getMethods
将返回一个包括这些接口中所有方法的Method
对象的数组,并且调用getMethod
将会在代理接口中找到期望的一些方法。 - 如果
Proxy.isProxyClass
方法传递代理类(由Proxy.getProxyClass
返回的类,或由Proxy.newProxyInstance
返回的对象的类),则该方法返回 true,否则返回 false。 - 代理类的
java.security.ProtectionDomain
与由引导类加载器(如java.lang.Object
)加载的系统类相同,原因是代理类的代码由受信任的系统代码生成。此保护域通常被授予java.security.AllPermission
。 - 每个代理类都有一个可以带一个参数(接口
InvocationHandler
的实现)的公共构造方法,用于设置代理实例的调用处理程序。并非必须使用反射 API 才能访问公共构造方法,通过调用Proxy.newInstance
方法(将调用Proxy.getProxyClass
的操作和调用带有调用处理程序的构造方法结合在一起)也可以创建代理实例。
代理实例具有以下属性:
- 提供代理实例
proxy
和一个由其代理类Foo
实现的接口,以下表达式将返回 true:proxy instanceof Foo
ClassCastException
):(Foo) proxy
- 每个代理实例都有一个关联的调用处理程序,它会被传递到其构造方法中。静态
Proxy.getInvocationHandler
方法将返回与作为其参数传递的代理实例相关的调用处理程序。 - 代理实例上的接口方法调用将按照该方法的文档描述进行编码,并被指派到调用处理程序的
Invoke
方法。 - 在代理实例上的
java.lang.Object
中声明的hashCode
、equals
或toString
方法的调用将按照与编码和指派接口方法调用相同的方式进行编码,并被指派到调用处理程序的invoke
方法,如上所述。传递到invoke
的Method
对象的声明类是java.lang.Object
。代理类不重写从java.lang.Object
继承的代理实例的其他公共方法,所以这些方法的调用行为与其对java.lang.Object
实例的操作一样。
在多代理接口中重复的方法
当代理类的两个或多个接口包含一个具有相同名称和参数签名的方法时,代理类的接口顺序变得非常重要。在代理实例上调用重复方法 时,传递到调用处理程序的 Method
对象没有必要成为其声明类可以从接口(通过该接口调用代理方法)的引用类型指派的对象。此限制存在的原因是,生成的代理类中的相应方法实现无法确定它通过哪一个接口调用。因此,在代理实例上调用重复方法时,第一个接口中的方法的 Method
对象包含接口的代理类列表中的方法(直接或通过超级接口继承),该对象会传递到调用处理程序的 invoke
方法,无论该方法调用通过哪一种引用类型发生。
如果代理接口包含某一方法,它的名称和参数签名与 java.lang.Object
的 hashCode
、equals
或 toString
方法相同,那么在代理实例上调用这样的方法时,传递到调用处理程序的 Method
对象将使 java.lang.Object
成为其声明类。换句话说,java.lang.Object
公共的非最终方法理论上在所有代理接口之前,以便确定哪一个 Method
对象传递到调用处理程序。
还要注意,当重复方法被指派到调用处理程序时,invoke
方法只可以抛出经过检查的异常类型,该异常类型可以使用所有 代理接口(可以通过它调用)中方法的 throws
子句指派一种异常类型。如果 invoke
方法抛出一个经过检查的异常,该异常没有指派给任何由一个代理接口(可以通过它调用)中的方法声明的异常类型,那么该代理实例上的调用将抛出一个未经检查的 UndeclaredThrowableException
。此限制表示并非所有的由传递到 invoke
方法的 Method
对象上调用 getExceptionTypes
返回的异常类型都可以由 invoke
方法成功抛出。
static Object | newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h) 返回一个指定接口的代理类实例,该接口可以将方法调用指派到指定的调用处理程序。 |
实现:
ProxyInovationHandler.java :
public class ProxyInovationHandler implements InvocationHandler{
private Object target;
public void settarget(Object target) {
this.target = target;
}
/**
* 生成代理类
* @return
*/
public Object getProxy() {
return Proxy.newProxyInstance(this.getClass().getClassLoader(), target.getClass().getInterfaces(),
this);
}
/**
* proxy是代理类
* method 代理类调用处理程序的方法对象
*
*/
@Override
public Object invoke(Object proxy, Method method, Object[] arg2) throws Throwable {
log(method.getName());
Object result = method.invoke(target, arg2);
return result;
}
public void log(String method) {
System.out.println("执行"+method+"方法");
}
private void seeHouse() {
System.out.println("take client to host");
}
private void fare() {
System.out.println("get money from client");
}
}
Client.java:
public class Client {
public static void main(String[] args) {
// Host host = new Host();
// ProxyInovationHandler pih = new ProxyInovationHandler();
// pih.settarget(host);
// Rent proxy = (Rent) pih.getProxy();
// proxy.rent(); //输出结果:执行rent方法
rent host
UserService userService = new UserServiceImpl();
ProxyInovationHandler pih = new ProxyInovationHandler();
pih.settarget(userService);
UserService proxy = (UserService) pih.getProxy();
proxy.add(); //输出结果:执行add方法
增加
ProxyInovationHandler pih = new ProxyInovationHandler();
pih.settarget(new ArrayList<>());
List list = (List) pih.getProxy();
list.add("123");
list.remove("123"); //输出结果:执行add方法
执行remove方法
}
}
一个动态代理一般代理某一类业务。一个动态代理可以代理多个类