一、代理模式与静态代理
代理模式
给某一个对象提供一个代理,并由代理对象控制对原对象的引用。
代理模式包含如下角色:
Subject:抽象主题角色
Proxy:代理主题角色
RealSubject:真实主题角色
静态代理
代理类在程序运行前就已经存在。通常情况下,静态代理中的代理类和委托类会实现同一接口或是派生自相同的父类。
静态代理例子:
public class ProxyDemo {
public static void main(String[] args) {
consumer(new TargetObject());
consumer(new SimpleProxy(new TargetObject()));
}
public static void consumer(Interface inter) {
inter.doSomething();
inter.somethingElse("Tomy");
}
interface Interface { //抽象主题角色
void doSomething();
void somethingElse(String arg);
}
static class TargetObject implements Interface { //真实主题角色
public void doSomething() {
System.out.println("target doSomething");
}
public void somethingElse(String arg) {
System.out.println("target somethingElse " + arg);
}
}
static class SimpleProxy implements Interface { //代理主题角色
private Interface target;
public SimpleProxy(Interface target) {
this.target = target;
}
public void doSomething() {
System.out.println("SimpleProxy doSomething");
target.doSomething();
}
public void somethingElse(String arg) {
System.out.println("SimpleProxy somethingElse " + arg);
target.somethingElse(arg);
}
}
}
//输出
target doSomething
target somethingElse Tomy
SimpleProxy doSomething
target doSomething
SimpleProxy somethingElse Tomy
target somethingElse Tomy
二、动态代理
动态代理概念
代理类在程序运行时创建的方式被成为动态代理。也就是说,代理类并不是在Java代码中定义的,而是在运行时根据我们在Java代码中的"指示"动态生成的。相比于静态代理,动态代理的优势在于可以很方便的对代理类的函数进行统一的处理,而不用修改每个代理类的函数。
动态代理相关类和接口
java.lang.reflect.Proxy:
Java动态代理机制的主类,提供了一组静态方法来为一组接口动态地生成代理类及其实例。
Proxy的静态方法:
//方法1: 该方法用于获取指定动态代理对象所关联的调用处理器
static InvocationHandler getInvocationHandler(Object proxy)
//方法2:该方法用于获取关联于指定类装载器和一组接口的动态代理对象
static Class getProxyClass(ClassLoader loader, Class[] interfaces)
//方法3:该方法用于判断指定类对象是否是一个动态代理类
static boolean isProxyClass(Class cl)
//方法4:该方法用于为指定类装载器、一组接口及调用处理器生成动态代理对象
static Object newProxyInstance(ClassLoader loader, Class[] interfaces, InvocationHandler h)
java.lang.reflect.InvocationHandler:
调用处理器接口,它自定义了一个invoke方法,用于集中处理在动态代理对象上的方法调用,通常在该方法中实现对委托类的代理访问。每次生成动态代理对象时都需要指定一个实现了该接口的调用处理器对象。
InvocationHandler的核心方法:
//该方法负责集中处理动态代理类上的所有方法调用。
//第一个参数是代理对象,第二个参数是被调用的方法对象,第三个方法是调用参数。
//调用处理器根据这三个参数进行预处理或分派到委托类实例上反射执行。
Object invoke(Object proxy, Method method, Object[] args)
java.lang.ClassLoader:
类装载器,负责将类的字节码装载到Java虚拟机(JVM)中并为其定义类对象,然后该类才能被使用。Proxy静态方法生成代理类同样需要通过类装载器来进行装载才能使用,它与普通类的唯一区别就是其字节码是由JVM在运行时动态生成的而非预存在于任何一个.class文件中。每次生成动态代理对象时都需要指定一个类装载器对象。
动态代理实现方法
方法一:
- 步骤1:通过实现InvocationHandler接口创建自己的调用处理器;
- 步骤2:通过为Proxy类指定ClassLoader对象和一组interface来创建动态代理类;
- 步骤3:通过反射机制获得动态代理类的构造函数,其唯一参数类型是调用处理器接口类型;
- 步骤4:通过构造函数创建动态代理对象,构造时调用处理器对象作为参数被传入。
//InvocationHandlerImpl实现了InvocationHandler接口,并能实现方法调用从代理类到委托类的分派转发
//其内部通常包含指向委托类实例的引用,用于真正执行分派转发过来的方法调用
InvocationHandler handler = new InvocationHandlerImpl(...);
//通过Proxy为包括Interface接口在内的一组接口动态创建代理类的类对象
Class clazz = Proxy.getProxyClass(classLoader, new Class[] { Interface.class, ... });
//通过反射从生成的类对象获得构造函数对象
Constructor constructor = clazz.getConstructor(new Class[] { InvocationHandler.class });
//通过构造函数对象创建动态代理对象
Interface Proxy = (Interface)constructor.newInstance(new Object[] { handler });
方法二:
- 还有更简便的动态代理实现方法,Proxy的静态方法newProxyInstance已经为我们封装了步骤2到步骤4的过程。
//InvocationHandlerImpl实现了InvocationHandler接口,并能实现方法调用从代理类到委托类的分派转发
InvocationHandler handler = new InvocationHandlerImpl(...);
//通过Proxy直接创建动态代理对象
Interface proxy = (Interface)Proxy.newProxyInstance(classLoader, new Class[] { Interface.class }, handler );
动态代理例子:
//测试代码
public class DynamicProxy {
public static void main(String[] args) {
System.getProperties().put("sun.misc.ProxyGenerator.saveGeneratedFiles", "true"); //保存生成的动态代理.class文件
TargetObject target = new TargetObject();
consumer(target);
//Insert a proxy and call again:
Interface proxy = (Interface)Proxy.newProxyInstance(
Interface.class.getClassLoader(),
new Class[]{ Interface.class },
new DynamicProxyHandler(target));
consumer(proxy);
}
public static void consumer(Interface iface) {
iface.doSomething();
iface.somethingElse("Tomy");
}
interface Interface { //抽象主题角色
void doSomething();
void somethingElse(String arg);
}
static class TargetObject implements Interface { //真实主题角色
public void doSomething() {
System.out.println("target doSomething");
}
public void somethingElse(String arg) {
System.out.println("target somethingElse " + arg);
}
}
static class DynamicProxyHandler implements InvocationHandler { //动态代理处理器
private Object target;
public DynamicProxyHandler(Object target) {
this.target = target;
}
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("InvocationHandler: " + proxy.getClass() +
", method: " + method + ", args: " + args);
if(args != null) {
for(Object arg : args) {
System.out.println("InvocationHandler args: " + arg);
}
}
return method.invoke(target, args);
}
}
}
//输出log
target doSomething
target somethingElse Tomy
InvocationHandler: class com.tomorrow.test.$Proxy0, method: public abstract void com.tomorrow.test.DynamicProxy$Interface.doSomething(), args: null
target doSomething
InvocationHandler: class com.tomorrow.test.$Proxy0, method: public abstract void com.tomorrow.test.DynamicProxy$Interface.somethingElse(java.lang.String), args: [Ljava.lang.Object;@16d3586
InvocationHandler args: Tomy
target somethingElse Tomy
生成的动态代理类:
package com.tomorrow.test;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.lang.reflect.UndeclaredThrowableException;
final class $Proxy0 extends Proxy
implements DynamicProxy.Interface
{
private static Method m1;
private static Method m3;
private static Method m2;
private static Method m4;
private static Method m0;
public $Proxy0(InvocationHandler paramInvocationHandler)
throws
{
super(paramInvocationHandler);
}
public final boolean equals(Object paramObject)
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 doSomething()
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 void somethingElse(String paramString)
throws
{
try
{
this.h.invoke(this, m4, new Object[] { paramString });
return;
}
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.tomorrow.test.DynamicProxy$Interface").getMethod("doSomething", new Class[0]);
m2 = Class.forName("java.lang.Object").getMethod("toString", new Class[0]);
m4 = Class.forName("com.tomorrow.test.DynamicProxy$Interface").getMethod("somethingElse", new Class[] { Class.forName("java.lang.String") });
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());
}
}
}
动态代理类的继承关系
动态代理对象的特点
- 每个动态代理对象都会关联一个调用处理器对象,可以通过Proxy提供的静态方法getInvocationHandler获得动态代理对象关联的调用处理器对象。
- 在动态代理对象上调用其代理的接口中所声明的方法时,这些方法最终都会执行调用处理器的invoke方法。
- 动态代理类的根类java.lang.Object中有三个方法同样会被分派到调用处理器的invoke方法中执行,它们是hashCode、equals和toString。
可能的原因有:
(1)因为这些方法为public且非final类型,能够被代理类覆写;
(2)因为这些方法往往呈现出一个类的某种特征属性,具有一定的区分度,所以为了保证代理类与委托类对外的一致性,这三个方法也应该被分派到委托类执行。
动态代理一组接口
当动态代理的一组接口有重复声明的方法且该方法被调用时,动态代理类总是从排在最前面的接口中获取方法对象并分派给调用处理器,而无论动态代理对象是否正在以该接口(或继承于该接口的某个子接口)的形式被外部引用,因为在动态代理类内部无法区分其当前的被引用类型。
例子:
//测试代码
public class DynamicProxy {
public static void main(String[] args) {
System.getProperties().put("sun.misc.ProxyGenerator.saveGeneratedFiles", "true"); //保存生成的动态代理.class文件
try {
TargetObject object = new TargetObject();
InvocationHandler handler = new DynamicProxyHandler(object);
Class<?> clazz = Proxy.getProxyClass(
TargetObject.class.getClassLoader(),
new Class[] {InterfaceB.class, InterfaceA.class}); //注意:这里先写接口B,然后写接口A
Constructor<?> constructor = clazz.getConstructor(new Class[] {InvocationHandler.class});
InterfaceA proxy_a = (InterfaceA) constructor.newInstance(new Object[] {handler});
InterfaceB proxy_b = (InterfaceB) constructor.newInstance(new Object[] {handler});
proxy_a.doSomething();
proxy_a.doA();
proxy_b.doSomething();
proxy_b.doB();
} catch(Exception e) {
e.printStackTrace();
}
}
public interface InterfaceA {
void doSomething();
void doA();
}
public interface InterfaceB {
void doSomething();
void doB();
}
static class TargetObject implements InterfaceA, InterfaceB {
public void doSomething() {
System.out.println("target doSomething");
}
public void doA() {
System.out.println("target doA");
}
public void doB() {
System.out.println("target doB");
}
}
static class DynamicProxyHandler implements InvocationHandler { //动态代理处理器
private Object target;
public DynamicProxyHandler(Object target) {
this.target = target;
}
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("InvocationHandler: " + proxy.getClass() +
", method: " + method + ", args: " + args);
if(args != null) {
for(Object arg : args) {
System.out.println("InvocationHandler args: " + arg);
}
}
return method.invoke(target, args);
}
}
}
//输出log
InvocationHandler: class com.sun.proxy.$Proxy0, method: public abstract void com.tomorrow.test.DynamicProxy$InterfaceB.doSomething(), args: null
==> 虽然动态代理对象proxy_a正以接口InterfaceA的形式被外部引用,但获取的方法对象是在getProxyClass方法排前面的InterfaceB中的。
target doSomething
InvocationHandler: class com.sun.proxy.$Proxy0, method: public abstract void com.tomorrow.test.DynamicProxy$InterfaceA.doA(), args: null
target doA
InvocationHandler: class com.sun.proxy.$Proxy0, method: public abstract void com.tomorrow.test.DynamicProxy$InterfaceB.doSomething(), args: null
target doSomething
InvocationHandler: class com.sun.proxy.$Proxy0, method: public abstract void com.tomorrow.test.DynamicProxy$InterfaceB.doB(), args: null
target doB
生成的动态代理类:
package com.sun.proxy;
import com.tomorrow.test.DynamicProxy.InterfaceA;
import com.tomorrow.test.DynamicProxy.InterfaceB;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.lang.reflect.UndeclaredThrowableException;
public final class $Proxy0 extends Proxy
implements DynamicProxy.InterfaceB, DynamicProxy.InterfaceA
{
private static Method m1;
private static Method m3;
private static Method m5;
private static Method m2;
private static Method m4;
private static Method m0;
public $Proxy0(InvocationHandler paramInvocationHandler)
throws
{
super(paramInvocationHandler);
}
public final boolean equals(Object paramObject)
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 doSomething()
throws
{
try
{
this.h.invoke(this, m3, null);
return;
}
catch (RuntimeException localRuntimeException)
{
throw localRuntimeException;
}
catch (Throwable localThrowable)
{
throw new UndeclaredThrowableException(localThrowable);
}
}
public final void doA()
throws
{
try
{
this.h.invoke(this, m5, 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 void doB()
throws
{
try
{
this.h.invoke(this, m4, null);
return;
}
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.tomorrow.test.DynamicProxy$InterfaceB").getMethod("doSomething", new Class[0]); //注意:获取的是排在前面的InterfaceB的doSomething方法
m5 = Class.forName("com.tomorrow.test.DynamicProxy$InterfaceA").getMethod("doA", new Class[0]);
m2 = Class.forName("java.lang.Object").getMethod("toString", new Class[0]);
m4 = Class.forName("com.tomorrow.test.DynamicProxy$InterfaceB").getMethod("doB", 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());
}
}
}
被动态代理的一组接口的特点
- 要注意不能有重复的接口,以避免动态代理类代码生成时出现编译错误。
- 这些接口对于类装载器必须可见,否则类装载器无法链接它们,将会导致动态代理类定义失败。
- 需要被代理的所有非public的接口必须在同一个包中,否则动态代理类代码生成也会失败。
- 接口的数目不能超过65535,这是JVM设定的限制。