Java动态代理
一、代理模式
给某一个对象提供一个代理,并由代理对象控制对原对象的引用。
代理模式包含如下角色:
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动态代理机制的主类,提供了一组静态方法来为一组接口动态地生成代理类及其实例。其实现了Serializable接口。
静态方法有如下四个:
//方法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)
在类里面定义了一个调用处理器h,getInvocationHandler()这个方法就是用来获取指定动态代理对象联系的调用处理器。
@CallerSensitive public static InvocationHandler getInvocationHandler(Object proxy) throws IllegalArgumentException { /* * Verify that the object is actually a proxy instance. */ if (!isProxyClass(proxy.getClass())) { throw new IllegalArgumentException("not a proxy instance"); } final Proxy p = (Proxy) proxy; final InvocationHandler ih = p.h; if (System.getSecurityManager() != null) { Class<?> ihClass = ih.getClass(); Class<?> caller = Reflection.getCallerClass(); if (ReflectUtil.needsPackageAccessCheck(caller.getClassLoader(), ihClass.getClassLoader())) { ReflectUtil.checkPackageAccess(ihClass); } } return ih; }
接下来,重点在于java.lang.reflect.InvocationHandler:
它是调用处理器接口,它自定义了一个invoke方法,用于集中处理在动态代理对象上的方法调用,通常在该方法中实现对委托类的代理访问。每次生成动态代理对象时都需要指定一个实现了该接口的调用处理器对象。
InvocationHandler的核心方法:
//该方法负责集中处理动态代理类上的所有方法调用。 //第一个参数是代理对象,第二个参数是被调用的方法对象,第三个方法是调用参数。 //调用处理器根据这三个参数进行预处理或分派到委托类实例上反射执行。 public Object invoke(Object proxy, Method method, Object[] args) throws Throwable;
动态代理实现方法
我们刚刚讲了这么多方法,但还没有具体分析动态代理具体实现是怎么实现的,接下来便进行分析。
方法一:
- 步骤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 {//抽象主题角色 interface Interface { void doSomething(); void somethingelse(String args); } static class RealObject implements Interface //真实主题角色 { @Override public void doSomething() { } @Override public void somethingelse(String args) { } } static class TargetObject implements Interface {//动态代理处理器 @Override public void doSomething() { System.out.println("target dosomething"); } @Override public void somethingelse(String args) { System.out.println("target somethingelse " + args); } } static class DynamicProxyHandler implements InvocationHandler { private Object targetObject; DynamicProxyHandler(Object target){ targetObject = target; } @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { System.out.println("InvocationHandler: " + proxy.getClass().getName() + ", method: " + method + ", args: " + args); if (args != null) { for (Object arg:args) { System.out.println("args: " + arg); } } return method.invoke(targetObject, args); } } public static void consumer(Interface inte) { inte.doSomething(); inte.somethingelse("jason"); } public static void main(String args[]) { System.getProperties().put("ProxyGenerator.saveGeneratedFiles", "true"); TargetObject targetObject = new TargetObject(); consumer(targetObject); Interface proxy = (Interface) Proxy.newProxyInstance(Interface.class.getClassLoader(), new Class[] {Interface.class}, new DynamicProxyHandler(targetObject)); consumer(proxy); } }
最后输出的结果如下:
target dosomething target somethingelse jason InvocationHandler: $Proxy0, method: public abstract void DynamicProxy$Interface.doSomething(), args: null target dosomething InvocationHandler: $Proxy0, method: public abstract void DynamicProxy$Interface.somethingelse(java.lang.String), args: [Ljava.lang.Object;@2503dbd3 args: jason target somethingelse jason Process finished with exit code 0
动态代理类的继承关系
动态代理对象的特点
- 每个动态代理对象都会关联一个调用处理器对象,可以通过Proxy提供的静态方法getInvocationHandler获得动态代理对象关联的调用处理器对象。
- 在动态代理对象上调用其代理的接口中所声明的方法时,这些方法最终都会执行调用处理器的invoke方法。
- 动态代理类的根类java.lang.Object中有三个方法同样会被分派到调用处理器的invoke方法中执行,它们是hashCode、equals和toString。
可能的原因有:
(1)因为这些方法为public且非final类型,能够被代理类覆写;
(2)因为这些方法往往呈现出一个类的某种特征属性,具有一定的区分度,所以为了保证代理类与委托类对外的一致性,这三个方法也应该被分派到委托类执行。
动态代理一组接口
当动态代理的一组接口有重复声明的方法且该方法被调用时,动态代理类总是从排在最前面的接口中获取方法对象并分派给调用处理器,而无论动态代理对象是否正在以该接口(或继承于该接口的某个子接口)的形式被外部引用,因为在动态代理类内部无法区分其当前的被引用类型。
举个例子,还照着刚刚的Interface,对其中interface方法进行改变,添加一个InterfaceB:
import java.lang.annotation.Target; import java.lang.reflect.Constructor; import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; import java.lang.reflect.Proxy; import java.net.InterfaceAddress; public class DynamicProxy { public interface InterfaceA { void doSomething(); void doA(); } public interface InterfaceB { void doSomething(); void doB(); } static class TargetObject implements InterfaceA, InterfaceB { @Override public void doSomething() { System.out.println("target doSomething"); } @Override public void doA() { System.out.println("target doA"); } @Override public void doB() { System.out.println("target doB"); } } static class DynamicProxyHandler implements InvocationHandler { private Object targetObject; DynamicProxyHandler(Object target){ targetObject = target; } @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { System.out.println("InvocationHandler: " + proxy.getClass().getName() + ", method: " + method + ", args: " + args); if (args != null) { for (Object arg:args) { System.out.println("args: " + arg); } } return method.invoke(targetObject, args); } } public static void main(String args[]) { System.getProperties().put("ProxyGenerator.saveGeneratedFiles", "true"); try { TargetObject targetObject = new TargetObject(); InvocationHandler invocationHandler = new DynamicProxyHandler(targetObject); Class<?> clas = Proxy.getProxyClass(TargetObject.class.getClassLoader(), new Class[] {InterfaceB.class, InterfaceA.class}); Constructor<?> constructor = clas.getConstructor(new Class[] {InvocationHandler.class}); InterfaceA proxy_A = (InterfaceA) constructor.newInstance(new Object[] {invocationHandler}); InterfaceB proxy_B = (InterfaceB) constructor.newInstance(new Object[] {invocationHandler}); proxy_A.doSomething(); proxy_A.doA(); proxy_B.doSomething(); proxy_B.doB(); } catch (Exception e) { e.printStackTrace(); } } }
结果:
InvocationHandler: com.sun.proxy.$Proxy0, method: public abstract void DynamicProxy$InterfaceB.doSomething(), args: null target doSomething InvocationHandler: com.sun.proxy.$Proxy0, method: public abstract void DynamicProxy$InterfaceA.doA(), args: null target doA InvocationHandler: com.sun.proxy.$Proxy0, method: public abstract void DynamicProxy$InterfaceB.doSomething(), args: null target doSomething InvocationHandler: com.sun.proxy.$Proxy0, method: public abstract void DynamicProxy$InterfaceB.doB(), args: null target doB Process finished with exit code 0
从结果我们可以看到,第一行虽然是动态代理对象proxy_A以InterfaceA的形式被外部引用,但是获取的对象方法是在getProxyClass方法排前面的InterfaceB中的。