什么是Class对象?
说到动态代理必须得先说Class对象。
一个Class对象(可通过Class.forName("")获取),代表正在运行中的java应用的类和接口,通过反射机制可以在运行时获取类信息,如:类名、成员属性、构造方法等。
其中 枚举类(enum)是一种特殊的类,注解 属于 一种接口 ,java中所有注解(@interface)都隐形地继承Annotation接口。
数组类也属于Class类,他的Class类对象被同类型数组(元素类型、数组维度相同)共享,具体看Class#getName()的说明。
什么是反射机制?
Java 反射机制是在运行时分析类、获取类信息、访问类的成员变量和方法的一种机制。可以动态地创建对象、调用方法、修改属性,增加了程序的灵活性和可扩展性。
什么是JDK动态代理?
JDK 动态代理是一种在运行时动态生成代理对象的机制。它基于接口实现,通过反射机制在运行时创建代理类并拦截对目标对象方法的调用,可在方法调用前后添加额外逻辑,如日志记录、权限校验等,实现对目标对象的功能增强,而无需修改目标对象的代码。
假设有这么个接口和实现类
接口IService
csharp
代码解读
复制代码
public interface IService { public void hello(); public void say(String name); public void goods(); }
接口IService的实现类 ServiceImpl
typescript
代码解读
复制代码
public class ServiceImpl implements IService { @Override public void hello() { System.out.println("Hello China!"); } @Override public void say(String name) { System.out.println("say()"); } @Override public void goods() { System.out.println("goods()"); } }
如果客户要求你在ServiceImpl 中的每个方法 执行前和执行后 分别输出 "目标方法执行前的输出。”和"目标方法执行后的输出。”,你是不是会想到这样:
csharp
代码解读
复制代码
public class ServiceProxy { private IService service; public ServiceProxy(IService service){ this.service=service; } public void hello() { System.out.println("目标方法执行前的输出。"); service.hello(); System.out.println("目标方法执行后的输出。"); } @Override public void say(String name) { System.out.println("目标方法执行前的输出。"); service.say(name); System.out.println("目标方法执行后的输出。"); } @Override public void goods() { System.out.println("目标方法执行前的输出。"); service.goods(); System.out.println("目标方法执行后的输出。"); } } //然后再main方法中执行 IService service = new ServiceImpl(); ServiceProxy proxy = new ServiceProxy(service); proxy.hello(); proxy.say("hello"); proxy.goods();
固然可以实现需求,但这无疑造成了代码冗余,也不利于后期代码的维护。
那么我们是不是可以在运行时拦截目标方法,然后将额外的需求织入其中并生成代理对象,最后通过反射机制,使用Method对象、目标对象以及方法参数来定位目标方法并执行?这就是接下来讲的JDK动态代理。
实现步骤
首先我们的代理类要实现 InvocationHandler 接口
typescript
代码解读
复制代码
public class ServiceProxy implements InvocationHandler { private IService realService; public ServiceProxy(IService realService) { this.realService = realService; } @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { System.out.println("目标方法执行前的输出。"); //通过反射来动态的调用目标方法 Object result = method.invoke(realService, args); System.out.println("目标方法执行后的输出。"); return result; } //获取代理对象 public Object getProxy() { return Proxy.newProxyInstance(realService.getClass().getClassLoader(), realService.getClass().getInterfaces(), this); } }
然后编写测试类
csharp
代码解读
复制代码
public class ProxyTest { public static void main(String[] args) { //实例化目标对象 IService real = new ServiceImpl(); //传入目标对象,创建代理类 ServiceProxy proxy = new ServiceProxy(real); //生成代理对象 IService service = (IService) proxy.getProxy(); //通过代理对象调用增强后的方法以及目标方法 service.hello(); //将生成的代理类的 class 文件保存到本地,方便反编译后查看 createProxyClassFile(); } @SuppressWarnings("unused") private static void createProxyClassFile() { // 将jdk生成的动态代理类的.class文件存放到本地 String name = "$ServiceProxy"; byte[] data = ProxyGenerator.generateProxyClass(name, new Class[] { IService.class }); FileOutputStream out = null; try { out = new FileOutputStream(name + ".class"); System.out.println((new File(name)).getAbsolutePath()); out.write(data); } catch (FileNotFoundException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } finally { if (null != out) try { out.close(); } catch (IOException e) { e.printStackTrace(); } } } }
源码解析
查看反编译后的代理类代码
typescript
代码解读
复制代码
public final class $ServiceProxy extends Proxy implements IService { private static Method m1; private static Method m5; private static Method m4; private static Method m2; private static Method m3; private static Method m0; public $ServiceProxy(InvocationHandler paramInvocationHandler) { super(paramInvocationHandler); } public final boolean equals(Object paramObject) { try { return ((Boolean)this.h.invoke(this, m1, new Object[] { paramObject })).booleanValue(); } catch (Error|RuntimeException error) { throw null; } catch (Throwable throwable) { throw new UndeclaredThrowableException(throwable); } } public final void goods() { try { this.h.invoke(this, m5, null); return; } catch (Error|RuntimeException error) { throw null; } catch (Throwable throwable) { throw new UndeclaredThrowableException(throwable); } } public final void hello() { try { this.h.invoke(this, m4, null); return; } catch (Error|RuntimeException error) { throw null; } catch (Throwable throwable) { throw new UndeclaredThrowableException(throwable); } } public final String toString() { try { return (String)this.h.invoke(this, m2, null); } catch (Error|RuntimeException error) { throw null; } catch (Throwable throwable) { throw new UndeclaredThrowableException(throwable); } } public final void say(String paramString) { try { this.h.invoke(this, m3, new Object[] { paramString }); return; } catch (Error|RuntimeException error) { throw null; } catch (Throwable throwable) { throw new UndeclaredThrowableException(throwable); } } public final int hashCode() { try { return ((Integer)this.h.invoke(this, m0, null)).intValue(); } catch (Error|RuntimeException error) { throw null; } catch (Throwable throwable) { throw new UndeclaredThrowableException(throwable); } } static { try { m1 = Class.forName("java.lang.Object").getMethod("equals", new Class[] { Class.forName("java.lang.Object") }); m5 = Class.forName("com.cen.proxy.IService").getMethod("goods", new Class[0]); m4 = Class.forName("com.cen.proxy.IService").getMethod("hello", new Class[0]); m2 = Class.forName("java.lang.Object").getMethod("toString", new Class[0]); m3 = Class.forName("com.cen.proxy.IService").getMethod("say", new Class[] { Class.forName("java.lang.String") }); m0 = Class.forName("java.lang.Object").getMethod("hashCode", new Class[0]); return; } catch (NoSuchMethodException noSuchMethodException) { throw new NoSuchMethodError(noSuchMethodException.getMessage()); } catch (ClassNotFoundException classNotFoundException) { throw new NoClassDefFoundError(classNotFoundException.getMessage()); } } }
我们可以看到,代理类先通过Class.forName 去获取被代理接口的Class对象,然后再调用Class对象的getMethod方法,通过方法名和方法传参来返回一个目标方法的Method对象
vbnet
代码解读
复制代码
Class.forName("com.cen.proxy.IService").getMethod("hello", new Class[0])
该Method对象会在$ServiceProxy调用方法的时候使用到,他将会作为参数传入
css
代码解读
复制代码
ServiceProxy#invoke(Object proxy, Method method, Object[] args)
并被使用
typescript
代码解读
复制代码
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { Object result = method.invoke(realService, args); return result; }
例如: hello()的调用,调用链是这样的:
步骤1. main方法
scss
代码解读
复制代码
service.hello()
步骤2. $ServiceProxy的static代码块
vbnet
代码解读
复制代码
m4 = Class.forName("com.cen.proxy.IService").getMethod("hello", new Class[0])
步骤3. $ServiceProxy#hello()
typescript
代码解读
复制代码
public final void hello() { try { this.h.invoke(this, m4, null); return; } catch (Error|RuntimeException error) { throw null; } catch (Throwable throwable) { throw new UndeclaredThrowableException(throwable); } }
步骤4. ServiceProxy#invoke
typescript
代码解读
复制代码
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { System.out.println("目标方法执行前的输出。"); Object proxyObj = method.invoke(realService, args); System.out.println("目标方法执行后的输出。"); return proxyObj; }